REBOL
Docs Blog Get-it

R3 View - Event Handling

R3 Graphical User Interface (GUI)

Concepts

An event is a signal that drives GUI actions.

It is a method of notification that includes an event type along with a set of related attributes. User inputs such as moving the mouse, clicking a mouse button, or pressing a key cause events to occur that are handled by mechanisms implemented within the GUI system.

For example, when a mouse button is pressed, you need to know which button was pressed, that it went down and also up, where the mouse was positioned, and other modifiers, such as the shift or control keys that were being held down at the time.

An event handler is software that processes an event when it occurs and makes the necessary changes to the state of GUI objects. For example, if the mouse button is clicked while the mouse was over a button image, then the handler will cause the logic of the button to operate, including its end result, a reactor.

See the lower level event system description for more information.

Mechanism

For simple GUI scripts, you do not need to understand the event system. You can create panels with buttons and other styles, and they will work just fine. However, some developers may want to know the basics, and the easiest way to understand the event system is to look at a specific example.

Basic Mechanism

Let's say the user has pressed down the left mouse button at position 100x100 within a specific window. Here's the basic sequence of events:

  1. User presses a mouse button.
  2. A sequence of event handlers is called to process the event.
  3. The the GUI handler calls style actors to update the display or take some other action.
  4. The actors may trigger a reactor that produces an end-result, such as opening a new window or launching a web browser.

This is the basic information you need to know to understand GUI events. However, if you need to do any special processing for events, such as write your own style actions, you may need to know more. See the next section.

Detailed Mechanism

For developers who want to understand the complete GUI event mechanism, here are the full details of the above mouse button click sequence.

  1. The user presses down the left mouse button.
  2. The underlying OS detects the event.
  3. The OS notifies the REBOL event device, low level C code functions.
  4. The device decodes the mouse information including the down state of the button, the window GOB, the position at 100x100, and any other details.
  5. An event! datatype is obtained (from a preallocated block). Its fields are set with the information above. For example, its type field is set to down.
  6. The event is queued to the system port. This is an event concentrator (a funnel.) From the C code side, all events of all kinds are queued here.
  7. Control returns to the REBOL interpreter.
  8. The event wakes up the system port.
  9. The system port redirects the event to the GUI event port. (The system port is an event dispatcher. All REBOL side events originate from here.)
  10. The GUI event port examines the event and uses the window GOB to locate a GUI handler specific to that window. Most of the time, this is the general GUI system handler, but users can provide custom handlers as well (e.g. for high-performance code, games, etc.)
  11. The GUI handler is called via its do-event function.
  12. The handler dispatches a specific function associated with the down event.
  13. The down event maps its location to a specific face, such as a button face that was displayed on screen earlier.
  14. If a face is present, the on-click actor for its style is called.
  15. The style actor then calls any reactor functions, such as setting a variable, evaluating a block of code, scrolling a panel, etc.
Missing image: /diagram-needed
Missing image: /diagram-needed

All of this is done very efficiently with as few extra steps as possible in order to keep the GUI highly responsive to user interaction.

For more information, see the Event System documentation or browse the GUI source code in the R3 DevBase archive.

Event Handling Layers

As you can see from the above discussion, there are various layers where events are handled.

