diff options
| author | Xavier Del Campo Romero <xavi92@disroot.org> | 2025-07-07 13:22:53 +0200 |
|---|---|---|
| committer | Xavier Del Campo Romero <xavi92@disroot.org> | 2025-11-11 00:08:15 +0100 |
| commit | 7861a52adf92a083bb2aed4c35f98d8035dce032 (patch) | |
| tree | 28cd3c40e4c878f730f5df3c1d93bdf91af490c3 /src/gfx | |
| parent | 7fc48e9216ff809da5f8055a50b0be17628ef1df (diff) | |
| download | wnix-7861a52adf92a083bb2aed4c35f98d8035dce032.tar.gz | |
Setup project skeleton
Diffstat (limited to 'src/gfx')
37 files changed, 1930 insertions, 0 deletions
diff --git a/src/gfx/CMakeLists.txt b/src/gfx/CMakeLists.txt new file mode 100644 index 0000000..85bf890 --- /dev/null +++ b/src/gfx/CMakeLists.txt @@ -0,0 +1,25 @@ +# wnix, a Unix-like operating system for WebAssembly applications. +# Copyright (C) 2025 Xavier Del Campo Romero +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +add_library(gfx) +add_subdirectory(src) +target_include_directories(gfx PUBLIC include) + +if(PS1_BUILD) + add_subdirectory(ps1) +elseif(SDL1_2_BUILD) + add_subdirectory(sdl-1.2) +endif() diff --git a/src/gfx/include/gfx/gfx.h b/src/gfx/include/gfx/gfx.h new file mode 100644 index 0000000..c9b4931 --- /dev/null +++ b/src/gfx/include/gfx/gfx.h @@ -0,0 +1,47 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef GFX_H +#define GFX_H + +#include <gfx/port.h> +#include <stdbool.h> + +int gfx_init(void); +int gfx_draw(void); +int gfx_update(void); +bool gfx_ready(void); +int gfx_toggle_fullscreen(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 gfx_rect_init(struct gfx_rect *r); +void semitrans_rect_init(struct gfx_rect *r); +void stp_4line_init(struct stp_4line *l); +void quad_sort(struct quad *q); +int gfx_rect_sort(struct gfx_rect *r); +void stp_4line_sort(struct stp_4line *l); +int quad_from_sprite(const struct gfx_sprite *s, struct quad *q); +bool gfx_inside_drawenv(short x, short y, short w, short h); +void gfx_deinit(void); + +extern int screen_w, screen_h; + +#endif /* GFX_H */ diff --git a/src/gfx/include/gfx/sprite.h b/src/gfx/include/gfx/sprite.h new file mode 100644 index 0000000..7b1bf8d --- /dev/null +++ b/src/gfx/include/gfx/sprite.h @@ -0,0 +1,32 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef GFX_SPRITE_H +#define GFX_SPRITE_H + +#include <gfx/port.h> +#include <stddef.h> + +typedef int (*gfx_read)(void *buf, size_t n, void *args); + +int gfx_sprite_load(struct gfx_sprite *s, gfx_read r, void *args); +int gfx_sprite_sort(struct gfx_sprite *s); +int gfx_sprite_clone(const struct gfx_sprite *src, struct gfx_sprite *dst); +void gfx_sprite_free(struct gfx_sprite *s); + +#endif diff --git a/src/gfx/ps1/CMakeLists.txt b/src/gfx/ps1/CMakeLists.txt new file mode 100644 index 0000000..f98ee50 --- /dev/null +++ b/src/gfx/ps1/CMakeLists.txt @@ -0,0 +1,40 @@ +# wnix, a Unix-like operating system for WebAssembly applications. +# Copyright (C) 2025 Xavier Del Campo Romero +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +add_subdirectory(src) +target_include_directories(gfx PUBLIC include PRIVATE private_include) +target_link_libraries(gfx PRIVATE + drv_ps1_event + drv_ps1_dma + drv_ps1_irq + drv_ps1_gpu +) + +set(modes VMODE_PAL VMODE_NTSC) + +if(VIDEO_MODE) + if(NOT "${VIDEO_MODE}" IN_LIST modes) + message(FATAL_ERROR "Invalid video mode ${VIDEO_MODE}. Available options:\n" + "${modes}\n" + "Run CMake again using one of the available video modes e.g.: cmake .. -DVIDEO_MODE=VMODE_PAL") + endif() + + target_compile_definitions(gfx PRIVATE VIDEO_MODE=${VIDEO_MODE}) +else() + message(FATAL_ERROR "Please define video mode. Available options:\n" + "${modes}\n" + "Run CMake again using one of the available video modes e.g.: cmake .. -DVIDEO_MODE=VMODE_PAL") +endif() diff --git a/src/gfx/ps1/include/gfx/port.h b/src/gfx/ps1/include/gfx/port.h new file mode 100644 index 0000000..faf9a50 --- /dev/null +++ b/src/gfx/ps1/include/gfx/port.h @@ -0,0 +1,126 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef GFX_PS1_H +#define GFX_PS1_H + +#include <stdint.h> + +union gfx_common +{ + struct + { + uint32_t tpagex :4, tpagey :1, stp :2, bpp :2, dither :1, + draw_to_disp :1, disable :1, xflip :1, yflip :1, :10; + uint8_t cmd; + } f; + + uint32_t mask; +}; + +union gfx_sznext +{ + struct + { + uint32_t next :24, sz :8; + } f; + + uint32_t cmd_next; +}; + +struct gfx_sprite +{ + union gfx_sznext sznext; + union gfx_common common; + uint8_t r, g, b; + uint8_t cmd; + int16_t x, y; + uint8_t u, v; + uint16_t clutid; + uint16_t w, h; +}; + +struct quad +{ + union gfx_sznext sznext; + uint8_t r, g, b; + uint8_t cmd; + int16_t x0, y0; + uint8_t u0, v0; + uint16_t clutid; + int16_t x1, y1; + uint8_t u1, v1; + + union + { + struct + { + /* 0-8 Same as GP0(E1h).Bit0-8. */ + uint16_t lb :9; + uint16_t :2; + /* 11 Same as GP0(E1h).Bit11. */ + uint16_t hb :1; + uint16_t :4; + } bit; + + uint16_t mask; + } tpage; + + int16_t x2, y2; + uint8_t u2, v2; + uint16_t :16; + int16_t x3, y3; + uint8_t u3, v3; + uint16_t :16; +}; + +struct gfx_rect +{ + union gfx_sznext sznext; + union gfx_common common; + uint8_t r, g, b; + uint8_t cmd; + int16_t x, y; + uint16_t w, h; +}; + +struct stp_4line +{ + union gfx_sznext sznext; + union gfx_common common; + uint8_t r, g, b; + uint8_t cmd; + int16_t x, y; + + struct stp_4line_vtx + { + uint32_t r :8, g :8, b :8, :8; + int16_t x, y; + } vertices[4]; + + uint32_t end; +}; + +void gfx_add_to_list(union gfx_sznext *p); +void *gfx_heap_top(void); +struct gfx_sprite *gfx_sprite_get(void); +struct quad *quad_get(void); +struct gfx_rect *gfx_rect_get(void); +struct stp_4line *stp_4line_get(void); + +#endif diff --git a/src/gfx/ps1/private_include/gfx/private.h b/src/gfx/ps1/private_include/gfx/private.h new file mode 100644 index 0000000..72d8f39 --- /dev/null +++ b/src/gfx/ps1/private_include/gfx/private.h @@ -0,0 +1,19 @@ +#ifndef GFX_PS1_PRIVATE_H +#define GFX_PS1_PRIVATE_H + +#include <gfx/gfx.h> + +enum +{ + SCREEN_W = 640, + SCREEN_H = 480 +}; + +void gfx_swapheap(void); +void gfx_initenvs(void); +void gfx_swapbuffers(void); +void gfx_swapenvs(void); + +extern int gfx_vblank_ev; + +#endif diff --git a/src/gfx/ps1/src/4line.c b/src/gfx/ps1/src/4line.c new file mode 100644 index 0000000..766eb2d --- /dev/null +++ b/src/gfx/ps1/src/4line.c @@ -0,0 +1,15 @@ +#include <gfx/gfx.h> +#include <gfx/private.h> +#include <psxgpu.h> +#include <stdint.h> +#include <string.h> + +void stp_4line_init(struct stp_4line *const l) +{ + memset(l, 0, sizeof *l); + l->sznext.f.sz = (sizeof *l - sizeof l->sznext) / sizeof (uint32_t); + l->common.f.cmd = DRAW_MODE; + l->cmd = 0x5A; + enum {TERMINATION_CODE = 0x55555555}; + l->end = TERMINATION_CODE; +} diff --git a/src/gfx/ps1/src/CMakeLists.txt b/src/gfx/ps1/src/CMakeLists.txt new file mode 100644 index 0000000..5b08b24 --- /dev/null +++ b/src/gfx/ps1/src/CMakeLists.txt @@ -0,0 +1,33 @@ +# wnix, a Unix-like operating system for WebAssembly applications. +# Copyright (C) 2025 Xavier Del Campo Romero +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +target_sources(gfx PRIVATE + # 4line.c + # env.c + draw.c + heap.c + init.c + ready.c + swapenvs.c + # quad.c + # rect.c + # sort.c + # sprite.c + vblank.c +) + +add_subdirectory(sprite) +add_subdirectory(rect) diff --git a/src/gfx/ps1/src/deinit.c b/src/gfx/ps1/src/deinit.c new file mode 100644 index 0000000..e5683f0 --- /dev/null +++ b/src/gfx/ps1/src/deinit.c @@ -0,0 +1,24 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <gfx/gfx.h> +#include <gfx/private.h> + +void gfx_deinit(void) +{ +} diff --git a/src/gfx/ps1/src/draw.c b/src/gfx/ps1/src/draw.c new file mode 100644 index 0000000..a935143 --- /dev/null +++ b/src/gfx/ps1/src/draw.c @@ -0,0 +1,71 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <gfx/gfx.h> +#include <gfx/private.h> +#include <drv/ps1/dma.h> +#include <drv/ps1/gpu.h> +#include <stddef.h> + +static union gfx_sznext *first, *last; + +void gfx_add_to_list(union gfx_sznext *const p) +{ + if (!first) + first = p; + else if (last) + last->f.next = (uint32_t)p; + + last = p; +} + +int gfx_draw(void) +{ + static union gfx_sznext term = {.cmd_next = 0xffffff}; + + gfx_add_to_list(&term); + + if (SCREEN_W != 640) + gfx_swapenvs(); + + gfx_swapheap(); + + GP1->mask = (const union drv_ps1_gpu_gp1) + { + .dma = + { + .cmd = GP1_DMA_DIR, + .dir = GP1_DMA_DIR_CPU_TO_GP0 + } + }.mask; + + D2_MADR->mask = (uint32_t)first; + D2_BCR->syncmode_2.reserved = 0; + D2_CHCR->mask = (const union chcr) + { + .bits = + { + .sync_mode = CHCR_SYNC_MODE_LINKED_LIST, + .dir = CHCR_DIR_FROM_RAM, + .start_busy = 1 + } + }.mask; + + first = NULL; + return 0; +} diff --git a/src/gfx/ps1/src/env.c b/src/gfx/ps1/src/env.c new file mode 100644 index 0000000..100f366 --- /dev/null +++ b/src/gfx/ps1/src/env.c @@ -0,0 +1,71 @@ +#include <gfx/gfx.h> +#include <gfx/private.h> +#include <psxgpu.h> +#include <errno.h> +#include <stdbool.h> + +static GsDispEnv dispenv; +enum {ENV_Y = SCREEN_H + 16}; + +static GsDrawEnv drawenv = +{ + .y = ENV_Y, + .w = SCREEN_W, + .h = SCREEN_H +}; + +int screen_w = SCREEN_W, screen_h = SCREEN_H; + +void gfx_swapbuffers(void) +{ + const short y = drawenv.y; + + drawenv.y = dispenv.y; + dispenv.y = y; + GsSetDrawEnv(&drawenv); + GsSetDispEnv(&dispenv); +} + +void gfx_initenvs(void) +{ + GsSetDrawEnv(&drawenv); + GsSetDispEnv(&dispenv); +} + +int gfx_toggle_fullscreen(void) +{ + errno = ENOTSUP; + return -1; +} + +int gfx_set_fullscreen(const short w, const short h) +{ + errno = ENOTSUP; + return -1; +} + +bool gfx_fullscreen_available(void) +{ + return false; +} + +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) +{ + return (x + w >= 0) + && x < drawenv.w + && (y + h >= 0) + && y < drawenv.h; +} diff --git a/src/gfx/ps1/src/heap.c b/src/gfx/ps1/src/heap.c new file mode 100644 index 0000000..402ee41 --- /dev/null +++ b/src/gfx/ps1/src/heap.c @@ -0,0 +1,74 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <gfx/gfx.h> +#include <gfx/private.h> +#include <stddef.h> + +static unsigned int sel; +static size_t heap_i; + +void gfx_swapheap(void) +{ + sel ^= 1; + heap_i = 0; +} + +static void *get_element(const size_t sz) +{ + enum + { + HEAP_SZ = sizeof (struct gfx_rect) + + (SCREEN_W / 10) * sizeof (struct gfx_sprite), + N_HEAPS = 2 + }; + + static char heaps[N_HEAPS][HEAP_SZ]; + const size_t new_sz = heap_i + sz; + void *ret = NULL; + + if (new_sz <= sizeof *heaps / sizeof **heaps) + { + ret = &heaps[sel][heap_i]; + heap_i = new_sz; + } + else + return NULL; + + return ret; +} + +struct gfx_sprite *gfx_sprite_get(void) +{ + return get_element(sizeof (struct gfx_sprite)); +} + +struct quad *quad_get(void) +{ + return get_element(sizeof (struct quad)); +} + +struct gfx_rect *gfx_rect_get(void) +{ + return get_element(sizeof (struct gfx_rect)); +} + +struct stp_4line *stp_4line_get(void) +{ + return get_element(sizeof (struct stp_4line)); +} diff --git a/src/gfx/ps1/src/init.c b/src/gfx/ps1/src/init.c new file mode 100644 index 0000000..a640202 --- /dev/null +++ b/src/gfx/ps1/src/init.c @@ -0,0 +1,199 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <gfx/gfx.h> +#include <gfx/private.h> +#include <drv/ps1/bios.h> +#include <drv/ps1/dma.h> +#include <drv/ps1/gpu.h> +#include <drv/ps1/irq.h> +#include <stddef.h> + +struct res +{ + int hres, hres2, vres, interlace; +}; + +static void getres(const short w, struct res *const r) +{ + *r = (const struct res){0}; + + switch (w) + { + case 320: + r->hres = 1; + break; + case 368: + r->hres2 = 1; + break; + case 512: + r->hres = 2; + break; + case 640: + r->hres = 3; + r->vres = 1; + r->interlace = 1; + break; + } +} + +volatile int vblank_set; + +static int irq(void) +{ + vblank_set = 1; + I_STAT->bits.vblank = 0; + return 0; +} + +int gfx_init(void) +{ + const int event = OpenEvent(CLASS_VBLANK, SPEC_INTERRUPTED, MODE_EXECUTE, + irq); + + if (event == -1) + return -1; + + I_MASK->bits.vblank = 1; + EnableEvent(gfx_vblank_ev = event); + + GP1->mask = (const union drv_ps1_gpu_gp1) + {.reset_gpu.cmd = GP1_RESET_GPU}.mask; + + GP0->mask = (const union drv_ps1_gpu_gp0) + { + .fill_vram = + { + .cmd = GP0_FILL_VRAM, + .w = 1024 / 16, + .h = 512 + } + }.mask; + + GP1->mask = (const union drv_ps1_gpu_gp1) + {.reset_cmdbuf.cmd = GP1_RESET_CMDBUF}.mask; + + DPCR->mask |= (const union dpcr){.bits.gpu_en = 1}.mask; + GP1->mask = (const union drv_ps1_gpu_gp1) + { + .dma = + { + .cmd = GP1_DMA_DIR, + .dir = GP1_DMA_DIR_CPU_TO_GP0 + } + }.mask; + + GP1->mask = (const union drv_ps1_gpu_gp1) + { + .display = + { + .cmd = GP1_DISPLAY_ENABLE, + } + }.mask; + + GP0->mask = (const union drv_ps1_gpu_gp0) + { + .draw_mode = + { + .cmd = GP0_DRAW_MODE, + .draw_to_display = 1 + } + }.mask; + + GP0->mask = (const union drv_ps1_gpu_gp0) + {.tex_window.cmd = GP0_TEX_WINDOW}.mask; + + GP0->mask = (const union drv_ps1_gpu_gp0) + {.draw_area_tl.cmd = GP0_DRAW_AREA_TOP_LEFT}.mask; + + GP0->mask = (const union drv_ps1_gpu_gp0) + { + .draw_area_br = + { + .cmd = GP0_DRAW_AREA_BOTTOM_RIGHT, + .x = SCREEN_W - 1, + .y = SCREEN_H - 1, + } + }.mask; + + GP1->mask = (const union drv_ps1_gpu_gp1) + { + .h_display_range = + { + .cmd = GP1_H_DISPLAY_RANGE, + .x1 = 0x260, + .x2 = 0x260 + 320 * 8 + } + }.mask; + + GP1->mask = (const union drv_ps1_gpu_gp1) + { + .disparea = + { + .cmd = GP1_START_DISPLAY_AREA + } + }.mask; + +#if 0 + GP1->mask = (const union drv_ps1_gpu_gp1) + { + .v_display_range = + { + .cmd = GP1_V_DISPLAY_RANGE, +#if VIDEO_MODE == VMODE_PAL + .y1 = 0xa3, + .y2 = 0xa3 + (SCREEN_H / 2) +#elif VIDEO_MODE == VMODE_NTSC + .y1 = 0x88 - (224 / 2), + .y2 = 0x88 + (224 / 2) +#endif + } + }.mask; +#else +#if 0 + /* TODO: use structs above. */ + GP1->mask = 0x06C4E24E; + GP1->mask = 0x07040010; +#else + /* values found from BIOS init state*/ + GP1->mask = 0x06c7e27e; + GP1->mask = 0x0704682b; +#endif +#endif + + struct res r; + + getres(SCREEN_W, &r); + + GP1->mask = (const union drv_ps1_gpu_gp1) + { + .display_mode = + { + .cmd = GP1_DISPLAY_MODE, + .hres = r.hres, + .hres2 = r.hres2, + .vres = r.vres, + .vinterlace = r.interlace, +#if VIDEO_MODE == VMODE_PAL + .vmode = 1 +#endif + } + }.mask; + + return 0; +} diff --git a/src/gfx/ps1/src/quad.c b/src/gfx/ps1/src/quad.c new file mode 100644 index 0000000..bfabee2 --- /dev/null +++ b/src/gfx/ps1/src/quad.c @@ -0,0 +1,24 @@ +#include <gfx/gfx.h> +#include <gfx/private.h> +#include <gfx/port.h> +#include <stdint.h> +#include <string.h> + +static void quad_init(struct quad *const q) +{ + memset(q, 0, sizeof *q); + q->sznext.f.sz = (sizeof *q - sizeof q->sznext) / sizeof (uint32_t); + q->cmd = 0x2d; +} + +int quad_from_sprite(const struct gfx_sprite *const s, struct quad *const q) +{ + quad_init(q); + q->tpage.mask = s->common.mask; + q->clutid = s->clutid; + q->u0 = q->u2 = s->u; + q->v0 = q->v1 = s->v; + q->u1 = q->u3 = s->u + s->w - 1; + q->v2 = q->v3 = s->v + s->h - 1; + return 0; +} diff --git a/src/gfx/ps1/src/ready.c b/src/gfx/ps1/src/ready.c new file mode 100644 index 0000000..bcb5b29 --- /dev/null +++ b/src/gfx/ps1/src/ready.c @@ -0,0 +1,38 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <gfx/gfx.h> +#include <gfx/private.h> +#include <drv/ps1/event.h> +#include <drv/ps1/gpu.h> +#include <stdbool.h> + +bool gfx_ready(void) +{ + extern volatile int vblank_set; + + if (GPUSTAT->bits.ready_cmd + && GPUSTAT->bits.ready_dma + && vblank_set) + { + vblank_set = 0; + return true; + } + + return false; +} diff --git a/src/gfx/ps1/src/rect/CMakeLists.txt b/src/gfx/ps1/src/rect/CMakeLists.txt new file mode 100644 index 0000000..a14378e --- /dev/null +++ b/src/gfx/ps1/src/rect/CMakeLists.txt @@ -0,0 +1,20 @@ +# wnix, a Unix-like operating system for WebAssembly applications. +# Copyright (C) 2025 Xavier Del Campo Romero +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +target_sources(gfx PRIVATE + init.c + sort.c +) diff --git a/src/gfx/ps1/src/rect/init.c b/src/gfx/ps1/src/rect/init.c new file mode 100644 index 0000000..5fe0a85 --- /dev/null +++ b/src/gfx/ps1/src/rect/init.c @@ -0,0 +1,41 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <gfx/gfx.h> +#include <gfx/private.h> +#include <drv/ps1/gpu.h> + +static void common_init(struct gfx_rect *const r) +{ + *r = (const struct gfx_rect){0}; + r->sznext.f.sz = (sizeof *r - sizeof r->sznext) / sizeof (uint32_t); + r->common.f.cmd = GP0_DRAW_MODE; + r->common.f.draw_to_disp = 1; +} + +void gfx_rect_init(struct gfx_rect *const r) +{ + common_init(r); + r->cmd = 0x60; +} + +void gfx_semitrans_rect_init(struct gfx_rect *const r) +{ + common_init(r); + r->cmd = 0x62; +} diff --git a/src/gfx/ps1/src/rect/sort.c b/src/gfx/ps1/src/rect/sort.c new file mode 100644 index 0000000..b7f5ebc --- /dev/null +++ b/src/gfx/ps1/src/rect/sort.c @@ -0,0 +1,27 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <gfx/gfx.h> +#include <gfx/private.h> +#include <drv/ps1/gpu.h> + +int gfx_rect_sort(struct gfx_rect *const r) +{ + gfx_add_to_list(&r->sznext); + return 0; +} diff --git a/src/gfx/ps1/src/sprite/CMakeLists.txt b/src/gfx/ps1/src/sprite/CMakeLists.txt new file mode 100644 index 0000000..32e1b9f --- /dev/null +++ b/src/gfx/ps1/src/sprite/CMakeLists.txt @@ -0,0 +1,22 @@ +# wnix, a Unix-like operating system for WebAssembly applications. +# Copyright (C) 2025 Xavier Del Campo Romero +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +target_sources(gfx PRIVATE + clone.c + free.c + load.c + sort.c +) diff --git a/src/gfx/ps1/src/sprite/clone.c b/src/gfx/ps1/src/sprite/clone.c new file mode 100644 index 0000000..45bc802 --- /dev/null +++ b/src/gfx/ps1/src/sprite/clone.c @@ -0,0 +1,26 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <gfx/gfx.h> + +int gfx_sprite_clone(const struct gfx_sprite *const src, + struct gfx_sprite *const dst) +{ + *dst = *src; + return 0; +} diff --git a/src/gfx/ps1/src/sprite/free.c b/src/gfx/ps1/src/sprite/free.c new file mode 100644 index 0000000..922c1ff --- /dev/null +++ b/src/gfx/ps1/src/sprite/free.c @@ -0,0 +1,23 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <gfx/gfx.h> + +void sprite_free(struct gfx_sprite *const src) +{ +} diff --git a/src/gfx/ps1/src/sprite/load.c b/src/gfx/ps1/src/sprite/load.c new file mode 100644 index 0000000..2dd12ae --- /dev/null +++ b/src/gfx/ps1/src/sprite/load.c @@ -0,0 +1,192 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <gfx/gfx.h> +#include <gfx/sprite.h> +#include <gfx/private.h> +#include <drv/ps1/gpu.h> +#include <stdint.h> + +static void init(struct gfx_sprite *const s) +{ + *s = (const struct gfx_sprite){0}; + s->sznext.f.sz = (sizeof *s - sizeof s->sznext) / sizeof (uint32_t); + s->common.f.cmd = GP0_DRAW_MODE; + s->common.f.draw_to_disp = 1; + s->cmd = GP0_TEXTRECT_VARSZ_OPAQ_RAW; +} + +struct tim_pos +{ + uint16_t x, y, w, h; +}; + +static void transfer_init(const struct tim_pos *const p) +{ + while (!GPUSTAT->bits.ready_cmd) + ; + + GP0->mask = (const union drv_ps1_gpu_gp0) + {.copy_rect.cmd = GP0_COPY_RECT_CPU_VRAM}.mask; + GP0->mask = (const union drv_ps1_gpu_gp0) + {.coord = {.x = p->x, .y = p->y}}.mask; + GP0->mask = (const union drv_ps1_gpu_gp0) + {.size = {.w = p->w, .h = p->h}}.mask; +} + +static int transfer(const size_t sz, const gfx_read r, void *const args) +{ + const size_t rem = sz % sizeof (uint32_t); + + if (sz >= sizeof (uint32_t)) + { + for (size_t i = 0; i < sz / sizeof (uint32_t); i++) + { + uint32_t pix; + + if (r(&pix, sizeof pix, args)) + return -1; + + GP0->mask = pix; + } + } + + if (rem) + { + uint32_t pix = 0; + + if (r(&pix, rem, args)) + return -1; + + GP0->mask = pix; + } + + return 0; +} + +struct header +{ + uint32_t sz; + struct tim_pos pos; +}; + +static short get_clutid(const short x, const short y) +{ + return (x & 0x3ff) >> 4 | (y & 0x1ff) << 6; +} + +static int upload_clut(struct gfx_sprite *const s, const gfx_read r, + void *const args) +{ + struct header clut; + + if (r(&clut, sizeof clut, args)) + return -1; + + transfer_init(&clut.pos); + + const size_t sz = clut.sz - sizeof clut; + + if (transfer(sz, r, args)) + return -1; + + s->clutid = get_clutid(clut.pos.x, clut.pos.y); + return 0; +} + +enum bpp +{ + BPP_4 = 0, + BPP_8 = 1, + BPP_16 = 2, + BPP_24 = 4 +}; + +static int upload_img(struct gfx_sprite *const s, const enum bpp bpp, + const gfx_read r, void *const args) +{ + struct header img; + + if (r(&img, sizeof img, args)) + return -1; + + transfer_init(&img.pos); + + const size_t sz = img.sz - sizeof img; + + if (transfer(sz, r, args)) + return -1; + + enum + { + VRAM_X = 1024, + VRAM_Y = 512, + TPAGE_WIDTH = 64 + }; + + s->common.f.tpagex = img.pos.x / TPAGE_WIDTH; + s->common.f.tpagey = img.pos.y / (VRAM_Y / 2); + s->u = img.pos.x % TPAGE_WIDTH; + s->v = img.pos.y % (VRAM_Y / 2); + + switch (bpp) + { + case BPP_4: + s->w = img.pos.w * 4; + s->u <<= 2; + break; + + case BPP_8: + s->w = img.pos.w * 2; + s->u <<= 1; + break; + + case BPP_16: + s->w = img.pos.w; + break; + + case BPP_24: + s->w = img.pos.w + (img.pos.w / 2); + break; + } + + s->h = img.pos.h; + return 0; +} + +int gfx_sprite_load(struct gfx_sprite *s, const gfx_read r, void *const args) +{ + init(s); + + struct + { + uint32_t version, bpp :3, has_clut :1, :28; + } h; + + enum {VERSION_ID = 0x10}; + + if (r(&h, sizeof h, args) + || h.version != VERSION_ID + || h.bpp == BPP_24 + || (h.has_clut && upload_clut(s, r, args)) + || upload_img(s, h.bpp, r, args)) + return -1; + + s->common.f.bpp = h.bpp ? __builtin_ctz(h.bpp) + 1 : 0; + return 0; +} diff --git a/src/gfx/ps1/src/sprite/sort.c b/src/gfx/ps1/src/sprite/sort.c new file mode 100644 index 0000000..3a4b020 --- /dev/null +++ b/src/gfx/ps1/src/sprite/sort.c @@ -0,0 +1,26 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <gfx/gfx.h> +#include <gfx/port.h> + +int gfx_sprite_sort(struct gfx_sprite *const s) +{ + gfx_add_to_list(&s->sznext); + return 0; +} diff --git a/src/gfx/ps1/src/swapenvs.c b/src/gfx/ps1/src/swapenvs.c new file mode 100644 index 0000000..0b08ee2 --- /dev/null +++ b/src/gfx/ps1/src/swapenvs.c @@ -0,0 +1,58 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <gfx/gfx.h> +#include <gfx/private.h> +#include <drv/ps1/gpu.h> + +void gfx_swapenvs(void) +{ + static short dispenv, drawenv = 256; + const short y = dispenv; + + dispenv = drawenv; + drawenv = y; + + GP0->mask = (const union drv_ps1_gpu_gp0) + { + .draw_area_tl = + { + .cmd = GP0_DRAW_AREA_TOP_LEFT, + .y = drawenv + } + }.mask; + + GP0->mask = (const union drv_ps1_gpu_gp0) + { + .draw_area_br = + { + .cmd = GP0_DRAW_AREA_BOTTOM_RIGHT, + .x = SCREEN_W - 1, + .y = drawenv + SCREEN_H - 1, + } + }.mask; + + GP1->mask = (const union drv_ps1_gpu_gp1) + { + .disparea = + { + .cmd = GP1_START_DISPLAY_AREA, + .y = dispenv + } + }.mask; +} diff --git a/src/gfx/ps1/src/vblank.c b/src/gfx/ps1/src/vblank.c new file mode 100644 index 0000000..eb47597 --- /dev/null +++ b/src/gfx/ps1/src/vblank.c @@ -0,0 +1,21 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <gfx/private.h> + +int gfx_vblank_ev; diff --git a/src/gfx/sdl-1.2/CMakeLists.txt b/src/gfx/sdl-1.2/CMakeLists.txt new file mode 100644 index 0000000..cd37d06 --- /dev/null +++ b/src/gfx/sdl-1.2/CMakeLists.txt @@ -0,0 +1,19 @@ +# wnix, a Unix-like operating system for WebAssembly applications. +# Copyright (C) 2025 Xavier Del Campo Romero +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +add_subdirectory(src) +target_include_directories(gfx PUBLIC include PRIVATE private_include) +target_link_libraries(gfx PUBLIC SDL::SDL PRIVATE SDL::SDL_gfx) diff --git a/src/gfx/sdl-1.2/include/gfx/port.h b/src/gfx/sdl-1.2/include/gfx/port.h new file mode 100644 index 0000000..bf94a38 --- /dev/null +++ b/src/gfx/sdl-1.2/include/gfx/port.h @@ -0,0 +1,58 @@ +#ifndef GFX_SDL_H +#define GFX_SDL_H + +#include <SDL.h> +#include <stdbool.h> + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct gfx_sprite +{ + SDL_Surface *s, *s_x; + short x, y, w, h; + unsigned char u, v; + bool transparent; +}; + +struct quad +{ + unsigned char r, g, b; + short x0, x1, x2, x3; + short y0, y1, y2, y3; + unsigned char u0, u1, u2, u3; + unsigned char v0, v1, v2, v3; + short w, h; + bool transparent; + SDL_Surface *s, *s_x; +}; + +struct gfx_rect +{ + unsigned char r, g, b; + short x, y, w, h; + bool stp; +}; + +struct stp_4line +{ + short x, y; + unsigned char r, g, b; + + struct stp_4line_vtx + { + unsigned char r, g, b; + short x, y; + } vertices[4]; +}; + +#define common_get_or_ret(t, x, ret) \ + struct t x##__, *const x = &x##__ + +#ifdef __cplusplus +} +#endif + +#endif /* GFX_SDL_H */ diff --git a/src/gfx/sdl-1.2/private_include/gfx/private.h b/src/gfx/sdl-1.2/private_include/gfx/private.h new file mode 100644 index 0000000..e1af764 --- /dev/null +++ b/src/gfx/sdl-1.2/private_include/gfx/private.h @@ -0,0 +1,26 @@ +#ifndef GFX_SDL_12_PRIVATE_H +#define GFX_SDL_12_PRIVATE_H + +#include <gfx/gfx.h> +#include <SDL.h> + +#ifdef __cplusplus +extern "C" +{ +#endif + +enum +{ + SCREEN_W = 320, + SCREEN_H = 240 +}; + +int sprite_screen_resize_ev(struct gfx_sprite *s); +void gfx_register_sprite(struct gfx_sprite *s); +SDL_Surface *gfx_screen(void); + +#ifdef __cplusplus +} +#endif + +#endif /* GFX_SDL_12_PRIVATE_H */ diff --git a/src/gfx/sdl-1.2/src/CMakeLists.txt b/src/gfx/sdl-1.2/src/CMakeLists.txt new file mode 100644 index 0000000..75ccb78 --- /dev/null +++ b/src/gfx/sdl-1.2/src/CMakeLists.txt @@ -0,0 +1,23 @@ +# wnix, a Unix-like operating system for WebAssembly applications. +# Copyright (C) 2025 Xavier Del Campo Romero +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +target_sources(gfx PRIVATE + env.c + line.c + quad.c + rect.c + sprite.c +) diff --git a/src/gfx/sdl-1.2/src/env.c b/src/gfx/sdl-1.2/src/env.c new file mode 100644 index 0000000..2a51b97 --- /dev/null +++ b/src/gfx/sdl-1.2/src/env.c @@ -0,0 +1,204 @@ +#include <gfx/gfx.h> +#include <gfx/private.h> +#include <sdl-1.2/gfx/private.h> +#include <SDL.h> +#include <inttypes.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> + +int screen_w = SCREEN_W, screen_h = SCREEN_H; +static bool fullscreen; +static SDL_Surface *screen; +static struct gfx_sprite **list; +static size_t list_len; + +void gfx_deinit(void) +{ + /* screen should be already freed by SDL_Quit. */ + free(list); + SDL_QuitSubSystem(SDL_INIT_VIDEO); +} + +static struct +{ + int w, h; +} display, windowed; + +void gfx_register_sprite(struct gfx_sprite *const s) +{ + list = realloc(list, (list_len + 1) * sizeof *list); + + if (list) + list[list_len++] = s; +} + +static int resize_screen(int w, int h, const bool full_screen) +{ + Uint32 flags = SDL_HWSURFACE | SDL_RESIZABLE | SDL_ANYFORMAT | SDL_DOUBLEBUF; + + const SDL_VideoInfo *const info = SDL_GetVideoInfo(); + + if (!info) + { + fprintf(stderr, "SDL_GetVideoInfo: %s\n", SDL_GetError()); + return -1; + } + + static bool init; + + if (!init) + { + display.w = info->current_w; + display.h = info->current_h; + init = true; + } + + if (fullscreen) + { + flags |= SDL_FULLSCREEN; + w = display.w; + h = display.h; + } + else + { + windowed.w = w; + windowed.h = h; + } + + int bpp = info->vfmt->BitsPerPixel; + + if (screen) + SDL_FreeSurface(screen); + + const int max_bpp = SDL_VideoModeOK(w, h, 0, flags); + + if (max_bpp < 0) + { + fprintf(stderr, "SDL_VideoModeOK: %s\n", SDL_GetError()); + return -1; + } + else if (bpp > max_bpp) + bpp = max_bpp; + + if (!(screen = SDL_SetVideoMode(w, h, 0, flags))) + { + fprintf(stderr, "SDL_SetVideoMode: %s\n", SDL_GetError()); + return -1; + } + + for (size_t i = 0; i < list_len; i++) + if (sprite_screen_resize_ev(list[i])) + return -1; + + screen_w = w; + screen_h = h; + fullscreen = full_screen; + return 0; +} + +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)) + { + fprintf(stderr, "%s: SDL_InitSubSystem: %s\n", + __func__, SDL_GetError()); + return -1; + } + + return resize_screen(screen_w, screen_h, fullscreen); +} + +bool gfx_inside_drawenv(const short x, const short y, const short w, + const short h) +{ + return (x + w >= 0) + && x < screen_w + && (y + h >= 0) + && y < screen_h; +} + +static int get_resize_events(void) +{ + int n; + SDL_Event ev; + + while ((n = SDL_PeepEvents(&ev, 1, SDL_GETEVENT, + SDL_VIDEORESIZEMASK)) > 0) + { + if (ev.type == SDL_VIDEORESIZE) + { + const SDL_ResizeEvent *const res = &ev.resize; + + if (resize_screen(res->w, res->h, false)) + return -1; + } + } + + if (n < 0) + { + fprintf(stderr, "%s: SDL_PeepEvents: %s\n", + __func__, SDL_GetError()); + return -1; + } + + return 0; +} + +int gfx_toggle_fullscreen(void) +{ + fullscreen ^= true; + + const int w = fullscreen ? display.w : windowed.w; + const int h = fullscreen ? display.h : windowed.h; + + return resize_screen(w, h, fullscreen); +} + +int gfx_set_fullscreen(const short w, const short h) +{ + return resize_screen(display.w = w, display.h = h, fullscreen = true); +} + +bool gfx_fullscreen_available(void) +{ + return true; +} + +bool gfx_fullscreen(void) +{ + return fullscreen; +} + +int gfx_draw(void) +{ + enum {FPS = 50, REFRESH_MS = 1000 / FPS}; + static Uint32 prev; + const Uint32 cur = SDL_GetTicks(); + + if (cur - prev < REFRESH_MS) + SDL_Delay(REFRESH_MS - (cur - prev)); + + prev = SDL_GetTicks(); + + if (SDL_Flip(screen)) + { + fprintf(stderr, "SDL_Flip: %s\n", SDL_GetError()); + return -1; + } + + get_resize_events(); + return 0; +} diff --git a/src/gfx/sdl-1.2/src/line.c b/src/gfx/sdl-1.2/src/line.c new file mode 100644 index 0000000..b06a414 --- /dev/null +++ b/src/gfx/sdl-1.2/src/line.c @@ -0,0 +1,16 @@ +#include <gfx/gfx.h> +#include <gfx/port.h> +#include <sdl-1.2/gfx/private.h> +#include <stddef.h> + +void stp_4line_init(struct stp_4line *const l) +{ +} + +void semitrans_stp_4line_init(struct stp_4line *r) +{ +} + +void stp_4line_sort(struct stp_4line *const r) +{ +} diff --git a/src/gfx/sdl-1.2/src/quad.c b/src/gfx/sdl-1.2/src/quad.c new file mode 100644 index 0000000..1617a8b --- /dev/null +++ b/src/gfx/sdl-1.2/src/quad.c @@ -0,0 +1,44 @@ +#include <gfx/gfx.h> +#include <gfx/port.h> +#include <sdl-1.2/gfx/private.h> +#include <stddef.h> +#include <stdlib.h> + +int quad_from_sprite(const struct gfx_sprite *const s, struct quad *const q) +{ + q->s = s->s; + q->s_x = s->s_x; + q->u0 = q->u2 = s->u; + q->v0 = q->v1 = s->v; + q->u1 = q->u3 = s->u + s->w - 1; + q->v2 = q->v3 = s->v + s->h - 1; + q->w = s->w; + q->h = s->h; + return 0; +} + +void quad_sort(struct quad *const q) +{ + const bool xflip = q->x0 > q->x1; + + SDL_Rect r = + { + .x = xflip ? q->x1 : q->x0, + .y = q->y0 + }; + + const short w = q->u1 - q->u0 + 1, h = q->v2 - q->v0 + 1; + + SDL_Rect clip = + { + .x = xflip ? q->w - q->u0 - w: q->u0, + .y = q->v0, + .w = w, + .h = h + }; + + SDL_Surface *const s = xflip ? q->s_x : q->s; + + if (SDL_BlitSurface(s, &clip, gfx_screen(), &r)) + fprintf(stderr, "SDL_BlitSurface: %s\n", SDL_GetError()); +} diff --git a/src/gfx/sdl-1.2/src/rect.c b/src/gfx/sdl-1.2/src/rect.c new file mode 100644 index 0000000..57a2ab3 --- /dev/null +++ b/src/gfx/sdl-1.2/src/rect.c @@ -0,0 +1,35 @@ +#include <gfx/gfx.h> +#include <gfx/port.h> +#include <sdl-1.2/gfx/private.h> +#include <SDL.h> + +void gfx_rect_sort(struct gfx_rect *const r) +{ + SDL_Rect rct = + { + .x = r->x, + .y = r->y, + .w = r->w, + .h = r->h + }; + + SDL_Surface *const screen = gfx_screen(); + const Uint32 map = SDL_MapRGB(screen->format, r->r, r->g, r->b); + + if (SDL_FillRect(screen, &rct, map)) + { + fprintf(stderr, "SDL_FillRect: %s\n", SDL_GetError()); + return; + } +} + +void gfx_rect_init(struct gfx_rect *const r) +{ + *r = (const struct gfx_rect){0}; +} + +void semitrans_rect_init(struct gfx_rect *const r) +{ + gfx_rect_init(r); + r->stp = true; +} diff --git a/src/gfx/sdl-1.2/src/sprite.c b/src/gfx/sdl-1.2/src/sprite.c new file mode 100644 index 0000000..086e2b5 --- /dev/null +++ b/src/gfx/sdl-1.2/src/sprite.c @@ -0,0 +1,143 @@ +#include <gfx/gfx.h> +#include <gfx/port.h> +#include <header.h> +#include <sdl-1.2/gfx/private.h> +#include <SDL.h> +#include <SDL_rotozoom.h> +#include <errno.h> +#include <stddef.h> +#include <stdlib.h> + +void sprite_free(struct gfx_sprite *const s) +{ + if (s && s->s) + SDL_FreeSurface(s->s); +} + +int sprite_clone(const struct gfx_sprite *const src, struct gfx_sprite *const dst) +{ + *dst = *src; + return 0; +} + +static int set_transparent(SDL_Surface *const s) +{ + /* Magenta as transparent. */ + const Uint32 map = SDL_MapRGB(s->format, 255, 0, 255); + const int ret = SDL_SetColorKey(s, SDL_SRCCOLORKEY | SDL_RLEACCEL, map); + + if (ret) + fprintf(stderr, "SDL_SetColorKey: %s\n", SDL_GetError()); + + return ret; +} + +int sprite_screen_resize_ev(struct gfx_sprite *const s) +{ + int ret = -1; + SDL_Surface *const old = s->s, *const old_x = s->s_x; + + if (s->transparent && (set_transparent(old) || set_transparent(old_x))) + goto end; + else if (!(s->s = SDL_DisplayFormat(old))) + { + fprintf(stderr, "SDL_DisplayFormat: %s\n", SDL_GetError()); + goto end; + } + else if (!(s->s_x = SDL_DisplayFormat(old_x))) + { + fprintf(stderr, "SDL_DisplayFormat: %s\n", SDL_GetError()); + goto end; + } + + ret = 0; + +end: + SDL_FreeSurface(old); + SDL_FreeSurface(old_x); + return ret; +} + +static int load_header(struct gfx_sprite *const s, FILE *const f) +{ + if (header_load_bool(f, "transparent", &s->transparent)) + return -1; + + return 0; +} + +static int load_bitmap(struct gfx_sprite *const s, FILE *const f) +{ + int ret = -1; + SDL_RWops *ops = NULL; + SDL_Surface *ts = NULL, *zs = NULL; + + if (!(ops = SDL_RWFromFP(f, 0))) + { + fprintf(stderr, "SDL_RWFromFP: %s\n", SDL_GetError()); + goto end; + } + else if (!(ts = SDL_LoadBMP_RW(ops, 0))) + { + fprintf(stderr, "SDL_LoadBMP_RW: %s\n", SDL_GetError()); + goto end; + } + else if (!(zs = zoomSurface(ts, -1, 1, 0))) + { + fprintf(stderr, "zoomSurface: %s\n", SDL_GetError()); + goto end; + } + else if (s->transparent && (set_transparent(ts) || set_transparent(zs))) + goto end; + else if (!(s->s = SDL_DisplayFormat(ts))) + { + fprintf(stderr, "SDL_DisplayFormat: %s\n", SDL_GetError()); + goto end; + } + else if (!(s->s_x = SDL_DisplayFormat(zs))) + { + fprintf(stderr, "SDL_DisplayFormat: %s\n", SDL_GetError()); + goto end; + } + + gfx_register_sprite(s); + s->w = ts->w; + s->h = ts->h; + ret = 0; + +end: + SDL_FreeRW(ops); + SDL_FreeSurface(ts); + SDL_FreeSurface(zs); + return ret; +} + +int sprite_from_fp(struct gfx_sprite *const s, FILE *const f) +{ + *s = (const struct gfx_sprite){0}; + + if (load_header(s, f) || load_bitmap(s, f)) + return -1; + + return 0; +} + +void sprite_sort(struct gfx_sprite *const s) +{ + SDL_Rect r = + { + .x = s->x, + .y = s->y + }; + + SDL_Rect clip = + { + .x = s->u, + .y = s->v, + .w = s->w, + .h = s->h + }; + + if (SDL_BlitSurface(s->s, &clip, gfx_screen(), &r)) + fprintf(stderr, "SDL_BlitSurface: %s\n", SDL_GetError()); +} diff --git a/src/gfx/src/CMakeLists.txt b/src/gfx/src/CMakeLists.txt new file mode 100644 index 0000000..b62fca4 --- /dev/null +++ b/src/gfx/src/CMakeLists.txt @@ -0,0 +1,20 @@ +# wnix, a Unix-like operating system for WebAssembly applications. +# Copyright (C) 2025 Xavier Del Campo Romero +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +target_sources(gfx PRIVATE + dim.c + update.c +) diff --git a/src/gfx/src/dim.c b/src/gfx/src/dim.c new file mode 100644 index 0000000..5413469 --- /dev/null +++ b/src/gfx/src/dim.c @@ -0,0 +1,21 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <gfx/private.h> + +int screen_w = SCREEN_W, screen_h = SCREEN_H; diff --git a/src/gfx/src/update.c b/src/gfx/src/update.c new file mode 100644 index 0000000..95980f9 --- /dev/null +++ b/src/gfx/src/update.c @@ -0,0 +1,27 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <gfx/gfx.h> + +int gfx_update(void) +{ + if (!gfx_ready()) + return 0; + + return gfx_draw(); +} |
