OpenHarmony源码解析之基于wayland的输入系统 原创 精华

深开鸿
发布于 2022-1-18 15:16
浏览
3收藏

作者:孙仲毅

简介

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

输入系统框架

OpenHarmony源码解析之基于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三方库的事件处理派发流程。

更多原创内容请关注:深开鸿技术团队

入门到精通、技巧到案例,系统化分享HarmonyOS开发技术,欢迎投稿和订阅,让我们一起携手前行共建鸿蒙生态。

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
已于2022-1-18 15:21:24修改
8
收藏 3
回复
举报
6条回复
按时间正序
/
按时间倒序
红叶亦知秋
红叶亦知秋

感谢孙老师分享,非常详细的讲解。

1
回复
2022-1-18 16:36:11
执着的刀斯林
执着的刀斯林

太牛了,讲的太透彻了

1
回复
2022-1-18 16:49:15
wx5ba6548f1b21b
wx5ba6548f1b21b

框架讲解很清晰,收获颇多,感谢作者的分享,期待新的文章

回复
2022-1-18 17:14:09
ㄩ

孙老师真的是大佬, Input 这块能分析得这么透彻... 收藏下来对着源码慢慢学~

1
回复
2022-1-18 17:27:45
wx63da29c4d6f3b
wx63da29c4d6f3b

请教孙老师,这篇文章是基于哪个分支的哪个版本分析的啊?文中提到的源码有些找不到。

回复
2023-2-13 14:14:37
Whyalone
Whyalone

就贼详细

回复
2023-2-13 18:13:31
回复
    相关推荐