flan/vendor/raylib/shim.c
Joseph Ferano 07d068d297 A struct that comes back unchanged proves nothing
Texture2D and Rectangle are the two structs the texture calls need, and they
are the ones whose layout can be silently wrong: five 4-byte fields in a row,
and four floats in a row, so a permutation still reads as plausible numbers
everywhere.

The obvious test — hand raylib a struct, read it back, compare — is worthless
here, and I only found that out by trying it. Storing and returning is
symmetric: swap two fields in the Flan defstruct and the round trip still
agrees with itself, because C writes and reads the same wrong slots. That test
passes whatever the layout is, which is the kind of test this project would
rather not have at all.

So the headless case uses the two things raylib computes from the fields
without a GPU. GetCollisionRec turns (0,0,10,4) and (6,1,10,10) into
(6,1,4,3), four different numbers each derived from a different pair of
fields, and no permutation of Rectangle survives it. SetShapesTexture keeps a
Texture2D without touching GL and substitutes 1 1 1 1 7 when the id is zero,
so a zero id pins the first field, the 7 pins the last, and a zero width
stored rather than substituted is what stops that pair from passing with id
and width swapped. Each of those was checked by permuting the defstruct and
watching the case fail.

What is left unpinned is width, height and mipmaps against each other; nothing
raylib does without a GL context reads them. That is stated in the program
rather than papered over, because the alternative is a case that looks like it
covers them.

set-shapes-texture, get-shapes-texture, get-shapes-texture-rectangle and
get-collision-rec are real bindings, not test scaffolding — they are bound
here because they are also the only pure consumers of these two structs.
2026-09-11 17:52:04 +07:00

105 lines
4.3 KiB
C

/* The C half of the raylib binding: one wrapper per `declare` in raylib.flan.
*
* The wrappers exist so that no aggregate is ever passed or returned across
* the Flan/C boundary. raylib takes Color by value and returns Vector2 by
* value, and a small aggregate is passed differently on x86-64 (<2 x float>,
* i32), on arm64, and on wasm32. Here, clang classifies each one correctly for
* whichever target the build is for; in the Flan backend it would be three
* conventions to reimplement. Everything below therefore trades in scalars and
* pointers only — see raylib.flan.
*
* raylib's own headers are not needed and not used: these prototypes are the
* declarations, so the build has no dependency on raylib-devel being
* installed, only on the shared library being linkable.
*/
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
typedef struct { float x, y; } Vector2;
typedef struct { unsigned char r, g, b, a; } Color;
typedef struct { unsigned int id; int width, height, mipmaps, format; } Texture2D;
typedef struct { float x, y, width, height; } Rectangle;
extern void InitWindow(int width, int height, const char *title);
extern void CloseWindow(void);
extern bool WindowShouldClose(void);
extern void SetTargetFPS(int fps);
extern void SetTraceLogLevel(int level);
extern bool IsKeyPressed(int key);
extern bool IsKeyDown(int key);
extern bool IsKeyReleased(int key);
extern bool IsMouseButtonPressed(int button);
extern bool IsMouseButtonDown(int button);
extern bool IsMouseButtonReleased(int button);
extern Vector2 GetMousePosition(void);
extern Color GetColor(unsigned int hex);
extern void BeginDrawing(void);
extern void EndDrawing(void);
extern void DrawFPS(int x, int y);
extern void ClearBackground(Color color);
extern void DrawRectangle(int x, int y, int width, int height, Color color);
extern void SetShapesTexture(Texture2D texture, Rectangle source);
extern Texture2D GetShapesTexture(void);
extern Rectangle GetShapesTextureRectangle(void);
extern Rectangle GetCollisionRec(Rectangle a, Rectangle b);
/* A Flan string arrives as ptr+len and is not NUL-terminated, so a C API that
* wants a C string needs a copy. The window title is the only one, it is short
* by nature, and truncating is better than reading past the end. */
static const char *cstr(const char *p, long long n, char *buf, size_t cap) {
size_t k = (size_t)n < cap - 1 ? (size_t)n : cap - 1;
memcpy(buf, p, k);
buf[k] = '\0';
return buf;
}
void flan_rl_init_window(int width, int height, const char *title, long long n) {
char buf[256];
InitWindow(width, height, cstr(title, n, buf, sizeof buf));
}
void flan_rl_close_window(void) { CloseWindow(); }
bool flan_rl_window_should_close(void) { return WindowShouldClose(); }
void flan_rl_set_target_fps(int fps) { SetTargetFPS(fps); }
void flan_rl_set_trace_log_level(int l) { SetTraceLogLevel(l); }
bool flan_rl_is_key_pressed(int key) { return IsKeyPressed(key); }
bool flan_rl_is_key_down(int key) { return IsKeyDown(key); }
bool flan_rl_is_key_released(int key) { return IsKeyReleased(key); }
bool flan_rl_is_mouse_button_pressed(int b) { return IsMouseButtonPressed(b); }
bool flan_rl_is_mouse_button_down(int b) { return IsMouseButtonDown(b); }
bool flan_rl_is_mouse_button_released(int b) { return IsMouseButtonReleased(b); }
void flan_rl_get_mouse_position(Vector2 *out) { *out = GetMousePosition(); }
void flan_rl_get_color(unsigned int hex, Color *out) { *out = GetColor(hex); }
void flan_rl_begin_drawing(void) { BeginDrawing(); }
void flan_rl_end_drawing(void) { EndDrawing(); }
void flan_rl_draw_fps(int x, int y) { DrawFPS(x, y); }
void flan_rl_clear_background(const Color *color) { ClearBackground(*color); }
void flan_rl_draw_rectangle(int x, int y, int width, int height,
const Color *color) {
DrawRectangle(x, y, width, height, *color);
}
void flan_rl_set_shapes_texture(const Texture2D *texture, const Rectangle *source) {
SetShapesTexture(*texture, *source);
}
void flan_rl_get_shapes_texture(Texture2D *out) { *out = GetShapesTexture(); }
void flan_rl_get_shapes_texture_rectangle(Rectangle *out) {
*out = GetShapesTextureRectangle();
}
void flan_rl_get_collision_rec(const Rectangle *a, const Rectangle *b,
Rectangle *out) {
*out = GetCollisionRec(*a, *b);
}