【UEFI实战】UEFI图形显示(从像素到字符)

news2024/11/15 10:31:57

GraphicsConsoleDxe

在【UEFI实战】UEFI图形显示(显示驱动)中已经介绍了如何使用显卡驱动安装的GOP来进行像素级别的显示,本文介绍的内容是对像素的包装,最终变成普通字符的输出。

模块简述

本模块将原本的GOP包装成了字符输出的显示模块。GOP输出的最小单元是像素,而经过包装之后,文本模式输出的最小单元变成了一个个的字符。

本模块也是一个UEFI Driver Model,对应的EFI_DRIVER_BINDING_PROTOCOL

EFI_DRIVER_BINDING_PROTOCOL  gGraphicsConsoleDriverBinding = {
  GraphicsConsoleControllerDriverSupported,
  GraphicsConsoleControllerDriverStart,
  GraphicsConsoleControllerDriverStop,
  0xa,
  NULL,
  NULL
};

Supported函数就是一系列Protocol的判断,包括:

  • gEfiGraphicsOutputProtocolGuidgEfiUgaDrawProtocolGuid,不过前面一节已经介绍过,gEfiGraphicsOutputProtocolGuid会被安装,它会被优先使用。
  • gEfiDevicePathProtocolGuid,对于一个PCI的显卡,这个也是会安装的。
  • gEfiHiiDatabaseProtocolGuidgEfiHiiFontProtocolGuid,两者是UEFI用户界面的基本接口,要使用显示输出,它们也是必须的。尤其是gEfiHiiFontProtocolGuid,它是像素和字符之间的桥梁,负责完成两者的转换。

Start函数的一个主要工作是初始化如下的结构体:

//
// Graphics Console Device Private Data template
//
GRAPHICS_CONSOLE_DEV  mGraphicsConsoleDevTemplate = {
  GRAPHICS_CONSOLE_DEV_SIGNATURE,		// 一个标识,SIGNATURE_32 ('g', 's', 't', 'o')
  (EFI_GRAPHICS_OUTPUT_PROTOCOL *)NULL,	// 显卡初始化模块安装的Protocol,实际绘制字体的接口
  (EFI_UGA_DRAW_PROTOCOL *)NULL,		// 如果上一个存在,这个就可以不需要了
  {	// EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL基本接口,也是后续UEFI代码操作的接口,完成字符输出及相关操作
    GraphicsConsoleConOutReset,
    GraphicsConsoleConOutOutputString,
    GraphicsConsoleConOutTestString,
    GraphicsConsoleConOutQueryMode,
    GraphicsConsoleConOutSetMode,
    GraphicsConsoleConOutSetAttribute,
    GraphicsConsoleConOutClearScreen,
    GraphicsConsoleConOutSetCursorPosition,
    GraphicsConsoleConOutEnableCursor,
    (EFI_SIMPLE_TEXT_OUTPUT_MODE *)NULL	// 它指向的就是下面的结构体
  },
  {	// EFI_SIMPLE_TEXT_OUTPUT_MODE
    0,		// QueryMode()和SetMode()支持的模式数,由于没有初始化,所以现在默认是0
    -1,		// 当前的模式,-1表示的是无效的模式
    EFI_TEXT_ATTR (EFI_LIGHTGRAY,          EFI_BLACK),	// 当前字体输出属性,包括前景色和背景色
    0,		// 光标列位置
    0,		// 光标行位置
    FALSE	// 光标是否可见
  },
  (GRAPHICS_CONSOLE_MODE_DATA *)NULL,	// 这个看上去是一个指针,但是实际上是一个数组,表示当前文本显示支持的模式
  (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *)NULL	// 像素点的属性,其实是一个蓝绿红表示的值
};

对应的Start函数的流程:

0
非0
获取底层绘画接口GOP
获取PCD表示的以像素为单位的水平和垂直的分辨率
判断分辨率的值
通过GOP获取最大支持的分辨率
使用获取到的分辨率来设置文本模式, 对应函数InitializeGraphicsConsoleTextMode
判断非0分辨率是否支持
使用默认的分辨率800*600
使用该非0分辨率
打印获取到的分辨率
使用该分辨率以及对应的模式号来完成后续初始化
最终安装gEfiSimpleTextOutProtocolGuid

整个流程主要是以下的几个步骤:

  • 获取可用的分辨率;
  • 初始化文本模式信息;
  • 安装最终的Protocol。

