OpenHarmony源碼解析之基于wayland的輸入系統(tǒng)
??51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)??
??https://harmonyos.51cto.com??
簡介
在之前一篇文章《OpenHarmony 多模輸入子系統(tǒng)源碼分析之事件派發(fā)流程 & 接口說明》中分析的輸入系統(tǒng)的邏輯的是基于openharmony L0系統(tǒng)的,而本篇文章是基于openharmony L2系統(tǒng)的,在L2系統(tǒng)中輸入系統(tǒng)并不是由InputManagerService, InputEventHub, InputEventDistributer來負責(zé)處理的輸入事件的,而是由第三方庫wayland來負責(zé)處理輸入事件的,所以本章內(nèi)容就是分析基于wayland協(xié)議的輸入系統(tǒng)。
輸入系統(tǒng)框架
整個輸入流程的派發(fā)過程:
kernel ->HDF->uinput -> libinput –> weston -> wayland client -> wm -> ACE -> JS應(yīng)用
當(dāng)?shù)讓佑惺录l(fā)生的時候會通過驅(qū)動給HDF, 然后通過HDI接口給uinput,然后uinput會通過注入事件的方式把事件注入到libinput中,當(dāng)libinput檢測到有事件傳上來的時候就會進行處理,處理完會給weston繼續(xù)處理和派發(fā),weston處理完后會通過wayland協(xié)議傳給wayland client端,這是一個IPC調(diào)用,wayland處理完后會繼續(xù)派發(fā)給windowmanager(簡稱wm),之后通過ACE傳給JS應(yīng)用。
輸入系統(tǒng)事件派發(fā)流程
首先在device_info.hcs和input_config.hcs配置文件中配置相應(yīng)的信息,多模輸入系統(tǒng)的HdfDeviceEventManager會在啟動的時候去bind對應(yīng)的hdf service, 然后通過RegisterReportCallback去注冊對應(yīng)的回調(diào)函數(shù)。
foundation\multimodalinput\input\uinput\hdf_device_event_manager.cpp
void HdfDeviceEventManager::ConnectHDFInit()
{
uint32_t ret = GetInputInterface(&inputInterface_);
if (ret != 0) {
HiLog::Error(LABEL, "Initialize %{public}s fail! ret is %{public}u", __func__, ret);
return;
}
if (inputInterface_ == nullptr || inputInterface_->iInputManager == nullptr) {
HiLog::Error(LABEL, "%{public}s inputInterface_ or iInputManager is NULL", __func__);
return;
}
thread_ = std::thread(&InjectThread::InjectFunc, injectThread_);
ret = inputInterface_->iInputManager->OpenInputDevice(TOUCH_DEV_ID);
if ((ret == INPUT_SUCCESS) && (inputInterface_->iInputReporter != nullptr)) {
ret = inputInterface_->iInputManager->GetInputDevice(TOUCH_DEV_ID, &iDevInfo_);
if (ret != INPUT_SUCCESS) {
HiLog::Error(LABEL, "%{public}s GetInputDevice error %{public}d", __func__, ret);
return;
}
std::unique_ptr<HdfDeviceEventDispatch> hdf = std::make_unique<HdfDeviceEventDispatch>(\
iDevInfo_->attrSet.axisInfo[ABS_MT_POSITION_X].max, iDevInfo_->attrSet.axisInfo[ABS_MT_POSITION_Y].max);
if (hdf == nullptr) {
HiLog::Error(LABEL, "%{public}s hdf is nullptr", __func__);
return;
}
callback_.EventPkgCallback = hdf->GetEventCallbackDispatch;
ret = inputInterface_->iInputReporter->RegisterReportCallback(TOUCH_DEV_ID, &callback_);
}
}
當(dāng)有事件上來的時候就會回調(diào)GetEventCallbackDispatch
foundation\multimodalinput\input\uinput\hdf_device_event_dispatch.cpp
void HdfDeviceEventDispatch::GetEventCallbackDispatch(
const EventPackage **pkgs, uint32_t count, uint32_t devIndex)
{
if (pkgs == nullptr) {
HiLog::Error(LABEL, " %{public}s fail! pkgs is nullptr", __func__);
return;
}
for (uint32_t i = 0; i < count; i++) {
if (pkgs[i] == nullptr) {
continue;
}
if ((pkgs[i]->type == 0) && (pkgs[i]->code == 0) && (pkgs[i]->value == 0)) {
InjectInputEvent injectInputSync = {injectThread_.TOUCH_SCREEN_DEVICE_ID, 0, SYN_MT_REPORT, 0};
injectThread_.WaitFunc(injectInputSync);
}
InjectInputEvent injectInputEvent = {
injectThread_.TOUCH_SCREEN_DEVICE_ID,
pkgs[i]->type,
pkgs[i]->code,
pkgs[i]->value
};
injectThread_.WaitFunc(injectInputEvent);
}
}
然后通過InjectThread::WaitFunc準(zhǔn)備對事件進行注入,在該函數(shù)中會通過notify_one來喚醒InjectFunc這個函數(shù)
foundation\multimodalinput\input\uinput\inject_thread.cpp
void InjectThread::InjectFunc() const
{
std::unique_lock<std::mutex> uniqueLock(mutex_);
while (true) {
conditionVariable_.wait(uniqueLock);
while (injectQueue_.size() > 0) {
if (injectQueue_[0].deviceId == TOUCH_SCREEN_DEVICE_ID) {
g_pTouchScreen->EmitEvent(injectQueue_[0].type, injectQueue_[0].code, injectQueue_[0].value);
} else if (injectQueue_[0].deviceId == KEYBOARD_DEVICE_ID) {
g_pKeyboard->EmitEvent(injectQueue_[0].type, injectQueue_[0].code, injectQueue_[0].value);
}
injectQueue_.erase(injectQueue_.begin());
}
}
}
void InjectThread::WaitFunc(InjectInputEvent injectInputEvent) const
{
std::lock_guard<std::mutex> lockGuard(mutex_);
injectQueue_.push_back(injectInputEvent);
conditionVariable_.notify_one();
}
最終會調(diào)用VirtualDevice::EmitEvent, 在該函數(shù)中會將事件寫入到uinput的設(shè)備文件中。
foundation\multimodalinput\input\uinput\virtual_device.cpp
fd_ = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
bool VirtualDevice::EmitEvent(uint16_t type, uint16_t code, uint32_t value) const
{
struct input_event event {};
event.type = type;
event.code = code;
event.value = value;
#ifndef __MUSL__
gettimeofday(&event.time, NULL);
#endif
if (write(fd_, &event, sizeof(event)) < static_cast<ssize_t>(sizeof(event))) {
HiLog::Error(LABEL, "Event write failed %{public}s aborting", __func__);
return false;
}
return true;
}
當(dāng)uinput有上報輸入事件的時候,fd就會發(fā)生變化從而就會調(diào)用回調(diào)函數(shù)libinput_source_dispatch,再繼續(xù)調(diào)用udev_input_dispatch,在udev_input_dispatch中再繼續(xù)調(diào)用process_events。
third_party\weston\libweston\libinput-seat.c
static int
udev_input_dispatch(struct udev_input *input)
{
if (libinput_dispatch(input->libinput) != 0)
weston_log("libinput: Failed to dispatch libinput\n");
process_events(input);
return 0;
}
static int
libinput_source_dispatch(int fd, uint32_t mask, void *data)
{
struct udev_input *input = data;
return udev_input_dispatch(input) != 0;
}
在process_events中會遍歷每個event,然后調(diào)用process_event來處理每個event。
third_party\weston\libweston\libinput-seat.c
static void
process_events(struct udev_input *input)
{
struct libinput_event *event;
while ((event = libinput_get_event(input->libinput))) {
process_event(event);
// for multi model input.
if (g_libinput_event_listener)
{
weston_log("process_events: call libinput_event_listener.\n");
g_libinput_event_listener(event);
}
else
{
weston_log("process_events: libinput_event_listener is not set.\n");
}
libinput_event_destroy(event);
}
}
在process_event中,udev_input_process_event這個函數(shù)是處理設(shè)備的添加和刪除,evdev_device_process_event_l這個函數(shù)是處理輸入事件的。
third_party\weston\libweston\libinput-seat.c
static void
process_event(struct libinput_event *event)
{
if (udev_input_process_event(event))
return;
if (evdev_device_process_event_l(event))
return;
}
在這個函數(shù)中會根據(jù)不同的事件類型調(diào)用不同事件類型的處理函數(shù)
third_party\weston\libweston\libinput-device.c
int
evdev_device_process_event_l(struct libinput_event *event)
{
struct libinput_device *libinput_device =
libinput_event_get_device(event);
struct evdev_device *device =
libinput_device_get_user_data(libinput_device);
int handled = 1;
bool need_frame = false;
switch (libinput_event_get_type(event)) {
case LIBINPUT_EVENT_KEYBOARD_KEY:
handle_keyboard_key(libinput_device,
libinput_event_get_keyboard_event(event));
break;
case LIBINPUT_EVENT_POINTER_MOTION:
need_frame = handle_pointer_motion(libinput_device,
libinput_event_get_pointer_event(event));
break;
case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE:
need_frame = handle_pointer_motion_absolute(
libinput_device,
libinput_event_get_pointer_event(event));
break;
case LIBINPUT_EVENT_POINTER_BUTTON:
need_frame = handle_pointer_button(libinput_device,
libinput_event_get_pointer_event(event));
break;
case LIBINPUT_EVENT_POINTER_AXIS:
need_frame = handle_pointer_axis(
libinput_device,
libinput_event_get_pointer_event(event));
break;
case LIBINPUT_EVENT_TOUCH_DOWN:
handle_touch_down(libinput_device,
libinput_event_get_touch_event(event));
break;
case LIBINPUT_EVENT_TOUCH_MOTION:
handle_touch_motion(libinput_device,
libinput_event_get_touch_event(event));
break;
case LIBINPUT_EVENT_TOUCH_UP:
handle_touch_up(libinput_device,
libinput_event_get_touch_event(event));
break;
case LIBINPUT_EVENT_TOUCH_FRAME:
handle_touch_frame(libinput_device,
libinput_event_get_touch_event(event));
break;
default:
handled = 0;
weston_log("unknown libinput event %d\n",
libinput_event_get_type(event));
}
if (need_frame)
notify_pointer_frame(device->seat);
return handled;
}
先以key事件為例,看handle_keyboard_key這個函數(shù),在這個函數(shù)中會獲取按鍵的狀態(tài)(按下和抬起),然后通過notify_key來派發(fā)事件。
third_party\weston\libweston\libinput-device.c
static void
handle_keyboard_key(struct libinput_device *libinput_device,
struct libinput_event_keyboard *keyboard_event)
{
struct evdev_device *device =
libinput_device_get_user_data(libinput_device);
int key_state =
libinput_event_keyboard_get_key_state(keyboard_event);
int seat_key_count =
libinput_event_keyboard_get_seat_key_count(keyboard_event);
struct timespec time;
/* Ignore key events that are not seat wide state changes. */
if ((key_state == LIBINPUT_KEY_STATE_PRESSED &&
seat_key_count != 1) ||
(key_state == LIBINPUT_KEY_STATE_RELEASED &&
seat_key_count != 0))
return;
timespec_from_usec(&time,
libinput_event_keyboard_get_time_usec(keyboard_event));
notify_key(device->seat, &time,
libinput_event_keyboard_get_key(keyboard_event),
key_state, STATE_UPDATE_AUTOMATIC);
}
在notiyf_key這個函數(shù)中,會執(zhí)行g(shù)rab->interface->key,grab是指向weston_keyboard_grab這個結(jié)構(gòu)體的函數(shù)指針,grab->interface->key其實就是調(diào)用default_grab_keyboard_key。
third_party\weston\libweston\input.c
WL_EXPORT void
notify_key(struct weston_seat *seat, const struct timespec *time, uint32_t key,
enum wl_keyboard_key_state state,
enum weston_key_state_update update_state)
{
struct weston_compositor *compositor = seat->compositor;
struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat);
struct weston_keyboard_grab *grab = keyboard->grab;
uint32_t *k, *end;
if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
weston_compositor_idle_inhibit(compositor);
} else {
weston_compositor_idle_release(compositor);
}
end = keyboard->keys.data + keyboard->keys.size;
for (k = keyboard->keys.data; k < end; k++) {
if (*k == key) {
/* Ignore server-generated repeats. */
if (state == WL_KEYBOARD_KEY_STATE_PRESSED)
return;
*k = *--end;
}
}
keyboard->keys.size = (void *) end - keyboard->keys.data;
if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
k = wl_array_add(&keyboard->keys, sizeof *k);
*k = key;
}
if (grab == &keyboard->default_grab ||
grab == &keyboard->input_method_grab) {
weston_compositor_run_key_binding(compositor, keyboard, time,
key, state);
grab = keyboard->grab;
}
grab->interface->key(grab, time, key, state);
if (keyboard->pending_keymap &&
keyboard->keys.size == 0)
update_keymap(seat);
if (update_state == STATE_UPDATE_AUTOMATIC) {
update_modifier_state(seat,
wl_display_get_serial(compositor->wl_display),
key,
state);
}
keyboard->grab_serial = wl_display_get_serial(compositor->wl_display);
if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
keyboard->grab_time = *time;
keyboard->grab_key = key;
}
}
在default_grab_keyboard_key中會繼續(xù)調(diào)用weston_keyboard_send_key。在wayland協(xié)議中,在Server和Client之間,對象是一一對應(yīng)的,互相知道這個對象的狀態(tài)。Client是 wl_proxy,與之對應(yīng)的,在Server就會有一個 wl_resource。之后會遍歷所有的wl_resource,然后調(diào)用各自的wl_keyboard_send_key。
third_party\weston\libweston\input.c
static void
default_grab_keyboard_key(struct weston_keyboard_grab *grab,
const struct timespec *time, uint32_t key,
uint32_t state)
{
weston_keyboard_send_key(grab->keyboard, time, key, state);
}
WL_EXPORT void
weston_keyboard_send_key(struct weston_keyboard *keyboard,
const struct timespec *time, uint32_t key,
enum wl_keyboard_key_state state)
{
struct wl_resource *resource;
struct wl_display *display = keyboard->seat->compositor->wl_display;
uint32_t serial;
struct wl_list *resource_list;
uint32_t msecs;
if (!weston_keyboard_has_focus_resource(keyboard))
return;
resource_list = &keyboard->focus_resource_list;
serial = wl_display_next_serial(display);
msecs = timespec_to_msec(time);
wl_resource_for_each(resource, resource_list) {
send_timestamps_for_input_resource(resource,
&keyboard->timestamps_list,
time);
wl_keyboard_send_key(resource, serial, msecs, key, state);
}
};
wl_keyboard_send_key其實會進行IPC調(diào)用,通過wayland協(xié)議,把server端的信息傳給client端。Wayland核心協(xié)議是通過protocol/wayland.xml這個文件定義的。它通過wayland_scanner這個程序掃描后會生成wayland-protocol.c, wayland-client-protocol.h和wayland-server-protocol.h三個文件。wayland-client-protocol.h是給Client用的;wayland-server-protocol.h是給Server用的; wayland-protocol.c描述了接口,Client和Server都會用。根據(jù)wayland協(xié)議,這個函數(shù)會調(diào)用到服務(wù)端wayland-server的wl_resouce_post_event。
out\ohos-arm-release\gen\third_party\wayland_standard\protocol\wayland-server-protocol.h
static inline void
wl_keyboard_send_key(struct wl_resource *resource_, uint32_t serial, uint32_t time, uint32_t key, uint32_t state)
{
wl_resource_post_event(resource_, WL_KEYBOARD_KEY, serial, time, key, state);
}
在wl_resource_post_event中會繼續(xù)調(diào)用wl_resource_post_event_array,在調(diào)用handle_array的時候會傳入函數(shù)指針wl_closure_send。
third_party\wayland_standard\src\wayland-server.c
WL_EXPORT void
wl_resource_post_event_array(struct wl_resource *resource, uint32_t opcode,
union wl_argument *args)
{
handle_array(resource, opcode, args, wl_closure_send);
}
WL_EXPORT void
wl_resource_post_event(struct wl_resource *resource, uint32_t opcode, ...)
{
union wl_argument args[WL_CLOSURE_MAX_ARGS];
struct wl_object *object = &resource->object;
va_list ap;
va_start(ap, opcode);
wl_argument_from_va_list(object->interface->events[opcode].signature,
args, WL_CLOSURE_MAX_ARGS, ap);
va_end(ap);
wl_resource_post_event_array(resource, opcode, args);
}
在handle_array中,會調(diào)用send_func這個函數(shù)指針說指向的函數(shù),實際上就是調(diào)用wl_closure_send這個函數(shù)。
third_party\wayland_standard\src\wayland-server.c
static void
handle_array(struct wl_resource *resource, uint32_t opcode,
union wl_argument *args,
int (*send_func)(struct wl_closure *, struct wl_connection *))
{
struct wl_closure *closure;
struct wl_object *object = &resource->object;
if (resource->client->error)
return;
if (!verify_objects(resource, opcode, args)) {
resource->client->error = 1;
return;
}
closure = wl_closure_marshal(object, opcode, args,
&object->interface->events[opcode]);
if (closure == NULL) {
resource->client->error = 1;
return;
}
log_closure(resource, closure, true);
if (send_func(closure, resource->client->connection))
resource->client->error = 1;
wl_closure_destroy(closure);
}
wl_connection代表Server與Client的連接,其中包含了in buffer和out buffer,分別作為輸入和輸出的緩沖區(qū)。在wl_closure_send這個函數(shù)中會調(diào)用wl_connection_write向wl_connection寫入數(shù)據(jù)。
third_party\wayland_standard\src\connection.c
int
wl_closure_send(struct wl_closure *closure, struct wl_connection *connection)
{
int size;
uint32_t buffer_size;
uint32_t *buffer;
int result;
if (copy_fds_to_connection(closure, connection))
return -1;
buffer_size = buffer_size_for_closure(closure);
buffer = zalloc(buffer_size * sizeof buffer[0]);
if (buffer == NULL)
return -1;
size = serialize_closure(closure, buffer, buffer_size);
if (size < 0) {
free(buffer);
return -1;
}
result = wl_connection_write(connection, buffer, size);
free(buffer);
return result;
}
在該函數(shù)中會通過wl_connection_flush向Client發(fā)送數(shù)據(jù), 把connection中out buffer的request通過socket發(fā)出去
third_party\wayland_standard\src\connection.c
int
wl_connection_write(struct wl_connection *connection,
const void *data, size_t count)
{
if (connection->out.head - connection->out.tail +
count > ARRAY_LENGTH(connection->out.data)) {
connection->want_flush = 1;
if (wl_connection_flush(connection) < 0)
return -1;
}
if (wl_buffer_put(&connection->out, data, count) < 0)
return -1;
connection->want_flush = 1;
return 0;
}
那么,Client是怎么讀取和處理這些event呢? 首先Client端需要監(jiān)聽這個wl_proxy,這是通過調(diào)用wl_registry_add_listener()->wl_proxy_add_listener()設(shè)置的。然后在Client的主循環(huán)中會調(diào)用wl_display_dispatch,并在wl_display_dispatch_queue()中處理收到的event和發(fā)出out buffer中的request。在wl_display_dispatch_queue中,wl_display_read_events負責(zé) 從connection的in buffer中讀出數(shù)據(jù),轉(zhuǎn)為wl_closure,插入到queue->event_list,等待后續(xù)處理。接著會調(diào)用wl_display_dispatch_queue_pending。
third_party\wayland_standard\src\wayland-client.c
WL_EXPORT int
wl_display_dispatch(struct wl_display *display)
{
return wl_display_dispatch_queue(display, &display->default_queue);
}
WL_EXPORT int
wl_display_dispatch_queue(struct wl_display *display,
struct wl_event_queue *queue)
{
int ret;
if (wl_display_prepare_read_queue(display, queue) == -1)
return wl_display_dispatch_queue_pending(display, queue);
while (true) {
ret = wl_display_flush(display);
if (ret != -1 || errno != EAGAIN)
break;
if (wl_display_poll(display, POLLOUT) == -1) {
wl_display_cancel_read(display);
return -1;
}
}
/* Don't stop if flushing hits an EPIPE; continue so we can read any
* protocol error that may have triggered it. */
if (ret < 0 && errno != EPIPE) {
wl_display_cancel_read(display);
return -1;
}
if (wl_display_poll(display, POLLIN) == -1) {
wl_display_cancel_read(display);
return -1;
}
if (wl_display_read_events(display) == -1)
return -1;
return wl_display_dispatch_queue_pending(display, queue);
}
在該函數(shù)中繼續(xù)調(diào)用dispatch_queue。
third_party\wayland_standard\src\wayland-client.c
WL_EXPORT int
wl_display_dispatch_queue_pending(struct wl_display *display,
struct wl_event_queue *queue)
{
int ret;
pthread_mutex_lock(&display->mutex);
ret = dispatch_queue(display, queue);
pthread_mutex_unlock(&display->mutex);
return ret;
}
在該函數(shù)中會將前面插入到queue當(dāng)中的event(wl_closure)依次拿出來處理調(diào)用dispatch_event。
third_party\wayland_standard\src\wayland-client.c
static int
dispatch_queue(struct wl_display *display, struct wl_event_queue *queue)
{
int count;
if (display->last_error)
goto err;
count = 0;
while (!wl_list_empty(&display->display_queue.event_list)) {
dispatch_event(display, &display->display_queue);
if (display->last_error)
goto err;
count++;
}
while (!wl_list_empty(&queue->event_list)) {
dispatch_event(display, queue);
if (display->last_error)
goto err;
count++;
}
return count;
err:
errno = display->last_error;
return -1;
}
在該函數(shù)中最后通過wl_closure_invoke()進行調(diào)用。
third_party\wayland_standard\src\wayland-client.c
static void
dispatch_event(struct wl_display *display, struct wl_event_queue *queue)
{
struct wl_closure *closure;
struct wl_proxy *proxy;
int opcode;
bool proxy_destroyed;
closure = wl_container_of(queue->event_list.next, closure, link);
wl_list_remove(&closure->link);
opcode = closure->opcode;
/* Verify that the receiving object is still valid by checking if has
* been destroyed by the application. */
validate_closure_objects(closure);
proxy = closure->proxy;
proxy_destroyed = !!(proxy->flags & WL_PROXY_FLAG_DESTROYED);
if (proxy_destroyed) {
destroy_queued_closure(closure);
return;
}
pthread_mutex_unlock(&display->mutex);
if (proxy->dispatcher) {
if (debug_client)
wl_closure_print(closure, &proxy->object, false);
wl_closure_dispatch(closure, proxy->dispatcher,
&proxy->object, opcode);
} else if (proxy->object.implementation) {
if (debug_client)
wl_closure_print(closure, &proxy->object, false);
wl_closure_invoke(closure, WL_CLOSURE_INVOKE_CLIENT,
&proxy->object, opcode, proxy->user_data);
}
pthread_mutex_lock(&display->mutex);
destroy_queued_closure(closure);
}
wl_closure_invoke其實是回調(diào)implementation指向的回調(diào)函數(shù)
third_party\wayland_standard\src\connection.c
void
wl_closure_invoke(struct wl_closure *closure, uint32_t flags,
struct wl_object *target, uint32_t opcode, void *data)
{
int count;
ffi_cif cif;
ffi_type *ffi_types[WL_CLOSURE_MAX_ARGS + 2];
void * ffi_args[WL_CLOSURE_MAX_ARGS + 2];
void (* const *implementation)(void);
count = arg_count_for_signature(closure->message->signature);
ffi_types[0] = &ffi_type_pointer;
ffi_args[0] = &data;
ffi_types[1] = &ffi_type_pointer;
ffi_args[1] = ⌖
convert_arguments_to_ffi(closure->message->signature, flags, closure->args,
count, ffi_types + 2, ffi_args + 2);
ffi_prep_cif(&cif, FFI_DEFAULT_ABI,
count + 2, &ffi_type_void, ffi_types);
implementation = target->implementation;
// OHOS fix: if listener function is not NULL, it will be call
if (implementation[opcode]) {
ffi_call(&cif, implementation[opcode], NULL, ffi_args);
}
wl_closure_clear_fds(closure);
}
在RegisterKeyboardListener中注冊了各種回調(diào)函數(shù)OnKeyboardKeymap,OnKeyboardEnter,OnKeyboardLeave,OnKeyboardKey,OnKeyboardModifiers,OnKeyboardRepeatInfo。
foundation\graphic\standard\frameworks\wm\src\input_listener_manager.cpp
void InputListenerManager::RegisterKeyboardListener(uint32_t caps)
{
bool haveKeyboardCapability = !!(caps & WL_SEAT_CAPABILITY_KEYBOARD);
if (haveKeyboardCapability == true && keyboard == nullptr) {
static struct wl_keyboard_listener listener = {
OnKeyboardKeymap,
OnKeyboardEnter,
OnKeyboardLeave,
OnKeyboardKey,
OnKeyboardModifiers,
OnKeyboardRepeatInfo,
};
keyboard = wl_seat_get_keyboard(seat);
if (keyboard) {
if (g_PowerKeyHandler == nullptr) {
std::shared_ptr<AppExecFwk::EventRunner> powerKeyRunner =
AppExecFwk::EventRunner::Create(GLOBAL_ACTION_THREAD_NAME);
g_PowerKeyHandler = std::make_shared<AppExecFwk::EventHandler>(powerKeyRunner);
powerKeyRunner->Run();
}
wl_keyboard_add_listener(keyboard, &listener, nullptr);
}
}
if (haveKeyboardCapability == false && keyboard != nullptr) {
wl_keyboard_destroy(keyboard);
keyboard = nullptr;
}
}
這些回調(diào)函數(shù)要被調(diào)用,首先Client端需要監(jiān)聽這個wl_proxy,這時InputListenerManager通過RegisterKeyboardListener將回調(diào)函數(shù)注冊到listener中,然后再調(diào)用wl_keyboard_add_listener。
out\ohos-arm-release\gen\third_party\wayland_standard\protocol\wayland-client-protocol.h
static inline int
wl_keyboard_add_listener(struct wl_keyboard *wl_keyboard,
const struct wl_keyboard_listener *listener, void *data)
{
return wl_proxy_add_listener((struct wl_proxy *) wl_keyboard,
(void (**)(void)) listener, data);
}
這是會通過wl_proxy_add_listener將回調(diào)函數(shù)放入到proxy->object.implementation中。也就是說wanland client接收到事件后最終會回調(diào)InputListenerManager注冊的回調(diào)函數(shù)。
third_party\wayland_standard\src\wayland-client.c
WL_EXPORT int
wl_proxy_add_listener(struct wl_proxy *proxy,
void (**implementation)(void), void *data)
{
if (proxy->flags & WL_PROXY_FLAG_WRAPPER)
wl_abort("Proxy %p is a wrapper\n", proxy);
if (proxy->object.implementation || proxy->dispatcher) {
wl_log("proxy %p already has listener\n", proxy);
return -1;
}
proxy->object.implementation = implementation;
proxy->user_data = data;
return 0;
}
現(xiàn)在再看回調(diào)函數(shù)OnKeyboardKey,接著調(diào)用listener->keyboardKey。
foundation\graphic\standard\frameworks\wm\src\input_listener_manager.cpp
void OnKeyboardKey(void *, struct wl_keyboard *,
uint32_t serial, uint32_t time, uint32_t key, uint32_t s)
{
auto state = static_cast<KeyboardKeyState>(s);
// Handle Power key
WMLOGFD("key: %{public}d, state: %{public}d", key, state);
if (key == KEY_POWER && g_PowerKeyHandler != nullptr) {
HandlePowerKey(time, key, state);
return;
}
const auto &inputListeners = g_getFocus();
for (const auto &listener : inputListeners) {
if (listener->keyboardKey) {
listener->keyboardKey(listener->GetWindow(), serial, time, key, state);
}
}
}
從這個添加監(jiān)聽的函數(shù)可以看出,當(dāng)調(diào)用keyboardKey的時候會調(diào)用KeyboardHandleKey。
foundation\graphic\standard\frameworks\wm\src\multimodal_listener_manager.cpp
sptr<MultimodalListener> MultimodalListenerManager::AddListener(void *window)
{
auto l = delegator.Dep<InputListenerManager>()->AddListener(window);
sptr<MultimodalListener> ml = new MultimodalListener(window);
ml->input = l;
l->pointerMotion = std::bind(&MultimodalListenerManager::PointerHandleMotion, this, POINTER_ENTER_ARG);
l->pointerButton = std::bind(&MultimodalListenerManager::PointerHandleButton, this, POINTER_BUTTON_ARG);
l->pointerFrame = std::bind(&MultimodalListenerManager::PointerHandleFrame, this, POINTER_FRAME_ARG);
l->pointerAxis = std::bind(&MultimodalListenerManager::PointerHandleAxis, this, POINTER_AXIS_ARG);
l->keyboardKey = std::bind(&MultimodalListenerManager::KeyboardHandleKey, this, KEYBOARD_KEY_ARG);
l->touchDown = std::bind(&MultimodalListenerManager::TouchHandleDown, this, TOUCH_DOWN_ARG);
l->touchUp = std::bind(&MultimodalListenerManager::TouchHandleUp, this, TOUCH_UP_ARG);
l->touchMotion = std::bind(&MultimodalListenerManager::TouchHandleMotion, this, TOUCH_MOTION_ARG);
l->touchFrame = std::bind(&MultimodalListenerManager::TouchHandleFrame, this, TOUCH_FRAME_ARG);
l->touchShape = std::bind(&MultimodalListenerManager::TouchHandleShape, this, TOUCH_SHAPE_ARG);
l->touchOrientation = std::bind(
&MultimodalListenerManager::TouchHandleOrientation, this, TOUCH_ORIENTATION_ARG);
if (windowCallback.find(window) == windowCallback.end()) {
windowCallback[window] = std::vector<sptr<MultimodalListener>>();
}
windowCallback[window].push_back(ml);
return ml;
}
在KeyboardHandleKey中會對按鍵信息進行處理,并且會把按鍵狀態(tài),鍵值,按鍵按下的時長這些值放入到KeyProperty結(jié)構(gòu)體中,然后把KeyProperty封裝成KeyEvent的數(shù)據(jù)結(jié)構(gòu)中用于派發(fā)。
foundation\graphic\standard\frameworks\wm\src\multimodal_listener_manager.cpp
void MultimodalListenerManager::KeyboardHandleKey(void *data,
uint32_t serial, uint32_t time, uint32_t key, KeyboardKeyState state)
{
KeyEvent event;
struct MultimodalProperty multiProperty = {
.highLevelEvent = 0,
.uuid = "",
.sourceType = MultimodalEvent::KEYBOARD,
.occurredTime = time,
.deviceId = "",
.inputDeviceId = 0,
.isHighLevelEvent = false,
};
struct KeyProperty keyProperty = {
.isPressed = (state == KEYBOARD_KEY_STATE_PRESSED),
.keyCode = key,
.keyDownDuration = 0,
};
static uint32_t keyDownTime = 0;
if (state == KEYBOARD_KEY_STATE_PRESSED) {
keyDownTime = time;
} else {
keyProperty.keyDownDuration = time - keyDownTime;
}
constexpr uint32_t linuxKeyBack = 158;
if (key == linuxKeyBack) {
keyProperty.keyCode = KeyEvent::CODE_BACK;
}
event.Initialize(multiProperty, keyProperty);
const auto &mls = GetInputCallback(data);
for (const auto &ml : mls) {
if (ml->keyboardKeyCb) {
ml->keyboardKeyCb(event);
}
}
}
由于目前openharmony代碼在ACE部分沒有完全支持對按鍵事件的處理,所以下面分析對touch事件的處理。現(xiàn)在也可以看TouchHandleUp這個回調(diào),這個函數(shù)會調(diào)用ml->onTouchCb,
foundation\graphic\standard\frameworks\wm\src\multimodal_listener_manager.cpp
void MultimodalListenerManager::TouchHandleUp(void *data, uint32_t serial, uint32_t time, int32_t id)
{
if (id < MAX_TOUCH_NUM) {
actionEventInfo.touchCount--;
actionEventInfo.isUp = true;
actionEventInfo.touchEventInfos[id].isRefreshed = true;
actionEventInfo.touchEventInfos[id].serial = serial;
actionEventInfo.touchEventInfos[id].currentTime = time;
}
void *window = nullptr;
if (id < MAX_TOUCH_NUM) {
window = touchWindows[id];
touchWindows[id] = nullptr;
}
WMLOGFD("window: %{public}p", window);
while (actionEventInfo.isUp || actionEventInfo.isDown || actionEventInfo.isMotion) {
TouchEvent touchEvent;
TouchEventEncap(actionEventInfo, touchEvent, MAX_TOUCH_NUM);
const auto &mls = GetInputCallback(data);
for (const auto &ml : mls) {
if (ml->onTouchCb) {
ml->onTouchCb(touchEvent);
}
}
}
}
通過RegistOnTouchCb注冊的方式,最終會調(diào)用aceView->DispatchTouchEvent。
foundation\graphic\standard\frameworks\wm\src\client\window_manager_controller_client.cpp
void LayerControllerClient::RegistOnTouchCb(int id, funcOnTouch cb)
{
LOCK(mutex);
WMLOG_I("LayerControllerClient::%{public}s", __func__);
if (cb) {
WMLOG_I("LayerControllerClient::RegistOnTouchCb OK");
GET_WINDOWINFO_VOID(windowInfo, id);
windowInfo->mmiListener->onTouchCb = cb;
}
}
foundation\graphic\standard\frameworks\wm\src\client\window_manager.cpp
void Window::RegistOnTouchCb(funcOnTouch cb)
{
WMLOG_I("Window::RegistOnTouchCb start, windowid %{public}d", this->m_windowid);
LayerControllerClient::GetInstance()->RegistOnTouchCb(m_windowid, cb);
WMLOG_I("Window::RegistOnTouchCb end windowid %{public}d", this->m_windowid);
}
foundation\ace\ace_engine\adapter\ohos\cpp\ace_ability.cpp
auto&& touchEventCallback = [aceView = flutterAceView](OHOS::TouchEvent event) -> bool {
LOGD("RegistOnTouchCb touchEventCallback called");
return aceView->DispatchTouchEvent(aceView, event);
};
window->OnTouch(touchEventCallback);
在DispatchTouchEvent中,會根據(jù)事件類型分為mouse event和touch event,這里先分析touch event,所以最后會調(diào)用ProcessTouchEvent。
foundation\ace\ace_engine\adapter\ohos\cpp\flutter_ace_view.cpp
bool FlutterAceView::DispatchTouchEvent(FlutterAceView* view, OHOS::TouchEvent& touchEvent)
{
if (touchEvent.GetAction() == OHOS::TouchEvent::OTHER && touchEvent.GetSourceDevice() == OHOS::TouchEvent::MOUSE) {
// mouse event
std::shared_ptr<MultimodalEvent> multimodalEvent = touchEvent.GetMultimodalEvent();
OHOS::MouseEvent* mouseEvent = (OHOS::MouseEvent*)multimodalEvent.get();
if (mouseEvent == nullptr) {
LOGE("mouseEvent is nullptr");
return false;
}
LOGI("DispatchTouchEvent MouseEvent");
view->ProcessMouseEvent(*mouseEvent);
} else {
// touch event
LOGI("DispatchTouchEvent TouchEvent");
return view->ProcessTouchEvent(touchEvent);
}
return true;
}
執(zhí)行touchEventCallback_ 函數(shù)指針指向的函數(shù),繼續(xù)看touchEventCallback_ 指向了哪個函數(shù)。
foundation\ace\ace_engine\adapter\ohos\cpp\flutter_ace_view.cpp
bool FlutterAceView::ProcessTouchEvent(OHOS::TouchEvent& touchEvent)
{
TouchPoint touchPoint = ConvertTouchEvent(touchEvent);
bool forbiddenToPlatform = false;
if (touchPoint.type != TouchType::UNKNOWN) {
if (touchEventCallback_) {
touchEventCallback_(touchPoint);
}
} else {
LOGW("Unknown event.");
}
#ifdef WEARABLE_PRODUCT
forbiddenToPlatform = forbiddenToPlatform || IsNeedForbidToPlatform(point);
#endif
// if last page, let os know so that to quit app.
return forbiddenToPlatform || (!IsLastPage());
}
看來touchEventCallback_是指向了RegisterTouchEventCallback通過參數(shù)穿過來的callback, 那繼續(xù)看調(diào)用RegisterTouchEventCallback的callback是什么。
foundation\ace\ace_engine\adapter\ohos\cpp\flutter_ace_view.cpp
void FlutterAceView::RegisterTouchEventCallback(TouchEventCallback&& callback)
{
ACE_DCHECK(callback);
touchEventCallback_ = std::move(callback);
}
在AceContainer::InitializeCallback中可以看出,最終這個callback就是執(zhí)行pipeline_context的OnTouchEvent。
foundation\ace\ace_engine\adapter\ohos\cpp\ace_container.cpp
void AceContainer::InitializeCallback()
{
ACE_FUNCTION_TRACE();
ACE_DCHECK(aceView_ && taskExecutor_ && pipelineContext_);
auto&& touchEventCallback = [context = pipelineContext_](const TouchPoint& event) {
context->GetTaskExecutor()->PostTask(
[context, event]() { context->OnTouchEvent(event); }, TaskExecutor::TaskType::UI);
};
aceView_->RegisterTouchEventCallback(touchEventCallback);
在這個函數(shù)中最終會調(diào)用eventManager_.DispatchTouchEvent。之后就會通過ACE的接口把事件傳給應(yīng)用端。
foundation\ace\ace_engine\frameworks\core\pipeline\pipeline_context.cpp
void PipelineContext::OnTouchEvent(const TouchPoint& point)
{
CHECK_RUN_ON(UI);
ACE_FUNCTION_TRACE();
if (!rootElement_) {
LOGE("root element is nullptr");
return;
}
auto scalePoint = point.CreateScalePoint(viewScale_);
if (scalePoint.type == TouchType::DOWN) {
LOGD("receive touch down event, first use touch test to collect touch event target");
TouchRestrict touchRestrict { TouchRestrict::NONE };
auto frontEnd = GetFrontend();
if (frontEnd && (frontEnd->GetType() == FrontendType::JS_CARD)) {
touchRestrict.UpdateForbiddenType(TouchRestrict::LONG_PRESS);
}
eventManager_.TouchTest(scalePoint, rootElement_->GetRenderNode(), touchRestrict);
}
if (scalePoint.type == TouchType::MOVE) {
isMoving_ = true;
}
if (isKeyEvent_) {
SetIsKeyEvent(false);
}
eventManager_.DispatchTouchEvent(scalePoint);
}
總結(jié)
通過本篇文章的學(xué)習(xí)可以了解OpenHarmony L2系統(tǒng)基于wayland三方庫的事件處理派發(fā)流程。
??51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)??
??https://harmonyos.51cto.com??