352 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			352 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #include "ground_lib.h"
 | |
| #include <SDL3/SDL.h>
 | |
| #include <SDL3/SDL_main.h>
 | |
| #include <SDL3/SDL_render.h>
 | |
| #include <iostream>
 | |
| 
 | |
| SDL_Window* window = nullptr;
 | |
| SDL_Renderer* renderer = nullptr;
 | |
| SDL_Event event;
 | |
| 
 | |
| int width = 0;
 | |
| int height = 0;
 | |
| bool hasRunInit = false;
 | |
| 
 | |
| // Helper function to clamp values
 | |
| int clamp(int value, int min, int max) {
 | |
|     if (value < min) return min;
 | |
|     if (value > max) return max;
 | |
|     return value;
 | |
| }
 | |
| 
 | |
| // Initialize SDL and create window
 | |
| GroundValue initSDL(GroundValue* args, int arg_count) {
 | |
|     if (hasRunInit) return GROUND_INT_VAL(2);
 | |
| 
 | |
|     VALIDATE_ARGS_3(GROUND_STRING, GROUND_INT, GROUND_INT);
 | |
| 
 | |
|     if (SDL_Init(SDL_INIT_VIDEO) < 0) {
 | |
|         std::cout << "SDL could not initialize! SDL Error: " << SDL_GetError() << std::endl;
 | |
|         return GROUND_INT_VAL(1);
 | |
|     }
 | |
| 
 | |
|     width = GET_INT(args[1]);
 | |
|     height = GET_INT(args[2]);
 | |
| 
 | |
|     // Validate dimensions
 | |
|     if (width <= 0 || height <= 0) {
 | |
|         std::cout << "Invalid window dimensions" << std::endl;
 | |
|         SDL_Quit();
 | |
|         return GROUND_INT_VAL(1);
 | |
|     }
 | |
| 
 | |
|     if (SDL_CreateWindowAndRenderer(GET_STRING(args[0]), width, height,
 | |
|                                    SDL_WINDOW_OPENGL, &window, &renderer) < 0) {
 | |
|         std::cout << "Window could not be created! SDL Error: " << SDL_GetError() << std::endl;
 | |
|         SDL_Quit();
 | |
|         return GROUND_INT_VAL(1);
 | |
|     }
 | |
| 
 | |
|     hasRunInit = true;
 | |
|     return GROUND_INT_VAL(0);
 | |
| }
 | |
| 
 | |
| // Poll for events (enhanced with more event types)
 | |
| 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);
 | |
|     }
 | |
| 
 | |
|     if (SDL_PollEvent(&event)) {
 | |
|         switch (event.type) {
 | |
|             case SDL_EVENT_QUIT:
 | |
|                 return GROUND_INT_VAL(0);
 | |
|             case SDL_EVENT_KEY_DOWN:
 | |
|                 return GROUND_INT_VAL(1);
 | |
|             case SDL_EVENT_KEY_UP:
 | |
|                 return GROUND_INT_VAL(2);
 | |
|             case SDL_EVENT_MOUSE_BUTTON_DOWN:
 | |
|                 return GROUND_INT_VAL(3);
 | |
|             case SDL_EVENT_MOUSE_BUTTON_UP:
 | |
|                 return GROUND_INT_VAL(4);
 | |
|             case SDL_EVENT_MOUSE_MOTION:
 | |
|                 return GROUND_INT_VAL(5);
 | |
|             default:
 | |
|                 return GROUND_INT_VAL(6); // Other event
 | |
|         }
 | |
|     }
 | |
|     return GROUND_INT_VAL(-1); // No event
 | |
| }
 | |
| 
 | |
| // Get last key pressed (requires polling events first)
 | |
| GroundValue getLastKey(GroundValue* args, int arg_count) {
 | |
|     if (!hasRunInit) {
 | |
|         std::cout << "Cannot get key, please run !grogs:initSDL first" << std::endl;
 | |
|         return GROUND_INT_VAL(-1);
 | |
|     }
 | |
| 
 | |
|     if (event.type == SDL_EVENT_KEY_DOWN || event.type == SDL_EVENT_KEY_UP) {
 | |
|         return GROUND_INT_VAL(event.key.key);
 | |
|     }
 | |
|     return GROUND_INT_VAL(-1);
 | |
| }
 | |
