精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

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 來源: 鴻蒙社區
相關推薦

2023-04-06 09:14:11

多模輸入子系統鴻蒙

2023-04-12 15:31:11

系統服務管理鴻蒙

2021-11-08 15:04:47

鴻蒙HarmonyOS應用

2021-12-17 16:42:09

鴻蒙HarmonyOS應用

2022-01-06 16:17:58

鴻蒙HarmonyOS應用

2021-09-18 14:40:37

鴻蒙HarmonyOS應用

2022-02-17 20:57:07

OpenHarmon操作系統鴻蒙

2022-01-10 15:30:11

鴻蒙HarmonyOS應用

2022-02-14 14:47:11

SystemUIOpenHarmon鴻蒙

2023-06-28 15:00:02

開源鴻蒙輸入系統架構

2021-09-17 14:38:58

鴻蒙HarmonyOS應用

2021-11-18 10:28:03

鴻蒙HarmonyOS應用

2022-05-10 11:17:27

電話子系統數據服務模塊

2022-05-17 10:42:36

reboot源碼解析

2022-05-24 15:46:51

Wi-FiSTA模式

2022-06-13 14:18:39

電源管理子系統耗電量服務

2021-09-16 15:08:08

鴻蒙HarmonyOS應用

2021-11-25 09:54:54

鴻蒙HarmonyOS應用

2022-07-05 16:03:29

電源管理子系統鴻蒙

2021-12-08 15:07:51

鴻蒙HarmonyOS應用
點贊
收藏

51CTO技術棧公眾號

