;;; -*- Mode:Common-Lisp; Package:X11; Base:10; Fonts:(MEDFNB HL12B HL12BI) -*-

#|
/***********************************************************
Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts,
and the Massachusetts Institute of Technology, Cambridge, Massachusetts.

                        All Rights Reserved

Permission to use, copy, modify, and distribute this software and its 
documentation for any purpose and without fee is hereby granted, 
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in 
supporting documentation, and that the names of Digital or MIT not be
used in advertising or publicity pertaining to distribution of the
software without specific, written prior permission.  

DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
SOFTWARE.

******************************************************************/
/* $Header: qvss_io.c,v 1.51 87/06/10 20:05:50 sue Exp $ */
|#

(DEFPARAMETER QUEUE NIL
  "Queue for mouse information.")

(DEFPARAMETER QLIMIT NIL
  "Maximum number of mouse events that can be store in QUEUE.")

(DEFPARAMETER SCREEN-BITMAP NIL
  "Bitmap that points to the real screen.")

(DEFSTRUCT (MOUSE-INFO (:CONC-NAME "MOUSE-INFO."))
  ;; (X,Y) hotspot for the mouse.
  (HOT-X 0 :TYPE INTEGER)
  (HOT-Y 0 :TYPE INTEGER)
  (MAX-CUR-X 0 :TYPE INTEGER)
  (MAX-CUR-Y 0 :TYPE INTEGER)

  ;; Temporary area for the mouse cursor bits.
  (CURSOR-BITS (MAKE-ARRAY `(,TV:*MICROCODE-MOUSE-CURSOR-BUFFER-HEIGHT*
                                              ,TV:*MICROCODE-MOUSE-CURSOR-BUFFER-WIDTH*)
                                            :ELEMENT-TYPE 'BIT
                                            :INITIAL-ELEMENT 0)
               :TYPE (ARRAY BIT 2))
  (BOX (MAKE-BOX :LEFT 0 :TOP 0 :RIGHT 0 :BOTTOM 0)
       :TYPE BOX))

(DEFPARAMETER MOUSE-DATA (MAKE-MOUSE-INFO))

(DEFPARAMETER CONSTRAINT-BOX (MAKE-BOX :LEFT 0 :TOP 0 :RIGHT 0 :BOTTOM 0))

Bool qvSetCursorPosition();

extern void miRecolorCursor();
extern int  qvGetMotionEvents();
extern void qvChangePointerControl(), qvChangeKeyboardControl(), qvBell();

extern int errno;

static struct qv_info	*qvInfo;
static vsBox		*mbox;
static vsCursor		*mouse;
static int		fdQVSS;
static int		lastEventTime;
static DevicePtr	qvKeyboard;
static DevicePtr	qvPointer;

#define MAX_LED 4

(DEFUN LISP-SAVE-SCREEN (SCREEN ON)
  (DECLARE (IGNORE SCREEN)
           (TYPE SCREEN SCREEN)
           (TYPE SCREEN-SAVER-TYPE ON)
           (VALUES BOOLEAN))
  (IF (= ON SCREEN-SAVER-FORCER)
      (PROGN
        (SETQ *LAST-EVENT-TIME* (GET-TIME-IN-MILLIS))
        T)
      ;;1ELSE*
      NIL))

(DEFUN LISP-SCREEN-CLOSE (INDEX SCREEN)
  (DECLARE (IGNORE SCREEN INDEX)
           (TYPE INTEGER INDEX)
           (TYPE SCREEN SCREEN)
           (VALUES BOOLEAN))
  T)


(DEFUN LISP-SCREEN-INIT (INDEX SCREEN ARGC ARGV)
  (DECLARE (IGNORE ARGC ARGV)
           (TYPE INTEGER INDEX)
           (TYPE SCREEN SCREEN)
           (TYPE INTEGER ARGC)
           (TYPE ARRAY ARGV)
           (VALUES BOOLEAN))

  ;; We will assume that we can always open the mouse device.  We also don't
  ;; need a box for the mouse since the variables
  ;; TV:*MICROCODE-MOUSE-CURSOR-BUFFER-HEIGHT* and
  ;; TV:*MICROCODE-MOUSE-CURSOR-BUFFER-WIDTH* contain this information.  We
  ;; also don't need a pointer to the mouse cursor since the variable
  ;; SYS:MOUSE-CURSOR-PATTERN contains the bits for the mouse.
  (SETQ QUEUE SI:MOUSE-BUTTONS-BUFFER)
  (SETQ QLIMIT (/ (LENGTH QUEUE) 4))

  (WHEN (NULL SCREEN-BITMAP)
    (SETQ SCREEN-BITMAP (TV:SHEET-SCREEN-ARRAY X-SCREEN)))
  (PROG1
    (WHEN (FBOUNDP 'MFB-SCREEN-INIT)
      (MFB-SCREEN-INIT INDEX SCREEN SCREEN-BITMAP X-SCREEN-WIDTH X-SCREEN-HEIGHT
                       ;; Pixels per inch.  Calculate this by averaging the horizontal PPI
                       ;; with the vertical PPI.
                       (ROUND (+ 86.87 90.14) 2)))
    (SETF (SCREEN-CLOSE-SCREEN             SCREEN) 'LISP-SCREEN-CLOSE)
    (SETF (SCREEN-QUERY-BEST-SIZE          SCREEN) 'LISP-QUERY-BEST-SIZE)
    (SETF (SCREEN-SAVE-SCREEN              SCREEN) 'LISP-SAVE-SCREEN)
    (SETF (SCREEN-POINTER-NON-INTEREST-BOX SCREEN) 'LISP-POINTER-NON-INTEREST-BOX)
    (SETF (SCREEN-CONSTRAIN-CURSOR         SCREEN) 'LISP-CONSTRAIN-CURSOR)
    (SETF (SCREEN-CURSOR-LIMITS            SCREEN) 'LISP-CURSOR-LIMITS)
    (SETF (SCREEN-DISPLAY-CURSOR           SCREEN) 'LISP-DISPLAY-CURSOR)
    (SETF (SCREEN-REALIZE-CURSOR           SCREEN) 'LISP-REALIZE-CURSOR)
    (SETF (SCREEN-UNREALIZE-CURSOR         SCREEN) 'LISP-UNREALIZE-CURSOR)
    ;; The recolor-cursor function is defined in another file.
    (WHEN (FBOUNDP 'LISP-RECOLOR-CURSOR)
      (SETF (SCREEN-RECOLOR-CURSOR           SCREEN) 'LISP-RECOLOR-CURSOR))
    (SETF (SCREEN-SET-CURSOR-POSITION      SCREEN) 'LISP-SET-CURSOR-POSITION)))

#|
I now realize that there is too much code between here and where the cursor
code is.  I am commenting all this stuff out for now.  It will need to be
translated later.

int
qvssMouseProc(pDev, onoff, argc, argv)
    DevicePtr pDev;
    int onoff, argc;
    char *argv[];
{
    int     i;
    BYTE map[4];

    switch (onoff)
    {
	case DEVICE_INIT: 
	    qvPointer = pDev;
	    pDev->devicePrivate = (pointer) &queue;
	    map[1] = 1;
	    map[2] = 2;
	    map[3] = 3;
	    InitPointerDeviceStruct(
		qvPointer, map, 3, qvGetMotionEvents, qvChangePointerControl);
	    SetInputCheck(&queue->head, &queue->tail);
	    break;
	case DEVICE_ON: 
	    pDev->on = TRUE;
            hotX = hotY = 0;
	    AddEnabledDevice(fdQVSS);
	    break;
	case DEVICE_OFF: 
	    pDev->on = FALSE;
/*	RemoveEnabledDevice(fdQVSS);   */
	    break;
	case DEVICE_CLOSE: 
	    break;
    }
    return Success;

}

#define LK_REPEAT_ON  0xe3
#define LK_REPEAT_OFF 0xe1

int
SetLKAutoRepeat (onoff)
    Bool onoff;
{
    extern char *AutoRepeatLKMode();
    extern char *UpDownLKMode();
    
    struct qv_kpcmd ioc; 
    register char  *divsets;
    divsets = onoff ? (char *) AutoRepeatLKMode() : (char *) UpDownLKMode();

    ioc.nbytes = 0;
    while (ioc.cmd = *divsets++)
	ioctl(fdQVSS, QIOCKPCMD, &ioc);
    ioc.cmd = ((onoff > 0) ? LK_REPEAT_ON : LK_REPEAT_OFF);
    return(ioctl(fdQVSS, QIOCKPCMD, &ioc));

}

int
qvssKeybdProc(pDev, onoff, argc, argv)
    DevicePtr pDev;
    int onoff, argc;
    char *argv[];
{
    BYTE map[MAP_LENGTH];
    KeySymsRec keySyms;
    ModifierMapRec modMap;

    switch (onoff)
    {
	case DEVICE_INIT: 
	    qvKeyboard = pDev;
	    pDev->devicePrivate = (pointer) & queue;
	    GetLK201Mappings( &keySyms, &modMap);
	    InitKeyboardDeviceStruct(
		    qvKeyboard, &keySyms, &modMap, qvBell,
		    qvChangeKeyboardControl);
            Xfree(keySyms.map);
	    SetLKAutoRepeat(FALSE);
	    break;
	case DEVICE_ON: 
	    pDev->on = TRUE;
	    AddEnabledDevice(fdQVSS);
	    break;
	case DEVICE_OFF: 
	    pDev->on = FALSE;
/*	    RemoveEnabledDevice(fdQVSS);  */
	    break;
	case DEVICE_CLOSE: 
	    break;
    }
    return Success;
}


/*****************
 * ProcessInputEvents:
 *    processes all the pending input events
 *****************/

extern int screenIsSaved;

void
ProcessInputEvents()
{
#define DEVICE_KEYBOARD 2
    register int    i;
    register    vsEvent * pE;
    xEvent	x;
    int     nowInCentiSecs, nowInMilliSecs, adjustCentiSecs;
    struct timeval  tp;
    int     needTime = 1;

    i = queue->head;
    while (i != queue->tail)
    {
	if (screenIsSaved == SCREEN_SAVER_ON)
	    SaveScreens(SCREEN_SAVER_OFF, ScreenSaverReset);
	pE = &queue->events[i];
	x.u.keyButtonPointer.rootX = pE->vse_x + hotX;
	x.u.keyButtonPointer.rootY = pE->vse_y + hotY;
    /* 
     * The following silly looking code is because the old version of the
     * driver only delivers 16 bits worth of centiseconds. We are supposed
     * to be keeping time in terms of 32 bits of milliseconds.
     */
	if (needTime)
	{
	    needTime = 0;
	    gettimeofday(&tp, 0);
	    nowInCentiSecs = ((tp.tv_sec * 100) + (tp.tv_usec / 10000)) & 0xFFFF;
	/* same as driver */
	    nowInMilliSecs = (tp.tv_sec * 1000) + (tp.tv_usec / 1000);
	/* beware overflow */
	}
	if ((adjustCentiSecs = nowInCentiSecs - pE->vse_time) < -20000)
	    adjustCentiSecs += 0x10000;
	else
	    if (adjustCentiSecs > 20000)
		adjustCentiSecs -= 0x10000;
	x.u.keyButtonPointer.time = lastEventTime =
	    nowInMilliSecs - adjustCentiSecs * 10;

	if ((pE->vse_type == VSE_BUTTON) &&
	    (pE->vse_device == DEVICE_KEYBOARD))
	{					/* better be a button */
	    x.u.u.detail = pE->vse_key;
	    switch (pE->vse_direction)
	    {
		case VSE_KBTDOWN: 
		    x.u.u.type = KeyPress;
		    (qvKeyboard->processInputProc)(&x, qvKeyboard);
		    break;
		case VSE_KBTUP: 
		    x.u.u.type = KeyRelease;
		    (qvKeyboard->processInputProc)(&x, qvKeyboard);
		    break;
		default: 	       /* hopefully BUTTON_RAW_TYPE */
		    ProcessLK201Input(&x, qvKeyboard);
	    }
	}
	else
	{
	    if (pE->vse_type == VSE_BUTTON)
	    {
		if (pE->vse_direction == VSE_KBTDOWN)
		    x.u.u.type = ButtonPress;
		else
		    x.u.u.type = ButtonRelease;
		/* mouse buttons numbered from one */
		x.u.u.detail = pE->vse_key + 1;
	    }
	    else
		x.u.u.type = MotionNotify;
	(*qvPointer->processInputProc)(&x, qvPointer);
	}

	if (i == qLimit)
	    i = queue->head = 0;
	else
	    i = ++queue->head;
    }
#undef DEVICE_KEYBOARD
}

TimeSinceLastInputEvent()
{
    if (lastEventTime == 0)
	lastEventTime = GetTimeInMillis();
    return GetTimeInMillis() - lastEventTime;
}

static void
qvBell(loud, pDevice)
    int loud;
    DevicePtr pDevice;
{
#define LK_ENABLE_BELL 0x23	/* enable bell / set volume 	*/
    struct qv_kpcmd ioc;

/* the lk201 volume is between 7 (quiet but audible) and 0 (loud) */
    loud = 7 - ((loud / 14) & 7);
    ioc.nbytes = 1;
    ioc.cmd = LK_ENABLE_BELL;
    ioc.par[0] = loud;
    ioctl(fdQVSS, QIOCKPCMD, &ioc);

    ioc.nbytes = 0;
    ioc.cmd = LK_RING_BELL;
    ioctl(fdQVSS, QIOCKPCMD, &ioc);
}

static void
ChangeLED(led, on)
    int led;
    Bool on ;
{
    struct qv_kpcmd ioc;

    if (led > MAX_LED)    
        return;
    if (on)
        ioc.cmd = LK_LED_ENABLE;
    else
        ioc.cmd = LK_LED_DISABLE;
    if (led == 1)
	ioc.par[0] = LED_1;
    else if (led == 2)
        ioc.par[0] = LED_2;
    else if (led == 3)
        ioc.par[0] = LED_3;
    else if (led == 4)
        ioc.par[0] = LED_4;
    ioc.par[1]  = 0;
    ioc.nbytes = 1;
    ioctl(fdQVSS, QIOCKPCMD, &ioc);
}


static void
qvChangeKeyboardControl(pDevice, ctrl)
    DevicePtr pDevice;
    KeybdCtrl *ctrl;
{
#define LK_ENABLE_CLICK 0x1b	/* enable keyclick / set volume	*/
#define LK_DISABLE_CLICK 0x99	/* disable keyclick entirely	*/
#define LK_ENABLE_BELL 0x23	/* enable bell / set volume 	*/

    struct qv_kpcmd ioc;
    int i;

    if (ctrl->click == 0)    /* turn click off */
    {
	ioc.nbytes = 0;
	ioc.cmd = LK_DISABLE_CLICK;
	ioctl(fdQVSS, QIOCKPCMD, &ioc);
    }
    else 
    {
        int volume = ctrl->click;
	volume -= 1;
	if (volume > 7) volume = 7;
	volume = 7 - volume;
	ioc.nbytes = 1;
	ioc.cmd = LK_ENABLE_CLICK;
	ioc.par[0] = volume;
	ioctl(fdQVSS, QIOCKPCMD, &ioc);
    }

    /* ctrl->bell: the DIX layer handles the base volume for the bell */
    
    /* ctrl->bell_pitch: as far as I can tell, you can't set this on lk201 */

    /* ctrl->bell_duration: as far as I can tell, you can't set this  */

    /* LEDs */
    for (i=1; i<=MAX_LED; i++)
        ChangeLED(i, (ctrl->leds & (1 << (i-1))));

    /* ctrl->autoRepeat: I'm turning it all on or all off.  */

    SetLKAutoRepeat(ctrl->autoRepeat);
}

static void
qvChangePointerControl(pDevice, ctrl)
    DevicePtr pDevice;
    PtrCtrl   *ctrl;
{
    qvInfo->mthreshold = ctrl->threshold;
    if (!(qvInfo->mscale = ctrl->num / ctrl->den))
	qvInfo->mscale = 1;	/* watch for den > num */
}

static int
qvGetMotionEvents(buff, start, stop)
    CARD32 start, stop;
    xTimecoord *buff;
{
    return 0;
}

|#


(DEFUN LISP-SET-CURSOR-POSITION (SCREEN NEW-X NEW-Y)
  (DECLARE (TYPE SCREEN SCREEN)
           (TYPE INTEGER NEW-X NEW-Y)
           (IGNORE SCREEN)
           (VALUES BOOLEAN))
  (SETQ SYS:MOUSE-X (- NEW-X (MOUSE-INFO.HOT-X MOUSE-INFO))
        SYS:MOUSE-Y (- NEW-Y (MOUSE-INFO.HOT-Y MOUSE-DATA)))
  T)

(DEFUN LISP-DISPLAY-CURSOR (SCREEN CURSOR X Y)
  (DECLARE (TYPE SCREEN SCREEN)
           (TYPE CURSOR-RECORD CURSOR)
           (TYPE INTEGER X Y)
           (VALUES BOOLEAN))
  ;; Load the cursor.
  (WHEN (OR (NOT (= (MOUSE-INFO.HOT-X MOUSE-DATA) (CURSOR-RECORD.XHOT CURSOR)))
            (NOT (= (MOUSE-INFO.HOT-Y MOUSE-DATA) (CURSOR-RECORD.YHOT CURSOR))))
    (SETF (MOUSE-INFO.HOT-X MOUSE-DATA) (CURSOR-RECORD.XHOT CURSOR))
    (SETF (MOUSE-INFO.HOT-Y MOUSE-DATA) (CURSOR-RECORD.YHOT CURSOR))
    (LISP-SET-CURSOR-POSITION SCREEN X Y)
    (LISP-CONSTRAIN-CURSOR SCREEN CONSTRAINT-BOX))
  ;; Copy the bits from the server into a temporary bit array.
  (LET ((SERVER-IMAGE-BITS   (CURSOR-RECORD.SOURCE CURSOR))
        (CURSOR-BITS (MOUSE-INFO.CURSOR-BITS MOUSE-DATA))
        (COLUMN-MINIMUM (MIN (CURSOR-RECORD.WIDTH  CURSOR)
                             TV:*MICROCODE-MOUSE-CURSOR-BUFFER-WIDTH*)))
    (DOTIMES (ROW (MIN (CURSOR-RECORD.HEIGHT CURSOR) TV:*MICROCODE-MOUSE-CURSOR-BUFFER-HEIGHT*))
      (DOTIMES (COL COLUMN-MINIMUM)
        (SETF (AREF CURSOR-BITS ROW COL) (AREF SERVER-IMAGE-BITS ROW COL)))))
    T)

(DEFUN LISP-POINTER-NON-INTEREST-BOX (SCREEN BOX)
  (DECLARE (TYPE SCREEN SCREEN)
           (TYPE BOX BOX)
           (IGNORE SCREEN)
           (VALUES NIL))
  (LET ((MOUSE-BOX (MOUSE-INFO.BOX MOUSE-DATA)))
    (SETF (BOX.LEFT   MOUSE-BOX) (BOX.LEFT   BOX))
    (SETF (BOX.TOP    MOUSE-BOX) (BOX.TOP    BOX))
    (SETF (BOX.RIGHT  MOUSE-BOX) (BOX.RIGHT  BOX))
    (SETF (BOX.BOTTOM MOUSE-BOX) (BOX.BOTTOM BOX))))

/* note that this driver does not support dealing with the minimums */
(DEFUN LISP-CONSTRAIN-CURSOR (SCREEN BOX)
  (DECLARE (TYPE SCREEN SCREEN)
           (TYPE BOX BOX)
           (IGNORE SCREEN)
           (VALUES NIL))
  (SETQ MOUSE.MAX-CUR-X (- (BOX.RIGHT  BOX) MOUSE.HOT-X)
        MOUSE.MAX-CUR-Y (- (BOX.BOTTOM BOX) MOUSE.HOT-Y)))

;;; qv cursor top-left corner cannot go to negative coordinates.
(DEFUN LISP-CURSOR-LIMITS (SCREEN CURSOR HOTBOX PHYSICAL-BOX)
  (DECLARE (TYPE SCREEN SCREEN)
           (TYPE CURSOR-RECORD CURSOR)
           (TYPE BOX HOTBOX PHYSICAL-BOX)
           (IGNORE SCREEN)
           (VALUES NIL))
  ;; Actually PHYSICAL-BOX is the return value.
  (SETF (BOX.LEFT   PHYSICAL-BOX) (MAX (BOX.LEFT   HOTBOX) (CURSOR-RECORD.XHOT CURSOR)))
  (SETF (BOX.TOP    PHYSICAL-BOX) (MAX (BOX.TOP    HOTBOX) (CURSOR-RECORD.YHOT CURSOR)))
  (SETF (BOX.RIGHT  PHYSICAL-BOX) (MIN (BOX.RIGHT  HOTBOX) (1- X-SCREEN-WIDTH)))
  (SETF (BOX.BOTTOM PHYSICAL-BOX) (MIN (BOX.BOTTOM HOTBOX) (1- X-SCREEN-HEIGHT))))

(DEFUN LISP-REALIZE-CURSOR (SCREEN CURSOR)
  (DECLARE (TYPE SCREEN SCREEN)
           (TYPE CURSOR-RECORD CURSOR)
           (IGNORE SCREEN)
           (VALUES BOOLEAN))
  (LET ((SERVER-IMAGE-BITS   (CURSOR-RECORD.SOURCE CURSOR))
        (SERVER-MASK-BITS    (CURSOR-RECORD.MASK   CURSOR))
        (COLUMN-MINIMUM (MIN (CURSOR-RECORD.WIDTH  CURSOR)
                             TV:*MICROCODE-MOUSE-CURSOR-BUFFER-WIDTH*)))
    (SETF (CURSOR-RECORD.DEV-PRIVATE CURSOR) TV:MOUSE-CURSOR-PATTERN)

    ;; Munge the SERVER-DEPENDENT, device-independent cursor bits into
    ;; what the device wants, which is a 2-D array of bits.
    (DOTIMES (ROW (MIN (CURSOR-RECORD.HEIGHT CURSOR) TV:*MICROCODE-MOUSE-CURSOR-BUFFER-HEIGHT*))
      (DOTIMES (COL COLUMN-MINIMUM)
        (SETF (AREF TV:MOUSE-CURSOR-PATTERN ROW COL) (LOGAND (IF SERVER-MASK-BITS
                                                                 (AREF SERVER-MASK-BITS ROW COL)
                                                                 ;;1ELSE*
                                                                 1)
                                                             (AREF SERVER-IMAGE-BITS ROW COL))))))
  T)

(DEFUN LISP-UNREALIZE-CURSOR (SCREEN CURSOR)
  (DECLARE (TYPE SCREEN SCREEN)
           (TYPE CURSOR-RECORD CURSOR)
           (IGNORE SCREEN CURSOR)
           (VALUES BOOLEAN))
  ;; We don't need to do anything since the garbage collector
  ;; cleans us up automatically.
  T)

(DEFUN LISP-QUERY-BEST-SIZE (CLASS WIDTH HEIGHT)
  (DECLARE (TYPE CURSOR-TYPE CLASS)
           (TYPE INTEGER WIDTH HEIGHT)
           (VALUES WIDTH HEIGHT))
  (CASE CLASS
    (CURSOR-SHAPE
     (VALUES 16 16))
    ((TILE-SHAPE STIPPLE-SHAPE)
     (VALUES (ASH 2 (IF (= (INTEGER-LENGTH WIDTH)
                           (INTEGER-LENGTH (1- WIDTH)))
                        (INTEGER-LENGTH WIDTH)
                        (1+ (INTEGER-LENGTH WIDTH))))
             HEIGHT))))