/* Sarien - A Sierra AGI resource interpreter engine * Copyright (C) 1999-2001 Stuart George and Claudio Matsuoka * * $Id: sdl.c,v 1.26 2001/09/13 02:25:53 cmatsuoka Exp $ * * 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; see docs/COPYING for further details. */ /* SDL support written by Claudio Matsuoka */ /* Sat, 31 Jul 1999 10:32:01 +0200 (MET DST) * SDL full screen mode added by Robert Bihlmeyer * * Wed Dec 22 15:51:22 EDT 1999 * Compile errors fixed by Ryan Gordon * * Sun, 5 Aug 2001 14:05:04 -0700 (PDT) * Mouse support added by Ryan C. Gordon */ #include #include #include #include #include "SDL.h" #include "sarien.h" #include "graphics.h" #include "keyboard.h" extern struct sarien_options opt; static int scale = 1; static int key_control = 0; static int key_alt = 0; static SDL_Surface *screen; SDL_Color color[32]; Uint32 mapped_color[32]; UINT32 clock_ticks; UINT32 clock_count; #define KEY_QUEUE_SIZE 16 static int key_queue[KEY_QUEUE_SIZE]; static int key_queue_start = 0; static int key_queue_end = 0; #define key_enqueue(k) do { key_queue[key_queue_end++] = (k); \ key_queue_end %= KEY_QUEUE_SIZE; } while (0) #define key_dequeue(k) do { (k) = key_queue[key_queue_start++]; \ key_queue_start %= KEY_QUEUE_SIZE; } while (0) static int init_vidmode (void); static int deinit_vidmode (void); static void sdl_put_block (int, int, int, int); static void _putpixels (int, int, int, Uint8 *); static void sdl_timer (void); static Uint32 timer_function (Uint32); int sdl_is_keypress (void); int sdl_get_keypress(void); static volatile UINT32 tick_timer = 0; #define TICK_SECONDS 20 static struct gfx_driver gfx_sdl = { init_vidmode, deinit_vidmode, sdl_put_block, _putpixels, sdl_timer, sdl_is_keypress, sdl_get_keypress }; extern struct gfx_driver *gfx; #define ASPECT_RATIO(x) ((x) * 6 / 5) /* ====================================================================*/ /* Some optimized put_pixel routines for the most common cases */ #define _putpixels_scale1(d) static void INLINE \ _putpixels_##d##bits_scale1 (int x, int y, int w, UINT8 *p) { \ Uint##d *s; \ if (w == 0) return; \ s = (Uint##d *)screen->pixels + x + y * screen->w; \ if (SDL_MUSTLOCK (screen)) { \ if (SDL_LockSurface (screen) < 0) \ return; \ } \ while (w--) { *s++ = mapped_color[*p++]; } \ if (SDL_MUSTLOCK (screen)) SDL_UnlockSurface (screen); \ } #define _putpixels_scale2(d) static void INLINE \ _putpixels_##d##bits_scale2 (int x, int y, int w, UINT8 *p) { \ Uint##d *s, *t; \ if (w == 0) return; \ x <<= 1; y <<= 1; \ s = (Uint##d *)screen->pixels + x + y * screen->w; \ t = s + screen->w; \ if (SDL_MUSTLOCK (screen)) { \ if (SDL_LockSurface (screen) < 0) \ return; \ } \ while (w--) { \ int c = mapped_color[*p]; \ *s++ = c; *s++ = c; *t++ = c; *t++ = c; p++; \ } \ if (SDL_MUSTLOCK (screen)) SDL_UnlockSurface (screen); \ } _putpixels_scale1(8); _putpixels_scale1(16); _putpixels_scale1(32); _putpixels_scale2(8); _putpixels_scale2(16); _putpixels_scale2(32); /* ====================================================================*/ /* Aspect ratio correcting put pixels handlers */ #define _putpixels_fixratio_scale1(d) static void \ _putpixels_fixratio_##d##bits_scale1 (int x, int y, int w, UINT8 *p) { \ if (y > 0 && ASPECT_RATIO (y) - 1 != ASPECT_RATIO (y - 1)) \ _putpixels_##d##bits_scale1 (x, ASPECT_RATIO(y) - 1, w, p);\ _putpixels_##d##bits_scale1 (x, ASPECT_RATIO(y), w, p); \ } #define _putpixels_fixratio_scale2(d) static void INLINE \ _putpixels_fixratio_##d##bits_scale2 (int x, int y, int w, Uint8 *p0) { \ Uint##d *s, *t, *u; Uint8 *p; int extra = 0; \ if (w == 0) return; \ x <<= 1; y <<= 1; \ if (y < ((GFX_WIDTH - 1) << 2) && ASPECT_RATIO (y) + 2 != ASPECT_RATIO (y + 2)) extra = w; \ y = ASPECT_RATIO(y); \ s = (Uint##d *)screen->pixels + x + y * screen->w; \ t = s + screen->w; \ u = t + screen->w; \ if (SDL_MUSTLOCK (screen)) { \ if (SDL_LockSurface (screen) < 0) \ return; \ } \ for (p = p0; w--; p++) { \ int c = mapped_color[*p]; \ *s++ = c; *s++ = c; *t++ = c; *t++ = c; \ } \ for (p = p0; extra--; p++) { \ int c = mapped_color[*p]; \ *u++ = c; *u++ = c; \ } \ if (SDL_MUSTLOCK (screen)) SDL_UnlockSurface (screen); \ } _putpixels_fixratio_scale1(8); _putpixels_fixratio_scale1(16); _putpixels_fixratio_scale1(32); _putpixels_fixratio_scale2(8); _putpixels_fixratio_scale2(16); _putpixels_fixratio_scale2(32); /* ====================================================================*/ /* Slow, non-optimized put pixel routine */ static void inline _put_pixel (int x, int y, int c) { Uint32 pixel; Uint8 *bits, bpp; pixel = mapped_color[c]; bpp = screen->format->BytesPerPixel; bits = ((UINT8 *) screen->pixels) + y * screen->pitch + x * bpp; /* Set the pixel */ switch (bpp) { case 1: *(Uint8 *)(bits) = pixel; break; case 2: *((UINT16 *) (bits)) = (Uint16) pixel; break; case 3:{ UINT8 r, g, b; r = (pixel >> screen->format->Rshift) & 0xff; g = (pixel >> screen->format->Gshift) & 0xff; b = (pixel >> screen->format->Bshift) & 0xff; *((bits) + screen->format->Rshift / 8) = r; *((bits) + screen->format->Gshift / 8) = g; *((bits) + screen->format->Bshift / 8) = b; } break; case 4: *((UINT32 *) (bits)) = (Uint32) pixel; break; } } static void _putpixels (int x, int y, int w, Uint8 *p) { register int c; register int i, j; if (w == 0) return; if (SDL_MUSTLOCK (screen)) { if (SDL_LockSurface (screen) < 0) return; } switch (scale) { case 1: while (w--) { _put_pixel (x++, y, *p++); } break; case 2: x <<= 1; y <<= 1; while (w--) { c = *p++; _put_pixel (x, y, c); _put_pixel (x++, y + 1, c); _put_pixel (x, y, c); _put_pixel (x++, y + 1, c); } break; default: x *= scale; y *= scale; while (w--) { c = *p++; for (i = 0; i < scale; i++) { for (j = 0; j < scale; j++) _put_pixel (x + i, y + j, c); } x += scale; } break; } if (SDL_MUSTLOCK (screen)) SDL_UnlockSurface (screen); } static void _putpixels_fixratio (int x, int y, int w, UINT8 *p) { if (y > 0 && ASPECT_RATIO (y) - 1 != ASPECT_RATIO (y - 1)) _putpixels (x, ASPECT_RATIO(y) - 1, w, p); _putpixels (x, ASPECT_RATIO(y), w, p); } /* ====================================================================*/ static void process_events () { SDL_Event event; int key = 0; while (SDL_PollEvent (&event) > 0) { switch (event.type) { case SDL_MOUSEBUTTONDOWN: key = (event.button.button == 1) ? BUTTON_LEFT : BUTTON_RIGHT; mouse.button = TRUE; key_enqueue (key); /* fall through */ case SDL_MOUSEMOTION: mouse.x = event.button.x / opt.scale; mouse.y = event.button.y / opt.scale; if (opt.fixratio) mouse.y = mouse.y * 5 / 6; break; case SDL_MOUSEBUTTONUP: mouse.button = FALSE; break; case SDL_KEYDOWN: switch (key = event.key.keysym.sym) { case SDLK_LSHIFT: case SDLK_RSHIFT: key = 0; break; case SDLK_LCTRL: key_control |= 1; key = 0; break; case SDLK_RCTRL: key_control |= 2; key = 0; break; case SDLK_LALT: key_alt |= 1; key = 0; break; case SDLK_RALT: key_alt |= 2; key = 0; break; case SDLK_UP: case SDLK_KP8: key = KEY_UP; break; case SDLK_LEFT: case SDLK_KP4: key = KEY_LEFT; break; case SDLK_DOWN: case SDLK_KP2: key = KEY_DOWN; break; case SDLK_RIGHT: case SDLK_KP6: key = KEY_RIGHT; break; case SDLK_HOME: case SDLK_KP7: key = KEY_UP_LEFT; break; case SDLK_KP5: key = KEY_STATIONARY; break; case SDLK_PAGEUP: case SDLK_KP9: key = KEY_UP_RIGHT; break; case SDLK_PAGEDOWN: case SDLK_KP3: key = KEY_DOWN_RIGHT; break; case SDLK_END: case SDLK_KP1: key = KEY_DOWN_LEFT; break; case SDLK_KP_ENTER: key = KEY_ENTER; break; case SDLK_KP_PLUS: key = '+'; break; case SDLK_KP_MINUS: key = '-'; break; case SDLK_TAB: key = 0x0009; break; case SDLK_F1: key = 0x3b00; break; case SDLK_F2: key = 0x3c00; break; case SDLK_F3: key = 0x3d00; break; case SDLK_F4: key = 0x3e00; break; case SDLK_F5: key = 0x3f00; break; case SDLK_F6: key = 0x4000; break; case SDLK_F7: key = 0x4100; break; case SDLK_F8: key = 0x4200; break; case SDLK_F9: key = 0x4300; break; case SDLK_F10: key = 0x4400; break; case SDLK_F11: key = KEY_STATUSLN; break; case SDLK_F12: key = KEY_PRIORITY; break; case SDLK_ESCAPE: key = 0x1b; break; default: if (!isalpha (key)) break; if (key_control) key = (key & ~0x20) - 0x40; else if (key_alt) key = scancode_table[(key & ~0x20) - 0x41] << 8; break; }; if (key) key_enqueue (key); break; case SDL_KEYUP: switch (event.key.keysym.sym) { case SDLK_LCTRL: key_control &= ~1; break; case SDLK_RCTRL: key_control &= ~2; break; case SDLK_LALT: key_alt &= ~1; break; case SDLK_RALT: key_alt &= ~2; break; default: break; } break; } } } int init_machine (int argc, char **argv) { gfx = &gfx_sdl; scale = opt.scale; clock_count = 0; clock_ticks = 0; return err_OK; } int deinit_machine () { return err_OK; } static int init_vidmode () { int i, mode; fprintf (stderr, "sdl: SDL support by claudio@helllabs.org\n"); /* Initialize SDL */ if (SDL_Init (SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER) < 0) { fprintf (stderr, "sdl: can't initialize: %s\n", SDL_GetError ()); return err_Unk; } mode = SDL_SWSURFACE | SDL_ANYFORMAT; if (opt.fullscreen) mode |= SDL_FULLSCREEN; if ((screen = SDL_SetVideoMode (320 * scale, (opt.fixratio ? ASPECT_RATIO(GFX_HEIGHT) : GFX_HEIGHT) * scale, 8, mode)) == NULL) { fprintf (stderr, "sdl: can't set video mode: %s\n", SDL_GetError ()); return err_Unk; } atexit (SDL_Quit); SDL_WM_SetCaption (TITLE " " VERSION, TITLE); tick_timer = 0; SDL_SetTimer (10, timer_function); for (i = 0; i < 32; i++) { color[i].r = palette[i * 3] << 2; color[i].g = palette[i * 3 + 1] << 2; color[i].b = palette[i * 3 + 2] << 2; mapped_color[i] = SDL_MapRGB (screen->format, color[i].r, color[i].g, color[i].b); } SDL_SetColors (screen, color, 0, 32); #define handle_case(d,s) case (d/8): \ gfx_sdl.put_pixels = _putpixels_##d##bits_scale##s; break; #define handle_fixratio_case(d,s) case (d/8): \ gfx_sdl.put_pixels = _putpixels_fixratio_##d##bits_scale##s; break; /* Use an optimized put_pixels if available */ if (!opt.fixratio) { gfx_sdl.put_pixels = _putpixels_fixratio; if (opt.gfxhacks) switch (scale) { case 1: switch (screen->format->BytesPerPixel) { handle_case(8,1); handle_case(16,1); handle_case(32,1); } break; case 2: switch (screen->format->BytesPerPixel) { handle_case(8,2); handle_case(16,2); handle_case(32,2); } break; } } else { if (opt.gfxhacks) switch (scale) { case 1: switch (screen->format->BytesPerPixel) { handle_fixratio_case(8,1); handle_fixratio_case(16,1); handle_fixratio_case(32,1); } break; case 2: switch (screen->format->BytesPerPixel) { handle_fixratio_case(8,2); handle_fixratio_case(16,2); handle_fixratio_case(32,2); } break; } } return err_OK; } static int deinit_vidmode () { _D ("()"); SDL_Quit (); return err_OK; } /* put a block onto the screen */ static void sdl_put_block (int x1, int y1, int x2, int y2) { if (x1 >= GFX_WIDTH) x1 = GFX_WIDTH - 1; if (y1 >= GFX_HEIGHT) y1 = GFX_HEIGHT - 1; if (x2 >= GFX_WIDTH) x2 = GFX_WIDTH - 1; if (y2 >= GFX_HEIGHT) y2 = GFX_HEIGHT - 1; if (scale > 1) { x1 *= scale; y1 *= scale; x2 = (x2 + 1) * scale - 1; y2 = (y2 + 1) * scale - 1; } if (opt.fixratio) { SDL_UpdateRect (screen, x1, ASPECT_RATIO(y1), x2 - x1 + 1, ASPECT_RATIO (y2 + 1) - ASPECT_RATIO(y1)); } else { SDL_UpdateRect (screen, x1, y1, x2 - x1 + 1, y2 - y1 + 1); } } int sdl_is_keypress () { process_events (); return key_queue_start != key_queue_end; } int sdl_get_keypress () { int k; while (key_queue_start == key_queue_end) /* block */ sdl_timer (); key_dequeue(k); return k; } static Uint32 timer_function (Uint32 i) { tick_timer++; return i; } static void sdl_timer () { static UINT32 m = 0; UINT32 dm; if (tick_timer < m) m = 0; while ((dm = tick_timer - m) < 5) { SDL_Delay (5); } m = tick_timer; process_events (); }