Mouse input

This topic covers how to implement mouse input for Google Play Games on PC for games where input translation mode doesn't provide an ideal player experience.

PC players typically have a keyboard and mouse rather than a touchscreen, making it important to consider whether your game accomodates mouse input. By default, Google Play Games on PC converts any left-click mouse event into a single virtual tap event. This is known as "input translation mode".

Although this mode makes your game functional with few changes, it does not provide PC players with a native-feeling experience. For that, we recommend that you implement the following:

  • Hover states for context menus rather than press and hold actions
  • Right-click for alternative actions that happen on long press or in a context menu
  • Mouselook for first or third person action games rather than a press and drag event

In order to support UI patterns that are common on PCs, you must disable input translation mode.

Input handling for Google Play Games on PC is identical to that of ChromeOS. The changes that support PCs also improve your game for all Android players.

Disable input translation mode

In your AndroidManifest.xml file, declare the android.hardware.type.pc feature. This indicates that your game uses PC hardware and disables input translation mode. In addition, adding required="false" helps ensure that your game can still be installed on phones and tablets without a mouse. For example:

<manifest...>
<uses-feature
android:name="android.hardware.type.pc"
android:required="false"/>
...
</manifest>

The production version of Google Play Games on PC switches to the correct mode when a game launches. When running in the developer emulator, you need to right-click the task bar icon, select Developer Options, and then PC mode(KiwiMouse) to receive raw mouse input.

Screenshot of the "PC mode(KiwiMouse)" selected in the context menu

After you do this, the mouse movement is reported by View.onGenericMotionEvent with the source SOURCE_MOUSE indicating that it's a mouse event.

Kotlin

gameView.setOnGenericMotionListener{_,motionEvent->
varhandled=false
if(motionEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)){
// handle the mouse event here
handled=true
}
handled
}

Java

gameView.setOnGenericMotionListener((view,motionEvent)->{
if(motionEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)){
// handle the mouse event here
returntrue;
}
returnfalse;
});

For details on handling mouse input, see the ChromeOS documentation.

Handling mouse movement

To detect mouse movement, listen to the ACTION_HOVER_ENTER, ACTION_HOVER_EXIT, and ACTION_HOVER_MOVE events.

This is best used to detect the user hovering over buttons or objects in a game, giving you a chance to display a hint box or implement a mouseover state to highlight what a player is about to select. For example:

Kotlin

gameView.setOnGenericMotionListener{_,motionEvent->
varhandled=false
if(motionEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)){
when(motionEvent.action){
MotionEvent.ACTION_HOVER_ENTER->Log.d("MA","Mouse entered at ${motionEvent.x}, ${motionEvent.y}")
MotionEvent.ACTION_HOVER_EXIT->Log.d("MA","Mouse exited at ${motionEvent.x}, ${motionEvent.y}")
MotionEvent.ACTION_HOVER_MOVE->Log.d("MA","Mouse hovered at ${motionEvent.x}, ${motionEvent.y}")
}
handled=true
}
handled
}

Java

gameView.setOnGenericMotionListener((view,motionEvent)->{
if(motionEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)){
switch(motionEvent.getAction()){
caseMotionEvent.ACTION_HOVER_ENTER:
Log.d("MA","Mouse entered at "+motionEvent.getX()+", "+motionEvent.getY());
break;
caseMotionEvent.ACTION_HOVER_EXIT:
Log.d("MA","Mouse exited at "+motionEvent.getX()+", "+motionEvent.getY());
break;
caseMotionEvent.ACTION_HOVER_MOVE:
Log.d("MA","Mouse hovered at "+motionEvent.getX()+", "+motionEvent.getY());
break;
}
returntrue;
}
returnfalse;
});

Handling mouse buttons

PCs have long had both left and right mouse buttons, giving interactive elements both primary and secondary actions. In a game, tap actions like tapping on a button are best mapped to left-click where touch & hold actions feel most natural with right-click. In real time strategy games you might also use left-click to select and right-click to move. First person shooters might assign primary and secondary fire-to-left and right-click. An infinite runner might use left-click to jump and right-click to dash. We have not added support for the middle-click event.

To handle button presses, use ACTION_DOWN and ACTION_UP. Then use getActionButton to determine which button triggered the action or getButtonState to get the state of all the buttons.

In this example, an enum is used to help display the result of getActionButton:

Kotlin

enumclassMouseButton{
LEFT,
RIGHT,
UNKNOWN;
companionobject{
funfromMotionEvent(motionEvent:MotionEvent):MouseButton{
returnwhen(motionEvent.actionButton){
MotionEvent.BUTTON_PRIMARY->LEFT
MotionEvent.BUTTON_SECONDARY->RIGHT
else->UNKNOWN
}
}
}
}

Java

