From 1b0c8fb53a4f3335dc9132e4beabe6602f682ea5 Mon Sep 17 00:00:00 2001 From: Ludovic Pouzenc Date: Sat, 14 Sep 2019 19:51:37 +0200 Subject: Use autotools instead of hand-written Makefile --- .gitignore | 27 ++++ Makefile | 18 --- Makefile.am | 2 + README | 20 ++- autogen.sh | 4 + configure.ac | 39 +++++ src/Makefile.am | 6 + src/demoscene-eo.c | 456 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.c | 456 ----------------------------------------------------- src/main.h | 1 + 10 files changed, 554 insertions(+), 475 deletions(-) delete mode 100644 Makefile create mode 100644 Makefile.am create mode 100755 autogen.sh create mode 100644 configure.ac create mode 100644 src/Makefile.am create mode 100644 src/demoscene-eo.c delete mode 100644 src/main.c diff --git a/.gitignore b/.gitignore index 3d1317d..a31b808 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,28 @@ demoscene-eo +*.la +*.lo +*.o +*~ +.deps +.libs +Makefile +Makefile.in +aclocal.m4 +autom4te.cache +autoscan.log +compile +config.guess +config.h +config.h.in +config.log +config.status +config.sub +configure +depcomp +install-sh +libtool +ltmain.sh +m4/*.m4 +missing +obj* +stamp-* diff --git a/Makefile b/Makefile deleted file mode 100644 index 6aebd5e..0000000 --- a/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -CFLAGS := -Wall $(CFLAGS) -#CFLAGS := -Wall -Werror -g -DDEBUG $(CFLAGS) -PKGLIBS:= caca sdl2 glu - -all: demoscene-eo - -server: demoscene-eo - xterm -e $(SHELL) -c "sleep 1; nc -v localhost 51914; exec $(SHELL)" & - CACA_DRIVER=raw CACA_GEOMETRY=80x24 ./demoscene-eo | cacaserver - -demoscene-eo: src/main.c src/main.h $(wildcard src/scene*.[ch]) Makefile - pkg-config --libs --cflags $(PKGLIBS) - gcc $(CFLAGS) `pkg-config --libs --cflags $(PKGLIBS)` src/main.c $(wildcard src/scene*.c) -pthread -o $@ - -clean: - rm -f demoscene-eo - -.PHONY=all clean run diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..bfd4c58 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,2 @@ +SUBDIRS = src +#dist_man_MANS = demoscene-eo.1 diff --git a/README b/README index aa92423..3966b66 100644 --- a/README +++ b/README @@ -1,3 +1,21 @@ +# Build dependencies +- linux and glibc (pthreads, mmap...), CACA, SDL2, GL, GLU, autotools, a decent C compiler +- mainly tested on Debian 10.1 amd64 +- sudo apt install automake autoconf-archive autotools-dev build-essential libcaca-dev libsdl2-dev libglu1-mesa-dev -apt install libcaca-dev libsdl2-dev libglu1-mesa-dev +# To enjoy it : +git clone https://www.pouzenc.fr/cgit/demoscene-eo/ +cd demoscene-eo +./autogen.sh +./configure +make +src/demoscene-eo + +# To improve it : +git clone git@pouzenc.fr:demoscene-eo +cd demoscene-eo +./autogen.sh +CFLAGS="-Wall -Werror -DDEBUG -g" ./configure +make +src/demoscene-eo diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..0a2ae13 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,4 @@ +#!/bin/sh +srcdir=`dirname "$0"` +test -z "$srcdir" || cd "$srcdir" +autoreconf -v --install diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..d9721db --- /dev/null +++ b/configure.ac @@ -0,0 +1,39 @@ +AC_PREREQ([2.65]) +AC_INIT([demoscene-eo], 0.1, [https://www.pouzenc.fr/cgit/demoscene-eo], demoscene-eo) +AM_INIT_AUTOMAKE([foreign dist-bzip2]) +AM_EXTRA_RECURSIVE_TARGETS([server]) + +AC_CONFIG_SRCDIR([src/demoscene-eo.c]) +AC_CONFIG_HEADERS([config.h]) +AC_CONFIG_MACRO_DIR([m4]) + +# Checks for programs. +AC_PROG_CC +AC_PROG_INSTALL + +# Checks for libraries. +AC_SEARCH_LIBS(caca_create_display, [caca]) +AC_SEARCH_LIBS(SDL_Init, [SDL2]) +AC_SEARCH_LIBS(glCompileShader, [GL]) +AC_SEARCH_LIBS(gluLookAt, [glu]) + +# Checks for header files. +AC_CHECK_HEADERS([stdlib.h strings.h sys/mman.h semaphore.h unistd.h sys/wait.h errno.h stdint.h stdio.h signal.h]) + +# Checks for typedefs, structures, and compiler characteristics. +AC_C_INLINE +AC_TYPE_UINT32_T + +# Checks for library functions. +AC_FUNC_FORK +AC_FUNC_MMAP +AC_CHECK_FUNCS([alarm atexit bzero]) + +ACX_PTHREAD +LIBS="$PTHREAD_LIBS $LIBS" +CFLAGS="$CFLAGS $PTHREAD_CFLAGS" +CC="$PTHREAD_CC" + +AC_CONFIG_FILES([Makefile src/Makefile]) +AC_OUTPUT + diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..d9f8a72 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,6 @@ +bin_PROGRAMS = demoscene-eo +demoscene_eo_SOURCES = demoscene-eo.c scene00.c scene01.c scene02.c + +server-local: demoscene-eo + xterm -e $(SHELL) -c "sleep 1; nc -v localhost 51914; exec $(SHELL)" & + CACA_DRIVER=raw CACA_GEOMETRY=80x24 ./demoscene-eo | cacaserver diff --git a/src/demoscene-eo.c b/src/demoscene-eo.c new file mode 100644 index 0000000..94db4e5 --- /dev/null +++ b/src/demoscene-eo.c @@ -0,0 +1,456 @@ +/* + * demoscene-eo, an ASCII art demoscene written as a gift for Emmanuel Otton retirement + * Copyright (C) 2019 Ludovic Pouzenc + * + * This file is part of demoscene-eo. + * + * demoscene-eo 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. + * + * demoscene-eo 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 demoscene-eo. If not, see + */ +#include "main.h" +#include "scene00.h" +#include "scene01.h" +#include "scene02.h" + +#include // calloc() +#include // bzero() +#include // mmap() +#include // sem_t, sem_init(), Link with -pthread +#include // fork() +#include // wait() +#include // errno +#include // uint32_t +#include // printf() +#include // sigaction() + +#ifdef DEBUG_SEM +#define TRACE_SEM(sem,op,hint) printf("%s(): %s(%s) %s\n", __func__, op, #sem, hint) +#else +#define TRACE_SEM(sem,op,hint) +#endif + +#define SCENE_COUNT 3 +#define SCENE_NEXT do { shm->scene = (shm->scene+1)%SCENE_COUNT; } while(0) + +#define SEM_POST(sem,errcode) \ + do { \ + TRACE_SEM(sem,"sem_post",""); \ + if (sem_post(sem) == -1) { return errcode; } \ + } while(0) + +#define SEM_WAIT(sem,errcode) \ + do { \ + TRACE_SEM(sem,"sem_wait","call"); \ + while (sem_wait(sem) == -1) { \ + switch(errno) { \ + case EINTR: \ + case EAGAIN: \ + break; \ + default: \ + return errcode; \ + } \ + } \ + TRACE_SEM(sem,"sem_wait","done"); \ + } while(0) + +typedef struct { + sem_t worker_gl_can_render, worker_sdl_can_render, parent_can_read_result; + int scene, paused, done; + graphical_env_t ge; + scene00_env_t s00e; + scene01_env_t s01e; + scene02_env_t s02e; +} shm_t; + +int do_fork1(); +int do_fork2(); +int parent(); +int worker_sdl(); +int worker_gl(); + +static int skip = 0; +shm_t *shm; + +int main() { + TRACE("call"); + shm = mmap(NULL, sizeof(shm_t), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, 0, 0); + if (shm == NULL) return 1; + bzero(shm, sizeof(shm_t)); + + if (sem_init(&shm->worker_gl_can_render, 1, 0) < 0) return 2; + if (sem_init(&shm->worker_sdl_can_render, 1, 0) < 0) return 3; + if (sem_init(&shm->parent_can_read_result, 1, 0) < 0) return 4; + + return do_fork1(); +} + +int do_fork1() { + TRACE("call"); + fflush(stdout); + switch (fork()) { + case -1: return 4; break; + case 0: return worker_sdl(); break; + default: return do_fork2(); break; + } +} + +int do_fork2() { + TRACE("call"); + fflush(stdout); + switch (fork()) { + case -1: return 5; break; + case 0: return worker_gl(); break; + default: return parent(); break; + } +} + +int parent() { + int lastscene=-1, res, child_status; + caca_event_t caca_ev; + + TRACE("call"); + + // Initialize libcaca (http://caca.zoy.org/doxygen/libcaca/caca_8h.html) + shm->ge.dp = caca_create_display(NULL); + if(!shm->ge.dp) return 1; + caca_set_display_title(shm->ge.dp, "demoscene-eo"); + caca_set_display_time(shm->ge.dp, 41666); // 1e6µs/24 = 41666.66... It is ~24 fps + shm->ge.cv = caca_get_canvas(shm->ge.dp); + + shm->ge.d = caca_create_dither(32, FBUF_W, FBUF_H, FBUF_W*4, 0x00ff0000, 0x0000ff00, 0x000000ff, 0); + if (!shm->ge.d) return 2; + caca_set_dither_color(shm->ge.d, "16"); + + // Main libcaca loop for caca window + shm->ge.framecount=0; + do { + + // Check canvas size at every frame because of unreliable CACA_EVENT_RESIZE + shm->ge.w = caca_get_canvas_width(shm->ge.cv); + shm->ge.h = caca_get_canvas_height(shm->ge.cv); + if ( EXPR_MIN_SIZE ) { + caca_set_color_ansi(shm->ge.cv, CACA_BLACK, CACA_WHITE); + caca_put_str(shm->ge.cv, 0, 0, "Need a minimum of " TEXT_MIN_SIZE " terminal"); + } else if (!shm->paused) { + // init / free if scene transition + if (lastscene != shm->scene) { + switch(lastscene) { + case 0: scene00_free_caca(&shm->ge, &shm->s00e); break; + case 1: scene01_free_caca(&shm->ge, &shm->s01e); break; + case 2: scene02_free_caca(&shm->ge, &shm->s02e); break; + } + } + while (lastscene != shm->scene) { + shm->ge.sdl_ticks = SDL_GetTicks(); + shm->ge.sc_framecount = 0; + switch(shm->scene) { + case 0: res = scene00_init_caca(&shm->ge, &shm->s00e); break; + case 1: res = scene01_init_caca(&shm->ge, &shm->s01e); break; + case 2: res = scene02_init_caca(&shm->ge, &shm->s02e); break; + } + // If scene init fail, skip to the next one + if (res) SCENE_NEXT; else lastscene = shm->scene; + } + shm->ge.sdl_ticks = SDL_GetTicks(); // This value wraps if the program runs for more than ~49 days + + SEM_POST(&shm->worker_gl_can_render,30); + SEM_WAIT(&shm->parent_can_read_result,31); + + SEM_POST(&shm->worker_sdl_can_render,32); + SEM_WAIT(&shm->parent_can_read_result,33); + + // Compute current scene frame (caca part) + switch(shm->scene) { + case 0: res = scene00_next_caca(&shm->ge, &shm->s00e); break; + case 1: res = scene01_next_caca(&shm->ge, &shm->s01e); break; + case 2: res = scene02_next_caca(&shm->ge, &shm->s02e); break; + } + if (res) SCENE_NEXT; + shm->ge.framecount++; + shm->ge.sc_framecount++; + } + // Display it + caca_refresh_display(shm->ge.dp); // Auto framerate limiting (see caca_set_display_time()) + + // Event handling for the libcaca "window" (depending on CACA_DRIVER env variable) + if (caca_get_event(shm->ge.dp, CACA_EVENT_KEY_PRESS|CACA_EVENT_RESIZE|CACA_EVENT_QUIT, &caca_ev, 0)) { + switch(caca_get_event_type(&caca_ev)) { + case CACA_EVENT_QUIT: + shm->done = 1; + break; + case CACA_EVENT_KEY_PRESS: + switch(caca_get_event_key_ch(&caca_ev)) { + case 'q': shm->done = 1; break; + case 'p': shm->paused = !shm->paused; break; + case CACA_KEY_ESCAPE: SCENE_NEXT; break; + } + break; + /* On Debian 10, no CACA_EVENT_RESIZE fired when using x11 CACA_DRIVER + case CACA_EVENT_RESIZE: + w = caca_get_event_resize_width(&caca_ev); + h = caca_get_event_resize_height(&caca_ev); + small = EXPR_MIN_SIZE; + break; + */ + default: + break; + } + } + } while(!shm->done); + + caca_free_dither(shm->ge.d); + caca_free_display(shm->ge.dp); + shm->ge.cv=NULL; + + // reap forked processes (order doesn't matter) + if (wait(&child_status) != -1) { + if (child_status > 0) return child_status; + } + if (wait(&child_status) != -1) { + if (child_status > 0) return child_status; + } + return 0; +} + +static void worker_sdl_sighandler(int sig) { + TRACE("call"); + if ( sig == SIGALRM ) { + skip = 1; + sem_post(&shm->worker_sdl_can_render); + } +} + +int worker_sdl() { + int lastscene=-1, res; + struct sigaction sa; +#ifdef DEBUG + Uint32 sdl_win_flags = 0; //XXX for final version, consider adding SDL_WINDOW_HIDDEN + SDL_Event sdl_ev; +#else + Uint32 sdl_win_flags = SDL_WINDOW_HIDDEN; +#endif + SDL_RendererInfo renderer_info; + + TRACE("call"); + + sa.sa_handler = worker_sdl_sighandler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + if (sigaction(SIGALRM, &sa, NULL) == -1) return 2; + + // Initialize SDL (http://wiki.libsdl.org/SDL_CreateWindowAndRenderer) + // Useful snippet : https://gist.github.com/koute/7391344 + res = SDL_Init(SDL_INIT_VIDEO); + if (res == -1) return 3; + atexit(SDL_Quit); + + res = SDL_CreateWindowAndRenderer(FBUF_W, FBUF_H, sdl_win_flags, &shm->ge.sdl_win, &shm->ge.sdl_rndr); + if (res == -1) return 4; + SDL_SetWindowTitle(shm->ge.sdl_win, "SDL debug"); + res = SDL_GetRendererInfo(shm->ge.sdl_rndr, &renderer_info); + if (res < 0) return 5; + if (!(renderer_info.flags & SDL_RENDERER_ACCELERATED)) return 6; + if (!(renderer_info.flags & SDL_RENDERER_TARGETTEXTURE)) return 7; + shm->ge.sdl_target = SDL_CreateTexture(shm->ge.sdl_rndr, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, FBUF_W, FBUF_H); + if (shm->ge.sdl_target == NULL) return 9; + + + while (!shm->done) { + alarm(1); + SEM_WAIT(&shm->worker_sdl_can_render,20); + alarm(0); + + if (!shm->paused && !skip) { + // init / free if scene transition + if (lastscene != shm->scene) { + switch(lastscene) { + case 0: scene00_free_sdl(&shm->ge, &shm->s00e); break; + case 1: scene01_free_sdl(&shm->ge, &shm->s01e); break; + case 2: scene02_free_sdl(&shm->ge, &shm->s02e); break; + } + } + while (lastscene != shm->scene ) { + shm->ge.sdl_ticks = SDL_GetTicks(); + shm->ge.sc_framecount = 0; + switch(shm->scene) { + case 0: res = scene00_init_sdl(&shm->ge, &shm->s00e); break; + case 1: res = scene01_init_sdl(&shm->ge, &shm->s01e); break; + case 2: res = scene02_init_sdl(&shm->ge, &shm->s02e); break; + } + // If scene init fail, skip to the next one + if (res) SCENE_NEXT; else lastscene = shm->scene; + } + // Compute current scene frame (sdl part) + switch(shm->scene) { + case 0: res = scene00_next_sdl(&shm->ge, &shm->s00e); break; + case 1: res = scene01_next_sdl(&shm->ge, &shm->s01e); break; + case 2: res = scene02_next_sdl(&shm->ge, &shm->s02e); break; + } + if (res) SCENE_NEXT; + } + if (skip) skip = 0; + +#ifdef DEBUG + // Event handling for the SDL window (debug purposes) + while(SDL_PollEvent(&sdl_ev)) { + switch(sdl_ev.type) { + case SDL_QUIT: + shm->done = 1; + break; + case SDL_KEYDOWN: + switch(sdl_ev.key.keysym.sym) { + case SDLK_q: shm->done = 1; break; + case SDLK_p: shm->paused = !shm->paused; break; + case SDLK_ESCAPE: SCENE_NEXT; break; + } + break; + default: + break; + } + } +#endif + SEM_POST(&shm->parent_can_read_result, 21); + } + + SDL_DestroyRenderer(shm->ge.sdl_rndr); + SDL_DestroyTexture(shm->ge.sdl_target); + SDL_DestroyWindow(shm->ge.sdl_win); + + return 0; +} + +static void worker_gl_sighandler(int sig) { + TRACE("call"); + if ( sig == SIGALRM ) { + skip = 1; + sem_post(&shm->worker_gl_can_render); + } +} + +int worker_gl() { + int lastscene=-1, res; + struct sigaction sa; +#ifdef DEBUG + Uint32 sdl_win_flags = SDL_WINDOW_OPENGL; + SDL_Event gl_ev; +#else + Uint32 sdl_win_flags = SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN; +#endif + SDL_RendererInfo renderer_info; + + TRACE("call"); + + sa.sa_handler = worker_gl_sighandler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + if (sigaction(SIGALRM, &sa, NULL) == -1) return 2; + + // Initialize SDL for GL. Useful snippet : https://gist.github.com/koute/7391344 + res = SDL_Init(SDL_INIT_VIDEO); + if (res == -1) return 3; + atexit(SDL_Quit); + + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1); + SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + + // Initialize OpenGL + //shm->ge.gl_win = SDL_CreateWindow("GL Debug", SDL_WINDOWPOS_CENTERED, 0, FBUF_W, FBUF_H, sdl_win_flags); + + res = SDL_CreateWindowAndRenderer(FBUF_W, FBUF_H, sdl_win_flags, &shm->ge.gl_win, &shm->ge.gl_rndr); + if (res == -1) return 4; + SDL_SetWindowTitle(shm->ge.gl_win, "GL debug"); + res = SDL_GetRendererInfo(shm->ge.gl_rndr, &renderer_info); + if (res < 0) return 5; + if (!(renderer_info.flags & SDL_RENDERER_ACCELERATED)) return 6; + if (!(renderer_info.flags & SDL_RENDERER_TARGETTEXTURE)) return 7; + shm->ge.gl_target = SDL_CreateTexture(shm->ge.gl_rndr, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, FBUF_W, FBUF_H); + if (shm->ge.gl_target == NULL) return 9; + shm->ge.gl_ctx = SDL_GL_CreateContext(shm->ge.gl_win); + if (shm->ge.gl_ctx == NULL) return 11; +/* + glBindFramebuffer(GL_FRAMEBUFFER,0); + glClearColor(0,0,0,1); + glClear(GL_COLOR_BUFFER_BIT); + SDL_GL_SwapWindow(shm->ge.gl_win); +*/ + while (!shm->done) { + alarm(1); + SEM_WAIT(&shm->worker_gl_can_render,10); + alarm(0); + + if (!shm->paused && !skip) { + // init / free if scene transition + if (lastscene != shm->scene) { + switch(lastscene) { + case 0: scene00_free_gl(&shm->ge, &shm->s00e); break; + case 1: scene01_free_gl(&shm->ge, &shm->s01e); break; + case 2: scene02_free_gl(&shm->ge, &shm->s02e); break; + } + } + while (lastscene != shm->scene) { + shm->ge.sdl_ticks = SDL_GetTicks(); + shm->ge.sc_framecount = 0; + switch(shm->scene) { + case 0: res = scene00_init_gl(&shm->ge, &shm->s00e); break; + case 1: res = scene01_init_gl(&shm->ge, &shm->s01e); break; + case 2: res = scene02_init_gl(&shm->ge, &shm->s02e); break; + } + // If scene init fail, skip to the next one + if (res) SCENE_NEXT; else lastscene = shm->scene; + } + + // Compute current scene frame (gl part) + switch(shm->scene) { + case 0: res = scene00_next_gl(&shm->ge, &shm->s00e); break; + case 1: res = scene01_next_gl(&shm->ge, &shm->s01e); break; + case 2: res = scene02_next_gl(&shm->ge, &shm->s02e); break; + } + if (res) SCENE_NEXT; + } + if (skip) skip = 0; + +#ifdef DEBUG + // Event handling for the GL window (debug purposes) + while(SDL_PollEvent(&gl_ev)) { + switch(gl_ev.type) { + case SDL_QUIT: + shm->done = 1; + break; + case SDL_KEYDOWN: + switch(gl_ev.key.keysym.sym) { + case SDLK_q: shm->done = 1; break; + case SDLK_p: shm->paused = !shm->paused; break; + case SDLK_ESCAPE: SCENE_NEXT; break; + } + break; + default: + break; + } + } +#endif + SEM_POST(&shm->parent_can_read_result, 11); + } + + SDL_DestroyTexture(shm->ge.gl_target); + SDL_GL_DeleteContext(shm->ge.gl_ctx); + SDL_DestroyWindow(shm->ge.gl_win); + + return 0; +} diff --git a/src/main.c b/src/main.c deleted file mode 100644 index 94db4e5..0000000 --- a/src/main.c +++ /dev/null @@ -1,456 +0,0 @@ -/* - * demoscene-eo, an ASCII art demoscene written as a gift for Emmanuel Otton retirement - * Copyright (C) 2019 Ludovic Pouzenc - * - * This file is part of demoscene-eo. - * - * demoscene-eo 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. - * - * demoscene-eo 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 demoscene-eo. If not, see - */ -#include "main.h" -#include "scene00.h" -#include "scene01.h" -#include "scene02.h" - -#include // calloc() -#include // bzero() -#include // mmap() -#include // sem_t, sem_init(), Link with -pthread -#include // fork() -#include // wait() -#include // errno -#include // uint32_t -#include // printf() -#include // sigaction() - -#ifdef DEBUG_SEM -#define TRACE_SEM(sem,op,hint) printf("%s(): %s(%s) %s\n", __func__, op, #sem, hint) -#else -#define TRACE_SEM(sem,op,hint) -#endif - -#define SCENE_COUNT 3 -#define SCENE_NEXT do { shm->scene = (shm->scene+1)%SCENE_COUNT; } while(0) - -#define SEM_POST(sem,errcode) \ - do { \ - TRACE_SEM(sem,"sem_post",""); \ - if (sem_post(sem) == -1) { return errcode; } \ - } while(0) - -#define SEM_WAIT(sem,errcode) \ - do { \ - TRACE_SEM(sem,"sem_wait","call"); \ - while (sem_wait(sem) == -1) { \ - switch(errno) { \ - case EINTR: \ - case EAGAIN: \ - break; \ - default: \ - return errcode; \ - } \ - } \ - TRACE_SEM(sem,"sem_wait","done"); \ - } while(0) - -typedef struct { - sem_t worker_gl_can_render, worker_sdl_can_render, parent_can_read_result; - int scene, paused, done; - graphical_env_t ge; - scene00_env_t s00e; - scene01_env_t s01e; - scene02_env_t s02e; -} shm_t; - -int do_fork1(); -int do_fork2(); -int parent(); -int worker_sdl(); -int worker_gl(); - -static int skip = 0; -shm_t *shm; - -int main() { - TRACE("call"); - shm = mmap(NULL, sizeof(shm_t), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, 0, 0); - if (shm == NULL) return 1; - bzero(shm, sizeof(shm_t)); - - if (sem_init(&shm->worker_gl_can_render, 1, 0) < 0) return 2; - if (sem_init(&shm->worker_sdl_can_render, 1, 0) < 0) return 3; - if (sem_init(&shm->parent_can_read_result, 1, 0) < 0) return 4; - - return do_fork1(); -} - -int do_fork1() { - TRACE("call"); - fflush(stdout); - switch (fork()) { - case -1: return 4; break; - case 0: return worker_sdl(); break; - default: return do_fork2(); break; - } -} - -int do_fork2() { - TRACE("call"); - fflush(stdout); - switch (fork()) { - case -1: return 5; break; - case 0: return worker_gl(); break; - default: return parent(); break; - } -} - -int parent() { - int lastscene=-1, res, child_status; - caca_event_t caca_ev; - - TRACE("call"); - - // Initialize libcaca (http://caca.zoy.org/doxygen/libcaca/caca_8h.html) - shm->ge.dp = caca_create_display(NULL); - if(!shm->ge.dp) return 1; - caca_set_display_title(shm->ge.dp, "demoscene-eo"); - caca_set_display_time(shm->ge.dp, 41666); // 1e6µs/24 = 41666.66... It is ~24 fps - shm->ge.cv = caca_get_canvas(shm->ge.dp); - - shm->ge.d = caca_create_dither(32, FBUF_W, FBUF_H, FBUF_W*4, 0x00ff0000, 0x0000ff00, 0x000000ff, 0); - if (!shm->ge.d) return 2; - caca_set_dither_color(shm->ge.d, "16"); - - // Main libcaca loop for caca window - shm->ge.framecount=0; - do { - - // Check canvas size at every frame because of unreliable CACA_EVENT_RESIZE - shm->ge.w = caca_get_canvas_width(shm->ge.cv); - shm->ge.h = caca_get_canvas_height(shm->ge.cv); - if ( EXPR_MIN_SIZE ) { - caca_set_color_ansi(shm->ge.cv, CACA_BLACK, CACA_WHITE); - caca_put_str(shm->ge.cv, 0, 0, "Need a minimum of " TEXT_MIN_SIZE " terminal"); - } else if (!shm->paused) { - // init / free if scene transition - if (lastscene != shm->scene) { - switch(lastscene) { - case 0: scene00_free_caca(&shm->ge, &shm->s00e); break; - case 1: scene01_free_caca(&shm->ge, &shm->s01e); break; - case 2: scene02_free_caca(&shm->ge, &shm->s02e); break; - } - } - while (lastscene != shm->scene) { - shm->ge.sdl_ticks = SDL_GetTicks(); - shm->ge.sc_framecount = 0; - switch(shm->scene) { - case 0: res = scene00_init_caca(&shm->ge, &shm->s00e); break; - case 1: res = scene01_init_caca(&shm->ge, &shm->s01e); break; - case 2: res = scene02_init_caca(&shm->ge, &shm->s02e); break; - } - // If scene init fail, skip to the next one - if (res) SCENE_NEXT; else lastscene = shm->scene; - } - shm->ge.sdl_ticks = SDL_GetTicks(); // This value wraps if the program runs for more than ~49 days - - SEM_POST(&shm->worker_gl_can_render,30); - SEM_WAIT(&shm->parent_can_read_result,31); - - SEM_POST(&shm->worker_sdl_can_render,32); - SEM_WAIT(&shm->parent_can_read_result,33); - - // Compute current scene frame (caca part) - switch(shm->scene) { - case 0: res = scene00_next_caca(&shm->ge, &shm->s00e); break; - case 1: res = scene01_next_caca(&shm->ge, &shm->s01e); break; - case 2: res = scene02_next_caca(&shm->ge, &shm->s02e); break; - } - if (res) SCENE_NEXT; - shm->ge.framecount++; - shm->ge.sc_framecount++; - } - // Display it - caca_refresh_display(shm->ge.dp); // Auto framerate limiting (see caca_set_display_time()) - - // Event handling for the libcaca "window" (depending on CACA_DRIVER env variable) - if (caca_get_event(shm->ge.dp, CACA_EVENT_KEY_PRESS|CACA_EVENT_RESIZE|CACA_EVENT_QUIT, &caca_ev, 0)) { - switch(caca_get_event_type(&caca_ev)) { - case CACA_EVENT_QUIT: - shm->done = 1; - break; - case CACA_EVENT_KEY_PRESS: - switch(caca_get_event_key_ch(&caca_ev)) { - case 'q': shm->done = 1; break; - case 'p': shm->paused = !shm->paused; break; - case CACA_KEY_ESCAPE: SCENE_NEXT; break; - } - break; - /* On Debian 10, no CACA_EVENT_RESIZE fired when using x11 CACA_DRIVER - case CACA_EVENT_RESIZE: - w = caca_get_event_resize_width(&caca_ev); - h = caca_get_event_resize_height(&caca_ev); - small = EXPR_MIN_SIZE; - break; - */ - default: - break; - } - } - } while(!shm->done); - - caca_free_dither(shm->ge.d); - caca_free_display(shm->ge.dp); - shm->ge.cv=NULL; - - // reap forked processes (order doesn't matter) - if (wait(&child_status) != -1) { - if (child_status > 0) return child_status; - } - if (wait(&child_status) != -1) { - if (child_status > 0) return child_status; - } - return 0; -} - -static void worker_sdl_sighandler(int sig) { - TRACE("call"); - if ( sig == SIGALRM ) { - skip = 1; - sem_post(&shm->worker_sdl_can_render); - } -} - -int worker_sdl() { - int lastscene=-1, res; - struct sigaction sa; -#ifdef DEBUG - Uint32 sdl_win_flags = 0; //XXX for final version, consider adding SDL_WINDOW_HIDDEN - SDL_Event sdl_ev; -#else - Uint32 sdl_win_flags = SDL_WINDOW_HIDDEN; -#endif - SDL_RendererInfo renderer_info; - - TRACE("call"); - - sa.sa_handler = worker_sdl_sighandler; - sigemptyset(&sa.sa_mask); - sa.sa_flags = 0; - if (sigaction(SIGALRM, &sa, NULL) == -1) return 2; - - // Initialize SDL (http://wiki.libsdl.org/SDL_CreateWindowAndRenderer) - // Useful snippet : https://gist.github.com/koute/7391344 - res = SDL_Init(SDL_INIT_VIDEO); - if (res == -1) return 3; - atexit(SDL_Quit); - - res = SDL_CreateWindowAndRenderer(FBUF_W, FBUF_H, sdl_win_flags, &shm->ge.sdl_win, &shm->ge.sdl_rndr); - if (res == -1) return 4; - SDL_SetWindowTitle(shm->ge.sdl_win, "SDL debug"); - res = SDL_GetRendererInfo(shm->ge.sdl_rndr, &renderer_info); - if (res < 0) return 5; - if (!(renderer_info.flags & SDL_RENDERER_ACCELERATED)) return 6; - if (!(renderer_info.flags & SDL_RENDERER_TARGETTEXTURE)) return 7; - shm->ge.sdl_target = SDL_CreateTexture(shm->ge.sdl_rndr, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, FBUF_W, FBUF_H); - if (shm->ge.sdl_target == NULL) return 9; - - - while (!shm->done) { - alarm(1); - SEM_WAIT(&shm->worker_sdl_can_render,20); - alarm(0); - - if (!shm->paused && !skip) { - // init / free if scene transition - if (lastscene != shm->scene) { - switch(lastscene) { - case 0: scene00_free_sdl(&shm->ge, &shm->s00e); break; - case 1: scene01_free_sdl(&shm->ge, &shm->s01e); break; - case 2: scene02_free_sdl(&shm->ge, &shm->s02e); break; - } - } - while (lastscene != shm->scene ) { - shm->ge.sdl_ticks = SDL_GetTicks(); - shm->ge.sc_framecount = 0; - switch(shm->scene) { - case 0: res = scene00_init_sdl(&shm->ge, &shm->s00e); break; - case 1: res = scene01_init_sdl(&shm->ge, &shm->s01e); break; - case 2: res = scene02_init_sdl(&shm->ge, &shm->s02e); break; - } - // If scene init fail, skip to the next one - if (res) SCENE_NEXT; else lastscene = shm->scene; - } - // Compute current scene frame (sdl part) - switch(shm->scene) { - case 0: res = scene00_next_sdl(&shm->ge, &shm->s00e); break; - case 1: res = scene01_next_sdl(&shm->ge, &shm->s01e); break; - case 2: res = scene02_next_sdl(&shm->ge, &shm->s02e); break; - } - if (res) SCENE_NEXT; - } - if (skip) skip = 0; - -#ifdef DEBUG - // Event handling for the SDL window (debug purposes) - while(SDL_PollEvent(&sdl_ev)) { - switch(sdl_ev.type) { - case SDL_QUIT: - shm->done = 1; - break; - case SDL_KEYDOWN: - switch(sdl_ev.key.keysym.sym) { - case SDLK_q: shm->done = 1; break; - case SDLK_p: shm->paused = !shm->paused; break; - case SDLK_ESCAPE: SCENE_NEXT; break; - } - break; - default: - break; - } - } -#endif - SEM_POST(&shm->parent_can_read_result, 21); - } - - SDL_DestroyRenderer(shm->ge.sdl_rndr); - SDL_DestroyTexture(shm->ge.sdl_target); - SDL_DestroyWindow(shm->ge.sdl_win); - - return 0; -} - -static void worker_gl_sighandler(int sig) { - TRACE("call"); - if ( sig == SIGALRM ) { - skip = 1; - sem_post(&shm->worker_gl_can_render); - } -} - -int worker_gl() { - int lastscene=-1, res; - struct sigaction sa; -#ifdef DEBUG - Uint32 sdl_win_flags = SDL_WINDOW_OPENGL; - SDL_Event gl_ev; -#else - Uint32 sdl_win_flags = SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN; -#endif - SDL_RendererInfo renderer_info; - - TRACE("call"); - - sa.sa_handler = worker_gl_sighandler; - sigemptyset(&sa.sa_mask); - sa.sa_flags = 0; - if (sigaction(SIGALRM, &sa, NULL) == -1) return 2; - - // Initialize SDL for GL. Useful snippet : https://gist.github.com/koute/7391344 - res = SDL_Init(SDL_INIT_VIDEO); - if (res == -1) return 3; - atexit(SDL_Quit); - - SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); - SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1); - SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); - SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); - SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); - SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); - - // Initialize OpenGL - //shm->ge.gl_win = SDL_CreateWindow("GL Debug", SDL_WINDOWPOS_CENTERED, 0, FBUF_W, FBUF_H, sdl_win_flags); - - res = SDL_CreateWindowAndRenderer(FBUF_W, FBUF_H, sdl_win_flags, &shm->ge.gl_win, &shm->ge.gl_rndr); - if (res == -1) return 4; - SDL_SetWindowTitle(shm->ge.gl_win, "GL debug"); - res = SDL_GetRendererInfo(shm->ge.gl_rndr, &renderer_info); - if (res < 0) return 5; - if (!(renderer_info.flags & SDL_RENDERER_ACCELERATED)) return 6; - if (!(renderer_info.flags & SDL_RENDERER_TARGETTEXTURE)) return 7; - shm->ge.gl_target = SDL_CreateTexture(shm->ge.gl_rndr, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, FBUF_W, FBUF_H); - if (shm->ge.gl_target == NULL) return 9; - shm->ge.gl_ctx = SDL_GL_CreateContext(shm->ge.gl_win); - if (shm->ge.gl_ctx == NULL) return 11; -/* - glBindFramebuffer(GL_FRAMEBUFFER,0); - glClearColor(0,0,0,1); - glClear(GL_COLOR_BUFFER_BIT); - SDL_GL_SwapWindow(shm->ge.gl_win); -*/ - while (!shm->done) { - alarm(1); - SEM_WAIT(&shm->worker_gl_can_render,10); - alarm(0); - - if (!shm->paused && !skip) { - // init / free if scene transition - if (lastscene != shm->scene) { - switch(lastscene) { - case 0: scene00_free_gl(&shm->ge, &shm->s00e); break; - case 1: scene01_free_gl(&shm->ge, &shm->s01e); break; - case 2: scene02_free_gl(&shm->ge, &shm->s02e); break; - } - } - while (lastscene != shm->scene) { - shm->ge.sdl_ticks = SDL_GetTicks(); - shm->ge.sc_framecount = 0; - switch(shm->scene) { - case 0: res = scene00_init_gl(&shm->ge, &shm->s00e); break; - case 1: res = scene01_init_gl(&shm->ge, &shm->s01e); break; - case 2: res = scene02_init_gl(&shm->ge, &shm->s02e); break; - } - // If scene init fail, skip to the next one - if (res) SCENE_NEXT; else lastscene = shm->scene; - } - - // Compute current scene frame (gl part) - switch(shm->scene) { - case 0: res = scene00_next_gl(&shm->ge, &shm->s00e); break; - case 1: res = scene01_next_gl(&shm->ge, &shm->s01e); break; - case 2: res = scene02_next_gl(&shm->ge, &shm->s02e); break; - } - if (res) SCENE_NEXT; - } - if (skip) skip = 0; - -#ifdef DEBUG - // Event handling for the GL window (debug purposes) - while(SDL_PollEvent(&gl_ev)) { - switch(gl_ev.type) { - case SDL_QUIT: - shm->done = 1; - break; - case SDL_KEYDOWN: - switch(gl_ev.key.keysym.sym) { - case SDLK_q: shm->done = 1; break; - case SDLK_p: shm->paused = !shm->paused; break; - case SDLK_ESCAPE: SCENE_NEXT; break; - } - break; - default: - break; - } - } -#endif - SEM_POST(&shm->parent_can_read_result, 11); - } - - SDL_DestroyTexture(shm->ge.gl_target); - SDL_GL_DeleteContext(shm->ge.gl_ctx); - SDL_DestroyWindow(shm->ge.gl_win); - - return 0; -} diff --git a/src/main.h b/src/main.h index 3af6815..bf622ed 100644 --- a/src/main.h +++ b/src/main.h @@ -3,6 +3,7 @@ #define GL_GLEXT_PROTOTYPES +#include "config.h" #include #include #include -- cgit v1.2.3