mirror of
https://github.com/Droid-Deck/DroidDeck.git
synced 2026-10-14 19:27:09 -04:00
Land taps and the pointer on what is drawn under them (#389)
The compositor takes touch and pointer positions over its whole output and removes the letterbox bars itself, but the session sent them already measured from the picture's edge, so the bars were taken off twice. Any session whose output is a different shape from the panel (16:9 on a 16:10 tablet or a foldable, a fixed resolution on a wide phone) had taps and the touchpad pointer pulled away from the centre, by up to a bar's height at the edges: on a 2400x1504 panel showing 1280x720, a tap near the top reached the first row and one near the bottom landed 32 rows low. Stretch to panel hid it because that output has no bars. Direct touch, the touchpad and mouse pointer, and the second-screen pointer now send positions over the whole view, so the compositor's own mapping puts them on the pixel under the finger.
This commit is contained in:
1 parent
a58b0ac3e0
commit
b6c1d3c09c
3 files changed
+71
-19
No files matched your search
@@ -45,6 +45,7 @@ import com.droiddeck.launcher.gpu.ScreenEffects
|
||||
import com.droiddeck.launcher.gpu.Lossless
|
||||
import com.droiddeck.launcher.gpu.TurnipDriver
|
||||
import com.droiddeck.launcher.input.EvdevKeys
|
||||
import com.droiddeck.launcher.input.InputSpace
|
||||
import com.droiddeck.launcher.input.KeyboardHost
|
||||
import com.droiddeck.launcher.input.ControllerPrefs
|
||||
import com.droiddeck.launcher.input.SessionClipboard
|
||||
@@ -1486,18 +1487,11 @@ class SessionActivity : ComponentActivity(), SurfaceHolder.Callback {
|
||||
}
|
||||
|
||||
private fun movePointer(x: Float, y: Float) {
|
||||
val width = surfaceView.width.takeIf { it > 0 } ?: return
|
||||
val height = surfaceView.height.takeIf { it > 0 } ?: return
|
||||
val out = SessionState.outputSize
|
||||
val scale = minOf(width / out.first.toFloat(), height / out.second.toFloat())
|
||||
val drawnW = out.first * scale
|
||||
val drawnH = out.second * scale
|
||||
val left = (width - drawnW) / 2f
|
||||
val top = (height - drawnH) / 2f
|
||||
val px = ((x - left) / drawnW * 1920f).toInt().coerceIn(0, 1919)
|
||||
val py = ((y - top) / drawnH * 1080f).toInt().coerceIn(0, 1079)
|
||||
WaylandCompositor.nativeSendPointer(1, px, py)
|
||||
showCursor(x.coerceIn(left, left + drawnW), y.coerceIn(top, top + drawnH))
|
||||
val rect = drawnRect() ?: return
|
||||
val cx = x.coerceIn(rect.left, rect.right)
|
||||
val cy = y.coerceIn(rect.top, rect.bottom)
|
||||
WaylandCompositor.nativeSendPointer(1, InputSpace.x(cx, surfaceView.width), InputSpace.y(cy, surfaceView.height))
|
||||
showCursor(cx, cy)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1644,10 +1638,11 @@ class SessionActivity : ComponentActivity(), SurfaceHolder.Callback {
|
||||
if (width <= 0f || height <= 0f) return
|
||||
val nx = (x / width).coerceIn(0f, 1f)
|
||||
val ny = (y / height).coerceIn(0f, 1f)
|
||||
WaylandCompositor.nativeSendPointer(1, (nx * 1919f).toInt(), (ny * 1079f).toInt())
|
||||
drawnRect()?.let { rect ->
|
||||
showCursor(rect.left + nx * rect.width(), rect.top + ny * rect.height())
|
||||
}
|
||||
val rect = drawnRect() ?: return
|
||||
val px = rect.left + nx * rect.width()
|
||||
val py = rect.top + ny * rect.height()
|
||||
WaylandCompositor.nativeSendPointer(1, InputSpace.x(px, surfaceView.width), InputSpace.y(py, surfaceView.height))
|
||||
showCursor(px, py)
|
||||
}
|
||||
|
||||
private fun closeSecondScreen(reset: Boolean) {
|
||||
@@ -1694,9 +1689,10 @@ class SessionActivity : ComponentActivity(), SurfaceHolder.Callback {
|
||||
}
|
||||
val rect = drawnRect() ?: return false
|
||||
fun sendTouch(action: Int, index: Int) {
|
||||
val x = ((event.getX(index) - rect.left) / rect.width()).coerceIn(0f, 1f)
|
||||
val y = ((event.getY(index) - rect.top) / rect.height()).coerceIn(0f, 1f)
|
||||
WaylandCompositor.nativeSendTouch(action, event.getPointerId(index), (x * 1919f).toInt(), (y * 1079f).toInt())
|
||||
val x = event.getX(index).coerceIn(rect.left, rect.right)
|
||||
val y = event.getY(index).coerceIn(rect.top, rect.bottom)
|
||||
WaylandCompositor.nativeSendTouch(action, event.getPointerId(index),
|
||||
InputSpace.x(x, surfaceView.width), InputSpace.y(y, surfaceView.height))
|
||||
}
|
||||
when (event.actionMasked) {
|
||||
// A first finger starts a new gesture, and Android has ended every earlier one: whatever
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.droiddeck.launcher.input
|
||||
|
||||
object InputSpace {
|
||||
const val WIDTH = 1920
|
||||
const val HEIGHT = 1080
|
||||
|
||||
fun x(position: Float, viewWidth: Int): Int = scale(position, viewWidth, WIDTH)
|
||||
|
||||
fun y(position: Float, viewHeight: Int): Int = scale(position, viewHeight, HEIGHT)
|
||||
|
||||
private fun scale(position: Float, extent: Int, size: Int): Int =
|
||||
if (extent <= 0) 0 else (position / extent * size).toInt().coerceIn(0, size - 1)
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.droiddeck.launcher.input
|
||||
|
||||
import org.junit.Assert.*
|
||||
import org.junit.Test
|
||||
|
||||
class InputSpaceTest {
|
||||
private fun sceneUnder(viewX: Float, viewY: Float, view: Pair<Int, Int>, scene: Pair<Int, Int>): Pair<Double, Double> {
|
||||
val k = minOf(view.first.toDouble() / scene.first, view.second.toDouble() / scene.second)
|
||||
val offX = (view.first - scene.first * k) / 2
|
||||
val offY = (view.second - scene.second * k) / 2
|
||||
val ox = InputSpace.x(viewX, view.first).toDouble() * view.first / InputSpace.WIDTH
|
||||
val oy = InputSpace.y(viewY, view.second).toDouble() * view.second / InputSpace.HEIGHT
|
||||
return ((ox - offX) / k).coerceIn(0.0, scene.first - 1.0) to ((oy - offY) / k).coerceIn(0.0, scene.second - 1.0)
|
||||
}
|
||||
|
||||
private fun assertLandsUnderFinger(view: Pair<Int, Int>, scene: Pair<Int, Int>) {
|
||||
val k = minOf(view.first.toFloat() / scene.first, view.second.toFloat() / scene.second)
|
||||
val offX = (view.first - scene.first * k) / 2
|
||||
val offY = (view.second - scene.second * k) / 2
|
||||
val stepX = view.first.toDouble() / InputSpace.WIDTH / k
|
||||
val stepY = view.second.toDouble() / InputSpace.HEIGHT / k
|
||||
for (sx in listOf(10, scene.first / 4, scene.first / 2, scene.first - 10))
|
||||
for (sy in listOf(10, scene.second / 4, scene.second / 2, scene.second - 10)) {
|
||||
val (x, y) = sceneUnder(offX + sx * k, offY + sy * k, view, scene)
|
||||
assertEquals("x at $sx,$sy on $view showing $scene", sx.toDouble(), x, stepX + 0.01)
|
||||
assertEquals("y at $sx,$sy on $view showing $scene", sy.toDouble(), y, stepY + 0.01)
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun aTouchReachesTheScenePixelUnderTheFinger() {
|
||||
assertLandsUnderFinger(2400 to 1504, 1280 to 720)
|
||||
assertLandsUnderFinger(2400 to 1080, 1280 to 720)
|
||||
assertLandsUnderFinger(2400 to 1504, 1148 to 720)
|
||||
assertLandsUnderFinger(1080 to 2400, 1280 to 720)
|
||||
}
|
||||
|
||||
@Test fun positionsOutsideTheViewStayInRange() {
|
||||
assertEquals(0, InputSpace.x(-50f, 2400))
|
||||
assertEquals(InputSpace.WIDTH - 1, InputSpace.x(2400f, 2400))
|
||||
assertEquals(InputSpace.HEIGHT - 1, InputSpace.y(5000f, 1504))
|
||||
assertEquals(0, InputSpace.y(10f, 0))
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user