文本模式

跟GOP类似,文本显示也有它自己的模式(这里以模式表示GOP的模式,以文本模式表示SimpleTextOutProtocol的模式),不过相比前者这个要简单许多:

/**
  @par Data Structure Description:
  Mode Structure pointed to by Simple Text Out protocol.
**/
typedef struct {
  ///
  /// The number of modes supported by QueryMode () and SetMode ().
  ///
  INT32    MaxMode;

  //
  // current settings
  //

  ///
  /// The text mode of the output device(s).
  ///
  INT32      Mode;
  ///
  /// The current character output attribute.
  ///
  INT32      Attribute;
  ///
  /// The cursor's column.
  ///
  INT32      CursorColumn;
  ///
  /// The cursor's row.
  ///
  INT32      CursorRow;
  ///
  /// The cursor is currently visible or not.
  ///
  BOOLEAN    CursorVisible;
} EFI_SIMPLE_TEXT_OUTPUT_MODE;

具体参数的意义可以看英文说明或者前面的注释。GOP中的显示是以像素为单位的,而文本模式中显示是以一个小的矩形为单位的,每个矩形包含一个字符,这里就通过Column和Row来指定位置。前面的两个结构体成员MaxModeMode跟GOP中的模式类似,也表示有多个,并指定了其中的一个,这也说明还存在另外的一个数组来表示所有支持的文本模式,它们描述了像素到字符矩形之间的关系,其结构体表示如下:

typedef struct {
  UINTN     Columns;		// 表示文本模式对应的列数
  UINTN     Rows;			// 表示文本模式对应的行数
  INTN      DeltaX;			// 文本显示相对于GOP显示的水平偏移
  INTN      DeltaY;			// 文本显示相对于GOP显示的垂直偏移
  UINT32    GopWidth;		// GOP显示的宽度,即水平像素个数
  UINT32    GopHeight;		// GOP显示的高度,即垂直像素个数
  UINT32    GopModeNumber;	// 对应GOP模式的Index
} GRAPHICS_CONSOLE_MODE_DATA;

这对应到前文提到的数组:

//
// Graphics Console Device Private Data template
//
GRAPHICS_CONSOLE_DEV  mGraphicsConsoleDevTemplate = {
  // 略
  (GRAPHICS_CONSOLE_MODE_DATA *)NULL,	// 这个看上去是一个指针,但是实际上是一个数组,表示当前文本显示支持的模式
  // 略
};

通过上述两个结构体就构成了完整的文本模式,并与底层GOP模式绑定。

下面的示例代码显示了当前支持的所有文本模式:

VOID
ShowTextMode (
  IN  EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *Stp
  )
{
  EFI_STATUS  Status = EFI_ABORTED;
  UINTN       Index = 0;
  UINTN       Col = 0;
  UINTN       Row = 0;

  Print (L"Current Text Mode:\r\n");
  Print (L" MaxMode       : %d\r\n", Stp->Mode->MaxMode);
  Print (L" Mode          : %d\r\n", Stp->Mode->Mode);
  Print (L" Attribute     : 0x%x\r\n", Stp->Mode->Attribute);
  Print (L" CursorColumn  : %d\r\n", Stp->Mode->CursorColumn);
  Print (L" CursorRow     : %d\r\n", Stp->Mode->CursorRow);
  Print (L" CursorVisible : %d\r\n", Stp->Mode->CursorVisible);

  Print (L"Supported Text Mode:\r\n");
  for (Index = 0; Index < Stp->Mode->MaxMode; Index++) {
    Status = Stp->QueryMode (Stp, Index, &Col, &Row);
    if (EFI_ERROR (Status)) {
      Print (L"%d. Not supported.\r\n", Index);
      continue;
    }
    Print (L"%d. Column: %d, Row: %d\r\n", Index, Col, Row);
  }
}

得到的结果:

在这里插入图片描述

这里有几点需要说明:

  1. 相比于GOP的QueryMode()函数,文本模式下的QueryMode()只能查看行和列两个参数,而底层的GRAPHICS_CONSOLE_MODE_DATA并不能直接查看,如果想要获取这些信息,可以通过在本模块增加DEBUG信息来查看:
ModeData 0
Columns       : 80
Rows          : 25
DeltaX        : 320
DeltaY        : 162
GopWidth      : 1280
GopHeight     : 800
GopModeNumber : 0
ModeData 1
Columns       : 0
Rows          : 0
DeltaX        : 0
DeltaY        : 0
GopWidth      : 1280
GopHeight     : 800
GopModeNumber : 0
ModeData 2
Columns       : 100
Rows          : 31
DeltaX        : 240
DeltaY        : 105
GopWidth      : 1280
GopHeight     : 800
GopModeNumber : 0
ModeData 3
Columns       : 128
Rows          : 40
DeltaX        : 128
DeltaY        : 20
GopWidth      : 1280
GopHeight     : 800
GopModeNumber : 0
ModeData 4
Columns       : 160
Rows          : 42
DeltaX        : 0
DeltaY        : 1
GopWidth      : 1280
GopHeight     : 800
GopModeNumber : 0
  1. 这里可以看到总共支持5个,至于为什么会是这么几个,会在后面的章节中说明。
  2. mode命令是查看当前支持的文本模式,可以看到少了几个,这个的原因之后再分析。

最后再说明一下文本模式中的Attribute这个结构体成员,它的值可以分为两类,一类是颜色,跟GOP的像素颜色对应;另一类表示文本是窄体还是宽体,它对应的一个示例就是英文是窄体而中文是宽体,它们的不同导致了绘制一个字体需要的像素的不同。后面一节会进一步介绍。Attribute目前的取值:

//
// EFI Console Colours
//
#define EFI_BLACK         0x00
#define EFI_BLUE          0x01
#define EFI_GREEN         0x02
#define EFI_CYAN          (EFI_BLUE | EFI_GREEN)
#define EFI_RED           0x04
#define EFI_MAGENTA       (EFI_BLUE | EFI_RED)
#define EFI_BROWN         (EFI_GREEN | EFI_RED)
#define EFI_LIGHTGRAY     (EFI_BLUE | EFI_GREEN | EFI_RED)
#define EFI_BRIGHT        0x08
#define EFI_DARKGRAY      (EFI_BLACK | EFI_BRIGHT)
#define EFI_LIGHTBLUE     (EFI_BLUE | EFI_BRIGHT)
#define EFI_LIGHTGREEN    (EFI_GREEN | EFI_BRIGHT)
#define EFI_LIGHTCYAN     (EFI_CYAN | EFI_BRIGHT)
#define EFI_LIGHTRED      (EFI_RED | EFI_BRIGHT)
#define EFI_LIGHTMAGENTA  (EFI_MAGENTA | EFI_BRIGHT)
#define EFI_YELLOW        (EFI_BROWN | EFI_BRIGHT)
#define EFI_WHITE         (EFI_BLUE | EFI_GREEN | EFI_RED | EFI_BRIGHT)

//
// Macro to accept color values in their raw form to create
// a value that represents both a foreground and background
// color in a single byte.
// For Foreground, and EFI_* value is valid from EFI_BLACK(0x00) to
// EFI_WHITE (0x0F).
// For Background, only EFI_BLACK, EFI_BLUE, EFI_GREEN, EFI_CYAN,
// EFI_RED, EFI_MAGENTA, EFI_BROWN, and EFI_LIGHTGRAY are acceptable
//
// Do not use EFI_BACKGROUND_xxx values with this macro.
//
#define EFI_TEXT_ATTR(Foreground, Background)  ((Foreground) | ((Background) << 4))

#define EFI_BACKGROUND_BLACK      0x00
#define EFI_BACKGROUND_BLUE       0x10
#define EFI_BACKGROUND_GREEN      0x20
#define EFI_BACKGROUND_CYAN       (EFI_BACKGROUND_BLUE | EFI_BACKGROUND_GREEN)
#define EFI_BACKGROUND_RED        0x40
#define EFI_BACKGROUND_MAGENTA    (EFI_BACKGROUND_BLUE | EFI_BACKGROUND_RED)
#define EFI_BACKGROUND_BROWN      (EFI_BACKGROUND_GREEN | EFI_BACKGROUND_RED)
#define EFI_BACKGROUND_LIGHTGRAY  (EFI_BACKGROUND_BLUE | EFI_BACKGROUND_GREEN | EFI_BACKGROUND_RED)

//
// We currently define attributes from 0 - 7F for color manipulations
// To internally handle the local display characteristics for a particular character,
// Bit 7 signifies the local glyph representation for a character.  If turned on, glyphs will be
// pulled from the wide glyph database and will display locally as a wide character (16 X 19 versus 8 X 19)
// If bit 7 is off, the narrow glyph database will be used.  This does NOT affect information that is sent to
// non-local displays, such as serial or LAN consoles.
//
#define EFI_WIDE_ATTRIBUTE  0x80