| 
 | |
| // Get mouse position
 | |
| GroundValue getMousePos(GroundValue* args, int arg_count) {
 | |
|     if (!hasRunInit) {
 | |
|         std::cout << "Cannot get mouse position, please run !grogs:initSDL first" << std::endl;
 | |
|         return GROUND_INT_VAL(-1);
 | |
|     }
 | |
| 
 | |
|     VALIDATE_ARGS_1(GROUND_INT);
 | |
|     int coord = GET_INT(args[0]); // 0 for x, 1 for y
 | |
| 
 | |
|     float mouseX, mouseY;
 | |
|     SDL_GetMouseState(&mouseX, &mouseY);
 | |
| 
 | |
|     if (coord == 0) {
 | |
|         return GROUND_INT_VAL((int)mouseX);
 | |
|     } else if (coord == 1) {
 | |
|         return GROUND_INT_VAL((int)mouseY);
 | |
|     }
 | |
|     return GROUND_INT_VAL(-1);
 | |
| }
 | |
| 
 | |
| // Clear renderer with optional color
 | |
| 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);
 | |
|     }
 | |
| 
 | |
|     if (arg_count == 3) {
 | |
|         VALIDATE_ARGS_3(GROUND_INT, GROUND_INT, GROUND_INT);
 | |
|         int r = clamp(GET_INT(args[0]), 0, 255);
 | |
|         int g = clamp(GET_INT(args[1]), 0, 255);
 | |
|         int b = clamp(GET_INT(args[2]), 0, 255);
 | |
|         SDL_SetRenderDrawColor(renderer, r, g, b, 255);
 | |
|     } else {
 | |
|         // Default to black
 | |
|         SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
 | |
|     }
 | |
| 
 | |
|     SDL_RenderClear(renderer);
 | |
|     return GROUND_INT_VAL(0);
 | |
| }
 | |
| 
 | |
| // Render frame to screen
 | |
| 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_INT_VAL(0);
 | |
| }
 | |
| 
 | |
| // Set a single pixel
 | |
| 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);
 | |
| 
 | |
|     int pixelX = GET_INT(args[0]);
 | |
|     int pixelY = GET_INT(args[1]);
 | |
|     int red = clamp(GET_INT(args[2]), 0, 255);
 | |
|     int green = clamp(GET_INT(args[3]), 0, 255);
 | |
|     int blue = clamp(GET_INT(args[4]), 0, 255);
 | |
| 
 | |
|     // Bounds checking
 | |
|     if (pixelX < 0 || pixelX >= width || pixelY < 0 || pixelY >= height) {
 | |
|         return GROUND_INT_VAL(-1); // Out of bounds
 | |
|     }
 | |
| 
 | |
|     SDL_SetRenderDrawColor(renderer, red, green, blue, 255);
 | |
|     SDL_RenderPoint(renderer, (float)pixelX, (float)pixelY);
 | |
| 
 | |
|     return GROUND_INT_VAL(0);
 | |
| }
 | |
| 
 | |
| // Draw a line
 | |
| GroundValue drawLine(GroundValue* args, int arg_count) {
 | |
|     if (!hasRunInit) {
 | |
|         std::cout << "Cannot draw line, please run !grogs:initSDL first" << std::endl;
 | |
|         return GROUND_INT_VAL(-1);
 | |
|     }
 | |
|     VALIDATE_ARGS_7(GROUND_INT, GROUND_INT, GROUND_INT, GROUND_INT, GROUND_INT, GROUND_INT, GROUND_INT);
 | |
| 
 | |
|     int x1 = GET_INT(args[0]);
 | |
|     int y1 = GET_INT(args[1]);
 | |
|     int x2 = GET_INT(args[2]);
 | |
|     int y2 = GET_INT(args[3]);
 | |
|     int red = clamp(GET_INT(args[4]), 0, 255);
 | |
|     int green = clamp(GET_INT(args[5]), 0, 255);
 | |
|     int blue = clamp(GET_INT(args[6]), 0, 255);
 | |
| 
 | |
|     SDL_SetRenderDrawColor(renderer, red, green, blue, 255);
 | |
|     SDL_RenderLine(renderer, (float)x1, (float)y1, (float)x2, (float)y2);
 | |
| 
 | |
|     return GROUND_INT_VAL(0);
 | |
| }
 | |
