-- XMKDriverImpl.mesa -- Created By Jeff Weinstein on 29-Mar-87 17:16:06 DIRECTORY BitBlt, Keys, Inline, Process, Runtime, System, UserTerminal, XDefs, XEventQ, XMKDriver; XMKDriverImpl:PROGRAM IMPORTS Inline, Process, Runtime, System, UserTerminal, XEventQ EXPORTS XMKDriver = BEGIN --Types buttonState:TYPE = {down, up, waitingRelease, waitingChord}; buttons: TYPE = MACHINE DEPENDENT {left(1), middle(2), right(3)}; -- Keyboard vars lastKeys, newKeys:Keys.KeyBits ← ALL[up]; keyboard: LONG POINTER TO Keys.KeyBits = LOOPHOLE[UserTerminal.keyboard]; keyboardEventsEnabled:BOOLEAN ← FALSE; -- Mouse vars lastMouse, newMouse:UserTerminal.Coordinate; mouseConstraintBox:XMKDriver.Box ← [0, 0, UserTerminal.screenWidth-1, UserTerminal.screenHeight-1]; mouseNoInterestBox:XMKDriver.Box ← [UserTerminal.screenWidth, UserTerminal.screenHeight,UserTerminal.screenWidth, UserTerminal.screenHeight]; mouseEventsEnabled:BOOLEAN ← FALSE; -- Button vars buttonStates:ARRAY buttons OF buttonState ← ALL[up]; chordFlag:BOOLEAN ← FALSE; buttonDownTime:System.Pulses; buttonDownCoord:UserTerminal.Coordinate; chordCounter:INTEGER ← -1; -- Misc Vars bitMask:ARRAY [0..7] OF INTEGER ← [128, 64, 32, 16, 8, 4, 2, 1]; time:System.Pulses; lastEventTime:System.Pulses ← [0]; XMKDriverProc:PUBLIC PROCEDURE = BEGIN Process.Detach[Process.GetCurrent[]]; Process.SetPriority[Process.priorityForeground]; DO --Give everyone else a chance to run... UserTerminal.WaitForScanLine[0]; --Read the new keyboard and mouse values, constraining the mouse position newKeys ← keyboard↑; newMouse ← ConstrainMouse[UserTerminal.mouse↑]; --Get the time in clock pulses time ← System.GetClockPulses[]; --Set the mouse and cursor to the constrained position UserTerminal.SetMousePosition[newMouse]; UserTerminal.SetCursorPosition[newMouse]; --Find the events and queue them up IF keyboardEventsEnabled THEN processKeys[]; IF mouseEventsEnabled THEN BEGIN processMouse[]; processButtons[]; END; --Save the new state lastKeys ← newKeys; lastMouse ← newMouse; ENDLOOP; END; ConstrainMouse:PROCEDURE[inCoord:UserTerminal.Coordinate] RETURNS[outCoord:UserTerminal.Coordinate] = BEGIN outCoord.x ← SELECT inCoord.x FROM <mouseConstraintBox.x1 => mouseConstraintBox.x1, >mouseConstraintBox.x2 => mouseConstraintBox.x2, ENDCASE => inCoord.x; outCoord.y ← SELECT inCoord.y FROM <mouseConstraintBox.y1 => mouseConstraintBox.y1, >mouseConstraintBox.y2 => mouseConstraintBox.y2, ENDCASE => inCoord.y; END; processMouse:PROCEDURE = BEGIN event:XEventQ.EventPtr; IF newMouse # lastMouse THEN BEGIN IF NOT ((newMouse.x IN [mouseNoInterestBox.x1..mouseNoInterestBox.x2]) AND (newMouse.y IN [mouseNoInterestBox.y1..mouseNoInterestBox.y2]))THEN BEGIN lastEventTime ← time; event ← XEventQ.NewEvent[]; event↑ ← [x:newMouse.x, y:newMouse.y, time:time, type:MotionNotify, key:0]; XEventQ.EnQEvent[event]; END; END; END; processKeys:PROCEDURE = BEGIN size:INTEGER = SIZE[Keys.KeyBits]; newPtr:LONG POINTER TO ARRAY[0..size) OF WORD; lastPtr:LONG POINTER TO ARRAY[0..size) OF WORD; nP:LONG POINTER TO Keys.KeyBits ← @newKeys; lp:LONG POINTER TO Keys.KeyBits ← @lastKeys; tmpKeys:ARRAY[0..size) OF WORD; tP:LONG POINTER TO ARRAY[0..size) OF WORD ← @tmpKeys; tmpPtr:LONG POINTER TO Keys.KeyBits ← LOOPHOLE[tP]; loByte, hiByte:INTEGER; newPtr ← LOOPHOLE[ nP ]; lastPtr ← LOOPHOLE [ lp ]; FOR i:INTEGER IN [0..size) DO tmpKeys[i] ← Inline.BITXOR[newPtr[i], lastPtr[i]]; ENDLOOP; tmpPtr[Point] ← down; tmpPtr[Adjust] ← down; FOR i:INTEGER IN [0..size) DO IF tmpKeys[i] # 0 THEN BEGIN hiByte ← Inline.HighByte[tmpKeys[i]]; loByte ← Inline.LowByte[tmpKeys[i]]; IF hiByte # 0 THEN FOR j:INTEGER IN [0..7] DO IF Inline.BITAND[hiByte, bitMask[j]] # 0 THEN sendKeyEvent[i*16+j]; ENDLOOP; IF loByte # 0 THEN FOR j:INTEGER IN [0..7] DO IF Inline.BITAND[loByte, bitMask[j]] # 0 THEN BEGIN sendKeyEvent[i*16+8+j]; IF i*16+8+j = 77 THEN Runtime.Interrupt; END ENDLOOP; END; ENDLOOP; END; sendKeyEvent:PROCEDURE[key:INTEGER] = BEGIN event:XEventQ.EventPtr; type:XDefs.XEventType ← SELECT newKeys[LOOPHOLE[key]] FROM up => KeyRelease, down => KeyPress, ENDCASE => ERROR; event ← XEventQ.NewEvent[]; event↑ ← [x:newMouse.x, y:newMouse.y, time:time, type:type, key:key]; XEventQ.EnQEvent[event]; lastEventTime ← time; END; processButtonDown:PROCEDURE[button:buttons] = BEGIN event:XEventQ.EventPtr; otherButton:buttons = SELECT button FROM left => right, right => left, ENDCASE => ERROR; SELECT buttonStates[otherButton] FROM waitingChord => BEGIN event ← XEventQ.NewEvent[]; event↑ ← [x:newMouse.x, y:newMouse.y, time:time, type:ButtonPress, key:LOOPHOLE[buttons.middle]]; XEventQ.EnQEvent[event]; lastEventTime ← time; chordFlag ← TRUE; chordCounter ← -1; buttonStates[button] ← down; buttonStates[otherButton] ← down; END; down, waitingRelease => BEGIN event ← XEventQ.NewEvent[]; event↑ ← [x:newMouse.x, y:newMouse.y, time:time, type:ButtonPress, key:LOOPHOLE[button]]; XEventQ.EnQEvent[event]; lastEventTime ← time; buttonStates[button] ← down; END; up => BEGIN buttonStates[button] ← waitingChord; buttonDownTime ← time; buttonDownCoord ← newMouse; chordCounter ← 5; END; ENDCASE => ERROR; END; processButtons:PROCEDURE = BEGIN IF newKeys[Point] # lastKeys[Point] THEN BEGIN SELECT newKeys[Point] FROM up => processButtonUp[left]; down => processButtonDown[left]; ENDCASE => ERROR; END; IF newKeys[Adjust] # lastKeys[Adjust] THEN SELECT newKeys[Adjust] FROM up => processButtonUp[right]; down => processButtonDown[right]; ENDCASE => ERROR; SELECT chordCounter FROM >0 => chordCounter ← chordCounter - 1; =0 => BEGIN button:buttons; event:XEventQ.EventPtr; IF buttonStates[left] = waitingChord THEN button ← left ELSE button ← right; event ← XEventQ.NewEvent[]; event↑ ← [x:buttonDownCoord.x, y:buttonDownCoord.y, time:buttonDownTime, type:ButtonPress, key:LOOPHOLE[button]]; XEventQ.EnQEvent[event]; lastEventTime ← time; chordCounter ← -1; buttonStates[button] ← down; END; ENDCASE; END; processButtonUp:PROCEDURE[button:buttons] = BEGIN event:XEventQ.EventPtr; otherButton:buttons = SELECT button FROM left => right, right => left, ENDCASE => ERROR; IF chordFlag THEN BEGIN event ← XEventQ.NewEvent[]; event↑ ← [x:newMouse.x, y:newMouse.y, time:time, type:ButtonRelease, key:LOOPHOLE[buttons.middle]]; XEventQ.EnQEvent[event]; lastEventTime ← time; buttonStates[button] ← up; chordFlag ← FALSE; buttonStates[otherButton] ← waitingRelease; END ELSE SELECT buttonStates[button] FROM waitingRelease => BEGIN buttonStates[button] ← up; END; waitingChord => BEGIN event ← XEventQ.NewEvent[]; event↑ ← [x:buttonDownCoord.x, y:buttonDownCoord.y, time:buttonDownTime, type:ButtonPress, key:LOOPHOLE[button]]; XEventQ.EnQEvent[event]; event ← XEventQ.NewEvent[]; event↑ ← [x:newMouse.x, y:newMouse.y, time:time, type:ButtonRelease, key:LOOPHOLE[button]]; XEventQ.EnQEvent[event]; lastEventTime ← time; buttonStates[button] ← up; chordCounter ← -1; END; down => BEGIN event ← XEventQ.NewEvent[]; event↑ ← [x:newMouse.x, y:newMouse.y, time:time, type:ButtonRelease, key:LOOPHOLE[button]]; XEventQ.EnQEvent[event]; lastEventTime ← time; buttonStates[button] ← up; END; ENDCASE => ERROR; END; setMouseInterest:PUBLIC PROCEDURE[box:XMKDriver.Box] = BEGIN mouseNoInterestBox ← box; END; setMouseConstraint:PUBLIC PROCEDURE[box:XMKDriver.Box] = BEGIN mouseConstraintBox ← box; END; setCursor:PUBLIC PROCEDURE[cursor:LONG POINTER TO UserTerminal.CursorArray] = BEGIN UserTerminal.SetCursorPattern[cursor↑]; END; setCursorPosition:PUBLIC PROCEDURE[x,y:CARDINAL] = BEGIN UserTerminal.SetCursorPosition[[x,y]]; UserTerminal.SetMousePosition[[x,y]]; END; getLastEventTime:PUBLIC PROCEDURE RETURNS[time:LONG CARDINAL] = BEGIN time ← System.PulsesToMicroseconds[lastEventTime]; END; setLastEventTime:PUBLIC PROCEDURE [time:LONG CARDINAL] = BEGIN lastEventTime ← System.MicrosecondsToPulses[time]; END; getTimeInMillis:PUBLIC PROCEDURE RETURNS[time:LONG CARDINAL] = BEGIN time ← System.PulsesToMicroseconds[System.GetClockPulses[]]; END; enableMouseEvents:PUBLIC PROCEDURE [enable:BOOLEAN] = BEGIN mouseEventsEnabled ← enable; END; enableKeyboardEvents:PUBLIC PROCEDURE[enable:BOOLEAN] = BEGIN keyboardEventsEnabled ← enable; END; displayInit:PUBLIC PROCEDURE RETURNS[addr:LONG POINTER] = BEGIN bbt:BitBlt.BBTable; [] ← UserTerminal.SetState[disconnected]; [] ← UserTerminal.SetState[on]; [] ← UserTerminal.SetBackground[black]; -- [] ← UserTerminal.SetState[off]; bbt ← UserTerminal.GetBitBltTable[]; addr ← bbt.dst.word; END; displayOn:PUBLIC PROCEDURE = BEGIN [] ← UserTerminal.SetState[on]; END; displayOff:PUBLIC PROCEDURE = BEGIN [] ← UserTerminal.SetState[off]; END; blinkDisplay:PUBLIC PROCEDURE = BEGIN UserTerminal.BlinkDisplay[]; END; displayClose:PUBLIC PROCEDURE = BEGIN [] ← UserTerminal.SetState[disconnected]; END; --Mainline Code END.