这里详细说明了当前支持的字体类型和颜色。

从GOP模式到文本模式

从GOP模式到文本模式,首先需要解决的就是前面章节中提到的像素转换成行列的问题,它主要在InitializeGraphicsConsoleTextMode()函数中完成:

EFI_STATUS
InitializeGraphicsConsoleTextMode (
  IN UINT32                       HorizontalResolution,
  IN UINT32                       VerticalResolution,
  IN UINT32                       GopModeNumber,
  OUT UINTN                       *TextModeCount,
  OUT GRAPHICS_CONSOLE_MODE_DATA  **TextModeData
  )

该函数的入参是像素构成的长和宽以及对应的GOP模式,出参是对应的可适配的文本模式。下面简单介绍该函数的实现,以此来了解像素到行列的转换关系。

  1. 首先根据水平和垂直的像素个数来确定最大支持的列和行,这个计算关系如下:
  MaxColumns = HorizontalResolution / EFI_GLYPH_WIDTH;	// 8
  MaxRows    = VerticalResolution / EFI_GLYPH_HEIGHT;	// 19

这里的8和19的来源是UEFI规范中定义的窄体字,后面会进一步说明。按照UEFI规范的要求,最小支持的行列必须要满足80x25的要求,所以紧接着有以下的判断:

  //
  // According to UEFI spec, all output devices support at least 80x25 text mode.
  //
  ASSERT ((MaxColumns >= 80) && (MaxRows >= 25));

以当前OVMF的示例,像素是1280x800,所以MaxColumns = 160,MaxRows = 42,它会被作为支持全屏的行列,因此会放到mGraphicsConsoleModeData中,这样实际代码中支持的所有行列是这样的:

GRAPHICS_CONSOLE_MODE_DATA  mGraphicsConsoleModeData[] = {
  { 100, 31 },  //  800 x 600
  { 128, 40 },  // 1024 x 768
  { 160, 42 },  // 1280 x 800
  { 240, 56 },  // 1920 x 1080
  // 上面的都是硬编码的
  //
  // New modes can be added here.
  // The last entry is specific for full screen mode.
  //
  { 160,   42  }	// 代码根据像素实际生成的
};

但是mGraphicsConsoleModeData并不是最终文本模式能够支持的行列(显然上表有重复),这里还需要进行一些处理。

  1. 第一个处理是增加几个默认的行列,主要是80x25和80x50两种情况:
  //
  // Mode 0 and mode 1 is for 80x25, 80x50 according to UEFI spec.
  //
  ValidCount = 0;

  NewModeBuffer[ValidCount].Columns       = 80;
  NewModeBuffer[ValidCount].Rows          = 25;
  NewModeBuffer[ValidCount].GopWidth      = HorizontalResolution;
  NewModeBuffer[ValidCount].GopHeight     = VerticalResolution;
  NewModeBuffer[ValidCount].GopModeNumber = GopModeNumber;
  NewModeBuffer[ValidCount].DeltaX        = (HorizontalResolution - (NewModeBuffer[ValidCount].Columns * EFI_GLYPH_WIDTH)) >> 1;
  NewModeBuffer[ValidCount].DeltaY        = (VerticalResolution - (NewModeBuffer[ValidCount].Rows * EFI_GLYPH_HEIGHT)) >> 1;
  ValidCount++;

  if ((MaxColumns >= 80) && (MaxRows >= 50)) {
    NewModeBuffer[ValidCount].Columns = 80;
    NewModeBuffer[ValidCount].Rows    = 50;
    NewModeBuffer[ValidCount].DeltaX  = (HorizontalResolution - (80 * EFI_GLYPH_WIDTH)) >> 1;
    NewModeBuffer[ValidCount].DeltaY  = (VerticalResolution - (50 * EFI_GLYPH_HEIGHT)) >> 1;
  }

  NewModeBuffer[ValidCount].GopWidth      = HorizontalResolution;
  NewModeBuffer[ValidCount].GopHeight     = VerticalResolution;
  NewModeBuffer[ValidCount].GopModeNumber = GopModeNumber;
  ValidCount++;

这里需要关注DeltaXDeltaY的值,这样做是为了保证即使文本模式不能全屏,但是也能够占据在屏幕的正中间。