| 
 | |
| // Draw a filled rectangle
 | |
| GroundValue drawRect(GroundValue* args, int arg_count) {
 | |
|     if (!hasRunInit) {
 | |
|         std::cout << "Cannot draw rectangle, please run !grogs:initSDL first" << std::endl;
 | |
|         return GROUND_INT_VAL(-1);
 | |
|     }
 | |
|     VALIDATE_ARGS_7(GROUND_INT, GROUND_INT, GROUND_INT, GROUND_INT, GROUND_INT, GROUND_INT, GROUND_INT);
 | |
| 
 | |
|     int x = GET_INT(args[0]);
 | |
|     int y = GET_INT(args[1]);
 | |
|     int w = GET_INT(args[2]);
 | |
|     int h = GET_INT(args[3]);
 | |
|     int red = clamp(GET_INT(args[4]), 0, 255);
 | |
|     int green = clamp(GET_INT(args[5]), 0, 255);
 | |
|     int blue = clamp(GET_INT(args[6]), 0, 255);
 | |
| 
 | |
|     if (w <= 0 || h <= 0) {
 | |
|         return GROUND_INT_VAL(-1); // Invalid dimensions
 | |
|     }
 | |
| 
 | |
|     SDL_FRect rect = {(float)x, (float)y, (float)w, (float)h};
 | |
|     SDL_SetRenderDrawColor(renderer, red, green, blue, 255);
 | |
|     SDL_RenderFillRect(renderer, &rect);
 | |
| 
 | |
|     return GROUND_INT_VAL(0);
 | |
| }
 | |
| 
 | |
| // Draw a rectangle outline
 | |
| GroundValue drawRectOutline(GroundValue* args, int arg_count) {
 | |
|     if (!hasRunInit) {
 | |
|         std::cout << "Cannot draw rectangle outline, please run !grogs:initSDL first" << std::endl;
 | |
|         return GROUND_INT_VAL(-1);
 | |
|     }
 | |
|     VALIDATE_ARGS_7(GROUND_INT, GROUND_INT, GROUND_INT, GROUND_INT, GROUND_INT, GROUND_INT, GROUND_INT);
 | |
| 
 | |
|     int x = GET_INT(args[0]);
 | |
|     int y = GET_INT(args[1]);
 | |
|     int w = GET_INT(args[2]);
 | |
|     int h = GET_INT(args[3]);
 | |
|     int red = clamp(GET_INT(args[4]), 0, 255);
 | |
|     int green = clamp(GET_INT(args[5]), 0, 255);
 | |
|     int blue = clamp(GET_INT(args[6]), 0, 255);
 | |
| 
 | |
|     if (w <= 0 || h <= 0) {
 | |
|         return GROUND_INT_VAL(-1); // Invalid dimensions
 | |
|     }
 | |
| 
 | |
|     SDL_FRect rect = {(float)x, (float)y, (float)w, (float)h};
 | |
|     SDL_SetRenderDrawColor(renderer, red, green, blue, 255);
 | |
|     SDL_RenderRect(renderer, &rect);
 | |
| 
 | |
|     return GROUND_INT_VAL(0);
 | |
| }
 | |
| 
 | |
| // Draw a circle (filled)
 | |
