;;; -*- Mode: LISP; Syntax: Common-lisp; Package: XLIB; Base: 10; Lowercase: Yes -*-

;; This file contains some of the system dependent code for CLX

;;;
;;;			 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.
;;;

(in-package :xlib)

;;;-------------------------------------------------------------------------
;;; Declarations
;;;-------------------------------------------------------------------------

;;; fix a bug in kcl's RATIONAL...
;;;   redefine both the function and the type.

#+(or kcl ibcl)
(shadow 'rational)

#+(or kcl ibcl)
(progn
  (defun rational (x)
    (if (rationalp x)
	x
	(lisp:rational x)))
  (deftype rational () 'lisp:rational))

;;; DECLAIM

#-ansi-common-lisp
(defmacro declaim (&rest decl-specs)
  (if (cdr decl-specs)
      `(progn
	 ,@(mapcar #'(lambda (decl-spec) `(proclaim ',decl-spec))
		   decl-specs))
    `(proclaim ',(car decl-specs))))

;;; VALUES value1 value2 ... -- Documents the values returned by the function.

#-lispm
(declaim (declaration values))

;;; ARGLIST arg1 arg2 ... -- Documents the arglist of the function.  Overrides
;;; the documentation that might get generated by the real arglist of the
;;; function.

#+lispm 
(import '(sys:arglist))

#+lcl3.0
(import '(lcl:arglist))

#-(or lispm lcl3.0)
(declaim (declaration arglist))

;;; DOWNWARD-FUNARG name1 name2 ... -- Tells callers of this function that
;;; closures passed in as the argument named by name can be consed on the
;;; stack, as they have dynamic extent.  In Genera keyword args can't be named
;;; this way.  Instead use * to specify all functional args have dynamic
;;; extent.

#+lispm 
(import '(sys:downward-funarg))

#-lispm
(declaim (declaration downward-funarg))

;;; DYNAMIC-EXTENT var -- Tells the compiler that the rest arg var has
;;; dynamic extent and therefore can be kept on the stack and not copied to
;;; the heap, even though the value is passed out of the function.

#+lcl3.0
(import '(lcl:dynamic-extent))

#-(or ansi-common-lisp lcl3.0)
(declaim (declaration dynamic-extent))

;;; ARRAY-REGISTER var1 var2 ... -- The variables mentioned are locals (not
;;; args) that hold vectors.  

#+Genera
(import '(sys:array-register))

#-Genera 
(declaim (declaration array-register))

;;; INDENTATION argpos1 arginden1 argpos2 arginden2 --- Tells the lisp editor how to
;;; indent calls to the function or macro containing the declaration.  

#+genera
(import '(zwei:indentation))

#-genera
(declaim (declaration indentation))

;;;-------------------------------------------------------------------------
;;; Declaration macros
;;;-------------------------------------------------------------------------

;;; WITH-VECTOR (variable type) &body body --- ensures the variable is a local
;;; and then does a type declaration and array register declaration
(defmacro with-vector ((var type) &body body)
  `(let ((,var ,var))
     (declare (type ,type ,var)
	      (array-register ,var))
     ,@body))

;;; WITHIN-DEFINITION (name type) &body body --- Includes definitions for
;;; Meta-.

#+lispm
(defmacro within-definition ((name type) &body body)
  `(zl:local-declare
     ((sys:function-parent ,name ,type))
     (sys:record-source-file-name ',name ',type)
     ,@body))

#-lispm
(defmacro within-definition ((name type) &body body)
  (declare (ignore name type))
  `(progn ,@body))


;;;-------------------------------------------------------------------------
;;; CLX can maintain a mapping from X server ID's to local data types.  If
;;; one takes the view that CLX objects will be instance variables of
;;; objects at the next higher level, then PROCESS-EVENT will typically map
;;; from resource-id to higher-level object.  In that case, the lower-level
;;; CLX mapping will almost never be used (except in rare cases like
;;; query-tree), and only serve to consume space (which is difficult to
;;; GC), in which case always-consing versions of the make-<mumble>s will
;;; be better.  Even when maps are maintained, it isn't clear they are
;;; useful for much beyond xatoms and windows (since almost nothing else
;;; ever comes back in events).
;;;--------------------------------------------------------------------------
(defconstant *clx-cached-types*
	     '( drawable
		window
		pixmap
;		gcontext
		cursor
		colormap
		font))

(defmacro resource-id-map-test ()
  #+excl '#'equal
  #-excl '#'eql)		    ; (eq fixnum fixnum) is not guaranteed.

(defmacro keysym->character-map-test ()
  #+excl '#'equal
  #-excl '#'eql)

;;; You must define this to match the real byte order.  It is used by
;;; overlapping array and image code.

#+(or lispm vax little-endian)
(eval-when (eval compile load)
  (pushnew :clx-little-endian *features*))

#+lcl3.0
(eval-when (compile eval load)
  (ecase lucid::machine-endian
    (:big)
    (:little (pushnew :clx-little-endian *features*))))

;;; Steele's Common-Lisp states:  "It is an error if the array specified
;;; as the :displaced-to argument  does not have the same :element-type
;;; as the array being created" If this is the case on your lisp, then
;;; leave the overlapping-arrays feature turned off.  Lisp machines
;;; (Symbolics TI and LMI) don't have this restriction, and allow arrays
;;; with different element types to overlap.  CLX will take advantage of
;;; this to do fast array packing/unpacking when the overlapping-arrays
;;; feature is enabled.

#+(and clx-little-endian lispm)
(eval-when (eval compile load)
  (pushnew :clx-overlapping-arrays *features*))

#+(and clx-overlapping-arrays genera)
(progn
(deftype overlap16 () '(unsigned-byte 16))
(deftype overlap32 () '(signed-byte 32))
)

#+(and clx-overlapping-arrays (or explorer lambda cadr))
(progn
(deftype overlap16 () '(unsigned-byte 16))
(deftype overlap32 () '(unsigned-byte 32))
)

(deftype buffer-bytes () `(simple-array (unsigned-byte 8) (*)))

#+clx-overlapping-arrays
(progn
(deftype buffer-words () `(vector overlap16))
(deftype buffer-longs () `(vector overlap32))
)

;;; This defines a type which is a subtype of the integers.
;;; This type is used to describe all variables that can be array indices.
;;; It is here because it is used below.
;;; This is inclusive because start/end can be 1 past the end.
(deftype array-index () `(integer 0 ,array-dimension-limit))


;; this is the best place to define these?

#-Genera
(progn

(defun make-index-typed (form)
  (if (constantp form) form `(the array-index ,form)))

(defun make-index-op (operator args)
  `(the array-index
	(values 
	  ,(case (length args)
	     (0 `(,operator))
	     (1 `(,operator
		  ,(make-index-typed (first args))))
	     (2 `(,operator
		  ,(make-index-typed (first args))
		  ,(make-index-typed (second args))))
	     (otherwise
	       `(,operator
		 ,(make-index-op operator (subseq args 0 (1- (length args))))
		 ,(make-index-typed (first (last args)))))))))

(defmacro index+ (&rest numbers) (make-index-op '+ numbers))
(defmacro index-logand (&rest numbers) (make-index-op 'logand numbers))
(defmacro index-logior (&rest numbers) (make-index-op 'logior numbers))
(defmacro index- (&rest numbers) (make-index-op '- numbers))
(defmacro index* (&rest numbers) (make-index-op '* numbers))

(defmacro index1+ (number) (make-index-op '1+ (list number)))
(defmacro index1- (number) (make-index-op '1- (list number)))

(defmacro index-incf (place &optional (delta 1))
  (make-index-op 'incf (list place delta)))
(defmacro index-decf (place &optional (delta 1))
  (make-index-op 'decf (list place delta)))

(defmacro index-min (&rest numbers) (make-index-op 'min numbers))
(defmacro index-max (&rest numbers) (make-index-op 'max numbers))

(defmacro index-floor (number divisor)
  (make-index-op 'floor (list number divisor)))
(defmacro index-ceiling (number divisor)
  (make-index-op 'ceiling (list number divisor)))
(defmacro index-truncate (number divisor)
  (make-index-op 'truncate (list number divisor)))

(defmacro index-mod (number divisor)
  (make-index-op 'mod (list number divisor)))

(defmacro index-ash (number count)
  (make-index-op 'ash (list number count)))

(defmacro index-plusp (number) `(plusp (the array-index ,number)))
(defmacro index-zerop (number) `(zerop (the array-index ,number)))
(defmacro index-evenp (number) `(evenp (the array-index ,number)))
(defmacro index-oddp  (number) `(oddp  (the array-index ,number)))

(defmacro index> (&rest numbers)
  `(> ,@(mapcar #'make-index-typed numbers)))
(defmacro index= (&rest numbers)
  `(= ,@(mapcar #'make-index-typed numbers)))
(defmacro index< (&rest numbers)
  `(< ,@(mapcar #'make-index-typed numbers)))
(defmacro index>= (&rest numbers)
  `(>= ,@(mapcar #'make-index-typed numbers)))
(defmacro index<= (&rest numbers)
  `(<= ,@(mapcar #'make-index-typed numbers)))

)

#+Genera
(progn

(defmacro index+ (&rest numbers) `(+ ,@numbers))
(defmacro index-logand (&rest numbers) `(logand ,@numbers))
(defmacro index-logior (&rest numbers) `(logior ,@numbers))
(defmacro index- (&rest numbers) `(- ,@numbers))
(defmacro index* (&rest numbers) `(* ,@numbers))

(defmacro index1+ (number) `(1+ ,number))
(defmacro index1- (number) `(1- ,number))

(defmacro index-incf (place &optional (delta 1)) `(setf ,place (index+ ,place ,delta)))
(defmacro index-decf (place &optional (delta 1)) `(setf ,place (index- ,place ,delta)))

(defmacro index-min (&rest numbers) `(min ,@numbers))
(defmacro index-max (&rest numbers) `(max ,@numbers))

(defun positive-power-of-two-p (x)
  (and (typep x 'fixnum) (plusp x) (zerop (logand x (1- x)))))

(defmacro index-floor (number divisor)
  (cond ((eql divisor 1) number)
	((and (positive-power-of-two-p divisor) (fboundp 'si:%fixnum-floor))
	 `(si:%fixnum-floor ,number ,divisor))
	(t `(floor ,number ,divisor))))

(defmacro index-ceiling (number divisor)
  (cond ((eql divisor 1) number)
	((and (positive-power-of-two-p divisor) (fboundp 'si:%fixnum-ceiling))
	 `(si:%fixnum-ceiling ,number ,divisor))
	(t `(ceiling ,number ,divisor))))

(defmacro index-truncate (number divisor)
  (cond ((eql divisor 1) number)
	((and (positive-power-of-two-p divisor) (fboundp 'si:%fixnum-floor))
	 `(si:%fixnum-floor ,number ,divisor))
	(t `(truncate ,number ,divisor))))

(defmacro index-mod (number divisor)
  (cond ((and (positive-power-of-two-p divisor) (fboundp 'si:%fixnum-mod))
	 `(si:%fixnum-mod ,number ,divisor))
	(t `(mod ,number ,divisor))))

(defmacro index-ash (number count)
  (cond ((eql count 0) number)
	((and (typep count 'fixnum) (minusp count) (fboundp 'si:%fixnum-floor))
	 `(si:%fixnum-floor ,number ,(expt 2 (- count))))
	((and (typep count 'fixnum) (plusp count) (fboundp 'si:%fixnum-multiply))
	 `(si:%fixnum-multiply ,number ,(expt 2 count)))
	(t `(ash ,number ,count))))

(defmacro index-plusp (number) `(plusp ,number))
(defmacro index-zerop (number) `(zerop ,number))
(defmacro index-evenp (number) `(evenp ,number))
(defmacro index-oddp  (number) `(oddp  ,number))

(defmacro index> (&rest numbers) `(> ,@numbers))
(defmacro index= (&rest numbers) `(= ,@numbers))
(defmacro index< (&rest numbers) `(< ,@numbers))
(defmacro index>= (&rest numbers) `(>= ,@numbers))
(defmacro index<= (&rest numbers) `(<= ,@numbers))

)

;;;; Stuff for BUFFER definition

(defconstant *replysize* 32.)

;; used in defstruct initializations to avoid compiler warnings
(defvar *empty-bytes* (make-sequence 'buffer-bytes 0))
(declaim (type buffer-bytes *empty-bytes*))
#+clx-overlapping-arrays
(progn
(defvar *empty-words* (make-sequence 'buffer-words 0))
(declaim (type buffer-words *empty-words*))
)
#+clx-overlapping-arrays
(progn
(defvar *empty-longs* (make-sequence 'buffer-longs 0))
(declaim (type buffer-longs *empty-longs*))
)

(defstruct (reply-buffer (:conc-name reply-) (:constructor make-reply-buffer-internal)
			 (:copier nil) (:predicate nil))
  (size 0 :type array-index)			;Buffer size
  ;; Byte (8 bit) input buffer
  (ibuf8 *empty-bytes* :type buffer-bytes)
  ;; Word (16bit) input buffer
  #+clx-overlapping-arrays
  (ibuf16 *empty-words* :type buffer-words)
  ;; Long (32bit) input buffer
  #+clx-overlapping-arrays
  (ibuf32 *empty-longs* :type buffer-longs)
  (next nil #-explorer :type #-explorer (or null reply-buffer))
  (data-size 0 :type array-index)
  )

(defconstant *buffer-text16-size* 256)
(deftype buffer-text16 () `(simple-array (unsigned-byte 16) (,*buffer-text16-size*)))

;; These are here because.

(defparameter *xlib-package* (find-package 'xlib))

(defun xintern (&rest parts)
  (intern (apply #'concatenate 'string (mapcar #'string parts)) *xlib-package*))

(defparameter *keyword-package* (find-package 'keyword))

(defun kintern (name)
  (intern (string name) *keyword-package*))

;;; Pseudo-class mechanism.

(defmacro def-clx-class ((name &rest options) &body slots)
  (let ((clos-package (or (find-package 'clos)
			  (find-package 'pcl)
			  (let ((lisp-pkg (find-package 'lisp)))
			    (and (find-symbol (string 'defclass) lisp-pkg)
				 lisp-pkg)))))
    (if clos-package
	(let ((constructor t)
	      (constructor-args t)
	      (include nil)
	      (print-function nil)
	      (copier t)
	      (predicate t))
	  (dolist (option options)
	    (ecase (pop option)
	      (:constructor
		(setf constructor (pop option))
		(setf constructor-args (if (null option) t (pop option))))
	      (:include
		(setf include (pop option)))
	      (:print-function
		(setf print-function (pop option)))
	      (:copier
		(setf copier (pop option)))
	      (:predicate
		(setf predicate (pop option)))))
	  (flet ((cintern (&rest symbols)
		   (intern (apply #'concatenate 'simple-string
				  (mapcar #'symbol-name symbols))
			   *package*))
		 (kintern (symbol)
		   (intern (symbol-name symbol) (find-package 'keyword)))
		 (closintern (symbol)
		   (intern (symbol-name symbol) clos-package)))
	    (when (eq constructor t)
	      (setf constructor (cintern 'make- name)))
	    (when (eq copier t)
	      (setf copier (cintern 'copy- name)))
	    (when (eq predicate t)
	      (setf predicate (cintern name '-p)))
	    (when include
	      (setf slots (append (get include 'def-clx-class) slots)))
	    (let* ((n-slots (length slots))
		   (slot-names (make-list n-slots))
		   (slot-initforms (make-list n-slots))
		   (slot-types (make-list n-slots)))
	      (dotimes (i n-slots)
		(let ((slot (elt slots i)))
		  (setf (elt slot-names i) (pop slot))
		  (setf (elt slot-initforms i) (pop slot))
		  (setf (elt slot-types i) (getf slot :type t))))
	      `(progn

		 (eval-when (compile load eval)
		   (setf (get ',name 'def-clx-class) ',slots))

		 ;; From here down are the system-specific expansions:

		 ,(cond (clos-package
			 `(within-definition (,name def-clx-class)
			    (,(closintern 'defclass)
			     ,name ,(and include `(,include))
			     (,@(map 'list
				     #'(lambda (slot-name slot-initform slot-type)
					 `(,slot-name
					   :initform ,slot-initform :type ,slot-type
					   :accessor ,(cintern name '- slot-name)
					   ,@(when (and constructor
							(or (eq constructor-args t)
							    (member slot-name
								    constructor-args)))
					       `(:initarg ,(kintern slot-name)))
					   ))
				     slot-names slot-initforms slot-types)))
			    ,(when constructor
			       (if (eq constructor-args t)
				   `(defun ,constructor (&rest args)
				      (apply #',(closintern 'make-instance)
					     ',name args))
				   `(defun ,constructor ,constructor-args
				      (,(closintern 'make-instance) ',name
				       ,@(mapcan #'(lambda (slot-name)
						     (and (member slot-name slot-names)
							  `(,(kintern slot-name) ,slot-name)))
						 constructor-args)))))
			    ,(when predicate
			       `(defun ,predicate (object)
				  (typep object ',name)))
			    ,(when copier
			       `(,(closintern 'defmethod) ,copier ((.object. ,name))
				 (,(closintern 'with-slots) ,slot-names .object.
				  (,(closintern 'make-instance) ',name
				   ,@(mapcan #'(lambda (slot-name)
						 `(,(kintern slot-name) ,slot-name))
					     slot-names)))))
			    ,(when print-function
			       `(,(closintern 'defmethod)
				 ,(closintern 'print-object)
				 ((object ,name) stream)
				 (,print-function object stream 0)))))

			#+Genera
			(t
			 `(within-definition (,name def-clx-class)
			    (flavor:defflavor ,name
				    (,@(map 'list
					    #'(lambda (slot-name slot-initform)
						`(,slot-name ,slot-initform))
					    slot-names slot-initforms))
				    ,(and include `(,include))
			      :initable-instance-variables
			      :locatable-instance-variables
			      :readable-instance-variables
			      :writable-instance-variables
			      ,(if constructor
				   `(:constructor ,constructor
				     ,(if (eq constructor-args t)
					  `(&key ,@slot-names)
					  constructor-args))
				   :abstract-flavor))
			    ,(when predicate
			       `(defun ,predicate (object)
				  (typep object ',name)))
			    ,(when copier
			       (error ":COPIER not supported."))
			    ,(when print-function
			       `(flavor:defmethod (sys:print-self ,name)
						  (stream depth *print-escape*)
				  (,print-function sys:self stream depth)))
			    (flavor:compile-flavor-methods ,name))))))))
	`(within-definition (,name def-clx-class)
	   (defstruct (,name ,@options)
	     ,@slots)))))

#+Genera
(progn
  (scl:defprop def-clx-class "CLX Class" si:definition-type-name)
  (scl:defprop def-clx-class zwei:defselect-function-spec-finder
	       zwei:definition-function-spec-finder))


;; We need this here so we can define DISPLAY for CLX.
;;
;; This structure is :INCLUDEd in the DISPLAY structure.
;; Overlapping (displaced) arrays are provided for byte
;; half-word and word access on both input and output.
;;
(def-clx-class (buffer (:constructor nil) (:copier nil) (:predicate nil))
  ;; Lock for multi-processing systems
  (lock (make-process-lock "CLX Buffer Lock"))
  #-excl (output-stream nil :type (or null stream))
  #+excl (output-stream -1 :type fixnum)
  ;; Buffer size
  (size 0 :type array-index)
  (request-number 0 :type (unsigned-byte 16))
  ;; Byte position of start of last request
  ;; used for appending requests and error recovery
  (last-request nil :type (or null array-index))
  ;; Byte position of start of last flushed request
  (last-flushed-request nil :type (or null array-index))
  ;; Current byte offset
  (boffset 0 :type array-index)
  ;; Byte (8 bit) output buffer
  (obuf8 *empty-bytes* :type buffer-bytes)
  ;; Word (16bit) output buffer
  #+clx-overlapping-arrays
  (obuf16 *empty-words* :type buffer-words)
  ;; Long (32bit) output buffer
  #+clx-overlapping-arrays
  (obuf32 *empty-longs* :type buffer-longs)
  ;; Holding buffer for 16-bit text
  (tbuf16 (make-sequence 'buffer-text16 *buffer-text16-size* :initial-element 0))
  ;; Probably EQ to Output-Stream
  #-excl (input-stream nil :type (or null stream))
  #+excl (input-stream -1 :type fixnum)
  ;; T when the host connection has gotten errors
  (dead nil :type (or null (not null)))
  ;; T makes buffer-flush a noop.  Manipulated with with-buffer-flush-inhibited.
  (flush-inhibit nil :type (or null (not null)))
  
  ;; Change these functions when using shared memory buffers to the server
  ;; Function to call when writing the buffer
  (write-function 'buffer-write-default)
  ;; Function to call when flushing the buffer
  (force-output-function 'buffer-force-output-default)
  ;; Function to call when closing a connection
  (close-function 'buffer-close-default)
  ;; Function to call when reading the buffer
  (input-function 'buffer-read-default)
  ;; Function to call to wait for data to be input
  (input-wait-function 'buffer-input-wait-default)
  ;; Function to call to listen for input data
  (listen-function 'buffer-listen-default)

  #+Genera (debug-io nil :type (or null stream))
  ) 

;;-----------------------------------------------------------------------------
;; Printing routines.
;;-----------------------------------------------------------------------------

#+(and (not ansi-common-lisp) Genera)
(import 'future-common-lisp:print-unreadable-object)

#-(or ansi-common-lisp Genera)
(defun print-unreadable-object-function (object stream type identity function)
  (princ "#<" stream)
  (when type
    (let ((type (type-of object))
	  (pcl-package (find-package 'pcl)))
      ;; Handle pcl type-of lossage
      (when (and pcl-package
		 (symbolp type)
		 (eq (symbol-package type) pcl-package)
		 (string-equal (symbol-name type) "STD-INSTANCE"))
	(setq type
	      (funcall (intern (symbol-name 'class-name) pcl-package)
		       (funcall (intern (symbol-name 'class-of) pcl-package)
				object))))
      (prin1 type stream)))
  (when (and type function) (princ " " stream))
  (when function (funcall function))
  (when (and (or type function) identity) (princ " " stream))
  (when identity (princ "???" stream))
  (princ ">" stream)
  nil)
  
#-(or ansi-common-lisp Genera)
(defmacro print-unreadable-object
	  ((object stream &key type identity) &body body)
  `(print-unreadable-object-function
     ,object ,stream ,type ,identity
     ,(and body `#'(lambda () ,@body))))


;;-----------------------------------------------------------------------------
;; Image stuff
;;-----------------------------------------------------------------------------

(defconstant *image-bit-lsb-first-p*
	     #+clx-little-endian t
	     #-clx-little-endian nil)

(defconstant *image-byte-lsb-first-p*
	     #+clx-little-endian t
	     #-clx-little-endian nil)

(defconstant *image-unit* 32)

(defconstant *image-pad* 32)
