OpenHarmony 源码解析之多模输入子系统(事件派发流程)
Posted HarmonyOS技术社区
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OpenHarmony 源码解析之多模输入子系统(事件派发流程)相关的知识,希望对你有一定的参考价值。
简介
多模输入系统主要用于接收按键,触摸等输入事件,并且会对这些原始输入事件进行处理,之后再对这些事件进行派发。同时多模输入系统还提供了注入事件的接口,应用可以通过调用这个接口产生输入事件,然后将该输入事件注入到输入系统中进行处理。
输入系统框架
多模输入系统主要是由InputManagerService, InputEventHub, InputEventDistributer来负责处理的。InputManagerService会启动InputEventHub,并且会通过创建子线程的方式来创建InputEventDistributer。当底层传来按键或触摸事件的时候,InputEventHub就会进行读取,并且会对这些原始的输入事件进行处理,处理完后会交给InputEventDistributer进行派发。InputEventDistributer又会通过InputEventClientProxy进行IPC交互的方式发给应用端。
多模输入系统事件派发流程
事件派发流程图
源码分析
下面就对多模输入系统事件派发流程的源码进行分析。
InputManagerService
\\foundation\\graphic\\wms\\services\\wms\\wms.cpp
```c++
int main()
{
DEBUG_PERFORMANCE_REGISTER_SIG();
OHOS::HiFbdevInit();
OHOS::GfxEngines::GetInstance()->InitDriver();
HOS_SystemInit();
OHOS::InputManagerService::GetInstance()->Run();
while (1) {
DEBUG_PERFORMANCE_PRINT_RESULT();
OHOS::LiteWM::GetInstance()->MainTaskHandler();
usleep(WMS_MAIN_TASK_PERIOD_IN_US);
}
}
InputManagerService的启动是在WMS的main函数中通过InputManagerService::GetInstance()->Run()执行的。
\\foundation\\graphic\\wms\\services\\ims\\input_manager_service.cpp
```c++
void InputManagerService::Run()
{
hub_ = InputEventHub::GetInstance();
hub_->RegisterReadCallback(ReadCallback);
hub_->SetUp();
distributerThreadCreated_ = pthread_create(&distributerThread_, nullptr, Distribute, nullptr);
if (!distributerThreadCreated_) {
pthread_detach(distributerThread_);
}
}
在InputManagerService::Run()中首先会创建InputEventHub的对象并通过RegisterReadCallback来注册InputEventHub的回调,然后通过SetUp来启动InputEventHub, InputEventHub主要是用于对底层原始输入事件的读取和处理,该函数的最后会创建distributerThread子线程,用于对输入事件的派发。
InputEventHub
\\foundation\\graphic\\wms\\services\\ims\\input_event_hub.cpp
```c++
void InputEventHub::SetUp()
{
int32t ret = GetInputInterface(&inputInterface);
if (ret != INPUT_SUCCESS) {
GRAPHIC_LOGE("get input driver interface failed!");
return;
}
uint8_t num = ScanInputDevice();
if (num == 0) {
GRAPHIC_LOGE("There is no device!");
return;
}
for (uint8t i = 0; i < num; i++) {
if (inputInterface == nullptr || inputInterface_->iInputManager == nullptr) {
GRAPHICLOGE("input interface or input manager is nullptr, open device failed!");
return;
}
ret = inputInterface->iInputManager->OpenInputDevice(mountDevIndex_[i]);
if (ret == INPUTSUCCESS && inputInterface->iInputReporter != nullptr) {
callback.EventPkgCallback = EventCallback;
ret = inputInterface->iInputReporter->RegisterReportCallback(mountDevIndex[i], &callback);
if (ret != INPUT_SUCCESS) {
GRAPHICLOGE("device dose not exist, can\'t register callback to it!");
return;
}
openDev = openDev_ | (1 << i);
}
}
}
在这个函数中InputEventHub主要的工作就是通过调用驱动层的OpenInputDevice来打开输入设备,并且会将EventCallback的回调函数通过驱动层的RegisterReportCallback进行注册。当底层有事件传递上来,EventCallback就会被调用。OpenInputDevice和RegisterReportCallback具体实现分别是在drivers/peripheral/input/hal/src/input_manager.c和drivers/peripheral/input/hal/src/input_reporter.c中。
\\foundation\\graphic\\wms\\services\\ims\\input_event_hub.cpp
```c++
void InputEventHub::EventCallback(const EventPackage **pkgs, uint32_t count, uint32_t devIndex)
{
if (pkgs == nullptr || readCallback_ == nullptr || count == 0) {
return;
}
RawEvent& data = InputEventHub::GetInstance()->data_;
for (uint32_t i = 0; i < count; i++) {
if (pkgs[i]->type == EV_REL) {
data.type = InputDevType::INDEV_TYPE_MOUSE;
if (pkgs[i]->code == REL_X)
data.x += pkgs[i]->value;
else if (pkgs[i]->code == REL_Y)
data.y += pkgs[i]->value;
} else if (pkgs[i]->type == EV_ABS) {
data.type = InputDevType::INDEV_TYPE_TOUCH;
if (pkgs[i]->code == ABS_MT_POSITION_X)
data.x = pkgs[i]->value;
else if (pkgs[i]->code == ABS_MT_POSITION_Y)
data.y = pkgs[i]->value;
} else if (pkgs[i]->type == EV_KEY) {
if (pkgs[i]->code == BTN_MOUSE || pkgs[i]->code == BTN_TOUCH) {
if (pkgs[i]->value == 0)
data.state = 0;
else if (pkgs[i]->value == 1)
data.state = 1;
}
} else if (pkgs[i]->type == EV_SYN) {
if (pkgs[i]->code == SYN_REPORT) {
break;
}
}
}
readCallback_(&data);
}
当底层有输入事件上来的话,EventCallback就会被调用,在这个函数里会通过EventPackage->type来判断输入事件的类型,其中
EV_REL是相对坐标的输入事件,比如轨迹球,鼠标事件
EV_ABS是绝对坐标的输入事件,比如触屏触摸事件
EV_KEY是按键输入事件,比如设备上的物理按键的点击事件
EV_SYN是Motion的一系列动作结束标志位
如果是鼠标事件,会将相对坐标值放入到data.x和data.y中,如果是触屏触摸事件,会将在触屏上触摸的坐标位置放入到data.x和data.y中,如果是按键事件会将按键的点击状态放入到data.state中。
处理完输入事件后,会将数据放入到data中,并通过readCallback传给InputManagerService进行处理,之后就会调用InputManagerService::ReadCallback。
\\foundation\\graphic\\wms\\services\\ims\\input_manager_service.cpp
```c++
void InputManagerService::ReadCallback(const RawEvent* event)
{
if (event == nullptr) {
return;
}
pthread_mutexlock(&lock);
while (eventQueue_.size() == MAX_EVENT_SIZE) {
pthread_condwait(&nonFull, &lock);
}
// push events into queue
eventQueue.push(event[0]);
pthread_mutexunlock(&lock);
pthread_condsignal(&nonEmpty);
}
void InputManagerService::Distribute(void args)
{
GRAPHIC_LOGI("InputManagerService::Distribute Ready to read distribute!");
while (true) {
pthread_mutexlock(&lock);
while (eventQueue_.size() == 0) {
pthread_condwait(&nonEmpty, &lock_);
}
// pop events from queue
RawEvent events[MAX_INPUT_DEVICE_NUM];
int32t len = (eventQueue.size() > MAX_EVENT_SIZE) ? MAX_EVENTSIZE : eventQueue.size();
for (int32t i = 0; i < len; i++) {
events[i] = eventQueue.front();
eventQueue.pop();
}
distributer.Distribute(events, len);
pthread_mutexunlock(&lock);
pthread_condsignal(&nonFull);
}
return nullptr;
}
ReadCallback这个函数首先会判断eventQueue这个事件队列里事件数量是否达到最大数量,如果达到最大数量该线程就一直等待,否则就会把该事件放到eventQueue这个事件队列里,并且同时也会发出nonEmpty的signal, 来让Distribute中的线程停止等待。
Distribute函数中,当eventQueue队列里没有事件的时候,就会一直等待,当有事件来的时候就会停止线程等待,然后会遍历整个eventQueue这个队列,把每个事件获取出来后放入到events这个数组中,并做为参数放入到InputEventDistributer::Distribute中进行事件的派发。
#### InputEventDistributer
\\foundation\\graphic\\wms\\services\\ims\\input_event_distributer.cpp
```c++
void InputEventDistributer::Distribute(const RawEvent* events, int32_t size)
{
for (int32_t i = 0; i < size; i++) {
for (auto listener : rawEventListeners_) {
if (listener != nullptr) {
listener->OnRawEvent(events[i]);
}
}
}
}
这个函数比较简单,主要就是遍历所有的InputEventClientProxy, 并且调用各自的onRawEvent进行实际的派发工作。
InputEventClientProxy
\\foundation\\graphic\\wms\\services\\ims\\input_event_client_proxy.cpp
```c++
void InputEventClientProxy::OnRawEvent(const RawEvent& event)
{
IpcIo io;
uint8_t tmpData[IMS_DEFAULT_IPC_SIZE];
IpcIoInit(&io, tmpData, IMS_DEFAULT_IPC_SIZE, 1);
IpcIoPushFlatObj(&io, static_cast<const void*>(&event), sizeof(RawEvent));
pthread_mutexlock(&lock);
std::map<pidt, ClientInfo>::iterator it;
for (it = clientInfoMap.begin(); it != clientInfoMap.end(); it++) {
if (it->second.alwaysInvoke || (event.state != lastState)) {
SendRequest(nullptr, it->second.svc, 0, &io, nullptr, LITEIPC_FLAGONEWAY, nullptr);
}
}
lastState = event.state;
pthread_mutexunlock(&lock);
}
这个函数主要就是通过ipc的交互方式把输入事件传给应用端。
到此整个多模输入系统的事件派发流程就结束了。
## 多模输入系统接口说明
### 模块
/foundation/multimodalinput/input
├── common # 公共代码
├── interfaces # 对外接口存放目录
│ └── native # 对外native层接口存放目录
│ └── innerkits # 对系统内部子系统提供native层接口存放目录
├── service # 服务框架代码
├── sa_profile # 服务启动配置文件
├── uinput # 输入事件注入模块
通过每个目录下的.gn文件可以看到每个目录下的模块都对应动态库
\\interfaces\\native\\innerkits\\event下的文件编出来的是mmi_event.so
\\interfaces\\native\\innerkits\\napi 下的文件编出来的是injecteventhandler.so
\\interfaces\\native\\innerkits\\proxy 下的文件编出来的是libmultimodalinput_proxy.so
\\service 下的文件编出来的是libmultimodalinput_service.so
\\uinput 下的文件编出来的是mmi_uinject.so
### 接口
多模输入目前提供的接口为事件注入接口,该接口目前仅对系统应用开放。
#### JS接口
InJectEventHandler是处理注入事件类。
| 接口名 | 描述 |
| :------------------------------------------- | :--------------------- |
| function injectEventSync(keyEvent: KeyEvent) | 注入按键事件的JS层接口 |
\\applications\\standard\\systemui\\navigationBar\\src\\main\\js\\default\\pages\\backKey\\backKey.js
```js
export default {
/**
* User start touching the back button
*/
backTouchStart() {
mLog.showInfo(TAG, `back touch start`);
res = input.injectEventSync({
isPressed: true,
keyCode: 2,
keyDownDuration: 1
});
mLog.showInfo(TAG, `injectEventHandler injectEventSync down res: ${res}`);
},
/**
* User stop touching the back button
* Trigger "Back" event
*/
backTouchEnd() {
mLog.showInfo(TAG, `back touch end and injectEventHandler injectEventSync`);
res = input.injectEventSync({
isPressed: false,
keyCode: 2,
keyDownDuration: 1
});
mLog.showInfo(TAG, `injectEventHandler injectEventSync up res: ${res}`);
}
}
可以从openharmony systemui的navigationbar的源码中看到, 当点击navigationbar的back键的时候,就会调用js的接口函数injectEventSync,并传入三个参数,其中
isPress: 按键的状态,true表示down, false表示up
keyCode:键值码,2表示back事件
keyDownDuration:按键按下到抬起之间的时长,单位ms,1表示1ms
C++接口
接口名 | 描述 |
---|---|
bool InjectEvent(const sptr<MultimodalEvent> event) | 注入按键事件的C++层接口 |
系统内部接口
在\\interfaces\\native\\innerkits\\events\\include下的头文件都定义了各自对内部系统调用的口。
KeyEvent的主要接口
接口名 | 描 |
---|---|
getKeyCode() | 获取当前按键类事件的keycode值。 |
getMaxKeyCode() | 获取当前定义的按键事件的最大keycode值。 |
getKeyDownDuration() | 获取当前按键被按下的持续时长。 |
isKeyDown() | 获取当前按键事件是否是按下状态。 |
KeyBoardEvent的主要接口
接口名 | 描述 |
---|---|
enableIme() | 启动输入法编辑器。 |
disableIme() | 关闭输入法编辑器。 |
isHandledByIme() | 判断输入法编辑器是否在使用。 |
isNoncharacterKeyPressed(int keycode) | 判定输入的单个NoncharacterKey是否处于按下状态。 |
isNoncharacterKeyPressed(int keycode1, int keycode2) | 判定输入的两个NoncharacterKey是否都处于按下状态。 |
isNoncharacterKeyPressed(int keycode1, int keycode2, int keycode3) | 判定输入的三个NoncharacterKey是否都处于按下状态。 |
getUnicode() | 获取按键对应的Unicode码。 |
ManipulationEvent的主要接口
接口名 | 描述 |
---|---|
getPointerCount() | 获取一次事件中触控或轨迹追踪的指针数量。 |
getPointerId(int index) | 获取一次事件中,指针的唯一标识Id。 |
setScreenOffset(float offsetX, float offsetY) | 设置相对屏幕坐标原点的偏移位置。 |
getPointerPosition(int index) | 获取一次事件中触控或轨迹追踪的某个指针相对于偏移位置的坐标。 |
getPointerScreenPosition(int index) | 获取一次事件中触控或轨迹追踪的某个指针相对屏幕坐标原点的坐标。 |
getRadius(int index) | 返回指定index手指与屏幕接触的半径值。 |
getForce(int index) | 获取指定index手指触控的压力值。 |
getStartTime() | 获取操作开始时间。 |
getPhase() | 获取事件所属阶段。 |
MmiPoint的主要接口
接口名 | 描述 |
---|---|
MmiPoint(float px, float py) | 创建一个只包含x和y坐标的MmiPoint对象。 |
MmiPoint(float px, float py, float pz) | 创建一个包含x,y和z坐标的MmiPoint对象。 |
getX() | 获取x坐标值。 |
getY() | 获取y坐标值。 |
getZ() | 获取z坐标值。 |
toString() | 返回包含x、y、z坐标值信息的字符串 |
MouseEvent的主要接口
接口名 | 描述 |
---|---|
getAction() | 获取鼠标设备产生事件的行为。 |
getActionButton() | 获取状态发生变化的鼠标按键。 |
getPressedButtons() | 获取所有按下状态的鼠标按键。 |
getCursor() | 获取鼠标指针的位置。 |
getCursorDelta(int axis) | 获取鼠标指针位置相对上次的变化值。 |
setCursorOffset(float offsetX, float offsetY) | 设置相对屏幕的偏移位置信息。 |
getScrollingDelta(int axis) | 获取滚轮的滚动值。 |
MultimodalEvent的主要接口
接口名 | 描述 |
---|---|
getDeviceId() | 获取输入设备所在的承载设备id,如当同时有两个鼠标连接到一个机器上,该机器为这两个鼠标的承载设备。 |
getInputDeviceId() | 获取产生当前事件的输入设备id,该id是该输入设备的唯一标识,如两个鼠标同时输入时,它们会分别产生输入事件,且从事件中获取到的deviceid是不同的,开发者可以将此id用来区分实际的输入设备源。 |
getSourceDevice() | 获取产生当前事件的输入设备类型。 |
getOccurredTime() | 获取产生当前事件的时间。 |
getUuid() | 获取事件的UUID。 |
isSameEvent(UUID id) | 判断当前事件与传入id的事件是否为同一事件。 |
isHighLevelInput() | 判断当前事件是否是一个高级事件 |
getHighLevelEvent() | 获取当前的高级事件 |
StylusEvent的主要接口
接口名 | 描述 |
---|---|
getAction() | 获取触笔设备产生事件的行为。 |
getButtons() | 获取触笔设备的按键。 |
TouchEvent的主要接口
接口名 | 描述 |
---|---|
getAction() | 获取触屏设备产生事件的行为。 |
getIndex() | 获取点击index手指 |
getForcePrecision() | 获取手指按压的压力值 |
getMaxForce() | 获取手指按压的最大压力值 |
getTapCount() | 获取点击的数量 |
getPhase() | 获取事件所属阶段 |
InjectEvent的实现逻辑
\\foundation\\multimodalinput\\input\\interfaces\\native\\innerkits\\napi\\src\\key_event_handler.cpp
```c++
static napi_value InjectEventSync(napi_env env, napi_callback_info info)
{
size_t argc = 2;
napi_value args[2] = { 0 };
napi_value thisArg = nullptr;
void* data = nullptr;
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &thisArg, &data));
napi_value eventObject = args[0];
int32_t ret = IsMatchType(eventObject, napi_object, env);
if (ret) {
return GetNapiInt32_t(ret, env);
}
napi_value isPressed, keyCode, keyDownDuration;
napi_get_named_property(env, eventObject, "isPressed", &isPressed);
napi_get_named_property(env, eventObject, "keyDownDuration", &keyDownDuration);
napi_get_named_property(env, eventObject, "keyCode", &keyCode);
if (IsMatchType(isPressed, napi_boolean, env) || IsMatchType(keyCode, napi_number, env)
|| IsMatchType(keyDownDuration, napi_number, env)) {
return GetNapiInt32_t(-1, env);
}
OHOS::KeyProperty keyProperty = {
.isPressed = GetCppBool(isPressed, env),
.keyCode = GetCppInt32_t(keyCode, env),
.keyDownDuration = GetCppInt32_t(keyDownDuration, env),
};
OHOS::MultimodalProperty multimodalProperty {
.highLevelEvent = 1,
.uuid = "11111",
.sourceType = 1,
.occurredTime = 1,
.deviceId = "11111",
.inputDeviceId = 1,
.isHighLevelEvent = true,
};
OHOS::sptr<OHOS::KeyEvent> event = new OHOS::KeyEvent();
if (!event) {
return GetNapiInt32_t(-1, env);
}
event->Initialize(multimodalProperty, keyProperty);
std::shared_ptr<OHOS::InjectManager> injectManager = OHOS::InjectManager::GetInstance();
bool isSucceed = injectManager->InjectEvent(event);
if (!isSucceed) {
return GetNapiInt32_t(-1, env);
}
return GetNapiInt32_t(0, env);
}
在key_event_handler.cpp中实现了InjectEventSync这个接口,通过NAPI获得应用端的isPressed,KeyDownDuration,KeyCode这三个数值,并将这三个参数放入到KeyProperty这个结构体中。然后调用KeyEvent的Initialize,将KeyProperty封装到KeyEvent中,最后再调用InjectManager的InjectEvent。
\\foundation\\multimodalinput\\input\\interfaces\\native\\innerkits\\proxy\\src\\inject_manager.cpp
```c++
bool InjectManager::InjectEvent(const sptr<MultimodalEvent> event)
{
std::lock_guard<std::mutex> guard(lock_);
if (!multimodalInputService_) {
return false;
}
int32_t result = multimodalInputService_->InjectEvent(event);
if (result == 0) {
return true;
}
MMI_LOGI("inject failed");
return false;
}
foundation\\multimodalinput\\input\\interfaces\\native\\innerkits\\proxy\\include\\inject_manager.h
```c++
sptr<IMultimodalInputService> multimodalInputService_{nullptr};
multimodalInputService_->InjectEvent其实是一个IPC进程间调用,这会调用到客户端的MultimodalInputServiceProxy的InjectEvent。
foundation\\multimodalinput\\input\\interfaces\\native\\innerkits\\proxy\\src\\multimodal_input_service_proxy.cpp
```c++
int32_t MultimodalInputServiceProxy::InjectEvent(const sptr<MultimodalEvent> &event)
{
MessageParcel data;
MessageParcel reply;
MessageOption option(MessageOption::TF_ASYNC);
if (!data.WriteInterfaceToken(MultimodalInputServiceProxy::GetDescriptor())) {
HiLog::Error(LABEL, "write descriptor fail");
return ERR_INVALID_VALUE;
}
if (!data.WriteInt32(MultimodalEvent::KEYBOARD)) {
HiLog::Error(LABEL, "write descriptor fail");
return ERR_INVALID_VALUE;
}
if (!data.WriteParcelable(event)) {
HiLog::Error(LABEL, "inject event fail, write event error");
return ERR_INVALID_VALUE;
}
int error = Remote()->SendRequest(INJECT_EVENT, data, reply, option);
if (error != ERR_NONE) {
HiLog::Error(LABEL, "inject event fail, error: %{public}d", error);
}
return error;
}
在MultimodalInputServiceProxy::InjectEvent会通过SendRequest向服务端MultimodalInputServiceStub发送数据。
foundation\\multimodalinput\\input\\service\\src\\multimodal_input_service_stub.cpp
```c++
int MultimodalInputServiceStub::OnRemoteRequest(
uint32_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option)
{
MMI_LOGD("OnReceived, cmd = %{public}u", code);
if (!IsPermissionValid()) {
MMI_LOGE("calling app not acquired multimodal permission");
return MMI_PERMISSION_ERR;
}
std::u16string myDescripter = MultimodalInputServiceStub::GetDescriptor();
std::u16string remoteDescripter = data.ReadInterfaceToken();
if (myDescripter != remoteDescripter) {
MMI_LOGE("descriptor checked fail");
return MMI_BAD_TYPE;
}
switch (code) {
case INJECT_EVENT: {
int32_t type = data.ReadInt32();
if (type == MultimodalEvent::KEYBOARD) {
sptr<MultimodalEvent> event = data.ReadParcelable<KeyEvent>();
return InjectEvent(event);
}
MMI_LOGE("recv bad type %{public}d", type);
return MMI_BAD_TYPE;
}
default: {
MMI_LOGE("default case, need check");
return IPCObjectStub::OnRemoteRequest(code, data, reply, option);
}
}
}
通过sendRequest将数据发送之后,服务端的MultimodalInputServiceStub的OnRemoteRequest就会被调用,最终会调用MultimodaInputService的InjectEvent。
\\foundation\\multimodalinput\\input\\service\\src\\multimodal_input_service.cpp
```C++
int32_t MultimodalInputService::InjectEvent(const sptr<MultimodalEvent> &event)
{
KeyEvent *eventPtr = reinterpret_cast<KeyEvent*>(event.GetRefPtr());
int keycode = eventPtr->GetKeyCode();
int state = 0;
if (eventPtr->IsKeyDown()) {
state = 1;
} else {
state = 0;
}
MMIS::KeyboardInject &inject = OHOS::MMIS::KeyboardInject::GetInstance();
MMI_LOGD("InjectEvent keycode %{public}d, state %{public}d", keycode, state);
inject.InjectKeyEvent(keycode, state);
return 0;
}
MultimodaInputService的InjectEvent实际上会调用KeyboardInject的InjectKeyEvent,从函数的实现来看,目前只使用了KeyboardInject,也就是说目前只支持键盘事件的注入。
\\foundation\\multimodalinput\\input\\uinput\\keyboard_inject.cpp
```c++
void KeyboardInject::InjectKeyEvent(uint16_t code, uint32_t value) const
{
std::lockguard<std::mutex> keyboardLock(mutex);
auto it = keyCodeMap.find(code);
if (it == keyCodeMap.end()) {
return;
}
InjectInputEvent injectInputEvent = {injectThread_->KEYBOARD_DEVICE_ID, EVKEY, it->second, value};
injectThread->WaitFunc(injectInputEvent);
InjectInputEvent injectInputSync = {injectThread_->KEYBOARD_DEVICE_ID, EV_SYN, SYNREPORT, 0};
injectThread->WaitFunc(injectInputSync);
}
在InjectKeyEvent中会通过InjectInputEvent的WaitFunc将注入事件继续向下注入。
\\foundation\\multimodalinput\\input\\uinput\\inject_thread.cpp
```c++
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();
}
在WaitFunc中会将injectInputEvent放入到injectQueue这个队列中,这个队列是用来存放injectInputEvent的,并且通过notify_one来唤醒InjectThread,由于目前只支持键盘类型事件的注入,所有只会调用g_pKeyboard->EmitEven(),g_pKeyboard是VirtualKeyboard的对象,VirtualKeyboard又继承自VirtualDevice,因此最终会调用VirtualKeyboard的EmitEvent。
foundation\\multimodalinput\\input\\uinput\\virtual_device.cpp
```c++
bool VirtualDevice::EmitEvent(uint16_t type, uint16_t code, uint32_t value) const
{
struct inputevent 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;
}
bool VirtualDevice::SetUp()
{
fd_ = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
在该函数中会将这个注入事件写入到文件描述符为fd_的设备文件中,从SetUp的函数中可以看出实际是写入到/dev/uinput这个设备文件中。
到此多模输入系统接口的介绍以及InjectEvent整个注入事件的流程就结束了。
## 总结
通过本文的学习可以了解多模输入系统事件派发的流程,以及多模输入系统的接口和注入事件的流程,结合以上的源码分析会对多模输入子系统会有更深入的理解。
作者:孙仲毅
更多原创内容请关注:[开鸿 HarmonyOS 学院](https://harmonyos.51cto.com/column/59)
入门到精通、技巧到案例,系统化分享HarmonyOS开发技术,欢迎投稿和订阅,让我们一起携手前行共建鸿蒙生态。
[想了解更多关于鸿蒙的内容,请访问:](https://harmonyos.51cto.com/#bkwz)
[51CTO和华为官方战略合作共建的鸿蒙技术社区](https://harmonyos.51cto.com/#bkwz)
https://harmonyos.51cto.com/#bkwz
以上是关于OpenHarmony 源码解析之多模输入子系统(事件派发流程)的主要内容,如果未能解决你的问题,请参考以下文章
OpenHarmony 源码解析之安全子系统 (应用权限管理)
OpenHarmony 源码解析之DFX子系统-Hiview(上)
OpenHarmony 源码解析之DFX子系统-标准系统接口使用说明
OpenHarmony ACE源码解析之JavaScript运行环境初始