1. 简介

本文介绍在非liteos-m内核的系统上,保留原生系统的相关功能,提供一种基于openharmony ArkUI Lite图形界面的轻量设备适配方案。
本方案使用 OpenHarmony v4.1 Release版本源码。

img

  1. ArkUI Lite提供了两种接口: Native API和 JS API, 可以基于这两种接口开发应用,简化应用开发过程,并提供应用安装管理功能。
  2. 在框架层提供了组件,多语言,2D图形库,图像库和轻量渲染框架能。

2. 适配方案

  • 不改变原生代码的编译系统和打包系统。
  • 使用原生代码的交叉编译工具链编译OpenHarmony为静态库,将静态库集成到原生代码中。
  • OpenHarmony中不编译LiteOS-M内核,使用原生代码的RTOS内核。
  • 原生代码中新增适配代码,以提供OpenHarmony需要的接口。

3. 原生系统集成

3.1 Input适配

Input事件上报过程

img

Input事件分发流程如上:

  • UI_Task调用InputDeviceManager::Callback接口
  • InputDeviceManager调用InputDevice::ProcessEvent获取驱动上报的信息。
  • InputDevice::Read读取驱动上报的信息。
  • InputDevice::DispatchEvent分发上报的信息到UI界面,处理不同的事件类型。

Input事件类型

UIKit支持处理下列事件类型:

  • CancelEvent
  • ClickEvent
  • DragEvent
  • KeyEvent
  • LongPressEvent
  • PressEvent
  • ReleaseEvent
  • RotateEvent

InputDevice类型

UIKit提供3中不同的InputDevice类型,分别处理分发相应的事件。

img

其中:

  • KeyInputDevice分发KeyEvent。
  • RotateInputDevice触发RotateEvent事件。
  • PointerInputDevice分发PressEvent、ReleaseEvent、DragStartEvent、DragEvent、DragEndEvent、LongPressEvent、CancelEvent。

适配过程

根据要支持的事件类型,继承实现相应InputDevice的Read接口,获取硬件上报的坐标信息。

参考bes2600的适配

// device\soc\bestechnic\bes2600\liteos_m\components\ui\touch_input.h
class TouchInput : public PointerInputDevice {
public:
    TouchInput(){}
    virtual ~TouchInput() {}
    static TouchInput* GetInstance();
    bool Read(DeviceData& data) override;

private:
    bool IsValidTouchMsg(struct touch_msg *msg);
    bool init = false;
    DevHandle handle = nullptr;
    struct touch_msg msg = {0};
};

// 获取硬件上报的坐标信息
bool TouchInput::Read(DeviceData &data)
{
    // merge msg with the same event
    struct touch_msg tmp[TOUCH_MSG_MAX] = {0};
    int i = 0;
    while (i < TOUCH_MSG_MAX && TouchRead(this->handle, &tmp[i], 0) == 0) {
        if (!IsValidTouchMsg(&tmp[i]))
            break;

        if (tmp[i].event == TOUCH_EVENT_MOVE) {
            tmp[i].event = TOUCH_EVENT_DOWN;
        }
        i++;
        if (IsValidTouchMsg(&this->msg)) {
            if (tmp[i - 1].event != this->msg.event) {
                break;
            }
        } else {
            if (i > 1 && tmp[i - 1].event != tmp[0].event)
                break;
        }
    }
    if (i <= 1) {
        data.point.x = this->msg.x;
        data.point.y = this->msg.y;
        data.state = (this->msg.event == TOUCH_EVENT_DOWN) ? STATE_PRESS : STATE_RELEASE;
    } else if (i <= TOUCH_MSG_MAX) {
        data.point.x = tmp[i - 2].x;
        data.point.y = tmp[i - 2].y;
        data.state = (tmp[i - 2].event == TOUCH_EVENT_DOWN) ? STATE_PRESS : STATE_RELEASE;
    }
    // GRAPHIC_LOGD("Touch {%d, %d} %d", data.point.x, data.point.y, data.state);
    if (i >= 1 && i <= TOUCH_MSG_MAX && IsValidTouchMsg(&tmp[i - 1]))
        this->msg = tmp[i - 1];

    return false;
}

