grogs graphics library
This commit is contained in:
92
grogs/grogs.cpp
Normal file
92
grogs/grogs.cpp
Normal file
@@ -0,0 +1,92 @@
|
||||
#include "ground_lib.h"
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3/SDL_main.h>
|
||||
#include <SDL3/SDL_render.h>
|
||||
|
||||
SDL_Window* window;
|
||||
SDL_Renderer* renderer;
|
||||
SDL_Event event;
|
||||
|
||||
int width;
|
||||
int height;
|
||||
|
||||
bool hasRunInit;
|
||||
|
||||
GroundValue initSDL(GroundValue* args, int arg_count) {
|
||||
if (hasRunInit) return GROUND_INT_VAL(2);
|
||||
|
||||
// args are window title, width, height
|
||||
VALIDATE_ARGS_3(GROUND_STRING, GROUND_INT, GROUND_INT);
|
||||
SDL_Init(SDL_INIT_VIDEO);
|
||||
width = GET_INT(args[1]);
|
||||
height = GET_INT(args[2]);
|
||||
SDL_CreateWindowAndRenderer(GET_STRING(args[0]), width, height, SDL_WINDOW_OPENGL, &window, &renderer);
|
||||
if (window == NULL) {
|
||||
std::cout << "Couldn't create SDL window" << std::endl;
|
||||
return GROUND_INT_VAL(1);
|
||||
}
|
||||
hasRunInit = true;
|
||||
return GROUND_INT_VAL(0);
|
||||
}
|
||||
|
||||
GroundValue pollEvent(GroundValue* args, int arg_count) {
|
||||
if (!hasRunInit) {
|
||||
std::cout << "Cannot poll event, please run !grogs:initSDL first" << std::endl;
|
||||
return GROUND_INT_VAL(-1);
|
||||
}
|
||||
|
||||
SDL_PollEvent(&event);
|
||||
if (event.type == SDL_EVENT_QUIT) {
|
||||
return GROUND_INT_VAL(0);
|
||||
}
|
||||
return GROUND_INT_VAL(-1);
|
||||
}
|
||||
|
||||
GroundValue clearRenderer(GroundValue* args, int arg_count) {
|
||||
if (!hasRunInit) {
|
||||
std::cout << "Cannot clear renderer, please run !grogs:initSDL first" << std::endl;
|
||||
return GROUND_INT_VAL(-1);
|
||||
}
|
||||
SDL_RenderClear(renderer);
|
||||
return GROUND_INT_VAL(0);
|
||||
}
|
||||
|
||||
GroundValue renderFrame(GroundValue* args, int arg_count) {
|
||||
if (!hasRunInit) {
|
||||
std::cout << "Cannot render frame, please run !grogs:initSDL first" << std::endl;
|
||||
return GROUND_INT_VAL(-1);
|
||||
}
|
||||
SDL_RenderPresent(renderer);
|
||||
return GROUND_BOOL_VAL(true);
|
||||
}
|
||||
|
||||
GroundValue setPixel(GroundValue* args, int arg_count) {
|
||||
if (!hasRunInit) {
|
||||
std::cout << "Cannot set pixel, please run !grogs:initSDL first" << std::endl;
|
||||
return GROUND_INT_VAL(-1);
|
||||
}
|
||||
VALIDATE_ARGS_5(GROUND_INT, GROUND_INT, GROUND_INT, GROUND_INT, GROUND_INT);
|
||||
// xpos, ypos, red, green, blue
|
||||
int pixelX = GET_INT(args[0]);
|
||||
int pixelY = GET_INT(args[1]);
|
||||
int red = GET_INT(args[2]);
|
||||
int green = GET_INT(args[3]);
|
||||
int blue = GET_INT(args[4]);
|
||||
SDL_SetRenderDrawColor(renderer, red, green, blue, 255);
|
||||
SDL_RenderPoint(renderer, pixelX, pixelY);
|
||||
|
||||
return GROUND_INT_VAL(0);
|
||||
}
|
||||
|
||||
GROUND_LIBRARY_INTERFACE()
|
||||
|
||||
GROUND_LIBRARY_INIT()
|
||||
REGISTER_GROUND_FUNCTION(initSDL);
|
||||
REGISTER_GROUND_FUNCTION(renderFrame);
|
||||
REGISTER_GROUND_FUNCTION(pollEvent);
|
||||
REGISTER_GROUND_FUNCTION(clearRenderer);
|
||||
REGISTER_GROUND_FUNCTION(setPixel);
|
||||
GROUND_LIBRARY_INIT_END()
|
||||
|
||||
GROUND_LIBRARY_CLEANUP()
|
||||
GROUND_LIBRARY_CLEANUP_END()
|
||||
BIN
grogs/grogs.so
Executable file
BIN
grogs/grogs.so
Executable file
Binary file not shown.
201
grogs/ground_lib.h
Normal file
201
grogs/ground_lib.h
Normal file
@@ -0,0 +1,201 @@
|
||||
#ifndef GROUND_LIB_H
|
||||
#define GROUND_LIB_H
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
|
||||
// Ground types - must match the interpreter
|
||||
typedef enum {
|
||||
GROUND_INT,
|
||||
GROUND_DOUBLE,
|
||||
GROUND_BOOL,
|
||||
GROUND_STRING,
|
||||
GROUND_CHAR
|
||||
} GroundType;
|
||||
|
||||
typedef struct {
|
||||
GroundType type;
|
||||
union {
|
||||
int int_val;
|
||||
double double_val;
|
||||
int bool_val;
|
||||
char* string_val;
|
||||
char char_val;
|
||||
} data;
|
||||
} GroundValue;
|
||||
|
||||
// Helper macros for creating GroundValue objects
|
||||
#define GROUND_INT_VAL(x) ({ GroundValue v; v.type = GROUND_INT; v.data.int_val = (x); v; })
|
||||
#define GROUND_DOUBLE_VAL(x) ({ GroundValue v; v.type = GROUND_DOUBLE; v.data.double_val = (x); v; })
|
||||
#define GROUND_BOOL_VAL(x) ({ GroundValue v; v.type = GROUND_BOOL; v.data.bool_val = (x) ? 1 : 0; v; })
|
||||
#define GROUND_CHAR_VAL(x) ({ GroundValue v; v.type = GROUND_CHAR; v.data.char_val = (x); v; })
|
||||
|
||||
// Helper function for creating string values
|
||||
inline GroundValue ground_string_val(const std::string& str) {
|
||||
GroundValue v;
|
||||
v.type = GROUND_STRING;
|
||||
char* result_str = new char[str.length() + 1];
|
||||
std::strcpy(result_str, str.c_str());
|
||||
v.data.string_val = result_str;
|
||||
return v;
|
||||
}
|
||||
|
||||
// Helper function for creating string values from C strings
|
||||
inline GroundValue ground_cstring_val(const char* str) {
|
||||
GroundValue v;
|
||||
v.type = GROUND_STRING;
|
||||
if (str) {
|
||||
size_t len = std::strlen(str);
|
||||
char* result_str = new char[len + 1];
|
||||
std::strcpy(result_str, str);
|
||||
v.data.string_val = result_str;
|
||||
} else {
|
||||
v.data.string_val = nullptr;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
// Helper macros for type checking
|
||||
#define IS_INT(v) ((v).type == GROUND_INT)
|
||||
#define IS_DOUBLE(v) ((v).type == GROUND_DOUBLE)
|
||||
#define IS_BOOL(v) ((v).type == GROUND_BOOL)
|
||||
#define IS_STRING(v) ((v).type == GROUND_STRING)
|
||||
#define IS_CHAR(v) ((v).type == GROUND_CHAR)
|
||||
|
||||
// Helper macros for extracting values
|
||||
#define GET_INT(v) ((v).data.int_val)
|
||||
#define GET_DOUBLE(v) ((v).data.double_val)
|
||||
#define GET_BOOL(v) ((v).data.bool_val != 0)
|
||||
#define GET_STRING(v) ((v).data.string_val)
|
||||
#define GET_CHAR(v) ((v).data.char_val)
|
||||
|
||||
// Helper macros for argument validation
|
||||
#define REQUIRE_ARGS(count) \
|
||||
if (arg_count < (count)) { \
|
||||
std::cerr << "Error: Expected at least " << (count) << " arguments, got " << arg_count << std::endl; \
|
||||
return GROUND_BOOL_VAL(false); \
|
||||
}
|
||||
|
||||
#define REQUIRE_TYPE(arg_index, expected_type) \
|
||||
if (args[arg_index].type != expected_type) { \
|
||||
std::cerr << "Error: Argument " << (arg_index + 1) << " must be of type " << #expected_type << std::endl; \
|
||||
return GROUND_BOOL_VAL(false); \
|
||||
}
|
||||
|
||||
// Convenience macro for checking both arg count and types
|
||||
#define VALIDATE_ARGS_1(type1) \
|
||||
REQUIRE_ARGS(1); \
|
||||
REQUIRE_TYPE(0, type1);
|
||||
|
||||
#define VALIDATE_ARGS_2(type1, type2) \
|
||||
REQUIRE_ARGS(2); \
|
||||
REQUIRE_TYPE(0, type1); \
|
||||
REQUIRE_TYPE(1, type2);
|
||||
|
||||
#define VALIDATE_ARGS_3(type1, type2, type3) \
|
||||
REQUIRE_ARGS(3); \
|
||||
REQUIRE_TYPE(0, type1); \
|
||||
REQUIRE_TYPE(1, type2); \
|
||||
REQUIRE_TYPE(2, type3);
|
||||
|
||||
#define VALIDATE_ARGS_5(type1, type2, type3, type4, type5) \
|
||||
REQUIRE_ARGS(5); \
|
||||
REQUIRE_TYPE(0, type1); \
|
||||
REQUIRE_TYPE(1, type2); \
|
||||
REQUIRE_TYPE(2, type3); \
|
||||
REQUIRE_TYPE(3, type4); \
|
||||
REQUIRE_TYPE(4, type5);
|
||||
|
||||
// Function registration helpers
|
||||
class GroundLibrary {
|
||||
private:
|
||||
std::vector<std::string> function_names;
|
||||
std::vector<void*> function_pointers;
|
||||
|
||||
public:
|
||||
void register_function(const std::string& name, void* ptr) {
|
||||
function_names.push_back(name);
|
||||
function_pointers.push_back(ptr);
|
||||
}
|
||||
|
||||
const char** get_function_names() {
|
||||
static std::vector<const char*> names;
|
||||
names.clear();
|
||||
for (const auto& name : function_names) {
|
||||
names.push_back(name.c_str());
|
||||
}
|
||||
names.push_back(nullptr); // Null terminator
|
||||
return names.data();
|
||||
}
|
||||
|
||||
void* get_function(const char* name) {
|
||||
for (size_t i = 0; i < function_names.size(); i++) {
|
||||
if (function_names[i] == name) {
|
||||
return function_pointers[i];
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
// Global library instance
|
||||
extern GroundLibrary ground_lib_registry;
|
||||
|
||||
// Macro to register functions easily
|
||||
#define REGISTER_GROUND_FUNCTION(func_name) \
|
||||
ground_lib_registry.register_function(#func_name, (void*)func_name)
|
||||
|
||||
// Macro to define the library interface
|
||||
#define GROUND_LIBRARY_INTERFACE() \
|
||||
GroundLibrary ground_lib_registry; \
|
||||
extern "C" { \
|
||||
const char** ground_get_functions() { \
|
||||
return ground_lib_registry.get_function_names(); \
|
||||
} \
|
||||
void* ground_get_function(const char* name) { \
|
||||
return ground_lib_registry.get_function(name); \
|
||||
} \
|
||||
}
|
||||
|
||||
// Optional initialization macro
|
||||
#define GROUND_LIBRARY_INIT() \
|
||||
extern "C" { \
|
||||
void ground_lib_init() {
|
||||
|
||||
#define GROUND_LIBRARY_INIT_END() \
|
||||
} \
|
||||
}
|
||||
|
||||
// Optional cleanup macro
|
||||
#define GROUND_LIBRARY_CLEANUP() \
|
||||
extern "C" { \
|
||||
void ground_lib_cleanup() {
|
||||
|
||||
#define GROUND_LIBRARY_CLEANUP_END() \
|
||||
} \
|
||||
}
|
||||
|
||||
// Utility function to print GroundValue for debugging
|
||||
inline void debug_print_ground_value(const GroundValue& v) {
|
||||
switch (v.type) {
|
||||
case GROUND_INT:
|
||||
std::cout << "INT: " << v.data.int_val << std::endl;
|
||||
break;
|
||||
case GROUND_DOUBLE:
|
||||
std::cout << "DOUBLE: " << v.data.double_val << std::endl;
|
||||
break;
|
||||
case GROUND_BOOL:
|
||||
std::cout << "BOOL: " << (v.data.bool_val ? "true" : "false") << std::endl;
|
||||
break;
|
||||
case GROUND_STRING:
|
||||
std::cout << "STRING: " << (v.data.string_val ? v.data.string_val : "(null)") << std::endl;
|
||||
break;
|
||||
case GROUND_CHAR:
|
||||
std::cout << "CHAR: '" << v.data.char_val << "'" << std::endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // GROUND_LIB_H
|
||||
52
grogs/test.grnd
Normal file
52
grogs/test.grnd
Normal file
@@ -0,0 +1,52 @@
|
||||
extern "grogs"
|
||||
|
||||
struct -rectangle
|
||||
init &topx -int
|
||||
init &topy -int
|
||||
init &bottomx -int
|
||||
init &bottomy -int
|
||||
init &red -int
|
||||
init &green -int
|
||||
init &blue -int
|
||||
endstruct
|
||||
|
||||
fun -int !draw -rectangle &r
|
||||
set &ycount $r.topy
|
||||
@columnloop
|
||||
set &xcount $r.topx
|
||||
add 1 $ycount &ycount
|
||||
greater $ycount $r.bottomy &cond
|
||||
if $cond %endloop
|
||||
@rowloop
|
||||
pusharg $xcount $ycount $r.red $r.green $r.blue
|
||||
!grogs:setPixel &out
|
||||
add 1 $xcount &xcount
|
||||
greater $xcount $r.bottomx &cond
|
||||
if $cond %columnloop
|
||||
jump %rowloop
|
||||
@endloop
|
||||
return 0
|
||||
endfun
|
||||
|
||||
pusharg "Grogs Window" 640 480
|
||||
!grogs:initSDL &status
|
||||
|
||||
init &rect -rectangle
|
||||
set &rect.topx 10
|
||||
set &rect.topy 10
|
||||
set &rect.bottomx 150
|
||||
set &rect.bottomy 100
|
||||
set &rect.red 0
|
||||
set &rect.blue 255
|
||||
set &rect.green 255
|
||||
|
||||
@loop
|
||||
pusharg $rect
|
||||
!draw &out
|
||||
!grogs:renderFrame &out
|
||||
!grogs:pollEvent &out
|
||||
equal $out 0 &cond
|
||||
if $cond %end
|
||||
jump %loop
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user