OpenHarmony源码解析之基于wayland的输入系统 原创 精华
作者:孙仲毅
简介
在之前一篇文章《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
当有事件上来的时候就会回调GetEventCallbackDispatch
foundation\multimodalinput\input\uinput\hdf_device_event_dispatch.cpp
然后通过InjectThread::WaitFunc准备对事件进行注入,在该函数中会通过notify_one来唤醒InjectFunc这个函数
foundation\multimodalinput\input\uinput\inject_thread.cpp
最终会调用VirtualDevice::EmitEvent, 在该函数中会将事件写入到uinput的设备文件中。
foundation\multimodalinput\input\uinput\virtual_device.cpp
当uinput有上报输入事件的时候,fd就会发生变化从而就会调用回调函数libinput_source_dispatch,再继续调用udev_input_dispatch,在udev_input_dispatch中再继续调用process_events。
third_party\weston\libweston\libinput-seat.c
在process_events中会遍历每个event,然后调用process_event来处理每个event。
third_party\weston\libweston\libinput-seat.c
在process_event中,udev_input_process_event这个函数是处理设备的添加和删除,evdev_device_process_event_l这个函数是处理输入事件的。
third_party\weston\libweston\libinput-seat.c
在这个函数中会根据不同的事件类型调用不同事件类型的处理函数
third_party\weston\libweston\libinput-device.c
先以key事件为例,看handle_keyboard_key这个函数,在这个函数中会获取按键的状态(按下和抬起),然后通过notify_key来派发事件。
third_party\weston\libweston\libinput-device.c
在notiyf_key这个函数中,会执行grab->interface->key,grab是指向weston_keyboard_grab这个结构体的函数指针,grab->interface->key其实就是调用default_grab_keyboard_key。
third_party\weston\libweston\input.c
在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
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
在wl_resource_post_event中会继续调用wl_resource_post_event_array,在调用handle_array的时候会传入函数指针wl_closure_send。
third_party\wayland_standard\src\wayland-server.c
在handle_array中,会调用send_func这个函数指针说指向的函数,实际上就是调用wl_closure_send这个函数。
third_party\wayland_standard\src\wayland-server.c
wl_connection代表Server与Client的连接,其中包含了in buffer和out buffer,分别作为输入和输出的缓冲区。在wl_closure_send这个函数中会调用wl_connection_write向wl_connection写入数据。
third_party\wayland_standard\src\connection.c
在该函数中会通过wl_connection_flush向Client发送数据, 把connection中out buffer的request通过socket发出去
third_party\wayland_standard\src\connection.c
那么,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
在该函数中继续调用dispatch_queue。
third_party\wayland_standard\src\wayland-client.c
在该函数中会将前面插入到queue当中的event(wl_closure)依次拿出来处理调用dispatch_event。
third_party\wayland_standard\src\wayland-client.c
在该函数中最后通过wl_closure_invoke()进行调用。
third_party\wayland_standard\src\wayland-client.c
wl_closure_invoke其实是回调implementation指向的回调函数
third_party\wayland_standard\src\connection.c
在RegisterKeyboardListener中注册了各种回调函数OnKeyboardKeymap,OnKeyboardEnter,OnKeyboardLeave,OnKeyboardKey,OnKeyboardModifiers,OnKeyboardRepeatInfo。
foundation\graphic\standard\frameworks\wm\src\input_listener_manager.cpp
这些回调函数要被调用,首先Client端需要监听这个wl_proxy,这时InputListenerManager通过RegisterKeyboardListener将回调函数注册到listener中,然后再调用wl_keyboard_add_listener。
out\ohos-arm-release\gen\third_party\wayland_standard\protocol\wayland-client-protocol.h
这是会通过wl_proxy_add_listener将回调函数放入到proxy->object.implementation中。也就是说wanland client接收到事件后最终会回调InputListenerManager注册的回调函数。
third_party\wayland_standard\src\wayland-client.c
现在再看回调函数OnKeyboardKey,接着调用listener->keyboardKey。
foundation\graphic\standard\frameworks\wm\src\input_listener_manager.cpp
从这个添加监听的函数可以看出,当调用keyboardKey的时候会调用KeyboardHandleKey。
foundation\graphic\standard\frameworks\wm\src\multimodal_listener_manager.cpp
在KeyboardHandleKey中会对按键信息进行处理,并且会把按键状态,键值,按键按下的时长这些值放入到KeyProperty结构体中,然后把KeyProperty封装成KeyEvent的数据结构中用于派发。
foundation\graphic\standard\frameworks\wm\src\multimodal_listener_manager.cpp
由于目前openharmony代码在ACE部分没有完全支持对按键事件的处理,所以下面分析对touch事件的处理。现在也可以看TouchHandleUp这个回调,这个函数会调用ml->onTouchCb,
foundation\graphic\standard\frameworks\wm\src\multimodal_listener_manager.cpp
通过RegistOnTouchCb注册的方式,最终会调用aceView->DispatchTouchEvent。
foundation\graphic\standard\frameworks\wm\src\client\window_manager_controller_client.cpp
foundation\graphic\standard\frameworks\wm\src\client\window_manager.cpp
foundation\ace\ace_engine\adapter\ohos\cpp\ace_ability.cpp
在DispatchTouchEvent中,会根据事件类型分为mouse event和touch event,这里先分析touch event,所以最后会调用ProcessTouchEvent。
foundation\ace\ace_engine\adapter\ohos\cpp\flutter_ace_view.cpp
执行touchEventCallback_ 函数指针指向的函数,继续看touchEventCallback_ 指向了哪个函数。
foundation\ace\ace_engine\adapter\ohos\cpp\flutter_ace_view.cpp
看来touchEventCallback_是指向了RegisterTouchEventCallback通过参数穿过来的callback, 那继续看调用RegisterTouchEventCallback的callback是什么。
foundation\ace\ace_engine\adapter\ohos\cpp\flutter_ace_view.cpp
在AceContainer::InitializeCallback中可以看出,最终这个callback就是执行pipeline_context的OnTouchEvent。
foundation\ace\ace_engine\adapter\ohos\cpp\ace_container.cpp
在这个函数中最终会调用eventManager_.DispatchTouchEvent。之后就会通过ACE的接口把事件传给应用端。
foundation\ace\ace_engine\frameworks\core\pipeline\pipeline_context.cpp
总结
通过本篇文章的学习可以了解OpenHarmony L2系统基于wayland三方库的事件处理派发流程。
更多原创内容请关注:深开鸿技术团队
入门到精通、技巧到案例,系统化分享HarmonyOS开发技术,欢迎投稿和订阅,让我们一起携手前行共建鸿蒙生态。
感谢孙老师分享,非常详细的讲解。
太牛了,讲的太透彻了
框架讲解很清晰,收获颇多,感谢作者的分享,期待新的文章
孙老师真的是大佬, Input 这块能分析得这么透彻... 收藏下来对着源码慢慢学~
请教孙老师,这篇文章是基于哪个分支的哪个版本分析的啊?文中提到的源码有些找不到。
就贼详细