diff options
author | thing1 <thing1@seacrossedlovers.xyz> | 2025-04-01 18:10:15 +0000 |
---|---|---|
committer | thing1 <thing1@seacrossedlovers.xyz> | 2025-04-01 18:10:15 +0000 |
commit | dabaff03992c102c395314629f63ce93a2c1bd3a (patch) | |
tree | 990472507186637085165b7cbbf7abf15c10889a /elpa/xelb-0.20/xcb-types.el |
init commit
Diffstat (limited to 'elpa/xelb-0.20/xcb-types.el')
-rw-r--r-- | elpa/xelb-0.20/xcb-types.el | 912 |
1 files changed, 912 insertions, 0 deletions
diff --git a/elpa/xelb-0.20/xcb-types.el b/elpa/xelb-0.20/xcb-types.el new file mode 100644 index 0000000..5a078aa --- /dev/null +++ b/elpa/xelb-0.20/xcb-types.el @@ -0,0 +1,912 @@ +;;; xcb-types.el --- Type definitions for XCB -*- lexical-binding: t -*- + +;; Copyright (C) 2015-2024 Free Software Foundation, Inc. + +;; Author: Chris Feng <chris.w.feng@gmail.com> + +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. + +;;; Commentary: + +;; This library defines various data types frequently used in XELB. Simple +;; types are defined with `cl-deftype' while others are defined as classes. +;; Basically, data types are used for converting objects to/from byte arrays +;; rather than for validation purpose. Classes defined elsewhere should also +;; support `xcb:marshal' and `xcb:unmarshal' methods in order to be considered +;; a type. Most classes defined here are direct or indirect subclasses of +;; `xcb:-struct', which has implemented the fundamental marshalling and +;; unmarshalling methods. These classes again act as the superclasses for more +;; concrete ones. You may use `eieio-browse' to easily get an overview of the +;; inheritance hierarchies of all classes defined. + +;; Please pay special attention to the byte order adopted in your application. +;; The global variable `xcb:lsb' specifies the byte order at the time of +;; instantiating a class (e.g. via `make-instance'). You may let-bind it to +;; temporarily change the byte order locally. + +;; Todo: +;; + The current implementation of `eieio-default-eval-maybe' only `eval's a +;; certain type of forms. If this is changed in the future, we will have to +;; adapt our codes accordingly. +;; + <paramref> for `xcb:-marshal-field'? + +;; References: +;; + X protocol (http://www.x.org/releases/X11R7.7/doc/xproto/x11protocol.txt) + +;;; Code: + +(require 'compat) +(require 'cl-lib) ; cl-coerce +(require 'cl-generic) +(require 'eieio) +(require 'xcb-debug) + +(define-minor-mode xcb:debug + "Debug-logging enabled if non-nil." + :group 'debug + :global t) + +(defmacro xcb:-log (&optional format-string &rest objects) + "Emit a message prepending the name of the function being executed. + +FORMAT-STRING is a string specifying the message to output, as in +`format'. The OBJECTS arguments specify the substitutions." + (unless format-string (setq format-string "")) + `(when xcb:debug + (xcb-debug:message ,(concat "%s%s:\t" format-string "\n") + (if xcb-debug:log-time-function + (funcall xcb-debug:log-time-function) + "") + (xcb-debug:compile-time-function-name) + ,@objects) + nil)) + +;;;; Utility functions + +(defsubst xcb:-pack-u1 (value) + "1 byte unsigned integer => byte array." + (vector value)) + +(defsubst xcb:-pack-i1 (value) + "1 byte signed integer => byte array." + (xcb:-pack-u1 (if (>= value 0) value + (1+ (logand #xFF (lognot (- value))))))) + +(defsubst xcb:-pack-u2 (value) + "2 bytes unsigned integer => byte array (MSB first)." + (vector (logand (ash value -8) #xFF) (logand value #xFF))) + +(defsubst xcb:-pack-u2-lsb (value) + "2 bytes unsigned integer => byte array (LSB first)." + (vector (logand value #xFF) (logand (ash value -8) #xFF))) + +(defsubst xcb:-pack-i2 (value) + "2 bytes signed integer => byte array (MSB first)." + (xcb:-pack-u2 (if (>= value 0) value + (1+ (logand #xFFFF (lognot (- value))))))) + +(defsubst xcb:-pack-i2-lsb (value) + "2 bytes signed integer => byte array (LSB first)." + (xcb:-pack-u2-lsb (if (>= value 0) value + (1+ (logand #xFFFF (lognot (- value))))))) + +;; Due to loss of significance of floating-point numbers, `xcb:-pack-u8' and +;; `xcb:-pack-u8-lsb' may return approximate results. +(eval-and-compile + (if (/= 0 (ash 1 32)) + ;; 64 bit + (progn + (defsubst xcb:-pack-u4 (value) + "4 bytes unsigned integer => byte array (MSB first, 64-bit)." + (vector (logand (ash value -24) #xFF) (logand (ash value -16) #xFF) + (logand (ash value -8) #xFF) (logand value #xFF))) + (defsubst xcb:-pack-u4-lsb (value) + "4 byte unsigned integer => byte array (LSB first, 64-bit)." + (vector (logand value #xFF) + (logand (ash value -8) #xFF) + (logand (ash value -16) #xFF) + (logand (ash value -24) #xFF))) + (defsubst xcb:-pack-u8 (value) + "8 bytes unsigned integer => byte array (MSB first)." + (if (integerp value) + (vector (logand (ash value -56) #xFF) + (logand (ash value -48) #xFF) + (logand (ash value -40) #xFF) + (logand (ash value -32) #xFF) + (logand (ash value -24) #xFF) + (logand (ash value -16) #xFF) + (logand (ash value -8) #xFF) + (logand value #xFF)) + (let* ((msdw (min 4294967295. (truncate value 4294967296.))) + (lsdw (min 4294967295. + (truncate (- value (* msdw 4294967296.0)))))) + (vector (logand (ash msdw -24) #xFF) (logand (ash msdw -16) #xFF) + (logand (ash msdw -8) #xFF) (logand msdw #xFF) + (logand (ash lsdw -24) #xFF) (logand (ash lsdw -16) #xFF) + (logand (ash lsdw -8) #xFF) (logand lsdw #xFF))))) + (defsubst xcb:-pack-u8-lsb (value) + "8 bytes unsigned integer => byte array (LSB first)." + (if (integerp value) + (vector (logand value #xFF) + (logand (ash value -8) #xFF) + (logand (ash value -16) #xFF) + (logand (ash value -24) #xFF) + (logand (ash value -32) #xFF) + (logand (ash value -40) #xFF) + (logand (ash value -48) #xFF) + (logand (ash value -56) #xFF)) + (let* ((msdw (min 4294967295. (truncate value 4294967296.))) + (lsdw (min 4294967295. + (truncate (- value (* msdw 4294967296.0)))))) + (vector (logand lsdw #xFF) (logand (ash lsdw -8) #xFF) + (logand (ash lsdw -16) #xFF) (logand (ash lsdw -24) #xFF) + (logand msdw #xFF) + (logand (ash msdw -8) #xFF) + (logand (ash msdw -16) #xFF) + (logand (ash msdw -24) #xFF)))))) + ;; 32 bit (30-bit actually; large numbers are represented as float type) + (defsubst xcb:-pack-u4 (value) + "4 bytes unsigned integer => byte array (MSB first, 32-bit)." + (if (integerp value) + (vector (logand (ash value -24) #xFF) (logand (ash value -16) #xFF) + (logand (ash value -8) #xFF) (logand value #xFF)) + (let* ((msw (truncate value #x10000)) + (lsw (truncate (- value (* msw 65536.0))))) + (vector (logand (ash msw -8) #xFF) (logand msw #xFF) + (logand (ash lsw -8) #xFF) (logand lsw #xFF))))) + (defsubst xcb:-pack-u4-lsb (value) + "4 bytes unsigned integer => byte array (LSB first, 32-bit)." + (if (integerp value) + (vector (logand value #xFF) (logand (ash value -8) #xFF) + (logand (ash value -16) #xFF) (logand (ash value -24) #xFF)) + (let* ((msw (truncate value #x10000)) + (lsw (truncate (- value (* msw 65536.0))))) + (vector (logand lsw #xFF) (logand (ash lsw -8) #xFF) + (logand msw #xFF) (logand (ash msw -8) #xFF))))) + (defsubst xcb:-pack-u8 (value) + "8 bytes unsigned integer => byte array (MSB first, 32-bit)." + (if (integerp value) + (vector 0 0 0 0 + (logand (ash value -24) #xFF) (logand (ash value -16) #xFF) + (logand (ash value -8) #xFF) (logand value #xFF)) + (let* ((msw (min #xFFFF (truncate value 281474976710656.))) + (w1 (min #xFFFF + (truncate (setq value + (- value (* msw 281474976710656.0))) + 4294967296.))) + (w2 (min #xFFFF + (truncate (setq value (- value (* w1 4294967296.0))) + #x10000))) + (lsw (min #xFFFF (truncate (- value (* w2 65536.0)))))) + (vector (logand (ash msw -8) #xFF) (logand msw #xFF) + (logand (ash w1 -8) #xFF) (logand w1 #xFF) + (logand (ash w2 -8) #xFF) (logand w2 #xFF) + (logand (ash lsw -8) #xFF) (logand lsw #xFF))))) + (defsubst xcb:-pack-u8-lsb (value) + "8 bytes unsigned integer => byte array (LSB first, 32-bit)." + (if (integerp value) + (vector (logand value #xFF) (logand (ash value -8) #xFF) + (logand (ash value -16) #xFF) (logand (ash value -24) #xFF) + 0 0 0 0) + (let* ((msw (min #xFFFF (truncate value 281474976710656.))) + (w1 (min #xFFFF + (truncate (setq value + (- value (* msw 281474976710656.0))) + 4294967296.))) + (w2 (min #xFFFF + (truncate (setq value (- value (* w1 4294967296.0))) + #x10000))) + (lsw (min #xFFFF (truncate (- value (* w2 65536.0)))))) + (vector (logand lsw #xFF) (logand (ash lsw -8) #xFF) + (logand w2 #xFF) (logand (ash w2 -8) #xFF) + (logand w1 #xFF) (logand (ash w1 -8) #xFF) + (logand msw #xFF) (logand (ash msw -8) #xFF))))))) + +(defsubst xcb:-pack-i4 (value) + "4 bytes signed integer => byte array (MSB first)." + (xcb:-pack-u4 (if (>= value 0) + value + (+ value 4294967296.)))) ;treated as float for 32-bit + +(defsubst xcb:-pack-i4-lsb (value) + "4 bytes signed integer => byte array (LSB first)." + (xcb:-pack-u4-lsb (if (>= value 0) + value + (+ value 4294967296.)))) ;treated as float for 32-bit + +(defsubst xcb:-unpack-u1 (data offset) + "Byte array => 1 byte unsigned integer." + (aref data offset)) + +(defsubst xcb:-unpack-i1 (data offset) + "Byte array => 1 byte signed integer." + (let ((value (xcb:-unpack-u1 data offset))) + (if (= 0 (logand #x80 value)) + value + (- (logand #xFF (lognot (1- value))))))) + +(defsubst xcb:-unpack-u2 (data offset) + "Byte array => 2 bytes unsigned integer (MSB first)." + (logior (ash (aref data offset) 8) (aref data (1+ offset)))) + +(defsubst xcb:-unpack-u2-lsb (data offset) + "Byte array => 2 bytes unsigned integer (LSB first)." + (logior (aref data offset) (ash (aref data (1+ offset)) 8))) + +(defsubst xcb:-unpack-i2 (data offset) + "Byte array => 2 bytes signed integer (MSB first)." + (let ((value (xcb:-unpack-u2 data offset))) + (if (= 0 (logand #x8000 value)) + value + (- (logand #xFFFF (lognot (1- value))))))) + +(defsubst xcb:-unpack-i2-lsb (data offset) + "Byte array => 2 bytes signed integer (LSB first)." + (let ((value (xcb:-unpack-u2-lsb data offset))) + (if (= 0 (logand #x8000 value)) + value + (- (logand #xFFFF (lognot (1- value))))))) + +;; Due to loss of significance of floating-point numbers, `xcb:-unpack-u8' and +;; `xcb:-unpack-u8-lsb' may return approximate results. +(eval-and-compile + (if (/= 0 (ash 1 32)) + ;; 64-bit + (progn + (defsubst xcb:-unpack-u4 (data offset) + "Byte array => 4 bytes unsigned integer (MSB first, 64-bit)." + (logior (ash (aref data offset) 24) (ash (aref data (1+ offset)) 16) + (ash (aref data (+ offset 2)) 8) (aref data (+ offset 3)))) + (defsubst xcb:-unpack-u4-lsb (data offset) + "Byte array => 4 bytes unsigned integer (LSB first, 64-bit)." + (logior (aref data offset) (ash (aref data (1+ offset)) 8) + (ash (aref data (+ offset 2)) 16) + (ash (aref data (+ offset 3)) 24))) + (defsubst xcb:-unpack-u8 (data offset) + "Byte array => 8 bytes unsigned integer (MSB first)." + (let ((msb (aref data offset))) + (+ (if (> msb 31) (* msb 72057594037927936.0) (ash msb 56)) + (logior (ash (aref data (1+ offset)) 48) + (ash (aref data (+ offset 2)) 40) + (ash (aref data (+ offset 3)) 32) + (ash (aref data (+ offset 4)) 24) + (ash (aref data (+ offset 5)) 16) + (ash (aref data (+ offset 6)) 8) + (aref data (+ offset 7)))))) + (defsubst xcb:-unpack-u8-lsb (data offset) + "Byte array => 8 bytes unsigned integer (LSB first)." + (let ((msb (aref data (+ offset 7)))) + (+ (if (> msb 31) (* msb 72057594037927936.0) (ash msb 56)) + (logior (ash (aref data (+ offset 6)) 48) + (ash (aref data (+ offset 5)) 40) + (ash (aref data (+ offset 4)) 32) + (ash (aref data (+ offset 3)) 24) + (ash (aref data (+ offset 2)) 16) + (ash (aref data (1+ offset)) 8) + (aref data offset)))))) + ;; 32-bit (30-bit actually; large numbers are represented as float type) + (defsubst xcb:-unpack-u4 (data offset) + "Byte array => 4 bytes unsigned integer (MSB first, 32-bit)." + (let ((msb (aref data offset))) + (+ (if (> msb 31) (* msb 16777216.0) (ash msb 24)) + (logior (ash (aref data (1+ offset)) 16) + (ash (aref data (+ offset 2)) 8) + (aref data (+ offset 3)))))) + (defsubst xcb:-unpack-u4-lsb (data offset) + "Byte array => 4 bytes unsigned integer (LSB first, 32-bit)." + (let ((msb (aref data (+ offset 3)))) + (+ (if (> msb 31) (* msb 16777216.0) (ash msb 24)) + (logior (aref data offset) + (ash (aref data (1+ offset)) 8) + (ash (aref data (+ offset 2)) 16))))) + (defsubst xcb:-unpack-u8 (data offset) + "Byte array => 8 bytes unsigned integer (MSB first, 32-bit)." + (+ (* (aref data offset) 72057594037927936.0) + (* (aref data (1+ offset)) 281474976710656.0) + (* (aref data (+ offset 2)) 1099511627776.0) + (* (aref data (+ offset 3)) 4294967296.0) + (* (aref data (+ offset 4)) 16777216.0) + (logior (ash (aref data (+ offset 5)) 16) + (ash (aref data (+ offset 6)) 8) + (aref data (+ offset 7))))) + (defsubst xcb:-unpack-u8-lsb (data offset) + "Byte array => 8 bytes unsigned integer (LSB first, 32-bit)." + (+ (* (aref data (+ offset 7)) 72057594037927936.0) + (* (aref data (+ offset 6)) 281474976710656.0) + (* (aref data (+ offset 5)) 1099511627776.0) + (* (aref data (+ offset 4)) 4294967296.0) + (* (aref data (+ offset 3)) 16777216.0) + (logior (ash (aref data (+ offset 2)) 16) + (ash (aref data (1+ offset)) 8) + (aref data offset)))))) + +(defsubst xcb:-unpack-i4 (data offset) + "Byte array => 4 bytes signed integer (MSB first)." + (let ((value (xcb:-unpack-u4 data offset))) + (if (< value 2147483648.) ;treated as float for 32-bit + value + (- value 4294967296.)))) ;treated as float for 32-bit + +(defsubst xcb:-unpack-i4-lsb (data offset) + "Byte array => 4 bytes signed integer (LSB first)." + (let ((value (xcb:-unpack-u4-lsb data offset))) + (if (< value 2147483648.) ;treated as float for 32-bit + value + (- value 4294967296.)))) ;treated as float for 32-bit + +(defsubst xcb:-f64-to-binary64 (value) + "Encode a 64-bit float VALUE as a binary64 (IEEE 754)." + (let* ((sigexp (frexp value)) + (exp (+ (cdr sigexp) 1022)) + (frac (abs (car sigexp))) + (isneg (< (copysign 1.0 (car sigexp)) 0)) ; use `copysign' to detect -0.0 + (signmask (if isneg #x8000000000000000 0))) + (+ (cond ((zerop frac) 0) ; 0 + ((isnan frac) #xff0000000000001) ; NaN + ((or (>= exp 2047) (= frac 1e+INF)) #x7ff0000000000000) ; Inf + ((<= exp 0) (ash (round (ldexp frac 52)) exp)) ; Subnormal + (t (+ (ash exp 52) (logand #xfffffffffffff + (round (ldexp frac 53)))))) ; Normal + signmask))) + +(defsubst xcb:-f32-to-binary32 (value) + "Encode a 32-bit float VALUE as a binary32 (IEEE 754)." + (let* ((sigexp (frexp value)) + (exp (+ (cdr sigexp) 126)) + (frac (abs (car sigexp))) + (isneg (< (copysign 1.0 (car sigexp)) 0)) ; use `copysign' to detect -0.0 + (signmask (if isneg #x80000000 0))) + (+ (cond ((zerop frac) 0) ; 0 + ((isnan frac) #x7f800001) ; NaN + ((or (>= exp 255) (= frac 1e+INF)) #x7f800000) ; Inf + ((<= exp 0) (ash (round (ldexp frac 23)) exp)) ; Subnormal + (t (+ (ash exp 23) (logand #x7fffff (round (ldexp frac 24)))))) ; Normal + signmask))) + +(defsubst xcb:-binary64-to-f64 (value) + "Decode binary64 VALUE into a float." + (let ((sign (pcase (ash value -63) + (0 +0.0) + (1 -0.0) + (_ (error "[XCB] Value too large for a float64: %d" value)))) + (exp (logand 2047 (ash value -52))) + (frac (logand #xfffffffffffff value))) + (copysign ; Use copysign, not multiplication, to deal with +/- NAN. + (pcase exp + (2047 (if (zerop frac) 1e+INF 1e+NaN)) ; INF/NAN + (0 (ldexp frac -1074)) ; Subnormal + (_ (ldexp (+ #x10000000000000 frac) (- exp 1075)))) ; Normal + sign))) + +(defsubst xcb:-binary32-to-f32 (value) + "Decode binary32 VALUE into a float." + (let ((sign (pcase (ash value -31) + (0 +0.0) + (1 -0.0) + (_ (error "[XCB] Value too large for a float32: %d" value)))) + (exp (logand 255 (ash value -23))) + (frac (logand #x7fffff value))) + (copysign ; Use copysign, not multiplication, to deal with +/- NAN. + (pcase exp + (255 (if (zerop frac) 1e+INF 1e+NaN)) ; INF/NAN + (0 (ldexp frac -149)) ; Subnormal + (_ (ldexp (+ #x800000 frac) (- exp 150)))) ; Normal + sign))) + +(defmacro xcb:-fieldref (field) + "Evaluate a <fieldref> field." + `(slot-value obj ,field)) + +(defmacro xcb:-paramref (field) + "Evaluate a <paramref> field." + `(slot-value ctx ,field)) + +(defsubst xcb:-request-class->reply-class (request) + "Return the reply class corresponding to the request class REQUEST." + (intern-soft (concat (symbol-name request) "~reply"))) + +;;;; Basic types + +;; typedef in C +(defmacro xcb:deftypealias (new-type old-type) + "Define NEW-TYPE as an alias of type OLD-TYPE. + +Also the fundamental type is stored in the `xcb--typealias' +variable property (for internal use only)." + `(progn + ;; FIXME: `new-type' should probably just not be eval'd at all, + ;; but that requires changing all callers not to quote their arg. + (cl-deftype ,(eval new-type t) nil ,old-type) + (put ,new-type 'xcb--typealias + (or (get ,old-type 'xcb--typealias) ,old-type)))) + +;; 1/2/4 B signed/unsigned integer +(cl-deftype xcb:-i1 () t) +(cl-deftype xcb:-i2 () t) +(cl-deftype xcb:-i4 () t) +(cl-deftype xcb:-u1 () t) +(cl-deftype xcb:-u2 () t) +(cl-deftype xcb:-u4 () t) +;; 8 B unsigned integer +(cl-deftype xcb:-u8 () t) +;; floats & doubles +(cl-deftype xcb:-f32 () t) +(cl-deftype xcb:-f64 () t) +;; <pad> +(cl-deftype xcb:-pad () t) +;; <pad> with align attribute +(cl-deftype xcb:-pad-align () t) +;; <fd> +(xcb:deftypealias 'xcb:fd 'xcb:-i4) +;; <list> +(cl-deftype xcb:-list () t) +;; <switch> +(cl-deftype xcb:-switch () t) +;; This type of data is not involved in marshalling/unmarshalling +(cl-deftype xcb:-ignore () t) +;; C types and types missing in XCB +(cl-deftype xcb:void () t) +(xcb:deftypealias 'xcb:char 'xcb:-u1) +(xcb:deftypealias 'xcb:BYTE 'xcb:-u1) +(xcb:deftypealias 'xcb:INT8 'xcb:-i1) +(xcb:deftypealias 'xcb:INT16 'xcb:-i2) +(xcb:deftypealias 'xcb:INT32 'xcb:-i4) +(xcb:deftypealias 'xcb:CARD8 'xcb:-u1) +(xcb:deftypealias 'xcb:CARD16 'xcb:-u2) +(xcb:deftypealias 'xcb:CARD32 'xcb:-u4) +(xcb:deftypealias 'xcb:CARD64 'xcb:-u8) +(xcb:deftypealias 'xcb:BOOL 'xcb:-u1) +(xcb:deftypealias 'xcb:float 'xcb:-f32) +(xcb:deftypealias 'xcb:double 'xcb:-f64) + +;;;; Struct type + +(eval-and-compile + (defvar xcb:lsb t + "Non-nil for LSB first (i.e., little-endian), nil otherwise. + +Consider let-bind it rather than change its global value.")) + +(defclass xcb:--struct () + nil) + +(cl-defmethod slot-unbound ((object xcb:--struct) class slot-name fn) + (unless (eq fn #'oref-default) + (xcb:-log "unbound-slot: %s" (list (eieio-class-name class) + (eieio-object-name object) + slot-name fn)))) + +(defclass xcb:-struct (xcb:--struct) + ((~lsb :initarg :~lsb + :initform (symbol-value 'xcb:lsb) ;see `eieio-default-eval-maybe' + :type xcb:-ignore) + (~size :initform nil :type xcb:-ignore)) + :documentation "Struct type.") + +(cl-defmethod xcb:marshal ((obj xcb:-struct)) + "Return the byte-array representation of struct OBJ." + (let ((slots (eieio-class-slots (eieio-object-class obj))) + result name type value) + (catch 'break + (dolist (slot slots) + (setq type (cl--slot-descriptor-type slot)) + (unless (eq type 'xcb:-ignore) + (setq name (eieio-slot-descriptor-name slot)) + (setq value (slot-value obj name)) + (when (symbolp value) ;see `eieio-default-eval-maybe' + (setq value (symbol-value value))) + (setq result + (vconcat result (xcb:-marshal-field obj type value + (length result)))) + (when (eq type 'xcb:-switch) ;xcb:-switch always finishes a struct + (throw 'break 'nil))))) + ;; If we specify a size, verify that it matches the actual size. + (when-let* ((size-exp (slot-value obj '~size)) + (size (eval size-exp `((obj . ,obj))))) + (unless (length= result size) + (error "[XCB] Unexpected size for type %s: got %d, expected %d" + (type-of obj) + (length result) + size))) + result)) + +(cl-defmethod xcb:-marshal-field ((obj xcb:-struct) type value &optional pos) + "Return the byte-array representation of a field in struct OBJ of type TYPE +and value VALUE. + +The optional POS argument indicates current byte index of the field (used by +`xcb:-pad-align' type)." + (pcase (or (get type 'xcb--typealias) type) + (`xcb:-u1 (xcb:-pack-u1 value)) + (`xcb:-i1 (xcb:-pack-i1 value)) + (`xcb:-u2 + (if (slot-value obj '~lsb) (xcb:-pack-u2-lsb value) (xcb:-pack-u2 value))) + (`xcb:-i2 + (if (slot-value obj '~lsb) (xcb:-pack-i2-lsb value) (xcb:-pack-i2 value))) + (`xcb:-u4 + (if (slot-value obj '~lsb) (xcb:-pack-u4-lsb value) (xcb:-pack-u4 value))) + (`xcb:-i4 + (if (slot-value obj '~lsb) (xcb:-pack-i4-lsb value) (xcb:-pack-i4 value))) + (`xcb:-u8 + (if (slot-value obj '~lsb) (xcb:-pack-u8-lsb value) (xcb:-pack-u8 value))) + (`xcb:-f32 + (let ((value (xcb:-f32-to-binary32 value))) + (if (slot-value obj '~lsb) (xcb:-pack-u4-lsb value) (xcb:-pack-u4 value)))) + (`xcb:-f64 + (let ((value (xcb:-f64-to-binary64 value))) + (if (slot-value obj '~lsb) (xcb:-pack-u8-lsb value) (xcb:-pack-u8 value)))) + (`xcb:void (vector value)) + (`xcb:-pad + (unless (integerp value) + (setq value (eval value `((obj . ,obj))))) + (make-vector value 0)) + (`xcb:-pad-align + ;; The length slot in xcb:-request is left out + (let ((len (if (object-of-class-p obj 'xcb:-request) (+ pos 2) pos))) + (when (vectorp value) + ;; Alignment with offset. + (setq len (- len (aref value 1)) + value (aref value 0))) + (unless (integerp value) + (setq value (eval value `((obj . ,obj))))) + (make-vector (% (- value (% len value)) value) 0))) + (`xcb:-list + (let* ((list-name (plist-get value 'name)) + (list-type (plist-get value 'type)) + (list-size (plist-get value 'size)) + (data (slot-value obj list-name))) + (unless (integerp list-size) + (setq list-size (eval list-size `((obj . ,obj)))) + (unless list-size + (setq list-size (length data)))) ;list-size can be nil + (cl-assert (= list-size (length data))) + ;; The data may be large, and if it's a string that's supposed + ;; to be converted to a vector of bytes, then the transform can + ;; be done trivially and much faster by just coercing. + (if (and (eq list-type 'xcb:BYTE) + (eq (type-of data) 'string)) + (cl-coerce data 'vector) + (mapconcat (lambda (i) (xcb:-marshal-field obj list-type i)) + data [])))) + (`xcb:-switch + (let ((slots (eieio-class-slots (eieio-object-class obj))) + (expression (plist-get value 'expression)) + (cases (plist-get value 'cases)) + result condition name-list flag slot-type) + (unless (integerp expression) + (setq expression (eval expression `((obj . ,obj))))) + (cl-assert (integerp expression)) + (dolist (i cases) + (setq condition (car i)) + (setq name-list (cdr i)) + (setq flag nil) + (cl-assert (or (integerp condition) (listp condition))) + (if (integerp condition) + (setq flag (/= 0 (logand expression condition))) + (if (eq 'logior (car condition)) + (setq flag (/= 0 (logand expression + (apply #'logior (cdr condition))))) + (setq flag (memq expression condition)))) + (when flag + (dolist (name name-list) + (catch 'break + (dolist (slot slots) ;better way to find the slot type? + (when (eq name (eieio-slot-descriptor-name slot)) + (setq slot-type (cl--slot-descriptor-type slot)) + (throw 'break nil)))) + (unless (eq slot-type 'xcb:-ignore) + (setq result + (vconcat result + (xcb:-marshal-field obj slot-type + (slot-value obj name) + (+ pos + (length result))))))))) + result)) + ((guard (child-of-class-p type 'xcb:-struct)) + (xcb:marshal value)) + (x (error "[XCB] Unsupported type for marshalling: %s" x)))) + +(cl-defmethod xcb:unmarshal ((obj xcb:-struct) byte-array &optional ctx + total-length) + "Fill in fields of struct OBJ according to its byte-array representation. + +The optional argument CTX is for <paramref>." + (unless total-length + (setq total-length (length byte-array))) + (let ((slots (eieio-class-slots (eieio-object-class obj))) + (result 0) + slot-name tmp type) + (catch 'break + (dolist (slot slots) + (setq type (cl--slot-descriptor-type slot)) + (unless (eq type 'xcb:-ignore) + (setq slot-name (eieio-slot-descriptor-name slot) + tmp (xcb:-unmarshal-field obj type byte-array result + (eieio-oref-default obj slot-name) + ctx total-length)) + (setf (slot-value obj slot-name) (car tmp)) + (setq result (+ result (cadr tmp))) + (when (eq type 'xcb:-switch) ;xcb:-switch always finishes a struct + (throw 'break 'nil))))) + ;; Let the struct compute it's size if a length field is specified. This lets us skip unknown + ;; fields. + (when-let* ((size-exp (slot-value obj '~size)) + (size (eval size-exp `((obj . ,obj))))) + ;; Make sure the stated size is reasonable. + (cond + ((< size result) + (error "[XCB] Object of type `%s' specified a size (%d) less than the number of bytes read (%d)" + (type-of obj) size result)) + ((length< byte-array (- size result)) + (error "[XCB] Object of type `%s' specified a size (%d) greater than the size of the input (%d)" + (type-of obj) size (+ result (length byte-array))))) + ;; Skip any additional bytes. + (setq result size)) + result)) + +(cl-defmethod xcb:-unmarshal-field ((obj xcb:-struct) type data offset + initform &optional ctx total-length) + "Return the value of a field in struct OBJ of type TYPE, byte-array +representation DATA, and default value INITFORM. + +The optional argument CTX is for <paramref>. + +This method returns a list of two components, with the first being the result +and the second the consumed length." + (pcase (or (get type 'xcb--typealias) type) + (`xcb:-u1 (list (aref data offset) 1)) + (`xcb:-i1 (let ((result (aref data offset))) + (list (if (< result 128) result (- result 255)) 1))) + (`xcb:-u2 (list (if (slot-value obj '~lsb) + (xcb:-unpack-u2-lsb data offset) + (xcb:-unpack-u2 data offset)) + 2)) + (`xcb:-i2 (list (if (slot-value obj '~lsb) + (xcb:-unpack-i2-lsb data offset) + (xcb:-unpack-i2 data offset)) + 2)) + (`xcb:-u4 (list (if (slot-value obj '~lsb) + (xcb:-unpack-u4-lsb data offset) + (xcb:-unpack-u4 data offset)) + 4)) + (`xcb:-i4 (list (if (slot-value obj '~lsb) + (xcb:-unpack-i4-lsb data offset) + (xcb:-unpack-i4 data offset)) + 4)) + (`xcb:-u8 (list (if (slot-value obj '~lsb) + (xcb:-unpack-u8-lsb data offset) + (xcb:-unpack-u8 data offset)) + 8)) + (`xcb:-f32 (list (xcb:-binary32-to-f32 + (if (slot-value obj '~lsb) + (xcb:-unpack-u4-lsb data offset) + (xcb:-unpack-u4 data offset))) + 4)) + (`xcb:-f64 (list (xcb:-binary64-to-f64 + (if (slot-value obj '~lsb) + (xcb:-unpack-u8-lsb data offset) + (xcb:-unpack-u8 data offset))) + 8)) + (`xcb:void (list (aref data offset) 1)) + (`xcb:-pad + (unless (integerp initform) + (when (eq 'quote (car initform)) + (setq initform (cadr initform))) + (setq initform (eval initform `((obj . ,obj) (ctx . ,ctx))))) + (list initform initform)) + (`xcb:-pad-align + (let ((len (- total-length (- (length data) offset)))) + (if (vectorp initform) + ;; Alignment with offset. + (setq len (- len (aref initform 1)) + initform (aref initform 0)) + (unless (integerp initform) + (when (eq 'quote (car initform)) + (setq initform (cadr initform))) + (setq initform (eval initform `((obj . ,obj) (ctx . ,ctx)))))) + (list initform (% (- initform (% len initform)) initform)))) + (`xcb:-list + (when (eq 'quote (car initform)) ;unquote the form + (setq initform (cadr initform))) + (let ((list-name (plist-get initform 'name)) + (list-type (plist-get initform 'type)) + (list-size (plist-get initform 'size))) + (unless (integerp list-size) + (setq list-size (eval list-size `((obj . ,obj) (ctx . ,ctx))))) + (cl-assert (integerp list-size)) + (pcase list-type + (`xcb:char ;as Latin-1 encoded string + (setf (slot-value obj list-name) + (decode-coding-string + (apply #'unibyte-string + (append (substring data offset + (+ offset list-size)) + nil)) + 'iso-latin-1))) + (`xcb:void ;for further unmarshalling + (setf (slot-value obj list-name) + (substring data offset (+ offset list-size)))) + (x + (let ((count 0) + result tmp) + (dotimes (_ list-size) + (setq tmp (xcb:-unmarshal-field obj x data (+ offset count) nil + nil total-length)) + (setq result (nconc result (list (car tmp)))) + (setq count (+ count (cadr tmp)))) + (setf (slot-value obj list-name) result) + (setq list-size count)))) ;to byte length + (list initform list-size))) + (`xcb:-switch + (let ((slots (eieio-class-slots (eieio-object-class obj))) + (expression (plist-get initform 'expression)) + (cases (plist-get initform 'cases)) + (count 0) + condition name-list flag slot-type tmp) + (unless (integerp expression) + (setq expression (eval expression `((obj . ,obj) (ctx . ,ctx))))) + (cl-assert (integerp expression)) + (dolist (i cases) + (setq condition (car i)) + (setq name-list (cdr i)) + (setq flag nil) + (cl-assert (or (integerp condition) (listp condition))) + (if (integerp condition) + (setq flag (/= 0 (logand expression condition))) + (if (eq 'logior (car condition)) + (setq flag (/= 0 (logand expression + (apply #'logior (cdr condition))))) + (setq flag (memq expression condition)))) + (when flag + (dolist (name name-list) + (catch 'break + (dolist (slot slots) ;better way to find the slot type? + (when (eq name (eieio-slot-descriptor-name slot)) + (setq slot-type (cl--slot-descriptor-type slot)) + (throw 'break nil)))) + (unless (eq slot-type 'xcb:-ignore) + (setq tmp (xcb:-unmarshal-field obj slot-type data (+ offset count) + (eieio-oref-default obj name) + nil total-length)) + (setf (slot-value obj name) (car tmp)) + (setq count (+ count (cadr tmp))))))) + (list initform count))) + ((and x (guard (child-of-class-p x 'xcb:-struct))) + (let* ((struct-obj (make-instance x)) + (tmp (xcb:unmarshal struct-obj (substring data offset) obj + total-length))) + (list struct-obj tmp))) + (x (error "[XCB] Unsupported type for unmarshalling: %s" x)))) + +;;;; Types derived directly from `xcb:-struct' + +(defclass xcb:-request (xcb:-struct) + nil + :documentation "X request type.") + +(defclass xcb:-reply (xcb:-struct) + ((~reply :initform 1 :type xcb:-u1)) + :documentation "X reply type.") + +(defclass xcb:-event (xcb:-struct) + ((~code :type xcb:-u1)) + :documentation "Event type.") +;; Implemented in 'xcb.el' +(cl-defgeneric xcb:-error-or-event-class->number (obj class)) +;; +(cl-defmethod xcb:marshal ((obj xcb:-event) connection &optional sequence) + "Return the byte-array representation of event OBJ. + +This method is mainly designed for `xcb:SendEvent', where it's used to +generate synthetic events. The CONNECTION argument is used to retrieve +the event number of extensions. If SEQUENCE is non-nil, it is used as +the sequence number of the synthetic event (if the event uses sequence +number); otherwise, 0 is assumed. + +This method auto-pads short results to 32 bytes." + (let ((event-number + (xcb:-error-or-event-class->number connection + (eieio-object-class obj))) + result) + (when (consp event-number) + (setq event-number (cdr event-number)) + (if (= 1 (length event-number)) + ;; XKB event. + (setf (slot-value obj 'xkbType) (aref event-number 0)) + ;; Generic event. + (setf (slot-value obj 'extensions) (aref event-number 0) + (slot-value obj 'evtype) (aref event-number 1)))) + (when (slot-exists-p obj '~sequence) + (setf (slot-value obj '~sequence) (or sequence 0))) + (setq result (cl-call-next-method obj)) + (when (> 32 (length result)) + (setq result (vconcat result (make-vector (- 32 (length result)) 0)))) + result)) + +(defclass xcb:-generic-event (xcb:-event) + ((~code :initform 35) + (~extension :type xcb:CARD8) + (~sequence :type xcb:CARD16) + (~length :type xcb:CARD32) + (~evtype :type xcb:CARD16)) + :documentation "Generic event type.") + +(defclass xcb:-error (xcb:-struct) + ((~error :initform 0 :type xcb:-u1) + (~code :type xcb:-u1) + (~sequence :type xcb:CARD16)) + :documentation "X error type.") + +(defclass xcb:-union (xcb:-struct) + ((~size :initarg :~size :type xcb:-ignore)) ;Size of the largest member. + :documentation "Union type.") +;; +(cl-defmethod slot-unbound ((_object xcb:-union) _class _slot-name _fn) + nil) +;; +(cl-defmethod xcb:marshal ((obj xcb:-union)) + "Return the byte-array representation of union OBJ. + +This result is converted from the first bounded slot." + (let ((slots (eieio-class-slots (eieio-object-class obj))) + (size (slot-value obj '~size)) + result slot type name tmp) + (while (and (not result) slots (> size (length result))) + (setq slot (pop slots)) + (setq type (cl--slot-descriptor-type slot) + name (eieio-slot-descriptor-name slot)) + (unless (or (not (slot-value obj name)) + (eq type 'xcb:-ignore) + ;; Dealing with `xcb:-list' type + (and (eq type 'xcb:-list) + (not (slot-value obj (plist-get (slot-value obj name) + 'name))))) + (setq tmp (xcb:-marshal-field obj (cl--slot-descriptor-type slot) + (slot-value obj name))) + (when (> (length tmp) (length result)) + (setq result tmp)))) + (cond + ((length< result size) + (setq result (vconcat result (make-vector (- size (length result)) 0)))) + ((length> result size) + (error "[XCB] Marshaled enum `%s' is larger than its declared size (%d > %d)" + (type-of obj) (length result) size))) + result)) +;; +(cl-defmethod xcb:unmarshal ((obj xcb:-union) byte-array &optional ctx + total-length) + "Fill in every field in union OBJ, according to BYTE-ARRAY. + +The optional argument CTX is for <paramref>." + (unless total-length + (setq total-length (length byte-array))) + (let ((slots (eieio-class-slots (eieio-object-class obj))) + slot-name tmp type) + (dolist (slot slots) + (setq type (cl--slot-descriptor-type slot)) + (unless (eq type 'xcb:-ignore) + (setq slot-name (eieio-slot-descriptor-name slot) + tmp (xcb:-unmarshal-field obj type byte-array 0 + (eieio-oref-default obj slot-name) + ctx total-length)) + (setf (slot-value obj (eieio-slot-descriptor-name slot)) (car tmp)))) + (slot-value obj '~size))) + + + +(provide 'xcb-types) + +;;; xcb-types.el ends here |