精品欧美一区二区三区在线观看 _久久久久国色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技術棧公眾號

中文字幕在线视频网站| 亚洲精品97久久中文字幕无码| 欧美精选一区二区三区| 欧美日韩高清不卡| 日韩欧美精品免费| 国产视频精品久久| 日本欧美韩国一区三区| 九九热精品视频| 亚洲区自拍偷拍| 亚洲日本va中文字幕| 日本韩国精品一区二区在线观看| 日韩一二区视频| 国产免费av高清在线| 风流少妇一区二区| 国产精品普通话| www.av视频| 欧美限制电影| 日韩黄色高清视频| 日韩av影视大全| 欧美黄色三级| 舔着乳尖日韩一区| 伊人婷婷久久| 九色在线观看| 不卡在线观看av| 成人妇女免费播放久久久| 波多野结衣国产| 欧美精品黄色| 日韩有码视频在线| 国产真人做爰视频免费| 999久久久久久久久6666| 欧美亚洲国产一区在线观看网站| 国产精品无码一区二区在线| av毛片在线免费| 亚洲国产精品黑人久久久| 久久99久久精品国产| 亚洲av永久无码国产精品久久| 另类调教123区| 国产成人亚洲综合91精品| 久久精品女人毛片国产| 欧美一区二区三区久久精品| 久久精品国产电影| 国产人与禽zoz0性伦| 久久不卡国产精品一区二区 | 亚洲精品中文字幕99999| 日韩一区二区在线观看视频播放| 国产又大又黄又粗又爽| 粉嫩一区二区三区| 欧美性xxxx极品hd欧美风情| 91丨porny丨探花| 人人澡人人添人人爽一区二区| 国产精品国产三级国产aⅴ入口| 日本公妇乱淫免费视频一区三区| 色吊丝在线永久观看最新版本| 不卡一二三区首页| 国产精品国产三级欧美二区| www.中文字幕| 成人永久免费视频| 国产精品久久国产三级国电话系列| 国产偷拍一区二区| 国产又黄又大久久| 51国偷自产一区二区三区的来源| av中文字幕免费| 国产成人午夜99999| 成人影片在线播放| 成人久久久精品国产乱码一区二区 | 青青草原一区二区| 黄色污污网站在线观看| 日本视频在线一区| 成人黄色午夜影院| 99久久亚洲精品日本无码| 国产一区 二区 三区一级| 亚洲综合最新在线| 日韩一级片免费观看| av成人动漫在线观看| 欧美日韩亚洲一区二区三区在线观看 | 日韩欧美国产综合在线| 国产精品蜜芽在线观看| 色网站国产精品| 亚洲36d大奶网| 欧美一区一区| 亚洲国产精品一区二区久| 少妇精品一区二区三区| 色综合久久网| 欧美劲爆第一页| 黄色av一级片| 国产在线不卡视频| 国产精品一码二码三码在线| 久久免费看视频| 一区二区中文字幕在线| www.av毛片| 国产极品久久久久久久久波多结野| 欧美猛男gaygay网站| 性欧美18—19sex性高清| 亚洲自拍都市欧美小说| 久久久精品999| 日韩av一二三区| 蜜桃av一区二区三区| 成人女人免费毛片| 亚洲区小说区图片区| 中文字幕在线不卡一区| 狠狠干 狠狠操| 欧美午夜三级| 日韩精品免费在线视频| 999精品在线视频| 国产精品久久久免费| 亚洲www永久成人夜色| 日韩一区二区三区中文字幕| 亚洲美女屁股眼交| 欧美少妇性生活视频| 中文字幕一区二区三区四区久久| 亚洲天堂av高清| 久久免费公开视频| 捆绑变态av一区二区三区| 国产精品一区二区三区免费观看| 在线观看免费网站黄| 偷拍一区二区三区四区| 红桃视频一区二区三区免费| 欧美人与拘性视交免费看| 欧美激情在线狂野欧美精品| 欧美性受xxx黑人xyx性爽| 99久久婷婷国产综合精品电影| ijzzijzzij亚洲大全| 日本精品在线中文字幕| 亚洲精品97久久| 欧美日韩国产精品一区二区三区| 美女看a上一区| 欧美日韩大片一区二区三区| 91资源在线观看| 欧美一级国产精品| 亚洲一区电影在线观看| 日本在线不卡视频| 蜜桃在线一区二区三区精品| 岛国毛片av在线| 日韩欧美国产wwwww| 日本在线一级片| 久久www免费人成看片高清| 日韩精品电影网站| 最近高清中文在线字幕在线观看1| 精品成人一区二区| 欧美性猛交xxxxx少妇| 国产制服丝袜一区| 一区不卡字幕| 色8久久久久| 中文字幕国产亚洲2019| 特级西西444www高清大视频| 国产性色一区二区| 久久精品午夜福利| 精品国产一区二区三区久久久蜜臀| 欧美性在线观看| 你懂得在线网址| 国产suv精品一区| 成人免费黄色大片| 99亚洲国产精品| 亚洲精品18| 欧美激情网站在线观看| 成人爽a毛片一区二区| 亚洲一区影音先锋| 婷婷五月精品中文字幕| 一区二区国产在线观看| 欧美人与性禽动交精品| av成人免费看| 日韩在线www| 亚洲av无码乱码在线观看性色| 亚洲一区二区成人在线观看| 精品人妻一区二区免费| 国产日韩欧美一区| 欧美色图亚洲自拍| 日韩漫画puputoon| 久久精品国产清自在天天线| 国产黄色一区二区| 午夜国产精品一区| 变态另类丨国产精品| 日韩高清不卡在线| 大桥未久一区二区| 电影一区二区在线观看| 欧美诱惑福利视频| 伊人免费在线| 精品精品国产高清a毛片牛牛| 丰满少妇乱子伦精品看片| 久久精品人人做人人爽人人| 99九九99九九九99九他书对| 尤物网精品视频| 欧美日韩精品中文字幕一区二区| 91九色成人| 性欧美xxxx视频在线观看| 能在线看的av| 日韩欧美一区二区免费| 日本视频在线观看免费| 亚洲欧洲日韩av| 欧美bbbbb性bbbbb视频| 久久国产精品99久久人人澡| 精品国产av无码一区二区三区 | 韩国三级电影一区二区| 精品婷婷色一区二区三区蜜桃| 456成人影院在线观看| 欧美美女18p| 精品美女视频在线观看免费软件 | 国产精品99久久久久久白浆小说| 国产精品va在线观看视色| 日韩电影免费在线观看中文字幕| 亚洲天天综合网| 五月婷婷综合激情| 久久福利免费视频| 99久久er热在这里只有精品15| 婷婷免费在线观看| 国产精品美女久久久| 日本一级淫片演员| 国产不卡一区| 都市激情久久久久久久久久久| julia一区二区三区中文字幕| 欧美激情一区二区三区久久久 | 一区二区三区av电影| 熟女少妇内射日韩亚洲| 99久久精品久久久久久清纯| 超碰在线免费av| 免费看日韩精品| 男人天堂999| 亚洲人成在线影院| 成人高清dvd| 97精品国产| 日日噜噜噜噜夜夜爽亚洲精品| 日本成人7777| 国产乱码精品一区二区三区卡| 亚洲精品伊人| 国产精品入口福利| 欧美成人黑人| 热久久免费视频精品| 18video性欧美19sex高清| 萌白酱国产一区二区| 在线播放麻豆| 一区二区亚洲欧洲国产日韩| 青青操视频在线| 日韩av有码在线| www.色日本| 日韩精品中文字幕在线一区| 国产女人18毛片水18精| 欧美日韩免费在线视频| 性高潮视频在线观看| 一本大道久久a久久精二百 | 欧美一区二区免费视频| 一级全黄少妇性色生活片| 欧美三级视频在线观看| 久久这里只有精品9| 日本久久电影网| 中文字幕高清在线免费播放| 日韩欧美在线视频免费观看| 日韩 欧美 综合| 精品美女永久免费视频| 日本网站免费观看| 狠狠干狠狠久久| 久草视频一区二区| 91久久精品午夜一区二区| 波多野结衣理论片| 欧美优质美女网站| 丰满熟女人妻一区二区三| 欧美少妇一区二区| 一级全黄裸体免费视频| 欧美一卡二卡三卡| 精品国产一级片| 亚洲丁香婷深爱综合| 性xxxx搡xxxxx搡欧美| 亚洲欧洲高清在线| 91电影在线播放| 久久精品中文字幕电影| 日本色护士高潮视频在线观看| 欧美激情视频免费观看| 亚洲最大网站| 国产精品日韩在线播放| 综合欧美精品| 国产亚洲自拍偷拍| 欧美人妖在线| 99精品一区二区三区的区别| 亚洲三级观看| 欧美伦理片在线看| 国产一区视频在线看| 乱码一区二区三区| 久久免费午夜影院| 亚洲aaa视频| 一区二区三区波多野结衣在线观看| 51国产偷自视频区视频| 欧美三级午夜理伦三级中视频| www.色婷婷.com| 亚洲欧美日韩图片| 草莓福利社区在线| 97在线观看免费| 欧美男男gaygay1069| 国产综合av一区二区三区| 日韩dvd碟片| 成年人看的毛片| 奇米影视一区二区三区| 野花视频免费在线观看| 久久精品无码一区二区三区| 欧美成人黄色网| 色天天综合色天天久久| 精品国产九九九| 亚洲人成在线观看| a免费在线观看| 国产成人精品电影| 亚洲午夜精品| 一区二区精品免费视频| 国产精品久久久久久久免费软件| 国产无色aaa| 久久久精品综合| 久久久久久久久精| 欧美视频日韩视频| 飘雪影院手机免费高清版在线观看| 久久资源免费视频| 日日av拍夜夜添久久免费| 99高清视频有精品视频| 欧美美女一区| 国产亚洲精品网站| 风间由美性色一区二区三区 | 亚洲二区精品| 在线观看中文av| 国产精品天干天干在线综合| 特级做a爱片免费69| 精品国产伦一区二区三区观看方式 | 国产在线播放观看| 国模少妇一区二区三区| 日本成人午夜影院| 欧美性色xo影院| 图片区 小说区 区 亚洲五月| 欧美成人精品在线观看| 日韩毛片一区| 欧洲亚洲一区二区| 午夜亚洲精品| 黄色录像a级片| 亚洲电影一区二区三区| 99在线观看精品视频| 久久久国产一区二区三区| 成人黄色免费观看| 日韩欧美一区二区在线观看 | 国产原创av在线| 欧美在线亚洲在线| 亚洲va久久久噜噜噜久久| 97视频久久久| 成人av在线网| 日本一二三区不卡| 亚洲国产欧美日韩精品| 黑人玩欧美人三根一起进| 7777精品久久久大香线蕉小说| 国产精品成人a在线观看| wwwwwxxxx日本| 1区2区3区精品视频| 91禁在线观看| 久久精品国产视频| 国产精品久一| 免费在线黄网站| av电影一区二区| youjizz在线视频| 亚洲热线99精品视频| 亚洲不卡系列| youjizz.com亚洲| 国产成人午夜电影网| 国产精品99无码一区二区| 亚洲精品av在线| 人成在线免费网站| 欧美亚洲丝袜| 免费观看日韩av| 婷婷久久综合网| 精品免费一区二区三区| xxx.xxx欧美| 免费成人av网站| 秋霞成人午夜伦在线观看| 手机免费观看av| 欧美一级在线免费| av蜜臀在线| 欧美久久久久久| 麻豆成人久久精品二区三区小说| www.av免费| 亚洲成在人线av| 日韩成人动漫| 天天操天天干天天玩| 成人a免费在线看| 超碰在线免费97| 久久亚洲欧美日韩精品专区| 国产香蕉精品| 91香蕉视频导航| 一二三四社区欧美黄| 日本不卡免费播放| 成人美女免费网站视频| 亚洲国产婷婷| 国产精品www爽爽爽| 日韩丝袜情趣美女图片| 涩涩视频在线免费看| 亚洲午夜久久久影院伊人| 国产福利一区在线| 伊人中文字幕在线观看| 久久久精品日本| 欧美挤奶吃奶水xxxxx| 午夜免费一级片| 狠狠做深爱婷婷久久综合一区 | 东京干手机福利视频| 国产成人极品视频| 欧美精品国产一区二区| 在线观看免费小视频| 亚洲变态欧美另类捆绑| 成人在线视频免费| 99热自拍偷拍| 亚洲精选视频免费看|