aboutsummaryrefslogtreecommitdiff
path: root/screen.c
diff options
context:
space:
mode:
Diffstat (limited to 'screen.c')
-rw-r--r--screen.c289
1 files changed, 289 insertions, 0 deletions
diff --git a/screen.c b/screen.c
new file mode 100644
index 0000000..2176cb4
--- /dev/null
+++ b/screen.c
@@ -0,0 +1,289 @@
+/*
+ * screen.c -- Abstracción sobre SDL para la entrada y salida gráfica.
+ * Copyright (C) 2018 Juan Marín Noguera
+ *
+ * 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 3 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, see <https://www.gnu.org/licenses/>.
+ */
+#include <malloc.h>
+#include <SDL2/SDL.h>
+#include <string.h>
+#include "error.h"
+#include "screen.h"
+
+#define TEXT_X 3
+#define TEXT_Y 460
+#define TEXT_FILE "56929.bmp"
+#define TEXT_H 8
+#define TEXT_W 8
+#define TEXT_PER_ROW 16
+
+struct Picture {
+ SDL_Surface *s;
+ SDL_Surface *f;
+};
+
+struct Screen {
+ SDL_Window *window;
+ SDL_Renderer *renderer;
+ SDL_Surface *surface;
+};
+
+Picture screen_text_bmp = NULL;
+
+static void screen_init()
+{
+ if (!SDL_WasInit(SDL_INIT_VIDEO) && SDL_Init(SDL_INIT_VIDEO) < 0)
+ error_sdl_exit();
+}
+
+Screen screen_create(const char *caption, int w, int h)
+{
+ Screen scr;
+
+ if (w <= 0 || h <= 0)
+ error_exit(ERR_OUT_OF_RANGE);
+
+ scr = malloc(sizeof(struct Screen));
+ if (scr == NULL)
+ error_libc_exit();
+
+ screen_init();
+
+ scr->window = SDL_CreateWindow(
+ caption,
+ SDL_WINDOWPOS_CENTERED,
+ SDL_WINDOWPOS_CENTERED,
+ w,
+ h,
+ 0);
+ if (scr->window == NULL)
+ error_sdl_exit();
+
+ scr->surface = SDL_GetWindowSurface(scr->window);
+ if (scr->surface == NULL)
+ error_sdl_exit();
+
+ scr->renderer = SDL_CreateSoftwareRenderer(scr->surface);
+ if (scr->renderer == NULL)
+ error_sdl_exit();
+
+ if (SDL_SetRenderDrawBlendMode(scr->renderer, SDL_BLENDMODE_BLEND) ==
+ -1)
+ error_sdl_exit();
+
+ return scr;
+}
+
+void screen_free(Screen scr)
+{
+ SDL_DestroyWindow(scr->window);
+ SDL_DestroyRenderer(scr->renderer);
+}
+
+void screen_end()
+{
+ if (screen_text_bmp != NULL)
+ screen_free_picture(screen_text_bmp);
+ SDL_Quit();
+}
+
+
+int screen_width(Screen scr)
+{
+ return scr->surface->w;
+}
+
+int screen_height(Screen scr)
+{
+ return scr->surface->h;
+}
+
+void screen_wait(int ms)
+{
+ SDL_Delay(ms);
+}
+
+void screen_update(Screen scr)
+{
+ if (SDL_UpdateWindowSurface(scr->window) == -1)
+ error_sdl_exit();
+}
+
+int screen_get_keyboard_event(SDL_Scancode *sc)
+{
+ SDL_Event event;
+ while (SDL_PollEvent(&event))
+ switch (event.type) {
+ case SDL_KEYDOWN:
+ if (event.key.repeat)
+ break;
+ if (sc != NULL)
+ *sc = event.key.keysym.scancode;
+ return SCREEN_KEYDOWN;
+ case SDL_KEYUP:
+ if (event.key.repeat)
+ break;
+ if (sc != NULL)
+ *sc = event.key.keysym.scancode;
+ return SCREEN_KEYUP;
+ case SDL_QUIT:
+ exit(0);
+ }
+
+ return 0;
+}
+
+int screen_key_pressed(SDL_Scancode scancode)
+{
+ int len;
+ const uint8_t *state;
+
+ SDL_PumpEvents();
+ state = SDL_GetKeyboardState(&len);
+ if (len <= scancode || scancode < 0)
+ error_exit(ERR_INVALID_SCANCODE);
+ return state[scancode];
+}
+
+void screen_fill(Screen scr, int r, int g, int b, int a)
+{
+ if (SDL_SetRenderDrawColor(scr->renderer, r, g, b, a) == -1)
+ error_sdl_exit();
+ if (SDL_RenderClear(scr->renderer) == -1)
+ error_sdl_exit();
+}
+
+void screen_place(Screen scr, Picture pict, int x, int y, int sx, int sy,
+ int w, int h, int flipped)
+{
+ SDL_Rect src, dst;
+ int total_width = screen_width(scr), total_height = screen_height(scr);
+ SDL_Surface *surface;
+
+ if (flipped) {
+ sx = pict->f->w - sx - w;
+ surface = pict->f;
+ } else {
+ surface = pict->s;
+ }
+
+ if (x > total_width || y > total_height || x + w <= 0 || y + h <= 0)
+ return; // Nothing to do here
+
+ src.x = x < 0 ? sx - x : sx;
+ src.y = y < 0 ? sy - y : sy;
+ src.w = x < 0 ? w + x :
+ x + w > total_width ? total_width - x :
+ w;
+ src.h = y < 0 ? h + y :
+ y + h > total_height ? total_height - y:
+ h;
+ dst.x = x < 0 ? 0 : x;
+ dst.y = y < 0 ? 0 : y;
+ dst.w = src.w;
+ dst.h = src.h;
+
+ if (SDL_BlitSurface(surface, &src, scr->surface, &dst) == -1)
+ error_sdl_exit();
+}
+
+Picture screen_get_picture(const char *file)
+{
+ SDL_Surface *raw, *converted, *flipped;
+ SDL_PixelFormat *pxf;
+ Picture pict = malloc(sizeof(struct Picture));
+
+ if (pict == NULL)
+ error_libc_exit();
+
+ raw = SDL_LoadBMP(file);
+ if (raw == NULL)
+ error_sdl_exit();
+
+ pxf = SDL_AllocFormat(SDL_PIXELFORMAT_ARGB8888);
+ if (pxf == NULL)
+ error_sdl_exit();
+
+ converted = SDL_ConvertSurface(raw, pxf, 0);
+ if (converted == NULL)
+ error_sdl_exit();
+
+ SDL_FreeFormat(pxf);
+ SDL_FreeSurface(raw);
+
+ flipped = SDL_CreateRGBSurface(0, raw->w, raw->h, 32,
+ pxf->Rmask, pxf->Gmask, pxf->Bmask, pxf->Amask);
+ if (flipped == NULL)
+ error_sdl_exit();
+ for (int y = 0; y < raw->h; y++)
+ for (int x = 0; x < raw->w; x++)
+ ((int*)flipped->pixels)[(y + 1) * raw->w - x - 1] =
+ ((int*)converted->pixels)[y * raw->w + x];
+ pict->s = converted;
+ pict->f = flipped;
+ return pict;
+}
+
+void screen_free_picture(Picture pict)
+{
+ SDL_FreeSurface(pict->f);
+ SDL_FreeSurface(pict->s);
+ free(pict);
+}
+
+void screen_print_char(Screen scr, int x, int y, char c)
+{
+ int i;
+ if (c >= '0' && c <= '9')
+ i = c - '0';
+ else if (c >= 'a' && c <= 'z')
+ i = c - 'a' + 10;
+ else if (c >= 'A' && c <= 'Z')
+ i = c - 'A' + 10;
+ else if (c == '.')
+ i = 36; // This is the copyright mark.
+ else if (c == '-')
+ i = 40;
+ else if (c == '*')
+ i = 41;
+ else if (c == '!')
+ i = 43;
+ else
+ return;
+ screen_place(scr, screen_text_bmp, x, y,
+ TEXT_X + (i % TEXT_PER_ROW) * TEXT_W,
+ TEXT_Y + (i / TEXT_PER_ROW) * TEXT_H,
+ TEXT_W,
+ TEXT_H,
+ 0);
+}
+
+void screen_text_print(Screen scr, int x, int y, int w, const char *str)
+{
+ if (screen_text_bmp == NULL)
+ screen_text_bmp = screen_get_picture(TEXT_FILE);
+
+ for (int i = 0; str[i] != '\0'; i++)
+ screen_print_char(
+ scr,
+ x + (w != 0 ? i % w : i) * TEXT_W,
+ y + (w != 0 ? i / w : 0) * TEXT_H,
+ str[i]);
+}
+
+void screen_text_centered(Screen scr, int x, int y, const char *str)
+{
+ int len = strlen(str);
+ screen_text_print(scr, x - len * TEXT_W / 2, y, 0, str);
+}