/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * mod_sdl.c: The SPL SDL module */ /** * A module for doing multimedia using the SDL library. * * Note: Only one SDL window can be opened by one host program at a time. * This is a limitation from SDL. */ #include #include #include #ifdef ENABLE_PTHREAD_SUPPORT #include #endif #include "spl.h" #include "compat.h" extern void SPL_ABI(spl_mod_sdl_init)(struct spl_vm *vm, struct spl_module *mod, int restore); extern void SPL_ABI(spl_mod_sdl_done)(struct spl_vm *vm, struct spl_module *mod); #ifdef ENABLE_PTHREAD_SUPPORT static pthread_mutex_t spl_sdl_init_lock = PTHREAD_MUTEX_INITIALIZER; #endif static int spl_sdl_initialized = 0; static SDL_Surface *spl_sdl_root = 0; static Uint32 spl_sdl_last_ticks = 0; static int spl_sdl_last_ticks_init = 0; static char *undupable_reason = "VM is active SDL user"; /*** SDL HNODE ***/ struct sdl_hnode_data { SDL_Surface *image; int revision_counter; int reference_counter; struct sdl_hnode_data *left, *right; }; static struct sdl_hnode_data *spl_sdl_hnlist = 0; static struct sdl_hnode_data *clib_get_hnd(struct spl_task *task, struct spl_node *n) { struct sdl_hnode_data *hnd = n ? n->hnode_data : 0; if (!n || (!n->hnode_name && !n->value && !n->subs_counter && !n->code && !n->ctx && !n->cls)) { return 0; } if (!n->hnode_name || strcmp(n->hnode_name, "sdl") || !hnd || !hnd->image) { spl_clib_exception(task, "SdlEx", "description", SPL_NEW_PRINTF("Expected SDL image (surface) node"), NULL); return 0; } return hnd; } static SDL_Surface *clib_get_surface(struct spl_task *task, int isdest) { struct spl_node *n = spl_cleanup(task, spl_clib_get_node(task)); struct sdl_hnode_data *hnd = n ? n->hnode_data : 0; if (!n || (!n->hnode_name && !n->value && !n->subs_counter && !n->code && !n->ctx && !n->cls)) { return spl_sdl_root; } if (!n->hnode_name || strcmp(n->hnode_name, "sdl") || !hnd || !hnd->image) { spl_clib_exception(task, "SdlEx", "description", SPL_NEW_PRINTF("Expected SDL image (surface) node"), NULL); return 0; } if (isdest) hnd->revision_counter++; return hnd->image; } static struct spl_node *sdl_hnode_new() { struct spl_node *n = SPL_NEW_STRING_DUP("SDL Node"); struct sdl_hnode_data *hnd = calloc(1, sizeof(struct sdl_hnode_data)); n->hnode_name = strdup("sdl"); n->hnode_data = hnd; if (spl_sdl_hnlist) { hnd->right = spl_sdl_hnlist; hnd->right->left = hnd; } spl_sdl_hnlist = hnd; hnd->reference_counter = 1; return n; } static void sdl_hnode_data_free(struct sdl_hnode_data *hnd) { if (hnd->image) { SDL_FreeSurface(hnd->image); hnd->image = 0; } } static void handler_sdl_node(struct spl_task UNUSED(*task), struct spl_vm UNUSED(*vm), struct spl_node *node, struct spl_hnode_args *args, void UNUSED(*data)) { if (args->action == SPL_HNODE_ACTION_PUT) { struct sdl_hnode_data *hnd = node->hnode_data; if (hnd && --hnd->reference_counter == 0) { sdl_hnode_data_free(hnd); *(hnd->left ? &hnd->left->right : &spl_sdl_hnlist) = hnd->right; if (hnd->right) hnd->right->left = hnd->left; free(hnd); } return; } if (args->action == SPL_HNODE_ACTION_LOOKUP) { struct sdl_hnode_data *hnd = node->hnode_data; char *mode = spl_hash_decode(args->key); if (hnd && hnd->image && !strcmp(mode, "w")) args->value = SPL_NEW_INT(hnd->image->w); if (hnd && hnd->image && !strcmp(mode, "h")) args->value = SPL_NEW_INT(hnd->image->h); free(mode); } return; } /*** SDL Functions ***/ /** * Initialize SDL and open a window with the specified size. * * The window is opened in fullscreen when the named parameter 'fullscreen' * is passed and has a 'true' (non-zero) value. * * The window is double-buffered when the named parameter 'doublebf' * is passed and has a 'true' (non-zero) value. */ // builtin sdl_init(width, height) static struct spl_node *handler_sdl_init(struct spl_task *task, void UNUSED(*data)) { struct spl_node *hargs = spl_cleanup(task, spl_clib_get_hargs(task)); struct spl_node *hnode; int width = spl_clib_get_int(task); int height = spl_clib_get_int(task); #ifdef ENABLE_PTHREAD_SUPPORT pthread_mutex_lock(&spl_sdl_init_lock); #endif if (spl_sdl_initialized || task->vm->sdl_initialized) { spl_clib_exception(task, "SdlEx", "description", SPL_NEW_PRINTF("SDL already in use"), NULL); goto fin; } if (SDL_Init(SDL_INIT_VIDEO) < 0) { spl_clib_exception(task, "SdlEx", "description", SPL_NEW_PRINTF("Unable to init SDL: %s", SDL_GetError()), NULL); goto fin; } int videoflags = SDL_ANYFORMAT | SDL_HWSURFACE; hnode = spl_lookup(task, hargs, "fullscreen", SPL_LOOKUP_TEST); if (hnode && spl_get_int(hnode)) videoflags |= SDL_FULLSCREEN; hnode = spl_lookup(task, hargs, "doublebuf", SPL_LOOKUP_TEST); if (hnode && spl_get_int(hnode)) videoflags |= SDL_DOUBLEBUF; spl_sdl_root = SDL_SetVideoMode(width, height, 32, videoflags); if (!spl_sdl_root) { spl_clib_exception(task, "SdlEx", "description", SPL_NEW_PRINTF("Unable to init SDL: %s", SDL_GetError()), NULL); SDL_Quit(); goto fin; } SDL_ShowCursor(0); spl_sdl_last_ticks = 0; spl_sdl_last_ticks_init = 1; spl_sdl_initialized = 1; task->vm->sdl_initialized = 1; spl_undumpable_inc(task->vm, undupable_reason); fin: #ifdef ENABLE_PTHREAD_SUPPORT pthread_mutex_unlock(&spl_sdl_init_lock); #endif return 0; } #define CHECK_INIT \ if (!spl_sdl_initialized || !task->vm->sdl_initialized) { \ spl_clib_exception(task, "SdlEx", "description", \ SPL_NEW_PRINTF("SDL not initialized"), \ NULL); \ return 0; \ } /** * Terminate the SDL context and close the window. */ // builtin sdl_quit() static struct spl_node *handler_sdl_quit(struct spl_task *task, void UNUSED(*data)) { // task is a NULL pointer when we // are called from SPL_ABI(spl_mod_sdl_done)() if (task) { CHECK_INIT } for (struct sdl_hnode_data *i = spl_sdl_hnlist; i; i = i->right) sdl_hnode_data_free(i); SDL_Quit(); spl_sdl_root = 0; spl_sdl_last_ticks = 0; spl_sdl_initialized = 0; if (task) { task->vm->sdl_initialized = 0; spl_undumpable_inc(task->vm, undupable_reason); } return 0; } /** * Set the window and icon name. */ // builtin sdl_title(title, icon) static struct spl_node *handler_sdl_title(struct spl_task *task, void UNUSED(*data)) { CHECK_INIT const char *title = spl_clib_get_string(task); const char *icon = spl_clib_get_string(task); SDL_WM_SetCaption(title, icon); return 0; } /** * Delay program execution for the specified number of milliseconds. * * Note that the given number of ms is relative to the end of the last call * to [[sdl_delay()]]. The return code is the effective number of ms the * program execution has been delayed. * * A zero or negative return code means that the function did not sleep and * you are likely to have a timing/performance problem in your application. */ // builtin sdl_delay(ms) static struct spl_node *handler_sdl_delay(struct spl_task *task, void UNUSED(*data)) { CHECK_INIT int delay = spl_clib_get_int(task); int ticks = SDL_GetTicks(); if (spl_sdl_last_ticks_init) { spl_sdl_last_ticks = ticks; spl_sdl_last_ticks_init = 0; } int returncode = (spl_sdl_last_ticks + delay) - ticks; if (returncode > 0) SDL_Delay(returncode); spl_sdl_last_ticks = SDL_GetTicks(); return SPL_NEW_INT(returncode); } /** * Update the window (switch backend framebuffers if the window is created * in doublebuffering mode). */ // builtin sdl_flip() static struct spl_node *handler_sdl_flip(struct spl_task *task, void UNUSED(*data)) { CHECK_INIT SDL_Flip(spl_sdl_root); return 0; } /** * Update the specified area of the window. */ // builtin sdl_update(x, y, w, h) static struct spl_node *handler_sdl_update(struct spl_task *task, void UNUSED(*data)) { CHECK_INIT int x = spl_clib_get_int(task); int y = spl_clib_get_int(task); int w = spl_clib_get_int(task); int h = spl_clib_get_int(task); SDL_UpdateRect(spl_sdl_root, x, y, w, h); return 0; } /** * Load the image from the specified filename and return the image handler. */ // builtin sdl_image_load(filename) static struct spl_node *handler_sdl_image_load(struct spl_task *task, void UNUSED(*data)) { CHECK_INIT const char *filename = spl_clib_get_string(task); struct spl_node *n = sdl_hnode_new(); struct sdl_hnode_data *hnd = n->hnode_data; SDL_Surface *imgbuffer = IMG_Load(filename); if (!imgbuffer) { spl_clib_exception(task, "SdlEx", "description", SPL_NEW_PRINTF("Can't load image '%s': %s", filename, IMG_GetError()), NULL); spl_put(task->vm, n); return 0; } hnd->image = SDL_DisplayFormatAlpha(imgbuffer); SDL_FreeSurface(imgbuffer); return n; } /** * Create an empty image with the specified size. */ // builtin sdl_image_create(w, h) static struct spl_node *handler_sdl_image_create(struct spl_task *task, void UNUSED(*data)) { CHECK_INIT int w = spl_clib_get_int(task); int h = spl_clib_get_int(task); struct spl_node *n = sdl_hnode_new(); struct sdl_hnode_data *hnd = n->hnode_data; Uint32 rmask, gmask, bmask, amask; #if SDL_BYTEORDER == SDL_BIG_ENDIAN rmask = 0xff000000; gmask = 0x00ff0000; bmask = 0x0000ff00; amask = 0x000000ff; #else rmask = 0x000000ff; gmask = 0x0000ff00; bmask = 0x00ff0000; amask = 0xff000000; #endif // It seems like there is no API for allocating a // surface in DisplayFormatAlpha format directly SDL_Surface *imgbuffer = SDL_CreateRGBSurface(0, w, h, 32, spl_sdl_root->format->Rmask, spl_sdl_root->format->Gmask, spl_sdl_root->format->Bmask, spl_sdl_root->format->Amask); hnd->image = SDL_DisplayFormatAlpha(imgbuffer); SDL_FreeSurface(imgbuffer); return n; } /** * Blit the source image to the specified coordinates in the destination * image. If 'undef' is passed as destination image, the image is blitted to * the window and will be displayed after the next call to [[sdl_flip()]]. */ // builtin sdl_blit(dstimg, srcimg, x, y) static struct spl_node *handler_sdl_blit(struct spl_task *task, void UNUSED(*data)) { CHECK_INIT SDL_Surface *dst = clib_get_surface(task, 1); SDL_Surface *src = clib_get_surface(task, 0); if (dst && src) { SDL_Rect srcrect; SDL_Rect dstrect; srcrect.x = 0; srcrect.y = 0; srcrect.w = src->w; srcrect.h = src->h; dstrect.x = spl_clib_get_int(task); dstrect.y = spl_clib_get_int(task); dstrect.w = srcrect.w; dstrect.h = srcrect.h; SDL_BlitSurface(src, &srcrect, dst, &dstrect); } return 0; } /** * Like [[sdl_blit()]], but only blit the specified rectangle. */ // builtin sdl_blitrect(dstimg, srcimg, dest_x, dest_y, src_x, src_y, w, h) static struct spl_node *handler_sdl_blitrect(struct spl_task *task, void UNUSED(*data)) { CHECK_INIT SDL_Surface *dst = clib_get_surface(task, 1); SDL_Surface *src = clib_get_surface(task, 0); if (dst && src) { SDL_Rect srcrect; SDL_Rect dstrect; dstrect.x = spl_clib_get_int(task); dstrect.y = spl_clib_get_int(task); srcrect.x = spl_clib_get_int(task); srcrect.y = spl_clib_get_int(task); dstrect.w = srcrect.w = spl_clib_get_int(task); dstrect.h = srcrect.h = spl_clib_get_int(task); SDL_BlitSurface(src, &srcrect, dst, &dstrect); } return 0; } /** * Copy the the specified rectangle to a new image. The new image is returned. */ // builtin sdl_copy(srcimg, x, y, w, h) static struct spl_node *handler_sdl_copy(struct spl_task *task, void UNUSED(*data)) { CHECK_INIT SDL_Surface *src = clib_get_surface(task, 0); int x = spl_clib_get_int(task); int y = spl_clib_get_int(task); int w = spl_clib_get_int(task); int h = spl_clib_get_int(task); if (src) { struct spl_node *n = sdl_hnode_new(); struct sdl_hnode_data *hnd = n->hnode_data; Uint32 rmask, gmask, bmask, amask; #if SDL_BYTEORDER == SDL_BIG_ENDIAN rmask = 0xff000000; gmask = 0x00ff0000; bmask = 0x0000ff00; amask = 0x000000ff; #else rmask = 0x000000ff; gmask = 0x0000ff00; bmask = 0x00ff0000; amask = 0xff000000; #endif // It seems like there is no API for allocating a // surface in DisplayFormatAlpha format directly SDL_Surface *imgbuffer = SDL_CreateRGBSurface(0, w, h, 32, spl_sdl_root->format->Rmask, spl_sdl_root->format->Gmask, spl_sdl_root->format->Bmask, spl_sdl_root->format->Amask); hnd->image = SDL_DisplayFormatAlpha(imgbuffer); SDL_FreeSurface(imgbuffer); SDL_Rect srcrect; SDL_Rect dstrect; srcrect.x = x; srcrect.y = y; srcrect.w = w; srcrect.h = h; dstrect.x = 0; dstrect.y = 0; dstrect.w = w; dstrect.h = h; SDL_BlitSurface(src, &srcrect, hnd->image, &dstrect); return n; } return 0; } /** * Fill the specified rectangle in the destination image with the specified * RGBA values. (The RGBA values are in the range from 0 to 255.) */ // builtin sdl_fill(dstimg, x, y, w, h, r, g, b, a) static struct spl_node *handler_sdl_fill(struct spl_task *task, void UNUSED(*data)) { CHECK_INIT SDL_Surface *dst = clib_get_surface(task, 1); if (dst) { SDL_Rect dstrect; dstrect.x = spl_clib_get_int(task); dstrect.y = spl_clib_get_int(task); dstrect.w = spl_clib_get_int(task); dstrect.h = spl_clib_get_int(task); int r = spl_clib_get_int(task); int g = spl_clib_get_int(task); int b = spl_clib_get_int(task); int a = spl_clib_get_int(task); Uint32 color = SDL_MapRGBA(dst->format, r, g, b, a); SDL_FillRect(dst, &dstrect, color); } return 0; } /** * Fill the specified rectangle in the destination image with a * pattern. */ // builtin sdl_fill_pattern(dstimg, x, y, w, h, patternimg) static struct spl_node *handler_sdl_fill_pattern(struct spl_task *task, void UNUSED(*data)) { CHECK_INIT SDL_Surface *dst = clib_get_surface(task, 1); if (dst) { int dx = spl_clib_get_int(task); int dy = spl_clib_get_int(task); int dw = spl_clib_get_int(task); int dh = spl_clib_get_int(task); SDL_Surface *src = clib_get_surface(task, 0); SDL_Rect srcrect; srcrect.x = 0; srcrect.y = 0; srcrect.w = src->w; srcrect.h = src->h; for (int x=0; xw) for (int y=0; yh) { SDL_Rect dstrect; dstrect.x = dx + x; dstrect.y = dy + y; dstrect.w = src->w; dstrect.h = src->h; if (dstrect.x + dstrect.w > dx + dw) dstrect.w = dx + dw - dstrect.x; if (dstrect.y + dstrect.h > dy + dh) dstrect.h = dy + dh - dstrect.y; SDL_BlitSurface(src, &srcrect, dst, &dstrect); } } return 0; } /** * Return '1' if the specified key is pressed and '0' if it isn't. The keynames * are listed at: * * http://www.libsdl.org/cgi/docwiki.cgi/SDLKey * * (lowercase versions of the SDLK_* strings without the SDLK_ prefix and with * blanks instead of underscores) */ // builtin sdl_keystat(keyname) static struct spl_node *handler_sdl_keystate(struct spl_task *task, void UNUSED(*data)) { CHECK_INIT const char *keyname = spl_clib_get_string(task); SDL_PumpEvents(); int numkeys; Uint8 *keystates = SDL_GetKeyState(&numkeys); for (int i=0; iimage; if (ihnd && --ihnd->reference_counter == 0) { sdl_hnode_data_free(ihnd); *(ihnd->left ? &ihnd->left->right : &spl_sdl_hnlist) = ihnd->right; if (ihnd->right) ihnd->right->left = ihnd->left; free(ihnd); } sprite_list[idx] = 0; sprite_list_resort = 1; free(hnd); } static void handler_sdl_sprite_node(struct spl_task *task, struct spl_vm UNUSED(*vm), struct spl_node *node, struct spl_hnode_args *args, void UNUSED(*data)) { if (args->action == SPL_HNODE_ACTION_PUT) { struct sdl_sprite_hnode_data *hnd = node->hnode_data; if (!hnd) return; if (hnd->oactive) hnd->schedule_destroy = 1; else destroy_sprite_data(hnd->idx); node->hnode_data = 0; return; } if (args->action == SPL_HNODE_ACTION_LOOKUP || args->action == SPL_HNODE_ACTION_CREATE) { struct sdl_sprite_hnode_data *hnd = node->hnode_data; char *mode = spl_hash_decode(args->key); if (!strcmp(mode, "x") || !strcmp(mode, "y") || !strcmp(mode, "z")) { int *value = 0; switch (*mode) { case 'x': value = &hnd->x; break; case 'y': value = &hnd->y; break; case 'z': value = &hnd->z; break; } if (args->action == SPL_HNODE_ACTION_LOOKUP) args->value = SPL_NEW_INT(*value); if (args->action == SPL_HNODE_ACTION_CREATE) { int new_value = spl_get_int(args->value); if (new_value != *value) { *value = spl_get_int(args->value); if (*mode == 'z') sprite_list_resort = 1; hnd->updated = 1; } } } if (!strcmp(mode, "image") && args->action == SPL_HNODE_ACTION_CREATE) { if (hnd->image) { struct sdl_hnode_data *ihnd = hnd->image; if (ihnd && --ihnd->reference_counter == 0) { sdl_hnode_data_free(ihnd); *(ihnd->left ? &ihnd->left->right : &spl_sdl_hnlist) = ihnd->right; if (ihnd->right) ihnd->right->left = ihnd->left; free(ihnd); } hnd->image = 0; } struct sdl_hnode_data *ihnd = clib_get_hnd(task, args->value); hnd->image = ihnd; if (hnd->image) { hnd->image->reference_counter++; hnd->orev = hnd->image->revision_counter; } hnd->updated = 1; } free(mode); } } static int sort_sprite_list_compar(const void *va, const void *vb) { struct sdl_sprite_hnode_data **pa = (void*)va; struct sdl_sprite_hnode_data **pb = (void*)vb; struct sdl_sprite_hnode_data *a = *pa; struct sdl_sprite_hnode_data *b = *pb; if (!a && !b) return 0; if (!a) return +1; if (!b) return -1; if (a->z < b->z) return -1; if (a->z > b->z) return +1; return 0; } static void sort_sprite_list() { if (!sprite_list_resort) return; qsort(sprite_list, sprite_list_nextfree, sizeof(struct sdl_sprite_hnode_data *), sort_sprite_list_compar); int i; for (i = 0; sprite_list[i] && i < sprite_list_nextfree; i++) sprite_list[i]->idx = i; sprite_list_nextfree = i; sprite_list_resort = 0; } /*** SDL Sprite Functions ***/ /** * Usually, when doing SDL programming in C, everyone is creating his own SDL * sprite library. This is possible when doing SDL programming with SDL to. But * for performance reasons I recommend to use the built-in sprites of this * module. * * This function returns a new sprite object. Such a sprite object has the * following attributes: * * .x * x-coordinate of the sprite (upper left corner) * .y * y-coordinate of the sprite (upper left corner) * * .z * z-coordinate of the sprite. * sprites with higher values overlap sprites with lower values * * .image * the image data for the sprite. * this must be set to one of SDL Image the object such as * returned by [[sdl_image_create()]]. */ // builtin sdl_sprite_create() static struct spl_node *handler_sdl_sprite_create(struct spl_task *task, void UNUSED(*data)) { CHECK_INIT struct sdl_sprite_hnode_data *hnd = calloc(1, sizeof(struct sdl_sprite_hnode_data)); if (sprite_list_nextfree >= sprite_list_roof) { if (sprite_list_roof < 32) sprite_list_roof = 64; else sprite_list_roof = sprite_list_roof * 2; sprite_list = realloc(sprite_list, sprite_list_roof * sizeof(struct sdl_sprite_hnode_data*)); } hnd->idx = sprite_list_nextfree++; sprite_list[hnd->idx] = hnd; sprite_list_resort = 1; struct spl_node *n = SPL_NEW_STRING_DUP("SDL Sprite"); n->hnode_name = strdup("sdl_sprite"); n->hnode_data = hnd; return n; } static void sprite_update_backend(int x, int y, int w, int h, int usexywh) { for (int i = 0; i < sprite_list_nextfree; i++) { if (!usexywh) { if (sprite_list[i]->schedule_destroy) { destroy_sprite_data(i); continue; } if (sprite_list[i]->image) sprite_list[i]->orev = sprite_list[i]->image->revision_counter; sprite_list[i]->updated = 0; } if (!sprite_list[i] || !sprite_list[i]->image || !sprite_list[i]->image->image) continue; struct sdl_sprite_hnode_data *hnd = sprite_list[i]; SDL_Surface *src = hnd->image->image; if (usexywh) { if (hnd->x + src->w < x) continue; if (hnd->y + src->h < y) continue; if (hnd->x > x + w) continue; if (hnd->y > y + h) continue; } SDL_Rect srcrect; SDL_Rect dstrect; if (usexywh) { if (hnd->x < x) { srcrect.x = x - hnd->x; dstrect.x = x; } else { srcrect.x = 0; dstrect.x = hnd->x; } if (hnd->y < y) { srcrect.y = y - hnd->y; dstrect.y = y; } else { srcrect.y = 0; dstrect.y = hnd->y; } srcrect.w = w; srcrect.h = h; dstrect.w = w; dstrect.h = h; } else { srcrect.x = 0; srcrect.y = 0; srcrect.w = src->w; srcrect.h = src->h; dstrect.x = sprite_list[i]->x; dstrect.y = sprite_list[i]->y; dstrect.w = srcrect.w; dstrect.h = srcrect.h; } SDL_BlitSurface(src, &srcrect, spl_sdl_root, &dstrect); } if (usexywh) { if (x < 0) x = 0; if (y < 0) y = 0; if (x + w > spl_sdl_root->w) w = spl_sdl_root->w - x; if (y + h > spl_sdl_root->h) h = spl_sdl_root->h - y; if (w > 0 && h > 0) SDL_UpdateRect(spl_sdl_root, x, y, w, h); } } /** * This redraws all sprites, also those which haven't been updated. * * This function is only needed if you are mixing sprites with direct blitting * to the window. Usually [[sdl_sprite_update()]] is used instead. * * In some corner cases (when many sprites have been updated) this function * might be faster than [[sdl_sprite_update()]]. But this is a very seldom * case. */ // builtin sdl_sprite_redraw() static struct spl_node *handler_sdl_sprite_redraw(struct spl_task *task, void UNUSED(*data)) { CHECK_INIT sort_sprite_list(); sprite_update_backend(0, 0, 0, 0, 0); SDL_UpdateRect(spl_sdl_root, 0, 0, 0, 0); return 0; } /** * This redraws all the updated sprites. */ // builtin sdl_sprite_update() static struct spl_node *handler_sdl_sprite_update(struct spl_task *task, void UNUSED(*data)) { CHECK_INIT sort_sprite_list(); for (int i = 0; i < sprite_list_nextfree; i++) { struct sdl_sprite_hnode_data *hnd = sprite_list[i]; if (hnd->image && hnd->orev != hnd->image->revision_counter) { hnd->orev = hnd->image->revision_counter; hnd->updated = 1; } if (!hnd->updated) continue; if (hnd->oactive && (hnd->x != hnd->ox || hnd->y != hnd->oy || !hnd->image || !hnd->image->image || hnd->image->image->w != hnd->ow || hnd->image->image->h != hnd->oh)) { sprite_update_backend(hnd->ox, hnd->oy, hnd->ow, hnd->oh, 1); } if (hnd->schedule_destroy) { destroy_sprite_data(hnd->idx); continue; } if (!hnd->image || !hnd->image->image) { hnd->updated = 0; hnd->oactive = 0; continue; } sprite_update_backend(hnd->x, hnd->y, hnd->image->image->w, hnd->image->image->h, 1); hnd->ox = hnd->x; hnd->oy = hnd->y; hnd->ow = hnd->image->image->w; hnd->oh = hnd->image->image->h; hnd->updated = 0; hnd->oactive = 1; } return 0; } /*** Initializations ***/ /** * An instance of this object is thrown on SDL errors. */ //object SdlEx /** * A description text describing the error. */ // var description; void SPL_ABI(spl_mod_sdl_init)(struct spl_vm *vm, struct spl_module *mod, int restore) { if (!restore) spl_eval(vm, 0, strdup(mod->name), "object SdlEx { }"); spl_hnode_reg(vm, "sdl", handler_sdl_node, 0); spl_hnode_reg(vm, "sdl_sprite", handler_sdl_sprite_node, 0); spl_clib_reg(vm, "sdl_init", handler_sdl_init, 0); spl_clib_reg(vm, "sdl_quit", handler_sdl_quit, 0); spl_clib_reg(vm, "sdl_title", handler_sdl_title, 0); spl_clib_reg(vm, "sdl_delay", handler_sdl_delay, 0); spl_clib_reg(vm, "sdl_flip", handler_sdl_flip, 0); spl_clib_reg(vm, "sdl_update", handler_sdl_update, 0); spl_clib_reg(vm, "sdl_image_load", handler_sdl_image_load, 0); spl_clib_reg(vm, "sdl_image_create", handler_sdl_image_create, 0); spl_clib_reg(vm, "sdl_blit", handler_sdl_blit, 0); spl_clib_reg(vm, "sdl_blitrect", handler_sdl_blitrect, 0); spl_clib_reg(vm, "sdl_copy", handler_sdl_copy, 0); spl_clib_reg(vm, "sdl_fill", handler_sdl_fill, 0); spl_clib_reg(vm, "sdl_fill_pattern", handler_sdl_fill_pattern, 0); spl_clib_reg(vm, "sdl_keystate", handler_sdl_keystate, 0); spl_clib_reg(vm, "sdl_sprite_create", handler_sdl_sprite_create, 0); spl_clib_reg(vm, "sdl_sprite_redraw", handler_sdl_sprite_redraw, 0); spl_clib_reg(vm, "sdl_sprite_update", handler_sdl_sprite_update, 0); } void SPL_ABI(spl_mod_sdl_done)(struct spl_vm *vm, struct spl_module UNUSED(*mod)) { if (vm->sdl_initialized) { spl_report(SPL_REPORT_HOST, vm, "Missing call to sdl_quit()!\n"); handler_sdl_quit(0, 0); vm->sdl_initialized = 0; } for (int i = 0; i < sprite_list_nextfree; i++) { if (sprite_list[i]) { if (sprite_list[i]->schedule_destroy) { destroy_sprite_data(i); } else { spl_report(SPL_REPORT_HOST, vm, "Found active non-null entry in SDL sprite list on module unload!\n"); goto skip_free_sprite_list; } } } free(sprite_list); sprite_list_resort = 0; sprite_list_nextfree = 0; sprite_list_roof = 0; skip_free_sprite_list: return; }