还需要注意那个if判断,显然这个条件在现在的OVMF环境下是满足要求的,这就导致了第二项中的行列都不会被赋值,都是默认的0。这比较奇怪,目前还不确定原因。

  1. 之后的代码开始处理mGraphicsConsoleModeData中的文本模式,判断是否有效的点有:a)行列不能超过最大值,所以{ 240, 56 }这一项就不满足要求,b)不能有重复项,这里主要是防止最后一项{MaxColumns,MaxRows}跟前面的冲突,其它的都是代码写死的,它们之间应该不存在重复的可能。

最终可用的行列在DEBUG信息中显示如下:

Graphics - Mode 0, Column = 80, Row = 25
Graphics - Mode 1, Column = 0, Row = 0
Graphics - Mode 2, Column = 100, Row = 31
Graphics - Mode 3, Column = 128, Row = 40
Graphics - Mode 4, Column = 160, Row = 42

这也跟前面的分析是一致的。

到这里已经将像素都分割成了行列,并且可以看到最适配的是160x42的情况,但是从前面的例子以及mode命令打印的情况来看,实际使用的却是100x31,这个由于不影响本节内容的说明,所以暂时不关注。

后面需要关注的是,当像素分割成一个个矩形之后(窄体对应矩形是8x19个像素),该如何在这个矩形中表示一个字符,这个可以通过下图很明显地看出来:

在这里插入图片描述

图中的最小的矩形就是一个个的像素,而其中的圆形可以通过不同的颜色来描述(注意不是真的有圆形),这样就可以勾勒出一个字。通过观察上图,就可以通过GOP来“写”出一个字符,这里以A这个字符为例,只需要将上图中有圆形的那些位置对应的像素改成其它颜色,就可以显示出来,它们对应的位置是:

  UINT8                         BltIndex[NARROW_HEIGHT * NARROW_WIDTH] = {
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 1, 0, 0, 0, 0,
    0, 0, 1, 1, 1, 0, 0, 0,
    0, 1, 1, 0, 1, 1, 0, 0,
    1, 1, 0, 0, 0, 1, 1, 0,
    1, 1, 0, 0, 0, 1, 1, 0,
    1, 1, 0, 0, 0, 1, 1, 0,
    1, 1, 0, 0, 0, 1, 1, 0,
    1, 1, 1, 1, 1, 1, 1, 0,
    1, 1, 0, 0, 0, 1, 1, 0,
    1, 1, 0, 0, 0, 1, 1, 0,
    1, 1, 0, 0, 0, 1, 1, 0,
    1, 1, 0, 0, 0, 1, 1, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0
  };

这里用数组模拟一个8x19的像素,只要值为1,就用其它颜色表示,这样就构造出了一个A字,下面是剩余的代码:

  for (Index = 0; Index < NARROW_HEIGHT * NARROW_WIDTH; Index++) {
    if (BltIndex[Index]) {
      Blt[Index].Red = 0xFF;
    }
  }

  Gop->Blt (
        Gop,
        Blt,
        EfiBltBufferToVideo,
        0,
        0,
        0,
        0,
        Width,
        Height,
        0
        );

最终得到的结果:

在这里插入图片描述

可以看到左上角就显示了一个A。不过这只是一个简单的例子,如何输出字符可以直接调用EFI_SIMPLE_TEXT_OUTPUT_PROTOCOLOutputString()函数即可,它的实现是:

EFI_STATUS
EFIAPI
GraphicsConsoleConOutOutputString (
  IN  EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  *This,
  IN  CHAR16                           *WString
  )

除了一些特殊字符和特殊情况(比如回车键意味着换行,而换行时刚好已经在显示的最后一行则要全局地向上平移,这个时候参数EfiBltVideoToVideo就能派上用处)的处理有所不同,其它的处理都在如下的函数:

/**
  Draw Unicode string on the Graphics Console device's screen.

  @param  This                  Protocol instance pointer.
  @param  UnicodeWeight         One Unicode string to be displayed.
  @param  Count                 The count of Unicode string.

  @retval EFI_OUT_OF_RESOURCES  If no memory resource to use.
  @retval EFI_UNSUPPORTED       If no Graphics Output protocol and UGA Draw
                                protocol exist.
  @retval EFI_SUCCESS           Drawing Unicode string implemented successfully.

**/
EFI_STATUS
DrawUnicodeWeightAtCursorN (
  IN  EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  *This,
  IN  CHAR16                           *UnicodeWeight,
  IN  UINTN                            Count
  )

