Skip to content

Commit a1243b4

Browse files
authored
Mouse keyboard adaptation for HarmonyOS Next (#18656)
1 parent 548aa0b commit a1243b4

File tree

9 files changed

+230
-9
lines changed

9 files changed

+230
-9
lines changed

native/cocos/platform/openharmony/OpenHarmonyPlatform.cpp

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,139 @@ void onSurfaceShowCB(OH_NativeXComponent* component, void* window) {
167167
sendMsgToWorker(cc::MessageType::WM_XCOMPONENT_SURFACE_SHOW, component, window);
168168
}
169169

170+
int ohKeyCodeToCocosKeyCode(OH_NativeXComponent_KeyCode ohKeyCode){
171+
static const int keyZeroInCocos = 48;
172+
static const int keyF1InCocos = 112;
173+
static const int keyAInCocos = 65;
174+
static std::unordered_map<OH_NativeXComponent_KeyCode, cc::KeyCode> keyCodeMap = {
175+
{KEY_ESCAPE, cc::KeyCode::ESCAPE},
176+
{KEY_GRAVE, cc::KeyCode::BACKQUOTE},
177+
{KEY_MINUS, cc::KeyCode::MINUS},
178+
{KEY_EQUALS, cc::KeyCode::EQUAL},
179+
{KEY_DEL, cc::KeyCode::BACKSPACE},
180+
{KEY_TAB, cc::KeyCode::TAB},
181+
{KEY_LEFT_BRACKET, cc::KeyCode::BRACKET_LEFT},
182+
{KEY_RIGHT_BRACKET, cc::KeyCode::BRACKET_RIGHT},
183+
{KEY_BACKSLASH, cc::KeyCode::BACKSLASH},
184+
{KEY_CAPS_LOCK, cc::KeyCode::CAPS_LOCK},
185+
{KEY_SEMICOLON, cc::KeyCode::SEMICOLON},
186+
{KEY_APOSTROPHE, cc::KeyCode::QUOTE},
187+
{KEY_ENTER, cc::KeyCode::ENTER},
188+
{KEY_SHIFT_LEFT, cc::KeyCode::SHIFT_LEFT},
189+
{KEY_COMMA, cc::KeyCode::COMMA},
190+
{KEY_PERIOD, cc::KeyCode::PERIOD},
191+
{KEY_SLASH, cc::KeyCode::SLASH},
192+
{KEY_SHIFT_RIGHT, cc::KeyCode::SHIFT_RIGHT},
193+
{KEY_CTRL_LEFT, cc::KeyCode::CONTROL_LEFT},
194+
{KEY_ALT_LEFT, cc::KeyCode::ALT_LEFT},
195+
{KEY_SPACE, cc::KeyCode::SPACE},
196+
{KEY_ALT_RIGHT, cc::KeyCode::ALT_RIGHT},
197+
{KEY_CTRL_RIGHT, cc::KeyCode::CONTROL_RIGHT},
198+
{KEY_DPAD_LEFT, cc::KeyCode::ARROW_LEFT},
199+
{KEY_DPAD_RIGHT, cc::KeyCode::ARROW_RIGHT},
200+
{KEY_DPAD_DOWN, cc::KeyCode::ARROW_DOWN},
201+
{KEY_DPAD_UP, cc::KeyCode::ARROW_UP},
202+
{KEY_INSERT, cc::KeyCode::INSERT},
203+
};
204+
if(keyCodeMap.find(ohKeyCode) != keyCodeMap.end()){
205+
return int(keyCodeMap[ohKeyCode]);
206+
}
207+
if(ohKeyCode >= KEY_0 && ohKeyCode <= KEY_9){
208+
return keyZeroInCocos + ohKeyCode - KEY_0;
209+
}
210+
if(ohKeyCode >= KEY_A && ohKeyCode <= KEY_Z){
211+
return keyAInCocos + ohKeyCode - KEY_A;
212+
}
213+
if(ohKeyCode >= KEY_F1 && ohKeyCode <= KEY_F12){
214+
return keyF1InCocos + ohKeyCode - KEY_F1;
215+
}
216+
return ohKeyCode;
217+
}
218+
219+
void dispatchKeyEventCB(OH_NativeXComponent* component, void* window) {
220+
OH_NativeXComponent_KeyEvent* keyEvent;
221+
if (OH_NativeXComponent_GetKeyEvent(component, &keyEvent) >= 0) {
222+
static const int keyCodeUnknownInOH = -1;
223+
static const int keyActionUnknownInOH = -1;
224+
OH_NativeXComponent_KeyAction action;
225+
OH_NativeXComponent_GetKeyEventAction(keyEvent, &action);
226+
OH_NativeXComponent_KeyCode code;
227+
OH_NativeXComponent_GetKeyEventCode(keyEvent, &code);
228+
if (code == keyCodeUnknownInOH || action == keyActionUnknownInOH) {
229+
CC_LOG_ERROR("unknown code and action don't callback");
230+
return;
231+
}
232+
cc::KeyboardEvent* ev = new cc::KeyboardEvent;
233+
ev->windowId = cc::ISystemWindow::mainWindowId;
234+
ev->action = 0 == action ? cc::KeyboardEvent::Action::PRESS : cc::KeyboardEvent::Action::RELEASE;
235+
236+
ev->key = ohKeyCodeToCocosKeyCode(code);
237+
sendMsgToWorker(cc::MessageType::WM_XCOMPONENT_KEY_EVENT, reinterpret_cast<void*>(ev), window);
238+
} else {
239+
CC_LOG_ERROR("OpenHarmonyPlatform::getKeyEventError");
240+
}
241+
}
242+
243+
244+
void dispatchMouseEventCB(OH_NativeXComponent* component, void* window) {
245+
OH_NativeXComponent_MouseEvent mouseEvent;
246+
int32_t ret = OH_NativeXComponent_GetMouseEvent(component, window, &mouseEvent);
247+
if (ret == OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
248+
if (mouseEvent.action == OH_NativeXComponent_MouseEventAction::OH_NATIVEXCOMPONENT_MOUSE_NONE)
249+
return;
250+
cc::MouseEvent* ev = new cc::MouseEvent;
251+
ev->windowId = cc::ISystemWindow::mainWindowId;
252+
ev->x = mouseEvent.x;
253+
ev->y = mouseEvent.y;
254+
switch (mouseEvent.action) {
255+
case OH_NativeXComponent_MouseEventAction::OH_NATIVEXCOMPONENT_MOUSE_PRESS:
256+
ev->type = cc::MouseEvent::Type::DOWN;
257+
break;
258+
case OH_NativeXComponent_MouseEventAction::OH_NATIVEXCOMPONENT_MOUSE_RELEASE:
259+
ev->type = cc::MouseEvent::Type::UP;
260+
break;
261+
case OH_NativeXComponent_MouseEventAction::OH_NATIVEXCOMPONENT_MOUSE_MOVE:
262+
ev->type = cc::MouseEvent::Type::MOVE;
263+
break;
264+
default:
265+
ev->type = cc::MouseEvent::Type::UNKNOWN;
266+
break;
267+
}
268+
switch (mouseEvent.button) {
269+
case OH_NativeXComponent_MouseEventButton::OH_NATIVEXCOMPONENT_LEFT_BUTTON:
270+
ev->button = 0;
271+
break;
272+
case OH_NativeXComponent_MouseEventButton::OH_NATIVEXCOMPONENT_RIGHT_BUTTON:
273+
ev->button = 2;
274+
break;
275+
case OH_NativeXComponent_MouseEventButton::OH_NATIVEXCOMPONENT_MIDDLE_BUTTON:
276+
ev->button = 1;
277+
break;
278+
case OH_NativeXComponent_MouseEventButton::OH_NATIVEXCOMPONENT_BACK_BUTTON:
279+
ev->button = 3;
280+
break;
281+
case OH_NativeXComponent_MouseEventButton::OH_NATIVEXCOMPONENT_FORWARD_BUTTON:
282+
ev->button = 4;
283+
break;
284+
case OH_NativeXComponent_MouseEventButton::OH_NATIVEXCOMPONENT_NONE_BUTTON:
285+
ev->button = -1;
286+
break;
287+
}
288+
if(mouseEvent.action == 1 && mouseEvent.button == 1) {
289+
cc::OpenHarmonyPlatform::getInstance()->isMouseLeftActive = true;
290+
}
291+
if(mouseEvent.action == 2 && mouseEvent.button == 1) {
292+
cc::OpenHarmonyPlatform::getInstance()->isMouseLeftActive = false;
293+
}
294+
sendMsgToWorker(cc::MessageType::WM_XCOMPONENT_MOUSE_EVENT, reinterpret_cast<void*>(ev), window);
295+
} else {
296+
CC_LOG_ERROR("OpenHarmonyPlatform::getMouseEventError");
297+
}
298+
}
299+
300+
void dispatchHoverEventCB(OH_NativeXComponent* component, bool isHover) {
301+
// OpenharmonyPlatform::DispatchHoverEventCB
302+
}
170303

171304
cc::TouchEvent::Type touchTypeTransform(OH_NativeXComponent_TouchEventType touchType) {
172305
if (touchType == OH_NATIVEXCOMPONENT_DOWN) {
@@ -267,6 +400,12 @@ void OpenHarmonyPlatform::setNativeXComponent(OH_NativeXComponent* component) {
267400
OH_NativeXComponent_RegisterCallback(_component, &_callback);
268401
OH_NativeXComponent_RegisterSurfaceHideCallback(_component, onSurfaceHideCB);
269402
OH_NativeXComponent_RegisterSurfaceShowCallback(_component, onSurfaceShowCB);
403+
// register KeyEvent
404+
OH_NativeXComponent_RegisterKeyEventCallback(_component, dispatchKeyEventCB);
405+
// register mouseEvent
406+
_mouseCallback.DispatchMouseEvent = dispatchMouseEventCB;
407+
_mouseCallback.DispatchHoverEvent = dispatchHoverEventCB;
408+
OH_NativeXComponent_RegisterMouseEventCallback(_component, &_mouseCallback);
270409
}
271410

272411
void OpenHarmonyPlatform::enqueue(const WorkerMessageData& msg) {
@@ -338,6 +477,18 @@ void OpenHarmonyPlatform::onMessageCallback(const uv_async_t* /* req */) {
338477
events::Touch::broadcast(*ev);
339478
delete ev;
340479
ev = nullptr;
480+
} else if (msgData.type == MessageType::WM_XCOMPONENT_KEY_EVENT) {
481+
KeyboardEvent* ev = reinterpret_cast<KeyboardEvent*>(msgData.data);
482+
CC_ASSERT(ev != nullptr);
483+
events::Keyboard::broadcast(*ev);
484+
delete ev;
485+
ev = nullptr;
486+
} else if (msgData.type == MessageType::WM_XCOMPONENT_MOUSE_EVENT || msgData.type == MessageType::WM_XCOMPONENT_MOUSE_WHEEL_EVENT ) {
487+
MouseEvent* ev = reinterpret_cast<MouseEvent*>(msgData.data);
488+
CC_ASSERT(ev != nullptr);
489+
events::Mouse::broadcast(*ev);
490+
delete ev;
491+
ev = nullptr;
341492
} else if (msgData.type == MessageType::WM_XCOMPONENT_SURFACE_CREATED) {
342493
CC_LOG_INFO("onMessageCallback WM_XCOMPONENT_SURFACE_CREATED ...");
343494
OH_NativeXComponent* nativexcomponet = reinterpret_cast<OH_NativeXComponent*>(msgData.data);
@@ -483,6 +634,23 @@ void OpenHarmonyPlatform::onSurfaceShow(void* window) {
483634
events::WindowRecreated::broadcast(ISystemWindow::mainWindowId);
484635
}
485636

637+
void OpenHarmonyPlatform::dispatchMouseWheelCB(std::string eventType, float offsetY) {
638+
if(isMouseLeftActive) {
639+
return;
640+
}
641+
if(eventType == "actionUpdate") {
642+
float moveScrollY = offsetY - scrollDistance;
643+
scrollDistance = offsetY;
644+
cc::MouseEvent* ev = new cc::MouseEvent;
645+
ev->windowId = cc::ISystemWindow::mainWindowId;
646+
ev->type = MouseEvent::Type::WHEEL;
647+
ev->x = 0;
648+
ev->y = moveScrollY;
649+
sendMsgToWorker(MessageType::WM_XCOMPONENT_MOUSE_WHEEL_EVENT, reinterpret_cast<void*>(ev), nullptr);
650+
} else {
651+
scrollDistance = 0;
652+
}
653+
}
486654

487655
ISystemWindow* OpenHarmonyPlatform::createNativeWindow(uint32_t windowId, void* externalHandle) {
488656
SystemWindow* window = ccnew SystemWindow(windowId, externalHandle);

native/cocos/platform/openharmony/OpenHarmonyPlatform.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,18 +73,22 @@ class OpenHarmonyPlatform : public UniversalPlatform {
7373
void onSurfaceDestroyed(OH_NativeXComponent* component, void* window);
7474
void onSurfaceHide();
7575
void onSurfaceShow(void* window);
76+
void dispatchMouseWheelCB(std::string eventType, float offsetY);
7677

7778
static void onMessageCallback(const uv_async_t* req);
7879
static void timerCb(uv_timer_t* handle);
7980

8081
OH_NativeXComponent* _component{nullptr};
8182
OH_NativeXComponent_Callback _callback;
82-
uv_timer_t _timerHandle;
83+
OH_NativeXComponent_MouseEvent_Callback _mouseCallback{nullptr};
84+
uv_timer_t _timerHandle{nullptr};
8385
uv_loop_t* _workerLoop{nullptr};
8486
uv_async_t _messageSignal{};
8587
bool _timerInited{false};
8688
WorkerMessageQueue _messageQueue;
8789
//game started
88-
bool g_started = false;
90+
bool g_started{false};
91+
bool isMouseLeftActive{false};
92+
float scrollDistance{0};
8993
};
9094
} // namespace cc

native/cocos/platform/openharmony/WorkerMessageQueue.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ namespace cc {
3636
enum class MessageType {
3737
WM_XCOMPONENT_SURFACE_CREATED = 0,
3838
WM_XCOMPONENT_TOUCH_EVENT,
39+
WM_XCOMPONENT_KEY_EVENT,
40+
WM_XCOMPONENT_MOUSE_EVENT,
41+
WM_XCOMPONENT_MOUSE_WHEEL_EVENT,
3942
WM_XCOMPONENT_SURFACE_CHANGED,
4043
WM_XCOMPONENT_SURFACE_HIDE,
4144
WM_XCOMPONENT_SURFACE_SHOW,

native/cocos/platform/openharmony/napi/NapiHelper.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ enum ContextType {
5454
WEBVIEW_UTILS,
5555
DISPLAY_UTILS,
5656
UV_ASYNC_SEND,
57-
VIDEO_UTILS
57+
VIDEO_UTILS,
58+
MOUSE_WHEEL_NAPI
5859
};
5960

6061
#define KEYCODE_BACK_OH 6
@@ -325,6 +326,16 @@ static void napiOnVideoEvent(const Napi::CallbackInfo &info) {
325326
}
326327
}
327328

329+
static void napiOnMouseWheel(const Napi::CallbackInfo &info) {
330+
if(info.Length() != 2) {
331+
Napi::Error::New(info.Env(), "napiOnMouseWheel , 1 argument expected").ThrowAsJavaScriptException();
332+
return;
333+
}
334+
std::string eventType = info[0].As<Napi::String>().ToString();
335+
float offsetY = info[1].As<Napi::Number>().FloatValue();
336+
OpenHarmonyPlatform::getInstance()->dispatchMouseWheelCB(eventType, offsetY);
337+
}
338+
328339
// NAPI Interface
329340
static Napi::Value getContext(const Napi::CallbackInfo &info) {
330341
Napi::Env env = info.Env();
@@ -400,6 +411,9 @@ static Napi::Value getContext(const Napi::CallbackInfo &info) {
400411
case VIDEO_UTILS: {
401412
exports["onVideoEvent"] = Napi::Function::New(env, napiOnVideoEvent);
402413
} break;
414+
case MOUSE_WHEEL_NAPI: {
415+
exports["onMouseWheel"] = Napi::Function::New(env, napiOnMouseWheel);
416+
} break;
403417
default:
404418
CC_LOG_ERROR("unknown type");
405419
}

pal/input/native/mouse-input.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,12 @@
2323
*/
2424

2525
import { screenAdapter } from 'pal/screen-adapter';
26+
import { systemInfo } from 'pal/system-info';
2627
import { EventMouse } from '../../../cocos/input/types';
2728
import { EventTarget } from '../../../cocos/core/event';
2829
import { Vec2 } from '../../../cocos/core/math';
2930
import { InputEventType } from '../../../cocos/input/types/event-enum';
31+
import { OS } from '../../system-info/enum-type';
3032

3133
export type MouseCallback = (res: EventMouse) => void;
3234

@@ -204,8 +206,14 @@ export class MouseInputSource {
204206
eventMouse.movementX = location.x - this._preMousePos.x;
205207
eventMouse.movementY = this._preMousePos.y - location.y;
206208

207-
const matchStandardFactor = 120;
208-
eventMouse.setScrollData(mouseEvent.wheelDeltaX * matchStandardFactor, mouseEvent.wheelDeltaY * matchStandardFactor);
209+
let matchStandardFactor = 0;
210+
if (systemInfo.os === OS.OPENHARMONY) {
211+
matchStandardFactor = 5;
212+
eventMouse.setScrollData(mouseEvent.wheelDeltaX, mouseEvent.wheelDeltaY * matchStandardFactor);
213+
} else {
214+
matchStandardFactor = 120;
215+
eventMouse.setScrollData(mouseEvent.wheelDeltaX * matchStandardFactor, mouseEvent.wheelDeltaY * matchStandardFactor);
216+
}
209217
// update previous mouse position.
210218
this._preMousePos.set(location.x, location.y);
211219
this._eventTarget.emit(eventType, eventMouse);

pal/system-info/native/system-info.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ class SystemInfo extends EventTarget {
115115
this.isXR = (typeof xr !== 'undefined' && typeof xr.XrEntry !== 'undefined');
116116

117117
const isHPE: boolean = typeof __supportHPE === 'function' ? __supportHPE() : false;
118+
const isHarmonyOSNext = this.platform === Platform.OPENHARMONY;
118119

119120
this._featureMap = {
120121
[Feature.WEBP]: true,
@@ -126,7 +127,7 @@ class SystemInfo extends EventTarget {
126127

127128
[Feature.INPUT_TOUCH]: this.isMobile,
128129
[Feature.EVENT_KEYBOARD]: true,
129-
[Feature.EVENT_MOUSE]: isHPE || !this.isMobile,
130+
[Feature.EVENT_MOUSE]: isHPE || !this.isMobile || isHarmonyOSNext,
130131
[Feature.EVENT_TOUCH]: true,
131132
[Feature.EVENT_ACCELEROMETER]: this.isMobile,
132133
[Feature.EVENT_GAMEPAD]: true,

templates/harmonyos-next/entry/src/main/cpp/types/libcocos/index.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ export interface context {
2525
resourceManagerInit: (resourceManager: resourceManager.ResourceManager) => void;
2626
writablePathInit: (cacheDir: string) => void;
2727
onVideoEvent: (param: string) => void;
28-
registerFunction: (name:string ,fun:Function) => void;
28+
onMouseWheel: (type: string, offsetY: number) => void;
29+
registerFunction: (name: string, fun: Function) => void;
2930
}
3031

3132
export const getContext: (type: ContextType) => context;

templates/harmonyos-next/entry/src/main/ets/common/Constants.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ export enum ContextType {
3434
WEBVIEW_UTILS,
3535
DISPLAY_UTILS,
3636
UV_ASYNC_SEND,
37-
VIDEO_UTILS
37+
VIDEO_UTILS,
38+
MOUSE_WHEEL_NAPI
3839
}
3940

4041
export class Constants {

templates/harmonyos-next/entry/src/main/ets/pages/index.ets

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ import { BusinessError } from '@kit.BasicServicesKit';
3636

3737
const nativePageLifecycle :context = nativerender.getContext(ContextType.JSPAGE_LIFECYCLE);
3838
const engineUtils :context = nativerender.getContext(ContextType.ENGINE_UTILS);
39-
39+
const mouseWheelNapi :context = nativerender.getContext(ContextType.MOUSE_WHEEL_NAPI);
4040

4141
function executeMethodAsync(nativeFunc: Function, funcData: string, funCb: Function): void {
4242
nativeFunc && nativeFunc(funcData, funCb);
@@ -107,6 +107,7 @@ struct Index {
107107
private webViewIndexMap: Map<number, number> = new Map<number, number>();
108108
private videoIndexMap: Map<number, number> = new Map<number, number>();
109109
private workPort: PortProxy = new PortProxy(WorkerManager.getInstance().getWorker());
110+
private panOption: PanGestureOptions = new PanGestureOptions({ direction: PanDirection.Up | PanDirection.Down});
110111
dialogController: CustomDialogController = new CustomDialogController({
111112
builder: EditBoxDialog({
112113
showInfo: this.showInfo,
@@ -431,13 +432,33 @@ struct Index {
431432
}
432433
}
433434

435+
onMouseWheel(eventType: string, event: PanGestureEvent) {
436+
if(event.source == SourceType.Mouse) {
437+
mouseWheelNapi.onMouseWheel(eventType, event.offsetY);
438+
}
439+
}
440+
434441
build() {
435442
Flex({
436443
direction: FlexDirection.Column,
437444
alignItems: ItemAlign.Center,
438445
justifyContent: FlexAlign.Center
439446
} as FlexOptions) {
440447
XComponent({ id: 'xcomponentId', type: 'surface', libraryname: 'cocos' })
448+
.focusable(true)
449+
.defaultFocus(true)
450+
.gesture(
451+
PanGesture(this.panOption)
452+
.onActionStart((event: GestureEvent) => {
453+
this.onMouseWheel("actionStart", event);
454+
})
455+
.onActionUpdate((event: GestureEvent) => {
456+
this.onMouseWheel("actionUpdate", event);
457+
})
458+
.onActionEnd((event: GestureEvent) => {
459+
this.onMouseWheel("actionEnd", event);
460+
})
461+
)
441462
.onLoad((context) => {
442463
// Set the cache directory in the ts layer.
443464
this.workPort.postMessage("onXCLoad", "XComponent");

0 commit comments

Comments
 (0)