From 3ce97dbf339a89d14da1620b6449ca4d46be05d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miloslav=20=C4=8C=C3=AD=C5=BE?= Date: Sat, 24 Apr 2021 18:13:22 +0200 Subject: [PATCH] Add linux terminal support --- README.md | 2 +- main_terminal.c | 121 +++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 115 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 1eee8e8..e955a6b 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ This game got some attention on 4chan: [1](https://archive.li/Yzcwt), [2](https: - **Extemely low HW demands** (much less than Doom, no GPU, no FPU, just kilobytes of RAM and storage). - **Suckless, KISS, minimal, simple**, short code (~10000 LOC). - **Extremely portable**. So far officially ported to and tested on: - - GNU/Linux PC: SDL, csfml, terminal + - GNU/Linux PC: SDL, csfml, terminal (experimental), Linux framebuffer (experimental) - Winshit XP SDL - Browser - Pokitto (220 x 116, 48 MHz ARM, 36 KB RAM, 256 KB flash) diff --git a/main_terminal.c b/main_terminal.c index 807ac11..09dbd07 100644 --- a/main_terminal.c +++ b/main_terminal.c @@ -1,5 +1,7 @@ /** - @file main_pokitto.cpp + @file main_terminal.c + + WARNING: VERY EXPERIMENTAL This is Linux terminal implementation of the game front end. If you replace the input methods, it will most likely run in other terminals as well. This @@ -15,6 +17,16 @@ whatsoever. */ +#ifndef USE_LINUX_FRAMEBUFFER + #define USE_LINUX_FRAMEBUFFER 1 +#endif + +// IMPORTANT: You must set these files correctly: +#define DEV_KEYBOARD "/dev/input/event3" +#define DEV_MOUSE "/dev/input/event1" +#define DEV_TTY "/dev/tty3" +#define DEV_FRAMBUFFER "/dev/fb0" + #include #include #include @@ -26,8 +38,22 @@ #include "smallinput.h" -#define SFG_SCREEN_RESOLUTION_X 127 -#define SFG_SCREEN_RESOLUTION_Y 42 +#if USE_LINUX_FRAMEBUFFER + #include + #include + #include + #include + #include +#endif + +#if USE_LINUX_FRAMEBUFFER + #define SFG_SCREEN_RESOLUTION_X 640 + #define SFG_SCREEN_RESOLUTION_Y 480 +#else + #define SFG_SCREEN_RESOLUTION_X 127 + #define SFG_SCREEN_RESOLUTION_Y 42 +#endif + #define SFG_DITHERED_SHADOW 1 #define SFG_FPS 30 @@ -36,6 +62,7 @@ #define SCREENSIZE ((SFG_SCREEN_RESOLUTION_X + 1) * SFG_SCREEN_RESOLUTION_Y + 1) char screen[SCREENSIZE]; +uint8_t *screenUnsigned = (uint8_t *) screen; const char shades[] = // adjust according to your terminal { @@ -54,8 +81,13 @@ uint32_t getTime() void SFG_setPixel(uint16_t x, uint16_t y, uint8_t colorIndex) { +#if USE_LINUX_FRAMEBUFFER + screenUnsigned[y * SFG_SCREEN_RESOLUTION_X + x] = + colorIndex; +#else screen[y * (SFG_SCREEN_RESOLUTION_X + 1) + x] = shades[(colorIndex > 7) * 8 + colorIndex % 8]; +#endif } uint32_t SFG_getTimeMs() @@ -125,7 +157,9 @@ int running = 1; void handleSignal(int signal) { +#if !USE_LINUX_FRAMEBUFFER puts("\033[?25h"); // show cursor +#endif running = 0; } @@ -142,6 +176,38 @@ int main() screen[SCREENSIZE - 1] = 0; // string terminator +#if USE_LINUX_FRAMEBUFFER + + #define CHECK(w,err,msg)\ + if ((w) == (err)) { puts(msg); return 1; } + + int tty = open(DEV_TTY,O_RDWR); + CHECK(tty,-1,"couldn't open TTY device") + + CHECK(ioctl(tty,KDSETMODE,KD_GRAPHICS),-1,"couldn't set graphic mode") + + int fb = open(DEV_FRAMBUFFER,O_RDWR); + CHECK(fb,-1,"couldn't open framebuffer device") + + struct fb_fix_screeninfo fixInfo; + struct fb_var_screeninfo varInfo; + + CHECK(ioctl(fb,FBIOGET_FSCREENINFO,&fixInfo),-1,"couldn't get fixInfo") + CHECK(ioctl(fb,FBIOGET_VSCREENINFO,&varInfo),-1,"couldn't get varInfo") + + int bpp = varInfo.bits_per_pixel / 8; + uint64_t screenSize = varInfo.xres * varInfo.yres * bpp; + + char *fbScreen = mmap(0,screenSize,PROT_READ | PROT_WRITE,MAP_SHARED,fb,0); + + CHECK(fbScreen,MAP_FAILED,"couldn't map framebuffer") + + int r = varInfo.red.offset / 8, + g = varInfo.green.offset / 8, + b = varInfo.blue.offset / 8, + t = varInfo.transp.offset / 8; + +#else for (uint16_t i = 1; i <= SFG_SCREEN_RESOLUTION_Y; ++i) screen[i * (SFG_SCREEN_RESOLUTION_X + 1) - 1] = '\n'; @@ -151,20 +217,61 @@ int main() putchar('\n'); puts("\033[?25l"); // hide cursor - +#endif + while (running) { input_update(); - + +#if USE_LINUX_FRAMEBUFFER + + char *p = fbScreen; + + int linePad = fixInfo.line_length - SFG_SCREEN_RESOLUTION_X * bpp; + + int index = 0; + + for (int y = 0; y < SFG_SCREEN_RESOLUTION_Y; ++y) + { + for (int x = 0; x < SFG_SCREEN_RESOLUTION_X; ++x) + { + // inefficient, should be a precomputed RGB32 palette + uint16_t c = paletteRGB565[screenUnsigned[index]]; + + *(p + b) = (c << 3) & 0xf8; + *(p + g) = (c >> 3) & 0xfc; + *(p + r) = (c >> 8) & 0xf8; + p += bpp; + index++; + } + + p += linePad; + } + +#else puts("\033[0;0H"); // move cursor to 0;0 puts(screen); fflush(stdout); +#endif if (!SFG_mainLoopBody()) running = 0; - } - puts("\033[?25h"); // show cursor +// if (SFG_game.frame > 1000) break; // uncomment for testing, prevents locking + + } input_end(); + +#if USE_LINUX_FRAMEBUFFER + munmap(screen,screenSize); + + ioctl(tty,KDSETMODE,KD_TEXT); // set back to text mode + + close(fb); + close(tty); +#else + puts("\033[?25h"); // show cursor +#endif + }