而将字符转换成像素,则依赖于EFI_HII_FONT_PROTOCOL

    Status = mHiiFont->StringToImage (
                         mHiiFont,
                         EFI_HII_IGNORE_IF_NO_GLYPH | EFI_HII_DIRECT_TO_SCREEN | EFI_HII_IGNORE_LINE_BREAK,
                         String,
                         FontInfo,
                         &Blt,
                         This->Mode->CursorColumn * EFI_GLYPH_WIDTH + Private->ModeData[This->Mode->Mode].DeltaX,
                         This->Mode->CursorRow * EFI_GLYPH_HEIGHT + Private->ModeData[This->Mode->Mode].DeltaY,
                         NULL,
                         NULL,
                         NULL
                         );

不仅仅是转换,该函数也完成了最终的输出。关于HII Font的实现,将在后续进一步介绍。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/697768.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MySQL数据库——主从复制优化及读写分离

目录 主从复制优化主服务器配置优化redo log&#xff08;事务日志&#xff09;的刷盘策略从服务器配置优化 搭建MySQL读写分离 主从复制优化 主服务器配置优化 vim /etc/my.cnf expire_logs_days7 #设置二进制日志文件过期时间&#xff0c;默认值为0&#xff0c;表示logs不…

Vue2生命周期

Vue2生命周期 1、概念2、生命周期过程2.1 流程图示2.2 三阶段 3、钩子函数详解3.1 beforeCreate() 创建前3.2 created()创建后3.3 beforeMount() 挂载前3.4 mounted()挂载完成3.5 beforeUpdate() 更新前3.6 updated() 更新后3.7 beforeDestroy() 销毁前3.8 destroyed() 销毁完成…

在群晖上安装运行Airflow

本文是应网友 &#xff1a; 要求折腾的&#xff1b; 什么是 Airflow &#xff1f; Apache Airflow 是一个开源平台&#xff0c;用于开发、调度和监控面向批处理的工作流。Airflow 的可扩展 Python 框架使您能够构建与几乎任何技术连接的工作流。Web 界面有助于管理工作流程的状…

RabbitMQ灵活运用,怎么理解五种消息模型

RabbitMQ灵活运用&#xff0c;怎么理解五种消息模型 简介一、AMQP协议二、交换机类型与默认交换机1. 交换机的四种类型2. 默认交换机 三、五种模式速览1. 一对一简单模式2. work模式&#xff08;轮询&#xff09;3. 发布/订阅模式4. 路由模式&#xff08;自称direct模式&#x…

Android 应用自动开启辅助(无障碍)功能并使用辅助(无障碍)功能

一.背景 由于最近的项目需要开启无障碍功能然后实现对应的功能需求,但是由于需求是需要安装后就开启辅助功能,不要在繁琐的在设置中开启辅助功能,所以需要如何在应用中开启辅助功能。 二.前提条件 将普通应用转换成系统应用,然后将系统的framework.jar包放到应用中并且可以…

vscode配置task.json和launch.json启动调试

首先说一下参考博文&#xff1a; 文章标题“VScode 调试教程 tasks.json和launch.json的设置&#xff08;超详细&#xff09;” 地址&#xff1a;https://blog.csdn.net/qq_59084325/article/details/125662393 官方文档太官方&#xff0c;其他人的文档也看过&#xff0c;单独…

微信小程序浏览docx,pdf等文件在线预览使用wx.openDocument

