Skip to content

Commit 8e2dff8

Browse files
committed
Add support for pointer capture
1 parent 37a9b63 commit 8e2dff8

File tree

8 files changed

+83
-1
lines changed

8 files changed

+83
-1
lines changed

app/src/main/java/com/gaurav/avnc/ui/prefs/PrefsActivity.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,10 @@ class PrefsActivity : AppCompatActivity(), PreferenceFragmentCompat.OnPreference
113113
}
114114
}
115115

116+
findPreference<SwitchPreference>("capture_pointer")!!.apply {
117+
showIf { Build.VERSION.SDK_INT >= 26 }
118+
}
119+
116120
findPreference<ListPreferenceEx>("gesture_swipe1")!!.apply {
117121
enableIf { it["gesture_style"] != "touchpad" }
118122
disabledStateSummary = getString(R.string.pref_gesture_action_move_pointer)

app/src/main/java/com/gaurav/avnc/ui/vnc/Dispatcher.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,11 @@ class Dispatcher(private val activity: VncActivity) {
155155
fun onMouseScroll(p: PointF, hs: Float, vs: Float) = directMode.doRemoteScrollFromMouse(p, hs, vs)
156156
fun onMouseBack(p: PointF) = config.mouseBackAction(p)
157157

158+
fun onCapturedMouseButtonDown(button: PointerButton) = relativeMode.doButtonDown(button, PointF())
159+
fun onCapturedMouseButtonUp(button: PointerButton) = relativeMode.doButtonUp(button, PointF())
160+
fun onCapturedMouseMove(dx: Float, dy: Float) = relativeMode.doMovePointer(PointF(), dx, dy)
161+
fun onCapturedMouseScroll(hs: Float, vs: Float) = relativeMode.doRemoteScrollFromMouse(PointF(), hs, vs)
162+
158163
fun onStylusTap(p: PointF) = directMode.doClick(PointerButton.Left, p)
159164
fun onStylusDoubleTap(p: PointF) = directMode.doDoubleClick(PointerButton.Left, p)
160165
fun onStylusLongPress(p: PointF) = directMode.doClick(PointerButton.Right, p)

app/src/main/java/com/gaurav/avnc/ui/vnc/FrameView.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,4 +101,8 @@ class FrameView(context: Context?, attrs: AttributeSet? = null) : GLSurfaceView(
101101
override fun onHoverEvent(event: MotionEvent): Boolean {
102102
return touchHandler.onHoverEvent(event)
103103
}
104+
105+
override fun onCapturedPointerEvent(event: MotionEvent): Boolean {
106+
return touchHandler.onCapturedPointerEvent(event)
107+
}
104108
}

app/src/main/java/com/gaurav/avnc/ui/vnc/TouchHandler.kt

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ class TouchHandler(private val frameView: FrameView, private val dispatcher: Dis
5252
return onHoverEvent(event) || handleStylusEvent(event) || handleMouseEvent(event)
5353
}
5454

55+
fun onCapturedPointerEvent(event: MotionEvent): Boolean {
56+
return handleCapturedPointerEvent(event)
57+
}
58+
5559
fun onHoverEvent(event: MotionEvent): Boolean {
5660
if (event.actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
5761
lastHoverPoint = event.point()
@@ -102,6 +106,37 @@ class TouchHandler(private val frameView: FrameView, private val dispatcher: Dis
102106
return !(e.buttonState == 0 && e.getToolType(0) != MotionEvent.TOOL_TYPE_MOUSE)
103107
}
104108

109+
private fun handleCapturedPointerEvent(e: MotionEvent): Boolean {
110+
if (Build.VERSION.SDK_INT < 26)
111+
return false
112+
113+
val screenDensity = frameView.context.resources.displayMetrics.density
114+
val dx: Float
115+
val dy: Float
116+
117+
if (e.isFromSource(InputDevice.SOURCE_MOUSE_RELATIVE)) {
118+
dx = getTotalAxisValue(e, MotionEvent.AXIS_X) * screenDensity
119+
dy = getTotalAxisValue(e, MotionEvent.AXIS_Y) * screenDensity
120+
} else if (e.isFromSource(InputDevice.SOURCE_TOUCHPAD)) {
121+
dx = getTotalAxisValue(e, MotionEvent.AXIS_RELATIVE_X)
122+
dy = getTotalAxisValue(e, MotionEvent.AXIS_RELATIVE_Y)
123+
} else
124+
return false
125+
126+
when (e.actionMasked) {
127+
MotionEvent.ACTION_BUTTON_PRESS -> dispatcher.onCapturedMouseButtonDown(convertButton(e.actionButton))
128+
MotionEvent.ACTION_BUTTON_RELEASE -> dispatcher.onCapturedMouseButtonUp(convertButton(e.actionButton))
129+
MotionEvent.ACTION_MOVE -> dispatcher.onCapturedMouseMove(dx, dy)
130+
MotionEvent.ACTION_SCROLL -> {
131+
val hs = e.getAxisValue(MotionEvent.AXIS_HSCROLL)
132+
val vs = e.getAxisValue(MotionEvent.AXIS_VSCROLL)
133+
dispatcher.onCapturedMouseScroll(hs, vs)
134+
}
135+
else -> return false
136+
}
137+
return true
138+
}
139+
105140
/**
106141
* Convert from [MotionEvent] button to [PointerButton]
107142
*/
@@ -112,6 +147,17 @@ class TouchHandler(private val frameView: FrameView, private val dispatcher: Dis
112147
else -> PointerButton.None
113148
}
114149

150+
/**
151+
* Returns value of given axis, added with historical values of the axis
152+
*/
153+
private fun getTotalAxisValue(e: MotionEvent, axis: Int): Float {
154+
var v = e.getAxisValue(axis)
155+
repeat(e.historySize) {
156+
v += e.getHistoricalAxisValue(axis, it)
157+
}
158+
return v
159+
}
160+
115161

116162
/****************************************************************************************
117163
* Stylus

app/src/main/java/com/gaurav/avnc/ui/vnc/VncActivity.kt

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,7 @@ class VncActivity : AppCompatActivity() {
264264
SamsungDex.setMetaKeyCapture(this, isConnected)
265265
layoutManager.onConnectionStateChanged()
266266
updateStatusContainerVisibility(isConnected)
267+
updatePointerCapture()
267268
autoReconnect(newState)
268269

269270
if (isConnected) {
@@ -285,6 +286,16 @@ class VncActivity : AppCompatActivity() {
285286
viewModel.saveProfile()
286287
}
287288

289+
private fun updatePointerCapture() {
290+
if (Build.VERSION.SDK_INT < 26 || !viewModel.pref.input.capturePointer)
291+
return
292+
293+
if (viewModel.state.value.isConnected)
294+
binding.frameView.requestPointerCapture()
295+
else
296+
binding.frameView.releasePointerCapture()
297+
}
298+
288299
private fun updateStatusContainerVisibility(isConnected: Boolean) {
289300
binding.statusContainer.isVisible = true
290301
binding.statusContainer
@@ -367,7 +378,10 @@ class VncActivity : AppCompatActivity() {
367378
override fun onWindowFocusChanged(hasFocus: Boolean) {
368379
super.onWindowFocusChanged(hasFocus)
369380
layoutManager.onWindowFocusChanged(hasFocus)
370-
if (hasFocus) viewModel.sendClipboardText()
381+
if (hasFocus) {
382+
viewModel.sendClipboardText()
383+
updatePointerCapture()
384+
}
371385
}
372386

373387

app/src/main/java/com/gaurav/avnc/util/AppPreferences.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ class AppPreferences(context: Context) {
7171
val vkRowCount; get() = prefs.getString("vk_row_count", null)?.toIntOrNull() ?: 2
7272

7373
val mousePassthrough; get() = prefs.getBoolean("mouse_passthrough", true)
74+
val capturePointer; get() = mousePassthrough && prefs.getBoolean("capture_pointer", false)
7475
val hideLocalCursor; get() = prefs.getBoolean("hide_local_cursor", false)
7576
val hideRemoteCursor; get() = prefs.getBoolean("hide_remote_cursor", false)
7677
val mouseBack; get() = prefs.getString("mouse_back", "right-click")!!

app/src/main/res/values/strings.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@
196196
<string name="pref_mouse_passthrough">Mouse passthrough</string>
197197
<string name="pref_mouse_passthrough_summary_off">Use mouse events for local gestures</string>
198198
<string name="pref_mouse_passthrough_summary_on">Send mouse events directly to server</string>
199+
<string name="pref_capture_pointer">Capture pointer</string>
199200
<string name="pref_hide_local_cursor">Hide local pointer</string>
200201
<string name="pref_mouse_back">Back button</string>
201202
<string name="pref_mouse_back_action_default">Default</string>

app/src/main/res/xml/pref_input.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,13 @@
122122
app:summaryOn="@string/pref_mouse_passthrough_summary_on"
123123
app:title="@string/pref_mouse_passthrough" />
124124

125+
<SwitchPreference
126+
app:defaultValue="false"
127+
app:dependency="mouse_passthrough"
128+
app:key="capture_pointer"
129+
app:summary="@string/pref_experimental"
130+
app:title="@string/pref_capture_pointer" />
131+
125132
<SwitchPreference
126133
app:defaultValue="false"
127134
app:key="hide_local_cursor"

0 commit comments

Comments
 (0)