Unleashed v1.4
[unleashed.git] / usr / src / boot / sys / boot / common / gfx_fb.c
blob7a41fd242e191266a1d43caa6b4c787ba2fd0773
1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
13 * Copyright 2016 Toomas Soome <tsoome@me.com>
17 * Common functions to implement graphical framebuffer support for console.
20 #include <sys/cdefs.h>
21 #include <sys/param.h>
22 #include <stand.h>
23 #if defined(EFI)
24 #include <efi.h>
25 #include <efilib.h>
26 #else
27 #include <btxv86.h>
28 #endif
29 #include <sys/tem_impl.h>
30 #include <sys/consplat.h>
31 #include <sys/visual_io.h>
32 #include <sys/multiboot2.h>
33 #include <sys/font.h>
34 #include <sys/endian.h>
35 #include <gfx_fb.h>
36 #include <pnglite.h>
37 #include <bootstrap.h>
40 * Global framebuffer struct, to be updated with mode changes.
42 multiboot_tag_framebuffer_t gfx_fb;
44 /* To support setenv, keep track of inverses and colors. */
45 static int gfx_inverse = 0;
46 static int gfx_inverse_screen = 0;
47 static uint8_t gfx_fg = DEFAULT_ANSI_FOREGROUND;
48 static uint8_t gfx_bg = DEFAULT_ANSI_BACKGROUND;
50 static int gfx_fb_cons_clear(struct vis_consclear *);
51 static void gfx_fb_cons_copy(struct vis_conscopy *);
52 static void gfx_fb_cons_display(struct vis_consdisplay *);
54 #if defined (EFI)
55 static int gfx_gop_cons_clear(uint32_t data, uint32_t width, uint32_t height);
56 static void gfx_gop_cons_copy(struct vis_conscopy *);
57 static void gfx_gop_cons_display(struct vis_consdisplay *);
58 #endif
59 static int gfx_bm_cons_clear(uint32_t data, uint32_t width, uint32_t height);
60 static void gfx_bm_cons_copy(struct vis_conscopy *);
61 static void gfx_bm_cons_display(struct vis_consdisplay *);
64 * Set default operations to use bitmap based implementation.
65 * In case of UEFI, if GOP is available, we will switch to GOP based
66 * implementation.
68 * Also note, for UEFI we do attempt to boost the execution by setting
69 * Task Priority Level (TPL) to TPL_NOTIFY, which is highest priority
70 * usable in application.
72 struct gfx_fb_ops {
73 int (*gfx_cons_clear)(uint32_t, uint32_t, uint32_t);
74 void (*gfx_cons_copy)(struct vis_conscopy *);
75 void (*gfx_cons_display)(struct vis_consdisplay *);
76 } gfx_fb_ops = {
77 .gfx_cons_clear = gfx_bm_cons_clear,
78 .gfx_cons_copy = gfx_bm_cons_copy,
79 .gfx_cons_display = gfx_bm_cons_display
83 * Translate platform specific FB address.
85 static uint8_t *
86 gfx_get_fb_address(void)
88 #if defined(EFI)
89 return ((uint8_t *)(uintptr_t)
90 gfx_fb.framebuffer_common.framebuffer_addr);
91 #else
92 return ((uint8_t *)PTOV((uint32_t)
93 gfx_fb.framebuffer_common.framebuffer_addr & 0xffffffff));
94 #endif
98 * Generic platform callbacks for tem.
100 void
101 plat_tem_get_prom_font_size(int *charheight, int *windowtop)
103 *charheight = 0;
104 *windowtop = 0;
107 void
108 plat_tem_get_colors(uint8_t *fg, uint8_t *bg)
110 *fg = gfx_fg;
111 *bg = gfx_bg;
114 void
115 plat_tem_get_inverses(int *inverse, int *inverse_screen)
117 *inverse = gfx_inverse;
118 *inverse_screen = gfx_inverse_screen;
122 * Utility function to parse gfx mode line strings.
124 bool
125 gfx_parse_mode_str(char *str, int *x, int *y, int *depth)
127 char *p, *end;
129 errno = 0;
130 p = str;
131 *x = strtoul(p, &end, 0);
132 if (*x == 0 || errno != 0)
133 return (false);
134 if (*end != 'x')
135 return (false);
136 p = end + 1;
137 *y = strtoul(p, &end, 0);
138 if (*y == 0 || errno != 0)
139 return (false);
140 if (*end != 'x') {
141 *depth = -1; /* auto select */
142 } else {
143 p = end + 1;
144 *depth = strtoul(p, &end, 0);
145 if (*depth == 0 || errno != 0 || *end != '\0')
146 return (false);
149 return (true);
153 * Support for color mapping.
155 uint32_t
156 gfx_fb_color_map(uint8_t index)
158 uint8_t c;
159 int pos, size;
160 uint32_t color;
162 if (gfx_fb.framebuffer_common.framebuffer_type !=
163 MULTIBOOT_FRAMEBUFFER_TYPE_RGB) {
164 if (index < nitems (solaris_color_to_pc_color))
165 return (solaris_color_to_pc_color[index]);
166 else
167 return (index);
170 c = cmap4_to_24.red[index];
171 pos = gfx_fb.u.fb2.framebuffer_red_field_position;
172 size = gfx_fb.u.fb2.framebuffer_red_mask_size;
173 color = ((c >> (8 - size)) & ((1 << size) - 1)) << pos;
175 c = cmap4_to_24.green[index];
176 pos = gfx_fb.u.fb2.framebuffer_green_field_position;
177 size = gfx_fb.u.fb2.framebuffer_green_mask_size;
178 color |= ((c >> (8 - size)) & ((1 << size) - 1)) << pos;
180 c = cmap4_to_24.blue[index];
181 pos = gfx_fb.u.fb2.framebuffer_blue_field_position;
182 size = gfx_fb.u.fb2.framebuffer_blue_mask_size;
183 color |= ((c >> (8 - size)) & ((1 << size) - 1)) << pos;
185 return (color);
188 static bool
189 color_name_to_ansi(const char *name, int *val)
191 if (strcasecmp(name, "black") == 0) {
192 *val = ANSI_COLOR_BLACK;
193 return (true);
195 if (strcasecmp(name, "red") == 0) {
196 *val = ANSI_COLOR_RED;
197 return (true);
199 if (strcasecmp(name, "green") == 0) {
200 *val = ANSI_COLOR_GREEN;
201 return (true);
203 if (strcasecmp(name, "yellow") == 0) {
204 *val = ANSI_COLOR_YELLOW;
205 return (true);
207 if (strcasecmp(name, "blue") == 0) {
208 *val = ANSI_COLOR_BLUE;
209 return (true);
211 if (strcasecmp(name, "magenta") == 0) {
212 *val = ANSI_COLOR_MAGENTA;
213 return (true);
215 if (strcasecmp(name, "cyan") == 0) {
216 *val = ANSI_COLOR_CYAN;
217 return (true);
219 if (strcasecmp(name, "white") == 0) {
220 *val = ANSI_COLOR_WHITE;
221 return (true);
223 return (false);
226 /* Callback to check and set colors */
227 static int
228 gfx_set_colors(struct env_var *ev, int flags, const void *value)
230 int val = 0;
231 char buf[2];
232 const void *evalue;
234 if (value == NULL)
235 return (CMD_OK);
237 if (color_name_to_ansi(value, &val)) {
238 snprintf(buf, sizeof (buf), "%d", val);
239 evalue = buf;
240 } else {
241 char *end;
243 errno = 0;
244 val = (int) strtol(value, &end, 0);
245 if (errno != 0 || *end != '\0') {
246 printf("Allowed values are either ansi color name or "
247 "number from range [0-7].\n");
248 return (CMD_OK);
250 evalue = value;
253 /* invalid value? */
254 if (val < 0 || val > 7) {
255 printf("Allowed values are either ansi color name or "
256 "number from range [0-7].\n");
257 return (CMD_OK);
260 if (strcmp(ev->ev_name, "tem.fg_color") == 0) {
261 /* is it already set? */
262 if (gfx_fg == val)
263 return (CMD_OK);
264 gfx_fg = val;
266 if (strcmp(ev->ev_name, "tem.bg_color") == 0) {
267 /* is it already set? */
268 if (gfx_bg == val)
269 return (CMD_OK);
270 gfx_bg = val;
272 env_setenv(ev->ev_name, flags | EV_NOHOOK, evalue, NULL, NULL);
273 plat_cons_update_mode(-1);
274 return (CMD_OK);
277 /* Callback to check and set inverses */
278 static int
279 gfx_set_inverses(struct env_var *ev, int flags, const void *value)
281 int t, f;
283 if (value == NULL)
284 return (CMD_OK);
286 t = strcmp(value, "true");
287 f = strcmp(value, "false");
289 /* invalid value? */
290 if (t != 0 && f != 0)
291 return (CMD_OK);
293 if (strcmp(ev->ev_name, "tem.inverse") == 0) {
294 /* is it already set? */
295 if (gfx_inverse == (t == 0))
296 return (CMD_OK);
297 gfx_inverse = (t == 0);
299 if (strcmp(ev->ev_name, "tem.inverse-screen") == 0) {
300 /* is it already set? */
301 if (gfx_inverse_screen == (t == 0))
302 return (CMD_OK);
303 gfx_inverse_screen = (t == 0);
305 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
306 plat_cons_update_mode(-1);
307 return (CMD_OK);
311 * Initialize gfx framework.
313 void
314 gfx_framework_init(struct visual_ops *fb_ops)
316 int rc;
317 char *env, buf[2];
318 #if defined (EFI)
319 extern EFI_GRAPHICS_OUTPUT *gop;
321 if (gop != NULL) {
322 gfx_fb_ops.gfx_cons_clear = gfx_gop_cons_clear;
323 gfx_fb_ops.gfx_cons_copy = gfx_gop_cons_copy;
324 gfx_fb_ops.gfx_cons_display = gfx_gop_cons_display;
326 #endif
328 /* Add visual io callbacks */
329 fb_ops->cons_clear = gfx_fb_cons_clear;
330 fb_ops->cons_copy = gfx_fb_cons_copy;
331 fb_ops->cons_display = gfx_fb_cons_display;
333 /* set up tem inverse controls */
334 env = getenv("tem.inverse");
335 if (env != NULL) {
336 if (strcmp(env, "true") == 0)
337 gfx_inverse = 1;
338 unsetenv("tem.inverse");
341 env = getenv("tem.inverse-screen");
342 if (env != NULL) {
343 if (strcmp(env, "true") == 0)
344 gfx_inverse_screen = 1;
345 unsetenv("tem.inverse-screen");
348 if (gfx_inverse)
349 env = "true";
350 else
351 env = "false";
353 env_setenv("tem.inverse", EV_VOLATILE, env, gfx_set_inverses,
354 env_nounset);
356 if (gfx_inverse_screen)
357 env = "true";
358 else
359 env = "false";
361 env_setenv("tem.inverse-screen", EV_VOLATILE, env, gfx_set_inverses,
362 env_nounset);
364 /* set up tem color controls */
365 env = getenv("tem.fg_color");
366 if (env != NULL) {
367 rc = (int) strtol(env, NULL, 0);
368 if (rc >= 0 && rc <= 7)
369 gfx_fg = rc;
370 unsetenv("tem.fg_color");
373 env = getenv("tem.bg_color");
374 if (env != NULL) {
375 rc = (int) strtol(env, NULL, 0);
376 if (rc >= 0 && rc <= 7)
377 gfx_bg = rc;
378 unsetenv("tem.bg_color");
381 snprintf(buf, sizeof (buf), "%d", gfx_fg);
382 env_setenv("tem.fg_color", EV_VOLATILE, buf, gfx_set_colors,
383 env_nounset);
384 snprintf(buf, sizeof (buf), "%d", gfx_bg);
385 env_setenv("tem.bg_color", EV_VOLATILE, buf, gfx_set_colors,
386 env_nounset);
390 * visual io callbacks.
393 #if defined (EFI)
394 static int
395 gfx_gop_cons_clear(uint32_t data, uint32_t width, uint32_t height)
397 EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer;
398 EFI_STATUS status;
399 extern EFI_GRAPHICS_OUTPUT *gop;
401 BltBuffer = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *)&data;
403 status = gop->Blt(gop, BltBuffer, EfiBltVideoFill, 0, 0,
404 0, 0, width, height, 0);
406 if (EFI_ERROR(status))
407 return (1);
408 else
409 return (0);
411 #endif
413 static int
414 gfx_bm_cons_clear(uint32_t data, uint32_t width, uint32_t height)
416 uint8_t *fb, *fb8;
417 uint32_t *fb32, pitch;
418 uint16_t *fb16;
419 uint32_t i, j;
421 fb = gfx_get_fb_address();
422 pitch = gfx_fb.framebuffer_common.framebuffer_pitch;
424 switch (gfx_fb.framebuffer_common.framebuffer_bpp) {
425 case 8: /* 8 bit */
426 for (i = 0; i < height; i++) {
427 (void) memset(fb + i * pitch, data, pitch);
429 break;
430 case 15:
431 case 16: /* 16 bit */
432 for (i = 0; i < height; i++) {
433 fb16 = (uint16_t *)(fb + i * pitch);
434 for (j = 0; j < width; j++)
435 fb16[j] = (uint16_t)(data & 0xffff);
437 break;
438 case 24: /* 24 bit */
439 for (i = 0; i < height; i++) {
440 fb8 = fb + i * pitch;
441 for (j = 0; j < pitch; j += 3) {
442 fb8[j] = (data >> 16) & 0xff;
443 fb8[j+1] = (data >> 8) & 0xff;
444 fb8[j+2] = data & 0xff;
447 break;
448 case 32: /* 32 bit */
449 for (i = 0; i < height; i++) {
450 fb32 = (uint32_t *)(fb + i * pitch);
451 for (j = 0; j < width; j++)
452 fb32[j] = data;
454 break;
455 default:
456 return (1);
459 return (0);
462 static int
463 gfx_fb_cons_clear(struct vis_consclear *ca)
465 uint32_t data, width, height;
466 int ret;
467 #if defined (EFI)
468 EFI_TPL tpl;
469 #endif
471 data = gfx_fb_color_map(ca->bg_color);
472 width = gfx_fb.framebuffer_common.framebuffer_width;
473 height = gfx_fb.framebuffer_common.framebuffer_height;
475 #if defined (EFI)
476 tpl = BS->RaiseTPL(TPL_NOTIFY);
477 #endif
478 ret = gfx_fb_ops.gfx_cons_clear(data, width, height);
479 #if defined (EFI)
480 BS->RestoreTPL(tpl);
481 #endif
482 return (ret);
485 #if defined (EFI)
486 static void
487 gfx_gop_cons_copy(struct vis_conscopy *ma)
489 UINTN width, height;
490 extern EFI_GRAPHICS_OUTPUT *gop;
492 width = ma->e_col - ma->s_col + 1;
493 height = ma->e_row - ma->s_row + 1;
495 (void) gop->Blt(gop, NULL, EfiBltVideoToVideo, ma->s_col, ma->s_row,
496 ma->t_col, ma->t_row, width, height, 0);
498 #endif
500 static void
501 gfx_bm_cons_copy(struct vis_conscopy *ma)
503 uint32_t soffset, toffset;
504 uint32_t width, height;
505 uint8_t *src, *dst, *fb;
506 uint32_t bpp, pitch;
508 fb = gfx_get_fb_address();
509 bpp = roundup2(gfx_fb.framebuffer_common.framebuffer_bpp, 8) >> 3;
510 pitch = gfx_fb.framebuffer_common.framebuffer_pitch;
512 soffset = ma->s_col * bpp + ma->s_row * pitch;
513 toffset = ma->t_col * bpp + ma->t_row * pitch;
514 src = fb + soffset;
515 dst = fb + toffset;
516 width = (ma->e_col - ma->s_col + 1) * bpp;
517 height = ma->e_row - ma->s_row + 1;
519 if (toffset <= soffset) {
520 for (uint32_t i = 0; i < height; i++) {
521 uint32_t increment = i * pitch;
522 (void)memmove(dst + increment, src + increment, width);
524 } else {
525 for (int i = height - 1; i >= 0; i--) {
526 uint32_t increment = i * pitch;
527 (void)memmove(dst + increment, src + increment, width);
532 static void
533 gfx_fb_cons_copy(struct vis_conscopy *ma)
535 #if defined (EFI)
536 EFI_TPL tpl;
538 tpl = BS->RaiseTPL(TPL_NOTIFY);
539 #endif
541 gfx_fb_ops.gfx_cons_copy(ma);
542 #if defined (EFI)
543 BS->RestoreTPL(tpl);
544 #endif
548 * Implements alpha blending for RGBA data, could use pixels for arguments,
549 * but byte stream seems more generic.
550 * The generic alpha blending is:
551 * blend = alpha * fg + (1.0 - alpha) * bg.
552 * Since our alpha is not from range [0..1], we scale appropriately.
554 static uint8_t
555 alpha_blend(uint8_t fg, uint8_t bg, uint8_t alpha)
557 uint16_t blend, h, l;
559 /* trivial corner cases */
560 if (alpha == 0)
561 return (bg);
562 if (alpha == 0xFF)
563 return (fg);
564 blend = (alpha * fg + (0xFF - alpha) * bg);
565 /* Division by 0xFF */
566 h = blend >> 8;
567 l = blend & 0xFF;
568 if (h + l >= 0xFF)
569 h++;
570 return (h);
573 /* Copy memory to framebuffer or to memory. */
574 static void
575 bitmap_cpy(uint8_t *dst, uint8_t *src, uint32_t len, int bpp)
577 uint32_t i;
578 uint8_t a;
580 switch (bpp) {
581 case 4:
583 * we only implement alpha blending for depth 32,
584 * use memcpy for other cases.
586 for (i = 0; i < len; i += bpp) {
587 a = src[i+3];
588 dst[i] = alpha_blend(src[i], dst[i], a);
589 dst[i+1] = alpha_blend(src[i+1], dst[i+1], a);
590 dst[i+2] = alpha_blend(src[i+2], dst[i+2], a);
591 dst[i+3] = a;
593 break;
594 default:
595 (void) memcpy(dst, src, len);
596 break;
600 #if defined (EFI)
601 static void
602 gfx_gop_cons_display(struct vis_consdisplay *da)
604 EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer;
605 uint32_t size;
606 int bpp;
607 extern EFI_GRAPHICS_OUTPUT *gop;
609 bpp = roundup2(gfx_fb.framebuffer_common.framebuffer_bpp, 8) >> 3;
610 size = sizeof (*BltBuffer) * da->width * da->height;
611 BltBuffer = malloc(size);
612 if (BltBuffer == NULL && gfx_get_fb_address() != NULL) {
613 /* Fall back to bitmap implementation */
614 gfx_bm_cons_display(da);
615 return;
618 (void) gop->Blt(gop, BltBuffer, EfiBltVideoToBltBuffer,
619 da->col, da->row, 0, 0, da->width, da->height, 0);
620 bitmap_cpy((void *)BltBuffer, da->data, size, bpp);
621 (void) gop->Blt(gop, BltBuffer, EfiBltBufferToVideo,
622 0, 0, da->col, da->row, da->width, da->height, 0);
623 free(BltBuffer);
625 #endif
627 static void
628 gfx_bm_cons_display(struct vis_consdisplay *da)
630 uint32_t size; /* write size per scanline */
631 uint8_t *fbp; /* fb + calculated offset */
632 int i, bpp, pitch;
634 bpp = roundup2(gfx_fb.framebuffer_common.framebuffer_bpp, 8) >> 3;
635 pitch = gfx_fb.framebuffer_common.framebuffer_pitch;
637 size = da->width * bpp;
638 fbp = gfx_get_fb_address();
639 fbp += da->col * bpp + da->row * pitch;
641 /* write all scanlines in rectangle */
642 for (i = 0; i < da->height; i++) {
643 uint8_t *dest = fbp + i * pitch;
644 uint8_t *src = da->data + i * size;
645 bitmap_cpy(dest, src, size, bpp);
649 static void
650 gfx_fb_cons_display(struct vis_consdisplay *da)
652 #if defined (EFI)
653 EFI_TPL tpl;
654 #endif
656 /* make sure we will not write past FB */
657 if ((uint32_t)da->col >= gfx_fb.framebuffer_common.framebuffer_width ||
658 (uint32_t)da->row >= gfx_fb.framebuffer_common.framebuffer_height ||
659 (uint32_t)da->col + da->width >
660 gfx_fb.framebuffer_common.framebuffer_width ||
661 (uint32_t)da->row + da->height >
662 gfx_fb.framebuffer_common.framebuffer_height)
663 return;
665 #if defined (EFI)
666 tpl = BS->RaiseTPL(TPL_NOTIFY);
667 #endif
668 gfx_fb_ops.gfx_cons_display(da);
669 #if defined (EFI)
670 BS->RestoreTPL(tpl);
671 #endif
674 void
675 gfx_fb_display_cursor(struct vis_conscursor *ca)
677 uint32_t fg, bg;
678 uint32_t offset, size, *fb32;
679 uint16_t *fb16;
680 uint8_t *fb8, *fb;
681 uint32_t bpp, pitch;
682 #if defined (EFI)
683 EFI_TPL tpl;
684 #endif
686 fb = gfx_get_fb_address();
687 bpp = roundup2(gfx_fb.framebuffer_common.framebuffer_bpp, 8) >> 3;
688 pitch = gfx_fb.framebuffer_common.framebuffer_pitch;
690 size = ca->width * bpp;
693 * Build cursor image. We are building mirror image of data on
694 * frame buffer by (D xor FG) xor BG.
696 offset = ca->col * bpp + ca->row * pitch;
697 #if defined (EFI)
698 tpl = BS->RaiseTPL(TPL_NOTIFY);
699 #endif
700 switch (gfx_fb.framebuffer_common.framebuffer_bpp) {
701 case 8: /* 8 bit */
702 fg = ca->fg_color.mono;
703 bg = ca->bg_color.mono;
704 for (int i = 0; i < ca->height; i++) {
705 fb8 = fb + offset + i * pitch;
706 for (uint32_t j = 0; j < size; j += 1) {
707 fb8[j] = (fb8[j] ^ (fg & 0xff)) ^ (bg & 0xff);
710 break;
711 case 15:
712 case 16: /* 16 bit */
713 fg = ca->fg_color.sixteen[0] << 8;
714 fg |= ca->fg_color.sixteen[1];
715 bg = ca->bg_color.sixteen[0] << 8;
716 bg |= ca->bg_color.sixteen[1];
717 for (int i = 0; i < ca->height; i++) {
718 fb16 = (uint16_t *)(fb + offset + i * pitch);
719 for (int j = 0; j < ca->width; j++) {
720 fb16[j] = (fb16[j] ^ (fg & 0xffff)) ^
721 (bg & 0xffff);
724 break;
725 case 24: /* 24 bit */
726 fg = ca->fg_color.twentyfour[0] << 16;
727 fg |= ca->fg_color.twentyfour[1] << 8;
728 fg |= ca->fg_color.twentyfour[2];
729 bg = ca->bg_color.twentyfour[0] << 16;
730 bg |= ca->bg_color.twentyfour[1] << 8;
731 bg |= ca->bg_color.twentyfour[2];
733 for (int i = 0; i < ca->height; i++) {
734 fb8 = fb + offset + i * pitch;
735 for (uint32_t j = 0; j < size; j += 3) {
736 fb8[j] = (fb8[j] ^ ((fg >> 16) & 0xff)) ^
737 ((bg >> 16) & 0xff);
738 fb8[j+1] = (fb8[j+1] ^ ((fg >> 8) & 0xff)) ^
739 ((bg >> 8) & 0xff);
740 fb8[j+2] = (fb8[j+2] ^ (fg & 0xff)) ^
741 (bg & 0xff);
744 break;
745 case 32: /* 32 bit */
746 fg = ca->fg_color.twentyfour[0] << 16;
747 fg |= ca->fg_color.twentyfour[1] << 8;
748 fg |= ca->fg_color.twentyfour[2];
749 bg = ca->bg_color.twentyfour[0] << 16;
750 bg |= ca->bg_color.twentyfour[1] << 8;
751 bg |= ca->bg_color.twentyfour[2];
752 for (int i = 0; i < ca->height; i++) {
753 fb32 = (uint32_t *)(fb + offset + i * pitch);
754 for (int j = 0; j < ca->width; j++)
755 fb32[j] = (fb32[j] ^ fg) ^ bg;
757 break;
759 #if defined (EFI)
760 BS->RestoreTPL(tpl);
761 #endif
765 * Public graphics primitives.
768 static int
769 isqrt(int num)
771 int res = 0;
772 int bit = 1 << 30;
774 /* "bit" starts at the highest power of four <= the argument. */
775 while (bit > num)
776 bit >>= 2;
778 while (bit != 0) {
779 if (num >= res + bit) {
780 num -= res + bit;
781 res = (res >> 1) + bit;
782 } else
783 res >>= 1;
784 bit >>= 2;
786 return (res);
789 /* set pixel in framebuffer using gfx coordinates */
790 void
791 gfx_fb_setpixel(uint32_t x, uint32_t y)
793 uint32_t c, offset, pitch, bpp;
794 uint8_t *fb;
795 text_color_t fg, bg;
797 if (plat_stdout_is_framebuffer() == 0)
798 return;
800 tem_get_colors((tem_vt_state_t)tems.ts_active, &fg, &bg);
801 c = gfx_fb_color_map(fg);
803 if (x >= gfx_fb.framebuffer_common.framebuffer_width ||
804 y >= gfx_fb.framebuffer_common.framebuffer_height)
805 return;
807 fb = gfx_get_fb_address();
808 pitch = gfx_fb.framebuffer_common.framebuffer_pitch;
809 bpp = roundup2(gfx_fb.framebuffer_common.framebuffer_bpp, 8) >> 3;
811 offset = y * pitch + x * bpp;
812 switch (gfx_fb.framebuffer_common.framebuffer_bpp) {
813 case 8:
814 fb[offset] = c & 0xff;
815 break;
816 case 15:
817 case 16:
818 *(uint16_t *)(fb + offset) = c & 0xffff;
819 break;
820 case 24:
821 fb[offset] = (c >> 16) & 0xff;
822 fb[offset + 1] = (c >> 8) & 0xff;
823 fb[offset + 2] = c & 0xff;
824 break;
825 case 32:
826 *(uint32_t *)(fb + offset) = c;
827 break;
832 * draw rectangle in framebuffer using gfx coordinates.
833 * The function is borrowed from fbsd vt_fb.c
835 void
836 gfx_fb_drawrect(uint32_t x1, uint32_t y1, uint32_t x2, uint32_t y2,
837 uint32_t fill)
839 uint32_t x, y;
841 if (plat_stdout_is_framebuffer() == 0)
842 return;
844 for (y = y1; y <= y2; y++) {
845 if (fill || (y == y1) || (y == y2)) {
846 for (x = x1; x <= x2; x++)
847 gfx_fb_setpixel(x, y);
848 } else {
849 gfx_fb_setpixel(x1, y);
850 gfx_fb_setpixel(x2, y);
855 void
856 gfx_fb_line(uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1, uint32_t wd)
858 int dx, sx, dy, sy;
859 int err, e2, x2, y2, ed, width;
861 if (plat_stdout_is_framebuffer() == 0)
862 return;
864 width = wd;
865 sx = x0 < x1? 1 : -1;
866 sy = y0 < y1? 1 : -1;
867 dx = x1 > x0? x1 - x0 : x0 - x1;
868 dy = y1 > y0? y1 - y0 : y0 - y1;
869 err = dx + dy;
870 ed = dx + dy == 0 ? 1: isqrt(dx * dx + dy * dy);
872 for (;;) {
873 gfx_fb_setpixel(x0, y0);
874 e2 = err;
875 x2 = x0;
876 if ((e2 << 1) >= -dx) { /* x step */
877 e2 += dy;
878 y2 = y0;
879 while (e2 < ed * width &&
880 (y1 != (uint32_t)y2 || dx > dy)) {
881 y2 += sy;
882 gfx_fb_setpixel(x0, y2);
883 e2 += dx;
885 if (x0 == x1)
886 break;
887 e2 = err;
888 err -= dy;
889 x0 += sx;
891 if ((e2 << 1) <= dy) { /* y step */
892 e2 = dx-e2;
893 while (e2 < ed * width &&
894 (x1 != (uint32_t)x2 || dx < dy)) {
895 x2 += sx;
896 gfx_fb_setpixel(x2, y0);
897 e2 += dy;
899 if (y0 == y1)
900 break;
901 err += dx;
902 y0 += sy;
908 * quadratic Bézier curve limited to gradients without sign change.
910 void
911 gfx_fb_bezier(uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1, uint32_t x2,
912 uint32_t y2, uint32_t wd)
914 int sx, sy, xx, yy, xy, width;
915 int dx, dy, err, curvature;
916 int i;
918 if (plat_stdout_is_framebuffer() == 0)
919 return;
921 width = wd;
922 sx = x2 - x1;
923 sy = y2 - y1;
924 xx = x0 - x1;
925 yy = y0 - y1;
926 curvature = xx*sy - yy*sx;
928 if (sx*sx + sy*sy > xx*xx+yy*yy) {
929 x2 = x0;
930 x0 = sx + x1;
931 y2 = y0;
932 y0 = sy + y1;
933 curvature = -curvature;
935 if (curvature != 0) {
936 xx += sx;
937 sx = x0 < x2? 1 : -1;
938 xx *= sx;
939 yy += sy;
940 sy = y0 < y2? 1 : -1;
941 yy *= sy;
942 xy = (xx*yy) << 1;
943 xx *= xx;
944 yy *= yy;
945 if (curvature * sx * sy < 0) {
946 xx = -xx;
947 yy = -yy;
948 xy = -xy;
949 curvature = -curvature;
951 dx = 4 * sy * curvature * (x1 - x0) + xx - xy;
952 dy = 4 * sx * curvature * (y0 - y1) + yy - xy;
953 xx += xx;
954 yy += yy;
955 err = dx + dy + xy;
956 do {
957 for (i = 0; i <= width; i++)
958 gfx_fb_setpixel(x0 + i, y0);
959 if (x0 == x2 && y0 == y2)
960 return; /* last pixel -> curve finished */
961 y1 = 2 * err < dx;
962 if (2 * err > dy) {
963 x0 += sx;
964 dx -= xy;
965 dy += yy;
966 err += dy;
968 if (y1 != 0) {
969 y0 += sy;
970 dy -= xy;
971 dx += xx;
972 err += dx;
974 } while (dy < dx ); /* gradient negates -> algorithm fails */
976 gfx_fb_line(x0, y0, x2, y2, width);
980 * draw rectangle using terminal coordinates and current foreground color.
982 void
983 gfx_term_drawrect(uint32_t ux1, uint32_t uy1, uint32_t ux2, uint32_t uy2)
985 int x1, y1, x2, y2;
986 int xshift, yshift;
987 int width, i;
988 uint32_t vf_width, vf_height;
990 if (plat_stdout_is_framebuffer() == 0)
991 return;
993 vf_width = tems.ts_font.vf_width;
994 vf_height = tems.ts_font.vf_height;
995 width = vf_width / 4; /* line width */
996 xshift = (vf_width - width) / 2;
997 yshift = (vf_height - width) / 2;
998 /* Terminal coordinates start from (1,1) */
999 ux1--;
1000 uy1--;
1001 ux2--;
1002 uy2--;
1004 /* mark area used in tem */
1005 tem_image_display(tems.ts_active, uy1 - 1, ux1 - 1, uy2, ux2);
1008 * Draw horizontal lines width points thick, shifted from outer edge.
1010 x1 = (ux1 + 1) * vf_width + tems.ts_p_offset.x;
1011 y1 = uy1 * vf_height + tems.ts_p_offset.y + yshift;
1012 x2 = ux2 * vf_width + tems.ts_p_offset.x;
1013 gfx_fb_drawrect(x1, y1, x2, y1 + width, 1);
1014 y2 = uy2 * vf_height + tems.ts_p_offset.y;
1015 y2 += vf_height - yshift - width;
1016 gfx_fb_drawrect(x1, y2, x2, y2 + width, 1);
1019 * Draw vertical lines width points thick, shifted from outer edge.
1021 x1 = ux1 * vf_width + tems.ts_p_offset.x + xshift;
1022 y1 = uy1 * vf_height + tems.ts_p_offset.y;
1023 y1 += vf_height;
1024 y2 = uy2 * vf_height + tems.ts_p_offset.y;
1025 gfx_fb_drawrect(x1, y1, x1 + width, y2, 1);
1026 x1 = ux2 * vf_width + tems.ts_p_offset.x;
1027 x1 += vf_width - xshift - width;
1028 gfx_fb_drawrect(x1, y1, x1 + width, y2, 1);
1030 /* Draw upper left corner. */
1031 x1 = ux1 * vf_width + tems.ts_p_offset.x + xshift;
1032 y1 = uy1 * vf_height + tems.ts_p_offset.y;
1033 y1 += vf_height;
1035 x2 = ux1 * vf_width + tems.ts_p_offset.x;
1036 x2 += vf_width;
1037 y2 = uy1 * vf_height + tems.ts_p_offset.y + yshift;
1038 for (i = 0; i <= width; i++)
1039 gfx_fb_bezier(x1 + i, y1, x1 + i, y2 + i, x2, y2 + i, width-i);
1041 /* Draw lower left corner. */
1042 x1 = ux1 * vf_width + tems.ts_p_offset.x;
1043 x1 += vf_width;
1044 y1 = uy2 * vf_height + tems.ts_p_offset.y;
1045 y1 += vf_height - yshift;
1046 x2 = ux1 * vf_width + tems.ts_p_offset.x + xshift;
1047 y2 = uy2 * vf_height + tems.ts_p_offset.y;
1048 for (i = 0; i <= width; i++)
1049 gfx_fb_bezier(x1, y1 - i, x2 + i, y1 - i, x2 + i, y2, width-i);
1051 /* Draw upper right corner. */
1052 x1 = ux2 * vf_width + tems.ts_p_offset.x;
1053 y1 = uy1 * vf_height + tems.ts_p_offset.y + yshift;
1054 x2 = ux2 * vf_width + tems.ts_p_offset.x;
1055 x2 += vf_width - xshift - width;
1056 y2 = uy1 * vf_height + tems.ts_p_offset.y;
1057 y2 += vf_height;
1058 for (i = 0; i <= width; i++)
1059 gfx_fb_bezier(x1, y1 + i, x2 + i, y1 + i, x2 + i, y2, width-i);
1061 /* Draw lower right corner. */
1062 x1 = ux2 * vf_width + tems.ts_p_offset.x;
1063 y1 = uy2 * vf_height + tems.ts_p_offset.y;
1064 y1 += vf_height - yshift;
1065 x2 = ux2 * vf_width + tems.ts_p_offset.x;
1066 x2 += vf_width - xshift - width;
1067 y2 = uy2 * vf_height + tems.ts_p_offset.y;
1068 for (i = 0; i <= width; i++)
1069 gfx_fb_bezier(x1, y1 - i, x2 + i, y1 - i, x2 + i, y2, width-i);
1073 gfx_fb_putimage(png_t *png)
1075 struct vis_consdisplay da;
1076 uint32_t i, j, height, width, color;
1077 int bpp;
1078 uint8_t r, g, b, a, *p;
1080 if (plat_stdout_is_framebuffer() == 0 ||
1081 png->color_type != PNG_TRUECOLOR_ALPHA) {
1082 return (1);
1085 bpp = roundup2(gfx_fb.framebuffer_common.framebuffer_bpp, 8) >> 3;
1086 width = png->width;
1087 height = png->height;
1088 da.width = png->width;
1089 da.height = png->height;
1090 da.col = gfx_fb.framebuffer_common.framebuffer_width -
1091 tems.ts_p_offset.x;
1092 da.col -= da.width;
1093 da.row = gfx_fb.framebuffer_common.framebuffer_height -
1094 tems.ts_p_offset.y;
1095 da.row -= da.height;
1098 * mark area used in tem
1100 tem_image_display(tems.ts_active, da.row / tems.ts_font.vf_height - 1,
1101 da.col / tems.ts_font.vf_width - 1,
1102 tems.ts_c_dimension.height - 1,
1103 tems.ts_c_dimension.width - 1);
1105 da.data = malloc(width * height * bpp);
1106 if (da.data == NULL)
1107 return (1);
1110 * Build image for our framebuffer.
1112 for (i = 0; i < height * width * png->bpp; i += png->bpp) {
1113 r = png->image[i];
1114 g = png->image[i+1];
1115 b = png->image[i+2];
1116 a = png->image[i+3];
1118 j = i / png->bpp * bpp;
1119 color = r >> (8 - gfx_fb.u.fb2.framebuffer_red_mask_size)
1120 << gfx_fb.u.fb2.framebuffer_red_field_position;
1121 color |= g >> (8 - gfx_fb.u.fb2.framebuffer_green_mask_size)
1122 << gfx_fb.u.fb2.framebuffer_green_field_position;
1123 color |= b >> (8 - gfx_fb.u.fb2.framebuffer_blue_mask_size)
1124 << gfx_fb.u.fb2.framebuffer_blue_field_position;
1126 switch (gfx_fb.framebuffer_common.framebuffer_bpp) {
1127 case 8: {
1128 uint32_t best, dist, k;
1129 int diff;
1131 color = 0;
1132 best = 256 * 256 * 256;
1133 for (k = 0; k < 16; k++) {
1134 diff = r - cmap4_to_24.red[k];
1135 dist = diff * diff;
1136 diff = g - cmap4_to_24.green[k];
1137 dist += diff * diff;
1138 diff = b - cmap4_to_24.blue[k];
1139 dist += diff * diff;
1141 if (dist < best) {
1142 color = k;
1143 best = dist;
1144 if (dist == 0)
1145 break;
1148 da.data[j] = solaris_color_to_pc_color[color];
1149 break;
1151 case 15:
1152 case 16:
1153 *(uint16_t *)(da.data+j) = color;
1154 break;
1155 case 24:
1156 p = (uint8_t *)&color;
1157 da.data[j] = p[0];
1158 da.data[j+1] = p[1];
1159 da.data[j+2] = p[2];
1160 break;
1161 case 32:
1162 color |= a << 24;
1163 *(uint32_t *)(da.data+j) = color;
1164 break;
1168 gfx_fb_cons_display(&da);
1169 free(da.data);
1170 return (0);
1173 static int
1174 load_mapping(int fd, struct font *fp, int n)
1176 size_t i, size;
1177 ssize_t rv;
1178 struct font_map *mp;
1180 if (fp->vf_map_count[n] == 0)
1181 return (0);
1183 size = fp->vf_map_count[n] * sizeof (*mp);
1184 mp = malloc(size);
1185 if (mp == NULL)
1186 return (ENOMEM);
1187 fp->vf_map[n] = mp;
1189 rv = read(fd, mp, size);
1190 if (rv < 0 || (size_t)rv != size) {
1191 free(fp->vf_map[n]);
1192 fp->vf_map[n] = NULL;
1193 return (EIO);
1196 for (i = 0; i < fp->vf_map_count[n]; i++) {
1197 mp[i].font_src = be32toh(mp[i].font_src);
1198 mp[i].font_dst = be16toh(mp[i].font_dst);
1199 mp[i].font_len = be16toh(mp[i].font_len);
1201 return (0);
1204 /* Load font from file. */
1205 static bitmap_data_t *
1206 load_font(char *path)
1208 int fd, i;
1209 uint32_t glyphs;
1210 struct font_header fh;
1211 struct fontlist *fl;
1212 bitmap_data_t *bp = NULL;
1213 struct font *fp;
1214 size_t size;
1215 ssize_t rv;
1217 /* Get our entry from the font list. */
1218 STAILQ_FOREACH(fl, &fonts, font_next) {
1219 if (strcmp(fl->font_name, path) == 0)
1220 break;
1222 if (fl == NULL)
1223 return (NULL); /* Should not happen. */
1224 bp = fl->font_data;
1225 if (bp->font != NULL)
1226 return (bp);
1228 fd = open(path, O_RDONLY);
1229 if (fd < 0) {
1230 return (NULL);
1233 size = sizeof (fh);
1234 rv = read(fd, &fh, size);
1235 if (rv < 0 || (size_t)rv != size) {
1236 bp = NULL;
1237 goto done;
1239 if (memcmp(fh.fh_magic, FONT_HEADER_MAGIC, sizeof (fh.fh_magic)) != 0) {
1240 bp = NULL;
1241 goto done;
1243 if ((fp = calloc(1, sizeof (struct font))) == NULL) {
1244 bp = NULL;
1245 goto done;
1247 for (i = 0; i < VFNT_MAPS; i++)
1248 fp->vf_map_count[i] = be32toh(fh.fh_map_count[i]);
1250 glyphs = be32toh(fh.fh_glyph_count);
1251 fp->vf_width = fh.fh_width;
1252 fp->vf_height = fh.fh_height;
1254 bp->uncompressed_size = howmany(bp->width, 8) * bp->height * glyphs;
1255 size = bp->uncompressed_size;
1256 if ((fp->vf_bytes = malloc(size)) == NULL)
1257 goto free_done;
1259 rv = read(fd, fp->vf_bytes, size);
1260 if (rv < 0 || (size_t)rv != size)
1261 goto free_done;
1262 for (i = 0; i < VFNT_MAPS; i++) {
1263 if (load_mapping(fd, fp, i) != 0)
1264 goto free_done;
1266 bp->font = fp;
1269 * Release previously loaded entry. We can do this now, as
1270 * the new font is loaded. Note, there can be no console
1271 * output till the new font is in place and tem is notified.
1272 * We do need to keep fl->font_data for glyph dimensions.
1274 STAILQ_FOREACH(fl, &fonts, font_next) {
1275 if (fl->font_data->width == bp->width &&
1276 fl->font_data->height == bp->height)
1277 continue;
1279 if (fl->font_data->font != NULL) {
1280 for (i = 0; i < VFNT_MAPS; i++)
1281 free(fl->font_data->font->vf_map[i]);
1283 /* Unset vf_bytes pointer in tem. */
1284 if (tems.ts_font.vf_bytes ==
1285 fl->font_data->font->vf_bytes) {
1286 tems.ts_font.vf_bytes = NULL;
1288 free(fl->font_data->font->vf_bytes);
1289 free(fl->font_data->font);
1290 fl->font_data->font = NULL;
1291 fl->font_data->uncompressed_size = 0;
1292 fl->font_flags = FONT_AUTO;
1296 /* free the uncompressed builtin font data in tem. */
1297 free(tems.ts_font.vf_bytes);
1298 tems.ts_font.vf_bytes = NULL;
1300 done:
1301 close(fd);
1302 return (bp);
1304 free_done:
1305 for (i = 0; i < VFNT_MAPS; i++)
1306 free(fp->vf_map[i]);
1307 free(fp->vf_bytes);
1308 free(fp);
1309 bp = NULL;
1310 goto done;
1314 struct name_entry {
1315 char *n_name;
1316 SLIST_ENTRY(name_entry) n_entry;
1319 SLIST_HEAD(name_list, name_entry);
1321 /* Read font names from index file. */
1322 static struct name_list *
1323 read_list(char *fonts)
1325 struct name_list *nl;
1326 struct name_entry *np;
1327 char buf[PATH_MAX];
1328 int fd, len;
1330 fd = open(fonts, O_RDONLY);
1331 if (fd < 0)
1332 return (NULL);
1334 nl = malloc(sizeof (*nl));
1335 if (nl == NULL) {
1336 close(fd);
1337 return (nl);
1340 SLIST_INIT(nl);
1341 while ((len = fgetstr(buf, sizeof (buf), fd)) > 0) {
1342 np = malloc(sizeof (*np));
1343 if (np == NULL) {
1344 close(fd);
1345 return (nl); /* return what we have */
1347 np->n_name = strdup(buf);
1348 if (np->n_name == NULL) {
1349 free(np);
1350 close(fd);
1351 return (nl); /* return what we have */
1353 SLIST_INSERT_HEAD(nl, np, n_entry);
1355 close(fd);
1356 return (nl);
1360 * Read the font properties and insert new entry into the list.
1361 * The font list is built in descending order.
1363 static bool
1364 insert_font(char *name)
1366 struct font_header fh;
1367 struct fontlist *fp, *previous, *entry, *next;
1368 size_t size;
1369 ssize_t rv;
1370 int fd;
1371 char *font_name;
1373 fd = open(name, O_RDONLY);
1374 if (fd < 0)
1375 return (false);
1376 rv = read(fd, &fh, sizeof (fh));
1377 close(fd);
1378 if (rv < 0 || (size_t)rv != sizeof (fh))
1379 return (false);
1381 if (memcmp(fh.fh_magic, FONT_HEADER_MAGIC, sizeof (fh.fh_magic)) != 0)
1382 return (false);
1384 font_name = strdup(name);
1385 if (font_name == NULL)
1386 return (false);
1389 * If we have an entry with the same glyph dimensions, just replace
1390 * the file name. We only support unique dimensions.
1392 STAILQ_FOREACH(entry, &fonts, font_next) {
1393 if (fh.fh_width == entry->font_data->width &&
1394 fh.fh_height == entry->font_data->height) {
1395 free(entry->font_name);
1396 entry->font_name = font_name;
1397 return (true);
1401 fp = calloc(sizeof (*fp), 1);
1402 if (fp == NULL) {
1403 free(font_name);
1404 return (false);
1406 fp->font_data = calloc(sizeof (*fp->font_data), 1);
1407 if (fp->font_data == NULL) {
1408 free(font_name);
1409 free(fp);
1410 return (false);
1412 fp->font_name = font_name;
1413 fp->font_flags = FONT_AUTO;
1414 fp->font_load = load_font;
1415 fp->font_data->width = fh.fh_width;
1416 fp->font_data->height = fh.fh_height;
1418 if (STAILQ_EMPTY(&fonts)) {
1419 STAILQ_INSERT_HEAD(&fonts, fp, font_next);
1420 return (true);
1423 previous = NULL;
1424 size = fp->font_data->width * fp->font_data->height;
1426 STAILQ_FOREACH(entry, &fonts, font_next) {
1427 /* Should fp be inserted before the entry? */
1428 if (size >
1429 entry->font_data->width * entry->font_data->height) {
1430 if (previous == NULL) {
1431 STAILQ_INSERT_HEAD(&fonts, fp, font_next);
1432 } else {
1433 STAILQ_INSERT_AFTER(&fonts, previous, fp,
1434 font_next);
1436 return (true);
1438 next = STAILQ_NEXT(entry, font_next);
1439 if (next == NULL ||
1440 size > next->font_data->width * next->font_data->height) {
1441 STAILQ_INSERT_AFTER(&fonts, entry, fp, font_next);
1442 return (true);
1444 previous = entry;
1446 return (true);
1449 static int
1450 font_set(struct env_var *ev __unused, int flags __unused, const void *value)
1452 struct fontlist *fl, *tmp;
1453 char *eptr;
1454 unsigned long x = 0, y = 0;
1457 * Attempt to extract values from "XxY" string. In case of error,
1458 * we have unmaching glyph dimensions and will just output the
1459 * available values.
1461 if (value != NULL) {
1462 x = strtoul(value, &eptr, 10);
1463 if (*eptr == 'x')
1464 y = strtoul(eptr + 1, &eptr, 10);
1466 STAILQ_FOREACH(fl, &fonts, font_next) {
1467 if (fl->font_data->width == x && fl->font_data->height == y)
1468 break;
1470 if (fl != NULL) {
1471 /* Reset any FONT_MANUAL flag. */
1472 STAILQ_FOREACH(tmp, &fonts, font_next)
1473 tmp->font_flags = FONT_AUTO;
1475 fl->font_flags = FONT_MANUAL;
1476 /* Trigger tem update. */
1477 tems.update_font = true;
1478 plat_cons_update_mode(-1);
1479 return (CMD_OK);
1482 printf("Available fonts:\n");
1483 STAILQ_FOREACH(fl, &fonts, font_next) {
1484 printf(" %dx%d\n", fl->font_data->width,
1485 fl->font_data->height);
1487 return (CMD_OK);
1490 void
1491 autoload_font(void)
1493 struct name_list *nl;
1494 struct name_entry *np;
1496 nl = read_list("/boot/fonts/fonts.dir");
1497 if (nl == NULL)
1498 return;
1500 while (!SLIST_EMPTY(nl)) {
1501 np = SLIST_FIRST(nl);
1502 SLIST_REMOVE_HEAD(nl, n_entry);
1503 if (insert_font(np->n_name) == false)
1504 printf("failed to add font: %s\n", np->n_name);
1505 free(np->n_name);
1506 free(np);
1509 unsetenv("screen-font");
1510 env_setenv("screen-font", EV_VOLATILE, NULL, font_set, env_nounset);
1511 /* Trigger tem update. */
1512 tems.update_font = true;
1513 plat_cons_update_mode(-1);
1516 COMMAND_SET(load_font, "loadfont", "load console font from file", command_font);
1518 static int
1519 command_font(int argc, char *argv[])
1521 int i, rc = CMD_OK;
1522 struct fontlist *fl;
1523 bitmap_data_t *bd;
1525 if (argc > 2) {
1526 printf("Usage: loadfont [file.fnt]\n");
1527 return (CMD_ERROR);
1530 if (argc == 2) {
1531 char *name = argv[1];
1533 if (insert_font(name) == false) {
1534 printf("loadfont error: failed to load: %s\n", name);
1535 return (CMD_ERROR);
1538 bd = load_font(name);
1539 if (bd == NULL) {
1540 printf("loadfont error: failed to load: %s\n", name);
1541 return (CMD_ERROR);
1544 /* Get the font list entry and mark it manually loaded. */
1545 STAILQ_FOREACH(fl, &fonts, font_next) {
1546 if (strcmp(fl->font_name, name) == 0)
1547 fl->font_flags = FONT_MANUAL;
1549 tems.update_font = true;
1550 plat_cons_update_mode(-1);
1551 return (CMD_OK);
1554 if (argc == 1) {
1556 * Walk entire font list, release any loaded font, and set
1557 * autoload flag. If the font list is empty, the tem will
1558 * get the builtin default.
1560 STAILQ_FOREACH(fl, &fonts, font_next) {
1561 if (fl->font_data->font != NULL) {
1562 /* Note the tem is releasing font bytes */
1563 for (i = 0; i < VFNT_MAPS; i++)
1564 free(fl->font_data->font->vf_map[i]);
1565 free(fl->font_data->font);
1566 fl->font_data->font = NULL;
1567 fl->font_data->uncompressed_size = 0;
1568 fl->font_flags = FONT_AUTO;
1571 tems.update_font = true;
1572 plat_cons_update_mode(-1);
1574 return (rc);