Notcurses 3.0.13
a blingful library for TUIs and character graphics
Loading...
Searching...
No Matches
linux.c
Go to the documentation of this file.
1#include "linux.h"
2#include "internal.h"
3
4// auxvecs for framebuffer are 1B each for cellpxx * cellpxy elements,
5// and store the original alpha value.
6static inline uint8_t*
7fbcon_auxiliary_vector(const sprixel* s){
8 int pixels = ncplane_pile(s->n)->cellpxy * ncplane_pile(s->n)->cellpxx;
9 uint8_t* ret = malloc(sizeof(*ret) * pixels);
10 if(ret){
11 memset(ret, 0, sizeof(*ret) * pixels);
12 }
13 return ret;
14}
15
16int fbcon_wipe(sprixel* s, int ycell, int xcell){
17 uint8_t* auxvec = fbcon_auxiliary_vector(s);
18 if(auxvec == NULL){
19 return -1;
20 }
21 const int cellpxy = ncplane_pile(s->n)->cellpxy;
22 const int cellpxx = ncplane_pile(s->n)->cellpxx;
23 char* glyph = s->glyph.buf;
24 for(int y = 0 ; y < cellpxy ; ++y){
25 if(ycell * cellpxy + y >= s->pixy){
26 break;
27 }
28 // number of pixels total above our pixel row
29 const size_t yoff = s->pixx * (ycell * cellpxy + y);
30 for(int x = 0 ; x < cellpxx ; ++x){
31 if(xcell * cellpxx + x >= s->pixx){
32 break;
33 }
34 size_t offset = (yoff + xcell * cellpxx + x) * 4;
35 const int vyx = (y % cellpxy) * cellpxx + x;
36 auxvec[vyx] = glyph[offset + 3];
37 glyph[offset + 3] = 0;
38 }
39 }
40 s->n->tam[s->dimx * ycell + xcell].auxvector = auxvec;
41 return 0;
42}
43
44int fbcon_blit(struct ncplane* n, int linesize, const void* data,
45 int leny, int lenx, const struct blitterargs* bargs){
46 uint32_t transcolor = bargs->transcolor;
47 sprixel* s = bargs->u.pixel.spx;
48 int cdimx = bargs->u.pixel.cellpxx;
49 int cdimy = bargs->u.pixel.cellpxy;
50 // FIXME this will need be a copy of the tinfo's fbuf map
51 size_t flen = leny * lenx * 4;
52 if(fbuf_reserve(&s->glyph, flen)){
53 return -1;
54 }
55 for(int l = 0 ; l < leny ; ++l){
56 int ycell = l / cdimy;
57 size_t soffset = l * linesize;
58 const uint8_t* src = (const unsigned char*)data + soffset;
59 size_t toffset = l * lenx * 4;
60 char* dst = (char *)s->glyph.buf + toffset;
61 for(int c = 0 ; c < lenx ; ++c){
62 int xcell = c / cdimx;
63 int tyx = xcell + ycell * bargs->u.pixel.spx->dimx;
64 if(n->tam[tyx].state >= SPRIXCELL_ANNIHILATED){
65 if(rgba_trans_p(*(uint32_t*)src, transcolor)){
66 ncpixel_set_a((uint32_t*)src, 0); // in case it was transcolor
67 if(c % cdimx == 0 && l % cdimy == 0){
68 n->tam[tyx].state = SPRIXCELL_ANNIHILATED_TRANS;
69 }
70 }else{
71 n->tam[tyx].state = SPRIXCELL_ANNIHILATED;
72 }
73 dst[3] = 0;
74 const int vyx = (l % cdimy) * cdimx + (c % cdimx);
75 ((uint8_t*)n->tam[tyx].auxvector)[vyx] = src[3];
76 }else{
77 if(rgba_trans_p(*(uint32_t*)src, transcolor)){
78 ncpixel_set_a((uint32_t*)src, 0); // in case it was transcolor
79 if(c % cdimx == 0 && l % cdimy == 0){
80 n->tam[tyx].state = SPRIXCELL_TRANSPARENT;
81 }else if(n->tam[tyx].state == SPRIXCELL_OPAQUE_SIXEL){
82 n->tam[tyx].state = SPRIXCELL_MIXED_SIXEL;
83 }
84 dst[3] = 0;
85 }else{
86 if(c % cdimx == 0 && l % cdimy == 0){
87 n->tam[tyx].state = SPRIXCELL_OPAQUE_SIXEL;
88 }else if(n->tam[tyx].state == SPRIXCELL_TRANSPARENT){
89 n->tam[tyx].state = SPRIXCELL_MIXED_SIXEL;
90 }
91 memcpy(dst + 3, src + 3, 1);
92 }
93 }
94 memcpy(dst, src + 2, 1);
95 memcpy(dst + 1, src + 1, 1);
96 memcpy(dst + 2, src, 1);
97 dst += 4;
98 src += 4;
99 }
100 }
101 scrub_tam_boundaries(n->tam, leny, lenx, cdimy, cdimx);
102 if(plane_blit_sixel(s, &s->glyph, leny, lenx, 0, n->tam, SPRIXEL_INVALIDATED) < 0){
103 goto error;
104 }
105 return 1;
106
107error:
108 fbuf_free(&s->glyph);
109 s->glyph.size = 0;
110 return -1;
111}
112
113int fbcon_scrub(const struct ncpile* p, sprixel* s){
114 return sixel_scrub(p, s);
115}
116
117#ifdef __linux__
118#include <sys/types.h>
119#include <sys/stat.h>
120#include <fcntl.h>
121#include <linux/fb.h>
122#include <linux/kd.h>
123#include <sys/ioctl.h>
124
125int fbcon_rebuild(sprixel* s, int ycell, int xcell, uint8_t* auxvec){
126 if(auxvec == NULL){
127 return -1;
128 }
129 const int cellpxy = ncplane_pile(s->n)->cellpxy;
130 const int cellpxx = ncplane_pile(s->n)->cellpxx;
132 for(int y = 0 ; y < cellpxy ; ++y){
133 if(ycell * cellpxy + y >= s->pixy){
134 break;
135 }
136 const size_t yoff = s->pixx * (ycell * cellpxy + y);
137 for(int x = 0 ; x < cellpxx ; ++x){
138 if(xcell * cellpxx + x >= s->pixx){
139 break;
140 }
141 size_t offset = (yoff + xcell * cellpxx + x) * 4;
142 const int vyx = (y % cellpxy) * cellpxx + x;
143 if(x == 0 && y == 0){
144 if(auxvec[vyx] == 0){
145 state = SPRIXCELL_TRANSPARENT;
146 }else{
148 }
149 }else{
150 if(auxvec[vyx] == 0 && state == SPRIXCELL_OPAQUE_SIXEL){
151 state = SPRIXCELL_MIXED_SIXEL;
152 }else if(auxvec[vyx] && state == SPRIXCELL_TRANSPARENT){
153 state = SPRIXCELL_MIXED_SIXEL;
154 }
155 }
156 s->glyph.buf[offset + 3] = auxvec[vyx];
157 }
158 }
159 s->n->tam[s->dimx * ycell + xcell].state = state;
161 return 1;
162}
163
164int fbcon_draw(const tinfo* ti, sprixel* s, int y, int x){
165 logdebug("id %" PRIu32 " dest %d/%d", s->id, y, x);
166 int wrote = 0;
167 const int cellpxy = ncplane_pile(s->n) ? ncplane_pile(s->n)->cellpxy : ti->cellpxy;
168 const int cellpxx = ncplane_pile(s->n) ? ncplane_pile(s->n)->cellpxx : ti->cellpxx;
169 for(unsigned l = 0 ; l < (unsigned)s->pixy && l + y * cellpxy < ti->pixy ; ++l){
170 // FIXME pixel size isn't necessarily 4B, line isn't necessarily psize*pixx
171 size_t offset = ((l + y * cellpxy) * ti->pixx + x * cellpxx) * 4;
172 uint8_t* tl = ti->linux_fbuffer + offset;
173 const char* src = (char*)s->glyph.buf + (l * s->pixx * 4);
174 for(unsigned c = 0 ; c < (unsigned)s->pixx && c < ti->pixx ; ++c){
175 uint32_t pixel;
176 memcpy(&pixel, src, 4);
177 if(!rgba_trans_p(pixel, 0)){
178 memcpy(tl, &pixel, 4);
179 wrote += 4;
180 }
181 src += 4;
182 tl += 4;
183 }
184 }
185 return wrote;
186}
187
188// we have some number of (cell) rows we want to scroll. scale by cell height,
189// and cap at the total pixel height (P) for N. that means we're *losing* N
190// rows from the top. we're *moving* all remaining P-N rows to the top, and
191// we're *clearing* N rows at the bottom. every pixel is written to once, and
192// they're written in order. if we're scrolling all rows, we're clearing the
193// entire space; we always clear something (we might not always move anything).
194void fbcon_scroll(const struct ncpile* p, tinfo* ti, int rows){
195 const int cellpxy = p->cellpxy;
196 const int cellpxx = p->cellpxx;
197 if(cellpxy < 1){
198 return;
199 }
200 logdebug("scrolling %d", rows);
201 const int rowbytes = cellpxx * p->dimx * 4;
202 const int totalrows = cellpxy * p->dimy;
203 int srows = rows * cellpxy; // number of pixel rows being scrolled
204 if(srows > totalrows){
205 srows = totalrows;
206 }
207 // srows is the number of rows we're *losing*
208 uint8_t* targ = ti->linux_fbuffer;
209 uint8_t* src = ti->linux_fbuffer + srows * rowbytes;
210 unsigned tocopy = rowbytes * (totalrows - srows);
211 if(tocopy){
212 memmove(targ, src, tocopy);
213 }
214 targ += tocopy;
215 memset(targ, 0, (totalrows * rowbytes) - tocopy);
216}
217
218// each row is a contiguous set of bits, starting at the msb
219static inline size_t
220row_bytes(const struct console_font_op* cfo){
221 return (cfo->width + 7) / 8;
222}
223
224static inline size_t
225glyph_bytes(const struct console_font_op* cfo){
226 size_t minb = row_bytes(cfo) * cfo->height;
227 return (minb + 31) / 32 * 32;
228}
229
230static unsigned char*
231get_glyph(struct console_font_op* cfo, unsigned idx){
232 if(idx >= cfo->charcount){
233 return NULL;
234 }
235 return (unsigned char*)cfo->data + glyph_bytes(cfo) * idx;
236}
237
238// idx is the glyph index within cfo->data. qbits are the occupied quadrants:
239// 0x8 = upper left
240// 0x4 = upper right
241// 0x2 = lower left
242// 0x1 = lower right
243static int
244shim_quad_block(struct console_font_op* cfo, unsigned idx, unsigned qbits){
245 unsigned char* glyph = get_glyph(cfo, idx);
246 if(glyph == NULL){
247 return -1;
248 }
249 unsigned r;
250 for(r = 0 ; r < cfo->height / 2 ; ++r){
251 unsigned char mask = 0x80;
252 unsigned char* row = glyph + row_bytes(cfo) * r;
253 unsigned x;
254 *row = 0;
255 for(x = 0 ; x < cfo->width / 2 ; ++x){
256 if(qbits & 0x8){
257 *row |= mask;
258 }
259 if((mask >>= 1) == 0){
260 mask = 0x80;
261 *++row = 0;
262 }
263 }
264 while(x < cfo->width){
265 if(qbits & 0x4){
266 *row |= mask;
267 }
268 if((mask >>= 1) == 0){
269 mask = 0x80;
270 *++row = 0;
271 }
272 ++x;
273 }
274 }
275 while(r < cfo->height){
276 unsigned char mask = 0x80;
277 unsigned char* row = glyph + row_bytes(cfo) * r;
278 unsigned x;
279 *row = 0;
280 for(x = 0 ; x < cfo->width / 2 ; ++x){
281 if(qbits & 0x2){
282 *row |= mask;
283 }
284 if((mask >>= 1) == 0){
285 mask = 0x80;
286 *++row = 0;
287 }
288 }
289 while(x < cfo->width){
290 if(qbits & 0x1){
291 *row |= mask;
292 }
293 if((mask >>= 1) == 0){
294 mask = 0x80;
295 *++row = 0;
296 }
297 ++x;
298 }
299 ++r;
300 }
301 return 0;
302}
303
304// use for drawing 1, 2, 3, 5, 6, and 7/8ths
305static int
306shim_lower_eighths(struct console_font_op* cfo, unsigned idx, int eighths){
307 unsigned char* glyph = get_glyph(cfo, idx);
308 if(glyph == NULL){
309 return -1;
310 }
311 unsigned ten8ths = cfo->height * 10 / 8;
312 unsigned start = cfo->height - (eighths * ten8ths / 10);
313 unsigned r;
314 for(r = 0 ; r < start ; ++r){
315 unsigned char* row = glyph + row_bytes(cfo) * r;
316 for(unsigned x = 0 ; x < cfo->width ; x += 8){
317 row[x / 8] = 0;
318 }
319 }
320 while(r < cfo->height){
321 unsigned char* row = glyph + row_bytes(cfo) * r;
322 for(unsigned x = 0 ; x < cfo->width ; x += 8){
323 row[x / 8] = 0xff;
324 }
325 ++r;
326 }
327 return 0;
328}
329
330// add UCS2 codepoint |w| to |map| for font idx |fidx|
331static int
332add_to_map(struct unimapdesc* map, wchar_t w, unsigned fidx){
333 logdebug("adding mapping U+%04x -> %03u", w, fidx);
334 struct unipair* tmp = realloc(map->entries, sizeof(*map->entries) * (map->entry_ct + 1));
335 if(tmp == NULL){
336 return -1;
337 }
338 map->entries = tmp;
339 map->entries[map->entry_ct].unicode = w;
340 map->entries[map->entry_ct].fontpos = fidx;
341 ++map->entry_ct;
342 return 0;
343}
344
345static int
346program_line_drawing_chars(int fd, struct unimapdesc* map){
347 struct simset {
348 wchar_t* ws;
349 } sets[] = {
350 {
351 .ws = L"/╱",
352 }, {
353 .ws = L"\\╲",
354 }, {
355 .ws = L"X╳☒",
356 }, {
357 .ws = L"O☐",
358 }, {
359 .ws = L"└┕┖┗╘╙╚╰",
360 }, {
361 .ws = L"┘┙┚┛╛╜╝╯",
362 }, {
363 .ws = L"┌┍┎┏╒╓╔╭",
364 }, {
365 .ws = L"┐┑┒┓╕╖╗╮",
366 }, {
367 .ws = L"─━┄┅┈┉╌╍═╼╾",
368 }, {
369 .ws = L"│┃┆┇┊┋╎╏║╽╿",
370 }, {
371 .ws = L"├┝┞┟┠┡┢┣╞╟╠",
372 }, {
373 .ws = L"┤┥┦┧┨┩┪┫╡╢╣",
374 }, {
375 .ws = L"┬┭┮┯┰┱┲┳╤╥╦",
376 }, {
377 .ws = L"┴┵┶┷┸┹┺┻╧╨╩",
378 }, {
379 .ws = L"┼┽┾┿╀╁╂╃╄╅╆╇╈╉╊╋╪╫╬",
380 },
381 };
382 int toadd = 0;
383 for(size_t sidx = 0 ; sidx < sizeof(sets) / sizeof(*sets) ; ++sidx){
384 int fontidx = -1;
385 struct simset* s = &sets[sidx];
386 size_t fsize = sizeof(bool) * wcslen(s->ws);
387 bool* found = malloc(fsize);
388 memset(found, 0, fsize);
389 for(unsigned idx = 0 ; idx < map->entry_ct ; ++idx){
390 for(size_t widx = 0 ; widx < wcslen(s->ws) ; ++widx){
391 if(map->entries[idx].unicode == s->ws[widx]){
392 logtrace("found desired character U+%04x -> %03u",
393 map->entries[idx].unicode, map->entries[idx].fontpos);
394 found[widx] = true;
395 if(fontidx == -1){
396 fontidx = map->entries[idx].fontpos;
397 }
398 }
399 }
400 }
401 if(fontidx > -1){
402 for(size_t widx = 0 ; widx < wcslen(s->ws) ; ++widx){
403 if(!found[widx]){
404 if(add_to_map(map, s->ws[widx], fontidx)){
405 free(found);
406 return -1;
407 }
408 ++toadd;
409 }
410 }
411 }else{
412 logwarn("couldn't find any glyphs for set %zu", sidx);
413 }
414 free(found);
415 }
416 if(toadd == 0){
417 return 0;
418 }
419 if(ioctl(fd, PIO_UNIMAP, map)){
420 logwarn("error setting kernel unicode map (%s)", strerror(errno));
421 return -1;
422 }
423 loginfo("added %d kernel unicode mapping%s",
424 toadd, toadd == 1 ? "" : "s");
425 return 0;
426}
427
428// we have to keep a copy of the linux framebuffer while we reprogram fonts
429struct framebuffer_copy {
430 void* map;
431 size_t maplen;
432 unsigned pixely, pixelx;
433};
434
435// build |fbdup| from the framebuffer owned by ti, which will be closed. this
436// is a necessary step prior to reprogramming the console font.
437static int
438copy_and_close_linux_fb(tinfo* ti, struct framebuffer_copy* fbdup){
439 if((fbdup->map = memdup(ti->linux_fbuffer, ti->linux_fb_len)) == NULL){
440 return -1;
441 }
442 munmap(ti->linux_fbuffer, ti->linux_fb_len);
443 fbdup->maplen = ti->linux_fb_len;
444 ti->linux_fbuffer = NULL;
445 ti->linux_fb_len = 0;
446 fbdup->pixely = ti->pixy;
447 fbdup->pixelx = ti->pixx;
448 return 0;
449}
450
451static void
452kill_fbcopy(struct framebuffer_copy* fbdup){
453 free(fbdup->map); // just a memdup(), not an actual mmap
454}
455
456static int
457program_block_drawing_chars(tinfo* ti, int fd, struct console_font_op* cfo,
458 struct unimapdesc* map, unsigned no_font_changes,
459 bool* halfblocks, bool* quadrants){
460 struct shimmer {
461 unsigned qbits;
462 wchar_t w;
463 bool found;
464 };
465 struct shimmer half[] = {
466 { .qbits = 0xc, .w = L'▀', .found = false, },
467 { .qbits = 0x3, .w = L'▄', .found = false, },
468 };
469 struct shimmer quads[] = {
470 // if we get these first two, we have the halfblocks
471 { .qbits = 0xa, .w = L'▌', .found = false, },
472 { .qbits = 0x5, .w = L'▐', .found = false, },
473 { .qbits = 0x8, .w = L'▘', .found = false, },
474 { .qbits = 0x4, .w = L'▝', .found = false, },
475 { .qbits = 0x2, .w = L'▖', .found = false, },
476 { .qbits = 0x1, .w = L'▗', .found = false, },
477 { .qbits = 0x7, .w = L'▟', .found = false, },
478 { .qbits = 0xb, .w = L'▙', .found = false, },
479 { .qbits = 0xd, .w = L'▜', .found = false, },
480 { .qbits = 0xe, .w = L'▛', .found = false, },
481 { .qbits = 0x9, .w = L'▚', .found = false, },
482 { .qbits = 0x6, .w = L'▞', .found = false, },
483 };
484 struct shimmer eighths[] = {
485 { .qbits = 7, .w = L'▇', .found = false, },
486 { .qbits = 6, .w = L'▆', .found = false, },
487 { .qbits = 5, .w = L'▅', .found = false, },
488 { .qbits = 3, .w = L'▃', .found = false, },
489 { .qbits = 2, .w = L'▂', .found = false, },
490 { .qbits = 1, .w = L'▁', .found = false, },
491 };
492 // first, take a pass to see which glyphs we already have
493 size_t numfound = 0;
494 size_t halvesfound = 0;
495 for(unsigned i = 0 ; i < cfo->charcount ; ++i){
496 if(map->entries[i].unicode >= 0x2580 && map->entries[i].unicode <= 0x259f){
497 for(size_t s = 0 ; s < sizeof(half) / sizeof(*half) ; ++s){
498 if(map->entries[i].unicode == half[s].w){
499 logdebug("found %lc at fontidx %u", half[s].w, i);
500 half[s].found = true;
501 ++halvesfound;
502 break;
503 }
504 }
505 for(size_t s = 0 ; s < sizeof(quads) / sizeof(*quads) ; ++s){
506 if(map->entries[i].unicode == quads[s].w){
507 logdebug("found %lc at fontidx %u", quads[s].w, i);
508 quads[s].found = true;
509 ++numfound;
510 break;
511 }
512 }
513 for(size_t s = 0 ; s < sizeof(eighths) / sizeof(*eighths) ; ++s){
514 if(map->entries[i].unicode == eighths[s].w){
515 logdebug("found %lc at fontidx %u", eighths[s].w, i);
516 eighths[s].found = true;
517 ++numfound;
518 break;
519 }
520 }
521 }
522 }
523 if(halvesfound == sizeof(half) / sizeof(*half)){
524 *halfblocks = true;
525 }
526 if(numfound + halvesfound == (sizeof(half) + sizeof(quads) + sizeof(eighths)) / sizeof(*quads)){
527 logdebug("all %zu desired glyphs were already present", numfound);
528 *quadrants = true;
529 return 0;
530 }
531 if(no_font_changes){
532 logdebug("not reprogramming kernel font per request");
533 return 0;
534 }
535 int added = 0;
536 int halvesadded = 0;
537 unsigned candidate = cfo->charcount;
538 // FIXME factor out a function here, crikey
539 for(size_t s = 0 ; s < sizeof(half) / sizeof(*half) ; ++s){
540 if(!half[s].found){
541 while(--candidate){
542 if(map->entries[candidate].unicode < 0x2580 || map->entries[candidate].unicode > 0x259f){
543 break;
544 }
545 }
546 if(candidate == 0){
547 logwarn("ran out of replaceable glyphs for U+%04lx", (long)half[s].w);
548 // FIXME maybe don't want to error out here?
549 return -1;
550 }
551 if(shim_quad_block(cfo, candidate, half[s].qbits)){
552 logwarn("error replacing glyph for U+%04lx at %u", (long)half[s].w, candidate);
553 return -1;
554 }
555 if(add_to_map(map, half[s].w, candidate)){
556 return -1;
557 }
558 ++halvesadded;
559 }
560 }
561 for(size_t s = 0 ; s < sizeof(quads) / sizeof(*quads) ; ++s){
562 if(!quads[s].found){
563 while(--candidate){
564 if(map->entries[candidate].unicode < 0x2580 || map->entries[candidate].unicode > 0x259f){
565 break;
566 }
567 }
568 if(candidate == 0){
569 logwarn("ran out of replaceable glyphs for U+%04lx", (long)quads[s].w);
570 // FIXME maybe don't want to error out here?
571 return -1;
572 }
573 if(shim_quad_block(cfo, candidate, quads[s].qbits)){
574 logwarn("error replacing glyph for U+%04lx at %u", (long)quads[s].w, candidate);
575 return -1;
576 }
577 if(add_to_map(map, quads[s].w, candidate)){
578 return -1;
579 }
580 ++added;
581 }
582 }
583 for(size_t s = 0 ; s < sizeof(eighths) / sizeof(*eighths) ; ++s){
584 if(!eighths[s].found){
585 while(--candidate){
586 if(map->entries[candidate].unicode < 0x2580 || map->entries[candidate].unicode > 0x259f){
587 break;
588 }
589 }
590 if(candidate == 0){
591 logwarn("ran out of replaceable glyphs for U+%04lx", (long)eighths[s].w);
592 return -1;
593 }
594 if(shim_lower_eighths(cfo, candidate, eighths[s].qbits)){
595 logwarn("error replacing glyph for U+%04lx at %u", (long)eighths[s].w, candidate);
596 return -1;
597 }
598 if(add_to_map(map, eighths[s].w, candidate)){
599 return -1;
600 }
601 ++added;
602 }
603 }
604 if(halvesadded == 0 && added == 0){
605 loginfo("didn't replace any glyphs, not calling ioctl");
606 return 0;
607 }
608 struct framebuffer_copy fbdup;
609 if(copy_and_close_linux_fb(ti, &fbdup)){
610 return -1;
611 }
612 cfo->op = KD_FONT_OP_SET;
613 if(ioctl(fd, KDFONTOP, cfo)){
614 logwarn("error programming kernel font (%s)", strerror(errno));
615 kill_fbcopy(&fbdup);
616 return -1;
617 }
618 if(ioctl(fd, PIO_UNIMAP, map)){
619 logwarn("error setting kernel unicode map (%s)", strerror(errno));
620 kill_fbcopy(&fbdup);
621 return -1;
622 }
623 if(halvesadded + halvesfound == sizeof(half) / sizeof(*half)){
624 *halfblocks = true;
625 }
626 if(added + numfound == (sizeof(quads) + sizeof(eighths)) / sizeof(*quads)){
627 *quadrants = true;
628 }
629 added += halvesadded;
630 loginfo("successfully added %d kernel font glyph%s via %d", added, added == 1 ? "" : "s", ti->linux_fb_fd);
631 if(ti->linux_fb_fd < 0){ // console doesn't imply framebuffer
632 kill_fbcopy(&fbdup);
633 return 0;
634 }
635 unsigned pixely, pixelx;
636 if(get_linux_fb_pixelgeom(ti, &pixely, &pixelx)){
637 kill_fbcopy(&fbdup);
638 return -1;
639 }
640 if(pixely != fbdup.pixely || pixelx != fbdup.pixelx || ti->linux_fb_len != fbdup.maplen){
641 logwarn("framebuffer changed size, not reblitting");
642 }else{
643 memcpy(ti->linux_fbuffer, fbdup.map, fbdup.maplen);
644 }
645 kill_fbcopy(&fbdup);
646 return 0;
647}
648
649static int
650reprogram_linux_font(tinfo* ti, int fd, struct console_font_op* cfo,
651 struct unimapdesc* map, unsigned no_font_changes,
652 bool* halfblocks, bool* quadrants){
653 if(ioctl(fd, KDFONTOP, cfo)){
654 logwarn("error reading Linux kernelfont (%s)", strerror(errno));
655 return -1;
656 }
657 loginfo("kernel font size (glyphcount): %u", cfo->charcount);
658 loginfo("kernel font character geometry: %ux%u", cfo->width, cfo->height);
659 if(cfo->charcount > 512){
660 logwarn("warning: kernel returned excess charcount");
661 return -1;
662 }
663 if(ioctl(fd, GIO_UNIMAP, map)){
664 logwarn("error reading Linux unimap (%s)", strerror(errno));
665 return -1;
666 }
667 loginfo("kernel unimap size: %u/%u", map->entry_ct, USHRT_MAX);
668 // for certain sets of characters, we're not going to draw them in, but we
669 // do want to ensure they map to something plausible...this doesn't reset
670 // the framebuffer, even if we do some reprogramming.
671 if(!no_font_changes){
672 if(program_line_drawing_chars(fd, map)){
673 return -1;
674 }
675 }
676 if(program_block_drawing_chars(ti, fd, cfo, map, no_font_changes,
677 halfblocks, quadrants)){
678 return -1;
679 }
680 return 0;
681}
682
683int reprogram_console_font(tinfo* ti, unsigned no_font_changes,
684 bool* halfblocks, bool* quadrants){
685 struct console_font_op cfo = {
686 .op = KD_FONT_OP_GET,
687 .charcount = 512,
688 .height = 32,
689 .width = 32,
690 };
691 size_t totsize = 128 * cfo.charcount; // FIXME enough?
692 cfo.data = malloc(totsize);
693 if(cfo.data == NULL){
694 logwarn("error acquiring %zub for font descriptors (%s)", totsize, strerror(errno));
695 return -1;
696 }
697 struct unimapdesc map = {0};
698 map.entry_ct = USHRT_MAX;
699 totsize = map.entry_ct * sizeof(struct unipair);
700 map.entries = malloc(totsize);
701 if(map.entries == NULL){
702 logwarn("error acquiring %zub for Unicode font map (%s)", totsize, strerror(errno));
703 free(cfo.data);
704 return -1;
705 }
706 int r = reprogram_linux_font(ti, ti->ttyfd, &cfo, &map, no_font_changes,
707 halfblocks, quadrants);
708 free(cfo.data);
709 free(map.entries);
710 return r;
711}
712
713// is the provided fd a Linux console? if so, returns true. if it is indeed
714// a Linux console, and the console font has the quadrant glyphs (either
715// because they were already present, or we added them), quadrants is set high.
716bool is_linux_console(int fd){
717 if(fd < 0){
718 return false;
719 }
720 int mode;
721 if(ioctl(fd, KDGETMODE, &mode)){
722 logdebug("not a Linux console (no KDGETMODE)");
723 return false;
724 }
725 loginfo("verified Linux console, mode %d", mode);
726 return true;
727}
728
729int get_linux_fb_pixelgeom(tinfo* ti, unsigned* ypix, unsigned *xpix){
730 unsigned fakey, fakex;
731 if(ypix == NULL){
732 ypix = &fakey;
733 }
734 if(xpix == NULL){
735 xpix = &fakex;
736 }
737 struct fb_var_screeninfo fbi = {0};
738 if(ioctl(ti->linux_fb_fd, FBIOGET_VSCREENINFO, &fbi)){
739 logerror("no framebuffer info from %s %d (%s?)", ti->linux_fb_dev,
740 ti->linux_fb_fd, strerror(errno));
741 return -1;
742 }
743 loginfo("linux %s geometry: %dx%d", ti->linux_fb_dev, fbi.yres, fbi.xres);
744 *ypix = fbi.yres;
745 *xpix = fbi.xres;
746 size_t len = *ypix * *xpix * fbi.bits_per_pixel / 8;
747 if(ti->linux_fb_len != len){
748 if(ti->linux_fbuffer != MAP_FAILED){
749 munmap(ti->linux_fbuffer, ti->linux_fb_len);
750 ti->linux_fbuffer = MAP_FAILED;
751 ti->linux_fb_len = 0;
752 }
753 ti->linux_fbuffer = mmap(NULL, len, PROT_READ|PROT_WRITE,
754 MAP_SHARED, ti->linux_fb_fd, 0);
755 if(ti->linux_fbuffer == MAP_FAILED){
756 logerror("couldn't map %zuB on %s (%s?)", len, ti->linux_fb_dev, strerror(errno));
757 return -1;
758 }
759 ti->linux_fb_len = len;
760 loginfo("mapped %zuB on %s", len, ti->linux_fb_dev);
761 }
762 return 0;
763}
764
766 // FIXME there might be multiple framebuffers present; how do we determine
767 // which one is ours?
768 const char* dev = "/dev/fb0";
769 loginfo("checking for Linux framebuffer at %s", dev);
770 int fd = open(dev, O_RDWR | O_CLOEXEC);
771 if(fd < 0){
772 logdebug("couldn't open framebuffer device %s", dev);
773 return false;
774 }
775 ti->linux_fb_fd = fd;
776 if((ti->linux_fb_dev = strdup(dev)) == NULL){
777 close(ti->linux_fb_fd);
778 ti->linux_fb_fd = -1;
779 return false;
780 }
781 if(get_linux_fb_pixelgeom(ti, &ti->pixy, &ti->pixx)){
782 close(fd);
783 ti->linux_fb_fd = -1;
784 free(ti->linux_fb_dev);
785 ti->linux_fb_dev = NULL;
786 return false;
787 }
788 return true;
789}
790#else
791int fbcon_rebuild(sprixel* s, int ycell, int xcell, uint8_t* auxvec){
792 (void)s;
793 (void)ycell;
794 (void)xcell;
795 (void)auxvec;
796 return 0;
797}
798
799int fbcon_draw(const tinfo* ti, sprixel* s, int y, int x){
800 (void)ti;
801 (void)s;
802 (void)y;
803 (void)x;
804 return 0;
805}
806
807void fbcon_scroll(const struct ncpile* p, tinfo* ti, int rows){
808 (void)p;
809 (void)ti;
810 (void)rows;
811}
812
813int get_linux_fb_pixelgeom(tinfo* ti, unsigned* ypix, unsigned *xpix){
814 (void)ti;
815 (void)ypix;
816 (void)xpix;
817 return -1;
818}
819#endif
const nccell * c
Definition egcpool.h:296
uint32_t idx
Definition egcpool.h:298
free(duplicated)
int r
Definition fbuf.h:226
int fbcon_scrub(const struct ncpile *p, sprixel *s)
Definition linux.c:113
int fbcon_draw(const tinfo *ti, sprixel *s, int y, int x)
Definition linux.c:799
int get_linux_fb_pixelgeom(tinfo *ti, unsigned *ypix, unsigned *xpix)
Definition linux.c:813
int fbcon_blit(struct ncplane *n, int linesize, const void *data, int leny, int lenx, const struct blitterargs *bargs)
Definition linux.c:44
void fbcon_scroll(const struct ncpile *p, tinfo *ti, int rows)
Definition linux.c:807
int fbcon_wipe(sprixel *s, int ycell, int xcell)
Definition linux.c:16
int fbcon_rebuild(sprixel *s, int ycell, int xcell, uint8_t *auxvec)
Definition linux.c:791
bool is_linux_console(int fd)
bool is_linux_framebuffer(struct tinfo *ti)
int reprogram_console_font(struct tinfo *ti, unsigned no_font_changes, bool *halfblocks, bool *quadrants)
#define logerror(fmt,...)
Definition logging.h:32
#define logtrace(fmt,...)
Definition logging.h:57
#define loginfo(fmt,...)
Definition logging.h:42
#define logdebug(fmt,...)
Definition logging.h:52
#define logwarn(fmt,...)
Definition logging.h:37
int y
Definition notcurses.h:1905
vopts n
Definition notcurses.h:3502
int int x
Definition notcurses.h:1905
API int API int const nccell unsigned len
Definition notcurses.h:2588
int sixel_scrub(const ncpile *p, sprixel *s)
Definition sixel.c:1439
sprixcell_e
Definition sprite.h:114
@ SPRIXCELL_ANNIHILATED
Definition sprite.h:120
@ SPRIXCELL_TRANSPARENT
Definition sprite.h:115
@ SPRIXCELL_ANNIHILATED_TRANS
Definition sprite.h:121
@ SPRIXCELL_MIXED_SIXEL
Definition sprite.h:118
@ SPRIXCELL_OPAQUE_SIXEL
Definition sprite.h:116
@ SPRIXEL_INVALIDATED
Definition sprite.h:24
struct blitterargs::@3::@5 pixel
sprixel * spx
Definition internal.h:388
union blitterargs::@3 u
uint32_t transcolor
Definition internal.h:380
char * buf
Definition fbuf.h:28
uint64_t size
Definition fbuf.h:26
unsigned cellpxx
Definition internal.h:326
unsigned dimx
Definition internal.h:325
unsigned dimy
Definition internal.h:325
unsigned cellpxy
Definition internal.h:326
tament * tam
Definition internal.h:106
unsigned dimx
Definition sprite.h:146
int pixx
Definition sprite.h:147
int pixy
Definition sprite.h:147
fbuf glyph
Definition sprite.h:138
struct ncplane * n
Definition sprite.h:142
uint32_t id
Definition sprite.h:139
sprixel_e invalidated
Definition sprite.h:143
void * auxvector
Definition sprite.h:128
sprixcell_e state
Definition sprite.h:127
unsigned cellpxx
Definition termdesc.h:117
unsigned pixx
Definition termdesc.h:113
unsigned pixy
Definition termdesc.h:112
unsigned cellpxy
Definition termdesc.h:116
int ttyfd
Definition termdesc.h:109
return NULL
Definition termdesc.h:229