天天看點

嵌入式Linux(awtk-linux-fb)适配滑鼠滾動事件

目錄

    • 一、前言
    • 二、awtk-linux-fb
    • 三、如何支援滑鼠滾輪事件
      • 3.1 判斷滑鼠驅動是否支援滾輪事件
      • 3.2 找到正确的滑鼠裝置檔案名
      • 3.3 适配滑鼠滾輪事件
        • 3.3.1 捕捉滾輪事件并将其轉化後分發給AWTK
        • 3.3.2 适配其他嵌入式 Linux 平台或其他事件

一、前言

近期嘗試了在嵌入式 Linux 上适配滑鼠滾輪事件,其難點主要在于從滑鼠驅動檔案中擷取滾輪事件,本文做個記錄。

注意:本文基于 AWTK 針對 arm-linux 平台的移植适配滑鼠滾輪事件。

AWTK 是為嵌入式系統開發的 GUI 引擎庫,GitHub 位址:https://github.com/zlgopen/awtk。

awtk-linux-fb 是 AWTK 針對 arm-linux 平台的移植,GitHub 位址:https://github.com/zlgopen/awtk-linux-fb。

二、awtk-linux-fb

由于不同的嵌入式 linux 硬體其滑鼠驅動檔案對滾輪事件的表現不同,甚至部分硬體的驅動并不支援滾輪事件,是以,滑鼠滾輪事件并不作為通用功能點直接加到 awtk-linux-fb 的源碼裡面。

此處,以樹莓派為例,講解如何讓 awtk-linux-fb 支援滑鼠滾輪事件,其他嵌入式 Linux 平台操作類似。

三、如何支援滑鼠滾輪事件

3.1 判斷滑鼠驅動是否支援滾輪事件

在終端中執行以下指令檢視輸入驅動的具體屬性:

cat /proc/bus/input/devices
           

例如,樹莓派中滑鼠驅動的屬性如下,其中有 “B: REL = xxx” 的字樣,即表示該滑鼠驅動支援相對坐标事件(EV_REL),其中包括滑鼠滾輪事件:

相對坐标事件 (REL) 的類型通常有 REL_X、REL_Y、REL_WHEEL,分别表示滑鼠在 x、y 方向上移動和滑鼠滾輪滾動。
I: Bus=0003 Vendor=093a Product=2510 Version=0111
N: Name="PixArt USB Optical Mouse"
P: Phys=usb-3f980000.usb-1.1.3/input0
S: Sysfs=/devices/platform/soc/3f980000.usb/usb1/1-1/1-1.1/1-1.1.3/1-1.1.3:1.0/0003:093A:2510.0004/input/input4
U: Uniq=
H: Handlers=mouse0 event3
B: PROP=0
B: EV=17
B: KEY=70000 0 0 0 0 0 0 0 0
B: REL=903
B: MSC=10
           

3.2 找到正确的滑鼠裝置檔案名

根據上述滑鼠驅動檔案屬性,可知滑鼠裝置檔案有兩個,分别是 /dev/input/mouse0 和 /dev/input/event3,通過 “hexdump /dev/input/xxx” 指令找到能正确識别滾輪事件的滑鼠裝置檔案名。

例如,在終端中執行以下指令:

然後,滾動滑鼠滾輪,有輸出相關資料,并且向上滾動與向下滾動時,其輸出資訊不同,即表明該滑鼠裝置檔案能正确識别滾輪事件。

也可通過指令 “cat /dev/input/event3 | od -t x1 -w16” 來檢視指定格式的輸出,od 指令的使用方法請上網搜尋。

3.3 适配滑鼠滾輪事件

awtk-linux-fb 在樹莓派中,滑鼠消息處理線程是 awtk-port/input_thread/mouse_thread.c 檔案中的 input_dispatch_one_event() 函數,是以隻需在該函數中捕捉滑鼠驅動檔案的滾輪事件,轉化成 AWTK 的 EVT_WHEEL 事件,再調用 input_dispatch() 函數将其分發給 AWTK 即可。

3.3.1 捕捉滾輪事件并将其轉化後分發給AWTK

滑鼠裝置檔案傳回的滾輪事件 type 為 EV_REL,其中 code 對應 REL_WHEEL(垂直滾動)和 REL_HWHEEL(水準滾輪),事件的 value 對應前後左右的方向和滾動的次數(機關)。

