Skip to content

Commit b7bf318

Browse files
committed
feat: inject right click #195
1 parent e6ec20e commit b7bf318

5 files changed

Lines changed: 138 additions & 12 deletions

File tree

app/src/main/java/xtr/keymapper/keymap/KeymapProfile.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import android.content.Context;
44
import android.os.Parcel;
55
import android.os.Parcelable;
6-
import android.util.Log;
76

87
import java.util.ArrayList;
98
import java.util.HashMap;

app/src/main/java/xtr/keymapper/server/IInputInterface.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
public interface IInputInterface {
1010
void injectEvent(float x, float y, int action, int pointerId);
1111
void injectHoverEvent(float x, float y, int pointerId);
12+
13+
void injectRightClickEvent(float x, float y, int pointerId, boolean press);
14+
1215
void injectScroll(float x, float y, float value);
1316
void pauseResumeKeymap();
1417
KeymapConfig getKeymapConfig();

app/src/main/java/xtr/keymapper/server/Input.java

Lines changed: 113 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,14 @@
1717
import java.lang.reflect.InvocationTargetException;
1818
import java.lang.reflect.Method;
1919

20+
import xtr.keymapper.server.event.MouseEventHandler;
21+
2022
public class Input {
2123

2224
static Method injectInputEventMethod;
2325
static Object inputManager;
2426
static Method setDisplayIdMethod;
27+
static Method setActionButtonMethod;
2528

2629
private final PointersState pointersState = new PointersState();
2730
private final MotionEvent.PointerProperties[] pointerProperties = new MotionEvent.PointerProperties[PointersState.MAX_POINTERS];
@@ -102,6 +105,114 @@ public void injectTouch(int action, int pointerId, float pressure, float x, floa
102105
injectInputEvent(motionEvent);
103106
}
104107

108+
void injectRightClick(long pointerId, float x, float y, boolean pressed) {
109+
long now = SystemClock.uptimeMillis();
110+
111+
final float pressure = 1.0f;
112+
final int actionButton = MotionEvent.BUTTON_SECONDARY;
113+
int action = pressed ? MotionEvent.ACTION_DOWN : MotionEvent.ACTION_UP;
114+
int buttons = pressed ? MotionEvent.BUTTON_SECONDARY : 0;
115+
116+
Point point = new Point(x, y);
117+
118+
int pointerIndex = pointersState.getPointerIndex(pointerId);
119+
if (pointerIndex == -1) {
120+
Log.e(RemoteService.TAG, "Too many pointers for touch event");
121+
}
122+
123+
Pointer pointer = pointersState.get(pointerIndex);
124+
pointer.setPoint(point);
125+
pointer.setPressure(pressure);
126+
127+
int source;
128+
if (pointerId == MouseEventHandler.pointerId) {
129+
// real mouse event, or event incompatible with a finger
130+
pointerProperties[pointerIndex].toolType = MotionEvent.TOOL_TYPE_MOUSE;
131+
source = InputDevice.SOURCE_MOUSE;
132+
pointer.setUp(buttons == 0);
133+
} else {
134+
// POINTER_ID_GENERIC_FINGER, POINTER_ID_VIRTUAL_FINGER or real touch from device
135+
pointerProperties[pointerIndex].toolType = MotionEvent.TOOL_TYPE_FINGER;
136+
source = InputDevice.SOURCE_TOUCHSCREEN;
137+
// Buttons must not be set for touch events
138+
buttons = 0;
139+
pointer.setUp(action == MotionEvent.ACTION_UP);
140+
}
141+
142+
int pointerCount = pointersState.update(pointerProperties, pointerCoords);
143+
if (pointerCount == 1) {
144+
if (action == MotionEvent.ACTION_DOWN) {
145+
lastTouchDown = now;
146+
}
147+
} else {
148+
// secondary pointers must use ACTION_POINTER_* ORed with the pointerIndex
149+
if (action == MotionEvent.ACTION_UP) {
150+
action = MotionEvent.ACTION_POINTER_UP | (pointerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT);
151+
} else {
152+
action = MotionEvent.ACTION_POINTER_DOWN | (pointerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT);
153+
}
154+
}
155+
156+
/* If the input device is a mouse (on API >= 23):
157+
* - the first button pressed must first generate ACTION_DOWN;
158+
* - all button pressed (including the first one) must generate ACTION_BUTTON_PRESS;
159+
* - all button released (including the last one) must generate ACTION_BUTTON_RELEASE;
160+
* - the last button released must in addition generate ACTION_UP.
161+
*
162+
* Otherwise, Chrome does not work properly: <https://github.com/Genymobile/scrcpy/issues/3635>
163+
*/
164+
if (source == InputDevice.SOURCE_MOUSE) {
165+
if (action == MotionEvent.ACTION_DOWN) {
166+
// First button pressed: ACTION_DOWN
167+
MotionEvent downEvent = MotionEvent.obtain(lastTouchDown, now, MotionEvent.ACTION_DOWN, pointerCount, pointerProperties,
168+
pointerCoords, 0, buttons, 1f, 1f, 0, 0, source, 0);
169+
injectInputEvent(downEvent);
170+
171+
// Any button pressed: ACTION_BUTTON_PRESS
172+
MotionEvent pressEvent = MotionEvent.obtain(lastTouchDown, now, MotionEvent.ACTION_BUTTON_PRESS, pointerCount, pointerProperties,
173+
pointerCoords, 0, buttons, 1f, 1f, 0, 0, source, 0);
174+
try {
175+
if (setActionButtonMethod != null) {
176+
setActionButtonMethod.invoke(pressEvent, actionButton);
177+
}
178+
} catch (IllegalAccessException | InvocationTargetException e) {
179+
Log.e(RemoteService.TAG, e.getMessage(), e);
180+
}
181+
182+
injectInputEvent(pressEvent);
183+
184+
return;
185+
}
186+
187+
if (action == MotionEvent.ACTION_UP) {
188+
// Any button released: ACTION_BUTTON_RELEASE
189+
MotionEvent releaseEvent = MotionEvent.obtain(lastTouchDown, now, MotionEvent.ACTION_BUTTON_RELEASE, pointerCount, pointerProperties,
190+
pointerCoords, 0, buttons, 1f, 1f, 0, 0, source, 0);
191+
try {
192+
if (setActionButtonMethod != null) {
193+
setActionButtonMethod.invoke(releaseEvent, actionButton);
194+
}
195+
} catch (IllegalAccessException | InvocationTargetException e) {
196+
Log.e(RemoteService.TAG, e.getMessage(), e);
197+
}
198+
199+
injectInputEvent(releaseEvent);
200+
201+
// Last button released: ACTION_UP
202+
MotionEvent upEvent = MotionEvent.obtain(lastTouchDown, now, MotionEvent.ACTION_UP, pointerCount, pointerProperties,
203+
pointerCoords, 0, buttons, 1f, 1f, 0, 0, source, 0);
204+
injectInputEvent(upEvent);
205+
206+
return;
207+
}
208+
}
209+
210+
MotionEvent event = MotionEvent.obtain(lastTouchDown, now, action, pointerCount, pointerProperties, pointerCoords, 0, buttons, 1f, 1f,
211+
0, 0, source, 0);
212+
injectInputEvent(event);
213+
}
214+
215+
105216
public void onScrollEvent(float x, float y, float value){
106217
mScrollThread.onScrollEvent(x, y, value);
107218
}
@@ -239,7 +350,8 @@ private void next() {
239350
setDisplayIdMethod = MotionEvent.class.getDeclaredMethod(methodName, Integer.TYPE);
240351
setDisplayIdMethod.setAccessible(true);
241352
}
242-
353+
setActionButtonMethod = MotionEvent.class.getDeclaredMethod("setActionButton", int.class);
354+
setActionButtonMethod.setAccessible(true);
243355
} catch (Exception e) {
244356
Log.e(RemoteService.TAG, e.getMessage(), e);
245357
}

app/src/main/java/xtr/keymapper/server/InputService.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,11 @@ public void injectHoverEvent(float x, float y, int pointerId) {
9494
input.injectTouch(MotionEvent.ACTION_HOVER_MOVE, pointerId, 1.0f, x, y);
9595
}
9696

97+
@Override
98+
public void injectRightClickEvent(float x, float y, int pointerId, boolean pressed) {
99+
input.injectRightClick(pointerId, x, y, pressed);
100+
}
101+
97102
public void injectScroll(float x, float y, float value) {
98103
input.onScrollEvent(x, y, value);
99104
}

app/src/main/java/xtr/keymapper/server/event/MouseEventHandler.java

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,7 @@ public class MouseEventHandler {
3131
float scroll_speed_multiplier;
3232
private MousePinchZoom pinchZoom;
3333
private MouseWheelZoom scrollZoomHandler;
34-
private final int pointerId = PointerId.pid1.id;
35-
private final int pointerIdRightClick = PointerId.pid3.id;
34+
public static final int pointerId = PointerId.pid1.id;
3635
private MouseAimHandler mouseAimHandler;
3736
private MouseAimHandler mouseCameraHandler;
3837
private Key rightClick;
@@ -45,16 +44,16 @@ public class MouseEventHandler {
4544
private MouseAimHandler mouseAimOrCameraHandler;
4645
private MouseWalkHandler mouseWalkHandler;
4746

48-
public void triggerMouseAim() {
49-
triggerMouseAimOrCamera(mouseAimHandler);
47+
public boolean triggerMouseAim() {
48+
return triggerMouseAimOrCamera(mouseAimHandler);
5049
}
5150

5251

5352
public void triggerCamera() {
5453
triggerMouseAimOrCamera(mouseCameraHandler);
5554
}
5655

57-
private void triggerMouseAimOrCamera(MouseAimHandler instance) {
56+
private boolean triggerMouseAimOrCamera(MouseAimHandler instance) {
5857
mouseAimOrCameraHandler = instance;
5958
if (instance != null) {
6059
mouseAimActive = !mouseAimActive;
@@ -71,7 +70,9 @@ private void triggerMouseAimOrCamera(MouseAimHandler instance) {
7170
instance.stop();
7271
mInput.showCursor();
7372
}
73+
return true;
7474
}
75+
return false;
7576
}
7677

7778
public MouseEventHandler(IInputInterface mInput) {
@@ -128,7 +129,7 @@ private void movePointerY() {
128129
mInput.moveCursorY(y1);
129130
}
130131

131-
private void handleRightClick(int value) {
132+
private boolean handleRightClick(int value) {
132133
if (value == 1) {
133134
if (mouseWalkHandler != null) {
134135
if (mouseWalkActive) {
@@ -138,12 +139,16 @@ private void handleRightClick(int value) {
138139
mouseWalkHandler.resetPointer();
139140
mouseWalkActive = true;
140141
}
142+
return true;
141143
}
142144
else if (mInput.getKeymapConfig().rightClickMouseAim)
143-
triggerMouseAim();
145+
return triggerMouseAim();
144146
}
145-
else if (rightClick != null)
146-
mInput.injectEvent(rightClick.x, rightClick.y, value, pointerIdRightClick);
147+
else if (rightClick != null) {
148+
mInput.injectEvent(rightClick.x, rightClick.y, value, pointerId);
149+
return true;
150+
}
151+
return false;
147152
}
148153

149154
public void handleEvent(int code, int value) {
@@ -187,7 +192,9 @@ private void handleMouseEvent(int code, int value) {
187192
break;
188193

189194
case BTN_RIGHT:
190-
handleRightClick(value);
195+
if (!handleRightClick(value)) {
196+
mInput.injectRightClickEvent(x1, y1, pointerId, value == 1);
197+
}
191198
break;
192199

193200
case BTN_EXTRA:

0 commit comments

Comments
 (0)