M doc/Client-API-Reference.md => doc/Client-API-Reference.md +133 -0
@@ 4,6 4,11 @@ The wayflan-client package includes all symbols from the [wayflan
package](./API-Reference.md) plus the interface for connecting to, sending
messages to, and dispatching events from a compositor.
+The macro forms **define-interface**, **define-enum**, **define-request**, and
+**define-event** are all used internally by `:wayflan-client-impl`, and are
+pragmatically likely not used for anything except for prototyping new protocols
+within Wayflan.
+
## [Metaclass] __wl-interface-class__ (__standard-class__)
**Description:**
@@ 202,3 207,131 @@ Return a lambda that accepts an event as specified in **wl-proxy-hooks**.
The lambda selects the clause by matching the *event-name* with the event type.
If a clause is selected, bind the event arguments by the
*destructuring-lambda-list* and run the clause *body*.
+
+## [Macro] **define-interface** *name () &body options*
+
+```
+option ::= (:version VERSION)
+ | (:interface-name INTERFACE-NAME)
+ | (:documentation DOCUMENTATION)
+```
+
+**Arguments and Values:**
+
+- *name* -- a __symbol__. The name of the interface class.
+- *version* -- a positive __integer__. Defaults to 1.
+- *interface-name* -- a __string__.
+- *documentation* -- a __string__. Documentation attached to the defined class
+
+**Description:**
+
+Define a wayland object interface as a subclass of wl-proxy.
+
+*Version* describes the latest supported version of the interface. Proxies can only be created with a version between 1 and *version* inclusive.
+*Interface-name* describes the interface's name as sent down the wire, both by __wl-registry__'s *:global* event and runtime-typed *:new-id* args.
+
+## [Macro] **define-enum** *name () &body (entry-specifiers &rest options)*
+
+```
+entry-specifier ::= (ENTRY-NAME VALUE &key DOCUMENTATION)
+option ::= (:since SINCE)
+ | (:bitfield BITFIELD)
+ | (:documentation DOCUMENTATION)
+```
+
+**Arguments and Values:**
+
+- *name* -- a __symbol__.
+- *entry-name* -- a __keyword__.
+- *value* -- a __wl-int__ or __wl-uint__.
+- *documentation* -- a __string__.
+- *since* -- a positive __integer__. Defaults to 1
+- *bitfield* -- a __boolean__. Defaults to __false__.
+
+**Description:**
+
+Defines an argument subtype that associates integer values with keyword symbols.
+When a request or event argument has the type `(:int NAME)` or `(:uint NAME)`,
+then either the integer value is automatically decoded into a keyword (for
+events), or the keyword argument is automatically encoded into an integer (for
+requests). If *bitfield* is __true__, then instead, integer values are
+automatically decoded into a __list__ of keywords whose associated values' bits
+are associated with the subject value (for events), and keyword list arguments
+are **logior**'d into a result integer (for requests).
+
+Define-enum defines methods for **wl-enum-value** and **wl-enum-keyword**, with
+the first argument specialized as *(eql NAME)*.
+
+## [Macro] **define-request** *(name interface opcode) &body (arg-specifiers &rest options)*
+
+```
+arg-specifier ::= (NAME &key TYPE DOCUMENTATION)
+option ::= (:type REQUEST-TYPE)
+ | (:since VERSION)
+ | (:documentation DOCUMENTATION)
+```
+
+**Arguments and Values:**
+
+- *name* -- a __symbol__.
+- *interface* -- a __symbol__, the name of an __interface-class__.
+- *opcode* -- a non-negative __integer__.
+- *request-type* -- either `:destructor` or *nil*. Defaults to *nil*.
+- *version* -- a positive __integer__. Defaults to 1.
+- *documentation* -- a __string__.
+
+**Description:**
+
+Defines a function implementing the interface's request.
+
+**Define-request** currently only supports up to one `:new-id` argument per
+request. If *request-type* is `:destructor`, then both the defined function and
+**destroy-proxy** sends the message and marks the proxy as destroyed.
+
+## [Macro] **define-event** *(name interface opcode) &body (arg-specifiers &rest options)*
+
+```
+arg-specifier ::= (NAME &key TYPE DOCUMENTATION)
+option ::= (:type EVENT-TYPE)
+ | (:since VERSION)
+ | (:documentation DOCUMENTATION)
+```
+
+**Arguments and Values:**
+
+- *name* -- a __keyword__.
+- *interface* -- a __symbol__, the name of an __interface-class__.
+- *opcode* -- a non-negative __integer__.
+- *event-type* -- either `:destructor` or *nil*. Defaults to *nil*.
+- *version* -- a positive __integer__. Defaults to 1.
+- *documentation* -- a __string__.
+
+**Description:**
+
+Defines an event associated with the interface and opcode. Hooks in proxies
+implementing this interface may accept an event payload beginning with the
+event name, followed by its arguments.
+
+## [ASDF Component] **:wayflan-client-impl** *name &key in-package export*
+
+**Arguments and Values:**
+
+- *name* -- a __string designator__.
+- *in-package* -- a __string designator__.
+- *export* -- a __boolean__. Defaults to *nil*.
+
+**Description:**
+
+Generates and loads a lisp file pointing to a protocol XML document.
+
+On __prepare-op__ and __prepare-source-op__, the client generates a cached lisp
+source file with a macro that points to the component input file's pathname.
+This macro generates all code required to implement the interfaces, enums,
+requests, and events defined in the protocol.
+
+The component then performs on this source file on __compile-op__ and
+__load-op__ like a normal source file component.
+
+*In-package* is required, and defines the package wherein all protocol code
+lives. If *export* is __true__, then all interned symbols are exported from the
+package.
M doc/Getting-Started-With-Wayflan-Client.md => doc/Getting-Started-With-Wayflan-Client.md +44 -1
@@ 1,7 1,9 @@
# Getting Started with Wayflan Client
This document target those familiar with Wayland fundamentals, and aims to
-explain how each Wayland concept integrates into Wayflan.
+explain how each Wayland concept integrates into Wayflan. It starts with a
+refresher, and then a more in-depth look at how each Wayland concept maps to
+Lisp code.
To help with brevity in the snippets, I'm assuming all forms are evaluated
under `(use-package :wayflan-client)`.
@@ 184,6 186,47 @@ functions will immediately affect all proxies it lives under:
(wl-proxy-hooks pointer))
```
+## From Zero to Globals
+
+This code snippet connects to a Wayland server, grabs the registry, and then
+listens for a global event that broadcasts a `wl_compositor` global:
+
+```lisp
+(with-open-display (display)
+ (let (registry compositor)
+ (setf registry (wl-display.get-registry display))
+ ;; Add a closure that listens for wl_registry.global events
+ (push (evlambda
+ (:global (name interface version)
+ (when (string= interface "wl_compositor")
+ (setf compositor (wl-registry.bind
+ registry name 'wl-compositor 1)))))
+ (wl-proxy-hooks registry))
+
+ ;; Process all events up to this point
+ (wl-display-roundtrip display)
+
+ ;; !! Use the compositor and any other globals here
+ (print compositor)))
+```
+
+Alternatively instead of matching for the string name, you can match on the
+interface class name instead:
+
+```lisp
+(push (evlambda
+ (:global (name interface version)
+ (when (find-interface-named interface)
+ (case (class-name (find-interface-named interface))
+ (wl-compositor
+ (setf compositor (wl-registry.bind
+ registry name 'wl-compositor 5)))
+ (wl-shm
+ (setf shm (wl-registry.bind
+ registry name 'wl-shm 1)))))))
+ (wl-proxy-hooks registry))
+```
+
## Marshalling Enums
In Wayflan, enums live as keywords rather than numeric values:
M src/client/asdf.lisp => src/client/asdf.lisp +3 -1
@@ 14,7 14,9 @@
(defclass wayflan-client-impl (cl-source-file)
((type :initform "xml")
(in-package :initarg :in-package)
- (export :initarg :export :initform nil)))
+ (export :initarg :export :initform nil))
+ (:documentation
+ "Generates and loads a lisp file pointing to a protocol XML document"))
(defun wayflan-scan-output-file (c)
(uiop:merge-pathnames*
M src/client/client.lisp => src/client/client.lisp +7 -10
@@ 390,7 390,7 @@ destructuring lambda-list bound under the case's body."
clauses :from-end t))
(defmacro define-interface (name () &body options)
- "Define a wl-proxy CLOS subclass and assign the interface's name to the interface table, accessible via #'FIND-INTERFACE-NAMED.
+ "Define a wayland object interface as subclass of wl-proxy.
NAME - The name of the interface class.
@@ 398,8 398,6 @@ OPTIONS:
(:version VERSION) - Latest supported version of the interface.
(:documentation DOCSTRING) - Docstring attached to the defined class.
-(:skip-defclass BOOLEAN) - If true, do not define the interface class.
- Used by cl-autowrap when it reads wl_display.
(:interface-name STRING) - The name of the interface as listed by the wl-registry on a wl-registry-global-event."
(%option-bind (version documentation skip-defclass interface-name) options
`(progn
@@ 438,15 436,14 @@ OPTIONS:
(push (car entry) result)))))
(defmacro define-enum (name () &body (entry-specifiers &rest options))
- "Define a parameter that associates each entry keyword with an index in the array.
-
+ " Defines an argument subtype that associates integer values with keyword symbols.
NAME - The name of the enum class. Used as arguments in Wayland types :INT and :UINT.
OPTIONS:
(:since INTEGER) - The interface version since it appeared.
-(:documentation DOCSTRING) - Ignored.
-(:bitfield BOOLEAN) - Whether individual bits have specific meanings. If set, enums are decoded as a list of arguments, rather than a single argument."
+(:bitfield BOOLEAN) - Whether individual bits have specific meanings. If set, enums are coded as a list of keywords, rather than a single keyword.
+(:documentation DOCSTRING) - Ignored."
(%option-bind (bitfield) options
`(let ((table ',(mapcar (%slambda (argument value &key &allow-other-keys)
(cons argument value))
@@ 467,7 464,7 @@ OPTIONS:
',name)))
(defmacro define-request ((name interface opcode) &body (arg-specifiers &rest options))
- "Define a function implementing the wl request.
+ "Define a function implementing the interface's request.
DEFINE-REQUEST currently only supports up to one :NEW-ID argument per request.
NAME - The name of the request function.
@@ 480,9 477,9 @@ ARG-SPECIFIERS - Each specifier takes the lambda list (name &key type documentat
OPTIONS:
-(:DOCUMENTATION STRING) - Provided to the function as its docstring.
(:TYPE KEYWORD) - So far, only :DESTRUCTOR is a valid type.
-(:SINCE VERSION) - Minimum interface version of the proxy object."
+(:SINCE VERSION) - Minimum interface version of the proxy object.
+(:DOCUMENTATION STRING) - Provided to the function as its docstring."
(assert (>= 1 (count :new-id arg-specifiers
:test #'%wltype=
:key (curry #'%sgetf :type))))