Linux 驅動事件詳情請參閱:https://www.kernel.org/doc/Documentation/input/event-codes.txt。

此處僅以 REL_WHEEL 垂直滾動為例,向上滾動時,value 為正數;向下滾動時,value 為負數。滾動一次,value的絕對值即為 1 ,滾動兩次,絕對值即為 2。

假設每滾動一次,滾動的距離為 #define MIN_WHEEL_DELTA 12,那麼捕捉滾輪事件,并将其轉化為 EVT_WHEEL 事件分發給 AWTK 的的代碼如下:

// awtk-port/input_thread/mouse_thread.c
...
/* 滾動一次的最小距離 */
#define MIN_WHEEL_DELTA 12

/* 設定滑鼠滾輪事件 */
static ret_t input_dispatch_set_mouse_wheel_event(run_info_t* info, event_queue_req_t* req, int32_t dy) {
  if (dy > 0) {
    req->wheel_event.dy = tk_max(MIN_WHEEL_DELTA, dy);
  } else if (dy < 0) {
    req->wheel_event.dy = tk_min(-MIN_WHEEL_DELTA, dy);
  }
  req->event.type = EVT_WHEEL;
  return RET_OK;
}

static ret_t input_dispatch_one_event(run_info_t* info) {
  ...
  else if (ret == sizeof(info->data.e)) {
    switch (info->data.e.type) {
      ...
      case EV_REL: {
        switch (info->data.e.code) {
          ...
          /* 從滑鼠裝置檔案中捕捉滑鼠滾輪事件 */
          case REL_WHEEL: {
            int32_t dy = MIN_WHEEL_DELTA * info->data.e.value;
            /* 将該事件轉化為 AWTK 的 EVT_WHEEL 事件 */
            input_dispatch_set_mouse_wheel_event(info, req, dy);
            break;
          }
          ...
        }

        if (req->event.type == EVT_NONE) {
          req->event.type = EVT_POINTER_MOVE;
        }

        break;
      }
      case EV_SYN: {
        switch (req->event.type) {
          case EVT_KEY_UP:
          case EVT_KEY_DOWN:
          case EVT_CONTEXT_MENU:
          case EVT_POINTER_DOWN:
          case EVT_POINTER_MOVE:
          case EVT_POINTER_UP:
          case EVT_WHEEL: {    /* 将轉化後的滾輪事件(EVT_WHEEL)分發給AWTK */
            return input_dispatch(info);
      ...
    }
  }

  return RET_OK;
}
...
           

3.3.2 适配其他嵌入式 Linux 平台或其他事件

其他嵌入式 Linux 平台操作支援滑鼠滾輪事件以及其他事件的步驟一樣:

1. 從裝置檔案中捕捉目标事件;

2. 将目标事件轉化為AWTK的事件;

3. 分發給AWTK進行處理。

需要注意的是若新增其他滑鼠事件,還需修改 awtk-prot/main_loop_linux.c 中的 input_dispatch_to_main_loop() 函數,根據消息類型設定 event_queue_req_t 中的 event.size 屬性,代碼如下:
// awtk-port/main_loop_linux.c
...
ret_t input_dispatch_to_main_loop(void* ctx, const event_queue_req_t* evt, const char* msg) {
  main_loop_t* l = (main_loop_t*)ctx;
  event_queue_req_t event = *evt;
  event_queue_req_t* e = &event;

  if (l != NULL && l->queue_event != NULL) {
    switch (e->event.type) {
      case EVT_KEY_DOWN:
      case EVT_KEY_UP:
      case EVT_KEY_LONG_PRESS: {
        e->event.size = sizeof(e->key_event);
        break;
      }
      case EVT_CONTEXT_MENU:
      case EVT_POINTER_DOWN:
      case EVT_POINTER_MOVE:
      case EVT_POINTER_UP: {
        e->event.size = sizeof(e->pointer_event);
        break;
      }
      /* 設定的滾輪消息 event.size 屬性 */
      case EVT_WHEEL: {
        e->event.size = sizeof(e->wheel_event);
        break;
      }
      default:
        break;
    }

    main_loop_queue_event(l, e);
    input_dispatch_print(ctx, e, msg);
  } else {
    return RET_BAD_PARAMS;
  }
  return RET_OK;
}
...
           

繼續閱讀