Skip to content

Patch SDLSurface to allow touch to be intercepted by python application #3157

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from

Conversation

kengoon
Copy link
Contributor

@kengoon kengoon commented May 15, 2025

This pull request adds a feature to intercept touch events in the SDLSurface class for both the SDL2 and SDL3 bootstraps. It introduces a mechanism to set a custom OnInterceptTouchListener, allowing touch events to be handled before further processing.

Touch Event Handling Enhancements

  • SDL2 Bootstrap (pythonforandroid/bootstraps/sdl2/build/src/patches/SDLSurface.java.patch)

    • Introduces an OnInterceptTouchListener interface and associated methods (setInterceptTouchListener and onTouch).
    • Allows a custom listener to intercept touch events. If the listener handles the event, subsequent processing is skipped.
  • SDL3 Bootstrap (pythonforandroid/bootstraps/sdl3/build/src/patches/SDLSurface.java.patch)

    • Implements the same OnInterceptTouchListener interface and methods as SDL2, ensuring consistency.
    • Adds documentation clarifying that touch interception enables handling by the Python application.

This feature is especially useful for forwarding touch events to Android native widgets rendered behind a Kivy widget, for example, when these metadata settings are applied:

surface.transparent = 1
surface.depth = 16
android.background_color = 0

and a native widget is inserted behind the Kivy widget:

class TouchListener(PythonJavaClass):
    __javacontext__ = 'app'
    __javainterfaces__ = [
        'org/libsdl/app/SDLSurface$OnInterceptTouchListener']

    def __init__(self, listener):
        self.listener = listener

    @java_method('(Landroid/view/MotionEvent;)Z')
    def onTouch(self, event):
        x = event.getX(0)
        y = event.getY(0)
        return self.listener(x, y)

  def _on_touch_listener(self, x, y):
      # invert Y !
      y = Window.height - y
      # x, y are in Window coordinate. Try to select the widget under the
      # touch.
      me = None
      final_widget = None
      for child in reversed(Window.children):
          widget = self._pick(child, x, y)
          if not widget:
              continue
          if self is widget:
              me = widget
          else:
              final_widget = widget
      if self is me and final_widget is None:
          return True

self._listener = TouchListener(self._on_touch_listener)

parent = cast(LinearLayout, PythonActivity.mSurface.getParent())
parent.addView(view, 0, LayoutParams(w, h))

PythonActivity.mSurface.setInterceptTouchListener(self._listener)

A practical example is the kivy-gmap project, which uses Android's native Google MapView behind a Kivy widget.

This capability was also present in older p4a toolchains. See this legacy implementation for reference.

Screen_Recording_20250516_010309.mp4

@kuzeyron kuzeyron added the core-providers Core code that's not a recipe label May 19, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
core-providers Core code that's not a recipe
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants