unofficial mirror of guile-user@gnu.org 
 help / color / mirror / Atom feed
From: Matt Wette <matt.wette@gmail.com>
To: Guile User <guile-user@gnu.org>
Subject: fun with wayland
Date: Sun, 5 Feb 2023 11:05:11 -0800	[thread overview]
Message-ID: <7e5725e9-4800-05f8-1157-761158effbcc@gmail.com> (raw)

I finally got my Wayland demo in guile working.  I thought I'd share 
some bits.
(I started with creating FFI to libwayland, but with all the callbacks 
it was
not worth it.)

Wayland is a display server for Linux (and others?), meant to replace X11.
It uses UNIX socket I/O between the "compositor" (i.e., server) and clients.
I have written  a client app without using libwayland: I've coded down 
to the
socket protocol in Guile Scheme.

Summary:
1) I created sendmsg/recvmsg! wrappers for Guile, in C.  This allows me
    to send file descriptors as shared buffer references to the server.
2) I am using my proposed mmap wrapper for Guile, in C, to create a file-
    mapped shared drawing.
3) I created a "scanner" program in Guile that converts protocol specs
    (e.g., wayland.xml) to scheme.
4) I created "sender", "receiver" and "monitor" tasks within Fibers to
    run the client app
5) I used my ffi-helper generated code to use cairo for drawing.

prototype code is located at https://github.com/mwette/guile-wl-play

Here are some snippets:

