Skip to content

Commit

Permalink
Merge pull request #186 from heuer/touch_fixes
Browse files Browse the repository at this point in the history
Fixed touch support
  • Loading branch information
flacjacket authored May 5, 2024
2 parents 8081cac + d50cc5c commit 99eb2e9
Show file tree
Hide file tree
Showing 5 changed files with 209 additions and 30 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
*.c
_build.py

# Virtual environment
.venv

# Test output
.coverage
.cache
Expand Down
14 changes: 13 additions & 1 deletion wlroots/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from __future__ import annotations

import weakref
from typing import Any
from typing import Any, Callable, TypeVar

from ._ffi import ffi, lib
from .version import version as _version
Expand All @@ -16,6 +16,8 @@

_weakkeydict: weakref.WeakKeyDictionary = weakref.WeakKeyDictionary()

T = TypeVar("T")


class Ptr:
"""Add equality checks for objects holding the same cdata
Expand Down Expand Up @@ -87,3 +89,13 @@ def ptr_or_null(obj: Ptr | None) -> ffi.CData:
otherwise the _ptr attribute of the object
"""
return obj._ptr if obj is not None else ffi.NULL


def instance_or_none(cls: Callable[[ffi.CData], T], ptr: ffi.CData) -> T | None:
"""
A factory function which returns eiher an instance of T or None.
The result depends on ``ptr`` if it is ffi.NULL, None will be returned,
otherwise the result of T(ptr)
"""
return cls(ptr) if ptr != ffi.NULL else None
26 changes: 25 additions & 1 deletion wlroots/ffi_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -2076,7 +2076,18 @@ def has_xwayland() -> bool:
} events;
...;
};
struct wlr_seat_touch_state { ...; };
struct wlr_seat_touch_state {
struct wlr_seat *seat;
struct wl_list touch_points; // wlr_touch_point.link
uint32_t grab_serial;
uint32_t grab_id;
struct wlr_seat_touch_grab *grab;
struct wlr_seat_touch_grab *default_grab;
...;
};
struct wlr_seat {
struct wl_global *global;
Expand Down Expand Up @@ -2209,11 +2220,22 @@ def has_xwayland() -> bool:
void wlr_seat_set_keyboard(struct wlr_seat *seat, struct wlr_keyboard *keyboard);
struct wlr_keyboard *wlr_seat_get_keyboard(struct wlr_seat *seat);
struct wlr_touch_point *wlr_seat_touch_get_point(struct wlr_seat *seat,
int32_t touch_id);
void wlr_seat_touch_point_focus(struct wlr_seat *seat,
struct wlr_surface *surface, uint32_t time_msec,
int32_t touch_id, double sx, double sy);
void wlr_seat_touch_point_clear_focus(struct wlr_seat *seat, uint32_t time_msec,
int32_t touch_id);
uint32_t wlr_seat_touch_send_down(struct wlr_seat *seat,
struct wlr_surface *surface, uint32_t time_msec,
int32_t touch_id, double sx, double sy);
void wlr_seat_touch_send_up(struct wlr_seat *seat, uint32_t time_msec,
int32_t touch_id);
void wlr_seat_touch_send_motion(struct wlr_seat *seat, uint32_t time_msec,
int32_t touch_id, double sx, double sy);
void wlr_seat_touch_send_cancel(struct wlr_seat *seat, struct wlr_surface *surface);
void wlr_seat_touch_send_frame(struct wlr_seat *seat);
uint32_t wlr_seat_touch_notify_down(struct wlr_seat *seat,
struct wlr_surface *surface, uint32_t time_msec,
int32_t touch_id, double sx, double sy);
Expand Down Expand Up @@ -2245,6 +2267,8 @@ def has_xwayland() -> bool:
bool wlr_seat_keyboard_has_grab(struct wlr_seat *seat);
bool wlr_seat_validate_pointer_grab_serial(struct wlr_seat *seat,
struct wlr_surface *origin, uint32_t serial);
bool wlr_surface_accepts_touch(struct wlr_seat *wlr_seat, struct wlr_surface *surface);
"""

# types/wlr_server_decoration.h
Expand Down
188 changes: 164 additions & 24 deletions wlroots/wlr_types/seat.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@
from __future__ import annotations

import warnings
from typing import Iterator
from weakref import WeakKeyDictionary

from pywayland.protocol.wayland import WlSeat
from pywayland.server import Display, Signal
from pywayland.utils import wl_list_for_each

from wlroots import Ptr, PtrHasData, ffi, lib
from wlroots import Ptr, PtrHasData, ffi, instance_or_none, lib, ptr_or_null

from .compositor import Surface
from .data_device_manager import Drag
Expand Down Expand Up @@ -119,18 +121,12 @@ def keyboard_state(self) -> SeatKeyboardState:
@property
def keyboard(self) -> Keyboard | None:
"""Get the active keyboard for the seat."""
keyboard_ptr = lib.wlr_seat_get_keyboard(self._ptr)
if keyboard_ptr == ffi.NULL:
return None
return Keyboard(keyboard_ptr)
return instance_or_none(Keyboard, lib.wlr_seat_get_keyboard(self._ptr))

@keyboard.setter
def keyboard(self, keyboard: Keyboard | None) -> None:
"""Set this keyboard as the active keyboard for the seat"""
if keyboard is None:
lib.wlr_seat_set_keyboard(self._ptr, ffi.NULL)
else:
lib.wlr_seat_set_keyboard(self._ptr, keyboard._ptr)
lib.wlr_seat_set_keyboard(self._ptr, ptr_or_null(keyboard))

def destroy(self) -> None:
"""Clean up the seat"""
Expand Down Expand Up @@ -295,19 +291,75 @@ def keyboard_has_grab(self) -> bool:
"""Whether or not the keyboard has a grab other than the default grab"""
return lib.wlr_seat_keyboard_has_grab(self._ptr)

def touch_notify_down(
def touch_send_down(
self,
surface: Surface,
time_msec: int,
touch_id: int,
surface_x: float,
surface_y: float,
) -> int:
"""
Send a touch down event to the client of the given surface. All future touch
events for this point will go to this surface. If the touch down is valid,
this will add a new touch point with the given `touch_id`. The touch down may
not be valid if the surface seat client does not accept touch input.
Coordinates are surface-local. This function does not respect touch grabs:
you probably want `touch_notify_down()` instead.
"""
return lib.wlr_seat_touch_send_down(
self._ptr, surface._ptr, time_msec, touch_id, surface_x, surface_y
)

def touch_send_up(self, time_msec: int, touch_id: int) -> None:
"""
Send a touch up event for the touch point given by the `touch_id`. The event
will go to the client for the surface given in the corresponding touch down
event. This will remove the touch point. This function does not respect touch
grabs: you probably want `touch_notify_up()` instead.
"""
lib.wlr_seat_touch_send_up(self._ptr, time_msec, touch_id)

def touch_send_motion(
self, time_msec: int, touch_id: int, surface_x: float, surface_y: float
) -> None:
"""
Send a touch motion event for the touch point given by the `touch_id`. The
event will go to the client for the surface given in the corresponding touch
down event. This function does not respect touch grabs: you probably want
`touch_notify_motion()` instead.
"""
lib.wlr_seat_touch_send_motion(
self._ptr, time_msec, touch_id, surface_x, surface_y
)

def touch_send_cancel(self, surface: Surface) -> None:
"""
Notify the seat that this is a global gesture and the client should cancel
processing it. The event will go to the client for the surface given.
This function does not respect touch grabs: you probably want
`touch_notify_cancel()` instead.
"""
lib.wlr_seat_touch_send_cancel(self._ptr, surface._ptr)

def touch_send_frame(self) -> None:
lib.wlr_seat_touch_send_frame(self._ptr)

def touch_notify_down(
self,
surface: Surface,
time_msec: int,
touch_id: int,
surface_x: float,
surface_y: float,
) -> int:
"""
Notify the seat of a touch down on the given surface. Defers to any grab of the
touch device.
Returns the serial id.
"""
lib.wlr_seat_touch_notify_down(
return lib.wlr_seat_touch_notify_down(
self._ptr, surface._ptr, time_msec, touch_id, surface_x, surface_y
)

Expand Down Expand Up @@ -379,6 +431,15 @@ def touch_has_grab(self) -> bool:
"""
return lib.wlr_seat_touch_has_grab(self._ptr)

def touch_get_point(self, touch_id: int) -> TouchPoint | None:
"""
Get the active touch point with the given `touch_id`.
If the touch point does not exist or is no longer active, returns None.
"""
ptr = lib.wlr_seat_touch_get_point(self._ptr, touch_id)
return instance_or_none(TouchPoint, ptr)

def set_selection(self, source, serial: int) -> None:
"""Sets the current selection for the seat
Expand Down Expand Up @@ -416,6 +477,9 @@ def start_pointer_drag(self, drag: Drag, serial: int) -> None:
"""
lib.wlr_seat_start_pointer_drag(self._ptr, drag._ptr, serial)

def surface_accepts_touch(self, surface: Surface) -> bool:
return lib.wlr_surface_accepts_touch(self._ptr, surface._ptr)

def __enter__(self) -> Seat:
"""Context manager to clean up the seat"""
return self
Expand Down Expand Up @@ -486,19 +550,40 @@ def serial(self) -> int:
return self._ptr.serial


class PointerFocusChangeEvent(Ptr):
class _FocusChangeEvent(Ptr):
"""
Base class for ...FocusChangeEvents which provides common properties.
"""

# TODO: wlr_seat *seat

@property
def old_surface(self) -> Surface:
# TODO: May old_surface be NULL?
return Surface(self._ptr.old_surface)

@property
def new_surface(self) -> Surface:
return Surface(self._ptr.new_surface)


class PointerFocusChangeEvent(_FocusChangeEvent):
def __init__(self, ptr) -> None:
self._ptr = ffi.cast("struct wlr_seat_pointer_focus_change_event *", ptr)

# TODO
@property
def surface_x(self) -> float:
return self._ptr.sx

@property
def surface_y(self) -> float:
return self._ptr.sy


class KeyboardFocusChangeEvent(Ptr):
class KeyboardFocusChangeEvent(_FocusChangeEvent):
def __init__(self, ptr) -> None:
self._ptr = ffi.cast("struct wlr_seat_keyboard_focus_change_event *", ptr)

# TODO


class SeatPointerState(Ptr):
def __init__(self, ptr) -> None:
Expand All @@ -510,13 +595,18 @@ def __init__(self, ptr) -> None:
data_wrapper=PointerFocusChangeEvent,
)

