├── domsson ├── .gitignore ├── cellphone.png ├── oldschool.png ├── futuristic.png ├── Makefile ├── README.md ├── test.c └── domsson.c ├── editor ├── icons.pcx ├── walls.pcx ├── editor.h ├── tiles.csv ├── README.md ├── image.h ├── test.xpm ├── Makefile ├── image.c └── editor.c ├── pocketmod ├── page.png ├── Makefile └── modder.c ├── screens ├── disable.ico ├── enable.ico ├── little.ico ├── screens.rc ├── README.md ├── Makefile ├── screens.h └── screens.c ├── mybas ├── examples │ ├── arrays3.bas │ ├── cmp2.bas │ ├── arrays4.bas │ ├── tester.bas │ ├── arrays.bas │ ├── print.bas │ ├── work.bas │ ├── arrays2.bas │ ├── loops.bas │ ├── test.bas │ └── cmp.bas ├── hash.h ├── bas.h ├── ast.h ├── Makefile ├── hash.c ├── README.md ├── ast.c └── bas.c ├── sbasic ├── getopt.h ├── test │ ├── io.bas │ ├── cond.bas │ ├── subs.bas │ ├── seqio.bas │ ├── database.bas │ ├── test.bas │ ├── lists.bas │ └── bifs.bas ├── README.md ├── makefile ├── getopt.c ├── ppdbmain.c ├── sbasic.h ├── listfuns.c └── main.c ├── snippets ├── getopt.h ├── rot13.c └── getopt.c ├── .gitignore ├── fenster-pocketmod ├── Makefile ├── main.c └── fenster_audio.h ├── fenster-microui ├── README.md ├── fenmui.h ├── Makefile ├── Cushion.h ├── fenmui.c ├── microui.h ├── demo.c └── fenster.h ├── cursed ├── testmap.txt ├── map.h ├── Makefile ├── map.c └── cursed.c ├── picol ├── Makefile ├── test.c └── picol.html ├── README.md └── LICENSE /domsson/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.x.h 3 | test.gif 4 | -------------------------------------------------------------------------------- /editor/icons.pcx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wernsey/junk-drawer/main/editor/icons.pcx -------------------------------------------------------------------------------- /editor/walls.pcx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wernsey/junk-drawer/main/editor/walls.pcx -------------------------------------------------------------------------------- /pocketmod/page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wernsey/junk-drawer/main/pocketmod/page.png -------------------------------------------------------------------------------- /screens/disable.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wernsey/junk-drawer/main/screens/disable.ico -------------------------------------------------------------------------------- /screens/enable.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wernsey/junk-drawer/main/screens/enable.ico -------------------------------------------------------------------------------- /screens/little.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wernsey/junk-drawer/main/screens/little.ico -------------------------------------------------------------------------------- /domsson/cellphone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wernsey/junk-drawer/main/domsson/cellphone.png -------------------------------------------------------------------------------- /domsson/oldschool.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wernsey/junk-drawer/main/domsson/oldschool.png -------------------------------------------------------------------------------- /domsson/futuristic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wernsey/junk-drawer/main/domsson/futuristic.png -------------------------------------------------------------------------------- /mybas/examples/arrays3.bas: -------------------------------------------------------------------------------- 1 | 10 LET A(5) = 10 2 | 11 A(1) = 333 3 | 20 FOR I=1 TO 10 : PRINT "A(" I ") is " A(I): NEXT I -------------------------------------------------------------------------------- /mybas/examples/cmp2.bas: -------------------------------------------------------------------------------- 1 | 4 DIM A, X, Y 2 | 10 GOSUB 60 3 | 20 END 4 | 60 LET X = A ^ 2 5 | 61 LET Y = X ^ 2 6 | 62 RETURN -------------------------------------------------------------------------------- /mybas/examples/arrays4.bas: -------------------------------------------------------------------------------- 1 | 10 DIM B(20,30) 2 | 20 LET B(1,22) = 1 * 100 + 22 3 | 25 LET B(2,1) = 3333 4 | 30 PRINT B(1,22) 5 | 30 PRINT B(2,1) -------------------------------------------------------------------------------- /mybas/examples/tester.bas: -------------------------------------------------------------------------------- 1 | 10 x = 5: y = 0 2 | 20 if x < 10 then print "x is " x : let y = 20 : goto 30 3 | 25 let y = 10 4 | 30 print "x is " x " and y is " y -------------------------------------------------------------------------------- /screens/screens.rc: -------------------------------------------------------------------------------- 1 | #include "screens.h" 2 | 3 | ID_APP_ICON ICON "little.ico" 4 | ID_TRAY_APP_ICON1 ICON "enable.ico" 5 | ID_TRAY_APP_ICON2 ICON "disable.ico" 6 | -------------------------------------------------------------------------------- /sbasic/getopt.h: -------------------------------------------------------------------------------- 1 | extern int opterr; 2 | extern int optind; 3 | extern int optopt; 4 | extern char *optarg; 5 | 6 | int getopt(int argc, char **argv, char *opts); 7 | -------------------------------------------------------------------------------- /snippets/getopt.h: -------------------------------------------------------------------------------- 1 | extern int opterr; 2 | extern int optind; 3 | extern int optopt; 4 | extern char *optarg; 5 | 6 | int getopt(int argc, char **argv, char *opts); 7 | -------------------------------------------------------------------------------- /editor/editor.h: -------------------------------------------------------------------------------- 1 | 2 | extern int vwidth, vheight; 3 | 4 | extern void error(const char *fmt, ...); 5 | extern void info(const char *fmt, ...); 6 | 7 | extern void putpix(int x, int y, unsigned int color); 8 | -------------------------------------------------------------------------------- /mybas/examples/arrays.bas: -------------------------------------------------------------------------------- 1 | 10 DIM A(10) 2 | 20 DIM B(20,30) 3 | 30 PRINT "A(5) is " A(5) 4 | 40 LET A(5) = 5 5 | 50 PRINT "A(5) is " A(5) 6 | 60 LET x = A(4+1) 7 | 70 PRINT "X is " x 8 | 80 PRINT "A(15) is " A(15) 9 | -------------------------------------------------------------------------------- /mybas/examples/print.bas: -------------------------------------------------------------------------------- 1 | 1 DIM x 2 | 2 LET x = 222 3 | 5 PRINT 4 | 10 PRINT x 5 | 20 PRINT x, 6 | 30 PRINT x, 10 7 | 30 PRINT x; 10 8 | 40 PRINT x 10 9 | 40 PRINT "FOOo", 10 10 | 40 PRINT "X is " x "; 5+5 is " 5 + 5 -------------------------------------------------------------------------------- /sbasic/test/io.bas: -------------------------------------------------------------------------------- 1 | REM If you embed the interpreter in another program then 2 | REM you can disable the INPUT and PRINT statements, in 3 | REM which case these won't work 4 | INPUT "Name?", name$ 5 | PRINT "hello, ", name$ 6 | -------------------------------------------------------------------------------- /editor/tiles.csv: -------------------------------------------------------------------------------- 1 | row,column,layers,category 2 | 0,0,0xFF,walls 3 | 0,1,0xFF,walls 4 | 0,2,0xFF,walls 5 | 0,3,0xFF,walls 6 | 1,0,0xFF,walls 7 | 1,1,0xFF,walls 8 | 2,0,0xFF,floor 9 | 2,1,0xFF,upper 10 | 2,2,0xFF,floor 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | *.o 3 | *.obj 4 | *.res 5 | 6 | *.lib 7 | *.a 8 | 9 | *.exe 10 | *.out 11 | 12 | .gdbinit 13 | *.log 14 | 15 | *~ 16 | cursed/*.wip 17 | pocketmod/*.pdf 18 | fenster-pocketmod/songs 19 | sbasic/docs.md.html 20 | *.zip -------------------------------------------------------------------------------- /mybas/examples/work.bas: -------------------------------------------------------------------------------- 1 | 10 DIM X,Y 2 | 20 FOR X = 1 TO 10 3 | 30 PRINT X 4 | 40 NEXT X 5 | 50 FOR Y = 1 TO 10 6 | 60 PRINT Y 7 | 70 NEXT Y 8 | 80 FOR X = 1 TO 10 9 | 90 FOR Y = 1 TO 10 10 | 100 PRINT X, Y 11 | 110 NEXT Y 12 | 120 NEXT X -------------------------------------------------------------------------------- /mybas/examples/arrays2.bas: -------------------------------------------------------------------------------- 1 | 10 DIM B(20,30) 2 | 20 FOR I = 1 TO 20 3 | 30 FOR J = 1 TO 30 4 | 40 LET B(I,J) = I * 100 + J 5 | 50 NEXT J 6 | 60 NEXT I 7 | 70 FOR I = 1 TO 20 8 | 80 FOR J = 1 TO 30 9 | 90 PRINT "B(" I "," J ") is " B(I,J) "(" (I * 100 + J) ")" 10 | 100 NEXT J 11 | 110 NEXT I 12 | 13 | -------------------------------------------------------------------------------- /editor/README.md: -------------------------------------------------------------------------------- 1 | # Editor 2 | 3 | An interface between ZSerge's [fenster][] framework and Sean Barret's [stb_tilemap_editor.h][] 4 | to create a simple level editor for 2D games. 5 | 6 | [fenster]: https://github.com/zserge/fenster 7 | [stb_tilemap_editor.h]: https://github.com/nothings/stb/blob/master/stb_tilemap_editor.h 8 | -------------------------------------------------------------------------------- /editor/image.h: -------------------------------------------------------------------------------- 1 | typedef struct { 2 | int w, h; 3 | int palcount; 4 | unsigned char palette[ 768 ]; 5 | uint8_t pixels[0]; 6 | } image_t; 7 | 8 | image_t *img_create(int w, int h); 9 | image_t *loadpcx( char const* filename); 10 | 11 | void blit_image(image_t *img, int dx, int dy, int dw, int dh, int sx, int sy, int sw, int sh); 12 | -------------------------------------------------------------------------------- /mybas/examples/loops.bas: -------------------------------------------------------------------------------- 1 | 10 DIM X,Y 2 | 15 GOTO 45 3 | 19 PRINT "Second" 4 | 20 FOR X = 1 TO 10 5 | 30 PRINT X 6 | 40 NEXT X 7 | 41 RETURN 8 | 45 PRINT "First" 9 | 46 GOSUB 19 10 | 47 PRINT "Third" 11 | 48 PRINT "X is"; X 12 | 50 READ X, Y 13 | 60 PRINT X, Y 14 | 70 DATA 5, 6, 7 15 | 75 REM END 16 | 80 PRINT "X is" X "and 5 + 3 is" 5+3 -------------------------------------------------------------------------------- /sbasic/test/cond.bas: -------------------------------------------------------------------------------- 1 | A = 1 2 | B = 2 3 | C = 0 4 | PRINT "" + a + " " + b + " " + c 5 | 6 | REM "these kinds of expressions didn't always work" 7 | IF A then print "A" 8 | IF A = 1 then print "A=1" 9 | IF A = 2 then print "A=2" 10 | IF B then print "B" 11 | IF C then print "C" 12 | IF A AND B then print "A AND B" 13 | IF A AND C then print "A AND C" 14 | IF A OR C then print "A OR C" 15 | 16 | PRINT "~ fin ~" 17 | -------------------------------------------------------------------------------- /mybas/examples/test.bas: -------------------------------------------------------------------------------- 1 | 5 REM https://github.com/norvig/pytudes/blob/master/ipynb/BASIC.ipynb 2 | 10 REM POWER TABLE 3 | 11 DATA 8, 4 4 | 15 READ N0, P0 5 | 20 PRINT "N", 6 | 25 FOR P = 2 TO P0 7 | 30 PRINT "N ^" P, 8 | 35 NEXT P 9 | 40 PRINT "SUM" 10 | 45 LET S = 0 11 | 50 FOR N = 2 TO N0 12 | 55 PRINT N, 13 | 60 FOR P = 2 TO P0 14 | 65 LET S = S + N ^ P 15 | 70 PRINT N ^ P, 16 | 75 NEXT P 17 | 80 PRINT S 18 | 85 NEXT N 19 | 99 END -------------------------------------------------------------------------------- /fenster-pocketmod/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS ?= -Wall -Wextra -std=c99 2 | 3 | ifeq ($(OS),Windows_NT) 4 | LDFLAGS = -lgdi32 -lwinmm 5 | else 6 | UNAME_S := $(shell uname -s) 7 | ifeq ($(UNAME_S),Darwin) 8 | LDFLAGS = -framework Cocoa -framework AudioToolbox 9 | else 10 | LDFLAGS = -lX11 -lasound 11 | endif 12 | endif 13 | 14 | main: main.c fenster.h fenster_audio.h pocketmod.h 15 | $(CC) main.c -I../.. -o $@ $(CFLAGS) $(LDFLAGS) 16 | 17 | clean: 18 | -rm -rf main main.exe -------------------------------------------------------------------------------- /mybas/examples/cmp.bas: -------------------------------------------------------------------------------- 1 | 4 DIM X, Y, B, Z 2 | 5 DATA 1 2 3 4 3 | 5 DATA 5 6 7 8 4 | 5 DATA 1 2 3 4 5 | 5 DATA 5 6 7 8 6 | 5 DATA 1 2 3 4 7 | 5 DATA 5 6 7 8 8 | 6 DEF FNA(X) = X*2 + B 9 | 7 LET Y = FNA(123) 10 | 10 LET X = (5 + 6) * 7 11 | 20 IF X = 5 THEN 10 12 | 30 READ X,Y,Z 13 | 40 IF X <> 5 THEN 10 14 | 50 END 15 | 1000 DEF FNS(A) = SIN(A * 22/7 * 180) + 1 16 | 1001 DEF FNC(A) = COS(A * 22/7 * 180) 17 | 55 DIM A 18 | 60 LET X = A ^ 2 19 | 70 IF X > 5 THEN LET X = 6 20 | 55 DIM A -------------------------------------------------------------------------------- /fenster-microui/README.md: -------------------------------------------------------------------------------- 1 | # fenster-microui 2 | 3 | An interface between ZSerge's [fenster][] framework and RXI's [microui][] 4 | immediate mode GUI toolkit for quick cross-platform GUI programs. 5 | 6 | The [Cushion][] font comes from DamienG's [ZX-Origins][zxorigins] collection. 7 | 8 | [fenster]: https://github.com/zserge/fenster 9 | [microui]: https://github.com/rxi/microui 10 | [cushion]: https://damieng.com/typography/zx-origins/cushion/ 11 | [zxorigins]: https://damieng.com/zx-origins 12 | -------------------------------------------------------------------------------- /fenster-microui/fenmui.h: -------------------------------------------------------------------------------- 1 | #ifdef FENSTER_H 2 | #ifdef MICROUI_H 3 | 4 | void fenmui_init(struct fenster *fp, mu_Context * ctxp); 5 | 6 | void fenmui_events(void); 7 | 8 | void fenmui_draw(void); 9 | 10 | void r_clear(mu_Color color); 11 | void r_set_clip_rect(mu_Rect rect); 12 | void r_draw_rect(mu_Rect rect, mu_Color color); 13 | void r_draw_icon(int id, mu_Rect rect, mu_Color color); 14 | void r_draw_text(const char *text, mu_Vec2 pos, mu_Color color); 15 | 16 | #endif 17 | #endif 18 | 19 | -------------------------------------------------------------------------------- /mybas/hash.h: -------------------------------------------------------------------------------- 1 | #define HASH_SIZE 31 // Must be prime 2 | 3 | typedef struct hash_element { 4 | char *name; 5 | void *data; 6 | struct hash_element *next; 7 | } hash_element; 8 | 9 | typedef hash_element *hash_table[HASH_SIZE]; 10 | 11 | void ht_init(hash_table tbl); 12 | void ht_destroy(hash_table tbl, void (*cfun)(void *)); 13 | void *ht_get(hash_table tbl, const char *name); 14 | void ht_put(hash_table tbl, const char *name, void *val); 15 | 16 | const char *ht_next(hash_table tbl, const char *key); -------------------------------------------------------------------------------- /mybas/bas.h: -------------------------------------------------------------------------------- 1 | enum Symbol { 2 | FEND = 0, NL, ID, NUM, STR, ARR, 3 | LET,READ,DATA,PRINT,GOTO,IF,FOR,NEXT,END, 4 | DEF,GOSUB,RETURN,DIM,REM,TO,THEN,STEP,STOP, 5 | 6 | SIN=256,COS,TAN,ATN,EXP,ABS,LOG,SQR,RND,INT,FN,FUN, 7 | PROG, LINE, GTE, LTE, NEQ, EQ, 8 | VARIABLE, STMTS, 9 | }; 10 | 11 | extern const char *Filename; 12 | 13 | extern void error(const char *msg, ...); 14 | extern const char *nodename(int type); 15 | extern int Line, BasicLine; 16 | 17 | struct Node; 18 | 19 | extern void compile(struct Node *node); 20 | -------------------------------------------------------------------------------- /pocketmod/Makefile: -------------------------------------------------------------------------------- 1 | 2 | CFLAGS= -c -Wall -g -O0 3 | LDFLAGS= -lhpdf 4 | 5 | 6 | # Add your source files here: 7 | SOURCES=modder.c 8 | OBJECTS=$(SOURCES:.c=.o) 9 | EXECUTABLE=modder 10 | ifeq ($(OS),Windows_NT) 11 | EXECUTABLE := $(EXECUTABLE).exe 12 | else 13 | endif 14 | 15 | all: $(EXECUTABLE) 16 | 17 | debug: 18 | make BUILD=debug 19 | 20 | $(EXECUTABLE): $(OBJECTS) 21 | $(CC) -o $@ $^ $(LDFLAGS) 22 | 23 | .c.o: 24 | $(CC) $(CFLAGS) $< -o $@ 25 | 26 | modder.o: modder.c 27 | 28 | .PHONY : clean 29 | 30 | clean: 31 | -rm -f $(EXECUTABLE) 32 | -rm -f *.o 33 | -rm -f *~ *.bak *.log *.ppm 34 | -------------------------------------------------------------------------------- /sbasic/test/subs.bas: -------------------------------------------------------------------------------- 1 | ' Tests for the `-u` command line parameter 2 | ' 3 | ' ./basic -u baz -u err -u foo subs.bas 4 | 5 | PRINT "Hello World" 6 | 7 | x = 1 8 | 9 | call &baz 10 | 11 | PRINT "~ fin. ~" 12 | 13 | END 14 | 15 | @foo 16 | PRINT "in foo: ", x 17 | x = x + 1 18 | RETURN 19 | 20 | ' If you're calling a sub through `sb_gosub` you 21 | ' can use either END or RETURN: 22 | 23 | @bar 24 | PRINT "in bar: ", x 25 | x = x * 2 26 | END 27 | 28 | @baz 29 | PRINT "in baz: ", x 30 | GOSUB foo 31 | x = - x + 3 32 | RETURN 33 | 34 | @err 35 | x = -100 36 | ERROR "This is a test" 37 | END 38 | -------------------------------------------------------------------------------- /cursed/testmap.txt: -------------------------------------------------------------------------------- 1 | # Cursed map file. 2 | CURSED 1 3 | 20 20 4 | 11111222212111111111 5 | 1 1 6 | 1 1 7 | 1 1 8 | 1 1 9 | # 1 10 | 1 1 11 | 1 1 12 | 1 1 13 | 1 1 14 | 1 1 15 | 1 1 16 | 1 1 17 | 1 1 18 | 1 1 19 | 1 1 20 | 1 1 21 | 1 1 22 | 1 1 23 | 11111111111111111111 24 | # Meta data, as key : value pairs 25 | meta { 26 | } 27 | -------------------------------------------------------------------------------- /sbasic/test/seqio.bas: -------------------------------------------------------------------------------- 1 | ' The interpreter supports sequential text I/O 2 | ' through the OPEN, READ, WRITE and CLOSE functions 3 | 4 | f = OPEN("data.txt", "w") 5 | PRINT("Opened file for writing as #" + f) 6 | WRITE(f, "Hello world!", 123, "some data") 7 | WRITE(f, "A string", 456, "more data") 8 | WRITE(f, "foo", 42, "A string value\nwith a newline") 9 | CLOSE(f) 10 | 11 | PRINT "Data written." 12 | 13 | f = OPEN("data.txt", "r") 14 | PRINT("Opened file for reading as #" + f) 15 | FOR i = 1 TO 6 16 | PRINT("Field " + i + ": " + READ(f)) 17 | NEXT 18 | 19 | READ f, &s$, &i, &n$ 20 | PRINT "Read: " + s$ + "; " + i + "; " + n$ 21 | 22 | CLOSE(f) 23 | -------------------------------------------------------------------------------- /screens/README.md: -------------------------------------------------------------------------------- 1 | # ScreenS 2 | 3 | Takes screenshots of your Windows desktop. 4 | 5 | Press `Alt+S` to save a screenshot in the directory from where the executable was run. 6 | 7 | There is an icon in the system tray from where you can control the application. 8 | 9 | If you have an issue with Windows 10 that the whole screen is not captured, 10 | you should set the scaling to 100% in the display settings. 11 | 12 | The icons are from the famfamfam [mini][] icons set. The link seems to be inactive now, 13 | so here's an [archive link][mini-archive]. 14 | 15 | [mini]: http://www.famfamfam.com/lab/icons/mini/ 16 | [mini-archive]: https://web.archive.org/web/20051016005539if_/http://www.famfamfam.com/lab/icons/mini/ -------------------------------------------------------------------------------- /screens/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS= -DNO_FONTS -c -I../common 2 | LFLAGS= -mwindows -lwtsapi32 3 | 4 | ifeq ($(BUILD),debug) 5 | # Debug mode: Unoptimized and with debugging symbols 6 | CFLAGS += -O0 -g 7 | LFLAGS += 8 | else 9 | # Release mode: Optimized and stripped of debugging info 10 | CFLAGS += -O2 -DNDEBUG 11 | LFLAGS += -s 12 | endif 13 | 14 | all: screens 15 | 16 | SOURCES= screens.c 17 | 18 | OBJECTS=$(SOURCES:.c=.o) 19 | 20 | screens : $(OBJECTS) screens.res 21 | gcc -o $@ $^ $(LFLAGS) 22 | 23 | screens.res : screens.rc little.ico enable.ico disable.ico 24 | windres $< -O coff -o $@ 25 | 26 | screens.o : screens.c screens.h 27 | 28 | clean: 29 | -rm -rf screens.exe xtract.exe 30 | -rm -rf *.o 31 | -rm -rf *.res -------------------------------------------------------------------------------- /mybas/ast.h: -------------------------------------------------------------------------------- 1 | enum NodeType {nt_opr, nt_num, nt_str, nt_id}; 2 | 3 | typedef struct Node { 4 | int line, basic_line; 5 | enum NodeType type; 6 | union { 7 | char *str; 8 | double num; 9 | struct { 10 | int op; /* operator */ 11 | int nc, ac; /* num children, allocated children */ 12 | struct Node **children; 13 | } opr; 14 | } u; 15 | } Node; 16 | 17 | Node *node_string(char *s); 18 | 19 | Node *node_id(char *i); 20 | 21 | Node *node_number(double num); 22 | 23 | Node *node_operator(int op, int nargs, ...); 24 | 25 | Node *node_add_child(Node *parent, Node *child); 26 | 27 | void free_node(Node *node); 28 | 29 | void print_tree(Node *node); 30 | -------------------------------------------------------------------------------- /editor/test.xpm: -------------------------------------------------------------------------------- 1 | /* XPM */ 2 | static char* pixmap[] = { 3 | "32 16 3 1", 4 | " c #000000", 5 | "! c #0000a8", 6 | "# c #dfdfdf", 7 | " ", 8 | " ", 9 | " !!!!! ", 10 | " !!!!!!!!!!!", 11 | " #### !", 12 | " ## ## !", 13 | " ## #### ## ## #### !", 14 | " #### ## ## ## ## ## ## !", 15 | " ## ## ## ## ## ######!! ", 16 | " ## ## ## ## #### ## ! ", 17 | " #### ### ## ## #####! ", 18 | " ! ", 19 | " !!!!!!!!!!! ! ", 20 | " !!!! ! ", 21 | " !!!!!!!!! ", 22 | " " 23 | }; 24 | -------------------------------------------------------------------------------- /cursed/map.h: -------------------------------------------------------------------------------- 1 | 2 | #define DEFAULT_WIDTH 20 3 | #define DEFAULT_HEIGHT 20 4 | 5 | #define APP_NAME "CURSED" 6 | #define VERSION 1 7 | 8 | #define MAX_META 16 9 | 10 | struct tile { 11 | char c; 12 | char index; 13 | char state; 14 | char ctr; 15 | }; 16 | 17 | struct Map { 18 | int w, h; 19 | struct tile *tiles; 20 | 21 | // Meta data 22 | struct { 23 | char *key; 24 | char *value; 25 | } meta[MAX_META]; 26 | int nmeta; 27 | }; 28 | 29 | #define MAP_TILE(m,x,y) m->tiles[(y) * m->w + (x)] 30 | 31 | struct Map *create_map(int w, int h); 32 | 33 | void free_map(struct Map *m); 34 | 35 | struct tile *get_tile(struct Map *m, int x, int y); 36 | 37 | struct Map *open_map(const char *filename); 38 | 39 | int save_map(struct Map *m, const char *filename); 40 | -------------------------------------------------------------------------------- /picol/Makefile: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | 3 | # Run `make debug` for a debug build 4 | # BUILD=debug 5 | 6 | CFLAGS = -c -Wall 7 | LDFLAGS = -lm 8 | 9 | # Add your source files here: 10 | SOURCES=test.c 11 | 12 | EXECUTABLE=picol 13 | 14 | ifeq ($(BUILD),debug) 15 | # Debug 16 | CFLAGS += -O0 -g 17 | LDFLAGS += 18 | else 19 | # Release mode 20 | CFLAGS += -O2 -DNDEBUG 21 | LDFLAGS += -s 22 | endif 23 | 24 | OBJECTS=$(SOURCES:.c=.o) 25 | 26 | ifeq ($(OS),Windows_NT) 27 | EXECUTABLE:=$(EXECUTABLE).exe 28 | endif 29 | 30 | all: $(EXECUTABLE) 31 | 32 | debug: 33 | make BUILD=debug 34 | 35 | $(EXECUTABLE): $(OBJECTS) 36 | $(CC) $^ $(LDFLAGS) -o $@ 37 | 38 | .c.o: 39 | $(CC) $(CFLAGS) $< -o $@ 40 | 41 | # Add header dependencies here 42 | test.o: test.c picol.h 43 | 44 | .PHONY : clean 45 | 46 | clean: 47 | -rm -f $(EXECUTABLE) 48 | -rm -f *.o 49 | -------------------------------------------------------------------------------- /sbasic/test/database.bas: -------------------------------------------------------------------------------- 1 | ' The interpreter has a simple key-value-store 2 | ' database that allows you to insert a value with 3 | ' the `POKE("key", "value")` function, and then 4 | ' retrieve the value again with the `PEEK("key")` 5 | ' function. 6 | ' You specify the file to use as a database with the 7 | ' `-d` command line option. 8 | ' Run this file like so: $ ./basic -d demo.db test/database.bas 9 | 10 | RANDOMIZE 11 | 12 | ' `&x` is just syntactic sugar for `"x"`: 13 | X = peek(&x) 14 | PRINT "X is ", X 15 | POKE "X", X+1 16 | 17 | key$ = "A string value" 18 | POKE &key$, key$ 19 | PRINT "key$ is ", peek(&key$) 20 | 21 | Y = peek(&y) 22 | PRINT "Y is ", Y 23 | POKE "Y", RND() 24 | 25 | PRINT "Z is ", peek(&z) 26 | 27 | ' You can insert a value for Z through this command: 28 | ' $ ./ppdb test.db poke Z 'A Value For Z' 29 | 30 | PRINT "foo is ", peek(&foo) 31 | POKE &foo, X + Y 32 | 33 | -------------------------------------------------------------------------------- /mybas/Makefile: -------------------------------------------------------------------------------- 1 | #CC=C:\Tools\tcc\tcc 2 | CC=gcc 3 | 4 | BUILD=debug 5 | 6 | CFLAGS = -c -Wall 7 | LDFLAGS = 8 | 9 | # Add your source files here: 10 | SOURCES=bas.c ast.c hash.c compile.c 11 | 12 | OBJECTS=$(SOURCES:.c=.o) 13 | 14 | DISTFILE=mybas.zip 15 | 16 | ifeq ($(BUILD),debug) 17 | # Debug 18 | CFLAGS += -O0 -g 19 | LDFLAGS += 20 | else 21 | # Release mode 22 | CFLAGS += -O2 -DNDEBUG 23 | LDFLAGS += -s 24 | endif 25 | 26 | all: bas.exe 27 | 28 | debug: 29 | make BUILD=debug 30 | 31 | bas.exe: $(OBJECTS) 32 | $(CC) $^ $(LDFLAGS) -o $@ 33 | 34 | .c.o: 35 | $(CC) $(CFLAGS) $< -o $@ 36 | 37 | # Add header dependencies here 38 | bas.o : bas.c bas.h ast.h hash.h 39 | ast.o : ast.c ast.h bas.h 40 | hash.o : hash.c hash.h 41 | compile.o : compile.c bas.h ast.h hash.h 42 | 43 | .PHONY : clean 44 | 45 | clean: 46 | -rm -f *.exe $(DISTFILE) 47 | -rm -f *.o 48 | -rm -f *.b.c *.bas.c *~ 49 | 50 | dist: clean 51 | zip $(DISTFILE) * -------------------------------------------------------------------------------- /cursed/Makefile: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | CFLAGS= -c -Wall 3 | LDFLAGS= 4 | 5 | # For building with PDCurses in MinGW: 6 | CURSES_PATH=/c/libs/PDCurses-3.9/ 7 | CFLAGS += -I $(CURSES_PATH) 8 | CURSES_LIB = $(CURSES_PATH)/wincon/pdcurses.a 9 | 10 | # Add your source files here: 11 | SOURCES=cursed.c map.c 12 | OBJECTS=$(SOURCES:.c=.o) 13 | EXECUTABLE=cursed 14 | 15 | ifeq ($(BUILD),debug) 16 | # Debug 17 | CFLAGS += -O0 -g 18 | LDFLAGS += 19 | else 20 | # Release mode 21 | CFLAGS += -O2 -DNDEBUG 22 | LDFLAGS += -s 23 | endif 24 | 25 | all: $(EXECUTABLE) 26 | 27 | debug: 28 | make BUILD=debug 29 | 30 | $(EXECUTABLE): $(OBJECTS) 31 | $(CC) $(LDFLAGS) $(OBJECTS) $(CURSES_LIB) -o $@ 32 | 33 | .c.o: 34 | $(CC) $(CFLAGS) $< -o $@ 35 | 36 | # Add header dependencies here, like so: 37 | cursed.o : map.h 38 | map.o : map.h 39 | 40 | .PHONY : clean 41 | 42 | clean: wipe 43 | -rm -f *.o $(EXECUTABLE) $(EXECUTABLE).exe 44 | 45 | wipe: 46 | -rm -f *~ *.wip 47 | -------------------------------------------------------------------------------- /fenster-microui/Makefile: -------------------------------------------------------------------------------- 1 | EXECUTABLE=ui 2 | 3 | CFLAGS= -c -Wall -g -O0 4 | 5 | # Add your source files here: 6 | SOURCES = microui.c demo.c fenmui.c 7 | 8 | ifeq ($(OS),Windows_NT) 9 | LDFLAGS = -lgdi32 10 | EXECUTABLE := $(EXECUTABLE).exe 11 | else 12 | UNAME_S := $(shell uname -s) 13 | ifeq ($(UNAME_S),Darwin) 14 | LDFLAGS = -framework Cocoa 15 | else 16 | LDFLAGS = -lX11 -lm 17 | endif 18 | endif 19 | 20 | OBJECTS=$(SOURCES:.c=.o) 21 | 22 | 23 | all: $(EXECUTABLE) 24 | 25 | debug: 26 | make BUILD=debug 27 | 28 | $(EXECUTABLE): $(OBJECTS) 29 | $(CC) -o $@ $(OBJECTS) $(LDFLAGS) 30 | 31 | .c.o: 32 | $(CC) $(CFLAGS) $< -o $@ 33 | 34 | # use `make deps` to refresh these dependencies: 35 | microui.o: microui.c microui.h 36 | demo.o: demo.c microui.h fenster.h fenmui.h 37 | fenmui.o: fenmui.c microui.h atlas.inl fenster.h fenmui.h Cushion.h 38 | 39 | .PHONY : clean deps 40 | 41 | deps: 42 | @ $(CC) -MM $(SOURCES) 43 | 44 | clean: 45 | -rm -f *.o $(EXECUTABLE) 46 | -------------------------------------------------------------------------------- /screens/screens.h: -------------------------------------------------------------------------------- 1 | #define ID_APP_ICON 5000 2 | #define ID_TRAY_APP_ICON1 5001 3 | #define ID_TRAY_APP_ICON2 5002 4 | 5 | #define APPLICATION_TITLE "Screenshot" 6 | 7 | /* 8 | http://blogs.msdn.com/b/calvin_hsia/archive/2005/08/02/446742.aspx 9 | https://github.com/msysgit/msysgit/blob/master/mingw/include/wtsapi32.h 10 | */ 11 | #define WTS_CONSOLE_CONNECT 0x1 12 | #define WTS_CONSOLE_DISCONNECT 0x2 13 | #define WTS_REMOTE_CONNECT 0x3 14 | #define WTS_REMOTE_DISCONNECT 0x4 15 | #define WTS_SESSION_LOGON 0x5 16 | #define WTS_SESSION_LOGOFF 0x6 17 | #define WTS_SESSION_LOCK 0x7 18 | #define WTS_SESSION_UNLOCK 0x8 19 | #define WTS_SESSION_REMOTE_CONTROL 0x9 20 | #define NOTIFY_FOR_ALL_SESSIONS 1 21 | #define NOTIFY_FOR_THIS_SESSION 0 22 | 23 | #define WM_WTSSESSION_CHANGE 0x02B1 24 | 25 | BOOL WINAPI WTSRegisterSessionNotification(HWND hWnd, DWORD dwFlags); -------------------------------------------------------------------------------- /sbasic/test/test.bas: -------------------------------------------------------------------------------- 1 | RANDOMIZE() 2 | 3 | PRINT "It Works" 4 | PRINT "Foo$ is '", Foo$, "'" 5 | GOSUB sub 6 | 7 | PRINT "Random numbers: "; RND(20); RND(10,20); RND() 8 | 9 | ' This is possible because we don't check for EOL after the statements 10 | a = 5 b = 6 11 | print "a = ", a, "; b = ", b 12 | 13 | foo$ = "The output value" 14 | 15 | PRINT "String\twith\nxxxxx\rescapes and \"quotes\" and \\slashes\\ and hex escapes: '\x5A\x7A\x3F'" 16 | 17 | INPUT "Enter a number [1-3], 4 for error> ", A 18 | ON A GOSUB sub1, sub2, sub3, sub4 19 | 20 | ' \e can be used as escape for VT100-style terminals 21 | PRINT "\e[31mThe end\e[0m" 22 | 23 | ' https://en.wikipedia.org/wiki/Box-drawing_character#Unix,_CP/M,_BBS 24 | PRINT "\e(0\x6A\x6B\x6C\x6D\x6E\x71\x74\x75\x76\x77\x78\e(B" 25 | 26 | END 27 | 28 | @sub PRINT "In subroutine" 29 | RETURN 30 | @sub1 PRINT "In subroutine 1" 31 | RETURN 32 | @sub2 PRINT "In subroutine 2" 33 | RETURN 34 | @sub3 PRINT "In subroutine 3" 35 | RETURN 36 | 37 | @sub4 ERROR "Test error" 38 | -------------------------------------------------------------------------------- /domsson/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS= -I../common 2 | LFLAGS= 3 | 4 | ifeq ($(BUILD),debug) 5 | # Debug mode: Unoptimized and with debugging symbols 6 | CFLAGS += -O0 -g 7 | LFLAGS += 8 | else 9 | # Release mode: Optimized and stripped of debugging info 10 | CFLAGS += -O2 -DNDEBUG 11 | LFLAGS += -s 12 | endif 13 | 14 | DOMSSON=domsson 15 | TEST=test 16 | ifeq ($(OS),Windows_NT) 17 | DOMSSON := $(DOMSSON).exe 18 | TEST := $(TEST).exe 19 | endif 20 | 21 | FONTS=cellphone.x.h futuristic.x.h oldschool.x.h 22 | 23 | all: $(DOMSSON) $(TEST) $(FONTS) 24 | 25 | $(DOMSSON) : domsson.o 26 | gcc -o $@ $^ $(LFLAGS) 27 | 28 | $(TEST) : test.o 29 | gcc -o $@ $^ $(LFLAGS) 30 | 31 | %.x.h: %.png | $(DOMSSON) 32 | ./$(DOMSSON) $^ > $@ 33 | 34 | %.o: %.c 35 | $(CC) $(CFLAGS) -c $< -o $@ 36 | 37 | domsson.o: domsson.c ../common/bmph.h ../common/stb_image.h 38 | test.o: test.c ../common/bmph.h oldschool.x.h futuristic.x.h cellphone.x.h 39 | 40 | 41 | clean: 42 | -rm -rf $(DOMSSON) $(TEST) 43 | -rm -rf *.o 44 | -rm -rf *.x.h 45 | -rm -rf test.gif -------------------------------------------------------------------------------- /snippets/rot13.c: -------------------------------------------------------------------------------- 1 | /* 2 | I swear I had a legitimate use for this. 3 | */ 4 | #include 5 | #include 6 | 7 | int main(int argc, char *argv[]) { 8 | FILE *i = stdin; 9 | FILE *o = stdout; 10 | 11 | /* Note that you might have \r\n related issues 12 | on windows if you use stdin and sdout directly. 13 | It caused me a bit of headscratching. 14 | */ 15 | 16 | if(argc > 1) i = fopen(argv[1], "rb"); 17 | if(!i) { 18 | perror(argv[1]); 19 | } 20 | if(argc > 2) o = fopen(argv[2], "wb"); 21 | if(!o) { 22 | perror(argv[2]); 23 | } 24 | 25 | while(!feof(i)) { 26 | int c = fgetc(i); 27 | if(c == EOF) break; 28 | if(isalpha(c)) { 29 | if(isupper(c)) { 30 | c = ((c - 'A') + 13) % 26 + 'A'; 31 | } else { 32 | c = ((c - 'a') + 13) % 26 + 'a'; 33 | } 34 | } 35 | putc(c, o); 36 | } 37 | 38 | if(i != stdin) fclose(i); 39 | if(o != stdout) fclose(o); 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /editor/Makefile: -------------------------------------------------------------------------------- 1 | EXECUTABLE=editor 2 | 3 | CFLAGS= -c -Wall -g -O0 4 | 5 | # Add your source files here: 6 | SOURCES=editor.c image.c 7 | 8 | ifeq ($(OS),Windows_NT) 9 | SOURCES += ../snippets/getopt.c 10 | LDFLAGS = -lgdi32 11 | EXECUTABLE := $(EXECUTABLE).exe 12 | else 13 | UNAME_S := $(shell uname -s) 14 | ifeq ($(UNAME_S),Darwin) 15 | LDFLAGS = -framework Cocoa 16 | else 17 | LDFLAGS = -lX11 -lm 18 | endif 19 | endif 20 | 21 | OBJECTS=$(SOURCES:.c=.o) 22 | 23 | 24 | all: $(EXECUTABLE) 25 | 26 | debug: 27 | make BUILD=debug 28 | 29 | $(EXECUTABLE): $(OBJECTS) 30 | $(CC) -o $@ $(OBJECTS) $(LDFLAGS) 31 | 32 | .c.o: 33 | $(CC) $(CFLAGS) $< -o $@ 34 | 35 | # use `make deps` to refresh these dependencies: 36 | 37 | editor.o: editor.c editor.h fenster.h image.h stb_tilemap_editor.h 38 | image.o: image.c editor.h image.h 39 | 40 | editor.o: CFLAGS += -Wno-unused-function -Wno-unused-variable \ 41 | -Wno-misleading-indentation -Wno-comment \ 42 | -Wno-unused-but-set-variable 43 | 44 | .PHONY : clean deps 45 | 46 | deps: 47 | @ $(CC) -MM $(SOURCES) 48 | 49 | clean: 50 | -rm -f *.o $(EXECUTABLE) 51 | -rm -f *~ *.log *.ppm 52 | -------------------------------------------------------------------------------- /sbasic/test/lists.bas: -------------------------------------------------------------------------------- 1 | ' The interpreter provides several functions that allows 2 | ' you to treat strings like "a,b,c" as a list of items 3 | ' `a`, `b` and `c` 4 | ' The functions aren't terribly efficient but opens up some 5 | ' possibilities that wouldn't otherwise be available. 6 | 7 | L$ = LIST("foo", "bar", "baz") 8 | 9 | PRINT "L$ = ", L$, "; len=", llen(l$) 10 | 11 | L$ = LADD(L$, "fred") 12 | L$ = LADD(L$, "quux") 13 | 14 | PRINT "L$ = ", L$, "; len=", llen(l$) 15 | 16 | PRINT "LGET(): ", lget(l$, 3) 17 | PRINT "Lfind(baz): ", lfind(l$, "baz") 18 | PRINT "Lfind(fob): ", lfind(l$, "fob") 19 | 20 | PRINT "LLEN(''): ", llen("") 21 | PRINT "LLEN(','): ", llen(",") 22 | PRINT "LLEN('a,'): ", llen("a,") 23 | PRINT "LLEN(',,'): ", llen(",,") 24 | PRINT "LLEN(',a,'): ", llen(",a,") 25 | PRINT "Lget(',a,',1): ", lget(",a,",1) 26 | 27 | PRINT "LHEAD(L$): ", LHEAD(L$) 28 | PRINT "LTAIL(L$): ", LTAIL(L$) 29 | PRINT "LHEAD(''): ", LHEAD("") 30 | PRINT "LTAIL(''): ", LTAIL("") 31 | PRINT "LHEAD('a'): ", LHEAD("a") 32 | PRINT "LTAIL('a'): ", LTAIL("a") 33 | PRINT "LHEAD('a,'): ", LHEAD("a,") 34 | PRINT "LTAIL('a,'): ", LTAIL("a,") 35 | PRINT "LHEAD('a,b'): ", LHEAD("a,b") 36 | PRINT "LTAIL('a,b'): ", LTAIL("a,b") 37 | -------------------------------------------------------------------------------- /picol/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define PICOL_IMPLEMENTATION 5 | #include "picol.h" 6 | 7 | static int picolCommandEnd(struct picolInterp *i, int argc, char **argv, void *pd) { 8 | if (argc > 2) return picolArityErr(i,argv[0]); 9 | if(argc < 2) 10 | exit(0); 11 | else 12 | exit(atoi(argv[1])); 13 | return PICOL_OK; 14 | } 15 | 16 | int main(int argc, char **argv) { 17 | struct picolInterp interp; 18 | picolInitInterp(&interp); 19 | picolRegisterCoreCommands(&interp); 20 | 21 | picolRegisterCommand(&interp, "end", picolCommandEnd, NULL); 22 | 23 | if (argc == 1) { 24 | while(1) { 25 | char clibuf[1024]; 26 | int retcode; 27 | printf("picol> "); fflush(stdout); 28 | if (fgets(clibuf,1024,stdin) == NULL) return 0; 29 | retcode = picolEval(&interp,clibuf); 30 | if (interp.result[0] != '\0') 31 | printf("[%d] %s\n", retcode, interp.result); 32 | } 33 | } else if (argc == 2) { 34 | char buf[1024*16]; 35 | FILE *fp = fopen(argv[1],"r"); 36 | if (!fp) { 37 | perror("open"); exit(1); 38 | } 39 | buf[fread(buf,1,1024*16,fp)] = '\0'; 40 | fclose(fp); 41 | if (picolEval(&interp,buf) != PICOL_OK) printf("%s\n", interp.result); 42 | } 43 | return 0; 44 | } 45 | -------------------------------------------------------------------------------- /sbasic/test/bifs.bas: -------------------------------------------------------------------------------- 1 | REM Test for some built in functions 2 | X$ = "This Is An Example String" 3 | PRINT X$ 4 | PRINT "1234567890123456789012345" 5 | PRINT "LEN: ", len(X$) 6 | PRINT "INSTR('Example'): ", instr(X$, "Example") 7 | PRINT "INSTR('foo'): ", instr(X$, "foo") 8 | PRINT "MID(1,4): ", mid(x$, 1, 4) 9 | PRINT "MID(12,7): ", mid(x$, 12, 7) 10 | PRINT "MID(20,10): ", mid(x$, 20, 10) 11 | PRINT "LEFT(4): ", left(x$, 4) 12 | PRINT "RIGHT(6): ", right(x$, 6) 13 | PRINT "UCASE: ", ucase(X$) 14 | PRINT "LCASE: ", lcase(X$) 15 | PRINT "IIF(1,2): ", iif(1,2) 16 | PRINT "IIF(0,2): ", iif(0,2) 17 | PRINT "IIF(1,2,3): ", iif(1,2,3) 18 | PRINT "IIF(0,2,3): ", iif(0,2,3) 19 | PRINT "10 + 2: ", 10 + 2 20 | PRINT "\"10\" + 2: ", "10" + 2 21 | PRINT "10 + 2 + X$: ", 10 + 2 + X$ 22 | PRINT "\"10\" + 2 + X$: ", "10" + 2 + X$ 23 | PRINT "10 - 2: ", 10 - 2 24 | PRINT "\"10\" - 2: ", "10" - 2 25 | PRINT "\"Hello \" + \"World\": ", "Hello " + "World" 26 | PRINT "4^5: ", 4^5 27 | PRINT "5 + -120: ", 5 + -120 28 | PRINT "5 + +120: ", 5 + +120 29 | PRINT "INT(\"10\" + 2) + 8: ", INT("10" + 2) + 8 30 | PRINT "STR(10) + 2: ", STR(10) + 2 31 | 32 | PRINT "MUX(2,one,two,three): ", MUX(2,"one","two","three") 33 | PRINT "MUX(4,one,two,three): ", MUX(4,"one","two","three") 34 | PRINT "DEMUX(two,one,two,three): ", DEMUX("two","one","two","three") 35 | PRINT "DEMUX(four,one,two,three): ", DEMUX("four","one","two","three") 36 | -------------------------------------------------------------------------------- /domsson/README.md: -------------------------------------------------------------------------------- 1 | OpenGameArt user domsson has a couple of 5x7 bitmap fonts: 2 | 3 | * https://opengameart.org/content/ascii-bitmap-font-cellphone 4 | * https://opengameart.org/content/ascii-bitmap-font-futuristic 5 | * https://opengameart.org/content/ascii-bitmap-font-oldschool 6 | 7 | This program just converts them to C code. 8 | 9 | `bmph.h` comes from here: https://github.com/wernsey/bitmap/ 10 | You'll also need stb_image.h 11 | 12 | For example: If you assign the bytes to a unsigned char array 13 | called `fontbytes` and have a function called `putpixel(x,y)` 14 | that can draw a pixel at (x,y) on the screen, then to draw a 15 | character from this font, you can use a function like this: 16 | 17 | ``` 18 | static const unsigned char fontbytes[] = { 19 | #include "oldschool.x.h" 20 | }; 21 | 22 | void printchar(int x, int y, int c) { 23 | if(c < ' ' || c >= 128) c = '*'; 24 | 25 | int xs = x, ye = y + 7; 26 | const uint8_t *p = &fontbytes[ (c - 0x20) * 7 ]; 27 | for(; y < ye; y++, p++) { 28 | if(y < clip.y) continue; 29 | if(y >= clip.y + clip.h) break; 30 | for(int i = 0x10, x = xs; i; i >>= 1, x++) { 31 | if(x < clip.x) continue; 32 | if(x >= clip.x + clip.w) break; 33 | if(i & *p) { 34 | putpixel(x, y); 35 | } 36 | } 37 | } 38 | } 39 | ``` 40 | 41 | You might be curious as to why I'm bothering with this when 42 | `../common/bmp.h` already has a cromulent built-in bitmap font. 43 | Apparently I'm cursed to write the same computer programs over and 44 | over again. -------------------------------------------------------------------------------- /domsson/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define BMPH_IMPLEMENTATION 4 | #include "bmph.h" 5 | 6 | static const unsigned char oldschool[] = { 7 | #include "oldschool.x.h" 8 | }; 9 | static const unsigned char futuristic[] = { 10 | #include "futuristic.x.h" 11 | }; 12 | static const unsigned char cellphone[] = { 13 | #include "cellphone.x.h" 14 | }; 15 | 16 | static Bitmap *b; 17 | 18 | static unsigned int color = 0xFFFFFF; 19 | 20 | const unsigned char *font = oldschool; 21 | 22 | static void printchar(int x, int y, int c) { 23 | if(c < ' ' || c >= 128) c = '*'; 24 | 25 | BmRect clip = bm_get_clip(b); 26 | int xs = x, ye = y + 7; 27 | const uint8_t *p = &font[ (c - 0x20) * 7 ]; 28 | for(; y < ye; y++, p++) { 29 | if(y < clip.y0) continue; 30 | if(y >= clip.y1) break; 31 | for(int i = 0x10, x = xs; i; i >>= 1, x++) { 32 | if(x < clip.x0) continue; 33 | if(x >= clip.x1) break; 34 | if(i & *p) { 35 | bm_set(b, x, y, color); 36 | } 37 | } 38 | } 39 | } 40 | 41 | void printstr(int x, int y, const char *s) { 42 | int xs = x; 43 | for(; *s; s++) { 44 | if(*s == '\n') { 45 | x = xs; 46 | y += 8; 47 | } else if(*s == '\r') { 48 | x = xs; 49 | } else if(*s == '\t') { 50 | x += 4 * 5; 51 | } else { 52 | printchar(x, y, *s); 53 | x += 6; 54 | } 55 | } 56 | } 57 | 58 | int main(int argc, char *argv[]) { 59 | b = bm_create(100, 100); 60 | printstr(10, 10, "Hello World!"); 61 | font = futuristic; 62 | printstr(10, 20, "Hello World!"); 63 | font = cellphone; 64 | printstr(10, 30, "Hello World!"); 65 | bm_save(b, "test.gif"); 66 | return 0; 67 | } -------------------------------------------------------------------------------- /sbasic/README.md: -------------------------------------------------------------------------------- 1 | A tiny BASIC interpreter 2 | ======================== 3 | 4 | From Chapter 7 of "C Power User's Guide" by Herbert Schildt, 1988. 5 | 6 | The book can be found on [the Internet Archive](https://archive.org/details/cpowerusersguide00schi_0/mode/2up). 7 | 8 | This version has been modified to add several features: 9 | 10 | - Converted it to C89 standard 11 | - Extracted the code as an API so you can script other programs with it 12 | - Call C-functions 13 | - Long variable names 14 | - Identifiers as labels, denoted as `@label` 15 | - `string$` variables... 16 | - ...and `"string literals"` in expressions 17 | - Logical operators `AND`, `OR` and `NOT` 18 | - String concatenation operator (`'+'`) 19 | - `ON` statements 20 | - Comments with the `REM` keyword and `'` character 21 | - Comparison operators `<>`, `<=` and `>=` 22 | - Escape sequences in string literals 23 | - Functions base on those from [C64 BASIC](https://www.c64-wiki.com/wiki/BASIC) 24 | - [RND](https://www.c64-wiki.com/wiki/RND) 25 | - [LEN](https://www.c64-wiki.com/wiki/LEN) 26 | - [LEFT$](https://www.c64-wiki.com/wiki/LEFT$), [RIGHT$](https://www.c64-wiki.com/wiki/RIGHT$) 27 | and [MID$](https://www.c64-wiki.com/wiki/MID$) 28 | - [INSTR](https://www.c64-wiki.com/wiki/INSTR) 29 | - `upper(s$)` and `lower(s$)` functions. 30 | - `IIF(cond, trueVal, falseVal)` built-in function. 31 | - References through the `&` operator 32 | - `&var` is just syntactic sugar for `"var"` 33 | 34 | TODO 35 | ---- 36 | 37 | [ ] `get_token()` should make sure tokens don't exceed `TOKEN_SIZE` 38 | 39 | Notes 40 | ----- 41 | 42 | This statement does not work: 43 | 44 | ``` 45 | PRINT "Some text" REM a comment 46 | ``` 47 | 48 | I'm not sure if I should consider it a bug. 49 | 50 | Links 51 | ----- 52 | 53 | These articles might be interesting: 54 | 55 | * [BASIC Variables & Strings — with Commodore](https://www.masswerk.at/nowgobang/2020/commodore-basic-variables) 56 | * [Commodore 64 BASIC 2.0 garbage collection, strings assignments in loops](https://retro64.altervista.org/blog/commodore-64-basic-2-0-garbage-collection-strings-assignments-loop/) 57 | -------------------------------------------------------------------------------- /domsson/domsson.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /* 4 | OpenGameArt user domsson has a couple of 5x7 bitmap fonts: 5 | 6 | * https://opengameart.org/content/ascii-bitmap-font-cellphone 7 | * https://opengameart.org/content/ascii-bitmap-font-futuristic 8 | * https://opengameart.org/content/ascii-bitmap-font-oldschool 9 | 10 | This program just converts them to C code. 11 | 12 | `bmph.h` comes from here: https://github.com/wernsey/bitmap/ 13 | You'll also need stb_image.h 14 | 15 | For example: If you assign the bytes to a unsigned char array 16 | called `fontbytes` and have a function called `putpixel(x,y)` 17 | that can draw a pixel at (x,y) on the screen, then to draw a 18 | character from this font, you can use a function like this: 19 | 20 | ``` 21 | void printchar(int x, int y, int c) { 22 | if(c < ' ' || c >= 128) c = '*'; 23 | 24 | int xs = x, ye = y + 7; 25 | const uint8_t *p = &fontbytes[ (c - 0x20) * 7 ]; 26 | for(; y < ye; y++, p++) { 27 | if(y < clip.y) continue; 28 | if(y >= clip.y + clip.h) break; 29 | for(int i = 0x10, x = xs; i; i >>= 1, x++) { 30 | if(x < clip.x) continue; 31 | if(x >= clip.x + clip.w) break; 32 | if(i & *p) { 33 | putpixel(x, y); 34 | } 35 | } 36 | } 37 | } 38 | ``` 39 | */ 40 | 41 | #define USESTB 42 | #define BMPH_IMPLEMENTATION 43 | #include "bmph.h" 44 | 45 | int main(int argc, char *argv[]) { 46 | if(argc < 2) { 47 | fprintf(stderr, "usage: %s font.png\n", argv[0]); 48 | return 1; 49 | } 50 | 51 | Bitmap *b = bm_load(argv[1]); 52 | if(!b) { 53 | fprintf(stderr, "Couldn't open %s\n", argv[1]); 54 | return 1; 55 | } 56 | 57 | int chars_per_row = '2' - ' '; 58 | for(int c = ' '; c <= '~'; c++) { 59 | int row = (c - ' ') / chars_per_row; 60 | int col = (c - ' ') % chars_per_row; 61 | int x = col * 7 + 1; 62 | int y = row * 9 + 1; 63 | for(int j = 0; j < 7; j++) { 64 | unsigned char byte = 0; 65 | int mask = 1 << 4; 66 | for(int i = 0; i < 5; i++) { 67 | if(bm_get(b, x + i, y + j) & 0x00FFFFFF) 68 | byte |= mask; 69 | mask >>= 1; 70 | } 71 | printf("0x%02X, ", byte); 72 | } 73 | printf(" /* '%c' */\n", c); 74 | } 75 | 76 | bm_free(b); 77 | 78 | return 0; 79 | } -------------------------------------------------------------------------------- /sbasic/makefile: -------------------------------------------------------------------------------- 1 | EXECUTABLE= basic 2 | 3 | # Add your source files here: 4 | SOURCES=main.c sbasic.c listfuns.c getopt.c 5 | 6 | CFLAGS= -Wall -std=c89 7 | LDFLAGS= 8 | 9 | # Test programs for the support libs 10 | PPDB= ppdb 11 | SEQIO= seqio 12 | 13 | ifeq ($(OS),Windows_NT) 14 | EXECUTABLE := $(EXECUTABLE).exe 15 | PPDB := $(PPDB).exe 16 | SEQIO := $(SEQIO).exe 17 | else 18 | UNAME_S := $(shell uname -s) 19 | ifeq ($(UNAME_S),Darwin) 20 | LDFLAGS+= 21 | else 22 | LDFLAGS+= 23 | endif 24 | endif 25 | 26 | ifeq ($(BUILD),debug) 27 | # Debug 28 | CFLAGS += -O0 -g 29 | LDFLAGS += 30 | else 31 | # Release mode 32 | CFLAGS += -O2 -DNDEBUG 33 | LDFLAGS += -s 34 | endif 35 | 36 | OBJECTS=$(SOURCES:.c=.o) 37 | 38 | all: $(EXECUTABLE) $(PPDB) $(SEQIO) docs 39 | 40 | debug: 41 | make BUILD=debug 42 | 43 | $(EXECUTABLE): $(OBJECTS) 44 | $(CC) $(LDFLAGS) $(OBJECTS) -o $@ 45 | 46 | .c.o: 47 | $(CC) $(CFLAGS) -c $< -o $@ 48 | 49 | main.o: main.c getopt.h sbasic.h ppdb.h seqio.h 50 | sbasic.o: sbasic.c sbasic.h 51 | listfuns.o: listfuns.c sbasic.h 52 | getopt.o: getopt.c 53 | 54 | $(PPDB): ppdbmain.c ppdb.h 55 | $(CC) $(CFLAGS) $< -o $@ 56 | 57 | $(SEQIO): seqio.h 58 | $(CC) $(CFLAGS) -DSEQIO_TEST -o $@ -xc $< 59 | 60 | docs: docs.md.html 61 | 62 | docs.md.html : sbasic.h sbasic.c listfuns.c main.c 63 | @echo Generating docs... 64 | @echo '' > $@ 65 | @awk '/\/\*\*/{sub(/\/\*\*[[:space:]]*/,"");incomment=1} incomment && /\*\//{incomment=0;sub(/[[:space:]]*\*\/.*/,"");print} incomment && /^[[:space:]]*\*/{sub(/^[[:space:]]*\*[[:space:]]?/,""); print}' $^ >> $@ 66 | @echo '' >> $@ 67 | @echo '' >> $@ 68 | @echo '' >> $@ 69 | @echo '' >> $@ 70 | 71 | shar: sbasic.shar 72 | 73 | sbasic.shar: README.md Makefile *.c *.h test/*.bas 74 | shar -Cgzip $^ > $@ 75 | 76 | .PHONY : clean deps shar 77 | 78 | deps: 79 | @ $(CC) -MM $(SOURCES) 80 | 81 | clean: 82 | -rm -f *.o $(EXECUTABLE) docs.md.html 83 | -rm -f $(PPDB) $(SEQIO) sbasic.shar 84 | -rm -f *~ *.db seqio.out data.txt 85 | -------------------------------------------------------------------------------- /mybas/hash.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "hash.h" 6 | 7 | void ht_init(hash_table tbl) { 8 | int i; 9 | for(i = 0; i < HASH_SIZE; i++) 10 | tbl[i] = NULL; 11 | } 12 | 13 | static void free_element(hash_element* v, void (*cfun)(void *)) { 14 | if(!v) return; 15 | free_element(v->next, cfun); 16 | if(cfun) cfun(v->data); 17 | free(v->name); 18 | free(v); 19 | } 20 | 21 | void ht_destroy(hash_table tbl, void (*cfun)(void *)) { 22 | int i; 23 | for(i = 0; i < HASH_SIZE; i++) 24 | free_element(tbl[i], cfun); 25 | } 26 | 27 | static unsigned int hash(const char *s) { 28 | unsigned int i = 0x5555; 29 | for(;s[0];s++) 30 | i = i << 3 ^ (s[0] | (s[0] << 8)); 31 | return i % HASH_SIZE; 32 | } 33 | 34 | static hash_element *search(hash_table tbl, const char *name, int *bucket) { 35 | hash_element* v; 36 | unsigned int h = hash(name); 37 | if(tbl[h]) { 38 | for(v = tbl[h]; v; v = v->next) 39 | if(!strcmp(v->name, name)) { 40 | if(bucket) *bucket = h; 41 | return v; 42 | } 43 | } 44 | return NULL; 45 | } 46 | 47 | void *ht_get(hash_table tbl, const char *name) { 48 | struct hash_element* v = search(tbl, name, NULL); 49 | if(v) 50 | return v->data; 51 | return NULL; 52 | } 53 | 54 | void ht_put(hash_table tbl, const char *name, void *val) { 55 | hash_element* v = search(tbl, name, NULL); 56 | if(v) /* duplicates not allowed */ 57 | return; 58 | v = malloc(sizeof *v); 59 | unsigned int h = hash(name); 60 | v->name = strdup(name); 61 | v->data = val; 62 | v->next = tbl[h]; 63 | tbl[h] = v; 64 | } 65 | 66 | const char *ht_next(hash_table tbl, const char *key) { 67 | int f; 68 | hash_element *i; 69 | 70 | if (key == NULL) { 71 | for (f = 0; f < HASH_SIZE && !tbl[f]; f++); 72 | if (f >= HASH_SIZE) 73 | return NULL; 74 | assert (tbl[f]); 75 | return tbl[f]->name; 76 | } 77 | i = search(tbl, key, &f); 78 | if (!i) 79 | return NULL; 80 | if (i->next) 81 | return i->next->name; 82 | else { 83 | f++; 84 | while (f < HASH_SIZE && !tbl[f]) 85 | f++; 86 | if (f >= HASH_SIZE) 87 | return NULL; 88 | assert (tbl[f]); 89 | return tbl[f]->name; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /sbasic/getopt.c: -------------------------------------------------------------------------------- 1 | /* 2 | Modified from the public domain getopt() posted to comp.sources.misc: 3 | https://groups.google.com/g/comp.sources.misc/c/egycYBQHHWY/m/9mXy43Ojax8J 4 | 5 | There's an even older version here: 6 | https://groups.google.com/g/mod.std.unix/c/HQlsXpuokHE/m/PSJxR3CzMccJ 7 | 8 | I just cleaned it up and ANSIfied it. 9 | */ 10 | 11 | /* 12 | * Here's something you've all been waiting for: the AT&T public domain 13 | * source for getopt(3). It is the code which was given out at the 1985 14 | * UNIFORUM conference in Dallas. I obtained it by electronic mail 15 | * directly from AT&T. The people there assure me that it is indeed 16 | * in the public domain. 17 | * 18 | * There is no manual page. That is because the one they gave out at 19 | * UNIFORUM was slightly different from the current System V Release 2 20 | * manual page. The difference apparently involved a note about the 21 | * famous rules 5 and 6, recommending using white space between an option 22 | * and its first argument, and not grouping options that have arguments. 23 | * Getopt itself is currently lenient about both of these things White 24 | * space is allowed, but not mandatory, and the last option in a group can 25 | * have an argument. That particular version of the man page evidently 26 | * has no official existence, and my source at AT&T did not send a copy. 27 | * The current SVR2 man page reflects the actual behavor of this getopt. 28 | * However, I am not about to post a copy of anything licensed by AT&T. 29 | */ 30 | 31 | #include 32 | #include 33 | 34 | #define ERR(s, c) if(opterr){ fprintf(stderr, "%s%s%c\n", argv[0], s, c); } 35 | 36 | int opterr = 1; 37 | int optind = 1; 38 | int optopt; 39 | char *optarg; 40 | 41 | int getopt(int argc, char **argv, char *opts) { 42 | static int sp = 1; 43 | register int c; 44 | register char *cp; 45 | 46 | if(sp == 1) { 47 | if(optind >= argc || argv[optind][0] != '-' || argv[optind][1] == '\0') 48 | return -1; 49 | else if(strcmp(argv[optind], "--") == 0) { 50 | optind++; 51 | return -1; 52 | } 53 | } 54 | optopt = c = argv[optind][sp]; 55 | if(c == ':' || (cp=strchr(opts, c)) == 0) { 56 | ERR(": illegal option -- ", c); 57 | if(argv[optind][++sp] == '\0') { 58 | optind++; 59 | sp = 1; 60 | } 61 | return('?'); 62 | } 63 | if(*++cp == ':') { 64 | if(argv[optind][sp+1] != '\0') 65 | optarg = &argv[optind++][sp+1]; 66 | else if(++optind >= argc) { 67 | ERR(": option requires an argument -- ", c); 68 | sp = 1; 69 | return('?'); 70 | } else 71 | optarg = argv[optind++]; 72 | sp = 1; 73 | } else { 74 | if(argv[optind][++sp] == '\0') { 75 | sp = 1; 76 | optind++; 77 | } 78 | optarg = NULL; 79 | } 80 | return(c); 81 | } 82 | -------------------------------------------------------------------------------- /snippets/getopt.c: -------------------------------------------------------------------------------- 1 | /* 2 | Modified from the public domain getopt() posted to comp.sources.misc: 3 | https://groups.google.com/g/comp.sources.misc/c/egycYBQHHWY/m/9mXy43Ojax8J 4 | 5 | There's an even older version here: 6 | https://groups.google.com/g/mod.std.unix/c/HQlsXpuokHE/m/PSJxR3CzMccJ 7 | 8 | I just cleaned it up and ANSIfied it. 9 | */ 10 | 11 | /* 12 | * Here's something you've all been waiting for: the AT&T public domain 13 | * source for getopt(3). It is the code which was given out at the 1985 14 | * UNIFORUM conference in Dallas. I obtained it by electronic mail 15 | * directly from AT&T. The people there assure me that it is indeed 16 | * in the public domain. 17 | * 18 | * There is no manual page. That is because the one they gave out at 19 | * UNIFORUM was slightly different from the current System V Release 2 20 | * manual page. The difference apparently involved a note about the 21 | * famous rules 5 and 6, recommending using white space between an option 22 | * and its first argument, and not grouping options that have arguments. 23 | * Getopt itself is currently lenient about both of these things White 24 | * space is allowed, but not mandatory, and the last option in a group can 25 | * have an argument. That particular version of the man page evidently 26 | * has no official existence, and my source at AT&T did not send a copy. 27 | * The current SVR2 man page reflects the actual behavor of this getopt. 28 | * However, I am not about to post a copy of anything licensed by AT&T. 29 | */ 30 | 31 | #include 32 | #include 33 | 34 | #define ERR(s, c) if(opterr){ fprintf(stderr, "%s%s%c\n", argv[0], s, c); } 35 | 36 | int opterr = 1; 37 | int optind = 1; 38 | int optopt; 39 | char *optarg; 40 | 41 | int getopt(int argc, char **argv, char *opts) { 42 | static int sp = 1; 43 | register int c; 44 | register char *cp; 45 | 46 | if(sp == 1) { 47 | if(optind >= argc || argv[optind][0] != '-' || argv[optind][1] == '\0') 48 | return -1; 49 | else if(strcmp(argv[optind], "--") == 0) { 50 | optind++; 51 | return -1; 52 | } 53 | } 54 | optopt = c = argv[optind][sp]; 55 | if(c == ':' || (cp=strchr(opts, c)) == 0) { 56 | ERR(": illegal option -- ", c); 57 | if(argv[optind][++sp] == '\0') { 58 | optind++; 59 | sp = 1; 60 | } 61 | return('?'); 62 | } 63 | if(*++cp == ':') { 64 | if(argv[optind][sp+1] != '\0') 65 | optarg = &argv[optind++][sp+1]; 66 | else if(++optind >= argc) { 67 | ERR(": option requires an argument -- ", c); 68 | sp = 1; 69 | return('?'); 70 | } else 71 | optarg = argv[optind++]; 72 | sp = 1; 73 | } else { 74 | if(argv[optind][++sp] == '\0') { 75 | sp = 1; 76 | optind++; 77 | } 78 | optarg = NULL; 79 | } 80 | return(c); 81 | } 82 | -------------------------------------------------------------------------------- /sbasic/ppdbmain.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define PP_USE_PP_RED_PP_BLACK 0 5 | 6 | #define PPDB_IMPLEMENTATION 7 | #include "ppdb.h" 8 | 9 | #define MEM_SIZE 8192 10 | char memory[MEM_SIZE]; 11 | 12 | static void showfun(const char *key, const char *value, void *cookie) { 13 | (void)cookie; 14 | printf("'%s' => '%s'\n", key, value); 15 | } 16 | 17 | int main(int argc, char *argv[]) { 18 | int save = 0, i; 19 | ppdb_t DB; 20 | FILE *f; 21 | 22 | pp_init(&DB, memory, MEM_SIZE); 23 | 24 | if(argc < 2) { 25 | fprintf(stderr, "usage: %s database.db commands...\n", argv[0]); 26 | fprintf(stderr, "where commands is a sequence of:\n"); 27 | fprintf(stderr, " * `poke {key} {value}`\n"); 28 | fprintf(stderr, " * `peek {key}`\n"); 29 | fprintf(stderr, " * `list`\n"); 30 | fprintf(stderr, " * `tree`\n"); 31 | fprintf(stderr, " * `compact`\n"); 32 | return 1; 33 | } 34 | 35 | f = fopen(argv[1], "rb"); 36 | if(f) { 37 | if(pp_load(&DB, f) != PP_OK) { 38 | fprintf(stderr, "Unable to load DB"); 39 | return 1; 40 | } 41 | fclose(f); 42 | } else { 43 | #ifdef __VBCC__ 44 | fprintf(stderr, "File %s does not exist. Creating it.\n", argv[1]); 45 | #else 46 | if(errno == ENOENT) { 47 | fprintf(stderr, "File %s does not exist. Creating it.\n", argv[1]); 48 | } else { 49 | fprintf(stderr, "error: Unable to open %s: %s\n", argv[1], strerror(errno)); 50 | return 1; 51 | } 52 | #endif 53 | } 54 | 55 | for(i = 2; i < argc; i++) { 56 | if(!strcmp(argv[i], "peek")) { 57 | const char *value; 58 | if(++i == argc) { 59 | fprintf(stderr, "error: `peek` expects a key\n"); 60 | return 1; 61 | } 62 | value = pp_peek(&DB, argv[i]); 63 | if(value) { 64 | printf("%s\n", value); 65 | } else 66 | fprintf(stderr, "error: key `%s` not found\n", argv[i]); 67 | } else if(!strcmp(argv[i], "poke")) { 68 | if(i+2 >= argc) { 69 | fprintf(stderr, "error: `poke` expects a key and a value\n"); 70 | return 1; 71 | } 72 | pp_poke(&DB, argv[i+1], argv[i+2]); 73 | save = 1; 74 | i += 2; 75 | } else if(!strcmp(argv[i], "list")) { 76 | pp_foreach(&DB, showfun, NULL); 77 | } else if(!strcmp(argv[i], "compact")) { 78 | pp_compact(&DB); 79 | save = 1; 80 | } else if(!strcmp(argv[i], "tree")) { 81 | pp_tree(&DB); 82 | } else { 83 | fprintf(stderr, "error: unknown command `%s`\n", argv[i]); 84 | } 85 | } 86 | 87 | if(save) { 88 | f = fopen(argv[1], "wb"); 89 | if(!f) { 90 | fprintf(stderr, "error: Unable to save %s: %s", argv[1], strerror(errno)); 91 | } 92 | if(pp_save(&DB, f) != PP_OK) { 93 | fprintf(stderr, "Unable to save DB"); 94 | return 1; 95 | } 96 | fclose(f); 97 | } 98 | 99 | return 0; 100 | } 101 | 102 | -------------------------------------------------------------------------------- /mybas/README.md: -------------------------------------------------------------------------------- 1 | BASIC-to-C compiler 2 | =================== 3 | 4 | There are many BASIC-to-C compilers out there, but this one is mine. 5 | 6 | I got the inspiration when I stumbled upon Peter Norvig's [BASIC interpreter][norvig] 7 | in Python, which I'll refer to as [norvig][]. This one follows mostly the same syntax 8 | and symantics. 9 | 10 | Normal statements and the math functions map quite simply to their C equivalents. 11 | 12 | `GOTO` statements also map to the C `goto` statement, but the line numbers are 13 | prefixed with `lbl_` to turn them into valid C labels. 14 | 15 | `GOSUB` and `RETURN` uses a `jump:switch(addr)` block around the generated code as a 16 | so that the `addr` variable can be used for a computed goto. The `GOSUB` stores a value 17 | in the variable `ret`, and then the `RETURN` copies that value to `addr` and does a 18 | `goto jump;`. 19 | 20 | `FOR` loops and their associated `NEXT` statements also make use of this computed goto 21 | mechanism to repeat the loop. 22 | 23 | Like [norvig][], there is no stack for `GOSUB` statements, so `GOSUB` 24 | statements cannot be nested. I see no reason why the variable `ret` can't 25 | be turned into a stack in the future. 26 | 27 | `PRINT` statements do padding as per [norvig][]: `','` pads 15 spaces, `','` pads with 3 spaces. 28 | The padding is done such that there is always at least one space character. 29 | 30 | An empty `PRINT` will just result in a newline being printed. It does not print a 31 | newline if you end a `PRINT` with a comma, eg. `PRINT S,`. 32 | 33 | `DIM A(20)` declares an array indexable from `A(0)` to `A(20)`, thus the array holds 34 | *n + 1* elements. If an array is not `DIM`'ed , it is treated as a `DIM a(10)`. 35 | 36 | Changes from the [norvig][] version: 37 | 38 | * Added unary `+` and `-` operators. 39 | * It supports multiple statements per line with the `':'` operator. 40 | * It is case insensitive; Keywords and identifiers are converted to uppercase internally. 41 | * `LET` keyword is optional; Lines starting with an identifier are treated as an implicit `LET` 42 | 43 | TODO 44 | ---- 45 | 46 | * `AND`, `OR` and `NOT` 47 | * Better string support. 48 | * [norvig][] doesn't have an `INPUT` statement, but I see it as a necessity. It can be done once I have better string support. 49 | * The compiler makes provision for using IDs instead of labels, but the parser 50 | doesn't. Likewise, line numbers can be made optional. 51 | * FOR loops can be strange 52 | * In a `FOR` statement, if `STEP` is negative, then we should check if the loop variable is greater than the `TO` value in the comparison so that you can have statements like `FOR X = 10 TO 1 STEP -1`, see the [C64 FOR command](https://www.c64-wiki.com/wiki/FOR). 53 | * We should probably also `assert()` that `STEP` is not zero in `FOR` loops. 54 | * The [C64 FOR-NEXT combination](https://www.c64-wiki.com/wiki/NEXT) actually uses a stack to track nested loops. 55 | * The identifier after the `NEXT` should be optional, eg `FOR I=1 TO 5 DO PRINT I : NEXT` 56 | * [Wikipedia][] has more syntax that can be implemented, eg. `ON ... GOTO` and `DO ... LOOP WHILE` 57 | 58 | 59 | [norvig]: https://github.com/norvig/pytudes/blob/master/ipynb/BASIC.ipynb 60 | [Wikipedia]: https://en.wikipedia.org/wiki/BASIC#Syntax 61 | [c64-wiki]: https://www.c64-wiki.com/wiki/C64-Commands 62 | [dim]: https://www.c64-wiki.com/wiki/DIM 63 | -------------------------------------------------------------------------------- /fenster-pocketmod/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "fenster.h" 4 | #include "fenster_audio.h" 5 | 6 | #define POCKETMOD_IMPLEMENTATION 7 | #include "pocketmod.h" 8 | 9 | #define W 320 10 | #define H 240 11 | 12 | char *readfile(const char *fname, long *lenp) { 13 | FILE *f; 14 | long r; 15 | char *str; 16 | 17 | if(!(f = fopen(fname, "rb"))) 18 | return NULL; 19 | 20 | fseek(f, 0, SEEK_END); 21 | *lenp = (long)ftell(f); 22 | rewind(f); 23 | 24 | if(!(str = malloc(*lenp+2))) 25 | return NULL; 26 | r = fread(str, 1, *lenp, f); 27 | 28 | if(r != *lenp) { 29 | free(str); 30 | return NULL; 31 | } 32 | 33 | fclose(f); 34 | str[*lenp] = '\0'; 35 | return str; 36 | } 37 | 38 | #define MIN(a,b) (((a) < (b))?(a):(b)) 39 | 40 | static int run() { 41 | 42 | uint32_t buf[W * H]; 43 | 44 | struct fenster f = { 45 | .title = "hello", 46 | .width = W, 47 | .height = H, 48 | .buf = buf, 49 | }; 50 | 51 | struct fenster_audio fa = {0}; 52 | 53 | pocketmod_context mod = {0}; 54 | 55 | //char *modfile = "songs/elysium.mod"; 56 | //char *modfile = "songs/nemesis.mod"; 57 | char *modfile = "songs/king.mod"; 58 | 59 | long mod_size; 60 | char *mod_data = readfile(modfile, &mod_size); 61 | if(!mod_data) { 62 | fprintf(stderr, "error: Couldn't read %s\n", modfile); 63 | return 1; 64 | } 65 | 66 | fenster_open(&f); 67 | fenster_audio_open(&fa); 68 | 69 | pocketmod_init(&mod, mod_data, mod_size, FENSTER_SAMPLE_RATE); 70 | 71 | uint32_t t = 0, u = 0; 72 | float audio[FENSTER_AUDIO_BUFSZ * 2]; 73 | float audio_out[FENSTER_AUDIO_BUFSZ]; 74 | int64_t now = fenster_time(); 75 | while (fenster_loop(&f) == 0) { 76 | t++; 77 | 78 | int n = fenster_audio_available(&fa); 79 | if (n > 0) { 80 | int samples_to_render = 2 * MIN(n, FENSTER_AUDIO_BUFSZ); 81 | int bytes_rendered = pocketmod_render(&mod, audio, samples_to_render * sizeof audio[0]); 82 | int samples_rendered = bytes_rendered / sizeof audio[0]; 83 | /* pocketmod renders the samples to stereo, but fenster_audio is mono, so 84 | I just mix the two channels together */ 85 | for(int j = 0; j < samples_rendered / 2; j++) { 86 | audio_out[j] = (audio[j*2] + audio[j*2+1]) / 2; 87 | } 88 | fenster_audio_write(&fa, audio_out, samples_rendered / 2); 89 | } 90 | 91 | if (f.keys[27]) 92 | break; 93 | 94 | for (int i = 0; i < 320; i++) { 95 | for (int j = 0; j < 240; j++) { 96 | /* White noise: */ 97 | /* fenster_pixel(&f, i, j) = (rand() << 16) ^ (rand() << 8) ^ rand(); */ 98 | 99 | /* Colourful and moving: */ 100 | /* fenster_pixel(&f, i, j) = i * j * t; */ 101 | 102 | /* Munching squares: */ 103 | fenster_pixel(&f, i, j) = i ^ j ^ t; 104 | } 105 | } 106 | int64_t time = fenster_time(); 107 | if (time - now < 1000 / 60) { 108 | fenster_sleep(time - now); 109 | } 110 | now = time; 111 | } 112 | fenster_audio_close(&fa); 113 | fenster_close(&f); 114 | return 0; 115 | } 116 | 117 | #if defined(_WIN32) 118 | int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pCmdLine, 119 | int nCmdShow) { 120 | (void)hInstance, (void)hPrevInstance, (void)pCmdLine, (void)nCmdShow; 121 | return run(); 122 | } 123 | #else 124 | int main() { return run(); } 125 | #endif 126 | -------------------------------------------------------------------------------- /editor/image.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "editor.h" 9 | #include "image.h" 10 | 11 | image_t *img_create(int w, int h) { 12 | image_t *img = malloc( (sizeof *img) + w * h); 13 | if(!img) { 14 | error("out of memory"); 15 | return NULL; 16 | } 17 | img->w = w; 18 | img->h = h; 19 | return img; 20 | } 21 | 22 | /* 23 | http://qzx.com/pc-gpe/pcx.txt 24 | https://moddingwiki.shikadi.net/wiki/PCX_Format 25 | */ 26 | #pragma pack(push, 1) 27 | struct PCX_Header { 28 | uint8_t manufacturer; 29 | uint8_t version; 30 | uint8_t encoding; 31 | uint8_t bpp; 32 | struct { 33 | uint16_t xmin, ymin, xmax, ymax; 34 | } window; 35 | uint16_t hdpi; 36 | unsigned short vdpi; 37 | uint8_t colormap[48]; 38 | uint8_t reserved; 39 | uint8_t nplanes; 40 | uint16_t bytesPerLine; 41 | uint16_t paletteInfo; 42 | uint16_t hScreenSize; 43 | uint16_t vScreenSize; 44 | uint8_t filler[54]; 45 | }; 46 | #pragma pack(pop) 47 | image_t *loadpcx( char const* filename ) { 48 | FILE *f = NULL; 49 | struct PCX_Header header; 50 | 51 | int w, h; 52 | 53 | info("Loading PCX `%s`", filename); 54 | 55 | f = fopen(filename, "rb"); 56 | if(!f) { 57 | error("Couldn't open %s: %s", filename, strerror(errno)); 58 | goto fail; 59 | } 60 | 61 | assert(sizeof(struct PCX_Header) == 128); 62 | 63 | if(fread(&header, 128, 1, f) != 1) { 64 | error("Couldn't read header %s", filename); 65 | goto fail; 66 | } 67 | 68 | w = header.window.xmax - header.window.xmin + 1; 69 | h = header.window.ymax - header.window.ymin + 1; 70 | 71 | if(header.bpp != 8 && header.nplanes != 1) { 72 | error("Unsupported PCX format %d:%d", header.bpp, header.nplanes); 73 | goto fail; 74 | } 75 | 76 | image_t *img = img_create(w, h); 77 | if(!img) 78 | goto fail; 79 | 80 | int i = 0; 81 | while(i < w * h) { 82 | uint8_t b = fgetc(f); 83 | if(b >= 192) { 84 | uint8_t c = fgetc(f); 85 | for(b &= 0x3F; b > 0; b--) 86 | img->pixels[i++] = c; 87 | } else 88 | img->pixels[i++] = b; 89 | } 90 | 91 | img->palcount = 256; 92 | fseek(f, -768, SEEK_END); 93 | fread(img->palette, 1, 768, f); 94 | 95 | fclose(f); 96 | 97 | return img; 98 | 99 | fail: 100 | if(img) 101 | free(img); 102 | if(f) 103 | fclose(f); 104 | return NULL; 105 | } 106 | 107 | void blit_image(image_t *img, int dx, int dy, int dw, int dh, int sx, int sy, int sw, int sh) { 108 | int x, y, u, v; 109 | if(sw == 0) sw = img->w; 110 | if(sh == 0) sh = img->h; 111 | if(dw == 0) dw = sw; 112 | if(dh == 0) dh = sh; 113 | // TODO: if (sw == dw && sh == dh) special case 114 | 115 | v = sy; 116 | int cy = 0; 117 | for(y = dy; y < dy + dh; y++) { 118 | if(y >= vheight) break; 119 | if(y >= 0) { 120 | 121 | u = sx; 122 | int cx = 0; 123 | for(x = dx; x < dx + dw; x++) { 124 | if(x >= vwidth) break; 125 | if(x >= 0) { 126 | uint8_t p = img->pixels[u + v * img->w] & 0x0F; 127 | uint8_t R = img->palette[p * 3 + 0], 128 | G = img->palette[p * 3 + 1], 129 | B = img->palette[p * 3 + 2]; 130 | 131 | putpix(x, y, (R << 16) + (G << 8) + B); 132 | } 133 | cx = cx + sw; 134 | while(cx >= dw) { 135 | u = u + 1; 136 | cx = cx - dw; 137 | } 138 | } 139 | 140 | } 141 | cy = cy + sh; 142 | while(cy >= dh) { 143 | v = v + 1; 144 | cy = cy - dh; 145 | } 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /sbasic/sbasic.h: -------------------------------------------------------------------------------- 1 | /** 2 | * **A tiny BASIC interpreter** 3 | * 4 | * From Chapter 7 of "C Power User's Guide" by Herbert Schildt, 1988. 5 | * The book can be found on [the Internet Archive](https://archive.org/details/cpowerusersguide00schi_0/mode/2up). 6 | * 7 | * This version has been modified to add several features: 8 | * 9 | * - Converted it to C89 standard 10 | * - Extracted the code as an API so you can script other programs with it 11 | * - Call C-functions 12 | * - Long variable names 13 | * - Identifiers as labels, denoted as `@label` 14 | * - `string$` variables... 15 | * - ...and string literals in expressions 16 | * - Logical operators `AND`, `OR` and `NOT` 17 | * - `ON` statements 18 | * - Comments with the `REM` keyword and `'` operator 19 | * - Additional comparison operators `<>`, `<=` and `>=` 20 | * - Escape sequences in string literals 21 | */ 22 | 23 | #ifndef SBASIC_H 24 | #define SBASIC_H 25 | 26 | #ifdef __cplusplus 27 | extern "C" { 28 | #endif 29 | 30 | /** 31 | * C API 32 | * ===== 33 | * 34 | * Types 35 | * ----- 36 | * 37 | * `value_t` 38 | * : A structure representing an integer or string value. 39 | */ 40 | 41 | typedef struct value { 42 | enum {V_INT, V_STR} type; 43 | union { 44 | int i; 45 | char *s; 46 | } v; 47 | } value_t; 48 | 49 | /** 50 | * Globals 51 | * ------- 52 | * 53 | */ 54 | 55 | typedef void (*sb_print_t)(const char *, ...); 56 | 57 | extern sb_print_t sb_print_error; 58 | extern sb_print_t sb_print; 59 | 60 | /** 61 | * Functions 62 | * --------- 63 | * 64 | * ### Binding C functions 65 | * 66 | * * `void add_function(const char *name, sb_function_t fun);` 67 | * 68 | * Adds a function named `name` implemented by the function pointed to 69 | * by `fun` to the interpreter. 70 | * 71 | * `sb_function_t` is defined like so: 72 | * 73 | * ```c 74 | * typedef void (*sb_function_t)(value_t *result, int argc, value_t argv[]); 75 | * ``` 76 | * 77 | * where `result` is a pointer to the `value` structure where the 78 | * result will be stored, `argc` is the number of arguments and 79 | * `argv` is an array of the aruments' values themselves 80 | * 81 | * * `void add_std_library()` 82 | * 83 | * Adds all the standard functions to the interpreter. 84 | * 85 | */ 86 | 87 | typedef void (*sb_function_t)(value_t *result, int argc, value_t argv[]); 88 | 89 | void add_function(const char *name, sb_function_t fun); 90 | 91 | void add_std_library(); 92 | 93 | /** 94 | * ### Utility functions 95 | * 96 | */ 97 | void sb_error(const char *error); 98 | char *sb_talloc(int len); 99 | char *sb_strdup(const char *s); 100 | 101 | /** 102 | * ### Manipulating Values 103 | */ 104 | int as_int(value_t *val); 105 | const char *as_string(value_t *val); 106 | 107 | value_t make_int(int i); 108 | value_t make_strn(const char *s, size_t len); 109 | value_t make_str(const char *s); 110 | 111 | value_t *get_variable(const char *var); 112 | 113 | value_t *set_variable(const char *var, const char *val); 114 | value_t *set_variablei(const char *name, int val); 115 | 116 | /** 117 | * ### Loading Programs 118 | * 119 | * `char *load_program(char *fname)` 120 | */ 121 | char *load_program(const char *fname); 122 | 123 | /** 124 | * ### Executing the interpreter 125 | * 126 | * `int execute(char *program);` 127 | * 128 | * Executes `program`. 129 | * 130 | * `int sb_gosub(const char *sub);` 131 | * 132 | * Finds `sub` and executes it. 133 | * 134 | */ 135 | int execute(char *program); 136 | 137 | int sb_gosub(const char *sub); 138 | 139 | int sb_line(void); 140 | 141 | void sb_clear(void); 142 | 143 | #ifdef __cplusplus 144 | } /* extern "C" */ 145 | #endif 146 | 147 | #endif /* SBASIC_H */ 148 | -------------------------------------------------------------------------------- /mybas/ast.c: -------------------------------------------------------------------------------- 1 | /* http://epaperpress.com/lexandyacc/ */ 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "ast.h" 10 | #include "bas.h" 11 | 12 | Node *node_string(char *s) { 13 | Node *node = malloc(sizeof * node); 14 | if(!node) return NULL; 15 | node->type = nt_str; 16 | node->u.str = strdup(s); 17 | node->line = Line; 18 | node->basic_line = BasicLine; 19 | return node; 20 | } 21 | 22 | Node *node_id(char *i) { 23 | Node *node = malloc(sizeof * node); 24 | if(!node) return NULL; 25 | node->type = nt_id; 26 | node->u.str = strdup(i); 27 | node->line = Line; 28 | node->basic_line = BasicLine; 29 | return node; 30 | } 31 | 32 | Node *node_number(double num) { 33 | Node *node = malloc(sizeof * node); 34 | if(!node) return NULL; 35 | node->type = nt_num; 36 | node->u.num = num; 37 | node->line = Line; 38 | node->basic_line = BasicLine; 39 | return node; 40 | } 41 | 42 | Node *node_operator(int op, int nargs, ...) { 43 | int i; 44 | va_list ap; 45 | 46 | Node *node = malloc(sizeof * node); 47 | if(!node) return NULL; 48 | node->type = nt_opr; 49 | node->u.opr.op = op; 50 | node->u.opr.nc = nargs; 51 | node->u.opr.ac = nargs; 52 | if(node->u.opr.ac == 0) 53 | node->u.opr.ac = 1; 54 | node->u.opr.children = calloc(node->u.opr.ac, sizeof *node->u.opr.children); 55 | if(!node->u.opr.children) { 56 | free(node); 57 | return NULL; 58 | } 59 | va_start(ap, nargs); 60 | for(i = 0; i < nargs; i++) { 61 | node->u.opr.children[i] = va_arg(ap, Node *); 62 | } 63 | va_end(ap); 64 | node->line = Line; 65 | node->basic_line = BasicLine; 66 | return node; 67 | } 68 | 69 | Node *node_add_child(Node *parent, Node *child) { 70 | int n; 71 | Node **re; 72 | assert(parent->type == nt_opr); 73 | 74 | n = parent->u.opr.nc; 75 | if(n == parent->u.opr.ac) { 76 | parent->u.opr.ac <<= 1; 77 | re = realloc(parent->u.opr.children, parent->u.opr.ac * sizeof *parent->u.opr.children); 78 | if(!re) 79 | return NULL; 80 | parent->u.opr.children = re; 81 | } 82 | assert(n < parent->u.opr.ac); 83 | parent->u.opr.children[n] = child; 84 | parent->u.opr.nc = n + 1; 85 | 86 | return parent; 87 | } 88 | 89 | void free_node(Node *node) { 90 | if(!node) return; 91 | switch(node->type) { 92 | case nt_id : 93 | case nt_str : free(node->u.str); break; 94 | case nt_opr : { 95 | int i; 96 | for(i = 0; i < node->u.opr.nc; i++) 97 | free_node(node->u.opr.children[i]); 98 | free(node->u.opr.children); 99 | } break; 100 | default : break; 101 | } 102 | free(node); 103 | } 104 | 105 | static void print_node(Node *node, int level) { 106 | int i; 107 | 108 | if(level) { 109 | printf("%*s-- ", level*2, "+"); 110 | } 111 | if(!node) { 112 | printf("NULL\n"); 113 | return; 114 | } 115 | 116 | switch(node->type) { 117 | case nt_num : printf("%g\n", node->u.num); break; 118 | case nt_id : printf("%s\n", node->u.str); break; 119 | case nt_str : printf("\"%s\"\n", node->u.str); break; 120 | case nt_opr : { 121 | if(node->u.opr.op < 0x20 || node->u.opr.op >= 0xFF) 122 | printf("<%s>\n", nodename(node->u.opr.op)); 123 | else if(node->u.opr.op < 128) 124 | printf("'%c'\n", node->u.opr.op); 125 | else 126 | printf("<%d>\n", (node->u.opr.op)); 127 | for(i = 0; i < node->u.opr.nc; i++) 128 | print_node(node->u.opr.children[i], level+1); 129 | } break; 130 | default : break; 131 | } 132 | } 133 | 134 | void print_tree(Node *node) { 135 | print_node(node, 0); 136 | } 137 | -------------------------------------------------------------------------------- /sbasic/listfuns.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "sbasic.h" 6 | 7 | /** 8 | * List Functions 9 | * -------------- 10 | * 11 | * The `listfuns.c` module provides some functions for 12 | * representing lists using strings of comma-separated values. 13 | * 14 | * For example, `"foo,bar,baz"` is a list of three values. 15 | * 16 | * !!! warning 17 | * I'm obligated to tell you that these functions 18 | * are $O(n)$ in complexity. 19 | * 20 | */ 21 | #define FS "," 22 | 23 | /** 24 | * `list(item1, item2...)` 25 | * : Creates a list of all the items in its arguments 26 | */ 27 | static void list_function(struct value *result, int argc, struct value argv[]) { 28 | int len = 0, i, p = 0; 29 | if(!argc) return; 30 | for(i = 0; i < argc; i++) 31 | len += strlen(as_string(&argv[i])) + 1; 32 | assert(len > 0); 33 | result->type = V_STR; 34 | result->v.s = sb_talloc(len); 35 | for(i = 0; i < argc; i++) { 36 | const char *s = as_string(&argv[i]); 37 | int t = strlen(s); 38 | memcpy(result->v.s + p, s, t); 39 | p += t; 40 | result->v.s[p++] = FS[0]; 41 | } 42 | assert(p == len); 43 | result->v.s[len - 1] = '\0'; 44 | } 45 | 46 | /** 47 | * `ladd(list$, item)` 48 | * : Appends `item` to the end of `list$` 49 | */ 50 | static void ladd_function(struct value *result, int argc, struct value argv[]) { 51 | int len = 0, l1, l2; 52 | if(argc < 2) 53 | sb_error("LADD expects two arguments"); 54 | l1 = strlen(as_string(&argv[0])); 55 | l2 = strlen(as_string(&argv[1])); 56 | len = l1 + l2 + 1; 57 | result->type = V_STR; 58 | result->v.s = sb_talloc(len + 1); 59 | memcpy(result->v.s, as_string(&argv[0]), l1); 60 | result->v.s[l1] = FS[0]; 61 | memcpy(result->v.s + l1 + 1, as_string(&argv[1]), l2); 62 | result->v.s[len] = '\0'; 63 | } 64 | 65 | /** 66 | * `llen(list$)` 67 | * : Returns the number of items in `list$` 68 | */ 69 | static void llen_function(struct value *result, int argc, struct value argv[]) { 70 | int i = 0; 71 | char *s, *t; 72 | if(argc < 1) return; 73 | s = sb_strdup(as_string(&argv[0])); 74 | for(t = strtok(s, FS); t; t = strtok(NULL, FS), i++); 75 | *result = make_int(i); 76 | } 77 | 78 | /** 79 | * `lget(list$, n)` 80 | * : Returns the `n`th item in `list$` 81 | */ 82 | static void lget_function(struct value *result, int argc, struct value argv[]) { 83 | int i = 1, n; 84 | char *s, *t; 85 | if(argc < 2) return; 86 | s = sb_strdup(as_string(&argv[0])); 87 | n = as_int(&argv[1]); 88 | for(t = strtok(s, FS); t; t = strtok(NULL, FS), i++) { 89 | if(i == n) { 90 | *result = make_str(t); 91 | return; 92 | } 93 | } 94 | } 95 | 96 | /** 97 | * `lfind(list$, i$)` 98 | * : Returns the index of `i$` in `list$`, or 0 if not found 99 | */ 100 | static void lfind_function(struct value *result, int argc, struct value argv[]) { 101 | int i = 1; 102 | char *s, *t; 103 | const char *n; 104 | if(argc < 2) return; 105 | s = sb_strdup(as_string(&argv[0])); 106 | n = as_string(&argv[1]); 107 | for(t = strtok(s, FS); t; t = strtok(NULL, FS), i++) { 108 | if(!strcmp(t, n)) { 109 | *result = make_int(i); 110 | return; 111 | } 112 | } 113 | *result = make_int(0); 114 | } 115 | 116 | /** 117 | * `lhead(list$)` 118 | * : Returns the first item in `list$` 119 | */ 120 | static void lhead_function(struct value *result, int argc, struct value argv[]) { 121 | char *s, *t; 122 | if(argc < 1) return; 123 | s = sb_strdup(as_string(&argv[0])); 124 | t = strchr(s, FS[0]); 125 | if(t) 126 | *t = '\0'; 127 | *result = make_str(s); 128 | } 129 | 130 | /** 131 | * `ltail(list$)` 132 | * : Returns all items in `list$` except the first. 133 | */ 134 | static void ltail_function(struct value *result, int argc, struct value argv[]) { 135 | char *s, *t; 136 | if(argc < 1) return; 137 | s = sb_strdup(as_string(&argv[0])); 138 | t = strchr(s, FS[0]); 139 | if(t) { 140 | t++; 141 | *result = make_str(t); 142 | } 143 | } 144 | 145 | void add_list_library() { 146 | add_function("list", list_function); 147 | add_function("ladd", ladd_function); 148 | add_function("llen", llen_function); 149 | add_function("lget", lget_function); 150 | add_function("lfind", lfind_function); 151 | add_function("lhead", lhead_function); 152 | add_function("ltail", ltail_function); 153 | } 154 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Junk Drawer 2 | 3 | Various utilities that I created over the years 4 | 5 | ## pocketmod 6 | 7 | A simple command-line program to generate [Pocketmod][] PDFs. 8 | 9 | It takes 8 PNG or JPG files as arguments and lays them out on a single page 10 | in a PDF file for printing. 11 | 12 | ``` 13 | ./modder -pg cover.png page1.png page2.png page3.png page4.png page5.png page6.png back.png 14 | ``` 15 | 16 | * `-o` Specifies the output file. The default is `pocketmod.pdf` 17 | * `-f` lay the page out as a [pocketfold][] instead. 18 | * `-m margin` specifies the margin around each image in its 19 | panel (default 10) 20 | * `-g` add folding guides to the output PDF 21 | * `-p` add page numbers to the output PDF. `-pp` adds 22 | page numbers to the front and back covers as well 23 | * `-T title` adds a title to the PDF metadata 24 | * `-A author` adds an author to the PDF metadata 25 | * `-C creator` adds a creator to the PDF metadata 26 | * `-S subject` adds a subject to the PDF metadata 27 | * `-K keywords` adds keywords to the PDF metadata 28 | 29 | PDFs are generated through [libharu][]. 30 | 31 | [Pocketmod]: https://pocketmod.com/ 32 | [libharu]: https://github.com/libharu/libharu 33 | [pocketfold]: https://3skulls.itch.io/pocketfold 34 | 35 | ## screens 36 | 37 | A simple Windows program that, when activated through its system tray icon, 38 | allows you to take a quick screenshot of your desktop by pressing `Alt+S` 39 | 40 | ## CursEd 41 | 42 | A [curses][]-based level editor for simple games. 43 | 44 | The Windows version is built with MinGW and [pdcurses][] 45 | 46 | [curses]: https://en.wikipedia.org/wiki/Curses_(programming_library) 47 | [pdcurses]: https://pdcurses.org/ 48 | 49 | ## mybas 50 | 51 | A simple BASIC-to-C _compiler_. 52 | 53 | ## Other people's code 54 | 55 | Some code written by other people that I have adapted for my own purposes: 56 | 57 | ### Picol 58 | 59 | This is a modified version of [Picol][], Salvatore Sanfilippo ("antirez")'s Tcl interpreter 60 | in ~500 lines. 61 | 62 | This version turns it into a [STB][]-style single header library, so that it can be embedded 63 | into another program as a command interpreter. 64 | 65 | [Picol]: http://oldblog.antirez.com/post/picol.html 66 | [STB]: https://github.com/nothings/stb/blob/master/docs/stb_howto.txt 67 | 68 | ### editor 69 | 70 | Reference implementation for Sean Barret's [stb_tilemap_editor.h][] 71 | to create a simple level editor for 2D games. 72 | 73 | It uses ZSerge's [fenster][] framework for displaying the user interface. 74 | 75 | [stb_tilemap_editor.h]: https://github.com/nothings/stb/blob/master/stb_tilemap_editor.h 76 | 77 | ### fenster-microui 78 | 79 | An interface between ZSerge's [fenster][] framework and RXI's [microui][] 80 | immediate mode GUI toolkit for quick cross-platform GUI programs. 81 | 82 | [fenster]: https://github.com/zserge/fenster 83 | [microui]: https://github.com/rxi/microui 84 | 85 | ### fenster-pocketmod 86 | 87 | A demo for using `fenster_audio.h` from ZSerge's [fenster][] framework to play 88 | module (MOD) music files through the [rombankzero/pocketmod][] library. 89 | 90 | To play the music, drop the contents of the original 91 | [pocketmod/songs](https://github.com/rombankzero/pocketmod/tree/master/songs) 92 | directory into `fenster-pocketmod/songs`. 93 | 94 | The irony that there's another unrelated tool called **pocketmod** 95 | that has nothing to do with MOD music in this collection is not 96 | lost on me. 97 | 98 | [rombankzero/pocketmod]: https://github.com/rombankzero/pocketmod 99 | 100 | ### sbasic 101 | 102 | A tiny BASIC interpreter from Chapter 7 of "C Power User's Guide" by Herbert Schildt, 1988. 103 | 104 | The book can be found on [the Internet Archive](https://archive.org/details/cpowerusersguide00schi_0/mode/2up). 105 | 106 | This version has been modified and modified and modified to add several features: 107 | 108 | - Converted it to C89 standard 109 | - Extracted the code as an API so you can script other programs with it 110 | - Call C-functions 111 | - Long variable names 112 | - Identifiers as labels, denoted as `@label` 113 | - `string$` variables... 114 | - ...and string literals in expressions 115 | - Logical operators `AND`, `OR` and `NOT` 116 | - `ON` statements 117 | - Comments with the `REM` keyword and `'` operator 118 | - Additional comparison operators `<>`, `<=` and `>=` 119 | - Escape sequences in string literals 120 | 121 | _You might notice a theme with all the interpreters in this collection._ 122 | 123 | ### domsson 124 | 125 | Converts OpenGameArt user [domsson][]'s bitmap fonts to byte arrays. 126 | 127 | [domsson]: https://opengameart.org/users/domsson 128 | -------------------------------------------------------------------------------- /picol/picol.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Documentation 5 | 61 | 62 |

