| // Copyright 2016 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <framebuffer.h> |
| #include <lib/gfx-font/gfx-font.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <xefi.h> |
| |
| #include "logo.h" |
| |
| static efi_graphics_output_protocol* fb_get_gop() { |
| static efi_graphics_output_protocol* gop = NULL; |
| if (!gop) { |
| gBS->LocateProtocol(&GraphicsOutputProtocol, NULL, (void**)&gop); |
| } |
| return gop; |
| } |
| |
| uint32_t get_gfx_mode() { |
| efi_graphics_output_protocol* gop = fb_get_gop(); |
| return gop->Mode->Mode; |
| } |
| |
| uint32_t get_gfx_max_mode() { |
| efi_graphics_output_protocol* gop = fb_get_gop(); |
| return gop->Mode->MaxMode; |
| } |
| |
| uint32_t get_gfx_hres() { |
| efi_graphics_output_protocol* gop = fb_get_gop(); |
| efi_graphics_output_mode_information* mode_info; |
| size_t info_size = 0; |
| efi_status status = gop->QueryMode(gop, gop->Mode->Mode, &info_size, &mode_info); |
| if (EFI_ERROR(status)) { |
| return 0; |
| } |
| return mode_info->HorizontalResolution; |
| } |
| |
| uint32_t get_gfx_vres() { |
| efi_graphics_output_protocol* gop = fb_get_gop(); |
| efi_graphics_output_mode_information* mode_info; |
| size_t info_size = 0; |
| efi_status status = gop->QueryMode(gop, gop->Mode->Mode, &info_size, &mode_info); |
| if (EFI_ERROR(status)) { |
| return 0; |
| } |
| return mode_info->VerticalResolution; |
| } |
| |
| void set_gfx_mode(uint32_t mode) { |
| efi_graphics_output_protocol* gop = fb_get_gop(); |
| if (!gop) |
| return; |
| if (mode >= gop->Mode->MaxMode) { |
| printf("invalid framebuffer mode: %u\n", mode); |
| return; |
| } |
| efi_status s = gop->SetMode(gop, mode); |
| if (EFI_ERROR(s)) { |
| printf("could not set mode: %s\n", xefi_strerror(s)); |
| } |
| gBS->Stall(1000); |
| gSys->ConOut->SetCursorPosition(gSys->ConOut, 0, 0); |
| } |
| |
| void set_gfx_mode_from_cmdline(const char* fbres) { |
| if (!fbres) |
| return; |
| efi_graphics_output_protocol* gop = fb_get_gop(); |
| if (!gop) |
| return; |
| |
| uint32_t hres = 0; |
| hres = atol(fbres); |
| |
| char* x = strchr(fbres, 'x'); |
| if (!x) |
| return; |
| x++; |
| |
| uint32_t vres = 0; |
| vres = atol(x); |
| if (!hres || !vres) |
| return; |
| |
| uint32_t max_mode = gop->Mode->MaxMode; |
| |
| for (uint32_t i = 0; i < max_mode; i++) { |
| efi_graphics_output_mode_information* mode_info; |
| size_t info_size = 0; |
| efi_status status = gop->QueryMode(gop, i, &info_size, &mode_info); |
| if (EFI_ERROR(status)) { |
| printf("Could not retrieve mode %d: %s\n", i, xefi_strerror(status)); |
| continue; |
| } |
| |
| if (mode_info->HorizontalResolution == hres && mode_info->VerticalResolution == vres) { |
| set_gfx_mode(i); |
| return; |
| } |
| } |
| printf("Could not find framebuffer mode %ux%u; using default mode = %ux%u\n", hres, vres, |
| gop->Mode->Info->HorizontalResolution, gop->Mode->Info->VerticalResolution); |
| gBS->Stall(5000000); |
| } |
| |
| void print_fb_modes() { |
| efi_graphics_output_protocol* gop = fb_get_gop(); |
| uint32_t max_mode = gop->Mode->MaxMode; |
| uint32_t cur_mode = gop->Mode->Mode; |
| for (uint32_t i = 0; i < max_mode; i++) { |
| efi_graphics_output_mode_information* mode_info; |
| size_t info_size = 0; |
| efi_status status = gop->QueryMode(gop, i, &info_size, &mode_info); |
| if (EFI_ERROR(status)) |
| continue; |
| printf(" (%u) %u x %u%s\n", i, mode_info->HorizontalResolution, mode_info->VerticalResolution, |
| i == cur_mode ? " (current)" : ""); |
| } |
| } |
| |
| static efi_graphics_output_blt_pixel font_white = { |
| .Red = 0xFF, |
| .Green = 0xFF, |
| .Blue = 0xFF, |
| }; |
| |
| static efi_graphics_output_blt_pixel font_black = { |
| .Red = 0x0, |
| .Green = 0x0, |
| .Blue = 0x0, |
| }; |
| |
| static efi_graphics_output_blt_pixel font_fuchsia = { |
| .Red = 0xFF, |
| .Green = 0x0, |
| .Blue = 0x80, |
| }; |
| |
| void draw_logo() { |
| efi_graphics_output_protocol* gop = fb_get_gop(); |
| if (!gop) |
| return; |
| |
| const uint32_t h_res = gop->Mode->Info->HorizontalResolution; |
| const uint32_t v_res = gop->Mode->Info->VerticalResolution; |
| |
| // Blank the screen, removing vendor UEFI logos |
| gop->Blt(gop, &font_black, EfiBltVideoFill, 0, 0, 0, 0, h_res, v_res, 0); |
| |
| efi_graphics_output_blt_pixel* tmp; |
| unsigned sz = sizeof(efi_graphics_output_blt_pixel) * logo_width * logo_height; |
| if (EFI_ERROR(gBS->AllocatePool(EfiLoaderData, sz, (void*)&tmp))) { |
| // Draw the Fuchsia stripe on the top of the screen |
| gop->Blt(gop, &font_fuchsia, EfiBltVideoFill, 0, 0, 0, 0, h_res, v_res / 100, 0); |
| return; |
| } |
| |
| logo_load(tmp); |
| gop->Blt(gop, tmp, EfiBltBufferToVideo, 0, 0, h_res - logo_width - (h_res / 75), |
| v_res - logo_height - (v_res / 75), logo_width, logo_height, 0); |
| |
| gBS->FreePool(tmp); |
| } |
| |
| static void putchar(efi_graphics_output_protocol* gop, const gfx_font_t* font, unsigned ch, |
| unsigned x, unsigned y, unsigned scale_x, unsigned scale_y, |
| efi_graphics_output_blt_pixel* fg, efi_graphics_output_blt_pixel* bg) { |
| const uint16_t* cdata = font->data + ch * font->height; |
| unsigned fw = font->width; |
| for (unsigned i = 0; i <= font->height; i++) { |
| uint16_t xdata = *cdata++; |
| for (unsigned j = 0; j < fw; j++) { |
| gop->Blt(gop, (xdata & 1) ? fg : bg, EfiBltVideoFill, 0, 0, x + scale_x * j, y + scale_y * i, |
| scale_x, scale_y, 0); |
| xdata >>= 1; |
| } |
| } |
| } |
| |
| void draw_text(const char* text, size_t length, const fb_font* font, int x, int y) { |
| efi_graphics_output_protocol* gop = fb_get_gop(); |
| efi_graphics_output_blt_pixel* fg_color = &font_white; |
| if (!gop) |
| return; |
| |
| if (font->color != NULL) { |
| fg_color = font->color; |
| } |
| |
| size_t offset = 0; |
| size_t scale = 1; |
| for (size_t i = 0; i < length; ++i) { |
| unsigned char c = text[i]; |
| if (c > 127) |
| continue; |
| putchar(gop, font->font, c, x + offset, y, scale, scale, fg_color, &font_black); |
| offset += font->font->width * scale; |
| } |
| } |
| |
| void draw_nodename(const char* nodename) { |
| efi_graphics_output_protocol* gop = fb_get_gop(); |
| if (!gop) |
| return; |
| |
| const fb_font font = { |
| .font = &gfx_font_18x32, |
| .color = &font_white, |
| }; |
| |
| const uint32_t h_res = gop->Mode->Info->HorizontalResolution; |
| const uint32_t v_res = gop->Mode->Info->VerticalResolution; |
| size_t length = strlen(nodename); |
| draw_text(nodename, length, &font, h_res - (length + 1) * font.font->width, |
| v_res / 100 + font.font->height); |
| } |
| |
| void draw_version(const char* version) { |
| efi_graphics_output_protocol* gop = fb_get_gop(); |
| if (!gop) |
| return; |
| |
| const char* prefix = "GigaBoot 20X6 - Version"; |
| size_t prefix_len = strlen(prefix); |
| size_t version_len = strlen(version); |
| |
| const fb_font font = { |
| .font = &gfx_font_9x16, |
| .color = &font_fuchsia, |
| }; |
| |
| draw_text(prefix, prefix_len, &font, 0, 0); |
| draw_text(version, version_len, &font, (prefix_len + 1) * font.font->width, 0); |
| } |