;;; -*- Mode: LISP; Syntax: Common-lisp; Package: XLIB; Base: 10; Lowercase: Yes -*- ;;; This file contains definitions for the DISPLAY object for Common-Lisp X windows version 11 ;;; ;;; 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. ;;; ;;; ;;; Change history: ;;; ;;; Date Author Description ;;; ------------------------------------------------------------------------------------- ;;; 12/10/87 LGO Created (in-package :xlib) ;; Event Resource (defvar *event-free-list* nil) ;; List of unused (processed) events (eval-when (eval compile load) (defconstant *max-events* 64) ;; Maximum number of events supported (the X11 alpha release only has 34) (defvar *event-key-vector* (make-array *max-events* :initial-element nil) "Vector of event keys - See define-event") ) (defvar *event-macro-vector* (make-array *max-events* :initial-element nil) "Vector of event handler functions - See declare-event") (defvar *event-handler-vector* (make-array *max-events* :initial-element nil) "Vector of event handler functions - See declare-event") (defvar *event-send-vector* (make-array *max-events* :initial-element nil) "Vector of event sending functions - See declare-event") (defun allocate-event () (or (threaded-atomic-pop *event-free-list* reply-next reply-buffer) (make-reply-buffer *replysize*))) (defun deallocate-event (reply-buffer) (declare (type reply-buffer reply-buffer)) (setf (reply-size reply-buffer) *replysize*) (threaded-atomic-push reply-buffer *event-free-list* reply-next reply-buffer)) ;; Extensions are handled as follows: ;; DEFINITION: Use DEFINE-EXTENSION ;; ;; CODE: Use EXTENSION-CODE to get the X11 opcode for an extension. ;; This looks up the code on the display-extension-alist. ;; ;; EVENTS: Use DECLARE-EVENT to define events. This calls ALLOCATE-EXTENSION-EVENT-CODE ;; at LOAD time to define an internal event-code number ;; (stored in the 'event-code property of the event-name) ;; used to index the following vectors: ;; *event-key-vector* Used for getting the event-key ;; *event-macro-vector* Used for getting the event-parameter getting macros ;; ;; The GET-INTERNAL-EVENT-CODE function can be called at runtime to convert ;; a server event-code into an internal event-code used to index the following ;; vectors: ;; *event-handler-vector* Used for getting the event-handler function ;; *event-send-vector* Used for getting the event-sending function ;; ;; The GET-EXTERNAL-EVENT-CODE function can be called at runtime to convert ;; internal event-codes to external (server) codes. ;; ;; ERRORS: Use DEFINE-ERROR to define new error decodings. ;; ;; Any event-code greater than 34 is for an extension (defparameter *first-extension-event-code* 35) (defvar *extensions* nil) ;; alist of (extension-name-symbol events errors) (defmacro define-extension (name &key events errors) ;; Define extension NAME with EVENTS and ERRORS. ;; Note: The case of NAME is important. ;; To define the request, Use: ;; (with-buffer-request (display (extension-opcode ,name)) ,@body) ;; See the REQUESTS file for lots of examples. ;; To define event handlers, use declare-event. ;; To define error handlers, use declare-error and define-condition. (declare (type stringable name) (type list events errors)) (let ((name-symbol (kintern name)) ;; Intern name in the keyword package (event-list (mapcar #'canonicalize-event-name events))) `(eval-when (compile load eval) (setq *extensions* (cons (list ',name-symbol ',event-list ',errors) (delete ',name-symbol *extensions* :key #'car)))))) (eval-when (compile eval load) (defun canonicalize-event-name (event) ;; Returns the event name keyword given an event name stringable (declare (type stringable event)) (declare (values event-key)) (kintern event)) ) ;; end eval-when (eval-when (compile eval load) (defun allocate-extension-event-code (name) ;; Allocate an event-code for an extension ;; This is executed at COMPILE and LOAD time from DECLARE-EVENT. ;; The event-code is used at compile-time by macros to index the following vectors: ;; *event-key-vector* *event-macro-vector* *event-handler-vector* *event-send-vector* (let ((event-code (get name 'event-code))) (declare (type (or null card8) event-code)) (unless event-code ;; First ensure the name is for a declared extension (unless (dolist (extension *extensions*) (when (member name (second extension)) (return t))) (x-type-error name 'event-key)) (setq event-code (position nil *event-key-vector* :start *first-extension-event-code*)) (setf (svref *event-key-vector* event-code) name) (setf (get name 'event-code) event-code)) event-code)) ) ;; end eval-when (defun get-internal-event-code (display code) ;; Given an X11 event-code, return the internal event-code. ;; The internal event-code is used for indexing into the following vectors: ;; *event-key-vector* *event-handler-vector* *event-send-vector* ;; Returns NIL when the event-code is for an extension that isn't handled. (declare (type display display) (type card8 code)) (declare (values (or null card8))) (setq code (logand #x7f code)) (if (< code *first-extension-event-code*) code (let* ((code-offset (- code *first-extension-event-code*)) (event-extensions (display-event-extensions display)) (code (if (< code-offset (length event-extensions)) (aref event-extensions code-offset) 0))) (declare (type card8 code-offset code)) (when (zerop code) (x-cerror "Ignore the event" 'unimplemented-event :event-code code :display display)) code))) (defun get-external-event-code (display event) ;; Given an X11 event name, return the event-code (declare (type display display) (type event-key event)) (declare (values card8)) (let ((code (get-event-code event))) (declare (type (or null card8) code)) (when (>= code *first-extension-event-code*) (setq code (+ *first-extension-event-code* (or (position code (display-event-extensions display)) (x-error 'undefined-event :display display :event-name event))))) code)) (defmacro extension-opcode (display name) ;; Returns the major opcode for extension NAME. ;; This is a macro to enable NAME to be interned for fast run-time ;; retrieval. ;; Note: The case of NAME is important. (let ((name-symbol (kintern name))) ;; Intern name in the keyword package `(or (second (assoc ',name-symbol (display-extension-alist ,display))) (x-error 'absent-extension :name ',name-symbol :display ,display)))) (defun initialize-extensions (display) ;; Initialize extensions for DISPLAY (let ((event-extensions (make-array 16 :element-type 'card8 :initial-element 0)) (extension-alist nil)) (declare (type vector event-extensions) (type list extension-alist)) (dolist (extension *extensions*) (let ((name (first extension)) (events (second extension))) (declare (type keyword name) (type list events)) (multiple-value-bind (major-opcode first-event first-error) (query-extension display name) (declare (type (or null card8) major-opcode first-event first-error)) (when (and major-opcode (plusp major-opcode)) (push (list name major-opcode first-event first-error) extension-alist) (when (plusp first-event) ;; When there are extension events ;; Grow extension vector when needed (let ((max-event (- (+ first-event (length events)) *first-extension-event-code*))) (declare (type card8 max-event)) (when (>= max-event (length event-extensions)) (let ((new-extensions (make-array (+ max-event 16) :element-type 'card8 :initial-element 0))) (declare (type vector new-extensions)) (replace new-extensions event-extensions) (setq event-extensions new-extensions)))) (dolist (event events) (declare (type symbol event)) (setf (aref event-extensions (- first-event *first-extension-event-code*)) (get-event-code event)) (incf first-event))))))) (setf (display-event-extensions display) event-extensions) (setf (display-extension-alist display) extension-alist))) ;; ;; Reply handlers ;; (defvar *pending-command-free-list* nil) (defun start-pending-command (display) (declare (type display display)) (let ((pending-command (or (threaded-atomic-pop *pending-command-free-list* pending-command-next pending-command) (make-pending-command)))) (declare (type pending-command pending-command)) (setf (pending-command-reply-buffer pending-command) nil) (setf (pending-command-process pending-command) (current-process)) (setf (pending-command-sequence pending-command) (ldb (byte 16 0) (1+ (buffer-request-number display)))) ;; Add the pending command to the end of the threaded list of pending ;; commands for the display. (with-event-queue-internal (display) (threaded-nconc pending-command (display-pending-commands display) pending-command-next pending-command)) pending-command)) (defun stop-pending-command (display pending-command) (declare (type display display) (type pending-command pending-command)) (with-event-queue-internal (display) ;; Remove the pending command from the threaded list of pending commands ;; for the display. (threaded-delete pending-command (display-pending-commands display) pending-command-next pending-command) ;; Deallocate any reply buffers in this pending command (loop (let ((reply-buffer (threaded-pop (pending-command-reply-buffer pending-command) reply-next reply-buffer))) (declare (type (or null reply-buffer) reply-buffer)) (if reply-buffer (deallocate-reply-buffer reply-buffer) (return nil))))) ;; Clear pointers to help the Garbage Collector (setf (pending-command-process pending-command) nil) ;; Deallocate this pending-command (threaded-atomic-push pending-command *pending-command-free-list* pending-command-next pending-command) nil) ;;; (defvar *reply-buffer-free-lists* (make-array 32 :initial-element nil)) (defun allocate-reply-buffer (size) (declare (type array-index size)) (if (index<= size *replysize*) (allocate-event) (let ((index (integer-length (index1- size)))) (declare (type array-index index)) (or (threaded-atomic-pop (svref *reply-buffer-free-lists* index) reply-next reply-buffer) (make-reply-buffer (index-ash 1 index)))))) (defun deallocate-reply-buffer (reply-buffer) (declare (type reply-buffer reply-buffer)) (let ((size (reply-size reply-buffer))) (declare (type array-index size)) (if (index<= size *replysize*) (deallocate-event reply-buffer) (let ((index (integer-length (index1- size)))) (declare (type array-index index)) (threaded-atomic-push reply-buffer (svref *reply-buffer-free-lists* index) reply-next reply-buffer))))) ;;; (defun read-error-input (display sequence reply-buffer token) (declare (type display display) (type reply-buffer reply-buffer) (type card16 sequence)) (tagbody start (with-event-queue-internal (display) (let ((command ;; Find any pending command with this sequence number. (threaded-dolist (pending-command (display-pending-commands display) pending-command-next pending-command) (when (= (pending-command-sequence pending-command) sequence) (return pending-command))))) (declare (type (or null pending-command) command)) (cond ((not (null command)) ;; Give this reply to the pending command (threaded-nconc reply-buffer (pending-command-reply-buffer command) reply-next reply-buffer) (process-wakeup (pending-command-process command))) ((member :immediately (display-report-asynchronous-errors display)) ;; No pending command and we should report the error immediately (go report-error)) (t ;; No pending command found, count this as an asynchronous error (threaded-nconc reply-buffer (display-asynchronous-errors display) reply-next reply-buffer))))) (return-from read-error-input nil) report-error (note-input-complete display token) (apply #'report-error display (prog1 (make-error display reply-buffer t) (deallocate-event reply-buffer))))) (defun read-reply-input (display sequence length reply-buffer) (declare (type display display) (type (or null reply-buffer) reply-buffer) (type card16 sequence) (type array-index length)) (unwind-protect (progn (when (index< *replysize* length) (let ((repbuf nil)) (declare (type (or null reply-buffer) repbuf)) (unwind-protect (progn (setq repbuf (allocate-reply-buffer length)) (buffer-replace (reply-ibuf8 repbuf) (reply-ibuf8 reply-buffer) 0 *replysize*) (deallocate-event (shiftf reply-buffer repbuf nil))) (when repbuf (deallocate-reply-buffer repbuf)))) (when (buffer-input display (reply-ibuf8 reply-buffer) *replysize* length) (return-from read-reply-input t)) (setf (reply-data-size reply-buffer) length)) (with-event-queue-internal (display) ;; Find any pending command with this sequence number. (let ((command (threaded-dolist (pending-command (display-pending-commands display) pending-command-next pending-command) (when (= (pending-command-sequence pending-command) sequence) (return pending-command))))) (declare (type (or null pending-command) command)) (when command ;; Give this reply to the pending command (threaded-nconc (shiftf reply-buffer nil) (pending-command-reply-buffer command) reply-next reply-buffer) (process-wakeup (pending-command-process command))))) nil) (when reply-buffer (deallocate-reply-buffer reply-buffer)))) (defun read-event-input (display code reply-buffer) (declare (type display display) (type card8 code) (type reply-buffer reply-buffer)) ;; Push the event in the input buffer on the display's event queue (setf (event-code reply-buffer) (get-internal-event-code display code)) (enqueue-event reply-buffer display) nil) (defun note-input-complete (display token) (declare (type display display)) (when (eq (display-input-in-progress display) token) ;; Indicate that input is no longer in progress (setf (display-input-in-progress display) nil) ;; Let the event process get the first chance to do input (let ((process (display-event-process display))) (when (not (null process)) (process-wakeup process))) ;; Then give processes waiting for command responses a chance (unless (display-input-in-progress display) (with-event-queue-internal (display) (threaded-dolist (command (display-pending-commands display) pending-command-next pending-command) (process-wakeup (pending-command-process command))))))) (defun read-input (display timeout force-output-p predicate &rest predicate-args) (declare (type display display) (type (or null number) timeout) (type boolean force-output-p) (dynamic-extent predicate-args)) (declare (type function predicate) #+clx-ansi-common-lisp (dynamic-extent predicate) #+(and lispm (not clx-ansi-common-lisp)) (sys:downward-funarg predicate)) (let ((reply-buffer nil) (token (or (current-process) (cons nil nil)))) (declare (type (or null reply-buffer) reply-buffer)) (unwind-protect (tagbody loop (when (display-dead display) (x-error 'closed-display :display display)) (when (apply predicate predicate-args) (return-from read-input nil)) ;; Check and see if we have to force output (when (and force-output-p (or (and (not (eq (display-input-in-progress display) token)) (not (conditional-store (display-input-in-progress display) nil token))) (null (buffer-listen display)))) (go force-output)) ;; Ensure that ony one process is reading input. (unless (or (eq (display-input-in-progress display) token) (conditional-store (display-input-in-progress display) nil token)) (if (eql timeout 0) (return-from read-input :timeout) (apply #'process-block "CLX Input Lock" #'(lambda (display predicate &rest predicate-args) (declare (type display display) (dynamic-extent predicate-args) (type function predicate) #+clx-ansi-common-lisp (dynamic-extent predicate) #+(and lispm (not clx-ansi-common-lisp)) (sys:downward-funarg predicate)) (or (apply predicate predicate-args) (null (display-input-in-progress display)) (not (null (display-dead display))))) display predicate predicate-args)) (go loop)) ;; Now start gobbling. (setq reply-buffer (allocate-event)) (with-buffer-input (reply-buffer :sizes (8 16 32)) (let ((type 0)) (declare (type card8 type)) ;; Wait for input before we disallow aborts. (unless (eql timeout 0) (let ((eof-p (buffer-input-wait display timeout))) (when eof-p (return-from read-input eof-p)))) (without-aborts (let ((eof-p (buffer-input display buffer-bbuf 0 *replysize* (if force-output-p 0 timeout)))) (when eof-p (when (eq eof-p :timeout) (if force-output-p (go force-output) (return-from read-input :timeout))) (setf (display-dead display) t) (return-from read-input eof-p))) (setf (reply-data-size reply-buffer) *replysize*) (when (= (the card8 (setq type (read-card8 0))) 1) ;; Normal replies can be longer than *replysize*, so we ;; have to handle them while aborts are still disallowed. (let ((value (read-reply-input display (read-card16 2) (index+ *replysize* (index* (read-card32 4) 4)) (shiftf reply-buffer nil)))) (when value (return-from read-input value)) (go loop)))) (if (zerop type) (read-error-input display (read-card16 2) (shiftf reply-buffer nil) token) (read-event-input display (read-card8 0) (shiftf reply-buffer nil))))) (go loop) force-output (note-input-complete display token) (display-force-output display) (setq force-output-p nil) (go loop)) (when (not (null reply-buffer)) (deallocate-reply-buffer reply-buffer)) (note-input-complete display token)))) (defun report-asynchronous-errors (display mode) (when (and (display-asynchronous-errors display) (member mode (display-report-asynchronous-errors display))) (let ((aborted t)) (unwind-protect (loop (let ((error (with-event-queue-internal (display) (threaded-pop (display-asynchronous-errors display) reply-next reply-buffer)))) (declare (type (or null reply-buffer) error)) (if error (apply #'report-error display (prog1 (make-error display error t) (deallocate-event error))) (return (setq aborted nil))))) ;; If we get aborted out of this, deallocate all outstanding asynchronous ;; errors. (when aborted (with-event-queue-internal (display) (loop (let ((reply-buffer (threaded-pop (display-asynchronous-errors display) reply-next reply-buffer))) (declare (type (or null reply-buffer) reply-buffer)) (if reply-buffer (deallocate-event reply-buffer) (return nil)))))))))) (defun wait-for-event (display timeout force-output-p) (declare (type display display) (type (or null number) timeout) (type boolean force-output-p)) (let ((event-process-p (not (eql timeout 0)))) (declare (type boolean event-process-p)) (unwind-protect (loop (when event-process-p (conditional-store (display-event-process display) nil (current-process))) (let ((eof (read-input display timeout force-output-p #'(lambda (display) (declare (type display display)) (or (not (null (display-new-events display))) (and (display-asynchronous-errors display) (member :before-event-handling (display-report-asynchronous-errors display)) t))) display))) (when eof (return eof))) ;; Report asynchronous errors here if the user wants us to. (when event-process-p (report-asynchronous-errors display :before-event-handling)) (when (not (null (display-new-events display))) (return nil))) (when (and event-process-p (eq (display-event-process display) (current-process))) (setf (display-event-process display) nil))))) (defun read-reply (display pending-command) (declare (type display display) (type pending-command pending-command)) (loop (when (read-input display nil nil #'(lambda (pending-command) (declare (type pending-command pending-command)) (not (null (pending-command-reply-buffer pending-command)))) pending-command) (x-error 'closed-display :display display)) (let ((reply-buffer (with-event-queue-internal (display) (threaded-pop (pending-command-reply-buffer pending-command) reply-next reply-buffer)))) (declare (type reply-buffer reply-buffer)) ;; Check for error. (with-buffer-input (reply-buffer) (ecase (read-card8 0) (0 (apply #'report-error display (prog1 (make-error display reply-buffer nil) (deallocate-reply-buffer reply-buffer)))) (1 (return reply-buffer))))))) ;;; (defun event-listen (display &optional (timeout 0)) (declare (type display display) (type (or null number) timeout) (values number-of-events-queued eof-or-timeout)) ;; Returns the number of events queued locally, if any, else nil. Hangs ;; waiting for events, forever if timeout is nil, else for the specified ;; number of seconds. (let* ((current-event-symbol (car (display-current-event-symbol display))) (current-event (and (boundp current-event-symbol) (symbol-value current-event-symbol))) (queue (if current-event (reply-next (the reply-buffer current-event)) (display-event-queue-head display)))) (declare (type symbol current-event-symbol) (type (or null reply-buffer) current-event queue)) (if queue (values (with-event-queue-internal (display :timeout timeout) (threaded-length queue reply-next reply-buffer)) nil) (with-event-queue (display :timeout timeout :inline t) (let ((eof-or-timeout (wait-for-event display timeout nil))) (if eof-or-timeout (values nil eof-or-timeout) (values (with-event-queue-internal (display :timeout timeout) (threaded-length (display-new-events display) reply-next reply-buffer)) nil))))))) (defun queue-event (display event-key &rest args &key append-p send-event-p &allow-other-keys) ;; The event is put at the head of the queue if append-p is nil, else the tail. ;; Additional arguments depend on event-key, and are as specified above with ;; declare-event, except that both resource-ids and resource objects are accepted ;; in the event components. (declare (type display display) (type event-key event-key) (type boolean append-p send-event-p) (dynamic-extent args)) (unless (get event-key 'event-code) (x-type-error event-key 'event-key)) (let* ((event (allocate-event)) (buffer (reply-ibuf8 event)) (event-code (get event-key 'event-code))) (declare (type reply-buffer event) (type buffer-bytes buffer) (type (or null card8) event-code)) (unless event-code (x-type-error event-key 'event-key)) (setf (event-code event) event-code) (with-display (display) (apply (svref *event-send-vector* event-code) display args) (buffer-replace buffer (display-obuf8 display) 0 *replysize* (index+ 12 (buffer-boffset display))) (setf (aref buffer 0) (if send-event-p (logior event-code #x80) event-code) (aref buffer 2) 0 (aref buffer 3) 0)) (with-event-queue (display) (if append-p (enqueue-event event display) (with-event-queue-internal (display) (threaded-requeue event (display-event-queue-head display) (display-event-queue-tail display) reply-next reply-buffer)))))) (defun enqueue-event (new-event display) (declare (type reply-buffer new-event) (type display display)) ;; Place EVENT at the end of the event queue for DISPLAY (let* ((event-code (event-code new-event)) (event-key (and (index< event-code (length *event-key-vector*)) (svref *event-key-vector* event-code)))) (declare (type array-index event-code) (type (or null keyword) event-key)) (if (null event-key) (unwind-protect (cerror "Ignore this event" "No handler for ~s event" event-key) (deallocate-event new-event)) (with-event-queue-internal (display) (threaded-enqueue new-event (display-event-queue-head display) (display-event-queue-tail display) reply-next reply-buffer) (unless (display-new-events display) (setf (display-new-events display) new-event)))))) (defmacro define-event (name code) `(eval-when (eval compile load) (setf (svref *event-key-vector* ,code) ',name) (setf (get ',name 'event-code) ,code))) ;; Event names. Used in "type" field in XEvent structures. Not to be ;; confused with event masks above. They start from 2 because 0 and 1 ;; are reserved in the protocol for errors and replies. */ (define-event :key-press 2) (define-event :key-release 3) (define-event :button-press 4) (define-event :button-release 5) (define-event :motion-notify 6) (define-event :enter-notify 7) (define-event :leave-notify 8) (define-event :focus-in 9) (define-event :focus-out 10) (define-event :keymap-notify 11) (define-event :exposure 12) (define-event :graphics-exposure 13) (define-event :no-exposure 14) (define-event :visibility-notify 15) (define-event :create-notify 16) (define-event :destroy-notify 17) (define-event :unmap-notify 18) (define-event :map-notify 19) (define-event :map-request 20) (define-event :reparent-notify 21) (define-event :configure-notify 22) (define-event :configure-request 23) (define-event :gravity-notify 24) (define-event :resize-request 25) (define-event :circulate-notify 26) (define-event :circulate-request 27) (define-event :property-notify 28) (define-event :selection-clear 29) (define-event :selection-request 30) (define-event :selection-notify 31) (define-event :colormap-notify 32) (define-event :client-message 33) (define-event :mapping-notify 34) (defmacro declare-event (event-codes &body declares) ;; Used to indicate the keyword arguments for handler functions in ;; process-event and event-case. ;; Generates the functions used in SEND-EVENT. ;; A compiler warning is printed when all of EVENT-CODES are not ;; defined by a preceding DEFINE-EXTENSION. ;; The body is a list of declarations, each of which has the form: ;; (type . items) Where type is a data-type, and items is a list of ;; symbol names. The item order corresponds to the order of fields ;; in the event sent by the server. An item may be a list of items. ;; In this case, each item is aliased to the same event field. ;; This is used to give all events an EVENT-WINDOW item. ;; See the INPUT file for lots of examples. (declare (type (or keyword list) event-codes) (type (alist (field-type symbol) (field-names list)) declares)) (when (atom event-codes) (setq event-codes (list event-codes))) (setq event-codes (mapcar #'canonicalize-event-name event-codes)) (let* ((keywords nil) (name (first event-codes)) (get-macro (xintern name '-event-get-macro)) (get-function (xintern name '-event-get)) (put-function (xintern name '-event-put))) (multiple-value-bind (get-code get-index get-sizes) (get-put-items 2 declares nil #'(lambda (type index item args) (flet ((event-get (type index item args) (unless (member type '(pad8 pad16)) `(,(kintern item) (,(getify type) ,index ,@args))))) (if (atom item) (event-get type index item args) (mapcan #'(lambda (item) (event-get type index item args)) item))))) (declare (ignore get-index)) (multiple-value-bind (put-code put-index put-sizes) (get-put-items 2 declares t #'(lambda (type index item args) (unless (member type '(pad8 pad16)) (if (atom item) (progn (push item keywords) `((,(putify type) ,index ,item ,@args))) (let ((names (mapcar #'(lambda (name) (kintern name)) item))) (setq keywords (append item keywords)) `((,(putify type) ,index (check-consistency ',names ,@item) ,@args))))))) (declare (ignore put-index)) `(within-definition (,name declare-event) (defun ,get-macro (display event-key variable) ;; Note: we take pains to macroexpand the get-code here to enable application ;; code to be compiled without having the CLX macros file loaded. (subst display '%buffer (getf `(:display (the display ,display) :event-key (the keyword ,event-key) :event-code (the card8 (logand #x7f (read-card8 0))) :send-event-p (the boolean (logbitp 7 (read-card8 0))) ,@',(mapcar #'macroexpand get-code)) variable))) (defun ,get-function (display event handler) (declare (type display display) (type reply-buffer event)) (declare (type function handler) #+clx-ansi-common-lisp (dynamic-extent handler) #+(and lispm (not clx-ansi-common-lisp)) (sys:downward-funarg handler)) (reading-event (event :display display :sizes (8 16 ,@get-sizes)) (funcall handler :display display :event-key (svref *event-key-vector* (event-code event)) :event-code (logand #x7f (card8-get 0)) :send-event-p (logbitp 7 (card8-get 0)) ,@get-code))) (defun ,put-function (display &key ,@(setq keywords (nreverse keywords)) &allow-other-keys) (declare (type display display)) ,(when (member 'sequence keywords) `(unless sequence (setq sequence (display-request-number display)))) (with-buffer-output (display :sizes ,put-sizes :index (index+ (buffer-boffset display) 12)) ,@put-code)) ,@(mapcar #'(lambda (name) (allocate-extension-event-code name) `(let ((event-code (or (get ',name 'event-code) (allocate-extension-event-code ',name)))) (setf (svref *event-macro-vector* event-code) (function ,get-macro)) (setf (svref *event-handler-vector* event-code) (function ,get-function)) (setf (svref *event-send-vector* event-code) (function ,put-function)))) event-codes) ',name))))) (defun check-consistency (names &rest args) ;; Ensure all args are nil or have the same value. ;; Returns the consistent non-nil value. (let ((value (car args))) (dolist (arg (cdr args)) (if value (when (and arg (not (eq arg value))) (x-error 'inconsistent-parameters :parameters (mapcan #'list names args))) (setq value arg))) value)) (declare-event (:key-press :key-release :button-press :button-release) ;; for key-press and key-release, code is the keycode ;; for button-press and button-release, code is the button number (data code) (card16 sequence) ((or null card32) time) (window root (window event-window)) ((or null window) child) (int16 root-x root-y x y) (card16 state) (boolean same-screen-p) ) (declare-event :motion-notify ((data boolean) hint-p) (card16 sequence) ((or null card32) time) (window root (window event-window)) ((or null window) child) (int16 root-x root-y x y) (card16 state) (boolean same-screen-p)) (declare-event (:enter-notify :leave-notify) ((data (member8 :ancestor :virtual :inferior :nonlinear :nonlinear-virtual)) kind) (card16 sequence) ((or null card32) time) (window root (window event-window)) ((or null window) child) (int16 root-x root-y x y) (card16 state) ((member8 :normal :grab :ungrab) mode) ((bit 0) focus-p) ((bit 1) same-screen-p)) (declare-event (:focus-in :focus-out) ((data (member8 :ancestor :virtual :inferior :nonlinear :nonlinear-virtual :pointer :pointer-root :none)) kind) (card16 sequence) (window (window event-window)) ((member8 :normal :while-grabbed :grab :ungrab) mode)) (declare-event :keymap-notify ((bit-vector256 0) keymap)) (declare-event :exposure (card16 sequence) (window (window event-window)) (card16 x y width height count)) (declare-event :graphics-exposure (card16 sequence) (drawable (drawable event-window)) (card16 x y width height) (card16 minor) ;; Minor opcode (card16 count) (card8 major)) (declare-event :no-exposure (card16 sequence) (drawable (drawable event-window)) (card16 minor) (card8 major)) (declare-event :visibility-notify (card16 sequence) (window (window event-window)) ((member8 :unobscured :partially-obscured :fully-obscured) state)) (declare-event :create-notify (card16 sequence) (window (parent event-window) window) (int16 x y) (card16 width height border-width) (boolean override-redirect-p)) (declare-event :destroy-notify (card16 sequence) (window event-window window)) (declare-event :unmap-notify (card16 sequence) (window event-window window) (boolean configure-p)) (declare-event :map-notify (card16 sequence) (window event-window window) (boolean override-redirect-p)) (declare-event :map-request (card16 sequence) (window (parent event-window) window)) (declare-event :reparent-notify (card16 sequence) (window event-window window parent) (int16 x y) (boolean override-redirect-p)) (declare-event :configure-notify (card16 sequence) (window event-window window) ((or null window) above-sibling) (int16 x y) (card16 width height border-width) (boolean override-redirect-p)) (declare-event :configure-request ((data (member :above :below :top-if :bottom-if :opposite)) stack-mode) (card16 sequence) (window (parent event-window) window) ((or null window) above-sibling) (int16 x y) (card16 width height border-width value-mask)) (declare-event :gravity-notify (card16 sequence) (window event-window window) (int16 x y)) (declare-event :resize-request (card16 sequence) (window (window event-window)) (card16 width height)) (declare-event :circulate-notify (card16 sequence) (window event-window window parent) ((member16 :top :bottom) place)) (declare-event :circulate-request (card16 sequence) (window (parent event-window) window) (pad16 1 2) ((member16 :top :bottom) place)) (declare-event :property-notify (card16 sequence) (window (window event-window)) (keyword atom) ;; keyword ((or null card32) time) ((member16 :new-value :deleted) state)) (declare-event :selection-clear (card16 sequence) ((or null card32) time) (window (window event-window)) (keyword selection) ;; keyword ) (declare-event :selection-request (card16 sequence) ((or null card32) time) (window (window event-window) requestor) (keyword selection target) ((or null keyword) property) ) (declare-event :selection-notify (card16 sequence) ((or null card32) time) (window (window event-window)) (keyword selection target) ((or null keyword) property) ) (declare-event :colormap-notify (card16 sequence) (window (window event-window)) ((or null colormap) colormap) (boolean new-p installed-p)) (declare-event :client-message (data format) (card16 sequence) (window (window event-window)) (keyword type) ((client-message-sequence format) data)) (declare-event :mapping-notify (card16 sequence) ((member8 :modifier :keyboard :pointer) request) (card8 start) ;; first key-code (card8 count)) ;; ;; EVENT-LOOP ;; (defun event-loop-setup (display) (declare (type display display) (values progv-vars progv-vals current-event-symbol current-event-discarded-p-symbol)) (let* ((progv-vars (display-current-event-symbol display)) (current-event-symbol (first progv-vars)) (current-event-discarded-p-symbol (second progv-vars))) (declare (type list progv-vars) (type symbol current-event-symbol current-event-discarded-p-symbol)) (values progv-vars (list (if (boundp current-event-symbol) ;; The current event is already bound, so bind it to the next ;; event. (let ((event (symbol-value current-event-symbol))) (declare (type (or null reply-buffer) event)) (and event (reply-next (the reply-buffer event)))) ;; The current event isn't bound, so bind it to the head of the ;; event queue. (display-event-queue-head display)) nil) current-event-symbol current-event-discarded-p-symbol))) (defun event-loop-step-before (display timeout force-output-p current-event-symbol) (declare (type display display) (type (or null number) timeout) (type boolean force-output-p) (type symbol current-event-symbol) (values event eof-or-timeout)) (unless (symbol-value current-event-symbol) (let ((eof-or-timeout (wait-for-event display timeout force-output-p))) (when eof-or-timeout (return-from event-loop-step-before (values nil eof-or-timeout)))) (setf (symbol-value current-event-symbol) (display-new-events display))) (let ((event (symbol-value current-event-symbol))) (declare (type reply-buffer event)) (with-event-queue-internal (display) (when (eq event (display-new-events display)) (setf (display-new-events display) (reply-next event)))) (values event nil))) (defun dequeue-event (display event) (declare (type display display) (type reply-buffer event) (values next)) ;; Remove the current event from the event queue (with-event-queue-internal (display) (let ((next (reply-next event)) (head (display-event-queue-head display))) (declare (type (or null reply-buffer) next head)) (when (eq event (display-new-events display)) (setf (display-new-events display) next)) (cond ((eq event head) (threaded-dequeue (display-event-queue-head display) (display-event-queue-tail display) reply-next reply-buffer)) ((null head) (setq next nil)) (t (do* ((previous head current) (current (reply-next previous) (reply-next previous))) ((or (null current) (eq event current)) (when (eq event current) (when (eq current (display-event-queue-tail display)) (setf (display-event-queue-tail display) previous)) (setf (reply-next previous) next))) (declare (type reply-buffer previous) (type (or null reply-buffer) current))))) next))) (defun event-loop-step-after (display event discard-p current-event-symbol current-event-discarded-p-symbol &optional aborted) (declare (type display display) (type reply-buffer event) (type boolean discard-p aborted) (type symbol current-event-symbol current-event-discarded-p-symbol)) (when (and discard-p (not aborted) (not (symbol-value current-event-discarded-p-symbol))) (discard-current-event display)) (let ((next (reply-next event))) (declare (type (or null reply-buffer) next)) (when (symbol-value current-event-discarded-p-symbol) (setf (symbol-value current-event-discarded-p-symbol) nil) (setq next (dequeue-event display event)) (deallocate-event event)) (setf (symbol-value current-event-symbol) next))) (defmacro event-loop ((display event timeout force-output-p discard-p) &body body) ;; Bind EVENT to the events for DISPLAY. ;; This is the "GUTS" of process-event and event-case. `(let ((.display. ,display) (.timeout. ,timeout) (.force-output-p. ,force-output-p) (.discard-p. ,discard-p)) (declare (type display .display.) (type (or null number) .timeout.) (type boolean .force-output-p. .discard-p.)) (with-event-queue (.display. ,@(and timeout `(:timeout .timeout.))) (multiple-value-bind (.progv-vars. .progv-vals. .current-event-symbol. .current-event-discarded-p-symbol.) (event-loop-setup .display.) (declare (type list .progv-vars. .progv-vals.) (type symbol .current-event-symbol. .current-event-discarded-p-symbol.)) (progv .progv-vars. .progv-vals. (loop (multiple-value-bind (.event. .eof-or-timeout.) (event-loop-step-before .display. .timeout. .force-output-p. .current-event-symbol.) (declare (type (or null reply-buffer) .event.)) (when (null .event.) (return (values nil .eof-or-timeout.))) (let ((.aborted. t)) (unwind-protect (progn (let ((,event .event.)) (declare (type reply-buffer ,event)) ,@body) (setq .aborted. nil)) (event-loop-step-after .display. .event. .discard-p. .current-event-symbol. .current-event-discarded-p-symbol. .aborted.)))))))))) (defun discard-current-event (display) ;; Discard the current event for DISPLAY. ;; Returns NIL when the event queue is empty, else T. ;; To ensure events aren't ignored, application code should only call ;; this when throwing out of event-case or process-next-event, or from ;; inside even-case, event-cond or process-event when :peek-p is T and ;; :discard-p is NIL. (declare (type display display) (values boolean)) (let* ((symbols (display-current-event-symbol display)) (event (let ((current-event-symbol (first symbols))) (declare (type symbol current-event-symbol)) (when (boundp current-event-symbol) (symbol-value current-event-symbol))))) (declare (type list symbols) (type (or null reply-buffer) event)) (unless (null event) ;; Set the discarded-p flag (let ((current-event-discarded-p-symbol (second symbols))) (declare (type symbol current-event-discarded-p-symbol)) (when (boundp current-event-discarded-p-symbol) (setf (symbol-value current-event-discarded-p-symbol) t))) ;; Return whether the event queue is empty (not (null (reply-next (the reply-buffer event))))))) ;; ;; PROCESS-EVENT ;; (defun process-event (display &key handler timeout peek-p discard-p (force-output-p t)) ;; If force-output-p is true, first invokes display-force-output. Invokes handler ;; on each queued event until handler returns non-nil, and that returned object is ;; then returned by process-event. If peek-p is true, then the event is not ;; removed from the queue. If discard-p is true, then events for which handler ;; returns nil are removed from the queue, otherwise they are left in place. Hangs ;; until non-nil is generated for some event, or for the specified timeout (in ;; seconds, if given); however, it is acceptable for an implementation to wait only ;; once on network data, and therefore timeout prematurely. Returns nil on ;; timeout. If handler is a sequence, it is expected to contain handler functions ;; specific to each event class; the event code is used to index the sequence, ;; fetching the appropriate handler. Handler is called with raw resource-ids, not ;; with resource objects. The arguments to the handler are described using declare-event. ;; ;; T for peek-p means the event (for which the handler returns non-nil) is not removed ;; from the queue (it is left in place), NIL means the event is removed. (declare (type display display) (type (or null number) timeout) (type boolean peek-p discard-p force-output-p)) (declare (type t handler) #+clx-ansi-common-lisp (dynamic-extent handler) #+(and lispm (not clx-ansi-common-lisp)) (sys:downward-funarg #+Genera * #-Genera handler)) (event-loop (display event timeout force-output-p discard-p) (let* ((event-code (event-code event)) ;; Event decoder defined by DECLARE-EVENT (event-decoder (and (index< event-code (length *event-handler-vector*)) (svref *event-handler-vector* event-code)))) (declare (type array-index event-code) (type (or null function) event-decoder)) (if event-decoder (let ((event-handler (if (functionp handler) handler (and (type? handler 'sequence) (< event-code (length handler)) (elt handler event-code))))) (if event-handler (let ((result (funcall event-decoder display event event-handler))) (when result (unless peek-p (discard-current-event display)) (return result))) (cerror "Ignore this event" "No handler for ~s event" (svref *event-key-vector* event-code)))) (cerror "Ignore this event" "Server Error: event with unknown event code ~d received." event-code))))) (defun make-event-handlers (&key (type 'array) default) (declare (type t type) ;Sequence type specifier (type function default) (values sequence)) ;Default handler for initial content ;; Makes a handler sequence suitable for process-event (make-sequence type *max-events* :initial-element default)) (defun event-handler (handlers event-key) (declare (type sequence handlers) (type event-key event-key) (values function)) ;; Accessor for a handler sequence (elt handlers (position event-key *event-key-vector* :test #'eq))) (defun set-event-handler (handlers event-key handler) (declare (type sequence handlers) (type event-key event-key) (type function handler) (values handler)) (setf (elt handlers (position event-key *event-key-vector* :test #'eq)) handler)) (defsetf event-handler set-event-handler) ;; ;; EVENT-CASE ;; (defmacro event-case ((&rest args) &body clauses) ;; If force-output-p is true, first invokes display-force-output. Executes the ;; matching clause for each queued event until a clause returns non-nil, and that ;; returned object is then returned by event-case. If peek-p is true, then the ;; event is not removed from the queue. If discard-p is true, then events for ;; which the clause returns nil are removed from the queue, otherwise they are left ;; in place. Hangs until non-nil is generated for some event, or for the specified ;; timeout (in seconds, if given); however, it is acceptable for an implementation ;; to wait only once on network data, and therefore timeout prematurely. Returns ;; nil on timeout. In each clause, event-or-events is an event-key or a list of ;; event-keys (but they need not be typed as keywords) or the symbol t or otherwise ;; (but only in the last clause). The keys are not evaluated, and it is an error ;; for the same key to appear in more than one clause. Args is the list of event ;; components of interest; corresponding values (if any) are bound to variables ;; with these names (i.e., the args are variable names, not keywords, the keywords ;; are derived from the variable names). An arg can also be a (keyword var) form, ;; as for keyword args in a lambda lists. If no t/otherwise clause appears, it is ;; equivalent to having one that returns nil. (declare (arglist (display &key timeout peek-p discard-p (force-output-p t)) (event-or-events ((&rest args) |...|) &body body) |...|)) ;; Event-case is just event-cond with the whole body in the test-form `(event-cond ,args ,@(mapcar #'(lambda (clause) `(,(car clause) ,(cadr clause) (progn ,@(cddr clause)))) clauses))) ;; ;; EVENT-COND ;; (defmacro event-cond ((display &key timeout peek-p discard-p (force-output-p t)) &body clauses) ;; The clauses of event-cond are of the form: ;; (event-or-events binding-list test-form . body-forms) ;; ;; EVENT-OR-EVENTS event-key or a list of event-keys (but they ;; need not be typed as keywords) or the symbol t ;; or otherwise (but only in the last clause). If ;; no t/otherwise clause appears, it is equivalent ;; to having one that returns nil. The keys are ;; not evaluated, and it is an error for the same ;; key to appear in more than one clause. ;; ;; BINDING-LIST The list of event components of interest. ;; corresponding values (if any) are bound to ;; variables with these names (i.e., the binding-list ;; has variable names, not keywords, the keywords are ;; derived from the variable names). An arg can also ;; be a (keyword var) form, as for keyword args in a ;; lambda list. ;; ;; The matching TEST-FORM for each queued event is executed until a ;; clause's test-form returns non-nil. Then the BODY-FORMS are ;; evaluated, returning the (possibly multiple) values of the last ;; form from event-cond. If there are no body-forms then, if the ;; test-form is non-nil, the value of the test-form is returned as a ;; single value. ;; ;; Options: ;; FORCE-OUTPUT-P When true, first invoke display-force-output if no ;; input is pending. ;; ;; PEEK-P When true, then the event is not removed from the queue. ;; ;; DISCARD-P When true, then events for which the clause returns nil ;; are removed from the queue, otherwise they are left in place. ;; ;; TIMEOUT If NIL, hang until non-nil is generated for some event's ;; test-form. Otherwise return NIL after TIMEOUT seconds have ;; elapsed. ;; (declare (arglist (display &key timeout peek-p discard-p force-output-p) (event-or-events (&rest args) test-form &body body) |...|)) (let ((event (gensym)) (disp (gensym)) (peek (gensym))) `(let ((,disp ,display) (,peek ,peek-p)) (declare (type display ,disp)) (event-loop (,disp ,event ,timeout ,force-output-p ,discard-p) (event-dispatch (,disp ,event ,peek) ,@clauses))))) (defun get-event-code (event) ;; Returns the event code given an event-key (declare (type event-key event)) (declare (values card8)) (or (get event 'event-code) (x-type-error event 'event-key))) (defun universal-event-get-macro (display event-key variable) (getf `(:display (the display ,display) :event-key (the keyword ,event-key) :event-code (the card8 (logand 127 (read-card8 0))) :send-event-p (the boolean (logbitp 7 (read-card8 0)))) variable)) (defmacro event-dispatch ((display event peek-p) &body clauses) ;; Helper macro for event-case ;; CLAUSES are of the form: ;; (event-or-events binding-list test-form . body-forms) (let ((event-key (gensym)) (all-events (make-array *max-events* :element-type 'bit :initial-element 0))) `(reading-event (,event) (let ((,event-key (svref *event-key-vector* (event-code ,event)))) (case ,event-key ,@(mapcar #'(lambda (clause) ; Translate event-cond clause to case clause (let* ((events (first clause)) (arglist (second clause)) (test-form (third clause)) (body-forms (cdddr clause))) (flet ((event-clause (display peek-p first-form rest-of-forms) (if rest-of-forms `(when ,first-form (unless ,peek-p (discard-current-event ,display)) (return (progn ,@rest-of-forms))) ;; No body forms, return the result of the test form (let ((result (gensym))) `(let ((,result ,first-form)) (when ,result (unless ,peek-p (discard-current-event ,display)) (return ,result))))))) (if (member events '(otherwise t)) ;; code for OTHERWISE clause. ;; Find all events NOT used by other clauses (let ((keys (do ((i 0 (1+ i)) (key nil) (result nil)) ((>= i *max-events*) result) (setq key (svref *event-key-vector* i)) (when (and key (zerop (aref all-events i))) (push key result))))) `(otherwise (binding-event-values (,display ,event-key ,(or keys :universal) ,@arglist) ,(event-clause display peek-p test-form body-forms)))) ;; Code for normal clauses (let (true-events) ;; canonicalize event-names (if (consp events) (progn (setq true-events (mapcar #'canonicalize-event-name events)) (dolist (event true-events) (setf (aref all-events (get-event-code event)) 1))) (setf true-events (canonicalize-event-name events) (aref all-events (get-event-code true-events)) 1)) `(,true-events (binding-event-values (,display ,event-key ,true-events ,@arglist) ,(event-clause display peek-p test-form body-forms)))))))) clauses)))))) (defmacro binding-event-values ((display event-key event-keys &rest value-list) &body body) ;; Execute BODY with the variables in VALUE-LIST bound to components of the ;; EVENT-KEYS events. (unless (consp event-keys) (setq event-keys (list event-keys))) (flet ((var-key (var) (kintern (if (consp var) (first var) var))) (var-symbol (var) (if (consp var) (second var) var))) ;; VARS is an alist of: ;; (component-key ((event-key event-key ...) . extraction-code) ;; ((event-key event-key ...) . extraction-code) ...) ;; There should probably be accessor macros for this, instead of things like cdadr. (let ((vars (mapcar #'(lambda (var) (list var)) value-list)) (multiple-p nil)) ;; Fill in the VARS alist with event-keys and extraction-code (do ((keys event-keys (cdr keys)) (temp nil)) ((endp keys)) (let* ((key (car keys)) (binder (case key (:universal #'universal-event-get-macro) (otherwise (svref *event-macro-vector* (get-event-code key)))))) (dolist (var vars) (let ((code (funcall binder display event-key (var-key (car var))))) (unless code (warn "~a isn't a component of the ~s event" (var-key (car var)) key)) (if (setq temp (member code (cdr var) :key #'cdr :test #'equal)) (push key (caar temp)) (push `((,key) . ,code) (cdr var))))))) ;; Bind all the values `(let ,(mapcar #'(lambda (var) (if (cddr var) ;; if more than one binding form (progn (setq multiple-p t) (var-symbol (car var))) (list (var-symbol (car var)) (cdadr var)))) vars) ;; When some values come from different places, generate code to set them ,(when multiple-p `(case ,event-key ,@(do ((keys event-keys (cdr keys)) (clauses nil) ;; alist of (event-keys bindings) (clause nil nil) (temp)) ((endp keys) (dolist (clause clauses) (unless (cdar clause) ;; Atomize single element lists (setf (car clause) (caar clause)))) clauses) ;; Gather up all the bindings associated with (car keys) (dolist (var vars) (when (cddr var) ;; when more than one binding form (dolist (events (cdr var)) (when (member (car keys) (car events)) ;; Optimize for event-window being the same as some other binding (if (setq temp (member (cdr events) clause :key #'caddr :test #'equal)) (setq clause (nconc clause `((setq ,(car var) ,(second (car temp)))))) (push `(setq ,(car var) ,(cdr events)) clause)))))) ;; Merge bindings for (car keys) with other bindings (when clause (if (setq temp (member clause clauses :key #'cdr :test #'equal)) (push (car keys) (caar temp)) (push `((,(car keys)) . ,clause) clauses)))))) ,@body)))) ;;;----------------------------------------------------------------------------- ;;; Error Handling ;;;----------------------------------------------------------------------------- (eval-when (eval compile load) (defparameter *xerror-vector* '#(unknown-error request-error ; 1 bad request code value-error ; 2 integer parameter out of range window-error ; 3 parameter not a Window pixmap-error ; 4 parameter not a Pixmap atom-error ; 5 parameter not an Atom cursor-error ; 6 parameter not a Cursor font-error ; 7 parameter not a Font match-error ; 8 parameter mismatch drawable-error ; 9 parameter not a Pixmap or Window access-error ; 10 attempt to access private resource" alloc-error ; 11 insufficient resources colormap-error ; 12 no such colormap gcontext-error ; 13 parameter not a GContext id-choice-error ; 14 invalid resource ID for this connection name-error ; 15 font or color name does not exist length-error ; 16 request length incorrect; ; internal Xlib error implementation-error ; 17 server is defective )) ) (defun make-error (display event asynchronous) (declare (type display display) (type reply-buffer event) (type boolean asynchronous)) (reading-event (event) (let* ((error-code (read-card8 1)) (error-key (get-error-key display error-code)) (error-decode-function (get error-key 'error-decode-function)) (params (funcall error-decode-function display event))) (list* error-code error-key :asynchronous asynchronous :current-sequence (display-request-number display) params)))) (defun report-error (display error-code error-key &rest params) (declare (type display display) (dynamic-extent params)) ;; All errors (synchronous and asynchronous) are processed by calling ;; an error handler in the display. The handler is called with the display ;; as the first argument and the error-key as its second argument. If handler is ;; an array it is expected to contain handler functions specific to ;; each error; the error code is used to index the array, fetching the ;; appropriate handler. Any results returned by the handler are ignored;; ;; it is assumed the handler either takes care of the error completely, ;; or else signals. For all core errors, additional keyword/value argument ;; pairs are: ;; :major integer ;; :minor integer ;; :sequence integer ;; :current-sequence integer ;; :asynchronous (member t nil) ;; For :colormap, :cursor, :drawable, :font, :GContext, :id-choice, :pixmap, and :window ;; errors another pair is: ;; :resource-id integer ;; For :atom errors, another pair is: ;; :atom-id integer ;; For :value errors, another pair is: ;; :value integer (let* ((handler (display-error-handler display)) (handler-function (if (type? handler 'sequence) (elt handler error-code) handler))) (apply handler-function display error-key params))) (defun request-name (code &optional display) (if (< code (length *request-names*)) (svref *request-names* code) (dolist (extension (and display (display-extension-alist display)) "unknown") (when (= code (second extension)) (return (first extension)))))) #-(or clx-ansi-common-lisp excl lcl3.0) (define-condition request-error (x-error) ((display :reader request-error-display) (error-key :reader request-error-error-key) (major :reader request-error-major) (minor :reader request-error-minor) (sequence :reader request-error-sequence) (current-sequence :reader request-error-current-sequence) (asynchronous :reader request-error-asynchronous)) (:report report-request-error)) (defun report-request-error (condition stream) (let ((error-key (request-error-error-key condition)) (asynchronous (request-error-asynchronous condition)) (major (request-error-major condition)) (minor (request-error-minor condition)) (sequence (request-error-sequence condition)) (current-sequence (request-error-current-sequence condition))) (format stream "~:[~;Asynchronous ~]~a in ~:[request ~d (last request was ~d) ~;current request~2* ~] Code ~d.~d [~a]" asynchronous error-key (= sequence current-sequence) sequence current-sequence major minor (request-name major (request-error-display condition))))) ;; Since the :report arg is evaluated as (function report-request-error) the ;; define-condition must come after the function definition. #+(or clx-ansi-common-lisp excl lcl3.0) (define-condition request-error (x-error) ((display :reader request-error-display :initarg :display) (error-key :reader request-error-error-key :initarg :error-key) (major :reader request-error-major :initarg :major) (minor :reader request-error-minor :initarg :minor) (sequence :reader request-error-sequence :initarg :sequence) (current-sequence :reader request-error-current-sequence :initarg :current-sequence) (asynchronous :reader request-error-asynchronous :initarg :asynchronous)) (:report report-request-error)) (define-condition resource-error (request-error) ((resource-id :reader resource-error-resource-id :initarg :resource-id)) (:report (lambda (condition stream) (report-request-error condition stream) (format stream " ID #x~x" (resource-error-resource-id condition))))) (define-condition unknown-error (request-error) ((error-code :reader unknown-error-error-code :initarg :error-code)) (:report (lambda (condition stream) (report-request-error condition stream) (format stream " Error Code ~d." (unknown-error-error-code condition))))) (define-condition access-error (request-error) ()) (define-condition alloc-error (request-error) ()) (define-condition atom-error (request-error) ((atom-id :reader atom-error-atom-id :initarg :atom-id)) (:report (lambda (condition stream) (report-request-error condition stream) (format stream " Atom-ID #x~x" (atom-error-atom-id condition))))) (define-condition colormap-error (resource-error) ()) (define-condition cursor-error (resource-error) ()) (define-condition drawable-error (resource-error) ()) (define-condition font-error (resource-error) ()) (define-condition gcontext-error (resource-error) ()) (define-condition id-choice-error (resource-error) ()) (define-condition illegal-request-error (request-error) ()) (define-condition length-error (request-error) ()) (define-condition match-error (request-error) ()) (define-condition name-error (request-error) ()) (define-condition pixmap-error (resource-error) ()) (define-condition value-error (request-error) ((value :reader value-error-value :initarg :value)) (:report (lambda (condition stream) (report-request-error condition stream) (format stream " Value ~d." (value-error-value condition))))) (define-condition window-error (resource-error)()) (define-condition implementation-error (request-error) ()) ;;----------------------------------------------------------------------------- ;; Internal error conditions signaled by CLX (define-condition x-type-error (type-error x-error) ((type-string :reader x-type-error-type-string :initarg :type-string)) (:report (lambda (condition stream) (format stream "~s isn't a ~a" (type-error-datum condition) (or (x-type-error-type-string condition) (type-error-expected-type condition)))))) (define-condition closed-display (x-error) ((display :reader closed-display-display :initarg :display)) (:report (lambda (condition stream) (format stream "Attempt to use closed display ~s" (closed-display-display condition))))) (define-condition lookup-error (x-error) ((id :reader lookup-error-id :initarg :id) (display :reader lookup-error-display :initarg :display) (type :reader lookup-error-type :initarg :type) (object :reader lookup-error-object :initarg :object)) (:report (lambda (condition stream) (format stream "ID ~d from display ~s should have been a ~s, but was ~s" (lookup-error-id condition) (lookup-error-display condition) (lookup-error-type condition) (lookup-error-object condition))))) (define-condition connection-failure (x-error) ((major-version :reader connection-failure-major-version :initarg :major-version) (minor-version :reader connection-failure-minor-version :initarg :minor-version) (host :reader connection-failure-host :initarg :host) (display :reader connection-failure-display :initarg :display) (reason :reader connection-failure-reason :initarg :reason)) (:report (lambda (condition stream) (format stream "Connection failure to X~d.~d server ~a display ~d: ~a" (connection-failure-major-version condition) (connection-failure-minor-version condition) (connection-failure-host condition) (connection-failure-display condition) (connection-failure-reason condition))))) (define-condition reply-length-error (x-error) ((reply-length :reader reply-length-error-reply-length :initarg :reply-length) (expected-length :reader reply-length-error-expected-length :initarg :expected-length) (display :reader reply-length-error-display :initarg :display)) (:report (lambda (condition stream) (format stream "Reply length was ~d when ~d words were expected for display ~s" (reply-length-error-reply-length condition) (reply-length-error-expected-length condition) (reply-length-error-display condition))))) (define-condition reply-timeout (x-error) ((timeout :reader reply-timeout-timeout :initarg :timeout) (display :reader reply-timeout-display :initarg :display)) (:report (lambda (condition stream) (format stream "Timeout after waiting ~d seconds for a reply for display ~s" (reply-timeout-timeout condition) (reply-timeout-display condition))))) (define-condition sequence-error (x-error) ((display :reader sequence-error-display :initarg :display) (req-sequence :reader sequence-error-req-sequence :initarg :req-sequence) (msg-sequence :reader sequence-error-msg-sequence :initarg :msg-sequence)) (:report (lambda (condition stream) (format stream "Reply out of sequence for display ~s.~% Expected ~d, Got ~d" (sequence-error-display condition) (sequence-error-req-sequence condition) (sequence-error-msg-sequence condition))))) (define-condition unexpected-reply (x-error) ((display :reader unexpected-reply-display :initarg :display) (msg-sequence :reader unexpected-reply-msg-sequence :initarg :msg-sequence) (req-sequence :reader unexpected-reply-req-sequence :initarg :req-sequence) (length :reader unexpected-reply-length :initarg :length)) (:report (lambda (condition stream) (format stream "Display ~s received a server reply when none was expected.~@ Last request sequence ~d Reply Sequence ~d Reply Length ~d bytes." (unexpected-reply-display condition) (unexpected-reply-req-sequence condition) (unexpected-reply-msg-sequence condition) (unexpected-reply-length condition))))) (define-condition missing-parameter (x-error) ((parameter :reader missing-parameter-parameter :initarg :parameter)) (:report (lambda (condition stream) (let ((parm (missing-parameter-parameter condition))) (if (consp parm) (format stream "One or more of the required parameters ~a is missing." parm) (format stream "Required parameter ~a is missing or null." parm)))))) ;; This can be signalled anywhere a pseudo font access fails. (define-condition invalid-font (x-error) ((font :reader invalid-font-font :initarg :font)) (:report (lambda (condition stream) (format stream "Can't access font ~s" (invalid-font-font condition))))) (define-condition device-busy (x-error) ((display :reader device-busy-display :initarg :display)) (:report (lambda (condition stream) (format stream "Device busy for display ~s" (device-busy-display condition))))) (define-condition unimplemented-event (x-error) ((display :reader unimplemented-event-display :initarg :display) (event-code :reader unimplemented-event-event-code :initarg :event-code)) (:report (lambda (condition stream) (format stream "Event code ~d not implemented for display ~s" (unimplemented-event-event-code condition) (unimplemented-event-display condition))))) (define-condition undefined-event (x-error) ((display :reader undefined-event-display :initarg :display) (event-name :reader undefined-event-event-name :initarg :event-name)) (:report (lambda (condition stream) (format stream "Event code ~d undefined for display ~s" (undefined-event-event-name condition) (undefined-event-display condition))))) (define-condition absent-extension (x-error) ((name :reader absent-extension-name :initarg :name) (display :reader absent-extension-display :initarg :display)) (:report (lambda (condition stream) (format stream "Extension ~a isn't defined for display ~s" (absent-extension-name condition) (absent-extension-display condition))))) (define-condition inconsistent-parameters (x-error) ((parameters :reader inconsistent-parameters-parameters :initarg :parameters)) (:report (lambda (condition stream) (format stream "inconsistent-parameters:~{ ~s~}" (inconsistent-parameters-parameters condition))))) (defun get-error-key (display error-code) (declare (type display display) (type array-index error-code)) ;; Return the error-key associated with error-code (if (< error-code (length *xerror-vector*)) (svref *xerror-vector* error-code) ;; Search the extensions for the error (dolist (entry (display-extension-alist display) 'unknown-error) (let* ((event-name (first entry)) (first-error (fourth entry)) (errors (third (assoc event-name *extensions*)))) (declare (type keyword event-name) (type array-index first-error) (type list errors)) (when (and errors (index<= first-error error-code (index+ first-error (index- (length errors) 1)))) (return (nth (index- error-code first-error) errors))))))) (defmacro define-error (error-key function) ;; Associate a function with ERROR-KEY which will be called with ;; parameters DISPLAY and REPLY-BUFFER and ;; returns a plist of keyword/value pairs which will be passed on ;; to the error handler. A compiler warning is printed when ;; ERROR-KEY is not defined in a preceding DEFINE-EXTENSION. ;; Note: REPLY-BUFFER may used with the READING-EVENT and READ-type ;; macros for getting error fields. See DECODE-CORE-ERROR for ;; an example. (declare (type symbol error-key) (type function function)) ;; First ensure the name is for a declared extension (unless (or (find error-key *xerror-vector*) (dolist (extension *extensions*) (when (member error-key (third extension)) (return t)))) (x-type-error error-key 'error-key)) `(setf (get ',error-key 'error-decode-function) (function ,function))) ;; All core errors use this, so we make it available to extensions. (defun decode-core-error (display event &optional arg) ;; All core errors have the following keyword/argument pairs: ;; :major integer ;; :minor integer ;; :sequence integer ;; In addition, many have an additional argument that comes from the ;; same place in the event, but is named differently. When the ARG ;; argument is specified, the keyword ARG with card32 value starting ;; at byte 4 of the event is returned with the other keyword/argument ;; pairs. (declare (type display display) (type reply-buffer event) (type (or null keyword) arg)) (declare (values keyword/arg-plist)) display (reading-event (event) (let* ((sequence (read-card16 2)) (minor-code (read-card16 8)) (major-code (read-card8 10)) (result (list :major major-code :minor minor-code :sequence sequence))) (when arg (setq result (list* arg (read-card32 4) result))) result))) (defun decode-resource-error (display event) (decode-core-error display event :resource-id)) (define-error unknown-error (lambda (display event) (list* :error-code (aref (reply-ibuf8 event) 1) (decode-core-error display event)))) (define-error request-error decode-core-error) ; 1 bad request code (define-error value-error ; 2 integer parameter out of range (lambda (display event) (decode-core-error display event :value))) (define-error window-error decode-resource-error) ; 3 parameter not a Window (define-error pixmap-error decode-resource-error) ; 4 parameter not a Pixmap (define-error atom-error ; 5 parameter not an Atom (lambda (display event) (decode-core-error display event :atom-id))) (define-error cursor-error decode-resource-error) ; 6 parameter not a Cursor (define-error font-error decode-resource-error) ; 7 parameter not a Font (define-error match-error decode-core-error) ; 8 parameter mismatch (define-error drawable-error decode-resource-error) ; 9 parameter not a Pixmap or Window (define-error access-error decode-core-error) ; 10 attempt to access private resource" (define-error alloc-error decode-core-error) ; 11 insufficient resources (define-error colormap-error decode-resource-error) ; 12 no such colormap (define-error gcontext-error decode-resource-error) ; 13 parameter not a GContext (define-error id-choice-error decode-resource-error) ; 14 invalid resource ID for this connection (define-error name-error decode-core-error) ; 15 font or color name does not exist (define-error length-error decode-core-error) ; 16 request length incorrect; ; internal Xlib error (define-error implementation-error decode-core-error) ; 17 server is defective