社区编辑申请
注册/登录
OpenHarmony源码解析之基于wayland的输入系统
系统 OpenHarmony
本篇文章是基于openharmony L2系统的,所以本章内容就是分析基于wayland协议的输入系统。

​想了解更多内容,请访问:​

​51CTO和华为官方合作共建的鸿蒙技术社区​

​https://harmonyos.51cto.com​

简介

在之前一篇文章《OpenHarmony 多模输入子系统源码分析之事件派发流程 & 接口说明》中分析的输入系统的逻辑的是基于openharmony L0系统的,而本篇文章是基于openharmony L2系统的,在L2系统中输入系统并不是由InputManagerService, InputEventHub, InputEventDistributer来负责处理的输入事件的,而是由第三方库wayland来负责处理输入事件的,所以本章内容就是分析基于wayland协议的输入系统。

输入系统框架

整个输入流程的派发过程:

kernel ->HDF->uinput -> libinput –> weston -> wayland client -> wm -> ACE -> JS应用

当底层有事件发生的时候会通过驱动给HDF, 然后通过HDI接口给uinput,然后uinput会通过注入事件的方式把事件注入到libinput中,当libinput检测到有事件传上来的时候就会进行处理,处理完会给weston继续处理和派发,weston处理完后会通过wayland协议传给wayland client端,这是一个IPC调用,wayland处理完后会继续派发给windowmanager(简称wm),之后通过ACE传给JS应用。

输入系统事件派发流程

首先在device_info.hcs和input_config.hcs配置文件中配置相应的信息,多模输入系统的HdfDeviceEventManager会在启动的时候去bind对应的hdf service, 然后通过RegisterReportCallback去注册对应的回调函数。

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_);
}
}

当有事件上来的时候就会回调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准备对事件进行注入,在该函数中会通过notify_one来唤醒InjectFunc这个函数

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();
}

最终会调用VirtualDevice::EmitEvent, 在该函数中会将事件写入到uinput的设备文件中。

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;
}

当uinput有上报输入事件的时候,fd就会发生变化从而就会调用回调函数libinput_source_dispatch,再继续调用udev_input_dispatch,在udev_input_dispatch中再继续调用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,然后调用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这个函数是处理设备的添加和删除,evdev_device_process_event_l这个函数是处理输入事件的。

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;
}

在这个函数中会根据不同的事件类型调用不同事件类型的处理函数

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这个函数,在这个函数中会获取按键的状态(按下和抬起),然后通过notify_key来派发事件。

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这个函数中,会执行grab->interface->key,grab是指向weston_keyboard_grab这个结构体的函数指针,grab->interface->key其实就是调用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中会继续调用weston_keyboard_send_key。在wayland协议中,在Server和Client之间,对象是一一对应的,互相知道这个对象的状态。Client是 wl_proxy,与之对应的,在Server就会有一个 wl_resource。之后会遍历所有的wl_resource,然后调用各自的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调用,通过wayland协议,把server端的信息传给client端。Wayland核心协议是通过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都会用。根据wayland协议,这个函数会调用到服务端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中会继续调用wl_resource_post_event_array,在调用handle_array的时候会传入函数指针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中,会调用send_func这个函数指针说指向的函数,实际上就是调用wl_closure_send这个函数。

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,分别作为输入和输出的缓冲区。在wl_closure_send这个函数中会调用wl_connection_write向wl_connection写入数据。

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;
}

在该函数中会通过wl_connection_flush向Client发送数据, 把connection中out buffer的request通过socket发出去

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端需要监听这个wl_proxy,这是通过调用wl_registry_add_listener()->wl_proxy_add_listener()设置的。然后在Client的主循环中会调用wl_display_dispatch,并在wl_display_dispatch_queue()中处理收到的event和发出out buffer中的request。在wl_display_dispatch_queue中,wl_display_read_events负责 从connection的in buffer中读出数据,转为wl_closure,插入到queue->event_list,等待后续处理。接着会调用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);
}

在该函数中继续调用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;
}

在该函数中会将前面插入到queue当中的event(wl_closure)依次拿出来处理调用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;
}

