flan/vendor/raylib/shim.c
Joseph Ferano a90badbcd5 A camera nothing looked through was a camera nothing tested
begin-mode-2d and end-mode-2d have been bound since Camera2D went in and
called by nothing, which is the same as not having bound them. The grid now
draws through a camera the arrow keys pan and comma and period zoom, and
paint has to undo that transform with get-screen-to-world-2d — so a camera
plumbed in wrongly is visible at once as grains landing somewhere other than
the cursor, rather than as nothing at all.

The shapes, the text and the timing come with it, and none of them can be
asserted: every one needs a GL context, and measure-text needs init-window
too — the default font is loaded there and nowhere else, so headless it
answers 0 for every string. Measured against libraylib.so.550, not assumed,
which is why it is absent from the acceptance table despite looking exactly
like a call that belongs in it.

So the HUD is built to be looked at instead: each shape binding appears once
and each is asymmetric enough that crossed arguments show. The ellipse is
wider than it is tall, the ring's sweep comes from get-time, the triangle has
its counter-clockwise winding with an outline over it as a control, and the
panel is sized by measure-text rather than by a guess.

draw-rectangle-rounded-lines takes no thickness in raylib 5.5 — it moved to
the -ex form, and both are here. The 5.1 header on this machine still shows
the five-argument version; nm -D on the library is what settled it.

Font loading stays unbound and says so: a Font carries a Texture2D, a
Rectangle* and a GlyphInfo*, and a GlyphInfo carries an Image.
2026-09-11 19:44:25 +07:00

