Mirai's Miscellaneous Misadventures
M36 / 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/engines/fbdev.h>
18#include <mimimi/assets.h>
19#include <mimimi/geometry.h>
20#include <mimimi/engines.h>
21#include <mimimi/behaviors.h>
22
23static struct mimimi_size mimimi_fbdev_size = {512, 256};
24static struct termios mimimi_fbdev_tio;
25
26static void mimimi_fbdev_exit(void)
27{
28 ioctl(0, KDSETMODE, KD_TEXT);
29 tcsetattr(0, TCSANOW, &mimimi_fbdev_tio);
30}
31
32struct mimimi_fbdev
33{
34 unsigned char *buffer;
35 int width;
36 int height;
37 int x_offset;
38 int y_offset;
39 int line_length;
40 int bytes_per_pixel;
41};
42
43static void mimimi_fbdev_stamp(void *opaque, int x0, int y0, struct mimimi_image *image)
44{
45 struct mimimi_fbdev *data = opaque;
46
47 static unsigned char rch[] = {0x11, 0x44, 0x77, 0x99, 0xCC, 0xFF};
48 static unsigned char gch[] = {0x11, 0x33, 0x55, 0x77, 0x99, 0xBB, 0xDD, 0xFF};
49 static unsigned char bch[] = {0x22, 0x55, 0x88, 0xBB, 0xEE};
50
51 for (int y = 0 ; y < image->height ; y++)
52 for (int x = 0 ; x < image->width ; x++)
53 {
54 if (y + y0 < 0) continue;
55 if (x + x0 < 0) continue;
56
57 if (y + y0 >= data->height) continue;
58 if (x + x0 >= data->width) continue;
59
60 int offset_x = (x + x0 + data->x_offset) * data->bytes_per_pixel;
61 int offset_y = (y + y0 + data->y_offset) * data->line_length;
62
63 int offset = offset_x + offset_y;
64
65 unsigned char color = image->colors[x + y * image->width];
66 if (color == 0) continue;
67
68 if (data->bytes_per_pixel == 4)
69 data->buffer[offset + 3] = 0xFF;
70
71 if (color < 0x10)
72 {
73 unsigned char ch = color * 0x11;
74 data->buffer[offset + 2] = ch;
75 data->buffer[offset + 1] = ch;
76 data->buffer[offset + 0] = ch;
77 continue;
78 }
79
80 color -= 0x10;
81 unsigned char r, g, b;
82 r = rch[(color / 40) % 6];
83 g = gch[(color / 5) % 8];
84 b = bch[(color / 1) % 5];
85
86 data->buffer[offset + 2] = r;
87 data->buffer[offset + 1] = g;
88 data->buffer[offset + 0] = b;
89 }
90}
91
92void mimimi_fbdev(struct mimimi_chapter *(*start)(struct mimimi_engine *engine))
93{
94 int fd = open("/dev/fb0", O_RDWR);
95 if (fd < 0) return;
96
97 struct fb_fix_screeninfo finfo;
98 if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) < 0) return;
99
100 struct fb_var_screeninfo vinfo;
101 if (ioctl(fd, FBIOGET_VSCREENINFO, &vinfo) < 0) return;
102
103 void *aux = malloc(finfo.smem_len);
104 if (aux == NULL) return;
105
106 unsigned char *buffer = mmap(NULL, finfo.smem_len, PROT_WRITE, MAP_SHARED, fd, 0);
107 if ((intptr_t) buffer == -1) return;
108
109 int bytes_per_pixel = vinfo.bits_per_pixel / 8;
110
111 if (bytes_per_pixel != 4 && bytes_per_pixel != 3) return;
112
113 unsigned int width = mimimi_fbdev_size.width;
114 unsigned int height = mimimi_fbdev_size.height;
115
116 if (vinfo.xres < width)
117 width = vinfo.xres;
118 if (vinfo.yres < height)
119 height = vinfo.yres;
120
121 tcgetattr(0, &mimimi_fbdev_tio);
122 tcflag_t flag = mimimi_fbdev_tio.c_lflag;
123 mimimi_fbdev_tio.c_lflag &= ~ICANON & ~ECHO;
124
125 atexit(mimimi_fbdev_exit);
126
127 ioctl(0, KDSETMODE, KD_GRAPHICS);
128 tcsetattr(0, TCSANOW, &mimimi_fbdev_tio);
129 mimimi_fbdev_tio.c_lflag = flag;
130
131 struct mimimi_fbdev data = {aux, width, height, vinfo.xoffset, vinfo.yoffset, finfo.line_length, bytes_per_pixel};
132
133 struct mimimi_engine engine;
134 engine.data = &data;
135 engine.stamp = &mimimi_fbdev_stamp;
136 engine.size = &mimimi_fbdev_size;
137 engine.allocator = mimimi_malloc;
138 struct mimimi_chapter *chapter = (*start)(&engine);
139 struct mimimi_behavior *behavior = chapter->behavior;
140
141 static char *paths[] =
142 {
143 "/dev/input/event0", "/dev/input/event1", "/dev/input/event2", "/dev/input/event3",
144 "/dev/input/event4", "/dev/input/event5", "/dev/input/event6", "/dev/input/event7",
145 "/dev/input/event8", "/dev/input/event9", "/dev/input/event10", "/dev/input/event11",
146 "/dev/input/event12", "/dev/input/event13", "/dev/input/event14", "/dev/input/event15",
147 "/dev/input/event16", "/dev/input/event17", "/dev/input/event18", "/dev/input/event19",
148 "/dev/input/event20", "/dev/input/event21", "/dev/input/event22", "/dev/input/event23",
149 "/dev/input/event24", "/dev/input/event25", "/dev/input/event26", "/dev/input/event27",
150 "/dev/input/event28", "/dev/input/event29", "/dev/input/event30", "/dev/input/event31",
151 };
152
153 int in_count;
154 int in[32];
155 for (in_count = 0 ; in_count < 32 ; in_count++)
156 {
157 int fd = open(paths[in_count], O_RDONLY);
158 if (fd < 0) break;
159 in[in_count] = fd;
160 }
161
162 struct timespec past;
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;
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;
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 chapter->left = event.value;
221 break;
222 case KEY_RIGHT:
223 chapter->right = event.value;
224 break;
225 case KEY_Q:
226 if (event.value == 0)
227 return;
228 break;
229 }
230 }
231 }
232
233 (*behavior->behave)(behavior->data);
234 memcpy(buffer, aux, finfo.smem_len);
235 }
236}