(* Aos, Copyright 2001, Pieter Muller, ETH Zurich *) MODULE Inputs; (** AUTHOR "pjm"; PURPOSE "Abstract input device"; *) (* Based on SemInput.Mod by Marc Frei *) IMPORT Machine, Kernel, Plugins; CONST (** KeyboardMsg flags. *) Release* = 0; (** a key release event, otherwise a key press or repeat. *) (** shift key states. *) LeftShift* = 1; RightShift* = 2; LeftCtrl* = 3; RightCtrl* = 4; LeftAlt* = 5; RightAlt* = 6; LeftMeta* = 7; RightMeta* = 8; (** combined shift key states. *) Shift* = {LeftShift, RightShift}; Ctrl* = {LeftCtrl, RightCtrl}; Alt* = {LeftAlt, RightAlt}; Meta* = {LeftMeta, RightMeta}; (** flags for KeyState *) SHIFT* = 0; CTRL* = 1; ALT* = 2; (** keysym values, similar to X11 keysyms *) KsNil* = 0FFFFFFH; (** no key *) (** TTY Functions, cleverly chosen to map to ascii *) KsBackSpace* = 0FF08H; (** back space, back char *) KsTab* = 0FF09H; KsReturn* = 0FF0DH; (** Return, enter *) KsPause* = 0FF13H; (** Pause, hold *) KsScrollLock* = 0FF14H; KsSysReq* = 0FF15H; KsEscape* = 0FF1BH; KsDelete* = 0FFFFH; (** Delete, rubout *) (** Cursor control & motion *) KsHome* = 0FF50H; KsLeft* = 0FF51H; (** Move left, left arrow *) KsUp* = 0FF52H; (** Move up, up arrow *) KsRight* = 0FF53H; (** Move right, right arrow *) KsDown* = 0FF54H; (** Move down, down arrow *) KsPageUp* = 0FF55H; (** Prior, previous *) KsPageDown* = 0FF56H; (** Next *) KsEnd* = 0FF57H; (** EOL *) (** Misc Functions *) KsPrint* = 0FF61H; KsInsert* = 0FF63H; (** Insert, insert here *) KsMenu* = 0FF67H; (** Windows menu *) KsBreak* = 0FF6BH; KsNumLock* = 0FF7FH; (** Keypad functions *) KsKPEnter* = 0FF8DH; (** enter *) KsKPMultiply* = 0FFAAH; KsKPAdd* = 0FFABH; KsKPSubtract* = 0FFADH; KsKPDecimal* = 0FFAEH; KsKPDivide* = 0FFAFH; (** Function keys *) KsF1* = 0FFBEH; KsF2* = 0FFBFH; KsF3* = 0FFC0H; KsF4* = 0FFC1H; KsF5* = 0FFC2H; KsF6* = 0FFC3H; KsF7* = 0FFC4H; KsF8* = 0FFC5H; KsF9* = 0FFC6H; KsF10* = 0FFC7H; KsF11* = 0FFC8H; KsF12* = 0FFC9H; (** Modifiers *) KsShiftL* = 0FFE1H; (** Left shift *) KsShiftR* = 0FFE2H; (** Right shift *) KsControlL* = 0FFE3H; (** Left control *) KsControlR* = 0FFE4H; (** Right control *) KsCapsLock* = 0FFE5H; (** Caps lock *) KsMetaL* = 0FFE7H; (** Left meta, Left Windows *) KsMetaR* = 0FFE8H; (** Right meta, Right Windows *) KsAltL* = 0FFE9H; (** Left alt *) KsAltR* = 0FFEAH; (** Right alt *) (** HID Consumer Keys**) KsScanPreviousTrack*= 0FF0000H; KsScanNextTrack*= 0FF0001H; KsALConsumerControl*= 0FF0002H; KsMute*= 0FF0003H; KsVolumeDecrement*= 0FF0004H; KsVolumeIncrement*= 0FF0005H; KsPlayPause*= 0FF0006H; KsStopOSC*= 0FF0007H; KsALEmailReader*= 0FF0008H; KsALCalculator*= 0FF0009H; KsACSearch*= 0FF000AH; KsACHome*= 0FF000BH; KsACBack*= 0FF000CH; KsACForward*= 0FF000DH; KsACBookmarks*= 0FF000EH; KsConsumerButtons*= 0FFF000H; TYPE Message* = RECORD END; (** generic message. *) KeyboardMsg* = RECORD (Message) ch*: CHAR; (** extended ASCII key code, or 0X if not relevant *) flags*: SET; (** key flags *) keysym*: LONGINT (** X11-compatible key code *) END; MouseMsg* = RECORD (Message) keys*: SET; (** mouse key state. *) dx*, dy*, dz*: LONGINT (** mouse movement vector. *) END; AbsMouseMsg*= RECORD(Message); keys*: SET; x*,y*,z*,dx*,dy*,dz*: LONGINT; END; PointerMsg* = RECORD (Message) keys*: SET; (** pointer key state. *) x*, y*, z*: LONGINT; (** pointer position. *) mx*, my*, mz*: LONGINT (** pointer max values. *) END; TYPE Sink* = OBJECT (** a message receiver. *) (** Handle is overriden by a concrete receiver. *) PROCEDURE Handle*(VAR msg: Message); BEGIN HALT(301) END Handle; END Sink; Group* = OBJECT (** a group of message receivers. *) (** Add a receiver to a group. *) PROCEDURE Register*(s: Sink); BEGIN HALT(301) END Register; (** Remove a receiver from a group. *) PROCEDURE Unregister*(s: Sink); BEGIN HALT(301) END Unregister; (** Send a message to all receivers currently in the group. *) PROCEDURE Handle*(VAR msg: Message); BEGIN HALT(301) END Handle; END Group; TYPE Pointer* = OBJECT (Sink) (** convert incremental movements into absolute positions *) VAR cur: PointerMsg; threshold, speedup: LONGINT; fixedKeys: SET; PROCEDURE Update; VAR p: PointerMsg; BEGIN IF cur.x < 0 THEN cur.x := 0 ELSIF cur.x > cur.mx THEN cur.x := cur.mx END; IF cur.y < 0 THEN cur.y := 0 ELSIF cur.y > cur.my THEN cur.y := cur.my END; IF cur.z < 0 THEN cur.z := 0 ELSIF cur.z > cur.mz THEN cur.z := cur.mz END; p := cur; p.keys := p.keys + fixedKeys; pointer.Handle(p) END Update; PROCEDURE SetKeys(keys: SET); BEGIN {EXCLUSIVE} fixedKeys := keys; Update END SetKeys; PROCEDURE Handle*(VAR m: Message); VAR dx, dy: LONGINT; BEGIN {EXCLUSIVE} IF m IS MouseMsg THEN WITH m: MouseMsg DO dx := m.dx; dy := m.dy; IF (ABS(dx) > threshold) OR (ABS(dy) > threshold) THEN dx := dx*speedup DIV 10; dy := dy*speedup DIV 10 END; INC(cur.x, dx); INC(cur.y, dy); INC(cur.z, m.dz); cur.keys := m.keys; Update; END; ELSIF m IS AbsMouseMsg THEN WITH m: AbsMouseMsg DO cur.x := m.x; cur.y := m.y; cur.z := m.z; cur.keys := m.keys; Update END; END END Handle; PROCEDURE SetLimits*(mx, my, mz: LONGINT); BEGIN {EXCLUSIVE} cur.mx := mx; cur.my := my; cur.mz := mz; Update END SetLimits; PROCEDURE &Init*(t, s: LONGINT); BEGIN threshold := t; speedup := s; cur.x := 0; cur.y := 0; cur.z := 0; cur.mx := 1; cur.my := 1; cur.mz := 1; cur.keys := {}; fixedKeys := {}; mouse.Register(SELF) END Init; END Pointer; TYPE List = POINTER TO RECORD next: List; s: Sink END; Broadcaster = OBJECT (Group) VAR sentinel: List; PROCEDURE Register*(s: Sink); VAR n: List; BEGIN {EXCLUSIVE} NEW(n); n.s := s; n.next := sentinel.next; sentinel.next := n END Register; PROCEDURE Unregister*(s: Sink); VAR n: List; BEGIN {EXCLUSIVE} n := sentinel; WHILE (n.next # NIL) & (n.next.s # s) DO n := n.next END; IF n.next # NIL THEN n.next := n.next.next END END Unregister; PROCEDURE Handle*(VAR msg: Message); VAR n: List; BEGIN {EXCLUSIVE} n := sentinel.next; WHILE n # NIL DO n.s.Handle(msg); n := n.next END END Handle; END Broadcaster; TYPE OberonInput* = OBJECT (Plugins.Plugin) VAR timer-: Kernel.Timer; PROCEDURE Mouse*(VAR x, y: INTEGER; VAR keys:SET); BEGIN HALT(99) (* abstract *) END Mouse; PROCEDURE Read*(VAR ch: CHAR; VAR break: BOOLEAN); BEGIN HALT(99) (* abstract *) END Read; PROCEDURE Available*(VAR num: INTEGER; VAR break: BOOLEAN); BEGIN HALT(99) (* abstract *) END Available; PROCEDURE KeyState*(VAR k: SET); BEGIN HALT(99) (* abstract *) END KeyState; PROCEDURE &Init*; BEGIN NEW(timer) END Init; END OberonInput; TYPE MouseFixer = OBJECT (Sink) VAR ctrl: BOOLEAN; PROCEDURE Handle*(VAR m: Message); VAR new: BOOLEAN; BEGIN {EXCLUSIVE} WITH m: KeyboardMsg DO new := m.flags * Ctrl # {}; IF new # ctrl THEN ctrl := new; IF ctrl THEN main.SetKeys({1}) ELSE main.SetKeys({}) END END END END Handle; PROCEDURE &Init*; BEGIN ctrl := FALSE; keyboard.Register(SELF) END Init; END MouseFixer; VAR keyboard*, mouse*, pointer*: Group; main*: Pointer; oberonInput*: Plugins.Registry; mouseFixer: MouseFixer; (** Return a default message broadcaster instance. *) PROCEDURE NewBroadcaster*(): Group; VAR b: Broadcaster; BEGIN NEW(b); NEW(b.sentinel); b.sentinel.next := NIL; RETURN b END NewBroadcaster; PROCEDURE Init; VAR s: ARRAY 16 OF CHAR; i, threshold, speedup: LONGINT; BEGIN Machine.GetConfig("Threshold", s); i := 0; threshold := Machine.StrToInt(i, s); IF threshold <= 0 THEN threshold := 5 END; Machine.GetConfig("Speedup", s); i := 0; speedup := Machine.StrToInt(i, s); IF speedup <= 0 THEN speedup := 15 END; NEW(main, threshold, speedup); Machine.GetConfig("MB", s); IF (s = "2") OR (s = "-2") THEN NEW(mouseFixer) END END Init; BEGIN keyboard := NewBroadcaster(); mouse := NewBroadcaster(); pointer := NewBroadcaster(); NEW(oberonInput, "Inputs", "Oberon input drivers"); Init END Inputs.