enumMouseButton{
LEFT,
RIGHT,
MIDDLE,
UNKNOWN;
staticMouseButtonfromMotionEvent(MotionEventmotionEvent){
switch(motionEvent.getActionButton()){
caseMotionEvent.BUTTON_PRIMARY:
returnMouseButton.LEFT;
caseMotionEvent.BUTTON_SECONDARY:
returnMouseButton.RIGHT;
default:
returnMouseButton.UNKNOWN;
}
}
}

In this example, the action is handled similar to the hover events:

Kotlin

// Handle the generic motion event
gameView.setOnGenericMotionListener{_,motionEvent->
varhandled=false
if(motionEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)){
when(motionEvent.action){
MotionEvent.ACTION_BUTTON_PRESS->Log.d(
"MA",
"${MouseButton.fromMotionEvent(motionEvent)} pressed at ${motionEvent.x}, ${motionEvent.y}"
)
MotionEvent.ACTION_BUTTON_RELEASE->Log.d(
"MA",
"${MouseButton.fromMotionEvent(motionEvent)} released at ${motionEvent.x}, ${motionEvent.y}"
)
}
handled=true
}
handled
}

Java

gameView.setOnGenericMotionListener((view,motionEvent)->{
if(motionEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)){
switch(motionEvent.getAction()){
caseMotionEvent.ACTION_BUTTON_PRESS:
Log.d("MA",MouseButton.fromMotionEvent(motionEvent)+" pressed at "+motionEvent.getX()+", "+motionEvent.getY());
break;
caseMotionEvent.ACTION_BUTTON_RELEASE:
Log.d("MA",MouseButton.fromMotionEvent(motionEvent)+" released at "+motionEvent.getX()+", "+motionEvent.getY());
break;
}
returntrue;
}
returnfalse;
});

Handle mousewheel scrolling

We recommend that you use the mouse scroll wheel in place of pinch to zoom gestures or touch and drag scroll areas in your game.

To read scroll wheel values, listen for the ACTION_SCROLL event. The delta since the last frame can be retrieved using getAxisValue with AXIS_VSCROLL for vertical offset and AXIS_HSCROLL for horizontal offset. For example:

Kotlin

gameView.setOnGenericMotionListener{_,motionEvent->
varhandled=false
if(motionEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)){
when(motionEvent.action){
MotionEvent.ACTION_SCROLL->{
valscrollX=motionEvent.getAxisValue(MotionEvent.AXIS_HSCROLL)
valscrollY=motionEvent.getAxisValue(MotionEvent.AXIS_VSCROLL)
Log.d("MA","Mouse scrolled $scrollX, $scrollY")
}
}
handled=true
}
handled
}

Java

gameView.setOnGenericMotionListener((view,motionEvent)->{
if(motionEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)){
switch(motionEvent.getAction()){
caseMotionEvent.ACTION_SCROLL:
floatscrollX=motionEvent.getAxisValue(MotionEvent.AXIS_HSCROLL);
floatscrollY=motionEvent.getAxisValue(MotionEvent.AXIS_VSCROLL);
Log.d("MA","Mouse scrolled "+scrollX+", "+scrollY);
break;
}
returntrue;
}
returnfalse;
});

Capture mouse input

Some games need to take full control of the mouse cursor such as first or third person action games that map mouse movement to camera movement. To take exclusive control of the mouse, invoke View.requestPointerCapture().

requestPointerCapture() only works when the view hierarchy containing your view has focus. For this reason, you cannot acquire pointer capture in the onCreate callback. You should either wait for player interaction to capture the mouse pointer, such as when interacting with the main menu, or use the onWindowFocusChanged callback. For example:

Kotlin

overridefunonWindowFocusChanged(hasFocus:Boolean){
super.onWindowFocusChanged(hasFocus)
if(hasFocus){
gameView.requestPointerCapture()
}
}

Java

@Override
publicvoidonWindowFocusChanged(booleanhasFocus){
super.onWindowFocusChanged(hasFocus);
if(hasFocus){
ViewgameView=findViewById(R.id.game_view);
gameView.requestPointerCapture();
}
}

Events captured by requestPointerCapture() are dispatched to the focusable view that registered OnCapturedPointerListener. For example:

Kotlin

gameView.focusable=View.FOCUSABLE
gameView.setOnCapturedPointerListener{_,motionEvent->
Log.d("MA","${motionEvent.x}, ${motionEvent.y}, ${motionEvent.actionButton}")
true
}

Java

gameView.setFocusable(true);
gameView.setOnCapturedPointerListener((view,motionEvent)->{
Log.d("MA",motionEvent.getX()+", "+motionEvent.getY()+", "+motionEvent.getActionButton());
returntrue;
});

In order to release exclusive mouse capture, such as to allow players to interact with a pause menu, invoke View.releasePointerCapture().

Content and code samples on this page are subject to the licenses described in the Content License. Java and OpenJDK are trademarks or registered trademarks of Oracle and/or its affiliates.

Last updated 2026年06月09日 UTC.