做一个 UEFI 下的 EasyX 图形库(4)

这个版本加入了Easxy的键盘的函数。经过研究,我放弃了在 EasyX 中的鼠标支持,因此多次测试下来感觉一般的UEFI 使用环境下并不能很好的支持键盘。

编写测试代码实现下面的功能:

按键功能
方向键 ↑↓←→画笔从当前位置向该方向移动 8 像素,并画出一段连线(像涂鸦板)
空格之后画的线换一种颜色(红→绿→青→黄→品红→白循环)
ESC退出图形界面,回到文本控制台并打印 EasyX UEFI Demo 结束…
任意移动后屏幕底部实时显示 Pen: (x, y) vkcode=0x..

完整代码:

#include <Uefi.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiLib.h>
#include <Library/PrintLib.h>
#include "easyx.h"

/**
  程序入口。
**/
EFI_STATUS
EFIAPI
UefiMain (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS  Status;
  INT32       W;
  INT32       H;
  INT32       PenX;
  INT32       PenY;
  IMAGE       Sprite  = { 0, 0, NULL };   // 使用前必须清零
  IMAGE       Grabbed = { 0, 0, NULL };   // getimage 会读取 Buffer 判断是否需释放
  ExMessage   Msg;
  BOOLEAN     Running;
  UINTN       ColorIndex;
  CHAR8       StatusBuf[96];
  CONST COLORREF  Palette[] = {
    LIGHTRED, LIGHTGREEN, LIGHTCYAN, YELLOW, LIGHTMAGENTA, WHITE
  };

  //
  // 初始化绘图设备(跟随 GOP 最大分辨率模式)。
  //
  Status = initgraph (0, 0);
  if (EFI_ERROR (Status)) {
    Print (L"initgraph 失败: %r\n", Status);
    return Status;
  }

  W = getwidth ();
  H = getheight ();

  //
  // 批量绘图,减少刷新次数。
  //
  BeginBatchDraw ();

  //
  // 背景填充深蓝。
  //
  setbkcolor (RGB (16, 16, 48));
  cleardevice ();

  //
  // 直线:不同颜色/线宽/线型。
  //
  setlinecolor (WHITE);
  setlinestyle (PS_SOLID, 1);
  line (20, 20, W - 20, 20);

  setlinecolor (LIGHTGREEN);
  setlinestyle (PS_SOLID, 3);
  line (20, 40, W - 20, 60);

  setlinecolor (YELLOW);
  setlinestyle (PS_DASH, 1);
  line (20, 80, W - 20, 80);

  setlinecolor (LIGHTCYAN);
  setlinestyle (PS_DOT, 1);
  line (20, 100, W - 20, 100);

  //
  // 矩形。
  //
  setlinestyle (PS_SOLID, 1);
  setlinecolor (LIGHTRED);
  rectangle (40, 140, 200, 260);

  setfillcolor (RGB (40, 120, 200));
  setlinecolor (WHITE);
  fillrectangle (240, 140, 400, 260);

  setfillcolor (LIGHTMAGENTA);
  solidrectangle (440, 140, 600, 260);

  //
  // 圆。
  //
  setlinecolor (WHITE);
  circle (110, 360, 60);

  setfillcolor (RGB (220, 160, 40));
  setlinecolor (WHITE);
  fillcircle (320, 360, 60);

  setfillcolor (LIGHTGREEN);
  solidcircle (520, 360, 60);

  //
  // 椭圆。
  //
  setlinecolor (LIGHTCYAN);
  ellipse (40, 440, 260, 540);

  setfillcolor (RGB (200, 60, 120));
  setlinecolor (WHITE);
  fillellipse (300, 440, 560, 540);

  //
  // 文字(P2):不同颜色/大小/背景模式。
  //
  settextcolor (WHITE);
  settextstyle (16, 8);
  outtextxy (620, 150, "EasyX for UEFI");

  settextcolor (YELLOW);
  settextstyle (24, 0);            // 高 24,宽按比例
  outtextxy (620, 180, "Hello, GOP!");

  settextcolor (LIGHTGREEN);
  settextstyle (32, 16);
  setbkmode (TRANSPARENT);
  outtextxy (620, 220, "0123456789");

  settextcolor (BLACK);
  setbkcolor (LIGHTGRAY);
  setbkmode (OPAQUE);
  settextstyle (16, 8);
  outtextxy (620, 270, " OPAQUE text mode ");

  setbkmode (TRANSPARENT);
  settextcolor (LIGHTRED);
  settextstyle (20, 10);
  outtextxy (620, 320, "The quick brown fox");
  outtextxy (620, 345, "jumps over lazy dog.");

  //
  // 图像(P3)。
  //
  // 1) 用 SetWorkingImage 把绘图重定向到内存图像,绘制一个“精灵”。
  //
  if (!EFI_ERROR (CreateImage (&Sprite, 80, 80))) {
    SetWorkingImage (&Sprite);

    setbkcolor (RGB (20, 20, 20));
    cleardevice ();
    setfillcolor (RGB (240, 200, 40));
    solidcircle (40, 40, 34);
    setfillcolor (BLACK);
    solidcircle (28, 32, 5);
    solidcircle (52, 32, 5);
    setlinecolor (BLACK);
    setlinestyle (PS_SOLID, 2);
    line (24, 54, 56, 54);

    SetWorkingImage (NULL);   // 恢复到屏幕

    // 2) 直接覆盖绘制到屏幕多个位置。
    putimage (620, 400, &Sprite);
    putimage (710, 400, &Sprite);

    // 3) 透明贴图:跳过图像四角的深灰背景色。
    putimagetrans (800, 400, &Sprite, RGB (20, 20, 20));
  }

  //
  // 4) getimage:抓取屏幕左上角一块区域,复制到别处。
  //
  if (!EFI_ERROR (getimage (&Grabbed, 40, 140, 160, 120))) {
    putimage (620, 500, &Grabbed);
    DeleteImage (&Grabbed);
  }

  DeleteImage (&Sprite);

  //
  // 键盘操作提示。
  //
  setbkmode (TRANSPARENT);
  settextcolor (WHITE);
  settextstyle (16, 8);
  outtextxy (20, H - 24, "Arrow keys=draw, Space=color, ESC=exit");

  //
  // 一次性刷新并结束批量绘图。
  //
  EndBatchDraw ();

  //
  // 键盘交互循环(P4):方向键移动画笔绘线。
  //
  PenX       = W / 2;
  PenY       = H / 2;
  Running    = TRUE;
  ColorIndex = 0;
  while (Running) {
    INT32  NewX;
    INT32  NewY;

    getmessage (&Msg, EX_KEY);

    if (Msg.message != WM_KEYDOWN) {
      continue;
    }

    NewX = PenX;
    NewY = PenY;

    switch (Msg.vkcode) {
      case VK_LEFT:   NewX -= 8; break;
      case VK_RIGHT:  NewX += 8; break;
      case VK_UP:     NewY -= 8; break;
      case VK_DOWN:   NewY += 8; break;
      case VK_SPACE:  ColorIndex++; break;
      case VK_ESCAPE: Running = FALSE; break;
      default:        break;
    }

    if (!Running) {
      break;
    }

    //
    // 夹取到屏幕范围内。
    //
    if (NewX < 0) {
      NewX = 0;
    }

    if (NewY < 0) {
      NewY = 0;
    }

    if (NewX > W - 1) {
      NewX = W - 1;
    }

    if (NewY > H - 1) {
      NewY = H - 1;
    }

    //
    // 从旧位置到新位置画一段线。
    //
    setlinecolor (Palette[ColorIndex % (sizeof (Palette) / sizeof (Palette[0]))]);
    setlinestyle (PS_SOLID, 2);
    line (PenX, PenY, NewX, NewY);
    PenX = NewX;
    PenY = NewY;

    //
    // 底部显示当前画笔位置与按键(先用背景色清除旧文字)。
    //
    setfillcolor (RGB (16, 16, 48));
    solidrectangle (20, H - 48, 420, H - 28);
    AsciiSPrint (
      StatusBuf,
      sizeof (StatusBuf),
      "Pen: (%d, %d)  vkcode=0x%02x",
      PenX,
      PenY,
      Msg.vkcode
      );
    setbkmode (TRANSPARENT);
    settextcolor (LIGHTCYAN);
    settextstyle (16, 8);
    outtextxy (20, H - 46, StatusBuf);
  }

  closegraph ();

  //
  // 恢复文本控制台。
  //
  gST->ConOut->ClearScreen (gST->ConOut);
  Print (L"EasyX UEFI Demo 结束。分辨率: %d x %d\n", W, H);

  return EFI_SUCCESS;
}

运行结果(中间的线段是运行时我使用方向键绘制的)

有兴趣的朋友可以试试。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注