// ----------------------------------------------------------------------------- // https://dataswamp.org/~incal/bad-el/src/bad-sdl2/bad-sdl2.c // ----------------------------------------------------------------------------- int plugin_is_GPL_compatible; #include "bad-sdl2.h" #include #include #include #include #include #include #include #include #include #define nas __attribute__((unused)) // ----------------------------------------------------------------------------- // Okay here it begins // ----------------------------------------------------------------------------- static SDL_Renderer* rnd = NULL; static SDL_Window* win = NULL; // ----------------------------------------------------------------------------- // font // ----------------------------------------------------------------------------- static TTF_Font* fnt = NULL; static SDL_Color fg = { 253, 254, 255, 254 }; static int fnt_sze = 16; // ----------------------------------------------------------------------------- // cube // ----------------------------------------------------------------------------- static SDL_Texture* screen_tex = NULL; static int cube_call = 0; static int cube_insert = 0; #define cube_side 256 #define cube_size 16777216 static SDL_Texture* cube[cube_size]; // ----------------------------------------------------------------------------- // thread // ----------------------------------------------------------------------------- static bool gthr_run = false; static SDL_Thread* thr = NULL; int gthr_main (void* data nas) { int fps = 60; int say = 16 * fps; float slp = 1.0 / fps; float slp_ms = slp * 1000; int pid = getpid(); int tid = SDL_GetThreadID (thr); const char* name = SDL_GetThreadName (thr); for (int i; gthr_run; i++) { if (!(i % say)) { printf("[%d: %d %d %s] hello from thread\n", i, pid, tid, name); } SDL_Delay(slp_ms); } printf("Goodbye from thread.\n"); return 0; } void gthr_init () { thr = SDL_CreateThread(gthr_main, "gthr", NULL); assert(thr); gthr_run = true; } void gthr_quit () { if (gthr_run) { gthr_run = false; SDL_WaitThread(thr, NULL); gthr_quit(); } } // ----------------------------------------------------------------------------- // string-to-texture hashing & caching // ----------------------------------------------------------------------------- void nullify_cube () { for (int i = 0; i < cube_size; i++) { cube[i] = NULL; }} void free_cube () { for (int i = 0; i < cube_size; i++) { if (cube[i]) { SDL_DestroyTexture(cube[i]); } } nullify_cube(); } void hash_coords (const char* str, uint8_t* x, uint8_t* y, uint8_t* z) { uint8_t h[SHA256_DIGEST_LENGTH]; SHA256((const uint8_t*)str, strlen(str), h); *x = h[0] % cube_side; *y = h[1] % cube_side; *z = h[2] % cube_side; } void find_create_store (const char* key, SDL_Rect rect) { const int max_all = 3750; static int max_nxt = max_all; cube_call++; if (max_nxt < cube_insert) { printf("deallocating %d textures at %d inserted.\n", max_all, max_nxt); free_cube(); max_nxt += max_all; } uint8_t x; uint8_t y; uint8_t z; hash_coords (key, &x, &y, &z); size_t i = (size_t)x + ((size_t)y * cube_side) + ((size_t)z * cube_side * cube_side); if (!cube[i]) { SDL_Surface* txt_sur = TTF_RenderText_Blended(fnt, key, fg); assert(txt_sur); cube[i] = SDL_CreateTextureFromSurface(rnd, txt_sur); assert(cube[i]); SDL_SetTextureBlendMode(cube[i], SDL_BLENDMODE_BLEND); cube_insert++; } SDL_RenderCopy (rnd, cube[i], NULL, &rect); } // ----------------------------------------------------------------------------- // Emacs dynamic module C functions, make interface to Emacs // ----------------------------------------------------------------------------- int emacs_module_init (struct emacs_runtime* rnt) { // check for incompatible Emacs binary if (rnt->size < (long int)sizeof(*rnt)) { return 1; } // check for incompatible module API emacs_env* env = rnt->get_environment(rnt); if (env->size < (long int)sizeof(*env)) { return 2; } // check for too old Emacs int nas emacs_ver; if ((long int)sizeof(struct emacs_env_31) <= env->size) { emacs_ver = 31; } else if ((long int)sizeof(struct emacs_env_30) <= env->size) { emacs_ver = 30; } else if ((long int)sizeof(struct emacs_env_29) <= env->size) { emacs_ver = 29; } else { return 3; } // draw_init emacs_value draw_init_func = env->make_function(env, 0, 0, draw_init, "Init draw.", NULL); emacs_value draw_init_symb = env->intern(env, "draw_init"); emacs_value draw_init_args[] = {draw_init_symb, draw_init_func}; env->funcall(env, env->intern(env, "defalias"), 2, draw_init_args); // draw_quit emacs_value draw_quit_func = env->make_function(env, 0, 0, draw_quit, "Quit draw.", NULL); emacs_value draw_quit_symb = env->intern(env, "draw_quit"); emacs_value draw_quit_args[] = {draw_quit_symb, draw_quit_func}; env->funcall(env, env->intern(env, "defalias"), 2, draw_quit_args); // draw_draw emacs_value draw_draw_func = env->make_function(env, 0, 0, draw_draw, "Draw draw.", NULL); emacs_value draw_draw_symb = env->intern(env, "draw_draw"); emacs_value draw_draw_args[] = {draw_draw_symb, draw_draw_func}; env->funcall(env, env->intern(env, "defalias"), 2, draw_draw_args); return 0; } // ----------------------------------------------------------------------------- // those interface functions // ----------------------------------------------------------------------------- emacs_value draw_init (emacs_env* env, ptrdiff_t nargs nas, emacs_value* args nas, void* data nas) { cube_insert = 0; cube_call = 0; nullify_cube(); SDL_SetHint(SDL_HINT_GRAB_KEYBOARD, "0"); SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); SDL_Init(SDL_INIT_VIDEO); TTF_Init(); win = SDL_CreateWindow("SDL2 draw", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, SDL_WINDOW_SHOWN); assert(win); // TODO rnd = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE); assert(rnd); fnt = TTF_OpenFont("/usr/share/fonts/truetype/ocr-a/OCRABold.ttf", fnt_sze); assert(fnt); // gthr_init(); int max_w, max_h; SDL_GetRendererOutputSize (rnd, &max_w, &max_h); screen_tex = SDL_CreateTexture(rnd, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_TARGET, max_w, max_h); return env->intern(env, "nil"); } emacs_value draw_draw (emacs_env* env, ptrdiff_t nargs nas, emacs_value* args nas, void* data nas) { // TODO: change name to draw_frame // validate assert(win && rnd && fnt); // clear SDL_SetRenderTarget (rnd, screen_tex); SDL_SetRenderDrawColor (rnd, 0, 0, 0, 255); SDL_RenderClear(rnd); // get grid data and emacs_value ef = env->intern(env, "bad-grid-string"); // Elisp function emacs_value es = env->funcall(env, ef, 0, NULL); ptrdiff_t l = 0; env->copy_string_contents(env, es, NULL, &l); char* txt_str = malloc(l); env->copy_string_contents(env, es, txt_str, &l); assert(txt_str); // render render_text(txt_str); SDL_SetRenderTarget (rnd, NULL); SDL_RenderCopy (rnd, screen_tex, NULL, NULL); SDL_RenderPresent (rnd); free(txt_str); txt_str = NULL; // done return env->intern(env, "nil"); } emacs_value draw_quit (emacs_env* env, ptrdiff_t nargs nas, emacs_value* args nas, void* data nas) { if (screen_tex) { SDL_DestroyTexture (screen_tex); screen_tex = NULL; } if (fnt) { TTF_CloseFont (fnt); fnt = NULL; } if (rnd) { SDL_DestroyRenderer (rnd); rnd = NULL; } free_cube(); TTF_Quit(); SDL_Quit(); printf("%d calls. %d inserts. %.1f%% computation\n", cube_call, cube_insert, (100 * (cube_insert / (1.0 + cube_call)))); cube_call = 0; cube_insert = 0; return env->intern(env, "nil"); } // ----------------------------------------------------------------------------- // text in SDL // ----------------------------------------------------------------------------- void render_text (const char* txt) { assert(txt && win && rnd && fnt); char* buff = my_strdup(txt); char* lne = strtok(buff, "\n"); int lne_h = TTF_FontHeight(fnt); int max_w, max_h; SDL_GetRendererOutputSize (rnd, &max_w, &max_h); SDL_Rect lne_rect; for (int l = 0; lne; l++) { lne_rect.x = 0; lne_rect.y = l * lne_h; lne_rect.w = max_w; lne_rect.h = lne_h; find_create_store(lne, lne_rect); lne = strtok(NULL, "\n"); } free(buff); buff = NULL; // rect /* int screenw = 1028; // todo: don't hardcode */ /* int screenh = 720; // todo: don't hardcode */ /* int fmargin = 128; */ /* draw_rect(fmargin, fmargin, screenw - 2 * fmargin, screenh - 2 * fmargin); */ } // ----------------------------------------------------------------------------- // more // ----------------------------------------------------------------------------- // Where is this function? Not here, in a way. char* my_strdup (const char* s) { if (s) { size_t len = strlen(s) + 1; char* cpy = malloc(len); assert(cpy); memcpy(cpy, s, len); return cpy; } return NULL; } // ----------------------------------------------------------------------------- // draw planets // ----------------------------------------------------------------------------- void draw_rect (int x, int y, int w, int h) { assert(rnd); SDL_SetRenderDrawBlendMode (rnd, SDL_BLENDMODE_BLEND); SDL_SetRenderDrawColor (rnd, 56, 16, 255, 128); SDL_Rect rect = { .x = x, .y = y, .w = w, .h = h }; SDL_RenderFillRect(rnd, &rect); } void draw_planet (int cx nas, int cy nas, int rad) { int diam = 2 * rad; int w = diam; int h = diam; SDL_Texture* tex = SDL_CreateTexture(rnd, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_STREAMING, w, h); int pitch = 0; void* pixels = NULL; SDL_LockTexture(tex, NULL, &pixels, &pitch); Uint32* pixel_array = (uint32_t*)pixels; int stride = pitch / sizeof(uint32_t); for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { float dx = x - rad; float dy = y - rad; float dist = sqrt(dx*dx + dy*dy); float bright = fmax(0.0f, 1.0f - dist / rad); float curve = powf(bright, 1.5f); Uint8 r = (Uint8)( 16 * curve); Uint8 g = (Uint8)( 32 * curve); Uint8 b = (Uint8)(156 * curve); Uint8 a = (Uint8)( 96 * curve); pixel_array[y * stride + x] = (a << 24) | (b << 16) | (g << 8) | r; } } }