diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | COPYING | 211 | ||||
-rw-r--r-- | Makefile | 16 | ||||
-rw-r--r-- | README | 3 | ||||
-rw-r--r-- | examples/cacaserver.c | 564 | ||||
-rw-r--r-- | examples/opengl3_hello.c | 199 | ||||
-rw-r--r-- | res/eo1.bmp | bin | 0 -> 76822 bytes | |||
-rw-r--r-- | src/main.c | 198 | ||||
-rw-r--r-- | src/main.h | 23 | ||||
-rw-r--r-- | src/scene00.c | 73 | ||||
-rw-r--r-- | src/scene00.h | 12 | ||||
-rw-r--r-- | src/scene01.c | 49 | ||||
-rw-r--r-- | src/scene01.h | 12 | ||||
-rw-r--r-- | src/scene02.c | 42 | ||||
-rw-r--r-- | src/scene02.h | 12 |
15 files changed, 1415 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3d1317d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +demoscene-eo @@ -0,0 +1,211 @@ +The libfreefare source code is licensed by the *modified* GNU Lesser +General Public License (LGPL), text provided below. A special exception +to the LGPL is included to allow you to distribute a combined work that +includes the libfreefare without being obliged to provide the source +code for any proprietary components. The exception text is also included +at the bottom of this file. + +The LGPL license text follows. + +------------------------------------------------------------------------ + + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. + +------------------------------------------------------------------------ + +The libfreefare LGPL Exception Text: + +Any libfreefare source code, whether modified or in it's original +release form, or whether in whole or in part, can only be distributed by +you under the terms of the GNU Lesser General Public License plus this +exception. An independent module is a module which is not derived from +or based on the libfreefare. + +Clause 1: + +Linking the libfreefare statically or dynamically with other modules is +making a combined work based on the libfreefare. Thus, the terms and +conditions of the GNU Lesser General Public License cover the whole +combination. + +As a special exception, the copyright holder of the libfreefare gives +you permission to link the libfreefare with independent modules that +communicate with the libfreefare solely through the libfreefare API +interface, regardless of the license terms of these independent modules, +and to copy and distribute the resulting combined work under terms of +your choice, provided that: + + - Every copy of the combined work is accompanied by a written + statement that details to the recipient the version of the + libfreefare used and an offer by yourself to provide the libfreefare + source code (including any modifications you may have made) should + the recipient request it. + + - The independent modules add significant and primary functionality to + the libfreefare and do not merely extend the existing functionality + already present in the libfreefare. + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..41a09e5 --- /dev/null +++ b/Makefile @@ -0,0 +1,16 @@ +CFLAGS=-Wall -Werror -g +PKGLIBS=caca sdl2 glu + +all: demoscene-eo + +server: demoscene-eo + CACA_DRIVER=raw CACA_GEOMETRY=80x25 ./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) -o $@ + +clean: + rm -f demoscene-eo + +.PHONY=all clean run @@ -0,0 +1,3 @@ + + +apt install libcaca-dev libsdl2-dev libglu1-mesa-dev diff --git a/examples/cacaserver.c b/examples/cacaserver.c new file mode 100644 index 0000000..825c3d4 --- /dev/null +++ b/examples/cacaserver.c @@ -0,0 +1,564 @@ +/* + * cacaserver Colour ASCII-Art library + * Copyright (c) 2006 Jean-Yves Lamoureux <jylam@lnxscene.org> + * 2006-2014 Sam Hocevar <sam@hocevar.net> + * All Rights Reserved + * + * This program is free software. It comes without any warranty, to + * the extent permitted by applicable law. You can redistribute it + * and/or modify it under the terms of the Do What the Fuck You Want + * to Public License, Version 2, as published by Sam Hocevar. See + * http://www.wtfpl.net/ for more details. + */ + +#include "config.h" + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#if defined(HAVE_ARPA_INET_H) +# include <arpa/inet.h> +#elif defined(HAVE_WINSOCK2_H) +# include <winsock2.h> +# include <ws2tcpip.h> +# define USE_WINSOCK 1 +#endif +#if defined(HAVE_NETINET_IN_H) +# include <netinet/in.h> +#endif +#if defined(HAVE_UNISTD_H) +# include <unistd.h> +#endif +#include <sys/types.h> +#include <sys/socket.h> +#include <fcntl.h> +#include <signal.h> +#include <errno.h> +#include <stdarg.h> + +#ifndef USE_WINSOCK +# define USE_WINSOCK 0 +#endif + +#include "caca.h" + +#define BACKLOG 1337 /* Number of pending connections */ +#define INBUFFER 32 /* Size of per-client input buffer */ +#define OUTBUFFER 300000 /* Size of per-client output buffer */ + +/* Following vars are static */ +#define INIT_PREFIX \ + "\xff\xfb\x01" /* WILL ECHO */ \ + "\xff\xfb\x03" /* WILL SUPPRESS GO AHEAD */ \ + "\xff\xfd\x31" /* DO NAWS */ \ + "\xff\x1f\xfa____" /* SB NAWS */ \ + "\xff\xf0" /* SE */ \ + "\033]2;caca for the network\x07" /* Change window title */ \ + "\033[H\033[J" /* Clear screen */ + /*"\033[?25l"*/ /* Hide cursor */ + +#define ANSI_PREFIX \ + "\033[1;1H" /* move(0,0) */ \ + "\033[1;1H" /* move(0,0) again */ + +#define ANSI_RESET \ + " " /* Garbage */ \ + "\033[?1049h" /* Clear screen */ \ + "\033[?1049h" /* Clear screen again */ + +static char const telnet_commands[16][5] = +{ + "SE ", "NOP ", "DM ", "BRK ", "IP ", "AO ", "AYT ", "EC ", + "EL ", "GA ", "SB ", "WILL", "WONT", "DO ", "DONT", "IAC " +}; + +static char const telnet_options[37][5] = +{ + "????", "ECHO", "????", "SUGH", "????", "STTS", "TIMK", "????", + "????", "????", "????", "????", "????", "????", "????", "????", + "????", "????", "????", "????", "????", "????", "????", "????", + "TTYP", "????", "????", "????", "????", "????", "????", "NAWS", + "TRSP", "RMFC", "LIMO", "????", "EVAR" +}; + +#define COMMAND_NAME(x) (x>=240)?telnet_commands[x-240]:"????" +#define OPTION_NAME(x) (x<=36)?telnet_options[x]:"????" + +struct client +{ + int fd; + int ready; + uint8_t inbuf[INBUFFER]; + int inbytes; + uint8_t outbuf[OUTBUFFER]; + int start, stop; +}; + +struct server +{ + unsigned int width, height; + unsigned int port; + int sockfd; + struct sockaddr_in my_addr; + socklen_t sin_size; + + /* Input buffer */ + uint8_t *input; + unsigned int read; + + char prefix[sizeof(INIT_PREFIX)]; + + caca_canvas_t *canvas; + void *buffer; + size_t buflen; + + int client_count; + struct client *clients; + + RETSIGTYPE (*sigpipe_handler)(int); +}; + +static void manage_connections(struct server *server); +static int send_data(struct server *server, struct client *c); +ssize_t nonblock_write(int fd, void *buf, size_t len); + +int main(void) +{ + int i, yes = 1, flags; + struct server *server; + char *tmp; + +#if USE_WINSOCK + WORD winsockVersion; + WSADATA wsaData; + winsockVersion = MAKEWORD(1, 1); + + WSAStartup(winsockVersion, &wsaData); +#endif + server = malloc(sizeof(struct server)); + + server->input = malloc(12); + server->read = 0; + + server->client_count = 0; + server->clients = NULL; + server->port = 0xCACA; /* 51914 */ + + /* FIXME, handle >255 sizes */ + memcpy(server->prefix, INIT_PREFIX, sizeof(INIT_PREFIX)); + tmp = strstr(server->prefix, "____"); + tmp[0] = (uint8_t) (server->width & 0xff00) >> 8; + tmp[1] = (uint8_t) server->width & 0xff; + tmp[2] = (uint8_t) (server->height & 0xff00) >> 8; + tmp[3] = (uint8_t) server->height & 0xff; + + if((server->sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) + { + perror("socket"); + return -1; + } + + if(setsockopt(server->sockfd, SOL_SOCKET, + SO_REUSEADDR, &yes, sizeof(int)) == -1) + { + perror("setsockopt SO_REUSEADDR"); + return -1; + } + + server->my_addr.sin_family = AF_INET; + server-> my_addr.sin_port = htons(server->port); + server->my_addr.sin_addr.s_addr = INADDR_ANY; + memset(&(server->my_addr.sin_zero), '\0', 8); + + if(bind(server->sockfd, (struct sockaddr *)&server->my_addr, + sizeof(struct sockaddr)) == -1) + { + perror("bind"); + return -1; + } + + /* Non blocking socket */ + flags = fcntl(server->sockfd, F_GETFL, 0); + fcntl(server->sockfd, F_SETFL, flags | O_NONBLOCK); + + if(listen(server->sockfd, BACKLOG) == -1) + { + perror("listen"); + return -1; + } + + server->canvas = caca_create_canvas(0, 0); + server->buffer = NULL; + + /* Ignore SIGPIPE */ + server->sigpipe_handler = signal(SIGPIPE, SIG_IGN); + + fprintf(stderr, "initialised network, listening on port %i\n", + server->port); + + /* Main loop */ + for(;;) + { +restart: + /* Manage new connections as this function will be called sometimes + * more often than display */ + manage_connections(server); + + /* Read data from stdin */ + if(server->read < 12) + { + read(0, server->input + server->read, 12 - server->read); + server->read = 12; + } + + while(caca_import_canvas_from_memory(server->canvas, server->input, + server->read, "caca") < 0) + { + memmove(server->input, server->input + 1, server->read - 1); + read(0, server->input + server->read - 1, 1); + } + + for(;;) + { + ssize_t needed, wanted; + + needed = caca_import_canvas_from_memory(server->canvas, + server->input, + server->read, "caca"); + if(needed < 0) + goto restart; + + if(needed > 0) + { + server->read -= needed; + memmove(server->input, server->input + needed, server->read); + break; + } + + server->input = realloc(server->input, server->read + 128); + wanted = read(0, server->input + server->read, 128); + if(wanted < 0) + goto restart; + server->read += wanted; + } + + /* Free the previous export buffer, if any */ + if(server->buffer) + { + free(server->buffer); + server->buffer = NULL; + } + + /* Get ANSI representation of the image and skip the end-of buffer + * linefeed ("\r\n", 2 byte) */ + server->buffer = caca_export_canvas_to_memory(server->canvas, "utf8cr", + &server->buflen); + server->buflen -= 2; + + for(i = 0; i < server->client_count; i++) + { + if(server->clients[i].fd == -1) + continue; + + if(send_data(server, &server->clients[i])) + { + fprintf(stderr, "[%i] dropped connection\n", + server->clients[i].fd); + close(server->clients[i].fd); + server->clients[i].fd = -1; + } + } + } + + /* Kill all remaining clients */ + for(i = 0; i < server->client_count; i++) + { + if(server->clients[i].fd == -1) + continue; + + close(server->clients[i].fd); + server->clients[i].fd = -1; + } + + if(server->buffer) + free(server->buffer); + + caca_free_canvas(server->canvas); + + /* Restore SIGPIPE handler */ + signal(SIGPIPE, server->sigpipe_handler); + + free(server); + +#if USE_WINSOCK + WSACleanup(); +#endif + return 0; +} + +/* + * XXX: The following functions are local + */ + +static void manage_connections(struct server *server) +{ + int fd, flags; + struct sockaddr_in remote_addr; + socklen_t len = sizeof(struct sockaddr_in); + + fd = accept(server->sockfd, (struct sockaddr *)&remote_addr, &len); + if(fd == -1) + return; + + fprintf(stderr, "[%i] connected from %s\n", + fd, inet_ntoa(remote_addr.sin_addr)); + + /* Non blocking socket */ + flags = fcntl(fd, F_SETFL, 0); + fcntl(fd, F_SETFL, flags | O_NONBLOCK); + + if(server->clients == NULL) + { + server->clients = malloc(sizeof(struct client)); + if(server->clients == NULL) + { + close(fd); + return; + } + } + else + { + server->clients = realloc(server->clients, + (server->client_count+1) * sizeof(struct client)); + } + + server->clients[server->client_count].fd = fd; + server->clients[server->client_count].ready = 0; + server->clients[server->client_count].inbytes = 0; + server->clients[server->client_count].start = 0; + server->clients[server->client_count].stop = 0; + + /* If we already have data to send, send it to the new client */ + if(send_data(server, &server->clients[server->client_count])) + { + fprintf(stderr, "[%i] dropped connection\n", fd); + close(fd); + server->clients[server->client_count].fd = -1; + return; + } + + server->client_count++; +} + +static int send_data(struct server *server, struct client *c) +{ + ssize_t ret; + + /* Not for us */ + if(c->fd < 0) + return -1; + + /* Listen to incoming data */ + for(;;) + { + ret = read(c->fd, c->inbuf + c->inbytes, 1); + if(ret <= 0) + break; + + c->inbytes++; + + /* Check for telnet sequences */ + if(c->inbuf[0] == 0xff) + { + if(c->inbytes == 1) + { + ; + } + else if(c->inbuf[1] == 0xfd || c->inbuf[1] == 0xfc) + { + if(c->inbytes == 3) + { + fprintf(stderr, "[%i] said: %.02x %.02x %.02x (%s %s %s)\n", + c->fd, c->inbuf[0], c->inbuf[1], c->inbuf[2], + COMMAND_NAME(c->inbuf[0]), COMMAND_NAME(c->inbuf[1]), OPTION_NAME(c->inbuf[2])); + /* Just ignore, lol */ + c->inbytes = 0; + } + } + else + c->inbytes = 0; + } + else if(c->inbytes == 1) + { + if(c->inbuf[0] == 0x03) + { + fprintf(stderr, "[%i] pressed C-c\n", c->fd); + return -1; /* User requested to quit */ + } + + c->inbytes = 0; + } + } + + /* Send the telnet initialisation commands */ + if(!c->ready) + { + ret = nonblock_write(c->fd, server->prefix, sizeof(INIT_PREFIX)); + if(ret == -1) + return (errno == EAGAIN) ? 0 : -1; + + if(ret < (ssize_t)sizeof(INIT_PREFIX)) + return 0; + + c->ready = 1; + } + + /* No error, there's just nothing to send yet */ + if(!server->buffer) + return 0; + + /* If we have backlog, send the backlog */ + if(c->stop) + { + ret = nonblock_write(c->fd, c->outbuf + c->start, c->stop - c->start); + + if(ret == -1) + { + if(errno == EAGAIN) + ret = 0; + else + { + fprintf(stderr, "[%i] failed (%s)\n", + c->fd, strerror(errno)); + return -1; + } + } + + if(ret == c->stop - c->start) + { + /* We got rid of the backlog! */ + c->start = c->stop = 0; + } + else + { + c->start += ret; + + if(c->stop - c->start + strlen(ANSI_PREFIX) + server->buflen + > OUTBUFFER) + { + /* Overflow! Empty buffer and start again */ + memcpy(c->outbuf, ANSI_RESET, strlen(ANSI_RESET)); + c->start = 0; + c->stop = strlen(ANSI_RESET); + return 0; + } + + /* Need to move? */ + if(c->stop + strlen(ANSI_PREFIX) + server->buflen > OUTBUFFER) + { + memmove(c->outbuf, c->outbuf + c->start, c->stop - c->start); + c->stop -= c->start; + c->start = 0; + } + + memcpy(c->outbuf + c->stop, ANSI_PREFIX, strlen(ANSI_PREFIX)); + c->stop += strlen(ANSI_PREFIX); + memcpy(c->outbuf + c->stop, server->buffer, server->buflen); + c->stop += server->buflen; + + return 0; + } + } + + /* We no longer have backlog, send our new data */ + + /* Send ANSI prefix */ + ret = nonblock_write(c->fd, ANSI_PREFIX, strlen(ANSI_PREFIX)); + if(ret == -1) + { + if(errno == EAGAIN) + ret = 0; + else + { + fprintf(stderr, "[%i] failed (%s)\n", c->fd, strerror(errno)); + return -1; + } + } + + if(ret < (ssize_t)strlen(ANSI_PREFIX)) + { + if(strlen(ANSI_PREFIX) + server->buflen > OUTBUFFER) + { + /* Overflow! Empty buffer and start again */ + memcpy(c->outbuf, ANSI_RESET, strlen(ANSI_RESET)); + c->start = 0; + c->stop = strlen(ANSI_RESET); + return 0; + } + + memcpy(c->outbuf, ANSI_PREFIX, strlen(ANSI_PREFIX) - ret); + c->stop = strlen(ANSI_PREFIX) - ret; + memcpy(c->outbuf + c->stop, server->buffer, server->buflen); + c->stop += server->buflen; + + return 0; + } + + /* Send actual data */ + ret = nonblock_write(c->fd, server->buffer, server->buflen); + if(ret == -1) + { + if(errno == EAGAIN) + ret = 0; + else + { + fprintf(stderr, "[%i] failed (%s)\n", c->fd, strerror(errno)); + return -1; + } + } + + if(ret < (int)server->buflen) + { + if(server->buflen > OUTBUFFER) + { + /* Overflow! Empty buffer and start again */ + memcpy(c->outbuf, ANSI_RESET, strlen(ANSI_RESET)); + c->start = 0; + c->stop = strlen(ANSI_RESET); + return 0; + } + + memcpy(c->outbuf, server->buffer, server->buflen - ret); + c->stop = server->buflen - ret; + + return 0; + } + + return 0; +} + +ssize_t nonblock_write(int fd, void *buf, size_t len) +{ + size_t total = 0; + ssize_t ret; + + while(total < len) + { + do + { + ret = write(fd, buf, len); + } + while(ret < 0 && errno == EINTR); + + if(ret < 0) + return ret; + else if(ret == 0) + return total; + + total += len; + buf = (void *)((uintptr_t)buf + len); + } + + return total; +} + diff --git a/examples/opengl3_hello.c b/examples/opengl3_hello.c new file mode 100644 index 0000000..7cc9070 --- /dev/null +++ b/examples/opengl3_hello.c @@ -0,0 +1,199 @@ +/* + Minimal SDL2 + OpenGL3 example. + + Author: https://github.com/koute + + This file is in the public domain; you can do whatever you want with it. + In case the concept of public domain doesn't exist in your jurisdiction + you can also use this code under the terms of Creative Commons CC0 license, + either version 1.0 or (at your option) any later version; for details see: + http://creativecommons.org/publicdomain/zero/1.0/ + + This software is distributed without any warranty whatsoever. + + Compile and run with: gcc opengl3_hello.c `sdl2-config --libs --cflags` -lGL -Wall && ./a.out +*/ + +#define GL_GLEXT_PROTOTYPES + +#include <SDL.h> +#include <SDL_opengl.h> + +#include <stdio.h> + +typedef float t_mat4x4[16]; + +static inline void mat4x4_ortho( t_mat4x4 out, float left, float right, float bottom, float top, float znear, float zfar ) +{ + #define T(a, b) (a * 4 + b) + + out[T(0,0)] = 2.0f / (right - left); + out[T(0,1)] = 0.0f; + out[T(0,2)] = 0.0f; + out[T(0,3)] = 0.0f; + + out[T(1,1)] = 2.0f / (top - bottom); + out[T(1,0)] = 0.0f; + out[T(1,2)] = 0.0f; + out[T(1,3)] = 0.0f; + + out[T(2,2)] = -2.0f / (zfar - znear); + out[T(2,0)] = 0.0f; + out[T(2,1)] = 0.0f; + out[T(2,3)] = 0.0f; + + out[T(3,0)] = -(right + left) / (right - left); + out[T(3,1)] = -(top + bottom) / (top - bottom); + out[T(3,2)] = -(zfar + znear) / (zfar - znear); + out[T(3,3)] = 1.0f; + + #undef T +} + +static const char * vertex_shader = + "#version 130\n" + "in vec2 i_position;\n" + "in vec4 i_color;\n" + "out vec4 v_color;\n" + "uniform mat4 u_projection_matrix;\n" + "void main() {\n" + " v_color = i_color;\n" + " gl_Position = u_projection_matrix * vec4( i_position, 0.0, 1.0 );\n" + "}\n"; + +static const char * fragment_shader = + "#version 130\n" + "in vec4 v_color;\n" + "out vec4 o_color;\n" + "void main() {\n" + " o_color = v_color;\n" + "}\n"; + +typedef enum t_attrib_id +{ + attrib_position, + attrib_color +} t_attrib_id; + +int main( int argc, char * argv[] ) +{ + SDL_Init( SDL_INIT_VIDEO ); + 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 ); + + static const int width = 800; + static const int height = 600; + + SDL_Window * window = SDL_CreateWindow( "", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN ); + SDL_GLContext context = SDL_GL_CreateContext( window ); + + GLuint vs, fs, program; + + vs = glCreateShader( GL_VERTEX_SHADER ); + fs = glCreateShader( GL_FRAGMENT_SHADER ); + + int length = strlen( vertex_shader ); + glShaderSource( vs, 1, ( const GLchar ** )&vertex_shader, &length ); + glCompileShader( vs ); + + GLint status; + glGetShaderiv( vs, GL_COMPILE_STATUS, &status ); + if( status == GL_FALSE ) + { + fprintf( stderr, "vertex shader compilation failed\n" ); + return 1; + } + + length = strlen( fragment_shader ); + glShaderSource( fs, 1, ( const GLchar ** )&fragment_shader, &length ); + glCompileShader( fs ); + + glGetShaderiv( fs, GL_COMPILE_STATUS, &status ); + if( status == GL_FALSE ) + { + fprintf( stderr, "fragment shader compilation failed\n" ); + return 1; + } + + program = glCreateProgram(); + glAttachShader( program, vs ); + glAttachShader( program, fs ); + + glBindAttribLocation( program, attrib_position, "i_position" ); + glBindAttribLocation( program, attrib_color, "i_color" ); + glLinkProgram( program ); + + glUseProgram( program ); + + glDisable( GL_DEPTH_TEST ); + glClearColor( 0.5, 0.0, 0.0, 0.0 ); + glViewport( 0, 0, width, height ); + + GLuint vao, vbo; + + glGenVertexArrays( 1, &vao ); + glGenBuffers( 1, &vbo ); + glBindVertexArray( vao ); + glBindBuffer( GL_ARRAY_BUFFER, vbo ); + + glEnableVertexAttribArray( attrib_position ); + glEnableVertexAttribArray( attrib_color ); + + glVertexAttribPointer( attrib_color, 4, GL_FLOAT, GL_FALSE, sizeof( float ) * 6, 0 ); + glVertexAttribPointer( attrib_position, 2, GL_FLOAT, GL_FALSE, sizeof( float ) * 6, ( void * )(4 * sizeof(float)) ); + + const GLfloat g_vertex_buffer_data[] = { + /* R, G, B, A, X, Y */ + 1, 0, 0, 1, 0, 0, + 0, 1, 0, 1, width, 0, + 0, 0, 1, 1, width, height, + + 1, 0, 0, 1, 0, 0, + 0, 0, 1, 1, width, height, + 1, 1, 1, 1, 0, height + }; + + glBufferData( GL_ARRAY_BUFFER, sizeof( g_vertex_buffer_data ), g_vertex_buffer_data, GL_STATIC_DRAW ); + + t_mat4x4 projection_matrix; + mat4x4_ortho( projection_matrix, 0.0f, (float)width, (float)height, 0.0f, 0.0f, 100.0f ); + glUniformMatrix4fv( glGetUniformLocation( program, "u_projection_matrix" ), 1, GL_FALSE, projection_matrix ); + + for( ;; ) + { + glClear( GL_COLOR_BUFFER_BIT ); + + SDL_Event event; + while( SDL_PollEvent( &event ) ) + { + switch( event.type ) + { + case SDL_KEYUP: + if( event.key.keysym.sym == SDLK_ESCAPE ) + return 0; + break; + } + } + + glBindVertexArray( vao ); + glDrawArrays( GL_TRIANGLES, 0, 6 ); + + SDL_GL_SwapWindow( window ); + SDL_Delay( 1 ); + } + + SDL_GL_DeleteContext( context ); + SDL_DestroyWindow( window ); + SDL_Quit(); + + return 0; +} + diff --git a/res/eo1.bmp b/res/eo1.bmp Binary files differnew file mode 100644 index 0000000..96a68d8 --- /dev/null +++ b/res/eo1.bmp diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..a73bf44 --- /dev/null +++ b/src/main.c @@ -0,0 +1,198 @@ +/* + * demoscene-eo, an ASCII art demoscene written as a gift for Emmanuel Otton retirement + * Copyright (C) 2019 Ludovic Pouzenc <ludovic@pouzenc.fr> + * + * 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 <http://www.gnu.org/licenses/> + */ +#include "main.h" +#include "scene00.h" +#include "scene01.h" +#include "scene02.h" +#define SCENE_COUNT 3 +#define SCENE_NEXT do { scene = (scene+1)%SCENE_COUNT; } while(0) + +#define EXPR_MIN_SIZE ( ge.w<80 || ge.h<25 ) +#define TEXT_MIN_SIZE "80x25" + +int main(void) +{ + int done=0, lastscene=-1, scene=0, paused=0, res; + Uint32 sdl_win_flags = SDL_WINDOW_OPENGL; + graphical_env_t ge; + scene00_env_t s00e; + scene01_env_t s01e; + scene02_env_t s02e; + + caca_event_t caca_ev; + SDL_RendererInfo renderer_info; + SDL_Event sdl_ev; + + // Initialize libcaca (http://caca.zoy.org/doxygen/libcaca/caca_8h.html) + ge.dp = caca_create_display(NULL); + if(!ge.dp) return 1; + caca_set_display_title(ge.dp, "demoscene-eo"); + caca_set_display_time(ge.dp, 16666); // 1e6µs/60 = 16666.66... It is ~60 fps + ge.cv = caca_get_canvas(ge.dp); + + ge.d = caca_create_dither(32, 256, 256, 256*4, 0x00ff0000, 0x0000ff00, 0x000000ff, 0); + if ( !ge.d ) return 2; + caca_set_dither_color(ge.d, "16"); + + // 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; + 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 ); + + //XXX for final version, consider sdl_win_flags |= SDL_WINDOW_HIDDEN; + res = SDL_CreateWindowAndRenderer(256, 256, sdl_win_flags, &ge.sdl_win, &ge.sdl_rndr); + if ( res == -1) return 4; + SDL_SetWindowTitle(ge.sdl_win, "SDL/GL debug"); + res = SDL_GetRendererInfo(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; + ge.sdl_target = SDL_CreateTexture(ge.sdl_rndr, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, 256, 256); + if ( ge.sdl_target == NULL ) return 9; + ge.raw_target = malloc(256*256*4); + if ( ge.raw_target == NULL ) return 10; + ge.gl_ctx = SDL_GL_CreateContext(ge.sdl_win); + if ( ge.gl_ctx == NULL ) return 11; + + + // Initialize OpenGL + glShadeModel(GL_SMOOTH); + glClearDepth(1.0f); + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LEQUAL); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); + + glDisable(GL_TEXTURE_2D); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glEnable(GL_CULL_FACE); + glCullFace(GL_FRONT); + glViewport(0, 0, (GLsizei)256, (GLsizei)256); + glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45.0f, 1.0f, 0.1f, 100.0f); // 1.0f==ratio + glMatrixMode(GL_MODELVIEW); glLoadIdentity(); + + // Main libcaca loop for caca window (OpenGL could be used in sceneN_next()) + ge.framecount=0; + do { + // Check canvas size at every frame because of unreliable CACA_EVENT_RESIZE + ge.w = caca_get_canvas_width(ge.cv); + ge.h = caca_get_canvas_height(ge.cv); + if ( EXPR_MIN_SIZE ) { + caca_set_color_ansi(ge.cv, CACA_BLACK, CACA_WHITE); + caca_put_str(ge.cv, 0, 0, "Need a minimum of " TEXT_MIN_SIZE " terminal"); + } else if (!paused) { + // init / free if scene transition + if ( lastscene != scene ) { + switch(lastscene) { + case 0: scene00_free(&ge, &s00e); break; + case 1: scene01_free(&ge, &s01e); break; + case 2: scene02_free(&ge, &s02e); break; + } + } + while ( lastscene != scene ) { + ge.sdl_ticks = SDL_GetTicks(); + ge.sc_framecount = 0; + switch(scene) { + case 0: res = scene00_init(&ge, &s00e); break; + case 1: res = scene01_init(&ge, &s01e); break; + case 2: res = scene02_init(&ge, &s02e); break; + } + // If scene init fail, skip to the next one + if (res) SCENE_NEXT; else lastscene = scene; + } + // Compute current scene0 frame + ge.sdl_ticks = SDL_GetTicks(); // This value wraps if the program runs for more than ~49 days + switch(scene) { + case 0: res = scene00_next(&ge, &s00e); break; + case 1: res = scene01_next(&ge, &s01e); break; + case 2: res = scene02_next(&ge, &s02e); break; + } + if (res) SCENE_NEXT; + ge.framecount++; + ge.sc_framecount++; + } + // Display it + caca_refresh_display(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(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: + done = 1; + break; + case CACA_EVENT_KEY_PRESS: + switch(caca_get_event_key_ch(&caca_ev)) { + case 'q': done = 1; break; + case 'p': paused = !paused; break; + case CACA_KEY_ESCAPE: SCENE_NEXT; break; + } + break; + /* On Debian 10, no CACA_EVENT_RESIZE 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; + } + } + // Event handling for the SDL/GL window (debug purposes) + while(SDL_PollEvent(&sdl_ev)) { + switch(sdl_ev.type) { + case SDL_QUIT: + done = 1; + break; + case SDL_KEYDOWN: + switch(sdl_ev.key.keysym.sym) { + case SDLK_q: done = 1; break; + case SDLK_p: paused = !paused; break; + case SDLK_ESCAPE: SCENE_NEXT; break; + } + break; + default: + break; + } + } + } while(!done); + + caca_free_dither(ge.d); + caca_free_display(ge.dp); + ge.cv=NULL; + SDL_GL_DeleteContext(ge.gl_ctx); + SDL_DestroyTexture(ge.sdl_target); + SDL_DestroyRenderer(ge.sdl_rndr); + SDL_DestroyWindow(ge.sdl_win); + SDL_Quit(); + return 0; +} diff --git a/src/main.h b/src/main.h new file mode 100644 index 0000000..b2b0a6e --- /dev/null +++ b/src/main.h @@ -0,0 +1,23 @@ +#ifndef MAIN_H_INCLUDED +#define MAIN_H_INCLUDED + +#include <caca.h> +#include <SDL2/SDL.h> +#include <SDL2/SDL_opengl.h> +#include <GL/glu.h> + +typedef struct { + caca_display_t *dp; + caca_canvas_t *cv; + caca_dither_t *d; + int w, h; // caca terminal size in characters + SDL_Window* sdl_win; + SDL_Renderer *sdl_rndr; + SDL_Texture *sdl_target; + void *raw_target; + SDL_GLContext gl_ctx; + Uint32 sdl_ticks; + Uint32 framecount; + Uint32 sc_framecount; +} graphical_env_t; +#endif diff --git a/src/scene00.c b/src/scene00.c new file mode 100644 index 0000000..746db29 --- /dev/null +++ b/src/scene00.c @@ -0,0 +1,73 @@ +/* + * demoscene-eo, an ASCII art demoscene written as a gift for Emmanuel Otton retirement + * Copyright (C) 2019 XXXXXX XXXXXXX <xxxxxx@xxxxxx.fr> + * + * 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 <http://www.gnu.org/licenses/> + */ +#include "scene00.h" + +int scene00_init(graphical_env_t *ge, scene00_env_t *se) { + SDL_Surface *bmpSurf = SDL_LoadBMP("./res/eo1.bmp"); + se->eo1 = SDL_CreateTextureFromSurface(ge->sdl_rndr, bmpSurf); + SDL_FreeSurface(bmpSurf); + + return 0; +} + +void scene00_free(graphical_env_t *ge, scene00_env_t *se) { + SDL_DestroyTexture(se->eo1); se->eo1=NULL; +} + +int scene00_next(graphical_env_t *ge, scene00_env_t *se) { + // Shorthands + caca_canvas_t *cv = ge->cv; + int w = ge->w, h = ge->h; + Uint32 frame = ge->sc_framecount; + SDL_Renderer *r = ge->sdl_rndr; + // Local vars + int res; + + // https://gist.github.com/Twinklebear/8265888 + // https://forums.libsdl.org/viewtopic.php?p=51634 + + // Render all the stuff on target texture + SDL_SetRenderTarget(r, ge->sdl_target); + SDL_RenderCopy(r, se->eo1, NULL, NULL); + // [...] + + // Copy the SDL screen to SDL debug window (and display it, not mandatory) + SDL_SetRenderTarget(r, NULL); + SDL_RenderCopy(r, ge->sdl_target, NULL, NULL); + SDL_RenderPresent(r); + + // Download the rendered texture from videocard to main memory + res = SDL_RenderReadPixels(r, NULL, 0, ge->raw_target, 256*4); + + // "convert" the raw pixel stream to ASCII art on caca canevas + if ( res == 0 ) caca_dither_bitmap(cv, 0, 0, w, h, ge->d, ge->raw_target); + + // Add things on top of caca canvas + caca_set_color_ansi(cv, CACA_BLACK, CACA_WHITE); + caca_put_str(cv, (w-17)/2, h/2, "This is a message"); + caca_draw_line(cv,6,6,w-14,11, '*'); + caca_draw_thin_line(cv,frame%10,frame%10,w-10+frame%10,h-10+frame%10); + + if ( frame >= 100 ) { + return 1; + } + return 0; +} + diff --git a/src/scene00.h b/src/scene00.h new file mode 100644 index 0000000..b1c9d9d --- /dev/null +++ b/src/scene00.h @@ -0,0 +1,12 @@ +#ifndef SCENE00_H_INCLUDED +#define SCENE00_H_INCLUDED + +#include "main.h" +typedef struct { + SDL_Texture *eo1; +} scene00_env_t; + +int scene00_init(graphical_env_t *ge, scene00_env_t *se); +void scene00_free(graphical_env_t *ge, scene00_env_t *se); +int scene00_next(graphical_env_t *ge, scene00_env_t *se); +#endif diff --git a/src/scene01.c b/src/scene01.c new file mode 100644 index 0000000..2b3e9f5 --- /dev/null +++ b/src/scene01.c @@ -0,0 +1,49 @@ +/* + * demoscene-eo, an ASCII art demoscene written as a gift for Emmanuel Otton retirement + * Copyright (C) 2019 XXXXXX XXXXXXX <xxxxxx@xxxxxx.fr> + * + * 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 <http://www.gnu.org/licenses/> + */ +#include "scene01.h" + +int scene01_init(graphical_env_t *ge, scene01_env_t *se) { + return 0; +} + +void scene01_free(graphical_env_t *ge, scene01_env_t *se) { +} + +int scene01_next(graphical_env_t *ge, scene01_env_t *se) { + // Shorthands + caca_canvas_t *cv = ge->cv; + int w = ge->w, h = ge->h; + Uint32 frame = ge->sc_framecount; + + //XXX remove me + ((Uint32 *)ge->raw_target)[frame%(256*256)]=frame<<((frame%3)*8); + + caca_clear_canvas(cv); + caca_set_dither_gamma(ge->d, (100-frame%100)/100.0); + caca_dither_bitmap(cv, 0, 0, w, h, ge->d, ge->raw_target); + + caca_set_color_ansi(cv, CACA_WHITE, CACA_BLACK); + caca_put_str(cv, (w-17)/2, h/2, "This is a message"); + + if ( frame >= 300 ) { + return 1; + } + return 0; +} diff --git a/src/scene01.h b/src/scene01.h new file mode 100644 index 0000000..1f22de7 --- /dev/null +++ b/src/scene01.h @@ -0,0 +1,12 @@ +#ifndef SCENE01_H_INCLUDED +#define SCENE01_H_INCLUDED + +#include "main.h" +typedef struct { + +} scene01_env_t; + +int scene01_init(graphical_env_t *ge, scene01_env_t *se); +void scene01_free(graphical_env_t *ge, scene01_env_t *se); +int scene01_next(graphical_env_t *ge, scene01_env_t *se); +#endif diff --git a/src/scene02.c b/src/scene02.c new file mode 100644 index 0000000..1989bf0 --- /dev/null +++ b/src/scene02.c @@ -0,0 +1,42 @@ +/* + * demoscene-eo, an ASCII art demoscene written as a gift for Emmanuel Otton retirement + * Copyright (C) 2019 XXXXXX XXXXXXX <xxxxxx@xxxxxx.fr> + * + * 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 <http://www.gnu.org/licenses/> + */ +#include "scene02.h" + +int scene02_init(graphical_env_t *ge, scene02_env_t *se) { + return 0; +} + +void scene02_free(graphical_env_t *ge, scene02_env_t *se) { +} + +int scene02_next(graphical_env_t *ge, scene02_env_t *se) { + // Shorthands + caca_canvas_t *cv = ge->cv; + //int w = ge->w, h = ge->h; + Uint32 frame = ge->sc_framecount; + + caca_clear_canvas(cv); + + if ( frame >= 100 ) { + return 1; + } + return 0; +} + diff --git a/src/scene02.h b/src/scene02.h new file mode 100644 index 0000000..49abbfb --- /dev/null +++ b/src/scene02.h @@ -0,0 +1,12 @@ +#ifndef SCENE02_H_INCLUDED +#define SCENE02_H_INCLUDED + +#include "main.h" +typedef struct { + +} scene02_env_t; + +int scene02_init(graphical_env_t *ge, scene02_env_t *se); +void scene02_free(graphical_env_t *ge, scene02_env_t *se); +int scene02_next(graphical_env_t *ge, scene02_env_t *se); +#endif |