Including wlr-foreign-toplevel-management via wayflan results in «Wayland message: Object ID 0 doesn't exist [Condition of type WL-MESSAGE-ERROR]».
(defpackage :xyz.hatis.core
(:use
:wayflan-client
:wayflan-client.xdg-shell
:xyz.shunter.wayflan.client.scanner
:cl)
(:local-nicknames (#:a #:alexandria)))
(in-package #:xyz.hatis.core)
(defparameter foreign-toplevel-manager nil)
(defparameter foreign-toplevel-handle nil)
(defparameter registry nil)
#|
handling toplevel: #<ZWLR-FOREIGN-TOPLEVEL-HANDLE-V1 :ID 4278190080 :VERSION 3 :HOOKS (0) {100A368493}>
Proxy received event :TITLE with args ("foot")
Proxy received event :APP-ID with args ("foot")
Proxy received event :STATE with args (#(2 0 0 0))
---
And then error: «Malformed Wayland message: Object ID 0 doesn't exist»
Seems like enum's not parsed
|#
(xyz.shunter.wayflan.client.scanner:wl-include
#P "/gnu/store/z8vzmkf6hydsfkv4cdv32s5ddvm9rys4-wlroots-0.17.4/share/wayland-protocols/wlr-foreign-toplevel-management-unstable-v1.xml"
:export t)
#| The form below won''t fix the situation. The :STATE still won't be recognized. «Proxy received event :STATE with args (#())»
(define-enum zwlr-foreign-toplevel-handle-v1.state ()
((:maximized 0)
(:minimized 1)
(:activated 2)
(:fullscreen 4))
(:documentation "A bitfield enum")
(:bitfield t))
|#
(defun handle-toplevel-handle (toplevel-handle)
(push (lambda (event-name &rest event-args)
(format t "Proxy received event ~S with args ~S~%"
event-name event-args))
(wl-proxy-hooks toplevel-handle))
(format t "stop handle ~%"))
(defun handle-toplevel-manager (display toplevel-manager)
(push (lambda (event-name &rest event-args)
(format t "Proxy received event ~S with args ~S~%"
event-name event-args)
(when (string= event-name :toplevel)
(setf foreign-toplevel-handle (car event-args))
(handle-toplevel-handle foreign-toplevel-handle)))
(wl-proxy-hooks toplevel-manager))
(format t "stop manager ~%"))
(defun handle-registry (display)
(setf registry (wl-display.get-registry display))
(push
(lambda (event-name &rest event-args)
(when (eq event-name :global)
(destructuring-bind (name interface version) event-args
(when (string= interface "zwlr_foreign_toplevel_manager_v1")
(setf foreign-toplevel-manager (wl-registry.bind
registry name
'zwlr-foreign-toplevel-manager-v1 version))
(handle-toplevel-manager display foreign-toplevel-manager)))))
(wl-proxy-hooks registry))
(wl-display-roundtrip display)
(wl-display-roundtrip display)
;; fails on 3rd roundtrip
(wl-display-roundtrip display))
(defun run ()
(with-open-display (display)
(handle-registry display)))
(run)
As you can see I am trying to work with zwlr-toplevel-handle
and it's only handles toplevel ONCE. It fails right after :state
event [or proxy-hook] triggered. As far as I've understand.
I guess that's because wayflan won't parse state enum returned by :state event correctly. It can be seen in a comment - still a bytevector and I've tried to force that enum resolution with no luck.
But maybe I am wrong or it's not the only problem. Please help.
Please also note that I an very new to CL and only been working with clojure and guile scheme previously. Came to SBCL just for this project. I want stable wayland library in lisp. That's the only one. Thanks for the great job.
Backtrace: 0: ((LAMBDA (#:PROXY353 #:BUFFER354)) #<ZWLR-FOREIGN-TOPLEVEL-HANDLE-V1 :ID 4278190080 :VERSION 3 :HOOKS (1) {100917E1F3}> (#.(SB-SYS:INT-SAP #X7F30C0005C80) 4 . 4)) Locals: #:BUFFER354 = (#.(SB-SYS:INT-SAP #X7F30C0005C80) 4 . 4) #:PROXY353 = #<ZWLR-FOREIGN-TOPLEVEL-HANDLE-V1 :ID 4278190080 :VERSION 3 :HOOKS (1) {100917E1F3}> 1: ((LAMBDA (XYZ.SHUNTER.WAYFLAN.CLIENT::SENDER-ID XYZ.SHUNTER.WAYFLAN.CLIENT::OPCODE #:CARRAY2 #:SIZE3) :IN WL-DISPLAY-DISPATCH-EVENT) 4278190080 7 #.(SB-SYS:INT-SAP #X7F30C0005C80) 4) Locals: XYZ.SHUNTER.WAYFLAN.CLIENT::OPCODE = 7 XYZ.SHUNTER.WAYFLAN.WIRE::PTR = #.(SB-SYS:INT-SAP #X7F30C0005C80) XYZ.SHUNTER.WAYFLAN.CLIENT::SENDER-ID = 4278190080 XYZ.SHUNTER.WAYFLAN.FFI:SIZE = 4 2: ((:METHOD XYZ.SHUNTER.WAYFLAN.WIRE::%CALL-WITH-MESSAGE (XYZ.SHUNTER.WAYFLAN.WIRE:DATA-SOCKET T)) #<XYZ.SHUNTER.WAYFLAN.WIRE:DATA-SOCKET {10090C37C3}> #<FUNCTION (LAMBDA (XYZ.SHUNTER.WAYFLAN.CLIENT::SE.. Locals: FUNCTION = #<FUNCTION (LAMBDA (XYZ.SHUNTER.WAYFLAN.CLIENT::SENDER-ID XYZ.SHUNTER.WAYFLAN.CLIENT::OPCODE #:CARRAY2 #:SIZE3) :IN WL-DISPLAY-DISPATCH-EVENT) {10091EA3CB}> XYZ.SHUNTER.WAYFLAN.FFI:SOCKET = #<XYZ.SHUNTER.WAYFLAN.WIRE:DATA-SOCKET {10090C37C3}> 3: (WL-DISPLAY-DISPATCH-EVENT #<WL-DISPLAY :ON #P"/run/user/1000/wayland-1" :ID 1 :VERSION 1 :HOOKS (0) {10090C3823}>) 4: (WL-DISPLAY-ROUNDTRIP #<WL-DISPLAY :ON #P"/run/user/1000/wayland-1" :ID 1 :VERSION 1 :HOOKS (0) {10090C3823}>) 5: (HANDLE-REGISTRY #<WL-DISPLAY :ON #P"/run/user/1000/wayland-1" :ID 1 :VERSION 1 :HOOKS (0) {10090C3823}>) 6: (RUN) 7: (SB-INT:SIMPLE-EVAL-IN-LEXENV (RUN) #<NULL-LEXENV>) 8: (EVAL (RUN)) --more--
edit: it has to be
(:fullscreen 3)
indefine-enum
. but still won't work properly
ok, the problem is not enum parsing. state event has to return an array and it returns one.
it might be somewhere it
<event name="output_enter"> <description summary="toplevel entered an output"> This event is emitted whenever the toplevel becomes visible on the given output. A toplevel may be visible on multiple outputs. </description> <arg name="output" type="object" interface="wl_output"/> </event> <event name="output_leave"> <description summary="toplevel left an output"> This event is emitted whenever the toplevel stops being visible on the given output. It is guaranteed that an entered-output event with the same output has been emitted before this event. </description> <arg name="output" type="object" interface="wl_output"/> </event>
because when i just delete them from an XML i am able to get to the :close event in a single call of toplevel-handle's proxies. but then it fails with
Malformed Wayland message: Message length mismatch [Condition of type WL-MESSAGE-ERROR]
.Maybe wl_output won't be created in the runtime?
Hi Grigory, thanks for the bug report!
It's been a while since I worked on Common Lisp, so I reinstalled my dev environment and I'll look into this over the week. I'll also take another pass at the tests and examples, as it seems even Waycalc breaks on KDE:
The assertion (PLUSP CFFI::MAX-CHARS) failed with CFFI::MAX-CHARS = 0. [Condition of type SIMPLE-ERROR] Restarts: 0: [CONTINUE] Retry assertion. 1: [RETRY] Retry SLIME REPL evaluation request. 2: [*ABORT] Return to SLIME's top level. 3: [ABORT] abort thread (#<THREAD tid=77665 "repl-thread" RUNNING {1000BB0003}>) Backtrace: 0: (SB-KERNEL:ASSERT-ERROR (PLUSP CFFI::MAX-CHARS) 1 CFFI::MAX-CHARS 0) 1: (CFFI:FOREIGN-STRING-TO-LISP #.(SB-SYS:INT-SAP #X7FBB58029940) :OFFSET 4 :COUNT NIL :MAX-CHARS 0 :ENCODING :UTF-8) 2: (XYZ.SHUNTER.WAYFLAN.WIRE:READ-WL-STRING (#.(SB-SYS:INT-SAP #X7FBB58029940) 4 . 8))I'll continue working on this
Thanks.
I've forgot to mention: you have to launch sway first to test any wlroots protocols. You can do that from the command line and then it will be launched in a separate window. In that window you can create a file repl.lisp with the following content:
;; repl.lisp (require :asdf) (asdf:require-system :slynk) (asdf:load-asd (merge-pathnames "wayflan.asd" (uiop:getcwd))) ;; something like this (slynk:create-server :port 4005 :dont-close t)And then launch the repl while still being in a sway window. Then connect to it from emacs in the WM of your choice (if it's not sway).
I was able to fix the bug I found earlier. Now that I'm back on the grind of developing both Lisp and Wayland, I can better approach the bug! In the meantime, I can give answers to a couple points:
The message "Wayland message: Object ID 0 doesn't exist" implies that the client read a number which, defined in the protocol, should be the ID of a Wayland object. The ID 0 is reserved to mean a null or non-existent object. Looks like the library never checks that it could be null, and tries to look up the object anyways. I'll have to see if the XML docs define whether object args could be nullable. Maybe they're nullable by default? I don't remember.
On the phrase:
I guess that's because wayflan won't parse state enum returned by :state event correctly.
Wayflan won't parse the state enum returned by the :state event by design. You can see in the XML doc that its sole argument is only known as an array, not an array of state values, so the protocol scanner wouldn't be able to tell at all. Wayflan lets you cope with this by giving you the tools to marshal enums manually.
As for removing the events
output_enter
andoutput_leave
, that will throw the client off, because now the underlying opcodes that describe the event are out of order and incorrect. That's why it works initially, until you get a wl-message-error.I'll try to reproduce the error and look into reading nullable objects. This is probably the source of the issue.
Cheers!
I've identified the root cause issue.
The scanner does, in fact, recognize the attribute
allow-null
, but the functions generated when reading the event is handled improperly. Insrc/client/client.lisp
:((:object &key interface allow-null) (@and `(%find-proxy! (wl-proxy-display ,proxy) id) (if allow-null `(when id ,|@|) @) `(let* ((id (read-wl-uint ,buffer)) (proxy ,|@|)) ,(when interface `(check-type proxy ,interface)) proxy)))The expression
(when id ,|@|)
, which guards against looking up the proxy, should instead be(when (plusp id) ,|2|)
. I'll verify this and push the fix. If you can verify this fixes the issue afterwards, that will give me the confidence to cut a release.
Fixed in develop. Pull this branch in and let me know when you verified this works.
Thanks!
Thanks. It will take me 3-4 days. Probably will report at the end of the week.
I don't know why but I can't even get a list of globals now with sample code from the readme:
(defun run () ;; Try to connect to a server socket at $XDG_RUNTIME_DIR/$WAYLAND_DISPLAY. ;; If $WAYLAND_DISPLAY describes an absolute path, connect to that directly. (with-open-display (display) ;; Create a registry to provide a list of all ;; globals the client can bind to. (let ((registry (wl-display.get-registry display))) ;; Push an event-listening closure to a list that is called ;; whenever the registry receives an event. (push (lambda (event-name &rest event-args) ;; The macro EVCASE dispatches based on the event automatically. ;; See examples/hello-world.lisp (when (eq event-name :global) (destructuring-bind (name interface version) event-args ;; Print all globals, their interface names, and latest ;; supported version (format t "#x~8,'0X ~32S v~D~%" name interface version)))) (wl-proxy-hooks registry)) ;; Listen until all wl-registry events are processed (format t "wl-registry globals:~%") (wl-display-roundtrip display)))) (run)results:
wl-registry globals:
↑ That's because of https://git.sr.ht/~shunter/wayflan/commit/145d3169b0f71fcb2555e5fff059796c19f74107 Didn't get it first.
Will report on stability of the latest changes a little later
I've updated the toplevel code piece above to match new API:
(defpackage :xyz.hatis.core (:use :wayflan-client :cl) (:local-nicknames (#:a #:alexandria))) (in-package #:xyz.hatis.core) (xyz.shunter.wayflan.client.scanner:wl-include #P "/gnu/store/z8vzmkf6hydsfkv4cdv32s5ddvm9rys4-wlroots-0.17.4/share/wayland-protocols/wlr-foreign-toplevel-management-unstable-v1.xml" :export t) (defparameter foreign-toplevel-manager nil) (defparameter foreign-toplevel-handle nil) (defparameter registry nil) (defun handle-toplevel-handle (toplevel-handle) (push (lambda (event) (destructuring-bind (name &rest args) event (format t "Proxy received event ~S with args ~S~%" event args))) (wl-proxy-hooks toplevel-handle)) (format t "stop handle ~%")) (defun handle-toplevel-manager (display toplevel-manager) (push (lambda (event) (destructuring-bind (name &rest args) event (format t "Proxy received event ~S with args ~S~%" event args) (setf foreign-toplevel-handle (first args)) (handle-toplevel-handle foreign-toplevel-handle))) (wl-proxy-hooks toplevel-manager)) (format t "stop manager ~%")) (defun handle-registry (display) (setf registry (wl-display.get-registry display)) (push (lambda (event) (destructuring-bind (name &optional id interface version) event (when (string= interface "zwlr_foreign_toplevel_manager_v1") (setf foreign-toplevel-manager (wl-registry.bind registry id 'zwlr-foreign-toplevel-manager-v1 version)) (handle-toplevel-manager display foreign-toplevel-manager)))) (wl-proxy-hooks registry)) (wl-display-roundtrip display) (wl-display-roundtrip display) ;; fails on 3rd roundtrip (wl-display-roundtrip display)) (defun run () (with-open-display (display) (handle-registry display))) (run)And it results:
The value of XYZ.SHUNTER.WAYFLAN.CLIENT::PROXY is NIL, which is not of type ZWLR-FOREIGN-TOPLEVEL-HANDLE-V1. [Condition of type SIMPLE-TYPE-ERROR] Restarts: 0: [STORE-VALUE] Supply a new value for XYZ.SHUNTER.WAYFLAN.CLIENT::PROXY. 1: [RETRY] Retry SLY interactive evaluation request. 2: [*ABORT] Return to SLY's top level. 3: [ABORT] abort thread (#<THREAD tid=9578 "slynk-worker" RUNNING {100779E583}>) Backtrace: 0: (SB-KERNEL:CHECK-TYPE-ERROR XYZ.SHUNTER.WAYFLAN.CLIENT::PROXY NIL ZWLR-FOREIGN-TOPLEVEL-HANDLE-V1 NIL) 1: ((LAMBDA (#:PROXY650 #:BUFFER651)) #<ZWLR-FOREIGN-TOPLEVEL-HANDLE-V1 :ID 4278190080 :VERSION 3 :HOOKS (1) {1008E62B13}> (#.(SB-SYS:INT-SAP #X7FD4C8005C80) 4 . 4)) Locals: #:BUFFER651 = (#.(SB-SYS:INT-SAP #X7FD4C8005C80) 4 . 4) #:PROXY650 = #<ZWLR-FOREIGN-TOPLEVEL-HANDLE-V1 :ID 4278190080 :VERSION 3 :HOOKS (1) {1008E62B13}> 2: ((LAMBDA (XYZ.SHUNTER.WAYFLAN.CLIENT::SENDER-ID XYZ.SHUNTER.WAYFLAN.CLIENT::OPCODE #:CARRAY2 #:SIZE3) :IN WL-DISPLAY-DISPATCH-EVENT) 4278190080 7 #.(SB-SYS:INT-SAP #X7FD4C8005C80) 4) Locals: XYZ.SHUNTER.WAYFLAN.CLIENT::OPCODE = 7 XYZ.SHUNTER.WAYFLAN.WIRE::PTR = #.(SB-SYS:INT-SAP #X7FD4C8005C80) XYZ.SHUNTER.WAYFLAN.CLIENT::SENDER-ID = 4278190080 XYZ.SHUNTER.WAYFLAN.FFI:SIZE = 4 3: ((:METHOD XYZ.SHUNTER.WAYFLAN.WIRE::%CALL-WITH-MESSAGE (XYZ.SHUNTER.WAYFLAN.WIRE:DATA-SOCKET T)) #<XYZ.SHUNTER.WAYFLAN.WIRE:DATA-SOCKET {1008D69C53}> #<FUNCTION (LAMBDA (XYZ.SHUNTER.WAYFLAN.CLIENT::SE.. Locals: FUNCTION = #<FUNCTION (LAMBDA (XYZ.SHUNTER.WAYFLAN.CLIENT::SENDER-ID XYZ.SHUNTER.WAYFLAN.CLIENT::OPCODE #:CARRAY2 #:SIZE3) :IN WL-DISPLAY-DISPATCH-EVENT) {1008E832FB}> XYZ.SHUNTER.WAYFLAN.FFI:SOCKET = #<XYZ.SHUNTER.WAYFLAN.WIRE:DATA-SOCKET {1008D69C53}>
So it seems like the allow-null error is fixed but wayflan still won't handle complicated wlroots toplevel-management lifecycle logic of I don't understand how it should work.
Also: replacing
handle-toplevel-handle
above with just:(defun handle-toplevel-handle (toplevel-handle) (zwlr-foreign-toplevel-handle-v1.close toplevel-handle))
will cause sigsegv
Found the source of the bug. I'm validating it with a test and making the change right now
Fixed, thanks! The issue was that it validates the type of the object received to make sure it matches the expected interface (in this case, toplevel handles), but didn't allow for nil. This bug was missed in the unit test since it only checked receiving nullable any-type objects.
I now go through receiving both typed and untyped, null and not-null objects.