This is a modified version of Picol, 63 | Salvatore Sanfilippo ("antirez")'s Tcl interpreter in ~500 lines.

64 |

This version turns it into a STB-style single header library, so 65 | that it can be embedded into another program as a command interpreter.

66 |

To use it, add a #define PICOL_IMPLEMENTATION above the 67 | #include "picol.h" in one of your source files.

68 |
 #define PICOL_IMPLEMENTATION
69 |  #include "picol.h"
70 | 
71 |

Now you can call picolInitInterp() to initialize an interpreter stucture 72 | (of type struct picolInterp), call picolRegisterCoreCommands() to register 73 | the core commands in the language, call picolRegisterCommand() to add custom 74 | commands to the interpreter, and then call picolEval() to interpret a string 75 | of text.

76 |
 struct picolInterp interp;
77 |  picolInitInterp(&interp);
78 |  picolRegisterCoreCommands(&interp);
79 |  int retcode = picolEval(&interp, text);
80 | 
81 |

Inside custom commands, you can use picolArityErr() when checking the number of 82 | paramters passed, and picolSetResult() to set the value that will be returned 83 | by the command.

84 |

I've kept the original REPL. It can be accessed by compiling this 85 | file with the PICOL_REPL symbol defined

86 |
 $ cp picol.h picol.h.c