添加InputDevice设备到InputDeviceManager管理类中,当task执行时,则处理相关input事件。

TouchInput* touch = TouchInput::GetInstance();
InputDeviceManager::GetInstance()->Add(touch);

3.2 Display适配

分辨率和刷新率设置

参考foundation\graphic\graphic_utils_lite\interfaces\innerkits\graphic_config.h, 其中默认屏幕分辨率如下:

/* Resolution width of a graphics display screen. The default value is <b>454</b>. */
#ifndef HORIZONTAL_RESOLUTION
#define HORIZONTAL_RESOLUTION                           454
#endif

/* Resolution height of a graphics display screen. The default value is <b>454</b>. */
#ifndef VERTICAL_RESOLUTION
#define VERTICAL_RESOLUTION                             454
#endif // VERTICAL_RESOLUTION

默认刷新间隔为16ms。

/* Default task execution period. The default value is <b>16</b> ms. */
static constexpr uint8_t DEFAULT_TASK_PERIOD = 16;

渲染和送显接口适配

BaseGfxEngine提供绘制和送显的接口。

参考foundation\arkui\ui_lite\interfaces\innerkits\engines\gfx\gfx_engine_manager.h,其接口声明如下:

class BaseGfxEngine : public HeapBase {
public:
    virtual void DrawArc(BufferInfo& dst,
                         ArcInfo& arcInfo,
                         const Rect& mask,
                         const Style& style,
                         OpacityType opacity,
                         uint8_t cap) = 0;
    virtual void MemoryBarrier() {}
    virtual void DrawLine(BufferInfo& dst,
                          const Point& start,
                          const Point& end,
                          const Rect& mask,
                          int16_t width,
                          ColorType color,
                          OpacityType opacity) = 0;
    virtual void DrawLetter(BufferInfo& gfxDstBuffer,
                            const uint8_t* fontMap,
                            const Rect& fontRect,
                            const Rect& subRect,
                            const uint8_t fontWeight,
                            const ColorType& color,
                            const OpacityType opa) = 0;
    virtual void DrawCubicBezier(BufferInfo& dst,
                                 const Point& start,
                                 const Point& control1,
                                 const Point& control2,
                                 const Point& end,
                                 const Rect& mask,
                                 int16_t width,
                                 ColorType color,
                                 OpacityType opacity) = 0;
    virtual void 
        DrawRect(BufferInfo& dst, const Rect& rect, const Rect& dirtyRect, const Style& style, OpacityType opacity) = 0;
    virtual void DrawTransform(BufferInfo& dst,
                               const Rect& mask,
                               const Point& position,
                               ColorType color,
                               OpacityType opacity,
                               const TransformMap& transMap,
                               const TransformDataInfo& dataInfo) = 0;
    virtual void ClipCircle(const ImageInfo* info, float x, float y, float radius) = 0;
    virtual void Blit(BufferInfo& dst,
                      const Point& dstPos,
                      const BufferInfo& src,
                      const Rect& subRect,
                      const BlendOption& blendOption) = 0;
    virtual void Fill(BufferInfo& dst, const Rect& fillArea, const ColorType color, const OpacityType opacity) = 0;
    virtual void DrawPath(BufferInfo& dst,
                          void* param,
                          const Paint& paint,
                          const Rect& rect,
                          const Rect& invalidatedArea,
                          const Style& style) = 0;
    virtual void FillPath(BufferInfo& dst,
                          void* param,
                          const Paint& paint,
                          const Rect& rect,
                          const Rect& invalidatedArea,
                          const Style& style) = 0;
    virtual uint8_t* AllocBuffer(uint32_t size, uint32_t usage) = 0;
    virtual void FreeBuffer(uint8_t* buffer, uint32_t usage) = 0;
    virtual BufferInfo* GetFBBufferInfo()
    virtual void AdjustLineStride(BufferInfo& info) {}
    virtual void Flush(const Rect& flushRect) {}
    virtual uint16_t GetScreenWidth();
    virtual uint16_t GetScreenHeight();
    virtual void SetScreenShape(ScreenShape screenShape);
    virtual ScreenShape GetScreenShape();
    static BaseGfxEngine* GetInstance();
    static void InitGfxEngine(BaseGfxEngine* gfxEngine);
protected:
    static BaseGfxEngine* baseEngine_;
    uint16_t screenWidth_ = HORIZONTAL_RESOLUTION;
    uint16_t screenHeight_ = VERTICAL_RESOLUTION;
    ScreenShape screenShape_ = RECTANGLE;
};
  • 渲染接口