@property
def surface_x(self) -> float:
return self._ptr.sx

@property
def surface_y(self) -> float:
return self._ptr.sy

@property
def focused_surface(self) -> Surface | None:
"""The surface that currently has keyboard focus"""
focused_surface = self._ptr.focused_surface
if focused_surface == ffi.NULL:
return None
return Surface(focused_surface)
return instance_or_none(Surface, self._ptr.focused_surface)


class SeatKeyboardState(Ptr):
Expand All @@ -532,7 +622,57 @@ def __init__(self, ptr) -> None:
@property
def focused_surface(self) -> Surface | None:
"""The surface that is currently focused"""
focused_surface = self._ptr.focused_surface
if focused_surface == ffi.NULL:
return None
return Surface(focused_surface)
return instance_or_none(Surface, self._ptr.focused_surface)


class SeatTouchState(Ptr):
def __init__(self, ptr) -> None:
"""The current state of touch on the seat"""
self._ptr = ptr

@property
def grab_serial(self) -> int:
return self._ptr.grab_serial

def grab_id(self) -> int:
return self._ptr.grab_id

@property
def touch_points(self) -> Iterator[TouchPoint]:
for ptr in wl_list_for_each(
"struct wlr_touch_point *",
self._ptr.touch_points,
"link",
ffi=ffi,
):
yield TouchPoint(ptr)


