Mirai's Miscellaneous Misadventures

M18 / engines / fbdev.c

1// copyright 2022 zamfofex
2// license: AGPLv3 or later
3
4// todo: this is currently not working, fix it
5
6#include <unistd.h>
7#include <stdlib.h>
8#include <termios.h>
9#include <time.h>
10#include <fcntl.h>
11#include <stdint.h>
12#include <string.h>
13#include <sys/mman.h>
14#include <sys/ioctl.h>
15#include <linux/fb.h>
16#include <linux/kd.h>
17#include <linux/input.h>
18
19#include "../mimimi.h"
20
21static struct termios mimimi_fbdev_tio;
22
23static void mimimi_fbdev_exit(void)
24{
25	ioctl(0, KDSETMODE, KD_TEXT);
26	tcsetattr(0, TCSANOW, &mimimi_fbdev_tio);
27}
28
29struct mimimi_fbdev
30{
31	unsigned char *buffer;
32	int width;
33	int height;
34	int x_offset;
35	int y_offset;
36	int line_length;
37};
38
39static void mimimi_fbdev_blank(struct mimimi_fbdev *data)
40{
41	for (int y = 0 ; y < data->height ; y++)
42	{
43		for (int x = 0 ; x < data->width ; x++)
44		{
45			int offset_x = (x + data->x_offset) * 4;
46			int offset_y = (y + data->y_offset) * data->line_length;
47			
48			int offset = offset_x + offset_y;
49			
50			data->buffer[offset + 0] = 0xEE;
51			data->buffer[offset + 1] = 0xEE;
52			data->buffer[offset + 2] = 0xEE;
53			data->buffer[offset + 3] = 0xFF;
54		}
55	}
56}
57
58static void mimimi_fbdev_stamp(void *opaque, int x0, int y0, struct mimimi_image *image)
59{
60	struct mimimi_fbdev *data = opaque;
61	
62	static unsigned char xterm[] = {0x00, 0x5F, 0x87, 0xAF, 0xD7, 0xFF};
63	
64	for (int y = 0 ; y < image->height ; y++)
65	for (int x = 0 ; x < image->width ; x++)
66	{
67		if (y + y0 < 0) continue;
68		if (x + x0 < 0) continue;
69		
70		if (y + y0 >= data->height) continue;
71		if (x + x0 >= data->width) continue;
72		
73		int offset_x = (x + x0 + data->x_offset) * 4;
74		int offset_y = (y + y0 + data->y_offset) * data->line_length;
75		
76		int offset = offset_x + offset_y;
77		
78		unsigned char color = image->colors[x + y * image->width];
79		if (color == 0) continue;
80		
81		color -= 16;
82		unsigned char r, g, b;
83		r = xterm[(color / 36) % 6];
84		g = xterm[(color / 6) % 6];
85		b = xterm[color % 6];
86		
87		data->buffer[offset + 0] = b;
88		data->buffer[offset + 1] = g;
89		data->buffer[offset + 2] = r;
90		data->buffer[offset + 3] = 0xFF;
91	}
92}
93
94int main(void)
95{
96	int fd = open("/dev/fb0", O_RDWR);
97	if (fd < 0) return -1;
98	
99	struct fb_fix_screeninfo finfo;
100	if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) < 0) return -1;
101	
102	struct fb_var_screeninfo vinfo;
103	if (ioctl(fd, FBIOGET_VSCREENINFO, &vinfo) < 0) return -1;
104	
105	void *aux = malloc(finfo.smem_len);
106	if (aux == NULL) return -1;
107	
108	unsigned char *buffer = mmap(NULL, finfo.smem_len, PROT_WRITE, MAP_SHARED, fd, 0);
109	if ((intptr_t) buffer == -1) return -1;
110	
111	int bytes_per_pixel = vinfo.bits_per_pixel / 8;
112	
113	if (bytes_per_pixel != 4 && bytes_per_pixel != 3) return -1;
114	
115	unsigned int width = mimimi_width;
116	unsigned int height = mimimi_height;
117	
118	if (vinfo.xres < width)
119		width = vinfo.xres;
120	if (vinfo.yres < height)
121		height = vinfo.yres;
122	
123	tcgetattr(0, &mimimi_fbdev_tio);
124	tcflag_t flag = mimimi_fbdev_tio.c_lflag;
125	mimimi_fbdev_tio.c_lflag &= ~ICANON & ~ECHO;
126	
127	atexit(mimimi_fbdev_exit);
128	
129	ioctl(0, KDSETMODE, KD_GRAPHICS);
130	tcsetattr(0, TCSANOW, &mimimi_fbdev_tio);
131	mimimi_fbdev_tio.c_lflag = flag;
132	
133	struct mimimi_fbdev data = {aux, width, height, vinfo.xoffset, vinfo.yoffset, finfo.line_length};
134	
135	struct mimimi_engine engine = {&data, &mimimi_fbdev_stamp};
136	struct mimimi_game *game = malloc(mimimi_game_size);
137	mimimi_start(game, &engine, mimimi_test);
138	
139	static char *paths[] =
140	{
141		"/dev/input/event0", "/dev/input/event1", "/dev/input/event2", "/dev/input/event3",
142		"/dev/input/event4", "/dev/input/event5", "/dev/input/event6", "/dev/input/event7",
143		"/dev/input/event8", "/dev/input/event9", "/dev/input/event10", "/dev/input/event11",
144		"/dev/input/event12", "/dev/input/event13", "/dev/input/event14", "/dev/input/event15",
145		"/dev/input/event16", "/dev/input/event17", "/dev/input/event18", "/dev/input/event19",
146		"/dev/input/event20", "/dev/input/event21", "/dev/input/event22", "/dev/input/event23",
147		"/dev/input/event24", "/dev/input/event25", "/dev/input/event26", "/dev/input/event27",
148		"/dev/input/event28", "/dev/input/event29", "/dev/input/event30", "/dev/input/event31",
149	};
150	
151	int in_count;
152	int in[32];
153	for (in_count = 0 ; in_count < 32 ; in_count++)
154	{
155		int fd = open(paths[in_count], O_RDONLY);
156		if (fd < 0) break;
157		in[in_count] = fd;
158	}
159	
160	struct timespec past;
161	
162	struct mimimi_keys keys = {0, 0};
163	
164	for (;;)
165	{
166		struct timespec tv;
167		clock_gettime(CLOCK_MONOTONIC, &tv);
168		
169		long int delay =
170			(tv.tv_sec - past.tv_sec) * 1000000000 +
171			(tv.tv_nsec - past.tv_nsec);
172		
173		if (delay < 0) delay = 0;
174		
175		past.tv_sec = tv.tv_sec;
176		past.tv_nsec = tv.tv_nsec;
177		
178		unsigned long int nsec;
179		if (delay < 33333333) nsec = 33333333 - delay;
180		else nsec = 0;
181		
182		tv.tv_sec = nsec / 1000000000;
183		tv.tv_nsec = nsec % 1000000000;
184		
185		int nfds = 0;
186		fd_set fds;
187		FD_ZERO(&fds);
188		for (int i = 0 ; i < in_count ; i++)
189		{
190			if (nfds < in[i]) nfds = in[i];
191			FD_SET(in[i], &fds);
192		}
193		
194		for (;;)
195		{
196			int available = pselect(nfds, &fds, NULL, NULL, &tv, NULL);
197			if (available == -1) return -1;
198			
199			if (available == 0) break;
200			
201			tv.tv_sec = 0;
202			tv.tv_nsec = 0;
203			
204			for (int i = 0 ; i < in_count ; i++)
205			{
206				int fd = in[i];
207				if (FD_ISSET(fd, &fds) == 0) continue;
208				
209				int size = sizeof (struct input_event);
210				struct input_event event;
211				ssize_t r = read(fd, &event, size);
212				if (r < size) return -1;
213				
214				if (event.type != EV_KEY) continue;
215				if (event.value == 2) continue;
216				
217				switch (event.code)
218				{
219					case KEY_LEFT:
220						keys.left = event.value;
221						break;
222					case KEY_RIGHT:
223						keys.right = event.value;
224						break;
225					case KEY_Q:
226						if (event.value == 0)
227							return 0;
228						break;
229				}
230			}
231		}
232		
233		mimimi_fbdev_blank(&data);
234		mimimi_step(game, keys);
235		memcpy(buffer, aux, finfo.smem_len);
236	}
237}