天天看點

OpenHarmony源碼解析之基于wayland的輸入系統

作者:孫仲毅

簡介

在之前一篇文章《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開發技術,歡迎投稿和訂閱,讓我們一起攜手前行共建鴻蒙生态。

想了解更多關于鴻蒙的内容,請通路:

51CTO和華為官方合作共建的鴻蒙技術社群

https://harmonyos.51cto.com/#bkwz

繼續閱讀