Mirai's Miscellaneous Misadventures
M45 / engines / fbdev.c
1
2
3
4#include <unistd.h>
5#include <stdlib.h>
6#include <termios.h>
7#include <time.h>
8#include <fcntl.h>
9#include <stdint.h>
10#include <string.h>
11#include <sys/mman.h>
12#include <sys/ioctl.h>
13#include <linux/fb.h>
14#include <linux/kd.h>
15#include <linux/input.h>
16
17#include <mimimi.h>
18
19struct mimimi_fbdev
20{
21 unsigned char *buffer;
22 int width, height;
23 int x_offset, y_offset;
24 int line_length;
25 int bytes_per_pixel;
26};
27
28static struct termios mimimi_fbdev_tio;
29
30static void mimimi_fbdev_exit(void)
31{
32 ioctl(0, KDSETMODE, KD_TEXT);
33 tcsetattr(0, TCSANOW, &mimimi_fbdev_tio);
34}
35
36static void mimimi_fbdev_stamp(void *data, int x0, int y0, void *texture)
37{
38 static unsigned char rch[] = {0x11, 0x44, 0x77, 0x99, 0xCC, 0xFF};
39 static unsigned char gch[] = {0x11, 0x33, 0x55, 0x77, 0x99, 0xBB, 0xDD, 0xFF};
40 static unsigned char bch[] = {0x22, 0x55, 0x88, 0xBB, 0xEE};
41
42 struct mimimi_image *image;
43 struct mimimi_fbdev *fbdev_data;
44 int x, y;
45 int offset_x, offset_y, offset;
46 unsigned char color;
47 unsigned char r, g, b;
48
49 image = texture;
50 fbdev_data = data;
51
52 for (y = 0 ; y < image->height ; y++)
53 for (x = 0 ; x < image->width ; x++)
54 {
55 if (y + y0 < 0) continue;
56 if (x + x0 < 0) continue;
57
58 if (y + y0 >= fbdev_data->height) continue;
59 if (x + x0 >= fbdev_data->width) continue;
60
61 offset_x = (x + x0 + fbdev_data->x_offset) * fbdev_data->bytes_per_pixel;
62 offset_y = (y + y0 + fbdev_data->y_offset) * fbdev_data->line_length;
63
64 offset = offset_x + offset_y;
65
66 color = image->colors[x + y * image->width];
67 if (color == 0) continue;
68
69 if (fbdev_data->bytes_per_pixel == 4)
70 fbdev_data->buffer[offset + 3] = 0xFF;
71
72 if (color < 0x10)
73 {
74 fbdev_data->buffer[offset + 2] = color * 0x11;
75 fbdev_data->buffer[offset + 1] = color * 0x11;
76 fbdev_data->buffer[offset + 0] = color * 0x11;
77 continue;
78 }
79
80 color -= 0x10;
81 r = rch[(color / 40) % 6];
82 g = gch[(color / 5) % 8];
83 b = bch[(color / 1) % 5];
84
85 fbdev_data->buffer[offset + 2] = r;
86 fbdev_data->buffer[offset + 1] = g;
87 fbdev_data->buffer[offset + 0] = b;
88 }
89}
90
91static void *mimimi_fbdev_texture(void *data, struct mimimi_image *image)
92{
93 (void) data;
94 return image;
95}
96
97static void mimimi_fbdev_invalidate(void *data, void *texture)
98{
99 (void) data;
100 (void) texture;
101}
102
103int mimimi_fbdev(void (*start)(void *chapter, struct mimimi_engine *engine), void (*tick)(void *chapter, unsigned char left, unsigned char right), unsigned long int chapter_size)
104{
105 static char *file_names[] =
106 {
107 "/dev/input/event0", "/dev/input/event1", "/dev/input/event2", "/dev/input/event3",
108 "/dev/input/event4", "/dev/input/event5", "/dev/input/event6", "/dev/input/event7",
109 "/dev/input/event8", "/dev/input/event9", "/dev/input/event10", "/dev/input/event11",
110 "/dev/input/event12", "/dev/input/event13", "/dev/input/event14", "/dev/input/event15",
111 "/dev/input/event16", "/dev/input/event17", "/dev/input/event18", "/dev/input/event19",
112 "/dev/input/event20", "/dev/input/event21", "/dev/input/event22", "/dev/input/event23",
113 "/dev/input/event24", "/dev/input/event25", "/dev/input/event26", "/dev/input/event27",
114 "/dev/input/event28", "/dev/input/event29", "/dev/input/event30", "/dev/input/event31",
115 };
116
117 void *chapter;
118 int fd, in_fd;
119 struct fb_fix_screeninfo finfo;
120 struct fb_var_screeninfo vinfo;
121 void *aux;
122 unsigned char *buffer;
123 int bytes_per_pixel;
124 unsigned int width, height;
125 struct mimimi_engine engine;
126 struct mimimi_fbdev data;
127 int in_count;
128 int in[32];
129 struct timeval past, tv;
130 struct timespec ts;
131 long int delay;
132 unsigned long int usec;
133 int i;
134 int nfds;
135 int available;
136 struct input_event event;
137 ssize_t size, r;
138 unsigned char left, right;
139 fd_set fds;
140 tcflag_t flag;
141
142 chapter = malloc(chapter_size);
143 if (chapter == NULL) return 1;
144
145 fd = open("/dev/fb0", O_RDWR);
146 if (fd < 0) return 1;
147 if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) < 0) return 1;
148 if (ioctl(fd, FBIOGET_VSCREENINFO, &vinfo) < 0) return 1;
149
150 aux = malloc(finfo.smem_len);
151 if (aux == NULL) return 1;
152
153 buffer = mmap(NULL, finfo.smem_len, PROT_WRITE, MAP_SHARED, fd, 0);
154 if ((intptr_t) buffer == -1) return 1;
155
156 bytes_per_pixel = vinfo.bits_per_pixel / 8;
157 if (bytes_per_pixel != 4 && bytes_per_pixel != 3) return 1;
158
159 width = 512;
160 height = 256;
161
162 if (vinfo.xres < width) width = vinfo.xres;
163 if (vinfo.yres < height) height = vinfo.yres;
164
165 tcgetattr(0, &mimimi_fbdev_tio);
166 flag = mimimi_fbdev_tio.c_lflag;
167 mimimi_fbdev_tio.c_lflag &= ~ICANON & ~ECHO;
168
169
170 tcsetattr(0, TCSANOW, &mimimi_fbdev_tio);
171 mimimi_fbdev_tio.c_lflag = flag;
172
173 atexit(&mimimi_fbdev_exit);
174
175 data.buffer = aux;
176 data.width = width;
177 data.height = height;
178 data.x_offset = vinfo.xoffset;
179 data.y_offset = vinfo.yoffset;
180 data.line_length = finfo.line_length;
181 data.bytes_per_pixel = bytes_per_pixel;
182
183 engine.data = &data;
184 engine.stamp = &mimimi_fbdev_stamp;
185 engine.texture = &mimimi_fbdev_texture;
186 engine.invalidate = &mimimi_fbdev_invalidate;
187 engine.size.width = width;
188 engine.size.height = height;
189
190 for (in_count = 0 ; in_count < 32 ; in_count++)
191 {
192 in_fd = open(file_names[in_count], O_RDONLY);
193 if (in_fd < 0) break;
194 in[in_count] = in_fd;
195 }
196
197 left = 0;
198 right = 0;
199
200 past.tv_sec = 0;
201 past.tv_usec = 0;
202
203 (*start)(chapter, &engine);
204
205 for (;;)
206 {
207 clock_gettime(CLOCK_MONOTONIC, &ts);
208 tv.tv_sec = ts.tv_sec;
209 tv.tv_usec = ts.tv_nsec / 1000;
210
211 delay =
212 (tv.tv_sec - past.tv_sec) * 1000000 +
213 (tv.tv_usec - past.tv_usec);
214
215 if (delay < 0) delay = 0;
216
217 past.tv_sec = tv.tv_sec;
218 past.tv_usec = tv.tv_usec;
219
220 usec = 0;
221 if (delay < 33333) usec = 33333 - delay;
222
223 tv.tv_sec = usec / 1000000;
224 tv.tv_usec = usec % 1000000;
225
226 nfds = 0;
227 FD_ZERO(&fds);
228 for (i = 0 ; i < in_count ; i++)
229 {
230 if (nfds < in[i]) nfds = in[i];
231 FD_SET(in[i], &fds);
232 }
233
234 for (;;)
235 {
236 available = select(nfds, &fds, NULL, NULL, &tv);
237 if (available == -1) return 1;
238 if (available == 0) break;
239
240 tv.tv_sec = 0;
241 tv.tv_usec = 0;
242
243 for (i = 0 ; i < in_count ; i++)
244 {
245 in_fd = in[i];
246 if (FD_ISSET(in_fd, &fds) == 0) continue;
247
248 size = sizeof event;
249 r = read(in_fd, &event, size);
250 if (r < size) return 1;
251
252 if (event.type != EV_KEY) continue;
253 if (event.value == 2) continue;
254
255 switch (event.code)
256 {
257 case KEY_LEFT:
258 case KEY_A:
259 left = event.value;
260 break;
261 case KEY_RIGHT:
262 case KEY_D:
263 right = event.value;
264 break;
265 case KEY_Q:
266 if (event.value == 0) return 0;
267 break;
268 }
269 }
270 }
271
272 (*tick)(chapter, left, right);
273 memcpy(buffer, aux, finfo.smem_len);
274 }
275}