如果硬件支持GPU或者其他硬件加速绘制,则实现以下接口,提高渲染速度。

virtual void DrawArc(...) = 0;
virtual void DrawLine(...) = 0;
virtual void DrawLetter(...) = 0;
virtual void DrawCubicBezier(...) = 0;
virtual void DrawRect(...) = 0;
virtual void DrawTransform(...) = 0;
virtual void ClipCircle(...) = 0;
virtual void Blit(...) = 0;
virtual void Fill(...) = 0;
virtual void DrawPath(...) = 0;
virtual void FillPath(...) = 0;

UIKit默认提供SoftEngine接口,支持CPU绘制。参考实现:

foundation\arkui\ui_lite\interfaces\innerkits\engines\gfx\soft_engine.h
  • 送显接口

针对屏幕的适配,需对接display已经设备,实现以下接口:

virtual uint8_t* AllocBuffer(uint32_t size, uint32_t usage) = 0;
virtual void Flush(const Rect& flushRect) {}
  • 参考实现

参考bes2600的适配:

device\soc\bestechnic\bes2600\liteos_m\components\ui
components/ui/
├── BUILD.gn
├── display_device.cpp    # 送显接口实现
├── display_device.h
├── fbdev.cpp            # 驱动封装
├── fbdev.h
├── touch_input.cpp        # 触摸板适配
├── touch_input.h
├── ui_main.cpp            # ui task
└── ui_main.h

bes2600默认使用CPU绘制,继承SoftEngine,只需重载实现Flush和GetFBBufferInfo接口即可。

class DisplayDevice : public SoftEngine
{
public:
    DisplayDevice() {}
    virtual ~DisplayDevice() {}
    static DisplayDevice *GetInstance();

    // 送显
    void Flush(const Rect&) override;
    // 获取显示完成的buffer,以进行绘制
    BufferInfo *GetFBBufferInfo() override;
    void UpdateFBBuffer();

private:
    bool isRegister_ = false;
    void *fbAddr = nullptr;
};

创建UiMainTask,启动图形界面。

static void *UiMainTask(void *arg)
{
    (void)arg;

    (void)pthread_setname_np(pthread_self(), "UiMain");
    osDelay(UI_MAIN_TASK_DELAY);
    InitUiKit();
    RunApp();

#if ENABLE_FPS_SUPPORT
    uint32_t start = HALTick::GetInstance().GetTime();
#endif
    while (1) {
#if FULLY_RENDER
        DisplayDevice::GetInstance()->UpdateFBBuffer();
#endif
        uint32_t temp = HALTick::GetInstance().GetTime();
        TaskManager::GetInstance()->TaskHandler();
        uint32_t time = HALTick::GetInstance().GetElapseTime(temp);
        if (time < DEFAULT_TASK_PERIOD) {
            osDelay(DEFAULT_TASK_PERIOD - time);
        }
#if ENABLE_FPS_SUPPORT
        if (HALTick::GetInstance().GetElapseTime(start) >= ONE_SECOND) {
            GRAPHIC_LOGD("%u fps", (uint32_t)RenderManager::GetInstance().GetFPS());
            start = HALTick::GetInstance().GetTime();
        }
#endif
    }
    return nullptr;
}

