home *** CD-ROM | disk | FTP | other *** search
- ;;; -*- Mode: LISP; Syntax: Common-lisp; Package: XLIB; Base: 10; Lowercase: Yes -*-
-
- ;;; GContext
-
- ;;;
- ;;; TEXAS INSTRUMENTS INCORPORATED
- ;;; P.O. BOX 2909
- ;;; AUSTIN, TEXAS 78769
- ;;;
- ;;; Copyright (C) 1987 Texas Instruments Incorporated.
- ;;;
- ;;; Permission is granted to any individual or institution to use, copy, modify,
- ;;; and distribute this software, provided that this complete copyright and
- ;;; permission notice is maintained, intact, in all copies and supporting
- ;;; documentation.
- ;;;
- ;;; Texas Instruments Incorporated provides this software "as is" without
- ;;; express or implied warranty.
- ;;;
-
- ;;; GContext values are usually cached locally in the GContext object.
- ;;; This is required because the X.11 server doesn't have any requests
- ;;; for getting GContext values back.
- ;;;
- ;;; GContext changes are cached until force-GContext-changes is called.
- ;;; All the requests that use GContext (including the GContext accessors,
- ;;; but not the SETF's) call force-GContext-changes.
- ;;; In addition, the macro WITH-GCONTEXT may be used to provide a
- ;;; local view if a GContext.
- ;;;
- ;;; Each GContext keeps a copy of the values the server has seen, and
- ;;; a copy altered by SETF, called the LOCAL-STATE (bad name...).
- ;;; The SETF accessors increment a timestamp in the GContext.
- ;;; When the timestamp in a GContext isn't equal to the timestamp in
- ;;; the local-state, changes have been made, and force-GContext-changes
- ;;; loops through the GContext and local-state, sending differences to
- ;;; the server, and updating GContext.
- ;;;
- ;;; WITH-GCONTEXT works by BINDING the local-state slot in a GContext to
- ;;; a private copy. This is easy (and fast) for lisp machines, but other
- ;;; lisps will have problems. Fortunately, most other lisps don't care,
- ;;; because they don't run in a multi-processing shared-address space
- ;;; environment.
-
- (in-package :xlib)
-
- ;; GContext state accessors
- ;; The state vector contains all card32s to speed server updating
-
- (eval-when (eval compile load)
-
- (defconstant *gcontext-fast-change-length* #.(length *gcontext-components*))
-
- (macrolet ((def-gc-internals (name &rest extras)
- (let ((macros nil)
- (indexes nil)
- (masks nil)
- (index 0))
- (dolist (name *gcontext-components*)
- (push `(defmacro ,(xintern 'gcontext-internal- name) (state)
- `(svref ,state ,,index))
- macros)
- (setf (getf indexes name) index)
- (push (ash 1 index) masks)
- (incf index))
- (dolist (extra extras)
- (push `(defmacro ,(xintern 'gcontext-internal- (first extra)) (state)
- `(svref ,state ,,index))
- macros)
- ;; don't override already correct index entries
- (unless (or (getf indexes (second extra)) (getf indexes (first extra)))
- (setf (getf indexes (or (second extra) (first extra))) index))
- (push (logior (ash 1 index)
- (if (second extra)
- (ash 1 (position (second extra) *gcontext-components*))
- 0))
- masks)
- (incf index))
- `(within-definition (def-gc-internals ,name)
- ,@(nreverse macros)
- (eval-when (eval compile load)
- (defconstant *gcontext-data-length* ,index)
- (defconstant *gcontext-indexes* ',indexes)
- (defconstant *gcontext-masks*
- ',(coerce (nreverse masks) 'simple-vector)))))))
- (def-gc-internals ignore
- (:clip :clip-mask) (:dash :dashes) (:font-obj :font) (:timestamp)))
-
- ) ;; end EVAL-WHEN
-
- (deftype gcmask () '(unsigned-byte #.*gcontext-fast-change-length*))
-
- (deftype xgcmask () '(unsigned-byte #.*gcontext-data-length*))
-
- (defstruct (gcontext-extension (:type vector) (:copier nil)) ;; un-named
- (name nil :type symbol :read-only t)
- (default nil :type t :read-only t)
- (set-function #'identity :type (function (gcontext t) t) :read-only t)
- (copy-function #'identity :type (function (gcontext gcontext t) t) :read-only t))
-
- (defvar *gcontext-extensions* nil) ;; list of gcontext-extension
-
- ;; Gcontext state Resource
- (defvar *gcontext-local-state-cache* nil) ;; List of unused gcontext local states
-
- (defmacro gcontext-state-next (state)
- `(svref ,state 0))
-
- (defun allocate-gcontext-state ()
- ;; Allocate a gcontext-state
- ;; Loop until a local state is found that's large enough to hold
- ;; any extensions that may exist.
- (let ((length (index+ *gcontext-data-length* (length *gcontext-extensions*))))
- (declare (type array-index length))
- (loop
- (let ((state (or (threaded-atomic-pop *gcontext-local-state-cache*
- gcontext-state-next gcontext-state)
- (make-array length :initial-element nil))))
- (declare (type gcontext-state state))
- (when (index>= (length state) length)
- (return state))))))
-
- (defun deallocate-gcontext-state (state)
- (declare (type gcontext-state state))
- (fill state nil)
- (threaded-atomic-push state *gcontext-local-state-cache*
- gcontext-state-next gcontext-state))
-
- ;; Temp-Gcontext Resource
- (defvar *temp-gcontext-cache* nil) ;; List of unused gcontexts
-
- (defun allocate-temp-gcontext ()
- (or (threaded-atomic-pop *temp-gcontext-cache* gcontext-next gcontext)
- (make-gcontext :local-state '#() :server-state '#())))
-
- (defun deallocate-temp-gcontext (gc)
- (declare (type gcontext gc))
- (threaded-atomic-push gc *temp-gcontext-cache* gcontext-next gcontext))
-
- ;; For each argument to create-gcontext (except clip-mask and clip-ordering) declared
- ;; as (type <type> <name>), there is an accessor:
-
- ;(defun gcontext-<name> (gcontext)
- ; ;; The value will be nil if the last value stored is unknown (e.g., the cache was
- ; ;; off, or the component was copied from a gcontext with unknown state).
- ; (declare (type gcontext gcontext)
- ; (values <type>)))
-
- ;; For each argument to create-gcontext (except clip-mask and clip-ordering) declared
- ;; as (type (or null <type>) <name>), there is a setf for the corresponding accessor:
-
- ;(defsetf gcontext-<name> (gcontext) (value)
- ; )
-
- ;; Generate all the accessors and defsetf's for GContext
-
- (defmacro xgcmask->gcmask (mask)
- `(the gcmask (logand ,mask #.(1- (ash 1 *gcontext-fast-change-length*)))))
-
- (defmacro access-gcontext ((gcontext local-state) &body body)
- `(let ((,local-state (gcontext-local-state ,gcontext)))
- (declare (type gcontext-state ,local-state))
- ,@body))
-
- (defmacro modify-gcontext ((gcontext local-state) &body body)
- ;; The timestamp must be altered after the modification
- `(let ((,local-state (gcontext-local-state ,gcontext)))
- (declare (type gcontext-state ,local-state))
- (prog1
- (progn ,@body)
- (setf (gcontext-internal-timestamp ,local-state) 0))))
-
- (defmacro def-gc-accessor (name type)
- (let* ((gcontext-name (xintern 'gcontext- name))
- (internal-accessor (xintern 'gcontext-internal- name))
- (internal-setfer (xintern 'set- gcontext-name)))
- `(within-definition (,name def-gc-accessor)
-
- (defun ,gcontext-name (gcontext)
- (declare (type gcontext gcontext))
- (declare (values (or null ,type)))
- (let ((value (,internal-accessor (gcontext-local-state gcontext))))
- (declare (type (or null card32) value))
- (when value ;; Don't do anything when value isn't known
- (let ((%buffer (gcontext-display gcontext)))
- (declare (type display %buffer))
- %buffer
- (decode-type ,type value)))))
-
- (defun ,internal-setfer (gcontext value)
- (declare (type gcontext gcontext)
- (type ,type value))
- (modify-gcontext (gcontext local-state)
- (setf (,internal-accessor local-state) (encode-type ,type value))
- ,@(when (eq type 'pixmap)
- ;; write-through pixmaps, because the protocol allows
- ;; the server to copy the pixmap contents at the time
- ;; of the store, rather than continuing to share with
- ;; the pixmap.
- `((let ((server-state (gcontext-server-state gcontext)))
- (setf (,internal-accessor server-state) nil))))
- value))
-
- (defsetf ,gcontext-name ,internal-setfer))))
-
- (defmacro incf-internal-timestamp (state)
- (let ((ts (gensym)))
- `(let ((,ts (the fixnum (gcontext-internal-timestamp ,state))))
- (declare (type fixnum ,ts))
- ;; the probability seems low enough
- (setq ,ts (if (= ,ts most-positive-fixnum)
- 1
- (the fixnum (1+ ,ts))))
- (setf (gcontext-internal-timestamp ,state) ,ts))))
-
- (def-gc-accessor function boole-constant)
- (def-gc-accessor plane-mask card32)
- (def-gc-accessor foreground card32)
- (def-gc-accessor background card32)
- (def-gc-accessor line-width card16)
- (def-gc-accessor line-style (member :solid :dash :double-dash))
- (def-gc-accessor cap-style (member :not-last :butt :round :projecting))
- (def-gc-accessor join-style (member :miter :round :bevel))
- (def-gc-accessor fill-style (member :solid :tiled :stippled :opaque-stippled))
- (def-gc-accessor fill-rule (member :even-odd :winding))
- (def-gc-accessor tile pixmap)
- (def-gc-accessor stipple pixmap)
- (def-gc-accessor ts-x int16) ;; Tile-Stipple-X-origin
- (def-gc-accessor ts-y int16) ;; Tile-Stipple-Y-origin
- ;; (def-GC-accessor font font) ;; See below
- (def-gc-accessor subwindow-mode (member :clip-by-children :include-inferiors))
- (def-gc-accessor exposures (member :off :on))
- (def-gc-accessor clip-x int16)
- (def-gc-accessor clip-y int16)
- ;; (def-GC-accessor clip-mask) ;; see below
- (def-gc-accessor dash-offset card16)
- ;; (def-GC-accessor dashes) ;; see below
- (def-gc-accessor arc-mode (member :chord :pie-slice))
-
-
- (defun gcontext-clip-mask (gcontext)
- (declare (type gcontext gcontext))
- (declare (values (or null (member :none) pixmap rect-seq)
- (or null (member :unsorted :y-sorted :yx-sorted :yx-banded))))
- (access-gcontext (gcontext local-state)
- (multiple-value-bind (clip clip-mask)
- (without-interrupts
- (values (gcontext-internal-clip local-state)
- (gcontext-internal-clip-mask local-state)))
- (if (null clip)
- (values (let ((%buffer (gcontext-display gcontext)))
- (declare (type display %buffer))
- (decode-type (or (member :none) pixmap) clip-mask))
- nil)
- (values (second clip)
- (decode-type (or null (member :unsorted :y-sorted :yx-sorted :yx-banded))
- (first clip)))))))
-
- (defsetf gcontext-clip-mask (gcontext &optional ordering) (clip-mask)
- ;; A bit strange, but retains setf form.
- ;; a nil clip-mask is transformed to an empty vector
- `(set-gcontext-clip-mask ,gcontext ,ordering ,clip-mask))
-
- (defun set-gcontext-clip-mask (gcontext ordering clip-mask)
- ;; a nil clip-mask is transformed to an empty vector
- (declare (type gcontext gcontext)
- (type (or null (member :unsorted :y-sorted :yx-sorted :yx-banded)) ordering)
- (type (or (member :none) pixmap rect-seq) clip-mask))
- (unless clip-mask (x-type-error clip-mask '(or (member :none) pixmap rect-seq)))
- (multiple-value-bind (clip-mask clip)
- (typecase clip-mask
- (pixmap (values (pixmap-id clip-mask) nil))
- ((member :none) (values 0 nil))
- (sequence
- (values nil
- (list (encode-type
- (or null (member :unsorted :y-sorted :yx-sorted :yx-banded))
- ordering)
- (copy-seq clip-mask))))
- (otherwise (x-type-error clip-mask '(or (member :none) pixmap rect-seq))))
- (modify-gcontext (gcontext local-state)
- (let ((server-state (gcontext-server-state gcontext)))
- (declare (type gcontext-state server-state))
- (without-interrupts
- (setf (gcontext-internal-clip local-state) clip
- (gcontext-internal-clip-mask local-state) clip-mask)
- (if (null clip)
- (setf (gcontext-internal-clip server-state) nil)
- (setf (gcontext-internal-clip-mask server-state) nil))
- (when (and clip-mask (not (zerop clip-mask)))
- ;; write-through clip-mask pixmap, because the protocol allows the
- ;; server to copy the pixmap contents at the time of the store,
- ;; rather than continuing to share with the pixmap.
- (setf (gcontext-internal-clip-mask server-state) nil))))))
- clip-mask)
-
- (defun gcontext-dashes (gcontext)
- (declare (type gcontext gcontext))
- (declare (values (or null card8 sequence)))
- (access-gcontext (gcontext local-state)
- (multiple-value-bind (dash dashes)
- (without-interrupts
- (values (gcontext-internal-dash local-state)
- (gcontext-internal-dashes local-state)))
- (if (null dash)
- dashes
- dash))))
-
- (defsetf gcontext-dashes set-gcontext-dashes)
-
- (defun set-gcontext-dashes (gcontext dashes)
- (declare (type gcontext gcontext)
- (type (or card8 sequence) dashes))
- (multiple-value-bind (dashes dash)
- (if (type? dashes 'sequence)
- (if (zerop (length dashes))
- (x-type-error dashes '(or card8 sequence) "non-empty sequence")
- (values nil (or (copy-seq dashes) (vector))))
- (values (encode-type card8 dashes) nil))
- (modify-gcontext (gcontext local-state)
- (let ((server-state (gcontext-server-state gcontext)))
- (declare (type gcontext-state server-state))
- (without-interrupts
- (setf (gcontext-internal-dash local-state) dash
- (gcontext-internal-dashes local-state) dashes)
- (if (null dash)
- (setf (gcontext-internal-dash server-state) nil)
- (setf (gcontext-internal-dashes server-state) nil))))))
- dashes)
-
- (defun gcontext-font (gcontext &optional metrics-p)
- ;; If the stored font is known, it is returned. If it is not known and
- ;; metrics-p is false, then nil is returned. If it is not known and
- ;; metrics-p is true, then a pseudo font is returned. Full metric and
- ;; property information can be obtained, but the font does not have a name or
- ;; a resource-id, and attempts to use it where a resource-id is required will
- ;; result in an invalid-font error.
- (declare (type gcontext gcontext)
- (type boolean metrics-p))
- (declare (values (or null font)))
- (access-gcontext (gcontext local-state)
- (let ((font (gcontext-internal-font-obj local-state)))
- (or font
- (when metrics-p
- ;; XXX this isn't correct
- (make-font :display (gcontext-display gcontext)
- :id (gcontext-id gcontext)
- :name nil))))))
-
- (defsetf gcontext-font set-gcontext-font)
-
- (defun set-gcontext-font (gcontext font)
- (declare (type gcontext gcontext)
- (type fontable font))
- (let* ((font-object (if (font-p font) font (open-font (gcontext-display gcontext) font)))
- (font (and font-object (font-id font-object))))
- ;; XXX need to check font has id (and name?)
- (modify-gcontext (gcontext local-state)
- (let ((server-state (gcontext-server-state gcontext)))
- (declare (type gcontext-state server-state))
- (without-interrupts
- (setf (gcontext-internal-font-obj local-state) font-object
- (gcontext-internal-font local-state) font)
- ;; check against font, not against font-obj
- (if (null font)
- (setf (gcontext-internal-font server-state) nil)
- (setf (gcontext-internal-font-obj server-state) font-object))))))
- font)
-
- (defun force-gcontext-changes-internal (gcontext)
- ;; Force any delayed changes.
- (declare (type gcontext gcontext))
- #.(declare-buffun)
-
- (let ((display (gcontext-display gcontext))
- (server-state (gcontext-server-state gcontext))
- (local-state (gcontext-local-state gcontext)))
- (declare (type display display)
- (type gcontext-state server-state local-state))
-
- ;; Update server when timestamps don't match
- (unless (= (the fixnum (gcontext-internal-timestamp local-state))
- (the fixnum (gcontext-internal-timestamp server-state)))
-
- ;; The display is already locked.
- (macrolet ((with-buffer ((buffer &key timeout) &body body)
- `(progn (progn ,buffer ,@(and timeout `(,timeout)) nil)
- ,@body)))
-
- ;; Because there is no locking on the local state we have to
- ;; assume that state will change and set timestamps up front,
- ;; otherwise by the time we figured out there were no changes
- ;; and tried to store the server stamp as the local stamp, the
- ;; local stamp might have since been modified.
- (setf (gcontext-internal-timestamp local-state)
- (incf-internal-timestamp server-state))
-
- (block no-changes
- (let ((last-request (buffer-last-request display)))
- (with-buffer-request (display *x-changegc*)
- (gcontext gcontext)
- (progn
- (do ((i 0 (index+ i 1))
- (bit 1 (the xgcmask (ash bit 1)))
- (nbyte 12)
- (mask 0)
- (local 0))
- ((index>= i *gcontext-fast-change-length*)
- (when (zerop mask)
- ;; If nothing changed, restore last-request and quit
- (setf (buffer-last-request display)
- (if (zerop (buffer-last-request display))
- nil
- last-request))
- (return-from no-changes nil))
- (card29-put 8 mask)
- (card16-put 2 (index-ash nbyte -2))
- (index-incf (buffer-boffset display) nbyte))
- (declare (type array-index i nbyte)
- (type xgcmask bit)
- (type gcmask mask)
- (type (or null card32) local))
- (unless (eql (the (or null card32) (svref server-state i))
- (setq local (the (or null card32) (svref local-state i))))
- (setf (svref server-state i) local)
- (card32-put nbyte local)
- (setq mask (the gcmask (logior mask bit)))
- (index-incf nbyte 4)))))))
-
- ;; Update GContext extensions
- (do ((extension *gcontext-extensions* (cdr extension))
- (i *gcontext-data-length* (index+ i 1))
- (local))
- ((endp extension))
- (unless (eql (svref server-state i)
- (setq local (svref local-state i)))
- (setf (svref server-state i) local)
- (funcall (gcontext-extension-set-function (car extension)) gcontext local)))
-
- ;; Update clipping rectangles
- (multiple-value-bind (local-clip server-clip)
- (without-interrupts
- (values (gcontext-internal-clip local-state)
- (gcontext-internal-clip server-state)))
- (unless (equalp local-clip server-clip)
- (setf (gcontext-internal-clip server-state) nil)
- (unless (null local-clip)
- (with-buffer-request (display *x-setcliprectangles*)
- (data (first local-clip))
- (gcontext gcontext)
- ;; XXX treat nil correctly
- (card16 (or (gcontext-internal-clip-x local-state) 0)
- (or (gcontext-internal-clip-y local-state) 0))
- ;; XXX this has both int16 and card16 values
- ((sequence :format int16) (second local-clip)))
- (setf (gcontext-internal-clip server-state) local-clip))))
-
- ;; Update dashes
- (multiple-value-bind (local-dash server-dash)
- (without-interrupts
- (values (gcontext-internal-dash local-state)
- (gcontext-internal-dash server-state)))
- (unless (equalp local-dash server-dash)
- (setf (gcontext-internal-dash server-state) nil)
- (unless (null local-dash)
- (with-buffer-request (display *x-setdashes*)
- (gcontext gcontext)
- ;; XXX treat nil correctly
- (card16 (or (gcontext-internal-dash-offset local-state) 0)
- (length local-dash))
- ((sequence :format card8) local-dash))
- (setf (gcontext-internal-dash server-state) local-dash))))))))
-
- (defun force-gcontext-changes (gcontext)
- ;; Force any delayed changes.
- (declare (type gcontext gcontext))
- (let ((display (gcontext-display gcontext))
- (server-state (gcontext-server-state gcontext))
- (local-state (gcontext-local-state gcontext)))
- (declare (type gcontext-state server-state local-state)
- (array-register server-state local-state))
- ;; Update server when timestamps don't match
- (unless (= (the fixnum (gcontext-internal-timestamp local-state))
- (the fixnum (gcontext-internal-timestamp server-state)))
- (with-display (display)
- (force-gcontext-changes-internal gcontext)))))
-
- ;;; WARNING: WITH-GCONTEXT WORKS MUCH MORE EFFICIENTLY WHEN THE OPTIONS BEING "BOUND" ARE
- ;;; SET IN THE GCONTEXT ON ENTRY. BECAUSE THERE'S NO WAY TO GET THE VALUE OF AN
- ;;; UNKNOWN GC COMPONENT, WITH-GCONTEXT MUST CREATE A TEMPORARY GC, COPY THE UNKNOWN
- ;;; COMPONENTS TO THE TEMPORARY GC, ALTER THE GC BEING USED, THEN COPY COMPOMENTS
- ;;; BACK.
-
- (defmacro with-gcontext ((gcontext &rest options &key clip-ordering
- &allow-other-keys)
- &body body)
- ;; "Binds" the gcontext components specified by options within the
- ;; dynamic scope of the body (i.e., indefinite scope and dynamic
- ;; extent), on a per-process basis in a multi-process environment.
- ;; The body is not surrounded by a with-display. If cache-p is nil or
- ;; the some component states are unknown, this will implement
- ;; save/restore by creating a temporary gcontext and doing
- ;; copy-gcontext-components to and from it.
-
- (declare (arglist (gcontext &rest options &key
- function plane-mask foreground background
- line-width line-style cap-style join-style
- fill-style fill-rule arc-mode tile stipple ts-x
- ts-y font subwindow-mode exposures clip-x clip-y
- clip-mask clip-ordering dash-offset dashes
- &allow-other-keys)
- &body body))
- (remf options :clip-ordering)
-
- (let ((gc (gensym))
- (saved-state (gensym))
- (temp-gc (gensym))
- (temp-mask (gensym))
- (temp-vars nil)
- (setfs nil)
- (indexes nil) ; List of gcontext field indices
- (extension-indexes nil) ; List of gcontext extension field indices
- (ts-index (getf *gcontext-indexes* :timestamp)))
-
- (do* ((option options (cddr option))
- (name (car option) (car option))
- (value (cadr option) (cadr option)))
- ((endp option) (setq setfs (nreverse setfs)))
- (let ((index (getf *gcontext-indexes* name)))
- (if index
- (push index indexes)
- (let ((extension (find name *gcontext-extensions*
- :key #'gcontext-extension-name)))
- (if extension
- (progn
- (push (xintern "Internal-" 'gcontext- name "-State-Index")
- extension-indexes))
- (x-type-error name 'gcontext-key)))))
- (let ((accessor `(,(xintern 'gcontext- name) ,gc
- ,@(when (eq name :clip-mask) `(,clip-ordering))))
- (temp-var (gensym)))
- (when value
- (push `(,temp-var ,value) temp-vars)
- (push `(when ,temp-var (setf ,accessor ,temp-var)) setfs))))
- (if setfs
- `(multiple-value-bind (,gc ,saved-state ,temp-mask ,temp-gc)
- (copy-gcontext-local-state ,gcontext ',indexes ,@extension-indexes)
- (declare (type gcontext ,gc)
- (type gcontext-state ,saved-state)
- (type xgcmask ,temp-mask)
- (type (or null resource-id) ,temp-gc))
- (with-gcontext-bindings (,gc ,saved-state
- ,(append indexes extension-indexes)
- ,ts-index ,temp-mask ,temp-gc)
- (let ,temp-vars
- ,@setfs)
- ,@body))
- `(progn ,@body))))
-
- (defun copy-gcontext-local-state (gcontext indexes &rest extension-indices)
- ;; Called from WITH-GCONTEXT to save the fields in GCONTEXT indicated by MASK
- (declare (type gcontext gcontext)
- (type list indexes)
- (dynamic-extent extension-indices))
- (let ((local-state (gcontext-local-state gcontext))
- (saved-state (allocate-gcontext-state))
- (cache-p (gcontext-cache-p gcontext)))
- (declare (type gcontext-state local-state saved-state))
- (setf (gcontext-internal-timestamp saved-state) 1)
- (let ((temp-gc nil)
- (temp-mask 0)
- (extension-mask 0))
- (declare (type xgcmask temp-mask)
- (type integer extension-mask))
- (dolist (i indexes)
- (when (or (not (setf (svref saved-state i) (svref local-state i)))
- (not cache-p))
- (setq temp-mask
- (the xgcmask (logior temp-mask
- (the xgcmask (svref *gcontext-masks* i)))))))
- (dolist (i extension-indices)
- (when (or (not (setf (svref saved-state i) (svref local-state i)))
- (not cache-p))
- (setq extension-mask
- (the xgcmask (logior extension-mask (ash 1 i))))))
- (when (or (plusp temp-mask)
- (plusp extension-mask))
- ;; Copy to temporary GC when field unknown or cache-p false
- (let ((display (gcontext-display gcontext)))
- (declare (type display display))
- (with-display (display)
- (setq temp-gc (allocate-temp-gcontext))
- (setf (gcontext-id temp-gc) (allocate-resource-id display gcontext 'gcontext)
- (gcontext-display temp-gc) display
- (gcontext-drawable temp-gc) (gcontext-drawable gcontext)
- (gcontext-server-state temp-gc) saved-state
- (gcontext-local-state temp-gc) saved-state)
- ;; Create a new (temporary) gcontext
- (with-buffer-request (display *x-creategc*)
- (gcontext temp-gc)
- (drawable (gcontext-drawable gcontext))
- (card29 0))
- ;; Copy changed components to the temporary gcontext
- (when (plusp temp-mask)
- (with-buffer-request (display *x-copygc*)
- (gcontext gcontext)
- (gcontext temp-gc)
- (card29 (xgcmask->gcmask temp-mask))))
- ;; Copy extension fields to the new gcontext
- (when (plusp extension-mask)
- ;; Copy extension fields from temp back to gcontext
- (do ((bit (ash extension-mask (- *gcontext-data-length*)) (ash bit -1))
- (i 0 (index+ i 1)))
- ((zerop bit))
- (let ((copy-function (gcontext-extension-copy-function
- (elt *gcontext-extensions* i))))
- (funcall copy-function gcontext temp-gc
- (svref local-state (index+ i *gcontext-data-length*))))))
- )))
- (values gcontext saved-state (logior temp-mask extension-mask) temp-gc))))
-
- (defun restore-gcontext-temp-state (gcontext temp-mask temp-gc)
- (declare (type gcontext gcontext temp-gc)
- (type xgcmask temp-mask))
- (let ((display (gcontext-display gcontext)))
- (declare (type display display))
- (with-display (display)
- (with-buffer-request (display *x-copygc*)
- (gcontext temp-gc)
- (gcontext gcontext)
- (card29 (xgcmask->gcmask temp-mask)))
- ;; Copy extension fields from temp back to gcontext
- (do ((bit (ash temp-mask (- *gcontext-data-length*)) (ash bit -1))
- (extensions *gcontext-extensions* (cdr extensions))
- (i *gcontext-data-length* (index+ i 1))
- (local-state (gcontext-local-state temp-gc)))
- ((zerop bit))
- (let ((copy-function (gcontext-extension-copy-function (car extensions))))
- (funcall copy-function temp-gc gcontext (svref local-state i))))
- ;; free gcontext
- (with-buffer-request (display *x-freegc*)
- (gcontext temp-gc))
- (deallocate-resource-id display (gcontext-id temp-gc) 'gcontext)
- (deallocate-temp-gcontext temp-gc)
- ;; Copy saved state back to server state
- (do ((server-state (gcontext-server-state gcontext))
- (bit (xgcmask->gcmask temp-mask) (the gcmask (ash bit -1)))
- (i 0 (index+ i 1)))
- ((zerop bit)
- (incf-internal-timestamp server-state))
- (declare (type gcontext-state server-state)
- (type gcmask bit)
- (type array-index i))
- (when (oddp bit)
- (setf (svref server-state i) nil))))))
-
- (defun create-gcontext (&rest options &key (drawable (required-arg drawable))
- function plane-mask foreground background
- line-width line-style cap-style join-style fill-style fill-rule
- arc-mode tile stipple ts-x ts-y font subwindow-mode
- exposures clip-x clip-y clip-mask clip-ordering
- dash-offset dashes
- (cache-p t)
- &allow-other-keys)
- ;; Only non-nil components are passed on in the request, but for effective caching
- ;; assumptions have to be made about what the actual protocol defaults are. For
- ;; all gcontext components, a value of nil causes the default gcontext value to be
- ;; used. For clip-mask, this implies that an empty rect-seq cannot be represented
- ;; as a list. Note: use of stringable as font will cause an implicit open-font.
- ;; Note: papers over protocol SetClipRectangles and SetDashes special cases. If
- ;; cache-p is true, then gcontext state is cached locally, and changing a gcontext
- ;; component will have no effect unless the new value differs from the cached
- ;; value. Component changes (setfs and with-gcontext) are always deferred
- ;; regardless of the cache mode, and sent over the protocol only when required by a
- ;; local operation or by an explicit call to force-gcontext-changes.
- (declare (type drawable drawable) ; Required to be non-null
- (type (or null boole-constant) function)
- (type (or null pixel) plane-mask foreground background)
- (type (or null card16) line-width dash-offset)
- (type (or null int16) ts-x ts-y clip-x clip-y)
- (type (or null (member :solid :dash :double-dash)) line-style)
- (type (or null (member :not-last :butt :round :projecting)) cap-style)
- (type (or null (member :miter :round :bevel)) join-style)
- (type (or null (member :solid :tiled :opaque-stippled :stippled)) fill-style)
- (type (or null (member :even-odd :winding)) fill-rule)
- (type (or null (member :chord :pie-slice)) arc-mode)
- (type (or null pixmap) tile stipple)
- (type (or null fontable) font)
- (type (or null (member :clip-by-children :include-inferiors)) subwindow-mode)
- (type (or null (member :on :off)) exposures)
- (type (or null (member :none) pixmap rect-seq) clip-mask)
- (type (or null (member :unsorted :y-sorted :yx-sorted :yx-banded)) clip-ordering)
- (type (or null card8 sequence) dashes)
- (dynamic-extent options)
- (type boolean cache-p))
- (declare (values gcontext))
- (let* ((display (drawable-display drawable))
- (gcontext (make-gcontext :display display :drawable drawable :cache-p cache-p))
- (local-state (gcontext-local-state gcontext))
- (server-state (gcontext-server-state gcontext))
- (gcontextid (allocate-resource-id display gcontext 'gcontext)))
- (declare (type display display)
- (type gcontext gcontext)
- (type resource-id gcontextid)
- (type gcontext-state local-state server-state))
- (setf (gcontext-id gcontext) gcontextid)
-
- (unless function (setf (gcontext-function gcontext) boole-1))
- ;; using the depth of the drawable would be better, but ...
- (unless plane-mask (setf (gcontext-plane-mask gcontext) #xffffffff))
- (unless foreground (setf (gcontext-foreground gcontext) 0))
- (unless background (setf (gcontext-background gcontext) 1))
- (unless line-width (setf (gcontext-line-width gcontext) 0))
- (unless line-style (setf (gcontext-line-style gcontext) :solid))
- (unless cap-style (setf (gcontext-cap-style gcontext) :butt))
- (unless join-style (setf (gcontext-join-style gcontext) :miter))
- (unless fill-style (setf (gcontext-fill-style gcontext) :solid))
- (unless fill-rule (setf (gcontext-fill-rule gcontext) :even-odd))
- (unless arc-mode (setf (gcontext-arc-mode gcontext) :pie-slice))
- (unless ts-x (setf (gcontext-ts-x gcontext) 0))
- (unless ts-y (setf (gcontext-ts-y gcontext) 0))
- (unless subwindow-mode (setf (gcontext-subwindow-mode gcontext)
- :clip-by-children))
- (unless exposures (setf (gcontext-exposures gcontext) :on))
- (unless clip-mask (setf (gcontext-clip-mask gcontext) :none))
- (unless clip-x (setf (gcontext-clip-x gcontext) 0))
- (unless clip-y (setf (gcontext-clip-y gcontext) 0))
- (unless dashes (setf (gcontext-dashes gcontext) 4))
- (unless dash-offset (setf (gcontext-dash-offset gcontext) 0))
- ;; a bit kludgy, but ...
- (replace server-state local-state)
-
- (when function (setf (gcontext-function gcontext) function))
- (when plane-mask (setf (gcontext-plane-mask gcontext) plane-mask))
- (when foreground (setf (gcontext-foreground gcontext) foreground))
- (when background (setf (gcontext-background gcontext) background))
- (when line-width (setf (gcontext-line-width gcontext) line-width))
- (when line-style (setf (gcontext-line-style gcontext) line-style))
- (when cap-style (setf (gcontext-cap-style gcontext) cap-style))
- (when join-style (setf (gcontext-join-style gcontext) join-style))
- (when fill-style (setf (gcontext-fill-style gcontext) fill-style))
- (when fill-rule (setf (gcontext-fill-rule gcontext) fill-rule))
- (when arc-mode (setf (gcontext-arc-mode gcontext) arc-mode))
- (when tile (setf (gcontext-tile gcontext) tile))
- (when stipple (setf (gcontext-stipple gcontext) stipple))
- (when ts-x (setf (gcontext-ts-x gcontext) ts-x))
- (when ts-y (setf (gcontext-ts-y gcontext) ts-y))
- (when font (setf (gcontext-font gcontext) font))
- (when subwindow-mode (setf (gcontext-subwindow-mode gcontext) subwindow-mode))
- (when exposures (setf (gcontext-exposures gcontext) exposures))
- (when clip-x (setf (gcontext-clip-x gcontext) clip-x))
- (when clip-y (setf (gcontext-clip-y gcontext) clip-y))
- (when clip-mask (setf (gcontext-clip-mask gcontext clip-ordering) clip-mask))
- (when dash-offset (setf (gcontext-dash-offset gcontext) dash-offset))
- (when dashes (setf (gcontext-dashes gcontext) dashes))
-
- (setf (gcontext-internal-timestamp server-state) 1)
- (setf (gcontext-internal-timestamp local-state)
- ;; SetClipRectangles or SetDashes request need to be sent?
- (if (or (gcontext-internal-clip local-state)
- (gcontext-internal-dash local-state))
- ;; Yes, mark local state "modified" to ensure
- ;; force-gcontext-changes will occur.
- 0
- ;; No, mark local state "unmodified"
- 1))
-
- (with-buffer-request (display *x-creategc*)
- (resource-id gcontextid)
- (drawable drawable)
- (progn (do* ((i 0 (index+ i 1))
- (bit 1 (the xgcmask (ash bit 1)))
- (nbyte 16)
- (mask 0)
- (local (svref local-state i) (svref local-state i)))
- ((index>= i *gcontext-fast-change-length*)
- (card29-put 12 mask)
- (card16-put 2 (index-ash nbyte -2))
- (index-incf (buffer-boffset display) nbyte))
- (declare (type array-index i nbyte)
- (type xgcmask bit)
- (type gcmask mask)
- (type (or null card32) local))
- (unless (eql local (the (or null card32) (svref server-state i)))
- (setf (svref server-state i) local)
- (card32-put nbyte local)
- (setq mask (the gcmask (logior mask bit)))
- (index-incf nbyte 4)))))
-
- ;; Initialize extensions
- (do ((extensions *gcontext-extensions* (cdr extensions))
- (i *gcontext-data-length* (index+ i 1)))
- ((endp extensions))
- (declare (type list extensions)
- (type array-index i))
- (setf (svref server-state i)
- (setf (svref local-state i)
- (gcontext-extension-default (car extensions)))))
-
- ;; Set extension values
- (do* ((option-list options (cddr option-list))
- (option (car option-list) (car option-list))
- (extension))
- ((endp option-list))
- (declare (type list option-list))
- (cond ((getf *gcontext-indexes* option)) ; Gcontext field
- ((member option '(:drawable :clip-ordering :cache-p))) ; Optional parameter
- ((setq extension (find option *gcontext-extensions*
- :key #'gcontext-extension-name))
- (funcall (gcontext-extension-set-function extension)
- gcontext (second option-list)))
- (t (x-type-error option 'gcontext-key))))
- gcontext))
-
- (defun copy-gcontext-components (src dst &rest keys)
- (declare (type gcontext src dst)
- (dynamic-extent keys))
- ;; you might ask why this isn't just a bunch of
- ;; (setf (gcontext-<mumble> dst) (gcontext-<mumble> src))
- ;; the answer is that you can do that yourself if you want, what we are
- ;; providing here is access to the protocol request, which will generally
- ;; be more efficient (particularly for things like clip and dash lists).
- (when keys
- (let ((display (gcontext-display src))
- (mask 0))
- (declare (type xgcmask mask))
- (with-display (display)
- (force-gcontext-changes-internal src)
- (force-gcontext-changes-internal dst)
-
- ;; collect entire mask and handle extensions
- (dolist (key keys)
- (let ((i (getf *gcontext-indexes* key)))
- (declare (type (or null array-index) i))
- (if i
- (setq mask (the xgcmask (logior mask
- (the xgcmask (svref *gcontext-masks* i)))))
- (multiple-value-bind (extension index)
- (find key *gcontext-extensions* :key #'gcontext-extension-name)
- (if extension
- (funcall (gcontext-extension-copy-function extension)
- src dst (svref (gcontext-local-state src)
- (index+ index *gcontext-data-length*)))
- (x-type-error key 'gcontext-key))))))
-
- (when (plusp mask)
- (do ((src-server-state (gcontext-server-state src))
- (dst-server-state (gcontext-server-state dst))
- (dst-local-state (gcontext-local-state dst))
- (bit mask (the xgcmask (ash bit -1)))
- (i 0 (index+ i 1)))
- ((zerop bit)
- (incf-internal-timestamp dst-server-state)
- (setf (gcontext-internal-timestamp dst-local-state) 0))
- (declare (type gcontext-state src-server-state dst-server-state dst-local-state)
- (type xgcmask bit)
- (type array-index i))
- (when (oddp bit)
- (setf (svref dst-local-state i)
- (setf (svref dst-server-state i) (svref src-server-state i)))))
- (with-buffer-request (display *x-copygc*)
- (gcontext src dst)
- (card29 (xgcmask->gcmask mask))))))))
-
- (defun copy-gcontext (src dst)
- (declare (type gcontext src dst))
- ;; Copies all components.
- (apply #'copy-gcontext-components src dst *gcontext-components*)
- (do ((extensions *gcontext-extensions* (cdr extensions))
- (i *gcontext-data-length* (index+ i 1)))
- ((endp extensions))
- (funcall (gcontext-extension-copy-function (car extensions))
- src dst (svref (gcontext-local-state src) i))))
-
- (defun free-gcontext (gcontext)
- (declare (type gcontext gcontext))
- (let ((display (gcontext-display gcontext)))
- (with-buffer-request (display *x-freegc*)
- (gcontext gcontext))
- (deallocate-resource-id display (gcontext-id gcontext) 'gcontext)
- (deallocate-gcontext-state (gcontext-server-state gcontext))
- (deallocate-gcontext-state (gcontext-local-state gcontext))
- nil))
-
- (defmacro define-gcontext-accessor (name &key default set-function copy-function)
- ;; This will define a new gcontext accessor called NAME.
- ;; Defines the gcontext-NAME accessor function and its defsetf.
- ;; Gcontext's will cache DEFAULT-VALUE and the last value SETF'ed when
- ;; gcontext-cache-p is true. The NAME keyword will be allowed in
- ;; CREATE-GCONTEXT, WITH-GCONTEXT, and COPY-GCONTEXT-COMPONENTS.
- ;; SET-FUNCTION will be called with parameters (GCONTEXT NEW-VALUE)
- ;; from create-gcontext, and force-gcontext-changes.
- ;; COPY-FUNCTION will be called with parameters (src-gc dst-gc src-value)
- ;; from copy-gcontext and copy-gcontext-components.
- ;; The copy-function defaults to:
- ;; (lambda (ignore dst-gc value)
- ;; (if value
- ;; (,set-function dst-gc value)
- ;; (error "Can't copy unknown GContext component ~a" ',name)))
- (declare (type symbol name)
- (type t default)
- (type (function (gcontext t) t) set-function) ;; required
- (type (or null (function (gcontext gcontext t) t))
- copy-function))
- (let* ((gc-name (intern (concatenate 'string
- (string 'gcontext-)
- (string name)))) ;; in current package
- (key-name (kintern name))
- (setfer (xintern "Set-" gc-name))
- (internal-set-function (xintern "Internal-Set-" gc-name))
- (internal-copy-function (xintern "Internal-Copy-" gc-name))
- (internal-state-index (xintern "Internal-" gc-name "-State-Index")))
- (unless copy-function
- (setq copy-function
- `(lambda (src-gc dst-gc value)
- (declare (ignore src-gc))
- (if value
- (,set-function dst-gc value)
- (error "Can't copy unknown GContext component ~a" ',name)))))
- `(progn
- (eval-when (compile load eval)
- (defparameter ,internal-state-index
- (add-gcontext-extension ',key-name ,default ',internal-set-function
- ',internal-copy-function))
- ) ;; end eval-when
- (defun ,gc-name (gcontext)
- (svref (gcontext-local-state gcontext) ,internal-state-index))
- (defun ,setfer (gcontext new-value)
- (let ((local-state (gcontext-local-state gcontext)))
- (setf (gcontext-internal-timestamp local-state) 0)
- (setf (svref local-state ,internal-state-index) new-value)))
- (defsetf ,gc-name ,setfer)
- (defun ,internal-set-function (gcontext new-value)
- (,set-function gcontext new-value)
- (setf (svref (gcontext-server-state gcontext) ,internal-state-index)
- (setf (svref (gcontext-local-state gcontext) ,internal-state-index)
- new-value)))
- (defun ,internal-copy-function (src-gc dst-gc new-value)
- (,copy-function src-gc dst-gc new-value)
- (setf (svref (gcontext-local-state dst-gc) ,internal-state-index)
- (setf (svref (gcontext-server-state dst-gc) ,internal-state-index)
- new-value)))
- ',name)))
-
- ;; GContext extension fields are treated in much the same way as normal GContext
- ;; components. The current value is stored in a slot of the gcontext-local-state,
- ;; and the value known to the server is in a slot of the gcontext-server-state.
- ;; The slot-number is defined by its position in the *gcontext-extensions* list.
- ;; The value of the special variable |Internal-GCONTEXT-name| (where "name" is
- ;; the extension component name) reflects this position. The position within
- ;; *gcontext-extensions* and the value of the special value are determined at
- ;; LOAD time to facilitate merging of seperately compiled extension files.
-
- (defun add-gcontext-extension (name default-value set-function copy-function)
- (declare (type symbol name)
- (type t default-value)
- (type (function (gcontext t) t) set-function)
- (type (function (gcontext gcontext t) t) copy-function))
- (let ((number (or (position name *gcontext-extensions* :key #'gcontext-extension-name)
- (prog1 (length *gcontext-extensions*)
- (push nil *gcontext-extensions*)))))
- (setf (nth number *gcontext-extensions*)
- (make-gcontext-extension :name name
- :default default-value
- :set-function set-function
- :copy-function copy-function))
- (+ number *gcontext-data-length*)))
-