(* https://web.archive.org/web/20041226165258/http://www.oberon.ethz.ch:80/ethoberon/defs/Oberon.Def.html *) DEFINITION Oberon; (* portable, except where noted *) (* Oberon system manager for dispatch of keyboard and mouse input, scheduling of tasks, cursor handling and command execution. *) IMPORT Display, Objects, Viewers, Fonts, Texts; CONST (* Message ids: *) defocus = 0; neutralize = 1; mark = 2; (* ControlMsg*) consume = 0; track = 1; (* InputMsg*) get = 0; set = 1; reset = 2; (* CaretMsg id, SelectMsg id*) TYPE Painter = PROCEDURE (x, y: INTEGER); Marker = RECORD Fade, Draw: Painter (* Remove and draw marker. *) END; Cursor = RECORD marker: Marker; (* Cursor marker. *) on: BOOLEAN; (* Is cursor shown? *) X, Y: INTEGER (* Absolute cursor position. *) END; ParList = POINTER TO ParRec; ParRec = RECORD (* Area for passing command parameters. *) vwr: Viewers.Viewer; (* Viewer in which command is executed. *) frame: Display.Frame; (* Frame of vwr from where command is executed. *) obj: Objects.Object; (* Object in vwr executing command. *) text: Texts.Text; (* Text parameter to be passed to command. *) pos: LONGINT (* Starting position in text of parameter. *) END; ControlMsg = RECORD ( Display.FrameMsg ) id: INTEGER; (* defocus, neutralize, mark *) X, Y: INTEGER (* Absolute mark position. *) END; InputMsg = RECORD ( Display.FrameMsg ) id: INTEGER; (* consume, track *) keys: SET; (* Mouse buttons. *) X, Y: INTEGER; (* Mouse position. *) ch: CHAR; (* Character typed. *) fnt: Fonts.Font; (* Font of typed character. *) col, voff: SHORTINT (* Color and vertical offset of typed character. *) END; CaretMsg = RECORD ( Display.FrameMsg ) (* Text caret handling. *) id: INTEGER; (* get, set, reset *) car: Display.Frame; (* Destination frame, returned frame. *) text: Texts.Text; (* Text represented by car. *) pos: LONGINT (* Caret position. *) END; SelectMsg = RECORD ( Display.FrameMsg ) (* Text selection handling. *) id: INTEGER; (* get, set, reset *) time: LONGINT; (* Time of the selection. *) sel: Display.Frame; (* Destination frame, returned frame. *) text: Texts.Text; (* Text represented by sel. *) beg, end: LONGINT (* Text stretch of the selection. *) END; ConsumeMsg = RECORD ( Display.FrameMsg ) (* Drag and drop control of text. *) text: Texts.Text; (* Text to be inserted. *) beg, end: LONGINT (* Text stretch to be inserted. *) END; RecallMsg = RECORD ( Display.FrameMsg ) END; Task = POINTER TO TaskDesc; Handler = PROCEDURE (me: Task); TaskDesc = RECORD next: Task; (* for internal use. *) time: LONGINT; (* Earliest time to schedule task. *) safe: BOOLEAN; (* Don't remove from task queue when a trap occurs. *) handle: Handler (* Task handler. *) END; VAR Arrow, Star: Marker; (* Normal Oberon arrow, and the star marker. *) Mouse, Pointer: Cursor; (* Normal Oberon mouse, and the star pointer. *) Log: Texts.Text; (* The Oberon log. *) Par: ParList; (* Actual parameters of executed command. *) CurFnt: Fonts.Font; (* Current input font when typing. *) CurCol, CurOff: SHORTINT; (* Current color and offset when typing. *) OptionChar: CHAR; (* Option character "/" or "" *) OpenText: PROCEDURE (title: ARRAY OF CHAR; T: Texts.Text; W, H: INTEGER); NextTask: Task; (* non-portable, for internal use. *) New: BOOLEAN; (* enable new style mouse handling suitable for two-button mice *) (* Get time (t) and date (d). day = d MOD 32, month = d DIV 32 MOD 16, year = 1900+d DIV 512, hour = t DIV 4096 MOD 32, minute = t DIV 64 MOD 64, second = t MOD 64 *) PROCEDURE GetClock (VAR t, d: LONGINT); (* Set time (t) and date (d). *) PROCEDURE SetClock (t, d: LONGINT); (* Return the number of timer ticks since Oberon startup. (See module Input for frequency) *) PROCEDURE Time (): LONGINT; (* Initialize a cursor, setting it to off, and at position 0, 0. *) PROCEDURE OpenCursor (VAR c: Cursor); (* Fade cursor if visible. *) PROCEDURE FadeCursor (VAR c: Cursor); (* Draw cursor c using marker m at position X, Y. *) PROCEDURE DrawCursor (VAR c: Cursor; VAR m: Marker; X, Y: INTEGER); (* Remove the caret by broadcasting a ControlMsg into the display space. Afterwards, no visual object should own either a caret for inserting text or objects. *) PROCEDURE Defocus; (* Fade the mouse and pointer cursors if located inside the screen area X, Y, W, H. This is required before drawing inside the area X, Y, W, H. *) PROCEDURE RemoveMarks (X, Y, W, H: INTEGER); (* Initialize a new display with user track width UW, system track width SW, and height H. The display is appended to the display space starting at X position Viewers.curW. Normally this procedure is only called once to configure the default layout of the Oberon screen. *) PROCEDURE OpenDisplay (UW, SW, H: INTEGER); (* non-portable *) (* Returns the width in pixels of the display that contains the X coordinate. *) PROCEDURE DisplayWidth (X: INTEGER): INTEGER; (* Returns the height in pixels of the display that contains the X coordinate. *) PROCEDURE DisplayHeight (X: INTEGER): INTEGER; (* Open a new track of width W at X. *) PROCEDURE OpenTrack (X, W: INTEGER); (* Get left margin of user track on display X. *) PROCEDURE UserTrack (X: INTEGER): INTEGER; (* Get left margin of the system track on display X. *) PROCEDURE SystemTrack (X: INTEGER): INTEGER; (* Allocate a new user viewer within the display located at DX. (X, Y) returns the suggested position. *) PROCEDURE AllocateUserViewer (DX: INTEGER; VAR X, Y: INTEGER); (* Allocate a new system viewer within the display located at DX. (X, Y) returns the suggested position. *) PROCEDURE AllocateSystemViewer (DX: INTEGER; VAR X, Y: INTEGER); (* Returns the star-marked viewer. *) PROCEDURE MarkedViewer (): Viewers.Viewer; (* Returns the star-marked frame. *) PROCEDURE MarkedFrame (): Display.Frame; (* Returns the text of the star-marked frame. *) PROCEDURE MarkedText (): Texts.Text; (* Execute an Oberon command. Name should be a string of the form "M.P", where M is the module and P is the procedure of the command. Par is the command parameter record; it will be assigned to Oberon.Par so that the command can pick up its parameters. The new flag indicates if the module M should be reloaded from disk (obly possible if M is a "top" module, i.e. it has no clients. Res indicates success (res = 0) or failure (res # 0). Modules.resMsg contains an explanation of what went wrong when res # 0. *) PROCEDURE Call (name: ARRAY OF CHAR; par: ParList; new: BOOLEAN; VAR res: INTEGER); (* Returns the selected stretch [beg, end[ of the current selected text T. Time indicates the time of selection; time = -1 indicates that no text is currently selected. *) PROCEDURE GetSelection (VAR text: Texts.Text; VAR beg, end, time: LONGINT); (* Install a background task. The background task is periodically activated by calling its handler when the system has nothing else to do. *) PROCEDURE Install (T: Task); (* Remove a background task. *) PROCEDURE Remove (T: Task); (* Request a garbage collection to be done. The GC will take place immediately. *) PROCEDURE Collect; (* Set the default font used when typing characters. *) PROCEDURE SetFont (fnt: Fonts.Font); (* Set the color of typed characters. *) PROCEDURE SetColor (col: SHORTINT); (* Set the vertical offset of typed characters. *) PROCEDURE SetOffset (voff: SHORTINT); (* Open a scanner at a specific section of the Oberon Text. Scans the first symbol in the section. Returns S.class = Texts.Inval on error. *) PROCEDURE OpenScanner (VAR S: Texts.Scanner; name: ARRAY OF CHAR); (* Main Oberon task dispatcher. Reads the mouse position and characters typed, informing the viewer of the display space of events using the Display.InputMsg. The loop broadcasts a ControlMsg (id = mark) when the marker is set. Pressing the neutralise key results in a ControlMsg (id = neutralize) to be broadcast. All frames receiving the neutralize message should remove selections and the caret. The Loop periodically activates background tasks and the garbage collector, if no mouse or keyboard events are arriving. *) PROCEDURE Loop; END Oberon. (* Remarks: 1. Command execution Execution of commands is the task of modules Module. Oberon.Call provides an abstraction for this mechanism and also a way to pass parameters in the form of a text to the executed command. After command execution, the global variable Oberon.Par is a pointer to a parameter record specifying a parameter text, a position in that text, and details what objects are involved in the commands. The vwr field of the ParRec points to the viewer in which the command was executed. The frame field of the ParRec points to the direct child of the vwr (the menu or the main frame) from which the command was executed. This semantics is compatible with older Oberon applications and is seldomly used today. The obj field of the ParRec points to the object (normally a frame) that executed the command. The Oberon.Par pointer is initialized before command execution to the parameter record passed to Oberon.Call. 2. Cursors and Markers Markers are a way to draw and undraw a shape on the display. Typically, draw and undraw can be realized by an invert display operation. Cursors keep track of the current position and state (visible or not) of a marker. The Mouse cursor is the standard mouse arrow, and the Pointer cursor is the star marker placed with the Setup key. Repeatedly calling Oberon.DrawCursor with different coordinates move a cursor (and marker) across the display. Before drawing in a certain area of the display, cursors should be removed with Oberon.RemoveMarks or Oberon.FadeCursor (failingly to do so may result in the cursor leaving garbage on the display when drawing operations are performed in its vicinity). Note that on some Oberon host platforms (Windows, Mac, Unix) the mouse cursor is under control of the host windowing system, and is automatically faded when required. It is recommended to fade the cursor on these platforms anyway, as your Oberon programs will then also work on native Oberon systems. 3. The InputMsg The InputMsg informs the frames of the display space of the current mouse position and character typed. It is repeatedly broadcast into the display space by the Oberon.Loop for each input event. An InputMsg id of Oberon.consume indicates a key was pressed. The ASCII keycode is contained in the ch field of the message (check the description of module Input for special keycodes). The fields fnt, col and voff give information about the requested font, color (index), and verticall offset (in pixels). These values are copied from hidden variables in the Oberon module, which are set with the procedures SetFont, SetColor, and SetOffset. Note that the TextGadgets ignore these fields when typing. Instead the font, color and vertical offset of the character immediately before the caret is used (thus the fields have fallen out of use). A frame should only process the consume message if it has the caret set. Afterwards the message should be invalidated by setting the message res field to 0 or a positive value. This prevents the character being consumed by other frames in the display space and also terminates the message broadcast. An InputMsg id of track indicates a mouse event. The display space normally only forwards this message to the frame located at position X, Y on the display. Field X, Y indicates the absolute mouse position (cursor hotspot) and keys the mouse button state (which mouse buttons are pressed). The mouse buttons are numbered 0, 1, 2 for right, middle, and left respectively. It is typical for a frame receiving a track message with keys # {} to temporarily taking control of the mouse by polling (using module Input). As soon as all mouse buttons are released, control must be passed back to the Oberon loop. A frame should invalidate the track message if it took action on a mouse event; otherwise the enclosing frame might think that the message could not be handled. In some cases a child frame takes no action on an event even though a mouse buttton is pressed and the mouse is located inside the frame. This is an indication that the child frame cannot or is unwilling to process the event, and the parent (and forwarder of the message in the display space) should take a default action. Note that during a tracking operation, no background tasks can be serviced. 4. The ControlMsg The control message manages display space wide events like removing the (one and only) caret, pressing Neutralise (for removing the caret and the selections), and setting the star marker with the Setup key. The id field of the control message is set to defocus, neutralize, and mark respectively. Note that the mark variant need not be handled by own frames; it is already processed by the Viewers. Messages of this type must never be invalidated during their travels through the display space. 5. The CaretMsg The CaretMsg controls the removing (id = reset), retrieving (id = get) and setting (id = set) of the caret on a frame to frame basis. All text editor-like frames should respond to this message. The car field of the message defines the editor frame involved for reset and set, and returns the editor frame that has the caret for get. The text field specifies which text is meant (or which text is returned for get). In the reset and set cases this field is mostly redundant but is checked for correctness ANYWAY. The pos field must be valid position in the text. The CaretMsg is always broadcast. 6. The SelectMsg In a similar way as the CaretMsg, the SelectMsg controls the removing (id = reset), retrieving (id = get) and setting (id = set) of the selection. In this case, the sel field indicates the destination frame or returned frame, in a similar manner as the car field of the CaretMsg. The SelectMsg is extended with fields for specifying/retrieving the selection time, starting and ending position. The SelectMsg is always broadcast. 7. Background tasks The handle procedure variable of installed background tasks are periodically called by the Oberon loop when the Oberon system is idle (no mouse or keyboard events). The task handlers have to be programmed in an inverted manner and should return as quickly as possible to the loop, otherwise the user will notice delays (typically when elapsed time is greater than 100-200ms). As tasks are activated periodically, a badly written task can cause a cascade of traps, one for each invocation. By default, the loop removes such a task that does not return from the task list (the safe flag prevents the loop from such an action). The garbage collector is realized as a task. A task can request to be invoked only at a specified time by setting the time field in the task descriptor. The time is measured according to Oberon.Time() at tick frequency specified by Input.TimeUnit. After each handler invocation, the task is expected to advance the time field to the next earliest event, overwise it will never be invoked in future. It is highly recommended to use this feature by specifying for tasks that are only invoked every few ms. This will save network traffic when using Oberon in an X-Window environment. 8. The Oberon Loop The Oberon loop is called when the Oberon system starts, and never returns until the Oberon system is left. After a trap occurs, the run-time stack is reset, and the Oberon loop is started afresh. The Oberon loop polls the mouse and keyboard for events that it broadcasts to the display space using messages. When no events are happening, background tasks are activated periodically in a round-robin fashion. *)