wx.downloadFile({ url: fileUrl,//pdf链接success(res) {wx.openDocument({ //打开文档filePath: res.tempFilePath,fileType: "pdf",//文档类型showMenu: true,success: function (res) {wx.showToast({title: 打开文档成功,})},fail: function (res) {wx.showToas…

Stable Diffusion使用“面部修复”时报TypeError: ‘NoneType‘ object is not subscriptable错

问题 Stable Diffusion使用“面部修复”时报TypeError: ‘NoneType’ object is not subscriptable错 解决方案 下载【detection_Resnet50_Final.pth】和【parsing_parsenet.pth】到【repositories\CodeFormer\weights\facelib】目录下&#xff0c;并重新运行项目即可。 ht…

Unity 基础之 URP 项目创建\项目转URP Pipline

Unity 基础之 URP 项目创建\项目转URP Pipline 目录 Unity 基础之 URP 项目创建\项目转URP Pipline 一、简单介绍 二、创建 URP 项目 三、工程项目转 URP 一、简单介绍 Unity中的一些基础知识点&#xff0c;方便日后查阅。 Unity游戏开发中&#xff0c;这里简单介绍如何创…

Methodot低代码开发教程——玩转表格增删改查分页

目录 1、背景介绍 2、连接数据源 2.1 新增数据源 2.2 填写数据源信息 3、表格数据的展示 3.1 新增查询&#xff0c;编写查询语句 3.2 使用表格组件 3.3 同步数据源与表格列名 4、表格的数据新增 4.1 新增查询&#xff0c;编写新增语句 4.2 表格配置新增一行&#xff0…

华为云专家出品《从零到一•Python图像处理入门》电子书

《华为云云享.书库》系列电子书来啦&#xff01; 本系列电子书旨在帮助开发者成长&#xff0c;汇聚华为云内外部专家技术精华制作而成。 本书《从零到一•Python图像处理》是该系列电子书第3部。 我们在华为开发者即将到来之际&#xff0c;开放电子书免费下载。 点击下方链接…

JVM探究

JVM探究 请谈谈你对JVM的理解&#xff1f;java8虚拟机和之前的变化、更新&#xff1f;什么是OOM&#xff0c;栈溢出StackOverFlowError&#xff1f;怎么分析&#xff1f;JVM的常用调优参数有哪些&#xff1f;内存快照如何 抓取&#xff0c;怎么分析Dump文件&#xff1f;知道吗…

Unity VR开发教程 OpenXR+XR Interaction Toolkit(八)手指触控 Poke Interaction

文章目录 &#x1f4d5;教程说明&#x1f4d5;XR Poke Interactor&#x1f4d5;与 UI 进行触控交互⭐添加 Tracked Device Graphic Raycaster 和 XR UI Input Module 让 UI 可被交互 &#x1f4d5;与物体进行交互⭐XR Simple Interactable⭐XR Poke Filter 往期回顾&#xff1a…

【Linux进程】进程的基本概念 {PCB结构体,进程表,Linux中的task_struct,查看进程,获取进程PID,使用fork创建子进程}

一、进程的基本概念 1.1 什么是进程&#xff1f; 进程是计算机中正在运行的程序的实例。它是操作系统进行资源分配和调度的基本单位。每个进程都有自己的内存空间、代码、数据和执行状态。进程可以独立运行&#xff0c;相互之间不会干扰。操作系统可以同时运行多个进程&#…

vue表格实现一个简单的合并单元格功能

用的是vue2ant-design-vue 但是vue3或者element-ui也是同理 先上效果 需要后端的数据将相同id的放在一起 否则也会有问题 例如&#xff1a; this.list [{id: 1,name: 舟山接收站,...}{id: 2,name: 舟山接收站碳中和LNG,...},{id: 2,name: 舟山接收站碳中和LNG,...} ]// th…

Redis7【⑤ Redis 发布 订阅】

Redis发布和订阅 本章了解即可&#xff0c;命令可以不用敲。 Redis 发布和订阅&#xff08;Publish/Subscribe&#xff0c;简称 Pub/Sub&#xff09;是一种消息传递模式&#xff0c;用于在 Redis 中实现消息的发布和订阅。 在 Redis 中&#xff0c;发布者&#xff08;Publi…

maven打包所有依赖,对外提供sdk.jar

maven打包所有依赖 <properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compile.source>1.8</maven.compile.source><maven.compile.target>1.8</maven.compile.target></properties><…

Swin Transformer训练报错问题

1. 训练遇到报错问题 &#xff08;1&#xff09;mportError: cannot import name _pil_interp from timm.data.transforms 原因&#xff1a; timm.data.transforms里面没有_pil_interp&#xff0c;只有str_to_pil_interp、_str_to_pil_interpolation、_pil_interpolation_to_s…

rancher 节点重启无感发布

这里设置 时间 为120s &#xff0c;保证 新节点起来后&#xff0c;和 老节点并行2分钟后再剔除&#xff0c;老节点

el-select修改样式

目录 准备 修改placeholder颜色 修改右侧箭头 修改圆角边框 准备 <el-select v-model"goodsId" clearable placeholder"请选择" :popper-append-to-body"false"><el-option v-for"item in kindList" :key"item.value…