在该函数中最后通过wl_closure_invoke()进行调用。

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其实是回调implementation指向的回调函数

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] = &target;

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中注册了各种回调函数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;
}
}

这些回调函数要被调用,首先Client端需要监听这个wl_proxy,这时InputListenerManager通过RegisterKeyboardListener将回调函数注册到listener中,然后再调用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将回调函数放入到proxy->object.implementation中。也就是说wanland client接收到事件后最终会回调InputListenerManager注册的回调函数。

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;
}

现在再看回调函数OnKeyboardKey,接着调用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);
}
}
}

从这个添加监听的函数可以看出,当调用keyboardKey的时候会调用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中会对按键信息进行处理,并且会把按键状态,键值,按键按下的时长这些值放入到KeyProperty结构体中,然后把KeyProperty封装成KeyEvent的数据结构中用于派发。

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事件的处理。现在也可以看TouchHandleUp这个回调,这个函数会调用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注册的方式,最终会调用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中,会根据事件类型分为mouse event和touch event,这里先分析touch event,所以最后会调用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;
}

执行touchEventCallback_ 函数指针指向的函数,继续看touchEventCallback_ 指向了哪个函数。

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通过参数穿过来的callback, 那继续看调用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就是执行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);

在这个函数中最终会调用eventManager_.DispatchTouchEvent。之后就会通过ACE的接口把事件传给应用端。

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);
}

总结

通过本篇文章的学习可以了解OpenHarmony L2系统基于wayland三方库的事件处理派发流程。

​想了解更多内容,请访问:​

​51CTO和华为官方合作共建的鸿蒙技术社区​

​https://harmonyos.51cto.com​

责任编辑:jianghua 来源: 鸿蒙社区
相关推荐

2022-04-02 20:45:04

Hi3516开发板操作系统鸿蒙

2021-12-06 06:19:03

2022-04-15 14:31:02

鸿蒙操作系统

2022-05-18 23:42:08

网络安全安全分析工具

2022-04-20 20:28:40

HDF 驱动框架鸿蒙操作系统

2021-12-14 10:16:00

2022-05-24 15:55:37

避障小车华为

2021-09-16 15:08:08

2022-05-11 15:08:52

驱动开发系统移植

2022-04-07 14:33:31

操作系统鸿蒙HarmonyOS

2022-05-09 15:08:56

存储厂商NFV领域华为

2022-04-06 11:27:05

harmonyeTS 开发NAPI开发

2022-05-20 14:54:33

数据安全数字化转型企业

2022-04-07 15:28:16

HarmonyOS鸿蒙操作系统

2022-05-11 14:48:33

腾讯云寿险民生保险

2022-04-15 14:57:57

Flex布局鸿蒙操作系统

2022-04-19 11:23:26

release3.1子系统鸿蒙

2022-04-01 15:18:04

HarmonyHDF 驱动鸿蒙

2022-02-24 16:39:41

OpenHarmonNiobe开发鸿蒙

2022-04-18 10:37:01

鸿蒙操作系统开发工具

同话题下的热门内容

DAYU200最新烧录OpenHarmony系统教程基于搭载OpenHarmony避障小车连接华为云心得体会OpenHarmony3.2的编译烧录OpenHarmony - ArkUI(ETS) 自定义图片查看组件OpenHarmony3.1-WIFI子系统之STA模式源码解析OpenHarmony之 eTS DataAbility 的使用及数据管理OpenHarmony之 网络管理 Socket 模块的使用HarmonyOs - ArkUI(JS)画布组件Canvas之自定义柱状图

编辑推荐

HarmonyOS 2.0鸿蒙第二期开发者Beta公测申请指南HarmonyOS LYEVK-3861开发板播放《蜜雪冰城》鸿蒙HarmonyOS分布式软总线:构建低时延、高带宽的多设备虚拟网络华为HarmonyOS的强势突围: 直面物联网迷宫的蓄力进击鸿蒙HarmonyOS2.0发布会现场回忆录
我收藏的内容
点赞
收藏

51CTO技术栈公众号