Mirai's Miscellaneous Misadventures
M18 / engines / fbdev.c
1
2
3
4
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}