87 |  $ gcc -DPICOL_REPL picol.h.c
88 | 
89 | 90 | -------------------------------------------------------------------------------- /fenster-microui/Cushion.h: -------------------------------------------------------------------------------- 1 | // Cushion font from https://damieng.com/zx-origins 2 | static const uint8_t FONT_CUSHION_BITMAP[] = { 3 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 4 | 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x00, // ! 5 | 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // " 6 | 0x00, 0x6c, 0xfe, 0x6c, 0xfe, 0x6c, 0x00, 0x00, // # 7 | 0x18, 0x3e, 0x60, 0x3c, 0x06, 0x7c, 0x18, 0x00, // $ 8 | 0x00, 0x60, 0x6c, 0x18, 0x30, 0x6c, 0x0c, 0x00, // % 9 | 0x38, 0x60, 0x66, 0x3c, 0x66, 0x66, 0x3e, 0x00, // & 10 | 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ' 11 | 0x1c, 0x30, 0x60, 0x60, 0x60, 0x60, 0x30, 0x1c, // ( 12 | 0x70, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x70, // ) 13 | 0x00, 0x6c, 0x38, 0xfe, 0x38, 0x6c, 0x00, 0x00, // * 14 | 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, // + 15 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x30, // , 16 | 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, // - 17 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, // . 18 | 0x0c, 0x0c, 0x18, 0x18, 0x30, 0x30, 0x60, 0x60, // / 19 | 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, // 0 20 | 0x18, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, // 1 21 | 0x7c, 0x06, 0x06, 0x3c, 0x60, 0x60, 0x7e, 0x00, // 2 22 | 0x7c, 0x06, 0x06, 0x1c, 0x06, 0x06, 0x7c, 0x00, // 3 23 | 0x66, 0x66, 0x66, 0x66, 0x3e, 0x06, 0x06, 0x00, // 4 24 | 0x7e, 0x60, 0x60, 0x7c, 0x06, 0x06, 0x7c, 0x00, // 5 25 | 0x3c, 0x60, 0x60, 0x7c, 0x66, 0x66, 0x3c, 0x00, // 6 26 | 0x7e, 0x06, 0x06, 0x0c, 0x0c, 0x18, 0x18, 0x00, // 7 27 | 0x3c, 0x66, 0x66, 0x3c, 0x66, 0x66, 0x3c, 0x00, // 8 28 | 0x3c, 0x66, 0x66, 0x3e, 0x06, 0x06, 0x3c, 0x00, // 9 29 | 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, // : 30 | 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x30, // ; 31 | 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x00, // < 32 | 0x00, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x00, 0x00, // = 33 | 0x60, 0x30, 0x18, 0x0c, 0x18, 0x30, 0x60, 0x00, // > 34 | 0x7c, 0x06, 0x06, 0x0c, 0x18, 0x00, 0x18, 0x00, // ? 35 | 0x7c, 0xc6, 0xde, 0xf6, 0xde, 0xc0, 0x7c, 0x00, // @ 36 | 0x3c, 0x66, 0x66, 0x7e, 0x66, 0x66, 0x66, 0x00, // A 37 | 0x7c, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x7c, 0x00, // B 38 | 0x3e, 0x60, 0x60, 0x60, 0x60, 0x60, 0x3e, 0x00, // C 39 | 0x7c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x00, // D 40 | 0x3e, 0x60, 0x60, 0x7c, 0x60, 0x60, 0x3e, 0x00, // E 41 | 0x3e, 0x60, 0x60, 0x7c, 0x60, 0x60, 0x60, 0x00, // F 42 | 0x3e, 0x60, 0x60, 0x66, 0x66, 0x66, 0x3e, 0x00, // G 43 | 0x66, 0x66, 0x66, 0x7e, 0x66, 0x66, 0x66, 0x00, // H 44 | 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, // I 45 | 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x7c, 0x00, // J 46 | 0x66, 0x6c, 0x78, 0x70, 0x78, 0x6c, 0x66, 0x00, // K 47 | 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x3e, 0x00, // L 48 | 0x6c, 0xfe, 0xd6, 0xd6, 0xc6, 0xc6, 0xc6, 0x00, // M 49 | 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, // N 50 | 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, // O 51 | 0x7c, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0x00, // P 52 | 0x3c, 0x66, 0x66, 0x66, 0x66, 0x6c, 0x36, 0x00, // Q 53 | 0x7c, 0x66, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0x00, // R 54 | 0x3c, 0x60, 0x60, 0x3c, 0x06, 0x06, 0x3c, 0x00, // S 55 | 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, // T 56 | 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, // U 57 | 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x00, // V 58 | 0xc6, 0xc6, 0xc6, 0xd6, 0xd6, 0xfe, 0x6c, 0x00, // W 59 | 0x66, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0x66, 0x00, // X 60 | 0x66, 0x66, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x00, // Y 61 | 0x7e, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x7e, 0x00, // Z 62 | 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c, // [ 63 | 0x60, 0x60, 0x30, 0x30, 0x18, 0x18, 0x0c, 0x0c, // 64 | 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c, // ] 65 | 0x18, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, // ^ 66 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, // _ 67 | 0x1e, 0x30, 0x30, 0x78, 0x30, 0x30, 0x7e, 0x00, // £ 68 | 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x3e, 0x00, // a 69 | 0x60, 0x60, 0x7c, 0x66, 0x66, 0x66, 0x3c, 0x00, // b 70 | 0x00, 0x00, 0x3c, 0x60, 0x60, 0x60, 0x3c, 0x00, // c 71 | 0x06, 0x06, 0x3e, 0x66, 0x66, 0x66, 0x3c, 0x00, // d 72 | 0x00, 0x00, 0x3c, 0x66, 0x7e, 0x60, 0x3e, 0x00, // e 73 | 0x1c, 0x30, 0x30, 0x78, 0x30, 0x30, 0x30, 0x00, // f 74 | 0x00, 0x00, 0x3c, 0x66, 0x66, 0x3e, 0x06, 0x3c, // g 75 | 0x60, 0x60, 0x7c, 0x66, 0x66, 0x66, 0x66, 0x00, // h 76 | 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, // i 77 | 0x0c, 0x00, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x38, // j 78 | 0x60, 0x60, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0x00, // k 79 | 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, // l 80 | 0x00, 0x00, 0x6c, 0xfe, 0xd6, 0xd6, 0xc6, 0x00, // m 81 | 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x00, // n 82 | 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x00, // o 83 | 0x00, 0x00, 0x3c, 0x66, 0x66, 0x7c, 0x60, 0x60, // p 84 | 0x00, 0x00, 0x3c, 0x66, 0x66, 0x3e, 0x06, 0x06, // q 85 | 0x00, 0x00, 0x3c, 0x60, 0x60, 0x60, 0x60, 0x00, // r 86 | 0x00, 0x00, 0x3c, 0x60, 0x3c, 0x06, 0x3c, 0x00, // s 87 | 0x18, 0x18, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x00, // t 88 | 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, // u 89 | 0x00, 0x00, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x00, // v 90 | 0x00, 0x00, 0xc6, 0xd6, 0xd6, 0xfe, 0x6c, 0x00, // w 91 | 0x00, 0x00, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0x00, // x 92 | 0x00, 0x00, 0x66, 0x66, 0x66, 0x3e, 0x06, 0x3c, // y 93 | 0x00, 0x00, 0x7e, 0x0c, 0x18, 0x30, 0x7e, 0x00, // z 94 | 0x0e, 0x18, 0x18, 0x70, 0x18, 0x18, 0x18, 0x0e, // { 95 | 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, // | 96 | 0x70, 0x18, 0x18, 0x0e, 0x18, 0x18, 0x18, 0x70, // } 97 | 0x66, 0xd6, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, // ~ 98 | 0x3c, 0x66, 0xdb, 0xf3, 0xf3, 0xdb, 0x66, 0x3c, // © 99 | }; 100 | -------------------------------------------------------------------------------- /fenster-pocketmod/fenster_audio.h: -------------------------------------------------------------------------------- 1 | #ifndef FENSTER_AUDIO_H 2 | #define FENSTER_AUDIO_H 3 | 4 | #ifndef FENSTER_SAMPLE_RATE 5 | #define FENSTER_SAMPLE_RATE 44100 6 | #endif 7 | 8 | #ifndef FENSTER_AUDIO_BUFSZ 9 | #define FENSTER_AUDIO_BUFSZ 8192 10 | #endif 11 | 12 | #if defined(__APPLE__) 13 | #include 14 | struct fenster_audio { 15 | AudioQueueRef queue; 16 | size_t pos; 17 | float buf[FENSTER_AUDIO_BUFSZ]; 18 | dispatch_semaphore_t drained; 19 | dispatch_semaphore_t full; 20 | }; 21 | #elif defined(_WIN32) 22 | #include 23 | #include 24 | struct fenster_audio { 25 | WAVEHDR header; 26 | HWAVEOUT wo; 27 | WAVEHDR hdr[2]; 28 | int16_t buf[2][FENSTER_AUDIO_BUFSZ]; 29 | }; 30 | #elif defined(__linux__) 31 | struct fenster_audio { 32 | void *pcm; 33 | float buf[FENSTER_AUDIO_BUFSZ]; 34 | size_t pos; 35 | }; 36 | #endif 37 | 38 | #ifndef FENSTER_API 39 | #define FENSTER_API extern 40 | #endif 41 | FENSTER_API int fenster_audio_open(struct fenster_audio *f); 42 | FENSTER_API int fenster_audio_available(struct fenster_audio *f); 43 | FENSTER_API void fenster_audio_write(struct fenster_audio *f, float *buf, 44 | size_t n); 45 | FENSTER_API void fenster_audio_close(struct fenster_audio *f); 46 | 47 | #ifndef FENSTER_HEADER 48 | #if defined(__APPLE__) 49 | static void fenster_audio_cb(void *p, AudioQueueRef q, AudioQueueBufferRef b) { 50 | struct fenster_audio *fa = (struct fenster_audio *)p; 51 | dispatch_semaphore_wait(fa->full, DISPATCH_TIME_FOREVER); 52 | memmove(b->mAudioData, fa->buf, sizeof(fa->buf)); 53 | dispatch_semaphore_signal(fa->drained); 54 | AudioQueueEnqueueBuffer(q, b, 0, NULL); 55 | } 56 | FENSTER_API int fenster_audio_open(struct fenster_audio *fa) { 57 | AudioStreamBasicDescription format = {0}; 58 | format.mSampleRate = FENSTER_SAMPLE_RATE; 59 | format.mFormatID = kAudioFormatLinearPCM; 60 | format.mFormatFlags = kLinearPCMFormatFlagIsFloat | kAudioFormatFlagIsPacked; 61 | format.mBitsPerChannel = 32; 62 | format.mFramesPerPacket = format.mChannelsPerFrame = 1; 63 | format.mBytesPerPacket = format.mBytesPerFrame = 4; 64 | fa->drained = dispatch_semaphore_create(1); 65 | fa->full = dispatch_semaphore_create(0); 66 | AudioQueueNewOutput(&format, fenster_audio_cb, fa, NULL, NULL, 0, &fa->queue); 67 | for (int i = 0; i < 2; i++) { 68 | AudioQueueBufferRef buffer = NULL; 69 | AudioQueueAllocateBuffer(fa->queue, FENSTER_AUDIO_BUFSZ * 4, &buffer); 70 | buffer->mAudioDataByteSize = FENSTER_AUDIO_BUFSZ * 4; 71 | memset(buffer->mAudioData, 0, buffer->mAudioDataByteSize); 72 | AudioQueueEnqueueBuffer(fa->queue, buffer, 0, NULL); 73 | } 74 | AudioQueueStart(fa->queue, NULL); 75 | return 0; 76 | } 77 | FENSTER_API void fenster_audio_close(struct fenster_audio *fa) { 78 | AudioQueueStop(fa->queue, false); 79 | AudioQueueDispose(fa->queue, false); 80 | } 81 | FENSTER_API int fenster_audio_available(struct fenster_audio *fa) { 82 | if (dispatch_semaphore_wait(fa->drained, DISPATCH_TIME_NOW)) 83 | return 0; 84 | return FENSTER_AUDIO_BUFSZ - fa->pos; 85 | } 86 | FENSTER_API void fenster_audio_write(struct fenster_audio *fa, float *buf, 87 | size_t n) { 88 | while (fa->pos < FENSTER_AUDIO_BUFSZ && n > 0) { 89 | fa->buf[fa->pos++] = *buf++, n--; 90 | } 91 | if (fa->pos >= FENSTER_AUDIO_BUFSZ) { 92 | fa->pos = 0; 93 | dispatch_semaphore_signal(fa->full); 94 | } 95 | } 96 | #elif defined(_WIN32) 97 | FENSTER_API int fenster_audio_open(struct fenster_audio *fa) { 98 | WAVEFORMATEX wfx = {WAVE_FORMAT_PCM, 1, FENSTER_SAMPLE_RATE, FENSTER_SAMPLE_RATE * 2, 2, 16, 0}; 99 | waveOutOpen(&fa->wo, WAVE_MAPPER, &wfx, 0, 0, CALLBACK_NULL); 100 | for (int i = 0; i < 2; i++) { 101 | fa->hdr[i].lpData = (LPSTR)fa->buf[i]; 102 | fa->hdr[i].dwBufferLength = FENSTER_AUDIO_BUFSZ * 2; 103 | waveOutPrepareHeader(fa->wo, &fa->hdr[i], sizeof(WAVEHDR)); 104 | waveOutWrite(fa->wo, &fa->hdr[i], sizeof(WAVEHDR)); 105 | } 106 | return 0; 107 | } 108 | FENSTER_API int fenster_audio_available(struct fenster_audio *fa) { 109 | for (int i = 0; i < 2; i++) 110 | if (fa->hdr[i].dwFlags & WHDR_DONE) 111 | return FENSTER_AUDIO_BUFSZ; 112 | return 0; 113 | } 114 | FENSTER_API void fenster_audio_write(struct fenster_audio *fa, float *buf, 115 | size_t n) { 116 | for (int i = 0; i < 2; i++) { 117 | if (fa->hdr[i].dwFlags & WHDR_DONE) { 118 | for (unsigned j = 0; j < n; j++) { 119 | fa->buf[i][j] = (int16_t)(buf[j] * 32767); 120 | } 121 | waveOutWrite(fa->wo, &fa->hdr[i], sizeof(WAVEHDR)); 122 | return; 123 | } 124 | } 125 | } 126 | FENSTER_API void fenster_audio_close(struct fenster_audio *fa) { 127 | waveOutClose(fa->wo); 128 | } 129 | #elif defined(__linux__) 130 | int snd_pcm_open(void **, const char *, int, int); 131 | int snd_pcm_set_params(void *, int, int, int, int, int, int); 132 | int snd_pcm_avail(void *); 133 | int snd_pcm_writei(void *, const void *, unsigned long); 134 | int snd_pcm_recover(void *, int, int); 135 | int snd_pcm_close(void *); 136 | FENSTER_API int fenster_audio_open(struct fenster_audio *fa) { 137 | if (snd_pcm_open(&fa->pcm, "default", 0, 0)) 138 | return -1; 139 | int fmt = (*(unsigned char *)(&(uint16_t){1})) ? 14 : 15; 140 | return snd_pcm_set_params(fa->pcm, fmt, 3, 1, FENSTER_SAMPLE_RATE, 1, 100000); 141 | } 142 | FENSTER_API int fenster_audio_available(struct fenster_audio *fa) { 143 | int n = snd_pcm_avail(fa->pcm); 144 | if (n < 0) 145 | snd_pcm_recover(fa->pcm, n, 0); 146 | return n; 147 | } 148 | FENSTER_API void fenster_audio_write(struct fenster_audio *fa, float *buf, 149 | size_t n) { 150 | int r = snd_pcm_writei(fa->pcm, buf, n); 151 | if (r < 0) 152 | snd_pcm_recover(fa->pcm, r, 0); 153 | } 154 | FENSTER_API void fenster_audio_close(struct fenster_audio *fa) { 155 | snd_pcm_close(fa->pcm); 156 | } 157 | #endif 158 | 159 | #endif /* FENSTER_HEADER */ 160 | #endif /* FENSTER_AUDIO_H */ 161 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | -------------------------------------------------------------------------------- /cursed/map.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "map.h" 7 | 8 | // For writing a portable app, this function 9 | // comes from externally (So that I can later provide a 10 | // SDL_RWops implementation) 11 | extern char *read_text_file(const char *fname); 12 | 13 | struct Map *create_map(int w, int h) { 14 | struct Map *m = malloc(sizeof *m); 15 | m->w = w; 16 | m->h = h; 17 | m->tiles = malloc(w * h * sizeof *m->tiles); 18 | 19 | memset(m->tiles, 0, w * h * sizeof *m->tiles); 20 | int i; 21 | for(i = 0; i < w * h; i++) 22 | m->tiles[i].c = ' '; 23 | 24 | m->nmeta = 0; 25 | 26 | return m; 27 | } 28 | 29 | void free_map(struct Map *m) { 30 | int i; 31 | if(!m) 32 | return; 33 | for(i = 0; i < m->nmeta; i++) { 34 | free(m->meta[i].key); 35 | free(m->meta[i].value); 36 | } 37 | free(m->tiles); 38 | free(m); 39 | } 40 | 41 | struct tile *get_tile(struct Map *m, int x, int y) { 42 | if(x < 0 || x >= m->w || y < 0 || y >= m->h) 43 | return NULL; 44 | return &m->tiles[(y) * m->w + (x)]; 45 | } 46 | 47 | #define SCAN_END 0 48 | #define SCAN_NUM 1 49 | #define SCAN_WORD 2 50 | #define SCAN_STRING 3 51 | static int scantok(char *in, char *tok, char **rem) { 52 | // TODO: Ought to guard against buffer overruns in tok, but not today. 53 | int t = SCAN_END; 54 | *tok = '\0'; 55 | if(!in) { 56 | in = *rem; 57 | } 58 | 59 | scan_start: 60 | while(isspace(*in)) 61 | in++; 62 | 63 | if(!*in) 64 | return SCAN_END; 65 | 66 | if(*in == '#') { 67 | while(*in && *in != '\n') 68 | in++; 69 | goto scan_start; 70 | } 71 | 72 | if(isalpha(*in)) { 73 | while(isalnum(*in)) 74 | *tok++ = *in++; 75 | *tok = '\0'; 76 | t = SCAN_WORD; 77 | } else if(isdigit(*in)) { 78 | while(isdigit(*in)) 79 | *tok++ = *in++; 80 | *tok = '\0'; 81 | t = SCAN_NUM; 82 | } else if(*in == '\"' || *in == '\'') { 83 | char term = *in++; 84 | while(*in != term) { 85 | if(!*in) { 86 | t = SCAN_END; 87 | goto end; 88 | } 89 | if(*in == '\\') { 90 | switch(*++in) { 91 | case 'n' : *tok++ = '\n'; break; 92 | case 't' : *tok++ = '\t'; break; 93 | default : *tok++ = *in; break; 94 | } 95 | in++; 96 | } else 97 | *tok++ = *in++; 98 | } 99 | in++; 100 | *tok = '\0'; 101 | t = SCAN_STRING; 102 | } else 103 | t = *in++; 104 | end: 105 | *rem = in; 106 | return t; 107 | } 108 | 109 | #define TOK_SIZE 64 110 | 111 | struct Map *open_map(const char *filename) { 112 | int ver, w, h; 113 | struct Map *m = NULL; 114 | 115 | char *text = read_text_file(filename); 116 | if(!text) 117 | return NULL; 118 | 119 | char token[TOK_SIZE], *rem; 120 | int t = scantok(text, token, &rem); 121 | if(t != SCAN_WORD) 122 | goto end; 123 | if(strcmp(token, APP_NAME)) 124 | goto end; 125 | 126 | t = scantok(NULL, token, &rem); 127 | if(t != SCAN_NUM) 128 | goto end; 129 | ver = atoi(token); 130 | 131 | (void)ver; // not using it at the moment, but we might later 132 | 133 | t = scantok(NULL, token, &rem); 134 | if(t != SCAN_NUM) 135 | goto end; 136 | w = atoi(token); 137 | 138 | t = scantok(NULL, token, &rem); 139 | if(t != SCAN_NUM) 140 | goto end; 141 | h = atoi(token); 142 | 143 | while(*rem != '\n') 144 | rem++; 145 | rem++; 146 | 147 | m = create_map(w,h); 148 | 149 | int x,y; 150 | for(y = 0; y < m->h; y++) { 151 | for(x = 0; x < m->w; x++) { 152 | m->tiles[(y) * m->w + (x)].c = *rem++; 153 | if(!m->tiles[(y) * m->w + (x)].c) { 154 | free_map(m); 155 | m = NULL; 156 | goto end; 157 | } 158 | } 159 | int c = *rem++; 160 | if(c == '\r') 161 | rem++; 162 | } 163 | 164 | t = scantok(rem, token, &rem); 165 | if(t != SCAN_END) { 166 | if(t == SCAN_WORD && !strcmp("meta", token)) { 167 | if((t = scantok(NULL, token, &rem)) != '{') { 168 | // printf("expected {\n"); 169 | goto meta_error; 170 | } 171 | t = scantok(NULL, token, &rem); 172 | 173 | while(t != '}') { 174 | char *key, *value; 175 | 176 | if(t == SCAN_WORD || t == SCAN_STRING) { 177 | key = strdup(token); 178 | } else { 179 | // printf("key expected"); 180 | goto meta_error; 181 | } 182 | 183 | if(scantok(NULL, token, &rem) != ':') { 184 | // printf("expected :\n"); 185 | goto meta_error; 186 | } 187 | 188 | t = scantok(NULL, token, &rem); 189 | if(t == SCAN_WORD || t == SCAN_NUM || t == SCAN_STRING) { 190 | value = strdup(token); 191 | } else { 192 | // printf("value expected"); 193 | goto meta_error; 194 | } 195 | 196 | int i = m->nmeta++; 197 | m->meta[i].key = key; 198 | m->meta[i].value = value; 199 | //printf("Meta[%d]: '%s' = '%s'\n", i, key, value); 200 | 201 | t = scantok(NULL, token, &rem); 202 | if(t == '}') 203 | break; 204 | else if(t != ',') { 205 | //printf("expected , or } (%d)\n", t); 206 | goto meta_error; 207 | } 208 | t = scantok(NULL, token, &rem); 209 | } 210 | } 211 | } 212 | 213 | goto end; 214 | 215 | meta_error: 216 | free_map(m); 217 | m = NULL; 218 | 219 | end: 220 | free(text); 221 | return m; 222 | } 223 | 224 | static char *escape_string(const char *str, char *buf) { 225 | char *sav = buf; 226 | while(*str) { 227 | switch(*str) { 228 | case '\n' : *buf++ = '\\'; *buf++ = 'n'; break; 229 | case '\t' : *buf++ = '\\'; *buf++ = 't'; break; 230 | case '\"' : *buf++ = '\\'; *buf++ = '\"'; break; 231 | case '\'' : *buf++ = '\\'; *buf++ = '\''; break; 232 | case '\\' : *buf++ = '\\'; *buf++ = '\\'; break; 233 | default : *buf++ = *str; 234 | } 235 | str++; 236 | } 237 | *buf = '\0'; 238 | return sav; 239 | } 240 | 241 | int save_map(struct Map *m, const char *filename) { 242 | FILE *f = fopen(filename, "w"); 243 | if(!f) 244 | return 0; 245 | 246 | fprintf(f, "# Cursed map file.\n"); 247 | fprintf(f, "%s %d\n", APP_NAME, VERSION); 248 | fprintf(f, "%d %d\n", m->w, m->h); 249 | 250 | int x,y; 251 | for(y = 0; y < m->h; y++) { 252 | for(x = 0; x < m->w; x++) { 253 | fputc(m->tiles[(y) * m->w + (x)].c, f); 254 | } 255 | fputc('\n', f); 256 | } 257 | fputs("# Meta data, as key : value pairs\n", f); 258 | fputs("meta {\n", f); 259 | for(x = 0; x < m->nmeta; x++) { 260 | char k[TOK_SIZE], v[TOK_SIZE]; 261 | fprintf(f, "\"%s\" : \"%s\"%s\n", 262 | escape_string(m->meta[x].key, k), 263 | escape_string(m->meta[x].value, v), 264 | x == m->nmeta - 1 ? "" : ","); 265 | 266 | } 267 | fputs("}\n", f); 268 | 269 | fclose(f); 270 | return 1; 271 | } 272 | -------------------------------------------------------------------------------- /fenster-microui/fenmui.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | // https://github.com/rxi/microui 6 | #include "microui.h" 7 | #include "atlas.inl" 8 | 9 | // https://github.com/zserge/fenster 10 | #define FENSTER_HEADER 11 | #include "fenster.h" 12 | 13 | #include "fenmui.h" 14 | 15 | #ifndef BUILTIN_FONT 16 | # define BUILTIN_FONT 1 17 | #endif 18 | 19 | #if !BUILTIN_FONT 20 | // https://damieng.com/typography/zx-origins/cushion/ 21 | #include "Cushion.h" 22 | #define TEXT_W 7 23 | static const uint8_t *font = FONT_CUSHION_BITMAP; 24 | #endif 25 | 26 | static struct fenster *F; 27 | static mu_Context *MC; 28 | 29 | void r_clear(mu_Color color) { 30 | uint32_t c = (color.r << 16) + (color.g << 8) + (color.b); 31 | for(int y = 0; y < F->height; y++) 32 | for(int x = 0; x < F->width; x++) 33 | fenster_pixel(F, x, y) = c; 34 | } 35 | 36 | static mu_Rect clip; 37 | 38 | void r_set_clip_rect(mu_Rect rect) { 39 | // Somehow the rect gets set a bit larger than needs be 40 | if(rect.w > F->width) rect.w = F->width; 41 | if(rect.h > F->height) rect.h = F->height; 42 | clip = rect; 43 | } 44 | 45 | void r_draw_rect(mu_Rect rect, mu_Color color) { 46 | uint32_t c = (color.r << 16) + (color.g << 8) + (color.b); 47 | for(int y = rect.y; y < rect.y + rect.h; y++) { 48 | if(y < clip.y) continue; 49 | if(y >= clip.y + clip.h) break; 50 | for(int x = rect.x; x < rect.x + rect.w; x++) { 51 | if(x < clip.x) continue; 52 | if(x >= clip.x + clip.w) break; 53 | fenster_pixel(F, x, y) = c; 54 | } 55 | } 56 | } 57 | 58 | static uint32_t c_lerp(uint32_t color1, uint32_t color2, int t) { 59 | if(t < 0) t = 0; 60 | if(t > 255) t = 255; 61 | int r1 = (color1 >> 16) & 0xFF, r2 = (color2 >> 16) & 0xFF; 62 | int g1 = (color1 >> 8) & 0xFF, g2 = (color2 >> 8) & 0xFF; 63 | int b1 = (color1 >> 0) & 0xFF, b2 = (color2 >> 0) & 0xFF; 64 | 65 | r1 = (r1 + (r2 - r1) * t / 255) & 0xFF; 66 | g1 = (g1 + (g2 - g1) * t / 255) & 0xFF; 67 | b1 = (b1 + (b2 - b1) * t / 255) & 0xFF; 68 | return (r1 << 16) + (g1 << 8) + b1; 69 | } 70 | 71 | static void draw_quad(mu_Rect src, mu_Rect rect, mu_Color color) { 72 | uint32_t col = (color.r << 16) + ( color.g << 8) + ( color.b ); 73 | 74 | int x = rect.x + (rect.w - src.w) / 2; 75 | int y = rect.y + (rect.h - src.h) / 2; 76 | rect = mu_rect(x, y, src.w, src.h); 77 | 78 | int v = src.y; 79 | int cy = 0; 80 | for(int y = rect.y; y < rect.y + rect.h; y++) { 81 | if(y >= clip.y + clip.h) break; 82 | if(y >= clip.y) { 83 | 84 | int u = src.x; 85 | int cx = 0; 86 | for(int x = rect.x; x < rect.x + rect.w; x++) { 87 | if(x >= clip.x + clip.w) break; 88 | if(x >= clip.x) { 89 | uint8_t p = atlas_texture[u + v * ATLAS_WIDTH]; 90 | fenster_pixel(F, x, y) = c_lerp(fenster_pixel(F, x, y), col, p); 91 | } 92 | cx = cx + src.w; 93 | while(cx >= rect.w) { 94 | u = u + 1; 95 | cx = cx - rect.w; 96 | } 97 | } 98 | 99 | } 100 | cy = cy + src.h; 101 | while(cy >= rect.h) { 102 | v = v + 1; 103 | cy = cy - rect.h; 104 | } 105 | } 106 | } 107 | 108 | void r_draw_icon(int id, mu_Rect rect, mu_Color color) { 109 | mu_Rect src = atlas[id]; 110 | draw_quad(src, rect, color); 111 | } 112 | 113 | #if BUILTIN_FONT 114 | static int text_width(mu_Font font, const char *text, int len) { 115 | int w = 0; 116 | for(; *text && len--; text++) { 117 | if((*text & 0xC0) == 0x80) continue; 118 | int chr = mu_min((unsigned char)*text, 127); 119 | w += atlas[ATLAS_FONT + chr].w; 120 | } 121 | return w; 122 | } 123 | 124 | static int text_height(mu_Font font) { 125 | return 18; 126 | } 127 | 128 | void r_draw_text(const char *text, mu_Vec2 pos, mu_Color color) { 129 | mu_Rect dst = {pos.x, pos.y, 0, 0}; 130 | for(; *text; text++) { 131 | if((*text & 0xC0) == 0x80) continue; 132 | int chr = mu_min((unsigned char)*text, 127); 133 | mu_Rect src = atlas[ATLAS_FONT + chr]; 134 | dst.w = src.w; 135 | dst.h = src.h; 136 | draw_quad(src, dst, color); 137 | dst.x += dst.w; 138 | } 139 | } 140 | 141 | #else 142 | 143 | static int text_width(mu_Font font, const char *text, int len) { 144 | if (len == -1) { len = strlen(text); } 145 | return len * TEXT_W; 146 | } 147 | 148 | static int text_height(mu_Font font) { 149 | return 8; 150 | } 151 | 152 | static void printchar(int x, int y, char c, uint32_t color) { 153 | if(c < ' ' || c >= 128) c = '*'; 154 | 155 | int xs = x, ye = y + 8; 156 | const uint8_t *p = &font[ (c - 0x20) * 8 ]; 157 | for(; y < ye; y++, p++) { 158 | if(y < clip.y) continue; 159 | if(y >= clip.y + clip.h) break; 160 | for(int i = 0x80, x = xs; i; i >>= 1, x++) { 161 | if(x < clip.x) continue; 162 | if(x >= clip.x + clip.w) break; 163 | if(i & *p) { 164 | fenster_pixel(F, x, y) = color; 165 | } 166 | } 167 | } 168 | } 169 | 170 | void r_draw_text(const char *text, mu_Vec2 pos, mu_Color color) { 171 | int x = pos.x, y = pos.y; 172 | uint32_t c = (color.r << 16) + (color.g << 8) + (color.b); 173 | for(; *text; text++) { 174 | if(*text == '\n') { 175 | x = pos.x; 176 | y += TEXT_W; 177 | } else if(*text == '\r') { 178 | x = pos.x; 179 | } else if(*text == '\t') { 180 | x += 4 * TEXT_W; 181 | } else { 182 | printchar(x, y, *text, c); 183 | x += TEXT_W; 184 | } 185 | } 186 | } 187 | 188 | #endif 189 | 190 | static int lastx, lasty, lastm; 191 | static int lastkeys[256], lastmod; 192 | 193 | void fenmui_init(struct fenster *fp, mu_Context * ctxp) { 194 | F = fp; 195 | MC = ctxp; 196 | MC->text_width = text_width; 197 | MC->text_height = text_height; 198 | 199 | clip.x = 0; 200 | clip.y = 0; 201 | clip.w = F->width; 202 | clip.h = F->height; 203 | 204 | lastx = F->x; 205 | lasty = F->y; 206 | lastm = F->mouse; 207 | lastmod = F->mod; 208 | memcpy(lastkeys, F->keys, sizeof lastkeys); 209 | } 210 | 211 | static const char key_map[256] = { 212 | [ 10 ] = MU_KEY_RETURN, 213 | [ 8 ] = MU_KEY_BACKSPACE, 214 | }; 215 | 216 | void fenmui_events() { 217 | if(lastx != F->x || lasty != F->y) { 218 | mu_input_mousemove(MC, F->x, F->y); 219 | lastx = F->x; 220 | lasty = F->y; 221 | } 222 | if(lastm != F->mouse) { 223 | if(F->mouse) 224 | mu_input_mousedown(MC, F->x, F->y, MU_MOUSE_LEFT); 225 | else 226 | mu_input_mouseup(MC, F->x, F->y, MU_MOUSE_LEFT); 227 | 228 | lastm = F->mouse; 229 | } 230 | for(int i = 0; i < 256; i++) { 231 | if(F->keys[i] != lastkeys[i]) { 232 | if(F->keys[i] && isprint(i)) { 233 | int c = i; 234 | if(isalpha(i) && !(F->mod & 0x2)) 235 | c = tolower(i); 236 | else if(F->mod & 0x2) { 237 | char *from = "`1234567890-=,./;'[]\\"; 238 | char *to = "~!@#$%^&*()_+<>?:\"{}|"; 239 | for(int j = 0; from[j]; j++) 240 | if(c == from[j]) { 241 | c = to[j]; 242 | break; 243 | } 244 | } 245 | char text[2] = {c, '\0'}; 246 | mu_input_text(MC, text); 247 | } 248 | if(key_map[i]) { 249 | if(F->keys[i]) 250 | mu_input_keydown(MC, key_map[i]); 251 | else 252 | mu_input_keyup(MC, key_map[i]); 253 | } 254 | } 255 | lastkeys[i] = F->keys[i]; 256 | } 257 | 258 | if(lastmod != F->mod) { 259 | int diff = lastmod ^ F->mod; 260 | if(diff & 0x1) { 261 | if(F->mod & 0x1) 262 | mu_input_keydown(MC, MU_KEY_CTRL); 263 | else 264 | mu_input_keyup(MC, MU_KEY_CTRL); 265 | } 266 | if(diff & 0x2) { 267 | if(F->mod & 0x2) 268 | mu_input_keydown(MC, MU_KEY_SHIFT); 269 | else 270 | mu_input_keyup(MC, MU_KEY_SHIFT); 271 | } 272 | if(diff & 0x4) { 273 | if(F->mod & 0x4) 274 | mu_input_keydown(MC, MU_KEY_ALT); 275 | else 276 | mu_input_keyup(MC, MU_KEY_ALT); 277 | } 278 | lastmod = F->mod; 279 | } 280 | } 281 | 282 | void fenmui_draw() { 283 | mu_Command *cmd = NULL; 284 | while (mu_next_command(MC, &cmd)) { 285 | switch (cmd->type) { 286 | case MU_COMMAND_TEXT: r_draw_text(cmd->text.str, cmd->text.pos, cmd->text.color); break; 287 | case MU_COMMAND_RECT: r_draw_rect(cmd->rect.rect, cmd->rect.color); break; 288 | case MU_COMMAND_ICON: r_draw_icon(cmd->icon.id, cmd->icon.rect, cmd->icon.color); break; 289 | case MU_COMMAND_CLIP: r_set_clip_rect(cmd->clip.rect); break; 290 | } 291 | } 292 | } 293 | -------------------------------------------------------------------------------- /sbasic/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | /* -std=c89 does not like getopt 6 | #if !defined(_WIN32) 7 | # include 8 | #else 9 | # include "getopt.h" 10 | #endif 11 | */ 12 | #include "getopt.h" 13 | 14 | #include "sbasic.h" 15 | 16 | #define PPDB_IMPLEMENTATION 17 | #include "ppdb.h" 18 | 19 | #define SEQIO_IMPL 20 | #include "seqio.h" 21 | 22 | #define MAX_SUBS 16 23 | 24 | /* See `listfuns.c` */ 25 | void add_list_library(); 26 | 27 | static void usage(const char *name, FILE *f) { 28 | fprintf(f, "usage: %s [options] \n", name); 29 | fprintf(f, "where [options] can be:\n"); 30 | fprintf(f, " -s var=value : set variable before executing\n"); 31 | fprintf(f, " -g var : get variable after executing\n"); 32 | fprintf(f, " -d dbfile : specify database file\n"); 33 | fprintf(f, " -u subroutine : call subroutine after execing\n"); 34 | } 35 | 36 | static ppdb_t DB; 37 | static char db_buffer[8192]; 38 | 39 | /** 40 | * Database Functions 41 | * ------------------ 42 | * 43 | * `poke(key, value)` 44 | * : Stores the `value` associated with `key` in the built-in 45 | * : key-value database. 46 | */ 47 | static void poke_function(struct value *result, int argc, struct value argv[]) { 48 | if(argc < 2) return; 49 | pp_poke(&DB, as_string(&argv[0]), as_string(&argv[1])); 50 | } 51 | 52 | /** 53 | * `peek(key)` 54 | * : Retrieves the value associated with `key` from the built-in 55 | * : key-value database. 56 | */ 57 | static void peek_function(struct value *result, int argc, struct value argv[]) { 58 | const char *v; 59 | if(argc < 1) return; 60 | v = pp_peek(&DB, as_string(&argv[0])); 61 | if(v) 62 | *result = make_str(v); 63 | } 64 | 65 | /** 66 | * Sequential IO Functions 67 | * ----------------------- 68 | * 69 | * `open(filename, mode)` 70 | * : Opens a file named `filename` for reading (`mode` = "r") or 71 | * : writing (`mode` = "w") sequential data. 72 | * : 73 | * : It returns a file number `file#` that is used with subsequent 74 | * : calls. 75 | */ 76 | #define MAX_FILES 4 77 | static SeqIO files[MAX_FILES]; 78 | static int nfiles = 0; 79 | 80 | static void open_function(struct value *result, int argc, struct value argv[]) { 81 | const char *n, *m; 82 | int i = 0, mode; 83 | if(argc < 2) sb_error("OPEN requires a filename and a mode"); 84 | n = as_string(&argv[0]); 85 | m = as_string(&argv[1]); 86 | 87 | mode = tolower(m[0]); 88 | if(mode != 'r' && mode != 'w') 89 | sb_error("OPEN(): Mode must be 'r' or 'w'"); 90 | 91 | if(nfiles < MAX_FILES) 92 | i = nfiles++; 93 | else { 94 | for(i = 0; i < MAX_FILES && files[i].data; i++); 95 | if(i == MAX_FILES) 96 | sb_error("Too many open files"); 97 | } 98 | if(mode == 'r') { 99 | if(!seq_infile(&files[i], n)) 100 | sb_error("unable to open file"); 101 | } else { 102 | if(!seq_outfile(&files[i], n)) 103 | sb_error("unable to open file"); 104 | } 105 | *result = make_int(i); 106 | } 107 | 108 | /** 109 | * `close(file#)` 110 | * : Closes a file previously opened with `open()` 111 | */ 112 | static void close_function(struct value *result, int argc, struct value argv[]) { 113 | int i; 114 | if(argc < 1) sb_error("CLOSE requires a file#"); 115 | i = as_int(&argv[0]); 116 | if(i < 0 || i >= MAX_FILES || !files[i].data) 117 | sb_error("CLOSE: invalid file#"); 118 | seq_close(&files[i]); 119 | } 120 | 121 | /** 122 | * `read(file#)` 123 | * : Reads a value from a file 124 | */ 125 | static void read_function(struct value *result, int argc, struct value argv[]) { 126 | int i; 127 | const char *val, *err; 128 | SeqIO *file; 129 | if(argc < 1) sb_error("READ requires a file#"); 130 | i = as_int(&argv[0]); 131 | if(i < 0 || i >= MAX_FILES || !files[i].data) 132 | sb_error("READ: invalid file#"); 133 | file = &files[i]; 134 | if(argc > 1) { 135 | for(i = 1; i < argc; i++) { 136 | val = seq_read(file); 137 | if((err = seq_error(file))) 138 | sb_error(err); 139 | if(!set_variable(as_string(&argv[i]), val)) 140 | sb_error("Unable to set variable"); 141 | } 142 | } else { 143 | val = seq_read(file); 144 | if((err = seq_error(file))) 145 | sb_error(err); 146 | } 147 | *result = make_str(val); 148 | } 149 | 150 | /** 151 | * `write(file#, val1, val2, ...valn)` 152 | * : Writes the values `val1` to `valn` sequentially to the file. 153 | */ 154 | static void write_function(struct value *result, int argc, struct value argv[]) { 155 | int i, n; 156 | const char *err; 157 | if(argc < 1) sb_error("READ requires a file#"); 158 | i = as_int(&argv[0]); 159 | if(i < 0 || i >= MAX_FILES || !files[i].data) 160 | sb_error("READ: invalid file#"); 161 | for(n = 1; n < argc; n++) { 162 | if(argv[n].type == V_INT) 163 | seq_write_int(&files[i], as_int(&argv[n])); 164 | else 165 | seq_write(&files[i], as_string(&argv[n])); 166 | if((err = seq_error(&files[i]))) 167 | sb_error(err); 168 | } 169 | seq_endl(&files[i]); 170 | } 171 | 172 | /** 173 | * Additional Functions 174 | * -------------------- 175 | * 176 | * `call(subroutine)` 177 | * : Calls the specified `subroutine` 178 | * : 179 | * : `CALL &sub` is functionally equivalent to `GOSUB sub` (albeit 180 | * : with a bit more overhead). 181 | */ 182 | static void call_function(struct value *result, int argc, struct value argv[]) { 183 | int res; 184 | if(argc < 1) sb_error("CALL requires a subroutine"); 185 | res = sb_gosub(as_string(&argv[0])); 186 | *result = make_int(res); 187 | } 188 | 189 | int main(int argc, char *argv[]) { 190 | char *p_buf; 191 | int c, i; 192 | const char *getter = NULL, *dbfile = NULL; 193 | const char *subs[MAX_SUBS]; 194 | int nsubs = 0; 195 | 196 | while ((c = getopt(argc, argv, "s:g:d:u:")) != -1) { 197 | switch (c) { 198 | case 's': { 199 | char *var = optarg, *val; 200 | val = strchr(var, '='); 201 | if(val) { 202 | *val = '\0'; 203 | val++; 204 | } else 205 | val = ""; 206 | if(!set_variable(var, val)) { 207 | fprintf(stderr, "Unable to set `%s`\n", var); 208 | return 1; 209 | } 210 | } break; 211 | case 'g': getter = optarg; break; 212 | case 'd': 213 | dbfile = optarg; 214 | break; 215 | case 'u': 216 | if(nsubs >= MAX_SUBS) { 217 | fprintf(stderr, "Too many subs (max %d)\n", MAX_SUBS); 218 | return 1; 219 | } 220 | subs[nsubs++] = optarg; 221 | break; 222 | default: usage(argv[0], stderr); return 1; 223 | } 224 | } 225 | 226 | if(optind == argc) { 227 | usage(argv[0], stderr); 228 | return 1; 229 | } 230 | 231 | add_std_library(); 232 | add_list_library(); 233 | 234 | add_function("peek", peek_function); 235 | add_function("poke", poke_function); 236 | add_function("open", open_function); 237 | add_function("close", close_function); 238 | add_function("read", read_function); 239 | add_function("write", write_function); 240 | add_function("call", call_function); 241 | 242 | pp_init(&DB, db_buffer, sizeof db_buffer); 243 | if(dbfile) { 244 | FILE *f = fopen(dbfile, "rb"); 245 | if(f) { 246 | if(pp_load(&DB, f) != PP_OK) { 247 | fprintf(stderr, "error: couldn't read database"); 248 | return 1; 249 | } 250 | fclose(f); 251 | } 252 | } 253 | 254 | if(!(p_buf = load_program(argv[optind]))) { 255 | fprintf(stderr, "couldn't load %s\n", argv[optind]); 256 | return 1; 257 | } 258 | 259 | if(!execute(p_buf)) { 260 | fprintf(stderr, "execution failed.\n"); 261 | return 1; 262 | } 263 | 264 | for(i = 0; i < nsubs; i++) { 265 | int result = sb_gosub(subs[i]); 266 | if(!result) 267 | break; 268 | } 269 | 270 | if(getter) { 271 | struct value *val = get_variable(getter); 272 | if(val) 273 | printf("%s = '%s'\n", getter, as_string(val)); 274 | else 275 | printf("No variable `%s` in script\n", getter); 276 | } 277 | 278 | if(dbfile) { 279 | FILE *f = fopen(dbfile, "wb"); 280 | if(f) { 281 | if(pp_save(&DB, f) != PP_OK) { 282 | fprintf(stderr, "error: couldn't write database"); 283 | } 284 | fclose(f); 285 | } else { 286 | fprintf(stderr, "error: couldn't open database for output"); 287 | } 288 | } 289 | 290 | free(p_buf); 291 | 292 | return 0; 293 | } 294 | -------------------------------------------------------------------------------- /pocketmod/modder.c: -------------------------------------------------------------------------------- 1 | /* 2 | Simple program to generate [Pocketmod][] PDFs using [libharu][] . 3 | 4 | You can use `-f` to use the [pocketfold][] arrangement. 5 | 6 | * Shoutout to [PDFGen][] which was considered as an alternative to [libharu][] 7 | * https://www.mylifeorganized.net/support/pocketmod/ 8 | * https://www.overleaf.com/latex/templates/creating-pocketmods-with-latex/nqbhpnrkskrx 9 | 10 | [Pocketmod]: https://pocketmod.com/ 11 | [libharu]: https://github.com/libharu/libharu 12 | [pocketfold]: https://3skulls.itch.io/pocketfold 13 | [PDFGen]: https://github.com/AndreRenaud/PDFGen/blob/master/pdfgen.h 14 | 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | 23 | #include "hpdf.h" 24 | 25 | HPDF_Doc pdf; 26 | HPDF_Font font; 27 | 28 | static double panelw, panelh; 29 | 30 | static int pocketmod[] = {0,1,2,3,4,5,6,7}; 31 | static int pocketfold[] = {7,0,1,2,3,4,5,6}; 32 | static int *translate = pocketmod; 33 | 34 | static int margin = 10; 35 | 36 | static int show_pagenums = 0; 37 | 38 | void error_handler(HPDF_STATUS error_no, 39 | HPDF_STATUS detail_no, 40 | void *user_data) { 41 | fprintf(stderr, "error_handler: %lu %lu\n", error_no, detail_no); 42 | } 43 | 44 | 45 | static void place_image(HPDF_Page page, int pageno, HPDF_Image image) { 46 | 47 | double angle = 3.141592; 48 | double x = 0, y = HPDF_Page_GetHeight(page) / 2; 49 | 50 | int mx = -margin, my = -margin; 51 | 52 | int pos = translate[pageno]; 53 | switch(pos) { 54 | case 0: // Front cover 55 | x = panelw; 56 | break; 57 | case 1: 58 | case 2: 59 | case 3: 60 | case 4: 61 | x = (pos - 1) * panelw; 62 | mx = margin; 63 | my = margin; 64 | angle = 0; 65 | break; 66 | case 5: 67 | x = 4 * panelw; 68 | break; 69 | case 6: 70 | x = 3 * panelw; 71 | break; 72 | case 7: // Back cover 73 | x = 2 * panelw; 74 | break; 75 | } 76 | 77 | double cr = cos(angle), sr = sin(angle); 78 | double wcr = (panelw - 2*margin) * cr, 79 | wsr = (panelw - 2*margin) * sr, 80 | hsr = (panelh - 2*margin) * -sr, 81 | hcr = (panelh - 2*margin) * cr; 82 | 83 | if(image) { 84 | HPDF_Page_GSave(page); 85 | HPDF_Page_Concat(page, wcr, wsr, hsr, hcr, x + mx, y + my); 86 | HPDF_Page_ExecuteXObject(page, image); 87 | HPDF_Page_GRestore(page); 88 | } 89 | 90 | char buffer[128]; 91 | snprintf(buffer, sizeof buffer, "%d", pageno); 92 | 93 | if(show_pagenums) { 94 | double tx = x - panelw/2 + 3, ty = HPDF_Page_GetHeight(page) / 2 - 5; 95 | switch(pos) { 96 | case 1: 97 | case 2: 98 | case 3: 99 | case 4: 100 | tx = x + panelw/2 - 3; 101 | ty = HPDF_Page_GetHeight(page) / 2 + 5; 102 | break; 103 | } 104 | if((pageno == 0 || pageno == 7) && show_pagenums < 2) return; 105 | HPDF_Page_BeginText(page); 106 | HPDF_Page_SetFontAndSize(page, font, 10); 107 | HPDF_Page_SetTextMatrix(page, cr, sr, -sr, cr, tx, ty); 108 | HPDF_Page_ShowText(page, buffer); 109 | HPDF_Page_EndText(page); 110 | } 111 | } 112 | 113 | 114 | void usage(FILE *f, const char *name) { 115 | fprintf(f, "Usage: %s [options] img0 ... img7\n", name); 116 | fprintf(f, "where options:\n"); 117 | fprintf(f, " -o file.pdf : Output filename\n"); 118 | fprintf(f, " -f : Pocketfold layout\n"); 119 | fprintf(f, " -m margin : Specify margin of each panel\n"); 120 | fprintf(f, " -g : Fold guide\n"); 121 | fprintf(f, " -p : Page numbers; `-pp` for page numbers on covers\n"); 122 | fprintf(f, " -T title : Set document title\n"); 123 | fprintf(f, " -A author : Set document author\n"); 124 | fprintf(f, " -C creator : Set document creator\n"); 125 | fprintf(f, " -S subject : Set document subject\n"); 126 | fprintf(f, " -K keywords : Set document keywords\n"); 127 | } 128 | 129 | int main(int argc, char *argv[]) { 130 | jmp_buf env; 131 | 132 | const char *outfile = "pocketmod.pdf"; 133 | 134 | pdf = HPDF_New(error_handler, NULL); 135 | if(!pdf) { 136 | fprintf(stderr, "error: Couldn't create PDF object\n"); 137 | return 1; 138 | } 139 | 140 | if(setjmp(env)) { 141 | HPDF_Free(pdf); 142 | return 1; 143 | } 144 | 145 | const char *T = NULL, *A = NULL, *C = NULL, *S = NULL, *K = NULL; 146 | int guide = 0; 147 | 148 | int opt; 149 | while((opt = getopt(argc, argv, "o:fm:T:A:gp?")) != -1) { 150 | switch(opt) { 151 | case 'o': outfile = optarg; break; 152 | case 'f': translate = pocketfold; break; 153 | case 'm': margin = atoi(optarg); break; 154 | case 'g': guide++; break; 155 | case 'p': show_pagenums++; break; 156 | case 'T': T = optarg; break; 157 | case 'A': A = optarg; break; 158 | case 'C': C = optarg; break; 159 | case 'S': S = optarg; break; 160 | case 'K': K = optarg; break; 161 | default: { 162 | usage(stdout, argv[0]); 163 | return 0; 164 | } 165 | } 166 | } 167 | 168 | if(T) HPDF_SetInfoAttr(pdf, HPDF_INFO_TITLE, T); 169 | if(A) HPDF_SetInfoAttr(pdf, HPDF_INFO_AUTHOR, A); 170 | if(C) HPDF_SetInfoAttr(pdf, HPDF_INFO_CREATOR, C); 171 | if(S) HPDF_SetInfoAttr(pdf, HPDF_INFO_SUBJECT, S); 172 | if(K) HPDF_SetInfoAttr(pdf, HPDF_INFO_KEYWORDS, K); 173 | 174 | HPDF_Image images[8]; 175 | int ni = 0; 176 | for(int i = optind; i < argc && ni < 8; i++) { 177 | HPDF_Image image = HPDF_LoadPngImageFromFile(pdf, argv[i]); 178 | if(!image) { 179 | fprintf(stderr, "error: Couldn't load %s", argv[i]); 180 | return 1; 181 | } 182 | images[ni++] = image; 183 | } 184 | 185 | HPDF_SetCompressionMode(pdf, HPDF_COMP_ALL); 186 | 187 | //HPDF_SetPageMode(pdf, HPDF_PAGE_MODE_USE_OUTLINE); 188 | 189 | HPDF_Page page = HPDF_AddPage(pdf); 190 | 191 | HPDF_Page_SetSize(page, HPDF_PAGE_SIZE_A4, HPDF_PAGE_LANDSCAPE); 192 | HPDF_Page_SetLineWidth(page, 0.5); 193 | 194 | panelw = HPDF_Page_GetWidth(page) / 4; 195 | panelh = HPDF_Page_GetHeight(page) / 2; 196 | 197 | font = HPDF_GetFont (pdf, "Helvetica", NULL); 198 | HPDF_Page_SetTextRenderingMode (page, HPDF_FILL); 199 | HPDF_Page_SetRGBFill (page, 0, 0, 0); 200 | 201 | for(int i = 0; i < 8; i++) { 202 | place_image(page, i, i < ni ? images[i] : NULL); 203 | } 204 | 205 | if(guide) { 206 | HPDF_REAL dash1[] = {6,4,2,4}; 207 | HPDF_REAL dash2[] = {3,3}; 208 | 209 | HPDF_Page_SetLineWidth (page, 0); 210 | HPDF_Page_SetRGBStroke (page, 0.5, 0.5, 0.5); 211 | HPDF_Page_SetDash(page, dash1, 4, 0); 212 | HPDF_Page_MoveTo (page, HPDF_Page_GetWidth(page)/4, HPDF_Page_GetHeight(page)); 213 | HPDF_Page_LineTo (page, HPDF_Page_GetWidth(page)/4, 0); 214 | HPDF_Page_MoveTo (page, HPDF_Page_GetWidth(page)/2, HPDF_Page_GetHeight(page)); 215 | HPDF_Page_LineTo (page, HPDF_Page_GetWidth(page)/2, 0); 216 | HPDF_Page_MoveTo (page, 3*HPDF_Page_GetWidth(page)/4, HPDF_Page_GetHeight(page)); 217 | HPDF_Page_LineTo (page, 3*HPDF_Page_GetWidth(page)/4, 0); 218 | HPDF_Page_Stroke (page); 219 | 220 | if(translate == pocketmod) { 221 | HPDF_Page_SetLineWidth (page, 0); 222 | HPDF_Page_SetRGBStroke (page, 0.5, 0.5, 0.5); 223 | HPDF_Page_SetDash(page, dash1, 4, 0); 224 | HPDF_Page_MoveTo (page, 0, HPDF_Page_GetHeight(page)/2); 225 | HPDF_Page_LineTo (page, HPDF_Page_GetWidth(page)/4, HPDF_Page_GetHeight(page)/2); 226 | HPDF_Page_MoveTo (page, 3*HPDF_Page_GetWidth(page)/4, HPDF_Page_GetHeight(page)/2); 227 | HPDF_Page_LineTo (page, HPDF_Page_GetWidth(page), HPDF_Page_GetHeight(page)/2); 228 | HPDF_Page_Stroke (page); 229 | 230 | HPDF_Page_SetLineWidth (page, 0); 231 | HPDF_Page_SetRGBStroke (page, 0.3, 0.3, 0.3); 232 | HPDF_Page_SetDash(page, dash2, 2, 0); 233 | HPDF_Page_MoveTo (page, HPDF_Page_GetWidth(page)/4, HPDF_Page_GetHeight(page)/2); 234 | HPDF_Page_LineTo (page, 3*HPDF_Page_GetWidth(page)/4, HPDF_Page_GetHeight(page)/2); 235 | HPDF_Page_Stroke (page); 236 | 237 | } else { 238 | HPDF_Page_SetLineWidth (page, 0); 239 | HPDF_Page_SetRGBStroke (page, 0.5, 0.5, 0.5); 240 | HPDF_Page_SetDash(page, dash1, 4, 0); 241 | HPDF_Page_MoveTo (page, 0, HPDF_Page_GetHeight(page)/2); 242 | HPDF_Page_LineTo (page, HPDF_Page_GetWidth(page), HPDF_Page_GetHeight(page)/2); 243 | HPDF_Page_Stroke (page); 244 | 245 | } 246 | } 247 | 248 | HPDF_SaveToFile(pdf, outfile); 249 | 250 | HPDF_Free(pdf); 251 | 252 | printf("Output written to %s\n~ fin ~\n", outfile); 253 | 254 | return 0; 255 | } -------------------------------------------------------------------------------- /cursed/cursed.c: -------------------------------------------------------------------------------- 1 | /* 2 | gcc -I /d/libs/PDCurses-3.4/ cursed.c /d/libs/PDCurses-3.4/win32/pdcurses.a 3 | */ 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | #include "map.h" 12 | 13 | #define MIN(a,b) ((a)<(b)?(a):(b)) 14 | 15 | // Reads an entire text file into memory 16 | // You need to free the returned pointer afterwards. 17 | char *read_text_file(const char *fname) { 18 | FILE *f; 19 | long len,r; 20 | char *str; 21 | 22 | if(!(f = fopen(fname, "rb"))) 23 | return NULL; 24 | 25 | fseek(f, 0, SEEK_END); 26 | len = ftell(f); 27 | rewind(f); 28 | 29 | if(!(str = malloc(len+2))) 30 | return NULL; 31 | r = fread(str, 1, len, f); 32 | 33 | if(r != len) { 34 | free(str); 35 | return NULL; 36 | } 37 | 38 | fclose(f); 39 | str[len] = '\0'; 40 | return str; 41 | } 42 | 43 | static void usage(const char *name) { 44 | fprintf(stderr, "Usage: %s [options] file\n", name); 45 | fprintf(stderr, "where options:\n"); 46 | fprintf(stderr, " -w width : Width of a new map.\n"); 47 | fprintf(stderr, " -h height : Height of a new map.\n"); 48 | fprintf(stderr, " -w and -h are only applicable when creating a new map.\n"); 49 | fprintf(stderr, " -n : Forces a new map.\n"); 50 | fprintf(stderr, " The new map will replace 'file'.\n"); 51 | fprintf(stderr, " A backup of 'file' will be created as 'file~'.\n"); 52 | fprintf(stderr, " -b c : Creates a default border of 'c' around the new map.\n"); 53 | fprintf(stderr, " -? : Displays this help.\n"); 54 | } 55 | 56 | static int get_attrs(int c) { 57 | int a = COLOR_PAIR(1); 58 | if(strchr("^|-", c)) { 59 | a = COLOR_PAIR(4) | A_BOLD; 60 | } else if(islower(c) || ispunct(c)) { 61 | a = COLOR_PAIR(3) | A_BOLD; 62 | } else if(isgraph(c)) { 63 | a = COLOR_PAIR(2); 64 | } 65 | 66 | return c | a; 67 | } 68 | 69 | static void draw_all(struct Map *m, WINDOW *win, int scry, int scrx) { 70 | int h, w; 71 | getmaxyx(win, h, w); 72 | int x, y; 73 | for(y = 0; y < m->h; y++){ 74 | int py = y - scry + 1; 75 | if(py < 1) continue; 76 | if(py >= h - 1) break; 77 | for(x = 0; x < m->w; x++){ 78 | int px = x - scrx + 1; 79 | if(px < 1) continue; 80 | if(px >= w - 1) break; 81 | wmove(win, y - scry + 1, x - scrx + 1); 82 | waddch(win, get_attrs(m->tiles[(y) * m->w + (x)].c)); 83 | } 84 | } 85 | box(win, ACS_VLINE, ACS_HLINE); 86 | if(m->w > w) { 87 | x = w * (scrx) / (m->w - w); 88 | if(x < 0) x = 0; 89 | if(x >= w) x = w - 1; 90 | wmove(win, h - 1 ,x); 91 | waddch(win, ACS_DIAMOND); 92 | } 93 | if(m->h > h) { 94 | y = (h ) * (scry) / (m->h - h - 2); 95 | if(y < 0) y = 0; 96 | if(y >= h) y = h - 1; 97 | wmove(win, y, w - 1); 98 | waddch(win, ACS_DIAMOND); 99 | } 100 | wrefresh(win); 101 | } 102 | 103 | int main(int argc, char*argv[]) { 104 | 105 | const char *filename; 106 | 107 | struct Map *m = NULL; 108 | 109 | // Before starting curses, check the command line parameters: 110 | int opt; 111 | int ow = DEFAULT_WIDTH, oh = DEFAULT_HEIGHT, forcenew = 0, 112 | b = '\0'; 113 | while((opt = getopt(argc, argv, "w:h:nb:?")) != -1) { 114 | switch(opt) { 115 | case 'w': { 116 | ow = atoi(optarg); 117 | } break; 118 | case 'h' : { 119 | oh = atoi(optarg); 120 | } break; 121 | case 'n' : { 122 | forcenew = 1; 123 | } break; 124 | case 'b' : { 125 | b = optarg[0]; 126 | } break; 127 | case '?' : { 128 | usage(argv[0]); 129 | return 1; 130 | } 131 | } 132 | } 133 | 134 | if(optind < argc) { 135 | filename = argv[optind]; 136 | } else { 137 | fprintf(stderr, "No filename specified.\n"); 138 | fprintf(stderr, "Run with -? for instructions.\n"); 139 | return 1; 140 | } 141 | 142 | m = open_map(filename); 143 | if(!m) { 144 | forcenew = 1; 145 | } else { 146 | // Create a backup. 147 | char buffer[128]; 148 | sprintf(buffer, "%s~", filename); 149 | save_map(m, buffer); 150 | } 151 | 152 | if(forcenew) { 153 | if(m) 154 | free_map(m); 155 | // Create a new map 156 | m = create_map(ow, oh); 157 | if(b) { 158 | int i; 159 | for(i = 0; i < ow; i++) { 160 | struct tile *t = get_tile(m, i, 0); 161 | t->c = b; 162 | t = get_tile(m, i, m->h - 1); 163 | t->c = b; 164 | } 165 | for(i = 0; i < oh; i++) { 166 | struct tile *t = get_tile(m, 0, i); 167 | t->c = b; 168 | t = get_tile(m, m->w - 1, i); 169 | t->c = b; 170 | } 171 | } 172 | } 173 | 174 | //return 0;// TESTING 175 | 176 | // No printf() calls beyond this point 177 | initscr(); 178 | int sh, sw; 179 | getmaxyx(stdscr, sh, sw); 180 | box(stdscr, ACS_VLINE, ACS_HLINE); 181 | //keypad(stdscr, TRUE); 182 | move(0, 2); 183 | printw(" %s ", filename); 184 | 185 | move(0,0); 186 | 187 | noecho(); 188 | 189 | if(has_colors()) { 190 | start_color(); 191 | 192 | init_pair(1, COLOR_WHITE, COLOR_BLACK); 193 | init_pair(2, COLOR_CYAN, COLOR_BLUE); 194 | init_pair(3, COLOR_YELLOW, COLOR_BLACK); 195 | init_pair(4, COLOR_CYAN, COLOR_BLACK); 196 | } 197 | 198 | refresh(); 199 | 200 | int wh = MIN(m->h+2,sh-2), ww = MIN(m->w+2,sw-2); 201 | //if(wh > 20) wh = 20; 202 | //if(ww > 20) ww = 20; 203 | 204 | /* Apparently you shouldn't have overlapping windows, so what I did here 205 | is wrong. It's not really a problem though with how the program uses those 206 | windows. */ 207 | // There is also the subwin() function, but the talk about touchwin() confused me. 208 | WINDOW *w = newwin(wh, ww,(sh-wh)/2,(sw-ww)/2); 209 | if(!w) { 210 | endwin(); 211 | fprintf(stderr, "error: Unable to create editor window\n"); 212 | return 1; 213 | } 214 | keypad(w, TRUE); 215 | box(w, ACS_VLINE, ACS_HLINE); 216 | 217 | int x = 0, y = 0; 218 | int scrx = 0, scry = 0; // scroll 219 | 220 | draw_all(m, w, scry, scrx); 221 | 222 | int autoright = 0; 223 | 224 | int done = 0; 225 | while(!done) { 226 | 227 | wmove(w, y - scry + 1, x - scrx + 1); 228 | wrefresh(w); 229 | 230 | int c = wgetch(w); 231 | switch(c) { 232 | case 27: { 233 | // Escape quits. 234 | done = 1; 235 | } break; 236 | case KEY_UP: y--; break; 237 | case KEY_DOWN: y++; break; 238 | case KEY_LEFT: x--; break; 239 | case KEY_RIGHT: x++; break; 240 | case KEY_F(1): { 241 | save_map(m, filename); 242 | } break; 243 | case KEY_F(2): { 244 | // Press F2 to toggle automatically moving the cursor 245 | autoright = !autoright; 246 | } break; 247 | default : { 248 | if(isprint(c)) { 249 | m->tiles[(y) * m->w + (x)].c = c; 250 | waddch(w,get_attrs(c)); 251 | if(autoright) x++; 252 | } else if(c == ' ' || c == KEY_BACKSPACE) { 253 | m->tiles[(y) * m->w + (x)].c = ' '; 254 | waddch(w,get_attrs(' ')); 255 | if(autoright) x++; 256 | } 257 | } break; 258 | } 259 | if(x < 0) x = 0; 260 | if(x >= m->w) 261 | x = m->w - 1; 262 | if(y < 0) y = 0; 263 | if(y >= m->h) 264 | y = m->h - 1; 265 | 266 | // Scrolling 267 | int nsx = x - ww/2; 268 | if(nsx < 0) 269 | nsx = 0; 270 | if(nsx + (ww - 2) >= m->w) 271 | nsx = m->w - (ww - 2); 272 | 273 | int nsy = y - wh/2; 274 | if(nsy < 0) 275 | nsy = 0; 276 | if(nsy + (wh - 2) >= m->h) 277 | nsy = m->h - (wh - 2); 278 | 279 | if(nsx != scrx || nsy != scry) { 280 | scrx = nsx; 281 | scry = nsy; 282 | draw_all(m, w, scry, scrx); 283 | } 284 | } 285 | delwin(w); 286 | endwin(); 287 | 288 | // Save a "work in progress" file, 289 | // in case they forgot to save before exit. 290 | char wip[128]; 291 | sprintf(wip, "%s.wip", filename); 292 | save_map(m, wip); 293 | 294 | free_map(m); 295 | 296 | return 0; 297 | } 298 | -------------------------------------------------------------------------------- /screens/screens.c: -------------------------------------------------------------------------------- 1 | /* 2 | Adapted from 3 | http://bobobobo.wordpress.com/2009/03/30/adding-an-icon-system-tray-win32-c/ 4 | */ 5 | 6 | ///////////////////////////////////////////// 7 | // // 8 | // Minimizing C++ Win32 App To System Tray // 9 | // // 10 | // You found this at bobobobo's weblog, // 11 | // http://bobobobo.wordpress.com // 12 | // // 13 | // Creation date: Mar 30/09 // 14 | // Last modified: Mar 30/09 // 15 | // // 16 | ///////////////////////////////////////////// 17 | 18 | // GIVING CREDIT WHERE CREDIT IS DUE!! 19 | // Thanks ubergeek! http://www.gidforums.com/t-5815.html 20 | 21 | #pragma region include and define 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #ifdef UNICODE 29 | #define stringcopy wcscpy 30 | #else 31 | #define stringcopy strcpy 32 | #endif 33 | 34 | #define BMPH_IMPLEMENTATION 35 | #include "bmph.h" 36 | #include "screens.h" 37 | 38 | #define ID_TRAY_EXIT_CONTEXT_MENU_ITEM 3000 39 | #define ID_TRAY_RESTORE_CONTEXT_MENU_ITEM 3001 40 | #define ID_TRAY_ENABLE_CONTEXT_MENU_ITEM 3002 41 | #define ID_TRAY_DISABLE_CONTEXT_MENU_ITEM 3003 42 | 43 | #define WM_TRAYICON ( WM_USER + 1 ) 44 | #pragma endregion 45 | 46 | #pragma region constants and globals 47 | UINT WM_TASKBARCREATED = 0 ; 48 | 49 | HWND g_hwnd ; 50 | HMENU g_menu ; 51 | 52 | NOTIFYICONDATA g_notifyIconData ; 53 | #pragma endregion 54 | 55 | LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM); 56 | 57 | #pragma region helper funcs 58 | 59 | HICON icon1, icon2; 60 | 61 | void Minimize() { 62 | Shell_NotifyIcon(NIM_ADD, &g_notifyIconData); 63 | ShowWindow(g_hwnd, SW_HIDE); 64 | } 65 | 66 | void Restore() { 67 | Shell_NotifyIcon(NIM_DELETE, &g_notifyIconData); 68 | ShowWindow(g_hwnd, SW_SHOW); 69 | } 70 | 71 | void saveScreenshot(); 72 | 73 | int enabled = 1; 74 | 75 | void InitNotifyIconData() 76 | { 77 | memset( &g_notifyIconData, 0, sizeof( NOTIFYICONDATA ) ) ; 78 | 79 | g_notifyIconData.cbSize = sizeof(NOTIFYICONDATA); 80 | 81 | g_notifyIconData.hWnd = g_hwnd; 82 | g_notifyIconData.uID = ID_TRAY_APP_ICON1; 83 | g_notifyIconData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; 84 | 85 | g_notifyIconData.uCallbackMessage = WM_TRAYICON; 86 | 87 | icon1 = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(ID_TRAY_APP_ICON1)); 88 | icon2 = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(ID_TRAY_APP_ICON2)); 89 | 90 | g_notifyIconData.hIcon = enabled ? icon1 : icon2; 91 | stringcopy(g_notifyIconData.szTip, enabled ? TEXT("Screens is enabled") : TEXT("Screens is disabled")); 92 | } 93 | #pragma endregion 94 | 95 | int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR args, int iCmdShow ) 96 | { 97 | TCHAR className[] = TEXT( "tray icon class" ); 98 | 99 | // listen for re-launching the taskbar. 100 | WM_TASKBARCREATED = RegisterWindowMessageA("TaskbarCreated") ; 101 | 102 | #pragma region get window up 103 | WNDCLASSEX wnd = { 0 }; 104 | 105 | wnd.hInstance = hInstance; 106 | wnd.lpszClassName = className; 107 | wnd.lpfnWndProc = WndProc; 108 | wnd.style = CS_HREDRAW | CS_VREDRAW ; 109 | wnd.cbSize = sizeof (WNDCLASSEX); 110 | 111 | wnd.hIcon = LoadIcon (NULL, IDI_APPLICATION); 112 | wnd.hIconSm = LoadIcon (NULL, IDI_APPLICATION); 113 | wnd.hCursor = LoadCursor (NULL, IDC_ARROW); 114 | wnd.hbrBackground = (HBRUSH)COLOR_APPWORKSPACE ; 115 | 116 | if (!RegisterClassEx(&wnd)) 117 | { 118 | FatalAppExit( 0, TEXT("Couldn't register window class!") ); 119 | } 120 | 121 | g_hwnd = CreateWindowEx ( 122 | 0, className, 123 | TEXT( APPLICATION_TITLE ), 124 | WS_OVERLAPPEDWINDOW, 125 | CW_USEDEFAULT, CW_USEDEFAULT, 126 | 400, 400, 127 | NULL, NULL, 128 | hInstance, NULL 129 | ); 130 | 131 | InitNotifyIconData(); 132 | 133 | WTSRegisterSessionNotification(g_hwnd, NOTIFY_FOR_THIS_SESSION); 134 | 135 | //RegisterHotKey(hwnd, 100, MOD_ALT | MOD_CONTROL, 'S'); 136 | RegisterHotKey(g_hwnd, 100, MOD_ALT, 'S'); 137 | 138 | Minimize(); 139 | #pragma endregion 140 | 141 | MSG msg ; 142 | while (GetMessage (&msg, NULL, 0, 0)) { 143 | TranslateMessage(&msg); 144 | DispatchMessage(&msg); 145 | } 146 | 147 | if( !IsWindowVisible( g_hwnd ) ) { 148 | Shell_NotifyIcon(NIM_DELETE, &g_notifyIconData); 149 | } 150 | 151 | return msg.wParam; 152 | } 153 | 154 | LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) 155 | { 156 | if ( message==WM_TASKBARCREATED && !IsWindowVisible( g_hwnd ) ) { 157 | Minimize(); 158 | return 0; 159 | } 160 | 161 | switch (message) 162 | { 163 | case WM_CREATE: 164 | 165 | g_menu = CreatePopupMenu(); 166 | 167 | AppendMenu(g_menu, MF_STRING, ID_TRAY_ENABLE_CONTEXT_MENU_ITEM, TEXT( "Enable" ) ); 168 | AppendMenu(g_menu, MF_STRING, ID_TRAY_DISABLE_CONTEXT_MENU_ITEM, TEXT( "Disable" ) ); 169 | AppendMenu(g_menu, MF_SEPARATOR, 0, 0 ); 170 | AppendMenu(g_menu, MF_STRING, ID_TRAY_EXIT_CONTEXT_MENU_ITEM, TEXT( "Exit" ) ); 171 | 172 | break; 173 | 174 | case WM_SYSCOMMAND: 175 | switch( wParam & 0xfff0 ) 176 | { 177 | case SC_MINIMIZE: 178 | case SC_CLOSE: 179 | Minimize() ; 180 | return 0 ; 181 | break; 182 | } 183 | break; 184 | case WM_HOTKEY: { 185 | if(enabled) saveScreenshot(); 186 | break; 187 | } 188 | case WM_TRAYICON: 189 | { 190 | switch(wParam) { 191 | case ID_TRAY_APP_ICON1: 192 | break; 193 | } 194 | 195 | if (lParam == WM_LBUTTONUP) { 196 | POINT curPoint ; 197 | GetCursorPos( &curPoint ); 198 | SetForegroundWindow(hwnd); 199 | 200 | printf("calling track\n"); 201 | UINT clicked = TrackPopupMenu( 202 | g_menu, 203 | TPM_RETURNCMD | TPM_NONOTIFY, 204 | curPoint.x, 205 | curPoint.y, 206 | 0, 207 | hwnd, 208 | NULL 209 | ); 210 | 211 | if (clicked == ID_TRAY_EXIT_CONTEXT_MENU_ITEM) { 212 | PostQuitMessage( 0 ) ; 213 | } else if (clicked == ID_TRAY_RESTORE_CONTEXT_MENU_ITEM) { 214 | Restore(); 215 | } else if (clicked == ID_TRAY_ENABLE_CONTEXT_MENU_ITEM) { 216 | enabled = 1; 217 | Shell_NotifyIcon(NIM_DELETE, &g_notifyIconData); 218 | g_notifyIconData.hIcon = icon1; 219 | stringcopy(g_notifyIconData.szTip, TEXT("Screens is enabled")); 220 | Shell_NotifyIcon(NIM_ADD, &g_notifyIconData); 221 | } else if (clicked == ID_TRAY_DISABLE_CONTEXT_MENU_ITEM) { 222 | enabled = 0; 223 | Shell_NotifyIcon(NIM_DELETE, &g_notifyIconData); 224 | g_notifyIconData.hIcon = icon2; 225 | stringcopy(g_notifyIconData.szTip, TEXT("Screens is disabled")); 226 | Shell_NotifyIcon(NIM_ADD, &g_notifyIconData); 227 | } 228 | 229 | } 230 | } 231 | break; 232 | 233 | case WM_CLOSE: 234 | Minimize() ; 235 | return 0; 236 | break; 237 | 238 | case WM_DESTROY: 239 | PostQuitMessage (0); 240 | break; 241 | 242 | } 243 | 244 | return DefWindowProc( hwnd, message, wParam, lParam ) ; 245 | } 246 | 247 | void saveScreenshot() { 248 | // http://stackoverflow.com/a/3291411/115589 249 | HDC hScreenDC = CreateDC("DISPLAY", NULL, NULL, NULL); 250 | HDC hMemoryDC = CreateCompatibleDC(hScreenDC); 251 | int width = GetDeviceCaps(hScreenDC, HORZRES); 252 | int height = GetDeviceCaps(hScreenDC, VERTRES); 253 | 254 | BITMAPINFO bmi; 255 | ZeroMemory(&bmi, sizeof bmi); 256 | bmi.bmiHeader.biSize = sizeof(BITMAPINFO); 257 | bmi.bmiHeader.biWidth = width; 258 | bmi.bmiHeader.biHeight = -height; // Order pixels from top to bottom 259 | bmi.bmiHeader.biPlanes = 1; 260 | bmi.bmiHeader.biBitCount = 32; // last byte not used, 32 bit for alignment 261 | bmi.bmiHeader.biCompression = BI_RGB; 262 | bmi.bmiHeader.biSizeImage = 0; 263 | bmi.bmiHeader.biXPelsPerMeter = 0; 264 | bmi.bmiHeader.biYPelsPerMeter = 0; 265 | bmi.bmiHeader.biClrUsed = 0; 266 | bmi.bmiHeader.biClrImportant = 0; 267 | bmi.bmiColors[0].rgbBlue = 0; 268 | bmi.bmiColors[0].rgbGreen = 0; 269 | bmi.bmiColors[0].rgbRed = 0; 270 | bmi.bmiColors[0].rgbReserved = 0; 271 | 272 | unsigned char *pixels; 273 | HBITMAP hbmp = CreateDIBSection( hMemoryDC, &bmi, DIB_RGB_COLORS, (void**)&pixels, NULL, 0 ); 274 | 275 | HBITMAP hOldBitmap = SelectObject(hMemoryDC, hbmp); 276 | 277 | BitBlt(hMemoryDC, 0, 0, width, height, hScreenDC, 0, 0, SRCCOPY); 278 | 279 | Bitmap *bmp = bm_bind(width, height, pixels); 280 | 281 | char filename[128]; 282 | time_t t; 283 | struct tm *tmp; 284 | 285 | t = time(NULL); 286 | tmp = localtime(&t); 287 | strftime(filename, sizeof filename, "screen-%Y%m%d%H%M%S.bmp", tmp); 288 | 289 | bm_save(bmp, filename); 290 | bm_unbind(bmp); 291 | 292 | DeleteObject(hbmp); 293 | 294 | DeleteDC(hMemoryDC); 295 | DeleteDC(hScreenDC); 296 | } 297 | -------------------------------------------------------------------------------- /fenster-microui/microui.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** Copyright (c) 2020 rxi 3 | ** 4 | ** This library is free software; you can redistribute it and/or modify it 5 | ** under the terms of the MIT license. See `microui.c` for details. 6 | */ 7 | 8 | #ifndef MICROUI_H 9 | #define MICROUI_H 10 | 11 | #define MU_VERSION "2.01" 12 | 13 | #define MU_COMMANDLIST_SIZE (256 * 1024) 14 | #define MU_ROOTLIST_SIZE 32 15 | #define MU_CONTAINERSTACK_SIZE 32 16 | #define MU_CLIPSTACK_SIZE 32 17 | #define MU_IDSTACK_SIZE 32 18 | #define MU_LAYOUTSTACK_SIZE 16 19 | #define MU_CONTAINERPOOL_SIZE 48 20 | #define MU_TREENODEPOOL_SIZE 48 21 | #define MU_MAX_WIDTHS 16 22 | #define MU_REAL float 23 | #define MU_REAL_FMT "%.3g" 24 | #define MU_SLIDER_FMT "%.2f" 25 | #define MU_MAX_FMT 127 26 | 27 | #define mu_stack(T, n) struct { int idx; T items[n]; } 28 | #define mu_min(a, b) ((a) < (b) ? (a) : (b)) 29 | #define mu_max(a, b) ((a) > (b) ? (a) : (b)) 30 | #define mu_clamp(x, a, b) mu_min(b, mu_max(a, x)) 31 | 32 | enum { 33 | MU_CLIP_PART = 1, 34 | MU_CLIP_ALL 35 | }; 36 | 37 | enum { 38 | MU_COMMAND_JUMP = 1, 39 | MU_COMMAND_CLIP, 40 | MU_COMMAND_RECT, 41 | MU_COMMAND_TEXT, 42 | MU_COMMAND_ICON, 43 | MU_COMMAND_MAX 44 | }; 45 | 46 | enum { 47 | MU_COLOR_TEXT, 48 | MU_COLOR_BORDER, 49 | MU_COLOR_WINDOWBG, 50 | MU_COLOR_TITLEBG, 51 | MU_COLOR_TITLETEXT, 52 | MU_COLOR_PANELBG, 53 | MU_COLOR_BUTTON, 54 | MU_COLOR_BUTTONHOVER, 55 | MU_COLOR_BUTTONFOCUS, 56 | MU_COLOR_BASE, 57 | MU_COLOR_BASEHOVER, 58 | MU_COLOR_BASEFOCUS, 59 | MU_COLOR_SCROLLBASE, 60 | MU_COLOR_SCROLLTHUMB, 61 | MU_COLOR_MAX 62 | }; 63 | 64 | enum { 65 | MU_ICON_CLOSE = 1, 66 | MU_ICON_CHECK, 67 | MU_ICON_COLLAPSED, 68 | MU_ICON_EXPANDED, 69 | MU_ICON_MAX 70 | }; 71 | 72 | enum { 73 | MU_RES_ACTIVE = (1 << 0), 74 | MU_RES_SUBMIT = (1 << 1), 75 | MU_RES_CHANGE = (1 << 2) 76 | }; 77 | 78 | enum { 79 | MU_OPT_ALIGNCENTER = (1 << 0), 80 | MU_OPT_ALIGNRIGHT = (1 << 1), 81 | MU_OPT_NOINTERACT = (1 << 2), 82 | MU_OPT_NOFRAME = (1 << 3), 83 | MU_OPT_NORESIZE = (1 << 4), 84 | MU_OPT_NOSCROLL = (1 << 5), 85 | MU_OPT_NOCLOSE = (1 << 6), 86 | MU_OPT_NOTITLE = (1 << 7), 87 | MU_OPT_HOLDFOCUS = (1 << 8), 88 | MU_OPT_AUTOSIZE = (1 << 9), 89 | MU_OPT_POPUP = (1 << 10), 90 | MU_OPT_CLOSED = (1 << 11), 91 | MU_OPT_EXPANDED = (1 << 12) 92 | }; 93 | 94 | enum { 95 | MU_MOUSE_LEFT = (1 << 0), 96 | MU_MOUSE_RIGHT = (1 << 1), 97 | MU_MOUSE_MIDDLE = (1 << 2) 98 | }; 99 | 100 | enum { 101 | MU_KEY_SHIFT = (1 << 0), 102 | MU_KEY_CTRL = (1 << 1), 103 | MU_KEY_ALT = (1 << 2), 104 | MU_KEY_BACKSPACE = (1 << 3), 105 | MU_KEY_RETURN = (1 << 4) 106 | }; 107 | 108 | 109 | typedef struct mu_Context mu_Context; 110 | typedef unsigned mu_Id; 111 | typedef MU_REAL mu_Real; 112 | typedef void* mu_Font; 113 | 114 | typedef struct { int x, y; } mu_Vec2; 115 | typedef struct { int x, y, w, h; } mu_Rect; 116 | typedef struct { unsigned char r, g, b, a; } mu_Color; 117 | typedef struct { mu_Id id; int last_update; } mu_PoolItem; 118 | 119 | typedef struct { int type, size; } mu_BaseCommand; 120 | typedef struct { mu_BaseCommand base; void *dst; } mu_JumpCommand; 121 | typedef struct { mu_BaseCommand base; mu_Rect rect; } mu_ClipCommand; 122 | typedef struct { mu_BaseCommand base; mu_Rect rect; mu_Color color; } mu_RectCommand; 123 | typedef struct { mu_BaseCommand base; mu_Font font; mu_Vec2 pos; mu_Color color; char str[1]; } mu_TextCommand; 124 | typedef struct { mu_BaseCommand base; mu_Rect rect; int id; mu_Color color; } mu_IconCommand; 125 | 126 | typedef union { 127 | int type; 128 | mu_BaseCommand base; 129 | mu_JumpCommand jump; 130 | mu_ClipCommand clip; 131 | mu_RectCommand rect; 132 | mu_TextCommand text; 133 | mu_IconCommand icon; 134 | } mu_Command; 135 | 136 | typedef struct { 137 | mu_Rect body; 138 | mu_Rect next; 139 | mu_Vec2 position; 140 | mu_Vec2 size; 141 | mu_Vec2 max; 142 | int widths[MU_MAX_WIDTHS]; 143 | int items; 144 | int item_index; 145 | int next_row; 146 | int next_type; 147 | int indent; 148 | } mu_Layout; 149 | 150 | typedef struct { 151 | mu_Command *head, *tail; 152 | mu_Rect rect; 153 | mu_Rect body; 154 | mu_Vec2 content_size; 155 | mu_Vec2 scroll; 156 | int zindex; 157 | int open; 158 | } mu_Container; 159 | 160 | typedef struct { 161 | mu_Font font; 162 | mu_Vec2 size; 163 | int padding; 164 | int spacing; 165 | int indent; 166 | int title_height; 167 | int scrollbar_size; 168 | int thumb_size; 169 | mu_Color colors[MU_COLOR_MAX]; 170 | } mu_Style; 171 | 172 | struct mu_Context { 173 | /* callbacks */ 174 | int (*text_width)(mu_Font font, const char *str, int len); 175 | int (*text_height)(mu_Font font); 176 | void (*draw_frame)(mu_Context *ctx, mu_Rect rect, int colorid); 177 | /* core state */ 178 | mu_Style _style; 179 | mu_Style *style; 180 | mu_Id hover; 181 | mu_Id focus; 182 | mu_Id last_id; 183 | mu_Rect last_rect; 184 | int last_zindex; 185 | int updated_focus; 186 | int frame; 187 | mu_Container *hover_root; 188 | mu_Container *next_hover_root; 189 | mu_Container *scroll_target; 190 | char number_edit_buf[MU_MAX_FMT]; 191 | mu_Id number_edit; 192 | /* stacks */ 193 | mu_stack(char, MU_COMMANDLIST_SIZE) command_list; 194 | mu_stack(mu_Container*, MU_ROOTLIST_SIZE) root_list; 195 | mu_stack(mu_Container*, MU_CONTAINERSTACK_SIZE) container_stack; 196 | mu_stack(mu_Rect, MU_CLIPSTACK_SIZE) clip_stack; 197 | mu_stack(mu_Id, MU_IDSTACK_SIZE) id_stack; 198 | mu_stack(mu_Layout, MU_LAYOUTSTACK_SIZE) layout_stack; 199 | /* retained state pools */ 200 | mu_PoolItem container_pool[MU_CONTAINERPOOL_SIZE]; 201 | mu_Container containers[MU_CONTAINERPOOL_SIZE]; 202 | mu_PoolItem treenode_pool[MU_TREENODEPOOL_SIZE]; 203 | /* input state */ 204 | mu_Vec2 mouse_pos; 205 | mu_Vec2 last_mouse_pos; 206 | mu_Vec2 mouse_delta; 207 | mu_Vec2 scroll_delta; 208 | int mouse_down; 209 | int mouse_pressed; 210 | int key_down; 211 | int key_pressed; 212 | char input_text[32]; 213 | }; 214 | 215 | 216 | mu_Vec2 mu_vec2(int x, int y); 217 | mu_Rect mu_rect(int x, int y, int w, int h); 218 | mu_Color mu_color(int r, int g, int b, int a); 219 | 220 | void mu_init(mu_Context *ctx); 221 | void mu_begin(mu_Context *ctx); 222 | void mu_end(mu_Context *ctx); 223 | void mu_set_focus(mu_Context *ctx, mu_Id id); 224 | mu_Id mu_get_id(mu_Context *ctx, const void *data, int size); 225 | void mu_push_id(mu_Context *ctx, const void *data, int size); 226 | void mu_pop_id(mu_Context *ctx); 227 | void mu_push_clip_rect(mu_Context *ctx, mu_Rect rect); 228 | void mu_pop_clip_rect(mu_Context *ctx); 229 | mu_Rect mu_get_clip_rect(mu_Context *ctx); 230 | int mu_check_clip(mu_Context *ctx, mu_Rect r); 231 | mu_Container* mu_get_current_container(mu_Context *ctx); 232 | mu_Container* mu_get_container(mu_Context *ctx, const char *name); 233 | void mu_bring_to_front(mu_Context *ctx, mu_Container *cnt); 234 | 235 | int mu_pool_init(mu_Context *ctx, mu_PoolItem *items, int len, mu_Id id); 236 | int mu_pool_get(mu_Context *ctx, mu_PoolItem *items, int len, mu_Id id); 237 | void mu_pool_update(mu_Context *ctx, mu_PoolItem *items, int idx); 238 | 239 | void mu_input_mousemove(mu_Context *ctx, int x, int y); 240 | void mu_input_mousedown(mu_Context *ctx, int x, int y, int btn); 241 | void mu_input_mouseup(mu_Context *ctx, int x, int y, int btn); 242 | void mu_input_scroll(mu_Context *ctx, int x, int y); 243 | void mu_input_keydown(mu_Context *ctx, int key); 244 | void mu_input_keyup(mu_Context *ctx, int key); 245 | void mu_input_text(mu_Context *ctx, const char *text); 246 | 247 | mu_Command* mu_push_command(mu_Context *ctx, int type, int size); 248 | int mu_next_command(mu_Context *ctx, mu_Command **cmd); 249 | void mu_set_clip(mu_Context *ctx, mu_Rect rect); 250 | void mu_draw_rect(mu_Context *ctx, mu_Rect rect, mu_Color color); 251 | void mu_draw_box(mu_Context *ctx, mu_Rect rect, mu_Color color); 252 | void mu_draw_text(mu_Context *ctx, mu_Font font, const char *str, int len, mu_Vec2 pos, mu_Color color); 253 | void mu_draw_icon(mu_Context *ctx, int id, mu_Rect rect, mu_Color color); 254 | 255 | void mu_layout_row(mu_Context *ctx, int items, const int *widths, int height); 256 | void mu_layout_width(mu_Context *ctx, int width); 257 | void mu_layout_height(mu_Context *ctx, int height); 258 | void mu_layout_begin_column(mu_Context *ctx); 259 | void mu_layout_end_column(mu_Context *ctx); 260 | void mu_layout_set_next(mu_Context *ctx, mu_Rect r, int relative); 261 | mu_Rect mu_layout_next(mu_Context *ctx); 262 | 263 | void mu_draw_control_frame(mu_Context *ctx, mu_Id id, mu_Rect rect, int colorid, int opt); 264 | void mu_draw_control_text(mu_Context *ctx, const char *str, mu_Rect rect, int colorid, int opt); 265 | int mu_mouse_over(mu_Context *ctx, mu_Rect rect); 266 | void mu_update_control(mu_Context *ctx, mu_Id id, mu_Rect rect, int opt); 267 | 268 | #define mu_button(ctx, label) mu_button_ex(ctx, label, 0, MU_OPT_ALIGNCENTER) 269 | #define mu_textbox(ctx, buf, bufsz) mu_textbox_ex(ctx, buf, bufsz, 0) 270 | #define mu_slider(ctx, value, lo, hi) mu_slider_ex(ctx, value, lo, hi, 0, MU_SLIDER_FMT, MU_OPT_ALIGNCENTER) 271 | #define mu_number(ctx, value, step) mu_number_ex(ctx, value, step, MU_SLIDER_FMT, MU_OPT_ALIGNCENTER) 272 | #define mu_header(ctx, label) mu_header_ex(ctx, label, 0) 273 | #define mu_begin_treenode(ctx, label) mu_begin_treenode_ex(ctx, label, 0) 274 | #define mu_begin_window(ctx, title, rect) mu_begin_window_ex(ctx, title, rect, 0) 275 | #define mu_begin_panel(ctx, name) mu_begin_panel_ex(ctx, name, 0) 276 | 277 | void mu_text(mu_Context *ctx, const char *text); 278 | void mu_label(mu_Context *ctx, const char *text); 279 | int mu_button_ex(mu_Context *ctx, const char *label, int icon, int opt); 280 | int mu_checkbox(mu_Context *ctx, const char *label, int *state); 281 | int mu_textbox_raw(mu_Context *ctx, char *buf, int bufsz, mu_Id id, mu_Rect r, int opt); 282 | int mu_textbox_ex(mu_Context *ctx, char *buf, int bufsz, int opt); 283 | int mu_slider_ex(mu_Context *ctx, mu_Real *value, mu_Real low, mu_Real high, mu_Real step, const char *fmt, int opt); 284 | int mu_number_ex(mu_Context *ctx, mu_Real *value, mu_Real step, const char *fmt, int opt); 285 | int mu_header_ex(mu_Context *ctx, const char *label, int opt); 286 | int mu_begin_treenode_ex(mu_Context *ctx, const char *label, int opt); 287 | void mu_end_treenode(mu_Context *ctx); 288 | int mu_begin_window_ex(mu_Context *ctx, const char *title, mu_Rect rect, int opt); 289 | void mu_end_window(mu_Context *ctx); 290 | void mu_open_popup(mu_Context *ctx, const char *name); 291 | int mu_begin_popup(mu_Context *ctx, const char *name); 292 | void mu_end_popup(mu_Context *ctx); 293 | void mu_begin_panel_ex(mu_Context *ctx, const char *name, int opt); 294 | void mu_end_panel(mu_Context *ctx); 295 | 296 | #endif 297 | -------------------------------------------------------------------------------- /fenster-microui/demo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // https://github.com/rxi/microui 5 | #include "microui.h" 6 | 7 | // https://github.com/zserge/fenster 8 | #include "fenster.h" 9 | 10 | #include "fenmui.h" 11 | 12 | #define WIDTH 800 13 | #define HEIGHT 600 14 | 15 | static char logbuf[64000]; 16 | static int logbuf_updated = 0; 17 | static float bg[3] = { 90, 95, 100 }; 18 | 19 | static void write_log(const char *text) { 20 | if (logbuf[0]) { strcat(logbuf, "\n"); } 21 | strcat(logbuf, text); 22 | logbuf_updated = 1; 23 | } 24 | 25 | static void test_window(mu_Context *ctx) { 26 | /* do window */ 27 | if (mu_begin_window(ctx, "Demo Window", mu_rect(40, 40, 300, 450))) { 28 | mu_Container *win = mu_get_current_container(ctx); 29 | win->rect.w = mu_max(win->rect.w, 240); 30 | win->rect.h = mu_max(win->rect.h, 300); 31 | 32 | /* window info */ 33 | if (mu_header(ctx, "Window Info")) { 34 | mu_Container *win = mu_get_current_container(ctx); 35 | char buf[64]; 36 | mu_layout_row(ctx, 2, (int[]) { 54, -1 }, 0); 37 | mu_label(ctx,"Position:"); 38 | sprintf(buf, "%d, %d", win->rect.x, win->rect.y); mu_label(ctx, buf); 39 | mu_label(ctx, "Size:"); 40 | sprintf(buf, "%d, %d", win->rect.w, win->rect.h); mu_label(ctx, buf); 41 | } 42 | 43 | /* labels + buttons */ 44 | if (mu_header_ex(ctx, "Test Buttons", MU_OPT_EXPANDED)) { 45 | mu_layout_row(ctx, 3, (int[]) { 86, -110, -1 }, 0); 46 | mu_label(ctx, "Test buttons 1:"); 47 | if (mu_button(ctx, "Button 1")) { write_log("Pressed button 1"); } 48 | if (mu_button(ctx, "Button 2")) { write_log("Pressed button 2"); } 49 | mu_label(ctx, "Test buttons 2:"); 50 | if (mu_button(ctx, "Button 3")) { write_log("Pressed button 3"); } 51 | if (mu_button(ctx, "Popup")) { mu_open_popup(ctx, "Test Popup"); } 52 | if (mu_begin_popup(ctx, "Test Popup")) { 53 | mu_button(ctx, "Hello"); 54 | mu_button(ctx, "World"); 55 | mu_end_popup(ctx); 56 | } 57 | } 58 | 59 | /* tree */ 60 | if (mu_header_ex(ctx, "Tree and Text", MU_OPT_EXPANDED)) { 61 | mu_layout_row(ctx, 2, (int[]) { 140, -1 }, 0); 62 | mu_layout_begin_column(ctx); 63 | if (mu_begin_treenode(ctx, "Test 1")) { 64 | if (mu_begin_treenode(ctx, "Test 1a")) { 65 | mu_label(ctx, "Hello"); 66 | mu_label(ctx, "world"); 67 | mu_end_treenode(ctx); 68 | } 69 | if (mu_begin_treenode(ctx, "Test 1b")) { 70 | if (mu_button(ctx, "Button 1")) { write_log("Pressed button 1"); } 71 | if (mu_button(ctx, "Button 2")) { write_log("Pressed button 2"); } 72 | mu_end_treenode(ctx); 73 | } 74 | mu_end_treenode(ctx); 75 | } 76 | if (mu_begin_treenode(ctx, "Test 2")) { 77 | mu_layout_row(ctx, 2, (int[]) { 54, 54 }, 0); 78 | if (mu_button(ctx, "Button 3")) { write_log("Pressed button 3"); } 79 | if (mu_button(ctx, "Button 4")) { write_log("Pressed button 4"); } 80 | if (mu_button(ctx, "Button 5")) { write_log("Pressed button 5"); } 81 | if (mu_button(ctx, "Button 6")) { write_log("Pressed button 6"); } 82 | mu_end_treenode(ctx); 83 | } 84 | if (mu_begin_treenode(ctx, "Test 3")) { 85 | static int checks[3] = { 1, 0, 1 }; 86 | mu_checkbox(ctx, "Checkbox 1", &checks[0]); 87 | mu_checkbox(ctx, "Checkbox 2", &checks[1]); 88 | mu_checkbox(ctx, "Checkbox 3", &checks[2]); 89 | mu_end_treenode(ctx); 90 | } 91 | mu_layout_end_column(ctx); 92 | 93 | mu_layout_begin_column(ctx); 94 | mu_layout_row(ctx, 1, (int[]) { -1 }, 0); 95 | mu_text(ctx, "Lorem ipsum dolor sit amet, consectetur adipiscing " 96 | "elit. Maecenas lacinia, sem eu lacinia molestie, mi risus faucibus " 97 | "ipsum, eu varius magna felis a nulla."); 98 | mu_layout_end_column(ctx); 99 | } 100 | 101 | /* background color sliders */ 102 | if (mu_header_ex(ctx, "Background Color", MU_OPT_EXPANDED)) { 103 | mu_layout_row(ctx, 2, (int[]) { -78, -1 }, 74); 104 | /* sliders */ 105 | mu_layout_begin_column(ctx); 106 | mu_layout_row(ctx, 2, (int[]) { 46, -1 }, 0); 107 | mu_label(ctx, "Red:"); mu_slider(ctx, &bg[0], 0, 255); 108 | mu_label(ctx, "Green:"); mu_slider(ctx, &bg[1], 0, 255); 109 | mu_label(ctx, "Blue:"); mu_slider(ctx, &bg[2], 0, 255); 110 | mu_layout_end_column(ctx); 111 | /* color preview */ 112 | mu_Rect r = mu_layout_next(ctx); 113 | mu_draw_rect(ctx, r, mu_color(bg[0], bg[1], bg[2], 255)); 114 | char buf[32]; 115 | sprintf(buf, "#%02X%02X%02X", (int) bg[0], (int) bg[1], (int) bg[2]); 116 | mu_draw_control_text(ctx, buf, r, MU_COLOR_TEXT, MU_OPT_ALIGNCENTER); 117 | } 118 | 119 | mu_end_window(ctx); 120 | } 121 | } 122 | 123 | 124 | static void log_window(mu_Context *ctx) { 125 | if (mu_begin_window(ctx, "Log Window", mu_rect(350, 40, 300, 200))) { 126 | /* output text panel */ 127 | mu_layout_row(ctx, 1, (int[]) { -1 }, -25); 128 | mu_begin_panel(ctx, "Log Output"); 129 | mu_Container *panel = mu_get_current_container(ctx); 130 | mu_layout_row(ctx, 1, (int[]) { -1 }, -1); 131 | mu_text(ctx, logbuf); 132 | mu_end_panel(ctx); 133 | if (logbuf_updated) { 134 | panel->scroll.y = panel->content_size.y; 135 | logbuf_updated = 0; 136 | } 137 | 138 | /* input textbox + submit button */ 139 | static char buf[128]; 140 | int submitted = 0; 141 | mu_layout_row(ctx, 2, (int[]) { -70, -1 }, 0); 142 | if (mu_textbox(ctx, buf, sizeof(buf)) & MU_RES_SUBMIT) { 143 | mu_set_focus(ctx, ctx->last_id); 144 | submitted = 1; 145 | } 146 | if (mu_button(ctx, "Submit")) { submitted = 1; } 147 | if (submitted) { 148 | write_log(buf); 149 | buf[0] = '\0'; 150 | } 151 | 152 | mu_end_window(ctx); 153 | } 154 | } 155 | 156 | 157 | static int uint8_slider(mu_Context *ctx, unsigned char *value, int low, int high) { 158 | static float tmp; 159 | mu_push_id(ctx, &value, sizeof(value)); 160 | tmp = *value; 161 | int res = mu_slider_ex(ctx, &tmp, low, high, 0, "%.0f", MU_OPT_ALIGNCENTER); 162 | *value = tmp; 163 | mu_pop_id(ctx); 164 | return res; 165 | } 166 | 167 | 168 | static void style_window(mu_Context *ctx) { 169 | static struct { const char *label; int idx; } colors[] = { 170 | { "text:", MU_COLOR_TEXT }, 171 | { "border:", MU_COLOR_BORDER }, 172 | { "windowbg:", MU_COLOR_WINDOWBG }, 173 | { "titlebg:", MU_COLOR_TITLEBG }, 174 | { "titletext:", MU_COLOR_TITLETEXT }, 175 | { "panelbg:", MU_COLOR_PANELBG }, 176 | { "button:", MU_COLOR_BUTTON }, 177 | { "buttonhover:", MU_COLOR_BUTTONHOVER }, 178 | { "buttonfocus:", MU_COLOR_BUTTONFOCUS }, 179 | { "base:", MU_COLOR_BASE }, 180 | { "basehover:", MU_COLOR_BASEHOVER }, 181 | { "basefocus:", MU_COLOR_BASEFOCUS }, 182 | { "scrollbase:", MU_COLOR_SCROLLBASE }, 183 | { "scrollthumb:", MU_COLOR_SCROLLTHUMB }, 184 | { NULL } 185 | }; 186 | 187 | if (mu_begin_window(ctx, "Style Editor", mu_rect(350, 250, 300, 240))) { 188 | int sw = mu_get_current_container(ctx)->body.w * 0.14; 189 | mu_layout_row(ctx, 6, (int[]) { 80, sw, sw, sw, sw, -1 }, 0); 190 | for (int i = 0; colors[i].label; i++) { 191 | mu_label(ctx, colors[i].label); 192 | uint8_slider(ctx, &ctx->style->colors[i].r, 0, 255); 193 | uint8_slider(ctx, &ctx->style->colors[i].g, 0, 255); 194 | uint8_slider(ctx, &ctx->style->colors[i].b, 0, 255); 195 | uint8_slider(ctx, &ctx->style->colors[i].a, 0, 255); 196 | mu_draw_rect(ctx, mu_layout_next(ctx), ctx->style->colors[i]); 197 | } 198 | mu_end_window(ctx); 199 | } 200 | } 201 | 202 | 203 | static void process_frame(mu_Context *ctx) { 204 | mu_begin(ctx); 205 | style_window(ctx); 206 | log_window(ctx); 207 | test_window(ctx); 208 | mu_end(ctx); 209 | } 210 | 211 | 212 | static uint32_t buf[WIDTH * HEIGHT]; 213 | 214 | int run(int argc, char *argv[]) { 215 | 216 | struct fenster f = { 217 | .title = "MicroUI", 218 | .width = WIDTH, 219 | .height = HEIGHT, 220 | .buf = buf 221 | }; 222 | 223 | memset(buf, 0, sizeof buf); 224 | fenster_open(&f); 225 | 226 | /* init microui */ 227 | mu_Context *ctx = malloc(sizeof(mu_Context)); 228 | mu_init(ctx); 229 | 230 | fenmui_init(&f, ctx); 231 | 232 | while(fenster_loop(&f) == 0) { 233 | int64_t start = fenster_time(); 234 | 235 | if(f.keys[27]) break; 236 | 237 | fenmui_events(); 238 | 239 | /* process frame */ 240 | process_frame(ctx); 241 | 242 | /* render */ 243 | r_clear(mu_color(bg[0], bg[1], bg[2], 255)); 244 | fenmui_draw(); 245 | 246 | 247 | int64_t time = fenster_time() - start; 248 | if(time < 1000 / 30) 249 | fenster_sleep((1000 / 30) - time); 250 | //int64_t deltat = fenster_time() - start; 251 | } 252 | 253 | fenster_close(&f); 254 | 255 | return 0; 256 | } 257 | 258 | 259 | #ifdef _WIN32 260 | /* 261 | Alternative to CommandLineToArgvW(). 262 | I used a compiler where shellapi.h was not available, 263 | so this function breaks it down according to the last set of rules in 264 | http://i1.blogs.msdn.com/b/oldnewthing/archive/2010/09/17/10063629.aspx 265 | */ 266 | static int split_cmd_line(char *cmdl, char *argv[], int max) { 267 | 268 | int argc = 0; 269 | char *p = cmdl, *q = p, *arg = p; 270 | int state = 1; 271 | while(state) { 272 | switch(state) { 273 | case 1: 274 | if(argc == max) return argc; 275 | if(!*p) { 276 | state = 0; 277 | } else if(isspace(*p)) { 278 | *q++ = *p++; 279 | } else if(*p == '\"') { 280 | state = 2; 281 | *q++ = *p++; 282 | arg = q; 283 | } else { 284 | state = 3; 285 | arg = q; 286 | *q++ = *p++; 287 | } 288 | break; 289 | case 2: 290 | if(!*p) { 291 | argv[argc++] = arg; 292 | *q++ = '\0'; 293 | state = 0; 294 | } else if(*p == '\"') { 295 | if(p[1] == '\"') { 296 | state = 2; 297 | *q++ = *p; 298 | p+=2; 299 | } else { 300 | state = 1; 301 | argv[argc++] = arg; 302 | *q++ = '\0'; 303 | p++; 304 | } 305 | } else { 306 | *q++ = *p++; 307 | } 308 | break; 309 | case 3: 310 | if(!*p) { 311 | state = 0; 312 | argv[argc++] = arg; 313 | *q++ = '\0'; 314 | } else if(isspace(*p)) { 315 | state = 1; 316 | argv[argc++] = arg; 317 | *q++ = '\0'; 318 | p++; 319 | } else { 320 | *q++ = *p++; 321 | } 322 | break; 323 | } 324 | } 325 | return argc; 326 | } 327 | int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pCmdLine, int nCmdShow) { 328 | (void)hInstance, (void)hPrevInstance, (void)pCmdLine, (void)nCmdShow; 329 | #define MAX_ARGS 32 330 | char *argv[MAX_ARGS]; 331 | LPTSTR cmdl = _strdup(GetCommandLine()); 332 | int argc = split_cmd_line(cmdl, argv, MAX_ARGS); 333 | 334 | int rv = run(argc, argv); 335 | 336 | free(cmdl); 337 | 338 | return rv; 339 | } 340 | #else 341 | int main(int argc, char *argv[]) { 342 | return run(argc, argv); 343 | } 344 | #endif 345 | -------------------------------------------------------------------------------- /mybas/bas.c: -------------------------------------------------------------------------------- 1 | /* 2 | https://github.com/norvig/pytudes/blob/master/ipynb/BASIC.ipynb 3 | https://en.wikipedia.org/wiki/Recursive_descent_parser 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "bas.h" 13 | #include "ast.h" 14 | #include "hash.h" 15 | 16 | const char *Filename; 17 | static int Sym = 0, LastSym = 0; 18 | static char Word[64], LastWord[64]; 19 | static char *Text = NULL; 20 | static char *Lex = NULL; 21 | int Line = 1, /* Actual line in the file */ 22 | BasicLine = 0; /* BASIC line number */ 23 | 24 | Node *Funs[26]; 25 | 26 | static struct { 27 | const char *name; 28 | int value; 29 | } Keywords[] = { 30 | {"LET", LET}, 31 | {"READ", READ}, 32 | {"DATA", DATA}, 33 | {"PRINT", PRINT}, 34 | {"GOTO", GOTO}, 35 | {"IF", IF}, 36 | {"FOR", FOR}, 37 | {"NEXT", NEXT}, 38 | {"END", END}, 39 | {"DEF", DEF}, 40 | {"GOSUB", GOSUB}, 41 | {"RETURN", RETURN}, 42 | {"DIM", DIM}, 43 | {"REM", REM}, 44 | {"TO", TO}, 45 | {"THEN", THEN}, 46 | {"STEP", STEP}, 47 | {"STOP", STOP}, 48 | {NULL,0} 49 | }, 50 | Functions[] = { 51 | {"SIN",SIN}, 52 | {"COS",COS}, 53 | {"TAN",TAN}, 54 | {"ATN",ATN}, 55 | {"EXP",EXP}, 56 | {"ABS",ABS}, 57 | {"LOG",LOG}, 58 | {"SQR",SQR}, 59 | {"RND",RND}, 60 | {"INT",INT}, 61 | {"FN",FN}, 62 | {NULL,0} 63 | }; 64 | const char *nodename(int type); 65 | static void nextsym(); 66 | void error(const char *msg, ...) { 67 | va_list ap; 68 | va_start(ap, msg); 69 | fprintf(stderr, "\nerror:%d (%d): ", BasicLine, Line); 70 | vfprintf(stderr, msg, ap); 71 | va_end(ap); 72 | exit(EXIT_FAILURE); 73 | } 74 | 75 | static int accept(int s) { 76 | if(Sym == s) { 77 | nextsym(); 78 | return 1; 79 | } 80 | return 0; 81 | } 82 | 83 | static int expect(int s) { 84 | if(accept(s)) 85 | return 1; 86 | error("'%s' expected; got '%s'", nodename(s), Word); 87 | return 0; 88 | } 89 | 90 | static void init(char *text) { 91 | Text = text; 92 | Lex = text; 93 | nextsym(); 94 | } 95 | #define SYMBOL(s) do{Sym=s;return;}while(0) 96 | static void nextsym() { 97 | LastSym = Sym; 98 | memcpy(LastWord, Word, sizeof LastWord); 99 | Word[0] = 0; 100 | int l = 0, i; 101 | while(isspace(Lex[0])) { 102 | if(Lex[0] == '\n') { 103 | Lex++; 104 | SYMBOL(NL); 105 | } 106 | Lex++; 107 | } 108 | if(!Lex[0]) 109 | SYMBOL(FEND); 110 | else if(isalpha(Lex[0])) { 111 | do { 112 | Word[l++] = toupper(Lex[0]); 113 | Lex++; 114 | if(l >= sizeof Word - 1) 115 | error("identifier too long"); 116 | } while(isalnum(Lex[0])); 117 | Word[l] = '\0'; 118 | for(i = 0; Keywords[i].name; i++) 119 | if(!strcmp(Keywords[i].name, Word)) 120 | SYMBOL(Keywords[i].value); 121 | for(i = 0; Functions[i].name; i++) 122 | if(!strcmp(Functions[i].name, Word)) 123 | SYMBOL(FUN); 124 | if(Word[0] == 'F' && Word[1] == 'N' && isupper(Word[2]) && !Word[3]) 125 | SYMBOL(FN); 126 | SYMBOL(ID); 127 | } else if(isdigit(Lex[0])) { 128 | do { 129 | Word[l++] = Lex[0]; 130 | Lex++; 131 | if(l >= sizeof Word - 1) 132 | error("number too long"); 133 | } while(isdigit(Lex[0]) || Lex[0] == '.'); 134 | Word[l] = '\0'; 135 | SYMBOL(NUM); 136 | } else if(Lex[0] == '"') { 137 | Lex++; 138 | do { 139 | if(!Lex[0] || Lex[0] =='\n') 140 | error("unterminated string literal"); 141 | Word[l++] = Lex[0]; 142 | Lex++; 143 | if(l >= sizeof Word - 1) 144 | error("string literal too long"); 145 | } while(Lex[0] != '"'); 146 | Lex++; 147 | Word[l] = '\0'; 148 | SYMBOL(STR); 149 | } else if(!memcmp(Lex, "<=", 2)){ 150 | Lex+=2; 151 | SYMBOL(LTE); 152 | } else if(!memcmp(Lex, ">=", 2)){ 153 | Lex+=2; 154 | SYMBOL(GTE); 155 | } else if(!memcmp(Lex, "<>", 2)){ 156 | Lex+=2; 157 | SYMBOL(NEQ); 158 | } 159 | Sym = *Lex++; 160 | Word[0] = Sym; Word[1] = '\0'; 161 | return; 162 | } 163 | 164 | /* Parser... */ 165 | 166 | Node *stmts(); 167 | Node *stmt(); 168 | Node *exprn(); 169 | Node *atom(); 170 | Node *product(); 171 | Node *power(); 172 | Node *unary(); 173 | 174 | /* 175 | program := lines* 176 | lines := NUM stmts NL 177 | | NL 178 | | FEND 179 | */ 180 | Node *program() { 181 | Node *prog = node_operator(PROG, 0); 182 | while(Sym != FEND) { 183 | if(accept(NL)) { 184 | Line++; 185 | continue; 186 | } 187 | expect(NUM); 188 | BasicLine = atoi(LastWord); 189 | if(BasicLine != atof(LastWord)) error("bad line number %s", LastWord); 190 | Node *ln = node_number(BasicLine); 191 | Node *s = stmts(); 192 | if(s){ 193 | Node *line = node_operator(LINE, 2, ln, s); 194 | node_add_child(prog, line); 195 | } 196 | if(Sym == FEND) break; 197 | expect(NL); 198 | Line++; 199 | } 200 | return prog; 201 | } 202 | 203 | /* 204 | stmts := stmt [':' stmt]+ 205 | */ 206 | Node *stmts() { 207 | Node *n = stmt(); 208 | if(!n) return NULL; 209 | Node *s = node_operator(STMTS, 1, n); 210 | while(accept(':')) { 211 | node_add_child(s, stmt()); 212 | } 213 | return s; 214 | } 215 | 216 | /* 217 | stmt := REM 218 | | DIM ID ['(' NUM (',' NUM ')')* ] 219 | | [LET] ID '=' expr 220 | | GOTO NUM 221 | | GOSUB NUM 222 | | IF expr ('='|'<>'|'>'|'>='|'<'|'<=') expr THEN (NUM | stmts) 223 | | PRINT (expr (','|';'|e))* 224 | | RETURN | END | STOP 225 | | FOR ID '=' expr TO expr (STEP expr) 226 | | NEXT ID 227 | | DATA NUM (',' NUM)* 228 | | READ ID (',' ID)* 229 | | DEF FN[A-Z] '(' ID ')' '=' expr 230 | */ 231 | Node *stmt() { 232 | if(accept(REM)) { 233 | while(Sym != NL && Sym != FEND) 234 | nextsym(); 235 | return NULL; 236 | } else if(accept(LET)) { 237 | expect(ID); 238 | Node *name = node_id(LastWord); 239 | Node *var; 240 | if(accept('(')) { 241 | var = node_operator(ARR, 1, name); 242 | do { 243 | Node *exp = exprn(); 244 | node_add_child(var, exp); 245 | } while(accept(',')); 246 | expect(')'); 247 | } else 248 | var = node_operator(VARIABLE, 1, name); 249 | expect('='); 250 | Node *expr = exprn(); 251 | return node_operator(LET, 2, var, expr); 252 | } else if(accept(ID)) { 253 | Node *var = node_operator(VARIABLE, 1, node_id(LastWord)); 254 | expect('='); 255 | Node *expr = exprn(); 256 | return node_operator(LET, 2, var, expr); 257 | } else if(accept(DIM)) { 258 | Node *p = node_operator(DIM, 0); 259 | do { 260 | expect(ID); 261 | Node *name = node_id(LastWord); 262 | if(accept('(')) { 263 | Node *arr = node_operator(ARR, 1, name); 264 | do { 265 | expect(NUM); 266 | node_add_child(arr, node_number(atoi(LastWord))); 267 | } while(accept(',')); 268 | expect(')'); 269 | node_add_child(p, arr); 270 | } else { 271 | node_add_child(p, name); 272 | } 273 | } while(accept(',')); 274 | return p; 275 | } else if(accept(GOTO) || accept(GOSUB)) { 276 | int op = LastSym; 277 | expect(NUM); 278 | int dest = atoi(LastWord); 279 | if(dest != atof(LastWord)) error("bad destination %s", LastWord); 280 | return node_operator(op, 1, node_number(dest)); 281 | } else if(accept(IF)) { 282 | Node *l, *r; 283 | int rel; 284 | l = exprn(); 285 | if(accept('=') || accept(NEQ) || accept(GTE) || accept(LTE) || accept('>') || accept('<')) { 286 | if(LastSym == '=') 287 | rel = EQ; 288 | else 289 | rel = LastSym; 290 | } else 291 | error("relational expected"); 292 | r = exprn(); 293 | expect(THEN); 294 | if(accept(NUM)) 295 | return node_operator(IF, 2, node_operator(rel, 2, l, r), node_operator(GOTO, 1, node_number(atoi(LastWord)))); 296 | else 297 | return node_operator(IF, 2, node_operator(rel, 2, l, r), stmts()); 298 | } else if(accept(PRINT)) { 299 | Node *p = node_operator(PRINT, 0); 300 | while(Sym != NL && Sym != FEND && Sym != ':') { 301 | if(accept(',') || accept(';')) 302 | p = node_add_child(p, node_operator(LastSym, 0)); 303 | else if(accept(STR)) 304 | p = node_add_child(p, node_string(LastWord)); 305 | else 306 | p = node_add_child(p, exprn()); 307 | } 308 | return p; 309 | } else if(accept(RETURN) || accept(END) || accept(STOP)) { 310 | return node_operator(LastSym, 0); 311 | } else if(accept(FOR)) { 312 | expect(ID); 313 | Node *name = node_operator(VARIABLE, 1, node_id(LastWord)); 314 | expect('='); 315 | Node *from = exprn(); 316 | expect(TO); 317 | Node *to = exprn(); 318 | if(accept(STEP)) { 319 | Node *step = exprn(); 320 | return node_operator(FOR, 4, name, from, to, step); 321 | } 322 | return node_operator(FOR, 3, name, from, to); 323 | } else if(accept(NEXT)) { 324 | expect(ID); 325 | return node_operator(NEXT, 1, node_operator(VARIABLE, 1, node_id(LastWord))); 326 | } else if(accept(DATA)) { 327 | Node *p = node_operator(DATA, 0); 328 | do { 329 | expect(NUM); 330 | node_add_child(p, node_number(atoi(LastWord))); 331 | } while(accept(',')); 332 | return p; 333 | } else if(accept(READ)) { 334 | Node *p = node_operator(READ, 0); 335 | do { 336 | expect(ID); 337 | p = node_add_child(p, node_operator(VARIABLE, 1, node_id(LastWord))); 338 | } while(accept(',')); 339 | return p; 340 | } else if(accept(DEF)) { 341 | expect(FN); 342 | assert(isupper(LastWord[2])); 343 | Node *name = node_id(LastWord); 344 | expect('('); 345 | expect(ID); 346 | Node *param = node_id(LastWord); 347 | expect(')'); 348 | expect('='); 349 | Node *body = exprn(); 350 | return node_operator(DEF, 3, name, param, body); 351 | } else 352 | error("statement expected"); 353 | return NULL; 354 | } 355 | Node *exprn() { 356 | Node *n = product(); 357 | while(accept('+') || accept('-')) { 358 | int op = LastSym; 359 | n = node_operator(op, 2, n, product()); 360 | } 361 | return n; 362 | } 363 | Node *product() { 364 | Node *n = power(); 365 | while(accept('*') || accept('/') || accept('%')) { 366 | int op = LastSym; 367 | n = node_operator(op, 2, n, power()); 368 | } 369 | return n; 370 | } 371 | Node *power() { 372 | Node *n = unary(); 373 | if(accept('^')) 374 | n = node_operator('^', 2, n, power()); 375 | return n; 376 | } 377 | Node *unary() { 378 | if(accept('-') || accept('+')) { 379 | int op = LastSym; 380 | return node_operator(op, 1, atom()); 381 | } 382 | return atom(); 383 | } 384 | Node *atom() { 385 | if(accept('(')) { 386 | Node *n = exprn(); 387 | expect(')'); 388 | return n; 389 | } else if(accept(ID)) { 390 | Node *name = node_id(LastWord); 391 | if(accept('(')) { 392 | Node *arr = node_operator(ARR, 1, name); 393 | do { 394 | Node *exp = exprn(); 395 | node_add_child(arr, exp); 396 | } while(accept(',')); 397 | expect(')'); 398 | return arr; 399 | } else { 400 | return node_operator(VARIABLE, 1, name); 401 | } 402 | } else if(accept(FUN) || accept(FN)) { 403 | Node *name = node_id(LastWord); 404 | expect('('); 405 | Node *arg = exprn(); 406 | expect(')'); 407 | return node_operator(FUN, 2, name, arg); 408 | } else if(accept(NUM)) { 409 | return node_number(atof(LastWord)); 410 | } else if(accept(STR)) 411 | return node_string(LastWord); 412 | error("value expected"); 413 | return NULL; 414 | } 415 | 416 | char *readfile(const char *fname) { 417 | FILE *f; 418 | long len,r; 419 | char *str; 420 | 421 | if(!(f = fopen(fname, "rb"))) 422 | return NULL; 423 | 424 | Filename = fname; 425 | 426 | fseek(f, 0, SEEK_END); 427 | len = ftell(f); 428 | rewind(f); 429 | 430 | if(!(str = malloc(len+2))) 431 | return NULL; 432 | r = fread(str, 1, len, f); 433 | 434 | if(r != len) { 435 | free(str); 436 | return NULL; 437 | } 438 | 439 | fclose(f); 440 | str[len] = '\0'; 441 | return str; 442 | } 443 | 444 | const char *nodename(int type) { 445 | 446 | switch(type) { 447 | case FEND: return "EOF"; 448 | case NL: return "newline"; 449 | case PROG: return ""; 450 | case LINE: return ""; 451 | case ID: return "identifier"; 452 | case NUM: return "number"; 453 | case STR: return "string"; 454 | case ARR: return "array access"; 455 | case GTE: return "'>='"; 456 | case LTE: return "'<='"; 457 | case NEQ: return "'<>'"; 458 | case EQ: return "'=='"; 459 | case FUN: return "call"; 460 | case VARIABLE: return "variable"; 461 | case STMTS: return "stmts"; 462 | } 463 | static char buffer[20]; 464 | int i; 465 | if(type > 0x20 && type < 0xFF) { 466 | snprintf(buffer, sizeof buffer, "'%c'", type); 467 | } else { 468 | for(i = 0; Keywords[i].name; i++) { 469 | if(Keywords[i].value == type) { 470 | return Keywords[i].name; 471 | } 472 | } 473 | for(i = 0; Functions[i].name; i++) { 474 | if(Functions[i].value == type) { 475 | return Functions[i].name; 476 | } 477 | } 478 | snprintf(buffer, sizeof buffer, "{%d}", type); 479 | } 480 | return buffer; 481 | } 482 | 483 | int main(int argc, char *argv[]) { 484 | if(argc < 2) { 485 | fprintf(stderr, "no input file\n"); 486 | return 1; 487 | } 488 | 489 | char *text = readfile(argv[1]); 490 | if(!text) { 491 | fprintf(stderr, "error reading %s\n", argv[1]); 492 | return 1; 493 | } 494 | 495 | init(text); 496 | 497 | Node * prog = program(); 498 | 499 | fputs("/*==========\n", stdout); 500 | 501 | print_tree(prog); 502 | 503 | fputs("==========*/\n\n", stdout); 504 | 505 | compile(prog); 506 | 507 | free_node(prog); 508 | 509 | free(text); 510 | return 0; 511 | } -------------------------------------------------------------------------------- /editor/editor.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #if !defined(_WIN32) 8 | # include 9 | #else 10 | # include "getopt.h" 11 | #endif 12 | 13 | #include "editor.h" 14 | 15 | #include "fenster.h" 16 | 17 | #include "image.h" 18 | 19 | #define CSV_IMPLEMENTATION 20 | #include "csvstrm.h" 21 | 22 | #define STB_TILEMAP_EDITOR_IMPLEMENTATION 23 | void get_spacing(int *spacing_x, int *spacing_y, int *palette_spacing_x, int *palette_spacing_y); 24 | 25 | #define WIDTH 800 26 | #define HEIGHT 560 27 | 28 | #define TILE_WIDTH 16 29 | #define TILE_HEIGHT 16 30 | 31 | #define KEY_UP 17 32 | #define KEY_DOWN 18 33 | #define KEY_RIGHT 19 34 | #define KEY_LEFT 20 35 | 36 | /* I wanted to go with names like MOD_ALT and MOD_SHIFT but 37 | it turns out some of them are already defined in `winuser.h` */ 38 | #define KEYMOD_CTRL 0x1 39 | #define KEYMOD_SHIFT 0x2 40 | #define KEYMOD_ALT 0x4 41 | #define KEYMOD_META 0x8 42 | 43 | int lo_res = 0, vwidth, vheight; 44 | 45 | static uint32_t *screen; 46 | 47 | int zoom = 8, pal_zoom = 8; 48 | 49 | typedef struct { 50 | unsigned int row, col; 51 | } map_tile_t; 52 | 53 | #define MAX_TILES 256 54 | map_tile_t tiles[MAX_TILES]; 55 | int num_tiles; 56 | 57 | static FILE *outfile; 58 | 59 | static struct fenster *F; 60 | 61 | image_t *walls, *icons; 62 | 63 | const char *layer_names[] = {"Floor", 64 | "Walls", 65 | "Decals", 66 | "Upper", 67 | "Entities" 68 | }; 69 | #define NUM_LAYERS (sizeof layer_names/ sizeof layer_names[0]) 70 | 71 | static int layer_index(const char *layer_name) { 72 | for(int i = 0; i < NUM_LAYERS; i++) { 73 | if(!strcasecmp(layer_names[i], layer_name)) { 74 | return i; 75 | } 76 | } 77 | return -1; 78 | } 79 | 80 | void putpix(int x, int y, unsigned int color) { 81 | screen[y * vwidth + x] = color; 82 | } 83 | 84 | void STBTE_DRAW_RECT(int x0, int y0, int x1, int y1, unsigned int color) { 85 | for(int y = y0; y < y1; y++) { 86 | if(y < 0) continue; 87 | if(y >= vheight) break; 88 | for(int x = x0; x < x1; x++) { 89 | if(x < 0) continue; 90 | if(x >= vwidth) break; 91 | screen[y * vwidth + x] = color; 92 | } 93 | } 94 | } 95 | 96 | void STBTE_DRAW_TILE(int x0, int y0, unsigned short id, int highlight, float *data) { 97 | unsigned int color = 0; 98 | int spacing_x, spacing_y, palette_spacing_x, palette_spacing_y; 99 | int w, h; 100 | get_spacing(&spacing_x, &spacing_y, &palette_spacing_x, &palette_spacing_y); 101 | if(data) { 102 | w = spacing_x; 103 | h = spacing_y; 104 | } else { 105 | w = palette_spacing_x; 106 | h = palette_spacing_y; 107 | } 108 | 109 | if(id == 0 || id > num_tiles) { 110 | /* TODO id > num_tiles when pasting. what does it mean? */ 111 | STBTE_DRAW_RECT(x0, y0, x0 + w, y0 + h, !id ? 0x000000 : 0x00FF00); 112 | return; 113 | } 114 | 115 | map_tile_t *tile = &tiles[id - 1]; 116 | 117 | blit_image(walls, x0, y0, w, h, tile->col * 32, tile->row * 32, 32, 32); 118 | 119 | if(highlight == -1) { // STBTE_drawmode_deemphasize = -1, 120 | for(int y = y0; y < y0 + h; y++) 121 | for(int x = x0; x < x0 + w; x++) 122 | if((x + y) & 1) 123 | putpix(x,y, 0xFFFFFF); 124 | 125 | } else if(highlight == 1) { // STBTE_drawmode_emphasize = 1, 126 | //STBTE_DRAW_RECT(x0, y0, x0 + w, y0 + h, 0x555555); 127 | for(int x = x0; x < x0+w; x++) { 128 | putpix(x, y0, 0x00FF00); 129 | putpix(x, y0+h, 0x00FF00); 130 | } 131 | for(int y = y0; y < y0+h; y++) { 132 | putpix(x0, y, 0x00FF00); 133 | putpix(x0+w, y, 0x00FF00); 134 | } 135 | } 136 | 137 | } 138 | 139 | static int stbte_prop_type(int n, short *tiledata, float *params); 140 | static char *stbte_prop_name(int n, short *tiledata, float *params); 141 | static float stbte_prop_min(int n, short *tiledata, float *params); 142 | static float stbte_prop_max(int n, short *tiledata, float *params); 143 | 144 | #define STBTE_PROP_TYPE(n, t, p) stbte_prop_type(n,t,p) 145 | #define STBTE_PROP_NAME(n,t,p) stbte_prop_name(n,t,p) 146 | #define STBTE_PROP_MIN(n,t,p) stbte_prop_min(n,t,p) 147 | #define STBTE_PROP_MAX(n,t,p) stbte_prop_max(n,t,p) 148 | 149 | #include "stb_tilemap_editor.h" 150 | 151 | static int stbte_prop_type(int n, short *tiledata, float *params) { 152 | switch(n) { 153 | case 0: return STBTE_PROP_int; 154 | } 155 | return 0; 156 | } 157 | static char *stbte_prop_name(int n, short *tiledata, float *params) { 158 | switch(n) { 159 | case 0: return "tag"; 160 | } 161 | return ""; 162 | } 163 | 164 | static float stbte_prop_min(int n, short *tiledata, float *params) { 165 | switch(n) { 166 | case 0: return 0; 167 | } 168 | return 0; 169 | } 170 | static float stbte_prop_max(int n, short *tiledata, float *params) { 171 | switch(n) { 172 | case 0: return 10; 173 | } 174 | return 0; 175 | } 176 | 177 | stbte_tilemap *Map; 178 | 179 | void get_spacing(int *spacing_x, int *spacing_y, int *palette_spacing_x, int *palette_spacing_y) 180 | { 181 | if(spacing_x) *spacing_x = Map->spacing_x; 182 | if(spacing_y) *spacing_y = Map->spacing_y; 183 | if(palette_spacing_x) *palette_spacing_x = Map->palette_spacing_x; 184 | if(palette_spacing_y) *palette_spacing_y = Map->palette_spacing_y; 185 | } 186 | 187 | int load_tiles(const char *filename) { 188 | CsvContext csv; 189 | FILE *f = fopen(filename, "r"); 190 | if(!f) { 191 | error("unable to open '%s': %s", filename, strerror(errno)); 192 | return 0; 193 | } 194 | csv_context_file(&csv, f); 195 | 196 | /* Slurp the header row */ 197 | if(!csv_read_record(&csv)) { 198 | error("Empty CSV file '%s'", filename); 199 | return 0; 200 | } 201 | 202 | num_tiles = 0; 203 | while(csv_read_record(&csv)) { 204 | if(csv_count(&csv) < 4) { 205 | error("%s: not enough columns at record %d",filename, csv_records); 206 | return 0; 207 | } 208 | if(num_tiles == MAX_TILES) { 209 | error("%s: too many tiles (max %d)", filename, MAX_TILES); 210 | return 0; 211 | } 212 | map_tile_t *tile = &tiles[num_tiles]; 213 | tile->row = strtoul(csv_field(&csv, 0), NULL, 0); 214 | tile->col = strtoul(csv_field(&csv, 1), NULL, 0); 215 | unsigned int layers = strtoul(csv_field(&csv, 2), NULL, 0); 216 | const char *category = csv_field(&csv, 3); 217 | int i = layer_index(category); 218 | if(i < 0) 219 | category = NULL; 220 | else 221 | category = layer_names[i]; 222 | 223 | stbte_define_tile(Map, num_tiles + 1, layers, category); 224 | num_tiles++; 225 | } 226 | 227 | return 1; 228 | } 229 | 230 | int stbte_init() { 231 | 232 | Map = stbte_create_map(20, 20, NUM_LAYERS, zoom, zoom, MAX_TILES + 1); 233 | 234 | stbte_set_display(0, 0, vwidth, vheight); 235 | 236 | stbte_set_background_tile(Map, 0); 237 | 238 | stbte_set_sidewidths(105, 5); 239 | 240 | stbte_set_spacing(Map, zoom, zoom, pal_zoom, pal_zoom); 241 | 242 | for(int i = 0; i < NUM_LAYERS; i++) 243 | stbte_set_layername(Map, i, layer_names[i]); 244 | 245 | // NOTE TO SELF: There is a STBTE_EMPTY defined as -1... 246 | 247 | // TILE ID 0 is meant for the empty/undefined tile 248 | stbte_define_tile(Map, 0, 0xFF, NULL); 249 | stbte_set_background_tile(Map, 0); 250 | 251 | if(!load_tiles("tiles.csv")) return 0; 252 | 253 | // scroll the map a bit 254 | if(lo_res) { 255 | Map->scroll_x -= 17 * Map->spacing_x; 256 | Map->scroll_y -= 4 * Map->spacing_y; 257 | } else { 258 | Map->scroll_x -= 4 * Map->spacing_x; 259 | Map->scroll_y -= 1 * Map->spacing_y; 260 | } 261 | return 1; 262 | } 263 | 264 | void save_map(const char *filename) { 265 | info("Save map: %s", filename); 266 | } 267 | 268 | void load_map(const char *filename) { 269 | info("Load map: %s", filename); 270 | } 271 | 272 | int stbte_update(double elapsedSeconds, const char *filename) { 273 | stbte_tick(Map, elapsedSeconds); 274 | 275 | /* Ctrl+click needs to substitute for right click */ 276 | int mx = F->x, my = F->y; 277 | if(lo_res) { 278 | mx /= 2; 279 | my /= 2; 280 | } 281 | stbte_mouse_move(Map, mx, my, F->mod & 2, F->mod & 1); 282 | stbte_mouse_button(Map, mx, my, (F->mod & KEYMOD_CTRL) ? 1 : 0, 283 | F->mouse, F->mod & KEYMOD_SHIFT, F->mod & KEYMOD_CTRL); 284 | 285 | if(F->keys['S'] && (F->mod & KEYMOD_CTRL)) { 286 | save_map(filename); 287 | F->keys['S'] = 0; 288 | } 289 | if(F->keys['R'] && (F->mod & KEYMOD_CTRL)) { 290 | load_map(filename); 291 | F->keys['R'] = 0; 292 | } 293 | if(F->keys['N'] && (F->mod & KEYMOD_CTRL)) { 294 | stbte_clear_map(Map); 295 | F->keys['N'] = 0; 296 | } 297 | if(F->keys['Q'] && (F->mod & KEYMOD_CTRL)) { 298 | F->keys['Q'] = 0; 299 | return 0; 300 | } 301 | 302 | struct { 303 | uint8_t key; 304 | int mod; 305 | enum stbte_action action; 306 | } shortcuts[] = { 307 | {'S', 0, STBTE_tool_select}, 308 | {'B', 0, STBTE_tool_brush}, 309 | {'E', 0, STBTE_tool_erase}, 310 | {'R', 0, STBTE_tool_rectangle}, 311 | {'D', 0, STBTE_tool_eyedropper}, 312 | {'L', KEYMOD_CTRL, STBTE_act_toggle_links}, 313 | {'L', 0, STBTE_tool_link}, 314 | {'G', KEYMOD_CTRL, STBTE_act_toggle_grid}, 315 | {'Z', KEYMOD_CTRL | KEYMOD_SHIFT, STBTE_act_redo}, 316 | {'Z', KEYMOD_CTRL, STBTE_act_undo}, 317 | {'X', KEYMOD_CTRL, STBTE_act_cut}, 318 | {'C', KEYMOD_CTRL, STBTE_act_copy}, 319 | {'V', KEYMOD_CTRL, STBTE_act_paste}, 320 | {KEY_UP, 0, STBTE_scroll_up}, 321 | {KEY_DOWN, 0, STBTE_scroll_down}, 322 | {KEY_LEFT, 0, STBTE_scroll_left}, 323 | {KEY_RIGHT, 0, STBTE_scroll_right}, 324 | {0,0,0} 325 | }; 326 | 327 | for(int i = 0; shortcuts[i].key; i++) { 328 | if(F->keys[shortcuts[i].key]) { 329 | if(!shortcuts[i].mod || ((shortcuts[i].mod & F->mod) == shortcuts[i].mod)) { 330 | stbte_action(Map, shortcuts[i].action); 331 | F->keys[shortcuts[i].key] = 0; 332 | break; 333 | } 334 | } 335 | } 336 | 337 | // Use '+' and '-' keys for zooming. 338 | // TODO: Is there a good way to keep the screen centered? 339 | if(F->keys['='] && zoom < 64) { 340 | zoom <<= 1; 341 | stbte_set_spacing(Map, zoom, zoom, pal_zoom, pal_zoom); 342 | F->keys['='] = 0; 343 | } 344 | if(F->keys['-'] && zoom > 8) { 345 | zoom >>= 1; 346 | stbte_set_spacing(Map, zoom, zoom, pal_zoom, pal_zoom); 347 | F->keys['-'] = 0; 348 | } 349 | 350 | stbte_draw(Map); 351 | 352 | return 1; 353 | } 354 | 355 | static uint32_t buf[WIDTH * HEIGHT]; 356 | 357 | static void usage(const char *name, FILE *f) { 358 | fprintf(f, "usage: %s [options] \n", name); 359 | fprintf(f, "where [options] can be:\n"); 360 | fprintf(f, " -h : hi-res mode\n"); 361 | } 362 | 363 | int run(int argc, char *argv[]) { 364 | const char *filename = "temp.map"; 365 | lo_res = 1; 366 | int c; 367 | while ((c = getopt(argc, argv, "h")) != -1) { 368 | switch (c) { 369 | case 'h': lo_res = 0; break; 370 | default: usage(argv[0], stderr); return 1; 371 | } 372 | } 373 | if(optind < argc) { 374 | filename = argv[optind]; 375 | } 376 | 377 | if(lo_res) { 378 | vwidth = WIDTH / 2; 379 | vheight = HEIGHT / 2; 380 | zoom = 8; 381 | pal_zoom = 16; 382 | } else { 383 | vwidth = WIDTH; 384 | vheight = HEIGHT; 385 | zoom = 32; 386 | pal_zoom = 32; 387 | } 388 | 389 | screen = malloc(vwidth * vheight * sizeof *screen); 390 | 391 | struct fenster f = { 392 | .title = "Editor", 393 | .width = WIDTH, .height = HEIGHT, 394 | .buf = buf 395 | }; 396 | 397 | F = &f; 398 | 399 | memset(buf, 0, sizeof buf); 400 | 401 | #if 1 402 | outfile = fopen("editor.log", "w"); 403 | #else 404 | outfile = stdout; 405 | #endif 406 | 407 | if(!stbte_init()) return 1; 408 | 409 | walls = loadpcx("walls.pcx"); 410 | if(!walls) { 411 | error("unable to load walls.pcx"); 412 | return 1; 413 | } 414 | icons = loadpcx("icons.pcx"); 415 | if(!icons) { 416 | error("unable to load icons.pcx"); 417 | return 1; 418 | } 419 | 420 | fenster_open(&f); 421 | 422 | int64_t deltat = 0ll; 423 | while(fenster_loop(&f) == 0) { 424 | int64_t start = fenster_time(); 425 | 426 | if(!stbte_update((double)deltat/1000.0, filename)) break; 427 | 428 | // Escape quits - for now. 429 | if(F->keys[27]) break; 430 | 431 | blit_image(icons, vwidth-16, vheight-16, 16, 16, 0, 0, 8, 8); 432 | 433 | int v = 0; 434 | int cy = 0; 435 | for(int y = 0; y < f.height; y++) { 436 | 437 | int u = 0; 438 | int cx = 0; 439 | for(int x = 0; x < f.width; x++) { 440 | 441 | fenster_pixel(F, x, y) = screen[v * vwidth + u]; 442 | 443 | cx = cx + vwidth; 444 | while(cx >= f.width) { 445 | u = u + 1; 446 | cx = cx - f.width; 447 | } 448 | } 449 | 450 | cy = cy + vheight; 451 | while(cy >= f.height) { 452 | v = v + 1; 453 | cy = cy - f.height; 454 | } 455 | } 456 | 457 | int64_t time = fenster_time() - start; 458 | if(time < 1000 / 30) 459 | fenster_sleep((1000 / 30) - time); 460 | deltat = fenster_time() - start; 461 | } 462 | 463 | fenster_close(&f); 464 | 465 | if(outfile != stdout) 466 | fclose(outfile); 467 | 468 | return 0; 469 | } 470 | 471 | #ifdef _WIN32 472 | /* 473 | Alternative to CommandLineToArgvW(). 474 | I used a compiler where shellapi.h was not available, 475 | so this function breaks it down according to the last set of rules in 476 | http://i1.blogs.msdn.com/b/oldnewthing/archive/2010/09/17/10063629.aspx 477 | */ 478 | static int split_cmd_line(char *cmdl, char *argv[], int max) { 479 | 480 | int argc = 0; 481 | char *p = cmdl, *q = p, *arg = p; 482 | int state = 1; 483 | while(state) { 484 | switch(state) { 485 | case 1: 486 | if(argc == max) return argc; 487 | if(!*p) { 488 | state = 0; 489 | } else if(isspace(*p)) { 490 | *q++ = *p++; 491 | } else if(*p == '\"') { 492 | state = 2; 493 | *q++ = *p++; 494 | arg = q; 495 | } else { 496 | state = 3; 497 | arg = q; 498 | *q++ = *p++; 499 | } 500 | break; 501 | case 2: 502 | if(!*p) { 503 | argv[argc++] = arg; 504 | *q++ = '\0'; 505 | state = 0; 506 | } else if(*p == '\"') { 507 | if(p[1] == '\"') { 508 | state = 2; 509 | *q++ = *p; 510 | p+=2; 511 | } else { 512 | state = 1; 513 | argv[argc++] = arg; 514 | *q++ = '\0'; 515 | p++; 516 | } 517 | } else { 518 | *q++ = *p++; 519 | } 520 | break; 521 | case 3: 522 | if(!*p) { 523 | state = 0; 524 | argv[argc++] = arg; 525 | *q++ = '\0'; 526 | } else if(isspace(*p)) { 527 | state = 1; 528 | argv[argc++] = arg; 529 | *q++ = '\0'; 530 | p++; 531 | } else { 532 | *q++ = *p++; 533 | } 534 | break; 535 | } 536 | } 537 | return argc; 538 | } 539 | int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pCmdLine, int nCmdShow) { 540 | (void)hInstance, (void)hPrevInstance, (void)pCmdLine, (void)nCmdShow; 541 | #define MAX_ARGS 32 542 | char *argv[MAX_ARGS]; 543 | LPTSTR cmdl = _strdup(GetCommandLine()); 544 | int argc = split_cmd_line(cmdl, argv, MAX_ARGS); 545 | 546 | int rv = run(argc, argv); 547 | 548 | free(cmdl); 549 | 550 | return rv; 551 | } 552 | #else 553 | int main(int argc, char *argv[]) { 554 | return run(argc, argv); 555 | } 556 | #endif 557 | 558 | void error(const char *fmt, ...) { 559 | char buffer[128]; 560 | va_list arg; 561 | va_start(arg, fmt); 562 | vsnprintf(buffer, sizeof buffer, fmt, arg); 563 | va_end(arg); 564 | fprintf(outfile, "error: %s\n", buffer); 565 | } 566 | 567 | void info(const char *fmt, ...) { 568 | char buffer[128]; 569 | va_list arg; 570 | va_start(arg, fmt); 571 | vsnprintf(buffer, sizeof buffer, fmt, arg); 572 | va_end(arg); 573 | fprintf(outfile, "info: %s\n", buffer); 574 | } 575 | 576 | 577 | 578 | 579 | -------------------------------------------------------------------------------- /fenster-microui/fenster.h: -------------------------------------------------------------------------------- 1 | #ifndef FENSTER_H 2 | #define FENSTER_H 3 | 4 | #if defined(__APPLE__) 5 | #include 6 | #include 7 | #include 8 | #elif defined(_WIN32) 9 | #include 10 | #else 11 | #define _DEFAULT_SOURCE 1 12 | #include 13 | #include 14 | #include 15 | #include 16 | #endif 17 | 18 | #include 19 | #include 20 | 21 | struct fenster { 22 | const char *title; 23 | const int width; 24 | const int height; 25 | uint32_t *buf; 26 | int keys[256]; /* keys are mostly ASCII, but arrows are 17..20 */ 27 | int mod; /* mod is 4 bits mask, ctrl=1, shift=2, alt=4, meta=8 */ 28 | int x; 29 | int y; 30 | int mouse; 31 | #if defined(__APPLE__) 32 | id wnd; 33 | #elif defined(_WIN32) 34 | HWND hwnd; 35 | #else 36 | Display *dpy; 37 | Window w; 38 | GC gc; 39 | XImage *img; 40 | #endif 41 | }; 42 | 43 | #ifndef FENSTER_API 44 | #define FENSTER_API extern 45 | #endif 46 | FENSTER_API int fenster_open(struct fenster *f); 47 | FENSTER_API int fenster_loop(struct fenster *f); 48 | FENSTER_API void fenster_close(struct fenster *f); 49 | FENSTER_API void fenster_sleep(int64_t ms); 50 | FENSTER_API int64_t fenster_time(void); 51 | #define fenster_pixel(f, x, y) ((f)->buf[((y) * (f)->width) + (x)]) 52 | 53 | #ifndef FENSTER_HEADER 54 | #if defined(__APPLE__) 55 | #define msg(r, o, s) ((r(*)(id, SEL))objc_msgSend)(o, sel_getUid(s)) 56 | #define msg1(r, o, s, A, a) \ 57 | ((r(*)(id, SEL, A))objc_msgSend)(o, sel_getUid(s), a) 58 | #define msg2(r, o, s, A, a, B, b) \ 59 | ((r(*)(id, SEL, A, B))objc_msgSend)(o, sel_getUid(s), a, b) 60 | #define msg3(r, o, s, A, a, B, b, C, c) \ 61 | ((r(*)(id, SEL, A, B, C))objc_msgSend)(o, sel_getUid(s), a, b, c) 62 | #define msg4(r, o, s, A, a, B, b, C, c, D, d) \ 63 | ((r(*)(id, SEL, A, B, C, D))objc_msgSend)(o, sel_getUid(s), a, b, c, d) 64 | 65 | #define cls(x) ((id)objc_getClass(x)) 66 | 67 | extern id const NSDefaultRunLoopMode; 68 | extern id const NSApp; 69 | 70 | static void fenster_draw_rect(id v, SEL s, CGRect r) { 71 | (void)r, (void)s; 72 | struct fenster *f = (struct fenster *)objc_getAssociatedObject(v, "fenster"); 73 | CGContextRef context = 74 | msg(CGContextRef, msg(id, cls("NSGraphicsContext"), "currentContext"), 75 | "graphicsPort"); 76 | CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB(); 77 | CGDataProviderRef provider = CGDataProviderCreateWithData( 78 | NULL, f->buf, f->width * f->height * 4, NULL); 79 | CGImageRef img = 80 | CGImageCreate(f->width, f->height, 8, 32, f->width * 4, space, 81 | kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little, 82 | provider, NULL, false, kCGRenderingIntentDefault); 83 | CGColorSpaceRelease(space); 84 | CGDataProviderRelease(provider); 85 | CGContextDrawImage(context, CGRectMake(0, 0, f->width, f->height), img); 86 | CGImageRelease(img); 87 | } 88 | 89 | static BOOL fenster_should_close(id v, SEL s, id w) { 90 | (void)v, (void)s, (void)w; 91 | msg1(void, NSApp, "terminate:", id, NSApp); 92 | return YES; 93 | } 94 | 95 | FENSTER_API int fenster_open(struct fenster *f) { 96 | msg(id, cls("NSApplication"), "sharedApplication"); 97 | msg1(void, NSApp, "setActivationPolicy:", NSInteger, 0); 98 | f->wnd = msg4(id, msg(id, cls("NSWindow"), "alloc"), 99 | "initWithContentRect:styleMask:backing:defer:", CGRect, 100 | CGRectMake(0, 0, f->width, f->height), NSUInteger, 3, 101 | NSUInteger, 2, BOOL, NO); 102 | Class windelegate = 103 | objc_allocateClassPair((Class)cls("NSObject"), "FensterDelegate", 0); 104 | class_addMethod(windelegate, sel_getUid("windowShouldClose:"), 105 | (IMP)fenster_should_close, "c@:@"); 106 | objc_registerClassPair(windelegate); 107 | msg1(void, f->wnd, "setDelegate:", id, 108 | msg(id, msg(id, (id)windelegate, "alloc"), "init")); 109 | Class c = objc_allocateClassPair((Class)cls("NSView"), "FensterView", 0); 110 | class_addMethod(c, sel_getUid("drawRect:"), (IMP)fenster_draw_rect, "i@:@@"); 111 | objc_registerClassPair(c); 112 | 113 | id v = msg(id, msg(id, (id)c, "alloc"), "init"); 114 | msg1(void, f->wnd, "setContentView:", id, v); 115 | objc_setAssociatedObject(v, "fenster", (id)f, OBJC_ASSOCIATION_ASSIGN); 116 | 117 | id title = msg1(id, cls("NSString"), "stringWithUTF8String:", const char *, 118 | f->title); 119 | msg1(void, f->wnd, "setTitle:", id, title); 120 | msg1(void, f->wnd, "makeKeyAndOrderFront:", id, nil); 121 | msg(void, f->wnd, "center"); 122 | msg1(void, NSApp, "activateIgnoringOtherApps:", BOOL, YES); 123 | return 0; 124 | } 125 | 126 | FENSTER_API void fenster_close(struct fenster *f) { 127 | msg(void, f->wnd, "close"); 128 | } 129 | 130 | // clang-format off 131 | static const uint8_t FENSTER_KEYCODES[128] = {65,83,68,70,72,71,90,88,67,86,0,66,81,87,69,82,89,84,49,50,51,52,54,53,61,57,55,45,56,48,93,79,85,91,73,80,10,76,74,39,75,59,92,44,47,78,77,46,9,32,96,8,0,27,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,26,2,3,127,0,5,0,4,0,20,19,18,17,0}; 132 | // clang-format on 133 | FENSTER_API int fenster_loop(struct fenster *f) { 134 | msg1(void, msg(id, f->wnd, "contentView"), "setNeedsDisplay:", BOOL, YES); 135 | id ev = msg4(id, NSApp, 136 | "nextEventMatchingMask:untilDate:inMode:dequeue:", NSUInteger, 137 | NSUIntegerMax, id, NULL, id, NSDefaultRunLoopMode, BOOL, YES); 138 | if (!ev) 139 | return 0; 140 | NSUInteger evtype = msg(NSUInteger, ev, "type"); 141 | switch (evtype) { 142 | case 1: /* NSEventTypeMouseDown */ 143 | f->mouse |= 1; 144 | break; 145 | case 2: /* NSEventTypeMouseUp*/ 146 | f->mouse &= ~1; 147 | break; 148 | case 5: 149 | case 6: { /* NSEventTypeMouseMoved */ 150 | CGPoint xy = msg(CGPoint, ev, "locationInWindow"); 151 | f->x = (int)xy.x; 152 | f->y = (int)(f->height - xy.y); 153 | return 0; 154 | } 155 | case 10: /*NSEventTypeKeyDown*/ 156 | case 11: /*NSEventTypeKeyUp:*/ { 157 | NSUInteger k = msg(NSUInteger, ev, "keyCode"); 158 | f->keys[k < 127 ? FENSTER_KEYCODES[k] : 0] = evtype == 10; 159 | NSUInteger mod = msg(NSUInteger, ev, "modifierFlags") >> 17; 160 | f->mod = (mod & 0xc) | ((mod & 1) << 1) | ((mod >> 1) & 1); 161 | return 0; 162 | } 163 | } 164 | msg1(void, NSApp, "sendEvent:", id, ev); 165 | return 0; 166 | } 167 | #elif defined(_WIN32) 168 | 169 | /* Make sure the client area fits; center the window in the process */ 170 | static void FitWindow(struct fenster *f) { 171 | RECT rcClient, rwClient; 172 | POINT ptDiff; 173 | HWND hwndParent; 174 | RECT rcParent, rwParent; 175 | POINT ptPos; 176 | 177 | GetClientRect(f->hwnd, &rcClient); 178 | GetWindowRect(f->hwnd, &rwClient); 179 | ptDiff.x = (rwClient.right - rwClient.left) - rcClient.right; 180 | ptDiff.y = (rwClient.bottom - rwClient.top) - rcClient.bottom; 181 | 182 | hwndParent = GetParent(f->hwnd); 183 | if (NULL == hwndParent) 184 | hwndParent = GetDesktopWindow(); 185 | 186 | GetWindowRect(hwndParent, &rwParent); 187 | GetClientRect(hwndParent, &rcParent); 188 | 189 | ptPos.x = rwParent.left + (rcParent.right - f->width) / 2; 190 | ptPos.y = rwParent.top + (rcParent.bottom - f->height) / 2; 191 | 192 | MoveWindow(f->hwnd, ptPos.x, ptPos.y, f->width + ptDiff.x, f->height + ptDiff.y, 0); 193 | } 194 | 195 | // clang-format off 196 | static const uint8_t FENSTER_KEYCODES[] = {0,27,49,50,51,52,53,54,55,56,57,48,45,61,8,9,81,87,69,82,84,89,85,73,79,80,91,93,10,0,65,83,68,70,71,72,74,75,76,59,39,96,0,92,90,88,67,86,66,78,77,44,46,47,0,0,0,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,17,3,0,20,0,19,0,5,18,4,26,127}; 197 | // clang-format on 198 | typedef struct BINFO{ 199 | BITMAPINFOHEADER bmiHeader; 200 | RGBQUAD bmiColors[3]; 201 | }BINFO; 202 | static LRESULT CALLBACK fenster_wndproc(HWND hwnd, UINT msg, WPARAM wParam, 203 | LPARAM lParam) { 204 | struct fenster *f = (struct fenster *)GetWindowLongPtr(hwnd, GWLP_USERDATA); 205 | switch (msg) { 206 | case WM_PAINT: { 207 | PAINTSTRUCT ps; 208 | HDC hdc = BeginPaint(hwnd, &ps); 209 | HDC memdc = CreateCompatibleDC(hdc); 210 | HBITMAP hbmp = CreateCompatibleBitmap(hdc, f->width, f->height); 211 | HBITMAP oldbmp = SelectObject(memdc, hbmp); 212 | BINFO bi = {{sizeof(bi), f->width, -f->height, 1, 32, BI_BITFIELDS}}; 213 | bi.bmiColors[0].rgbRed = 0xff; 214 | bi.bmiColors[1].rgbGreen = 0xff; 215 | bi.bmiColors[2].rgbBlue = 0xff; 216 | SetDIBitsToDevice(memdc, 0, 0, f->width, f->height, 0, 0, 0, f->height, 217 | f->buf, (BITMAPINFO *)&bi, DIB_RGB_COLORS); 218 | BitBlt(hdc, 0, 0, f->width, f->height, memdc, 0, 0, SRCCOPY); 219 | SelectObject(memdc, oldbmp); 220 | DeleteObject(hbmp); 221 | DeleteDC(memdc); 222 | EndPaint(hwnd, &ps); 223 | } break; 224 | case WM_CLOSE: 225 | DestroyWindow(hwnd); 226 | break; 227 | case WM_LBUTTONDOWN: 228 | case WM_LBUTTONUP: 229 | f->mouse = (msg == WM_LBUTTONDOWN); 230 | break; 231 | case WM_MOUSEMOVE: 232 | f->y = HIWORD(lParam), f->x = LOWORD(lParam); 233 | break; 234 | case WM_KEYDOWN: 235 | case WM_KEYUP: { 236 | f->mod = ((GetKeyState(VK_CONTROL) & 0x8000) >> 15) | 237 | ((GetKeyState(VK_SHIFT) & 0x8000) >> 14) | 238 | ((GetKeyState(VK_MENU) & 0x8000) >> 13) | 239 | (((GetKeyState(VK_LWIN) | GetKeyState(VK_RWIN)) & 0x8000) >> 12); 240 | f->keys[FENSTER_KEYCODES[HIWORD(lParam) & 0x1ff]] = !((lParam >> 31) & 1); 241 | } break; 242 | case WM_DESTROY: 243 | PostQuitMessage(0); 244 | break; 245 | default: 246 | return DefWindowProc(hwnd, msg, wParam, lParam); 247 | } 248 | return 0; 249 | } 250 | 251 | FENSTER_API int fenster_open(struct fenster *f) { 252 | HINSTANCE hInstance = GetModuleHandle(NULL); 253 | WNDCLASSEX wc = {0}; 254 | wc.cbSize = sizeof(WNDCLASSEX); 255 | wc.style = CS_VREDRAW | CS_HREDRAW; 256 | wc.lpfnWndProc = fenster_wndproc; 257 | wc.hInstance = hInstance; 258 | wc.lpszClassName = f->title; 259 | wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); 260 | wc.hCursor = LoadCursor(NULL, IDC_ARROW); 261 | RegisterClassEx(&wc); 262 | f->hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, f->title, f->title, 263 | WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, 264 | CW_USEDEFAULT, CW_USEDEFAULT, 265 | f->width, f->height, NULL, NULL, hInstance, NULL); 266 | 267 | if (f->hwnd == NULL) 268 | return -1; 269 | 270 | FitWindow(f); 271 | 272 | SetWindowLongPtr(f->hwnd, GWLP_USERDATA, (LONG_PTR)f); 273 | ShowWindow(f->hwnd, SW_NORMAL); 274 | UpdateWindow(f->hwnd); 275 | return 0; 276 | } 277 | 278 | FENSTER_API void fenster_close(struct fenster *f) { (void)f; } 279 | 280 | FENSTER_API int fenster_loop(struct fenster *f) { 281 | MSG msg; 282 | while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { 283 | if (msg.message == WM_QUIT) 284 | return -1; 285 | TranslateMessage(&msg); 286 | DispatchMessage(&msg); 287 | } 288 | InvalidateRect(f->hwnd, NULL, TRUE); 289 | return 0; 290 | } 291 | #else 292 | // clang-format off 293 | static int FENSTER_KEYCODES[124] = {XK_BackSpace,8,XK_Delete,127,XK_Down,18,XK_End,5,XK_Escape,27,XK_Home,2,XK_Insert,26,XK_Left,20,XK_Page_Down,4,XK_Page_Up,3,XK_Return,10,XK_Right,19,XK_Tab,9,XK_Up,17,XK_apostrophe,39,XK_backslash,92,XK_bracketleft,91,XK_bracketright,93,XK_comma,44,XK_equal,61,XK_grave,96,XK_minus,45,XK_period,46,XK_semicolon,59,XK_slash,47,XK_space,32,XK_a,65,XK_b,66,XK_c,67,XK_d,68,XK_e,69,XK_f,70,XK_g,71,XK_h,72,XK_i,73,XK_j,74,XK_k,75,XK_l,76,XK_m,77,XK_n,78,XK_o,79,XK_p,80,XK_q,81,XK_r,82,XK_s,83,XK_t,84,XK_u,85,XK_v,86,XK_w,87,XK_x,88,XK_y,89,XK_z,90,XK_0,48,XK_1,49,XK_2,50,XK_3,51,XK_4,52,XK_5,53,XK_6,54,XK_7,55,XK_8,56,XK_9,57}; 294 | // clang-format on 295 | FENSTER_API int fenster_open(struct fenster *f) { 296 | f->dpy = XOpenDisplay(NULL); 297 | int screen = DefaultScreen(f->dpy); 298 | f->w = XCreateSimpleWindow(f->dpy, RootWindow(f->dpy, screen), 0, 0, f->width, 299 | f->height, 0, BlackPixel(f->dpy, screen), 300 | WhitePixel(f->dpy, screen)); 301 | f->gc = XCreateGC(f->dpy, f->w, 0, 0); 302 | XSelectInput(f->dpy, f->w, 303 | ExposureMask | KeyPressMask | KeyReleaseMask | ButtonPressMask | 304 | ButtonReleaseMask | PointerMotionMask); 305 | XStoreName(f->dpy, f->w, f->title); 306 | XMapWindow(f->dpy, f->w); 307 | XSync(f->dpy, f->w); 308 | f->img = XCreateImage(f->dpy, DefaultVisual(f->dpy, 0), 24, ZPixmap, 0, 309 | (char *)f->buf, f->width, f->height, 32, 0); 310 | return 0; 311 | } 312 | FENSTER_API void fenster_close(struct fenster *f) { XCloseDisplay(f->dpy); } 313 | FENSTER_API int fenster_loop(struct fenster *f) { 314 | XEvent ev; 315 | XPutImage(f->dpy, f->w, f->gc, f->img, 0, 0, 0, 0, f->width, f->height); 316 | XFlush(f->dpy); 317 | while (XPending(f->dpy)) { 318 | XNextEvent(f->dpy, &ev); 319 | switch (ev.type) { 320 | case ButtonPress: 321 | case ButtonRelease: 322 | f->mouse = (ev.type == ButtonPress); 323 | break; 324 | case MotionNotify: 325 | f->x = ev.xmotion.x, f->y = ev.xmotion.y; 326 | break; 327 | case KeyPress: 328 | case KeyRelease: { 329 | int mask = 0; 330 | int k = XkbKeycodeToKeysym(f->dpy, ev.xkey.keycode, 0, 0); 331 | switch(k) { 332 | case XK_Control_L: 333 | case XK_Control_R: mask = 0x1; break; 334 | case XK_Shift_L: 335 | case XK_Shift_R: mask = 0x2; break; 336 | case XK_Alt_L: 337 | case XK_Alt_R: mask = 0x4; break; 338 | case XK_Super_L: 339 | case XK_Super_R: mask = 0x8; break; 340 | } 341 | if(mask) { 342 | f->mod = (ev.type == KeyPress) ? f->mod | mask : f->mod & ~mask; 343 | } else { 344 | for (unsigned int i = 0; i < 124; i += 2) { 345 | if (FENSTER_KEYCODES[i] == k) { 346 | f->keys[FENSTER_KEYCODES[i + 1]] = (ev.type == KeyPress); 347 | break; 348 | } 349 | } 350 | } 351 | } break; 352 | } 353 | } 354 | return 0; 355 | } 356 | #endif 357 | 358 | #ifdef _WIN32 359 | FENSTER_API void fenster_sleep(int64_t ms) { Sleep(ms); } 360 | FENSTER_API int64_t fenster_time() { 361 | LARGE_INTEGER freq, count; 362 | QueryPerformanceFrequency(&freq); 363 | QueryPerformanceCounter(&count); 364 | return (int64_t)(count.QuadPart * 1000.0 / freq.QuadPart); 365 | } 366 | #else 367 | FENSTER_API void fenster_sleep(int64_t ms) { 368 | struct timespec ts; 369 | ts.tv_sec = ms / 1000; 370 | ts.tv_nsec = (ms % 1000) * 1000000; 371 | nanosleep(&ts, NULL); 372 | } 373 | FENSTER_API int64_t fenster_time(void) { 374 | struct timespec time; 375 | clock_gettime(CLOCK_REALTIME, &time); 376 | return time.tv_sec * 1000 + (time.tv_nsec / 1000000); 377 | } 378 | #endif 379 | 380 | #ifdef __cplusplus 381 | class Fenster { 382 | struct fenster f; 383 | int64_t now; 384 | 385 | public: 386 | Fenster(const int w, const int h, const char *title) 387 | : f{.title = title, .width = w, .height = h} { 388 | this->f.buf = new uint32_t[w * h]; 389 | this->now = fenster_time(); 390 | fenster_open(&this->f); 391 | } 392 | ~Fenster() { 393 | fenster_close(&this->f); 394 | delete[] this->f.buf; 395 | } 396 | bool loop(const int fps) { 397 | int64_t t = fenster_time(); 398 | if (t - this->now < 1000 / fps) { 399 | fenster_sleep(t - now); 400 | } 401 | this->now = t; 402 | return fenster_loop(&this->f) == 0; 403 | } 404 | inline uint32_t &px(const int x, const int y) { 405 | return fenster_pixel(&this->f, x, y); 406 | } 407 | bool key(int c) { return c >= 0 && c < 128 ? this->f.keys[c] : false; } 408 | int x() { return this->f.x; } 409 | int y() { return this->f.y; } 410 | int mouse() { return this->f.mouse; } 411 | int mod() { return this->f.mod; } 412 | }; 413 | #endif /* __cplusplus */ 414 | 415 | #endif /* !FENSTER_HEADER */ 416 | #endif /* FENSTER_H */ 417 | --------------------------------------------------------------------------------