3.3 其他接口的集成

3.3.1 对接hilog输出接口

参考base\hiviewdfx\hilog_lite\interfaces\native\kits\hilog_lite\hiview_log.h,hilog_lite接口定义如下:

#define HILOG_DEBUG(mod, fmt, ...) HiLogPrintf(mod, HILOG_LV_DEBUG, FUN_ARG_NUM(__VA_ARGS__), fmt, ##__VA_ARGS__)
#define HILOG_INFO(mod, fmt, ...) HiLogPrintf(mod, HILOG_LV_INFO, FUN_ARG_NUM(__VA_ARGS__), fmt, ##__VA_ARGS__)
#define HILOG_WARN(mod, fmt, ...) HiLogPrintf(mod, HILOG_LV_WARN, FUN_ARG_NUM(__VA_ARGS__), fmt, ##__VA_ARGS__)
#define HILOG_ERROR(mod, fmt, ...) HiLogPrintf(mod, HILOG_LV_ERROR, FUN_ARG_NUM(__VA_ARGS__), fmt, ##__VA_ARGS__)
#define HILOG_FATAL(mod, fmt, ...) HiLogPrintf(mod, HILOG_LV_FATAL, FUN_ARG_NUM(__VA_ARGS__), fmt, ##__VA_ARGS__)

可以根据hilog_lite框架实现日志的打印存储等功能,可以不依赖hilog_lite的实现,直接重新定义HiLogPrintf的实现。

3.3.2 新增LiteOS接口

Arkui Lite系统中调用的LiteOS-M的相关接口:

  • LOS_CurTaskIDGet

  • LOS_TaskCreate

  • LOS_TaskDelete

  • LOS_TaskLock

  • LOS_TaskUnlock

  • LOS_TickCountGet

  • LOS_BinarySemCreate

  • LOS_SemCreate

  • LOS_SemDelete

  • LOS_SemPost

  • LOS_SemPend

  • LOS_MuxCreate

  • LOS_MuxDelete

  • LOS_MuxPend

  • LOS_MuxPost

  • osThreadGetArgument

  • OhosMalloc

  • OhosFree

  • RefreshAllServiceTimeStamp

3.3.3 文件路径设置

在bundle_framework_lite子系统中,app的安装卸载需依赖文件系统支持,需实现相关接口,并设置文件路径。

参考foundation\bundlemanager\bundle_framework_lite\services\bundlemgr_lite\include\bundle_common.h

const char INSTALL_PATH[] = "user/ace/run";
const char DATA_PATH[] = "user/ace/data";
const char SYSTEM_BUNDLE_PATH[] = "system/ace/sys";
const char THIRD_SYSTEM_BUNDLE_PATH[] = "system/ace/vendor";
.....

3.3.4 RTOS相关的升级

在原生代码中升级模块或新增OpenHarmony调用的接口。如集成ui_lite到FreeRTOS系统中,可从以下步骤入手分析:

  • 升级RTOS

由于不支持OpenHarmony中的底层接口,FreeRTOS内核从版本10.0.1升级到版本v10.3.1,适配其HAL层和 OSI层接口。
FreeRTOS源码来自于官网地址: https://github.com/FreeRTOS/FreeRTOS

  • 新增CMSIS接口

原生系统kernel中新增cmsis目录,包含CMSIS的源码和头文件。
CMSIS源码来自于开源项目CMSIS-FreeRTOS,地址: https://github.com/ARM-software/CMSIS-FreeRTOS
修改部分源码适配系统源码,并修改kernel的CMakeFile.txt,将源码中的cmsis_os2.c文件加入编译。

4. 参考文档

一种OpenHarmony轻量系统适配方案 - 文章 OpenHarmony开发者论坛

docs: OpenHarmony documentation | OpenHarmony开发者文档 - Gitee.com

Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