class TouchPoint(Ptr):
def __init__(self, ptr):
self._ptr = ptr

@property
def touch_id(self) -> int:
return self._ptr.touch_id

@property
def surface_x(self) -> float:
return self._ptr.sx

@property
def surface_y(self) -> float:
return self._ptr.sy

@property
def surface(self) -> Surface | None:
return instance_or_none(Surface, self._ptr.surface)

@property
def focused_surface(self) -> Surface | None:
"""The surface that is currently focused
Note: wlroot calls it "focus_surface" renamed it to "focused_surface"
to keep the name aligned to "SeatKeyboardState" and "SeatPointerState".
"""
return instance_or_none(Surface, self._ptr.focus_surface)
8 changes: 4 additions & 4 deletions wlroots/wlr_types/touch.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def touch_id(self) -> int:

class TouchDownEvent(_TouchEvent):
def __init__(self, ptr) -> None:
self._ptr = ffi.cast("struct wlr_event_touch_down *", ptr)
self._ptr = ffi.cast("struct wlr_touch_down_event *", ptr)

@property
def x(self) -> float:
Expand All @@ -68,12 +68,12 @@ def y(self) -> float:

class TouchUpEvent(_TouchEvent):
def __init__(self, ptr) -> None:
self._ptr = ffi.cast("struct wlr_event_touch_up *", ptr)
self._ptr = ffi.cast("struct wlr_touch_up_event *", ptr)


class TouchMotionEvent(_TouchEvent):
def __init__(self, ptr) -> None:
self._ptr = ffi.cast("struct wlr_event_touch_motion *", ptr)
self._ptr = ffi.cast("struct wlr_touch_motion_event *", ptr)

@property
def x(self) -> float:
Expand All @@ -86,4 +86,4 @@ def y(self) -> float:

class TouchCancelEvent(_TouchEvent):
def __init__(self, ptr) -> None:
self._ptr = ffi.cast("struct wlr_event_touch_cancel *", ptr)
self._ptr = ffi.cast("struct wlr_touch_cancel_event *", ptr)

0 comments on commit 99eb2e9

Please sign in to comment.