Skip to content

Commit

Permalink
Add additional constructors to Client
Browse files Browse the repository at this point in the history
This provides 2 alternative ways to construct Client instances:

- via the `from_resource` class method, from a wl_resource pointer. This
  accepts a pointer cdata rather than a `Resource` instance, as should
  be feasible to use this method without requiring an interface to
  implement the `Resource` generic for it. E.g. the layer shell protocol
  doesn't have a `Resource` class and this is fine; the requirement
  would disable usage of `Client.from_resource` from layer shell
  components.
- by passing `ptr=` to `Client` directly to represent an existing
  wl_client rather than creating a fresh one.
  • Loading branch information
m-col committed Jul 23, 2022
1 parent 7295fad commit 69a39aa
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 10 deletions.
2 changes: 2 additions & 0 deletions pywayland/ffi_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,8 @@
int
wl_resource_get_version(struct wl_resource *resource);
struct wl_client * wl_resource_get_client(struct wl_resource *resource);
void
wl_resource_add_destroy_listener(struct wl_resource *resource,
struct wl_listener * listener);
Expand Down
51 changes: 41 additions & 10 deletions pywayland/server/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,16 @@

import functools
import logging
from typing import Any
from typing import Any, TYPE_CHECKING

from pywayland import ffi, lib
from pywayland.utils import ensure_valid
from .display import Display
from .listener import Listener

if TYPE_CHECKING:
from pywayland.protocol_core import Resource


def _client_destroy(display: Display, cdata: ffi.ClientCData) -> None:
# do nothing if the display is already destroyed
Expand All @@ -39,7 +42,8 @@ class Client:
Given a file descriptor corresponding to one end of a socket, create a
client struct and add the new client to the compositors client list. At
that point, the client is initialized and ready to run, as if the client
had connected to the servers listening socket.
had connected to the servers listening socket. Alternatively, pass a pointer to an
existing client and use that instead of creating a new one.
The other end of the socket can be passed to
:meth:`~pywayland.client.Display.connect()` on the client side or used with
Expand All @@ -49,24 +53,36 @@ class Client:
:type display: :class:`Display`
:param fd: The file descriptor for the socket to the client
:type fd: `int`
:param ptr: A pointer to an existing wl_client
:type ptr: `ffi.ClientCData`
"""

def __init__(self, display: Display, fd: int) -> None:
if display.destroyed:
raise ValueError("Display has been destroyed")
def __init__(
self,
display: Display | None = None,
fd: int | None = None,
ptr: ffi.ClientCData | None = None,
) -> None:
if ptr is None:
if display is None or fd is None:
raise ValueError("display and fd needed to create new client")

if display.destroyed:
raise ValueError("Display has been destroyed")

ptr = lib.wl_client_create(display._ptr, fd)
ptr = lib.wl_client_create(display._ptr, fd)

destructor = functools.partial(_client_destroy, display)
self._ptr: ffi.ClientCData | None = ffi.gc(ptr, destructor)
self._display: Display | None = display
destructor = functools.partial(_client_destroy, display)
self._ptr: ffi.ClientCData | None = ffi.gc(ptr, destructor)

else:
self._ptr = ptr

def destroy(self) -> None:
"""Destroy the client"""
if self._ptr is not None:
ffi.release(self._ptr)
self._ptr = None
self._display = None

@ensure_valid
def flush(self) -> None:
Expand Down Expand Up @@ -128,3 +144,18 @@ def get_object(self, object_id: int) -> Any:

resource_handle = lib.wl_resource_get_user_data(res_ptr)
return ffi.from_handle(resource_handle)

@classmethod
def from_resource(cls, resource: Resource) -> Client:
"""Look up the corresponding wl_client for a wl_resource
:param resource: The wl_resource
:type resource: pywayland.protocol_core.Resource
:returns:
A `Client` instance.
"""
return cls(ptr=lib.wl_resource_get_client(resource))

def __eq__(self, other) -> bool:
"""Compare this client with another"""
return hasattr(other, "_ptr") and self._ptr == other._ptr

0 comments on commit 69a39aa

Please # to comment.