guile-user
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

fun with wayland


From: Matt Wette
Subject: fun with wayland
Date: Sun, 5 Feb 2023 11:05:11 -0800
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Thunderbird/102.4.2

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 via email to

[Prev in Thread] Current Thread [Next in Thread]