native levelThe machine code native level written in C code. This is high-performance code and should only be modified by experts. (It's source is part of the R3 Host-kit.)
system portUsed to isolate and concentrate the native level from the interpreter level. All events, including GUI and networking events, pass through this code. It is also highly tuned code, and should only be modified by experts.
view handlerHandles events for the graphics system. REBOL provides two default handlers: a simple one for simple window event handling, such as done in GOB windows, and an advanced one for processing GUI events. Users can create their own window handlers.
style actorsEvery style has actor functions that process events for faces, and they can be written by users familiar with GUI style and face operation.
face reactorsThe highest level functions. These provide the end result for an event, such as opening a window, writing a file, evaluating code, etc.

Although code can be added or modified at any layer, it is advised that you only change the layers that you understand completely. Improper changes can cause intermittent problems, lost events, or event queue overflows.

System Port Awake

As mentioned above, the system port provides the main handler for REBOL. It's main purpose is to isolate native events from interpreter events, it decouples the two subsystems. This is necessary to prevent the host and the interpreter from intermixing their specific environments in a way that would cause problems for system resources, including heap and stack allocation.

At the native host level, when host code is running, all events are appended to an event queue. Eventually, when control returns to the REBOL interpreter kernel, it examines the queue to determine what events have occurred. This process decouples the host from the interpreter.

At the core of the system port is an event handler called awake that examines the event queue and dispatches events to specific port-based event handlers. This is done through a special wake-up function.

Generally, you should not modify the system port awake handler unless you know what you're doing. This code needs to be efficient. Even adding a few more lines to it will slow down all REBOL port I/O event handling.

However, if you have discovered an improvement or tweak that improves the code without serious side effects, please post it on R3 Chat (DevBase).

The system port awake function as of publication of this document is:

awake: func [
    sport "System port (State block holds events)"
    ports "Port list (Copy of block passed to WAIT)"
    /local event port waked
][
    waked: sport/data ; The wake list (pending awakes)

    ; Process all events (even if no awake ports).
    ; Do only 8 events at a time (to prevent polling lockout).
    loop 8 [
        unless event: take sport/state [break]
        port: event/port
        if wake-up port event [
            ; Add port to wake list:
            ;print ["==System-waked:" port/spec/ref]
            unless find waked port [append waked port]
        ]
    ]

    ; No wake ports (just a timer), return now.
    unless block? ports [return none]

    ; Are any of the requested ports awake?
    forall ports [
        if find waked first ports [return true]
    ]

    false ; keep waiting
]

It's archive location is in mezz-ports.r in the R3 Mezzanine section of DevBase.

View Handlers

The View graphics system provides the next layer of event handling. These types of events are related to user inputs that occur through windowing and related user input devices.

Event Scheme

The event scheme provides a port definition that is used for view system events. It stores a list of handler objects that are dispatched by a central view-based event awake function. When an event occurs, its window field is examined for a handler.

Handler Objects

The view system defines a standard handler object that consists of these fields:

Field Datatype Description
name word! Identification of the handler. Used for debugging purposes, and it's useful to figure out what's going on when you have more than one handler running
about string! Short description of the handler. Used for help.
status word! The status of the handler.
priority integer! The urgency of the handler. Controls where the handler is inserted into the handler list. A higher values is processed earlier.
do-event function! Main function to process events. Called whenever events occur.

The base event handler object is defined as:

base-handler: context [
    do-event: func [event] [ ; at top for performance
        print "(Missing event handler)"
        event
    ]
    win-gob: none
    status: 'made
    name: 'no-name
    priority: 0
    about: "Main template for VIEW event handlers."
]

Handler API

The functions for view handlers are:

Function Description
handler-events Adds a handler to the view event system.
unhandle-events Removes a hander from the view event system.
handled-events? Returns event handler object matching a given name.
do-events Waits for window events. Returns when all windows are closed.

Adding a Handler

There are two ways to add a view-level handler: you can provide it as an option to the view function or install it with the handle-events function.

The format of a handler is an object definition block of the form described above.

Here's an example handler, notice it's just a block here:

my-handler: [
    name: 'my-handler
    priority: 50
    handler: func [event] [
        print ["event:" event/type event/offset]
        if switch event/type [
            close [true]
            key [event/key = escape]
        ] [
            unhandle-events self
            unview event/window
            quit
        ]
        show event/window
        none
    ]
]

It can be provided to the view function at the same time the window GOB is provided:

view/options window [handler: my-handler]

Or, it can be installed with:

handle-events my-handler

Notice that this view handler will remove itself with unhandle-events when its window is closed.

Event Fields

An event value can decode these fields using refinements. Some fields may be meaningless for specific events.

Field Datatype Description
type word! A word that indicates the type of event. See list below.
port port! The port for the event. For the GUI, this is the event port; however, for non-GUI ports, this field is overloaded and can contain other information.
gob gob! The GOB where the event occurred. By default, the window GOB.
window gob! Alias for above.
offset pair! The position (only valid for mouse and size events).
key char! word! Key char or word (only valid for keyboard events). See list below for word-based characters.
flags block! none! A block of possible modifiers: double control shift
code integer! The integer code for a key down event.
data file! none! For a drop-file event, provides the file name.

Event Types

Possible event types are defined in system/view/event-types. They are:

ignore
interrupt
device
custom
error
init
open
close
connect
accept
read
write
wrote
lookup
ready
done
time
show
hide
offset
resize
active
inactive
minimize
maximize
restore
move
down
up
alt-down
alt-up
aux-down
aux-up
key
key-up
scroll-line
scroll-page
drop-file

Keyboard Events

Keyboard events can be character value or word value for virtual keys like insert and :home.

Current virtual key words are defined in system/view/event-keys:

page-up
page-down
end
home
left
up
right
down
insert
delete
f1
f2
f3
f4
f5
f6
f7
f8
f9
f10
f11
f12

Of course, F10 may be a problem on most Windows systems.

About | Contact | PrivacyREBOL Technologies 2024