| GroundValue drawCircle(GroundValue* args, int arg_count) {
 | |
|     if (!hasRunInit) {
 | |
|         std::cout << "Cannot draw circle, please run !grogs:initSDL first" << std::endl;
 | |
|         return GROUND_INT_VAL(-1);
 | |
|     }
 | |
|     VALIDATE_ARGS_6(GROUND_INT, GROUND_INT, GROUND_INT, GROUND_INT, GROUND_INT, GROUND_INT);
 | |
| 
 | |
|     int centerX = GET_INT(args[0]);
 | |
|     int centerY = GET_INT(args[1]);
 | |
|     int radius = GET_INT(args[2]);
 | |
|     int red = clamp(GET_INT(args[3]), 0, 255);
 | |
|     int green = clamp(GET_INT(args[4]), 0, 255);
 | |
|     int blue = clamp(GET_INT(args[5]), 0, 255);
 | |
| 
 | |
|     if (radius <= 0) {
 | |
|         return GROUND_INT_VAL(-1); // Invalid radius
 | |
|     }
 | |
| 
 | |
|     SDL_SetRenderDrawColor(renderer, red, green, blue, 255);
 | |
| 
 | |
|     // Simple circle drawing using midpoint algorithm
 | |
|     for (int y = -radius; y <= radius; y++) {
 | |
|         for (int x = -radius; x <= radius; x++) {
 | |
|             if (x*x + y*y <= radius*radius) {
 | |
|                 SDL_RenderPoint(renderer, (float)(centerX + x), (float)(centerY + y));
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return GROUND_INT_VAL(0);
 | |
| }
 | |
| 
 | |
| // Get window dimensions
 | |
| GroundValue getWindowSize(GroundValue* args, int arg_count) {
 | |
|     if (!hasRunInit) {
 | |
|         std::cout << "Cannot get window size, please run !grogs:initSDL first" << std::endl;
 | |
|         return GROUND_INT_VAL(-1);
 | |
|     }
 | |
| 
 | |
|     VALIDATE_ARGS_1(GROUND_INT);
 | |
|     int dimension = GET_INT(args[0]); // 0 for width, 1 for height
 | |
| 
 | |
|     if (dimension == 0) {
 | |
|         return GROUND_INT_VAL(width);
 | |
|     } else if (dimension == 1) {
 | |
|         return GROUND_INT_VAL(height);
 | |
|     }
 | |
|     return GROUND_INT_VAL(-1);
 | |
| }
 | |
| 
 | |
| // Delay execution (useful for frame rate control)
 | |
| GroundValue delay(GroundValue* args, int arg_count) {
 | |
|     VALIDATE_ARGS_1(GROUND_INT);
 | |
|     int milliseconds = GET_INT(args[0]);
 | |
| 
 | |
|     if (milliseconds < 0) {
 | |
|         return GROUND_INT_VAL(-1);
 | |
|     }
 | |
| 
 | |
|     SDL_Delay(milliseconds);
 | |
|     return GROUND_INT_VAL(0);
 | |
| }
 | |
| 
 | |
| // Cleanup SDL resources
 | |
| GroundValue cleanup(GroundValue* args, int arg_count) {
 | |
|     if (hasRunInit) {
 | |
|         if (renderer) {
 | |
|             SDL_DestroyRenderer(renderer);
 | |
|             renderer = nullptr;
 | |
|         }
 | |
|         if (window) {
 | |
|             SDL_DestroyWindow(window);
 | |
|             window = nullptr;
 | |
|         }
 | |
|         SDL_Quit();
 | |
|         hasRunInit = false;
 | |
|     }
 | |
|     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(getLastKey);
 | |
|     REGISTER_GROUND_FUNCTION(getMousePos);
 | |
|     REGISTER_GROUND_FUNCTION(clearRenderer);
 | |
|     REGISTER_GROUND_FUNCTION(setPixel);
 | |
|     REGISTER_GROUND_FUNCTION(drawLine);
 | |
|     REGISTER_GROUND_FUNCTION(drawRect);
 | |
|     REGISTER_GROUND_FUNCTION(drawRectOutline);
 | |
|     REGISTER_GROUND_FUNCTION(drawCircle);
 | |
|     REGISTER_GROUND_FUNCTION(getWindowSize);
 | |
|     REGISTER_GROUND_FUNCTION(delay);
 | |
|     REGISTER_GROUND_FUNCTION(cleanup);
 | |
| GROUND_LIBRARY_INIT_END()
 | |
| 
 | |
| GROUND_LIBRARY_CLEANUP()
 | |
|     cleanup(nullptr, 0);
 | |
| GROUND_LIBRARY_CLEANUP_END()
 |