;; auto-generated by "scanner" from wayland.xml:
(define-public encode-wl_display:sync
   (lambda (obj-id bv ix callback)
     (define (encode-body)
       (bytevector-u32-native-set! bv (+ ix 8) callback)
       (values (+ ix 12) #f))
     (call-with-values
       encode-body
       (lambda (msg-size control)
         (bytevector-u32-native-set! bv ix obj-id)
         (bytevector-u16-native-set! bv (+ ix 6) msg-size)
         (bytevector-u16-native-set! bv (+ ix 4) 0)
         (values msg-size control)))))

;; dispatch routine to handle events from socket
(define (dispatch obj-id opcode bv ix cm)
   (let* ((dec-vec (vector-ref object-decoders-vec obj-id))
          (decoder (and (vector-ref dec-vec opcode)))
          (hlr-vec (vector-ref object-handlers-vec obj-id))
          (handler (and (vector-ref hlr-vec opcode))))
     (if (and decoder handler)
         (call-with-values (lambda () (decoder obj-id bv ix cm)) handler)
         (begin
           (sferr "dispatch: missing decoder or handler: id=~S op=~S\n"
                  obj-id opcode)
           (sferr "  dec-vec?=~S decoder?=~S hlr-vec?=~S handler?=~S\n"
                  (and dec-vec #t) (and decoder #t)
                  (and hlr-vec #t) (and handler #t))))))

(define-syntax define-wl-request
   (lambda (x)
     (syntax-case x ()
       ((_ iface meth arg ...)
        #`(define (#,(gen-id x #'iface ":" #'meth) obj-id arg ...)
            (when wl-debug (sferr "=> ~S:~S ...\n" 'iface 'meth))
            (put-message rq-chan
              (lambda ()
                (#,(gen-id x "encode-" #'iface ":" #'meth)
                 obj-id rq-iobuf 0 arg ...))))))))

(define-wl-request wl_display sync callback)

(define (handle-wl_callback:done obj-id callback-data)
   (let ((val (vector-ref object-value-vec obj-id)))
     (if (condition? val) (signal-condition! val))
     (vector-set! object-value-vec obj-id #f)))

(define (sync-and-wait)
   (let ((id (alloc-id)) (cd (make-condition)))
     (set-object! id 'wl_callback cd)
     (wl_display:sync display-id id)
     (wait cd)))

(define socket-path
   (let ((dir (getenv "XDG_RUNTIME_DIR"))
     (dpy (getenv "WAYLAND_DISPLAY")))
     (and dir dpy (string-append dir "/" dpy))))

(define (connect-display)
   (let* ((path socket-path)
          (style (logior SOCK_STREAM SOCK_CLOEXEC))
          (sock (socket PF_UNIX style 0))
          (conn (connect sock AF_UNIX path)))
     (fcntl sock F_SETFL (logior O_NONBLOCK (fcntl sock F_GETFL)))
     (set! rq-iobuf (make-bytevector 1024))
     (set! ev-iobuf (make-bytevector 1024))
     sock))

(define (sender)
   (let loop ((n-sent 0) (n-left 0) (cm #f) (rqq '()))
     (fsleep 0.01)
     (cond
      ((positive? n-left)
       (let ((n (sendmsg wl-sock rq-iobuf n-sent n-left cm)))
         (loop (+ n-sent n) (- n-left n) #f rqq)))
      ((pair? rqq)
       (call-with-values (car rqq)
         (lambda (ln cm)
           (loop 0 ln cm (cdr rqq)))))
      ((get-message rq-chan) =>
       (lambda (req)
         (loop n-sent n-left cm (cons req rqq))))
      (else
       (sferr "sender says wtf\n")))))

(define (receiver)
   (let loop ((n-have 0) (object-id #f) (msg-size 8) (opcode #f) 
(control #f))
     (cond
      ((< n-have msg-size)
       (let* ((res (recvmsg! wl-sock ev-iobuf n-have))
              (n-read (vector-ref res 0))
              (control (or control (vector-ref res 1)))
              (flags (vector-ref res 2)))
         (when (zero? n-read) (fsleep 0.1)) ; SLEEP HERE
         (loop (+ n-have n-read) object-id msg-size opcode control)))
      ((not object-id)
       (let* ((object-id (bytevector-u32-native-ref ev-iobuf 0))
              (word1 (bytevector-u32-native-ref ev-iobuf 4))
              (msg-size (bytevector-u16-native-ref ev-iobuf 
msg-size-offset))
              (opcode (bytevector-u16-native-ref ev-iobuf opcode-offset)))
         (loop n-have object-id msg-size opcode control)))
      (else
       (dispatch object-id opcode ev-iobuf 8 control)
       (if (> n-have msg-size)
           (bytevector-copy! ev-iobuf msg-size ev-iobuf 0 (- n-have 
msg-size)))
       (loop (- n-have msg-size) #f 8 opcode control)))))

(define (monitor)
   (sferr "monitor starting ...\n")
   (let* ((server (spawn-coop-repl-server)))
     (let loop ()
       (poll-coop-repl-server server)
       (yield-current-task)
       (fsleep 0.1)
       (loop))))

(define done-cond #f)

(define (done)
   (and done-cond (signal-condition! done-cond)))

(define (appl-main)
   (run-fibers
    (lambda ()
      (set! wl-sock (connect-display))
      (set! done-cond (make-condition))
      (set! rq-chan (make-channel))
      (install-handlers)
      (init-object-pool)
      ;;
      (spawn-fiber receiver)
      (spawn-fiber sender)
      (get-registry)
      (sync-and-wait)
      (init-globals)
      (sync-and-wait)
      (create-file-buffer)
      (spawn-fiber monitor)
      ;;
      (create-it)
      ;;(wait done-cond)
      (fsleep 3.0)
      (force-output (current-error-port))
      (close-port wl-sock))
    #:hz 0 #:install-suspendable-ports? #f))


(use-modules (ffi ffi-help-rt))
(use-modules (ffi cairo))

(define* (create-file-buffer #:optional (size #x1000000))
   (let* ((port (let ((port (tmpfile))) (truncate-file port size) port))
          (fd (port->fdes port))
          (bv (my-mmap 0 size (logior PROT_READ PROT_WRITE) MAP_SHARED 
fd 0)))
     (set! my-buf-bv bv)
     (set! my-buf-fd fd)
     (if #f #f)))

(define (draw-buffer)
   (let* ((format 'CAIRO_FORMAT_ARGB32)
          (format 'CAIRO_FORMAT_RGB24)
          (buffer my-buf-bv)
          (width 500) (height 300) (stride (* width 4))
          (srf (cairo_image_surface_create_for_data
                buffer format width height stride))
          (cro (cairo_create srf))
          (extents (make-cairo_text_extents_t))
          (text "Hello, world!"))
     (bytevector-fill! buffer #xee)

     (cairo_move_to cro 0.0 0.0)
     (cairo_move_to cro 20.0 20.0)
     (cairo_line_to cro 120.0 120.0)
     (cairo_stroke cro)

     (cairo_select_font_face
      cro "Sans" 'CAIRO_FONT_SLANT_NORMAL 'CAIRO_FONT_WEIGHT_NORMAL)
     (cairo_set_font_size cro 32.0)
     (cairo_text_extents cro text (pointer-to extents))
     (cairo_move_to cro 50.0 50.0)
     (cairo_show_text cro text)

     (if #f #f)))

(appl-main)





             reply	other threads:[~2023-02-05 19:05 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-02-05 19:05 Matt Wette [this message]
2023-02-06  2:59 ` fun with wayland 宋文武
2023-02-06  7:25 ` Sascha Ziemann
2023-02-06 13:30   ` Matt Wette
2023-02-07  6:09     ` Blake Shaw
2023-02-11 21:50       ` Matt Wette
2023-02-12  8:25         ` Blake Shaw
2023-02-12 16:06           ` Matt Wette

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/guile/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=7e5725e9-4800-05f8-1157-761158effbcc@gmail.com \
    --to=matt.wette@gmail.com \
    --cc=guile-user@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).