麻豆专区一区二区三区四区五区| 成人看片爽爽爽| 亚洲国产精品国自产拍av| 国产精品欧美一区二区| 国产精品 欧美激情| 盗摄牛牛av影视一区二区| 色综合久久综合中文综合网| 亚洲一区三区| 人人妻人人澡人人爽久久av| 日本午夜精品一区二区三区电影| 欧美成人在线免费| 国产全是老熟女太爽了| 国产精品日韩精品在线播放 | 亚洲三区视频| 日韩专区第一页| 老司机精品视频一区二区三区| 97色伦亚洲国产| 国产稀缺精品盗摄盗拍| 国产欧美一区二区精品久久久| 日韩一区二区三区四区| 手机在线免费观看毛片| 538在线视频| 亚洲人成网站在线| 日韩电影免费观看高清完整| 欧性猛交ⅹxxx乱大交| 美女任你摸久久| 久久久久久国产免费| 国产精品99久久久久久成人| 欧美三级伦理在线| 亚洲精品久久久久久久久久久久久| 图片区乱熟图片区亚洲| 国产欧美自拍| 在线观看免费亚洲| 欧在线一二三四区| 黄视频免费在线看| 亚洲午夜精品在线| 日韩人妻一区二区三区蜜桃视频| 日韩毛片久久久| 国产精品区一区二区三区 | 色综合咪咪久久网| 亚洲午夜精品视频| 美国黄色a级片| 日韩人体视频| 亚洲激情在线观看视频免费| 蜜臀av粉嫩av懂色av| 亚洲图色一区二区三区| 日韩欧美国产一区二区三区| 日本少妇激三级做爰在线| 亚洲精品tv| 欧美区在线观看| 亚洲第一成肉网| 自拍偷拍亚洲| 欧美无乱码久久久免费午夜一区| 日韩一级片播放| 成人h在线观看| 欧美亚洲国产一区二区三区| 日韩在线第三页| 日韩经典一区| 欧美色视频在线| 777一区二区| 国产精品欧美一区二区三区不卡| 日韩一区二区中文字幕| www.黄色网| 国产精品对白久久久久粗| 亚洲国产91精品在线观看| 无码人妻aⅴ一区二区三区| 亚欧洲精品视频在线观看| 亚洲欧美日韩国产中文| 黄色三级生活片| 999久久久精品国产| 麻豆国产精品va在线观看不卡| 欧美精品xxxxx| 亚洲国产日韩在线| 日韩美女在线观看| 中日韩av在线| 国产成人免费av在线| 97av自拍| 久草在线网址| 国产精品毛片久久久久久久| 国产精品一二三在线观看| 污污网站在线看| 精品国产老师黑色丝袜高跟鞋| 欧美 日韩 国产一区| 小明成人免费视频一区| 日韩三级精品电影久久久| 国产精品第七页| 蜜桃精品噜噜噜成人av| 日韩在线观看高清| 日韩网红少妇无码视频香港| 日本美女一区二区| 国产66精品久久久久999小说| 日本1级在线| 17c精品麻豆一区二区免费| 欧美国产视频一区| 欧美羞羞视频| 日韩亚洲欧美一区二区三区| 素人fc2av清纯18岁| 国产精品福利在线观看播放| 97视频在线免费观看| 亚洲 小说区 图片区| 国产成人aaa| 日韩中文字幕一区二区| 大黄网站在线观看| 欧美午夜视频网站| 日本护士做爰视频| 久久久久久久久久久久久久久久久久 | 日韩av高清在线观看| 懂色中文一区二区三区在线视频| 69久久夜色| 欧美日韩中文字幕在线| 人妻巨大乳一二三区| 欧洲grand老妇人| 韩国19禁主播vip福利视频| 中文字幕乱码无码人妻系列蜜桃| 成人av高清在线| 91免费视频黄| 国产精品久久久久久久久久齐齐 | aaa在线观看| 天天射综合影视| 两女双腿交缠激烈磨豆腐| 欧美丝袜一区| 日本一区视频在线播放| 青青草原免费观看| 日韩综合一区二区| 国产在线视频欧美一区二区三区| 黄网站视频在线观看| 色久综合一二码| 男人网站在线观看| 欧美日韩国产欧| 成人国产精品一区二区| 第一福利在线| 欧美性猛交xxxx富婆| 亚洲视频在线播放免费| 欧美激情综合色综合啪啪| 91沈先生在线观看| 高清美女视频一区| 日本韩国欧美国产| 中文字幕一二三四区| 亚洲神马久久| 精品亚洲欧美日韩| а√天堂中文在线资源8| 精品福利在线导航| 国产亚洲成人精品| 粉嫩绯色av一区二区在线观看| 成人午夜免费剧场| 久久中文字幕一区二区| 免费av一区二区| 国产高潮在线观看| 一区二区高清在线| 手机在线播放av| 好看的av在线不卡观看| 俄罗斯精品一区二区三区| 青草视频在线免费直播| 精品噜噜噜噜久久久久久久久试看 | 无码视频在线观看| 欧美国产一区在线| 99草草国产熟女视频在线| 波多野结衣的一区二区三区| 国产区亚洲区欧美区| 国产精品剧情一区二区在线观看| 7777精品伊人久久久大香线蕉完整版 | 亚洲国产合集| 国产suv精品一区二区| av在线日韩国产精品| 欧美久久久久中文字幕| 日本天堂中文字幕| 成人国产一区二区三区精品| 日本日本19xxxⅹhd乱影响| 中文有码一区| 国产一区二区色| 日本高清成人vr专区| 日韩av在线免费| 伊人久久国产精品| 亚洲欧美激情小说另类| 99re久久精品国产| 青青草国产精品亚洲专区无| 裸体裸乳免费看| 欧美精品中文| 国产欧美精品一区二区三区-老狼| 中文字幕伦理免费在线视频| 日韩av网站在线| 中文字幕无线码一区| 尤物视频一区二区| 日本aaa视频| 国产综合色视频| 又粗又黑又大的吊av| 久久婷婷蜜乳一本欲蜜臀| 国产精品麻豆免费版| h1515四虎成人| 欧美激情亚洲另类| 国产区视频在线| 精品国产一区二区国模嫣然| 无码人妻丰满熟妇精品| 亚洲色图丝袜美腿| 三上悠亚影音先锋| 国产一区二区三区观看| 男人操女人免费| 欧美国产激情| 日韩欧美亚洲v片| 久久精品论坛| 91天堂在线视频| 桃花岛tv亚洲品质| 高清欧美性猛交xxxx| 日本www在线观看| 亚洲精品在线观看www| 国产黄色片免费观看| 欧美羞羞免费网站| 欧美亚韩一区二区三区| 亚洲精品成a人| 99re6热在线精品视频| 久久夜色精品国产欧美乱极品| japan高清日本乱xxxxx| 老色鬼精品视频在线观看播放| 天天夜碰日日摸日日澡性色av| 亚洲字幕久久| 亚洲人成人77777线观看| 伊人久久综合影院| 国产在线精品一区二区三区》| 日韩视频一二区| 国产日韩在线免费| 三级成人黄色影院| 97在线免费视频| 黄色免费在线看| www.日韩.com| av网站在线免费播放| 亚洲天堂av在线免费观看| 天天干视频在线| 亚洲黄页视频免费观看| 亚洲av无码国产综合专区| 在线成人av影院| 一区二区三区午夜| 欧美日韩一级二级| 中文字幕乱码中文字幕| 欧美伊人久久久久久久久影院| 好看的av在线| 一本色道久久加勒比精品 | 精品在线不卡| 精品国产影院| 国产一区二区三区奇米久涩| 超碰成人在线免费| 国产精品久久久久久免费观看| 91综合久久爱com| av免费观看久久| 97久久精品| 国产日韩欧美一区二区| 精品淫伦v久久水蜜桃| 九九九九精品| 亚洲裸色大胆大尺寸艺术写真| 久久久水蜜桃| 国产成人一区| 一区二区三区国产福利| 66视频精品| 99在线免费视频观看| 亚洲第一黄色| 超碰97人人射妻| 日韩高清在线一区| 国产色视频在线播放| 激情综合网av| 性生交大片免费看l| 成人高清免费观看| 屁屁影院国产第一页| 国产亚洲欧美中文| 国产一二三av| 一区二区三区四区亚洲| 日韩av在线电影| 色老汉一区二区三区| 国产尤物在线观看| 日韩一级高清毛片| 天堂中文在线看| 一道本无吗dⅴd在线播放一区| 最新97超碰在线| 久久99精品久久久久久青青91| 久草免费在线视频| 国产精品久久久久久久久粉嫩av| 96视频在线观看欧美| 高清视频一区二区三区| 少妇精品久久久一区二区三区| www亚洲国产| 最新亚洲激情| 香蕉视频999| 成人免费视频一区二区| 极品尤物一区二区| 夜夜嗨av一区二区三区中文字幕| 国产成人无码av| 日韩欧美在线不卡| 日本电影一区二区在线观看| 久久不射电影网| 神马电影网我不卡| 成人在线视频电影| 欧美午夜精彩| 日韩精品在线观看av| 免费在线一区观看| 久久精品女同亚洲女同13| 国产精品国产三级国产有无不卡| 国产污视频在线看| 欧美三级视频在线| 偷拍精品一区二区三区| 久久久久99精品久久久久| www.精品| 国产偷国产偷亚洲高清97cao| 日韩国产一区二区| 大j8黑人w巨大888a片| 国产综合成人久久大片91| 欧美黄色一级生活片| 亚洲一区二区三区四区不卡| 亚洲天堂2021av| 亚洲男人天堂2023| av影院在线免费观看| 91中文字幕在线| 成人直播大秀| 99999精品视频| 成人高清视频在线观看| wwwav国产| 欧美老年两性高潮| 国产高清一区在线观看| 91av在线不卡| 成人盗摄视频| 嫩草影院中文字幕| 激情综合色播五月| 国产熟女一区二区| 色综合久久综合网97色综合| 日本成人动漫在线观看| 另类图片亚洲另类| 精品国产亚洲日本| 三年中文高清在线观看第6集| 日本aⅴ精品一区二区三区| www.中文字幕av| 黑人狂躁日本妞一区二区三区 | 岛国一区二区三区| av激情在线观看| 4438x亚洲最大成人网| h网站视频在线观看| 日本电影亚洲天堂| 亚洲婷婷影院| 日韩精品视频久久| 久久一夜天堂av一区二区三区| 久久黄色精品视频| 亚洲久久久久久久久久| 国产欧美一区二区三区精品酒店| 久久精品国产理论片免费| 国产偷自视频区视频一区二区| 影音先锋黄色资源| 懂色aⅴ精品一区二区三区蜜月| 亚洲欧美日韩成人在线| 91高清视频免费| 美女久久久久| 无码少妇一区二区三区芒果| 亚洲国产精品国自产拍av| 中文字幕一区二区人妻| 久久久av亚洲男天堂| 欧美电影在线观看一区| www.69av| 99久久精品免费看| 台湾佬中文在线| 中文字幕亚洲精品| 国产日韩欧美中文在线| 国产精品无码电影在线观看| 波多野结衣在线一区| 精产国品一区二区| 久久精品国产清自在天天线| 日韩免费高清视频网站| 精品无码一区二区三区爱欲| 久久综合久久综合亚洲| 亚洲性猛交富婆| 美女扒开尿口让男人操亚洲视频网站| av成人资源| 日韩免费毛片视频| 国产精品久久久久精k8 | 国产极品久久久久久久久波多结野| 亚洲人成网站在线播放2019| 国产高清精品在线| www.国产高清| 爽爽爽爽爽爽爽成人免费观看| 欧美激情三级| 久草青青在线观看| 中文字幕一区二区三区四区不卡 | 国产精品羞羞答答在线| 精品少妇一区二区30p| 天堂成人娱乐在线视频免费播放网站 | 成人av资源在线观看| 亚洲精品国产精品乱码视色| 久久中文字幕国产| 亚洲免费成人av在线| 九九久久久久久| 狠狠色狠狠色综合日日五| 香蕉视频国产在线观看| 极品日韩久久| 麻豆一区二区99久久久久| 日产欧产va高清| 久久精品久久久久久国产 免费| 猫咪成人在线观看| 欧美美女一级片| 欧美午夜精品久久久久久人妖 | 国产精品视频免费观看www| 国内成人在线| 成人18视频免费69| 日韩国产高清视频在线| 日韩三级网址| 亚洲xxx在线观看| 欧美性猛交xxxx黑人|