477 lines
20 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 <limits.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;
typedef struct { Vector2 offset, target; float rotation, zoom; } Camera2D;
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);
extern Texture2D LoadTexture(const char *fileName);
extern bool IsTextureValid(Texture2D texture);
extern void UnloadTexture(Texture2D texture);
extern void DrawTexture(Texture2D texture, int posX, int posY, Color tint);
extern void DrawTextureV(Texture2D texture, Vector2 position, Color tint);
extern void DrawTextureEx(Texture2D texture, Vector2 position, float rotation,
float scale, Color tint);
extern void DrawTextureRec(Texture2D texture, Rectangle source,
Vector2 position, Color tint);
extern void BeginMode2D(Camera2D camera);
extern void EndMode2D(void);
extern Vector2 GetScreenToWorld2D(Vector2 position, Camera2D camera);
extern Vector2 GetWorldToScreen2D(Vector2 position, Camera2D camera);
extern bool CheckCollisionRecs(Rectangle rec1, Rectangle rec2);
extern bool CheckCollisionCircles(Vector2 c1, float r1, Vector2 c2, float r2);
extern bool CheckCollisionCircleRec(Vector2 center, float radius, Rectangle rec);
extern bool CheckCollisionCircleLine(Vector2 center, float radius,
Vector2 p1, Vector2 p2);
extern bool CheckCollisionPointRec(Vector2 point, Rectangle rec);
extern bool CheckCollisionPointCircle(Vector2 point, Vector2 center, float radius);
extern bool CheckCollisionPointTriangle(Vector2 point, Vector2 p1, Vector2 p2,
Vector2 p3);
extern bool CheckCollisionPointLine(Vector2 point, Vector2 p1, Vector2 p2,
int threshold);
extern bool CheckCollisionPointPoly(Vector2 point, const Vector2 *points,
int pointCount);
extern bool CheckCollisionLines(Vector2 a1, Vector2 a2, Vector2 b1, Vector2 b2,
Vector2 *collisionPoint);
/* A Flan string arrives as ptr+len and is not NUL-terminated, so a C API that
* wants a C string needs a copy. Two callers want one: the window title and a
* texture's file path. Each passes a buffer big enough for what it is — 256
* for a title, PATH_MAX for a path — and truncating is better than reading
* past the end. A truncated path fails to open and LoadTexture returns an id
* of 0, which is what texture-valid? is for. */
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);
}
void flan_rl_load_texture(const char *path, long long n, Texture2D *out) {
char buf[PATH_MAX];
*out = LoadTexture(cstr(path, n, buf, sizeof buf));
}
bool flan_rl_is_texture_valid(const Texture2D *texture) {
return IsTextureValid(*texture);
}
void flan_rl_unload_texture(const Texture2D *texture) { UnloadTexture(*texture); }
void flan_rl_draw_texture(const Texture2D *texture, int x, int y,
const Color *tint) {
DrawTexture(*texture, x, y, *tint);
}
void flan_rl_draw_texture_v(const Texture2D *texture, const Vector2 *position,
const Color *tint) {
DrawTextureV(*texture, *position, *tint);
}
void flan_rl_draw_texture_ex(const Texture2D *texture, const Vector2 *position,
float rotation, float scale, const Color *tint) {
DrawTextureEx(*texture, *position, rotation, scale, *tint);
}
void flan_rl_draw_texture_rec(const Texture2D *texture, const Rectangle *source,
const Vector2 *position, const Color *tint) {
DrawTextureRec(*texture, *source, *position, *tint);
}
void flan_rl_begin_mode_2d(const Camera2D *camera) { BeginMode2D(*camera); }
void flan_rl_end_mode_2d(void) { EndMode2D(); }
void flan_rl_get_screen_to_world_2d(const Vector2 *position,
const Camera2D *camera, Vector2 *out) {
*out = GetScreenToWorld2D(*position, *camera);
}
void flan_rl_get_world_to_screen_2d(const Vector2 *position,
const Camera2D *camera, Vector2 *out) {
*out = GetWorldToScreen2D(*position, *camera);
}
bool flan_rl_check_collision_recs(const Rectangle *a, const Rectangle *b) {
return CheckCollisionRecs(*a, *b);
}
bool flan_rl_check_collision_circles(const Vector2 *c1, float r1,
const Vector2 *c2, float r2) {
return CheckCollisionCircles(*c1, r1, *c2, r2);
}
bool flan_rl_check_collision_circle_rec(const Vector2 *center, float radius,
const Rectangle *rec) {
return CheckCollisionCircleRec(*center, radius, *rec);
}
bool flan_rl_check_collision_circle_line(const Vector2 *center, float radius,
const Vector2 *p1, const Vector2 *p2) {
return CheckCollisionCircleLine(*center, radius, *p1, *p2);
}
bool flan_rl_check_collision_point_rec(const Vector2 *point, const Rectangle *rec) {
return CheckCollisionPointRec(*point, *rec);
}
bool flan_rl_check_collision_point_circle(const Vector2 *point,
const Vector2 *center, float radius) {
return CheckCollisionPointCircle(*point, *center, radius);
}
bool flan_rl_check_collision_point_triangle(const Vector2 *point, const Vector2 *a,
const Vector2 *b, const Vector2 *c) {
return CheckCollisionPointTriangle(*point, *a, *b, *c);
}
bool flan_rl_check_collision_point_line(const Vector2 *point, const Vector2 *p1,
const Vector2 *p2, int threshold) {
return CheckCollisionPointLine(*point, *p1, *p2, threshold);
}
/* A Flan slice arrives as ptr+len, the same shape a string does. raylib wants
* an int count, and a polygon with more than INT_MAX points is not a thing
* that happens; the clamp is there so the conversion is not silent. */
bool flan_rl_check_collision_point_poly(const Vector2 *point,
const Vector2 *points, long long n) {
if (n > INT_MAX) n = INT_MAX;
return CheckCollisionPointPoly(*point, points, (int)n);
}
bool flan_rl_check_collision_lines(const Vector2 *a1, const Vector2 *a2,
const Vector2 *b1, const Vector2 *b2,
Vector2 *out) {
return CheckCollisionLines(*a1, *a2, *b1, *b2, out);
}
/* ── Images ─────────────────────────────────────────────────────────
*
* An Image is pixels in RAM, so all of this runs with no window and no GL
* context — which is why it is the part of the package that a headless test
* can actually assert rather than merely link. `data` is the pixel buffer
* raylib owns; `format` is a PixelFormat enum and GenImageColor makes 7
* (uncompressed R8G8B8A8).
*
* raylib 5.5 spells the predicate IsImageValid. IsImageReady, which the 5.1
* header still had, is gone — checked with nm -D, not remembered.
*/
typedef struct { void *data; int width, height, mipmaps, format; } Image;
extern Image LoadImage(const char *fileName);
extern bool IsImageValid(Image image);
extern void UnloadImage(Image image);
extern bool ExportImage(Image image, const char *fileName);
extern Image GenImageColor(int width, int height, Color color);
extern void ImageResize(Image *image, int newWidth, int newHeight);
extern void ImageResizeNN(Image *image, int newWidth, int newHeight);
extern void ImageCrop(Image *image, Rectangle crop);
extern void ImageFlipHorizontal(Image *image);
extern void ImageFlipVertical(Image *image);
extern void ImageDrawPixel(Image *dst, int posX, int posY, Color color);
extern Color GetImageColor(Image image, int x, int y);
extern Texture2D LoadTextureFromImage(Image image);
void flan_rl_load_image(const char *path, long long n, Image *out) {
char buf[PATH_MAX];
*out = LoadImage(cstr(path, n, buf, sizeof buf));
}
bool flan_rl_is_image_valid(const Image *image) { return IsImageValid(*image); }
void flan_rl_unload_image(const Image *image) { UnloadImage(*image); }
bool flan_rl_export_image(const Image *image, const char *path, long long n) {
char buf[PATH_MAX];
return ExportImage(*image, cstr(path, n, buf, sizeof buf));
}
void flan_rl_gen_image_color(int width, int height, const Color *color,
Image *out) {
*out = GenImageColor(width, height, *color);
}
void flan_rl_image_resize(Image *image, int w, int h) { ImageResize(image, w, h); }
void flan_rl_image_resize_nn(Image *image, int w, int h) { ImageResizeNN(image, w, h); }
void flan_rl_image_crop(Image *image, const Rectangle *crop) {
ImageCrop(image, *crop);
}
void flan_rl_image_flip_horizontal(Image *image) { ImageFlipHorizontal(image); }
void flan_rl_image_flip_vertical(Image *image) { ImageFlipVertical(image); }
void flan_rl_image_draw_pixel(Image *dst, int x, int y, const Color *color) {
ImageDrawPixel(dst, x, y, *color);
}
void flan_rl_get_image_color(const Image *image, int x, int y, Color *out) {
*out = GetImageColor(*image, x, y);
}
void flan_rl_load_texture_from_image(const Image *image, Texture2D *out) {
*out = LoadTextureFromImage(*image);
}
/* ── Shapes, text and timing ─────────────────────────────────────────
*
* All of the drawing below needs a GL context and therefore a window, so none
* of it can be in the acceptance table — it is exercised by running sand.flan
* and looking. The three prototypes most likely to be remembered wrong were
* checked against nm -D on libraylib.so.550 rather than against a header:
* raylib 5.5 moved the line thickness off DrawRectangleRoundedLines onto
* DrawRectangleRoundedLinesEx, so the four-argument form here is the 5.5 one
* and not the 5.1 one.
*
* MeasureText, GetFrameTime, GetTime and GetScreenWidth/Height need no GL
* context but do need InitWindow: the first reads the default font, which
* only InitWindow loads, and the others read window state. Headless they all
* answer 0 — measured, not assumed — so they are no more assertable than the
* drawing is.
*/
extern void DrawPixel(int posX, int posY, Color color);
extern void DrawPixelV(Vector2 position, Color color);
extern void DrawLine(int startPosX, int startPosY, int endPosX, int endPosY,
Color color);
extern void DrawLineV(Vector2 startPos, Vector2 endPos, Color color);
extern void DrawLineEx(Vector2 startPos, Vector2 endPos, float thick, Color color);
extern void DrawCircle(int centerX, int centerY, float radius, Color color);
extern void DrawCircleV(Vector2 center, float radius, Color color);
extern void DrawCircleLines(int centerX, int centerY, float radius, Color color);
extern void DrawCircleLinesV(Vector2 center, float radius, Color color);
extern void DrawEllipse(int centerX, int centerY, float radiusH, float radiusV,
Color color);
extern void DrawEllipseLines(int centerX, int centerY, float radiusH,
float radiusV, Color color);
extern void DrawRing(Vector2 center, float innerRadius, float outerRadius,
float startAngle, float endAngle, int segments, Color color);
extern void DrawRingLines(Vector2 center, float innerRadius, float outerRadius,
float startAngle, float endAngle, int segments,
Color color);
extern void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color);
extern void DrawTriangleLines(Vector2 v1, Vector2 v2, Vector2 v3, Color color);
extern void DrawRectangleV(Vector2 position, Vector2 size, Color color);
extern void DrawRectangleRec(Rectangle rec, Color color);
extern void DrawRectangleLines(int posX, int posY, int width, int height,
Color color);
extern void DrawRectangleLinesEx(Rectangle rec, float lineThick, Color color);
extern void DrawRectangleRounded(Rectangle rec, float roundness, int segments,
Color color);
extern void DrawRectangleRoundedLines(Rectangle rec, float roundness,
int segments, Color color);
extern void DrawRectangleRoundedLinesEx(Rectangle rec, float roundness,
int segments, float lineThick,
Color color);
extern void DrawText(const char *text, int posX, int posY, int fontSize,
Color color);
extern int MeasureText(const char *text, int fontSize);
extern float GetFrameTime(void);
extern double GetTime(void);
extern int GetScreenWidth(void);
extern int GetScreenHeight(void);
void flan_rl_draw_pixel(int x, int y, const Color *c) { DrawPixel(x, y, *c); }
void flan_rl_draw_pixel_v(const Vector2 *p, const Color *c) {
DrawPixelV(*p, *c);
}
void flan_rl_draw_line(int x1, int y1, int x2, int y2, const Color *c) {
DrawLine(x1, y1, x2, y2, *c);
}
void flan_rl_draw_line_v(const Vector2 *a, const Vector2 *b, const Color *c) {
DrawLineV(*a, *b, *c);
}
void flan_rl_draw_line_ex(const Vector2 *a, const Vector2 *b, float thick,
const Color *c) {
DrawLineEx(*a, *b, thick, *c);
}
void flan_rl_draw_circle(int x, int y, float radius, const Color *c) {
DrawCircle(x, y, radius, *c);
}
void flan_rl_draw_circle_v(const Vector2 *center, float radius, const Color *c) {
DrawCircleV(*center, radius, *c);
}
void flan_rl_draw_circle_lines(int x, int y, float radius, const Color *c) {
DrawCircleLines(x, y, radius, *c);
}
void flan_rl_draw_circle_lines_v(const Vector2 *center, float radius,
const Color *c) {
DrawCircleLinesV(*center, radius, *c);
}
void flan_rl_draw_ellipse(int x, int y, float rh, float rv, const Color *c) {
DrawEllipse(x, y, rh, rv, *c);
}
void flan_rl_draw_ellipse_lines(int x, int y, float rh, float rv, const Color *c) {
DrawEllipseLines(x, y, rh, rv, *c);
}
void flan_rl_draw_ring(const Vector2 *center, float inner, float outer,
float start, float end, int segments, const Color *c) {
DrawRing(*center, inner, outer, start, end, segments, *c);
}
void flan_rl_draw_ring_lines(const Vector2 *center, float inner, float outer,
float start, float end, int segments,
const Color *c) {
DrawRingLines(*center, inner, outer, start, end, segments, *c);
}
void flan_rl_draw_triangle(const Vector2 *a, const Vector2 *b, const Vector2 *d,
const Color *c) {
DrawTriangle(*a, *b, *d, *c);
}
void flan_rl_draw_triangle_lines(const Vector2 *a, const Vector2 *b,
const Vector2 *d, const Color *c) {
DrawTriangleLines(*a, *b, *d, *c);
}
void flan_rl_draw_rectangle_v(const Vector2 *position, const Vector2 *size,
const Color *c) {
DrawRectangleV(*position, *size, *c);
}
void flan_rl_draw_rectangle_rec(const Rectangle *rec, const Color *c) {
DrawRectangleRec(*rec, *c);
}
void flan_rl_draw_rectangle_lines(int x, int y, int w, int h, const Color *c) {
DrawRectangleLines(x, y, w, h, *c);
}
void flan_rl_draw_rectangle_lines_ex(const Rectangle *rec, float thick,
const Color *c) {
DrawRectangleLinesEx(*rec, thick, *c);
}
void flan_rl_draw_rectangle_rounded(const Rectangle *rec, float roundness,
int segments, const Color *c) {
DrawRectangleRounded(*rec, roundness, segments, *c);
}
/* Four arguments, not five: 5.5's DrawRectangleRoundedLines has no thickness
* and the Ex variant below is where it went. Getting this wrong links fine. */
void flan_rl_draw_rectangle_rounded_lines(const Rectangle *rec, float roundness,
int segments, const Color *c) {
DrawRectangleRoundedLines(*rec, roundness, segments, *c);
}
void flan_rl_draw_rectangle_rounded_lines_ex(const Rectangle *rec,
float roundness, int segments,
float thick, const Color *c) {
DrawRectangleRoundedLinesEx(*rec, roundness, segments, thick, *c);
}
void flan_rl_draw_text(const char *text, long long n, int x, int y,
int font_size, const Color *c) {
char buf[512];
DrawText(cstr(text, n, buf, sizeof buf), x, y, font_size, *c);
}
int flan_rl_measure_text(const char *text, long long n, int font_size) {
char buf[512];
return MeasureText(cstr(text, n, buf, sizeof buf), font_size);
}
float flan_rl_get_frame_time(void) { return GetFrameTime(); }
double flan_rl_get_time(void) { return GetTime(); }
int flan_rl_get_screen_width(void) { return GetScreenWidth(); }
int flan_rl_get_screen_height(void) { return GetScreenHeight(); }