4 Events

4.1  Event Messages

Events are messages which are sent to the owner process of the object when the user interacts with the object in some way. A simple case is the user pressing a button. An event is then delivered to the owner process of the button (the process that created the button). In the following example, the program creates a button object and enables the events click and enter. This example shows that events are enabled in the same way as objects are configured with options.

B = gs:create(button,Win, [{click,true},{enter,true}]),
event_loop(B).
    

The process is now ready to receive click and enter events from the button. The events delivered are always five tuples and consist of:

{gs, IdOrName, EventType, Data, Args}
    
  • gs is a tag which says it is an event from the gs graphics server.
  • IdOrName contains the object identifier or the name of the object in which the event occurred.
  • EventType contains the type of event which has occurred. In the example shown, it is either click or enter.
  • Data is a field which the user can set to any Erlang term. It is very useful to have the object store arbitrary data which is delivered with the event.
  • Args is a list which contains event specific information. In a motion event, the Args argument would contain the x and y coordinates.

There are two categories of events:

  • generic events
  • object specific events.

4.2  Generic Events

Generic events are the same for all types of objects. The following table shows a list of generic event types which the graphics server can send to a process. For generic events, the Args argument always contains the same data, independent of which object delivers it.

The following sub-sections explains the event types and what they are used for.

Event Args Description
buttonpress [ButtonNo,X,Y|_] A mouse button was pressed over the object.
buttonrelease [ButtonNo,X,Y|_] A mouse button was released over the object.
enter [] Delivered when the mouse pointer enters the objects area.
focus [Int|_] Keyboard focus has changed. 0 means lost focus. 1 means gained focus.
keypress [KeySym,Keycode, Shift, Control|_] A key has been pressed.
leave [] Mouse pointer leaves the object.
motion [X,Y|_] The mouse pointer is moving in the object. Used when tracking the mouse in a window.
Table 4.1:   Generic Event Types

The Buttonpress and Buttonrelease Events

These events are generated when a mouse button is pressed or released inside the object frame of a window, or frame object type. The button events are not object specific (compare to click). The format of the buttonpress event is:

{gs,ObjectId,buttonpress,Data,[MouseButton,X,Y|_]}
      

The mouse button number which was pressed is the first argument in the Args field list. This number is either 1, 2 or 3, if you have a three button mouse. The X and Y coordinates are sent along to track in what position the user pressed down the button. These events are useful for programming things like "rubberbanding", which is to draw out an area with the mouse. In detail, this event can be described as pressing the mouse button at a specific coordinate and releasing it at another coordinate in order to define a rectangular area. This action is often used in combination with motion events.

The Enter and Leave Events

These events are generated when the mouse pointer (cursor) enters or leaves an object.

The Focus Event

The focus event tracks which object currently holds the keyboard focus. Only one object at a time can hold the keyboard focus. To have the keyboard focus means that all keypresses from the keyboard will be delivered to that object. The format of a focus event is:

{gs,ObjectId,focus, Data,[FocusFlag|_]}
      

The FocusFlag argument is either 1, which means that the object has gained keyboard focus, or 0, which means that the object has lost keyboard focus.

The Keypress Event

This event is generated by an object which receives text input from the user, like entry objects. It can also be generated by window objects. The format of a keypress event is:

{gs,ObjectId,keypress,Data,[Keysym,Keycode,Shift,Control|_]}
      

The Keysym argument is either the character key which was pressed, or a word which describes which key it was. Examples of Keysyms are; a,b,c.., 1,2,3..., 'Return', 'Delete', 'Insert', 'Home', 'BackSpace', 'End'. The Keycode argument is the keycode number for the key that was pressed. Either the Keysym or the Keycode argument can be used to find out which key was pressed. The Shift argument contains either a 0 or a 1 to indicate if the Shift key was held down when the character key was pressed. The Control argument is similar to the Shift key argument, but applies to the Control key instead of the Shift key.

The Motion Event

The motion event is used to track the mouse position in a window. When the user moves the mouse pointer (cursor) to a new position a motion event is generated. The format of a motion event is:

{gs,ObjectId,motion,Data,[X,Y|_]}
      

The current x and y coordinates of the cursor are sent along in the Args field.

4.3  Object Specific Events

The click and doubleclick events are the object specific event types. Only some objects have these events and the Args field of the events vary for different type of objects. A click on a check button generates a click event where the data field contains the on/off value of the indicator. On the other hand, the click event for a list box contains information on which item was chosen.

Event Args Description
click <object specific> Pressing a button or operating on a object in some predefined way.
double-click <object specific> Pressing the mouse button twice quickly. Useful with list boxes.
Table 4.2:   Object Specific Events

4.4  Matching Events Against Object Identifiers

Events can be matched against the object identifier in the receive statement. The disadvantage of matching against identifiers is that the program must pass the object identifiers as arguments to the event loop.


-module(ex3).
-copyright('Copyright (c) 1991-97 Ericsson Telecom AB').
-vsn('$Revision: /main/release/2 $ ').

-export([init/0]).

init() ->
    S = gs:start(),
    W = gs:create(window,S,[{width,300},{height,200}]),
    B1 = gs:create(button,W,[{label, {text,"Button1"}},{y,0}]),
    B2 = gs:create(button,W,[{label, {text,"Button2"}},{y,40}]),
    gs:config(W, {map,true}),
    loop(B1,B2).

loop(B1,B2) ->
    receive
        {gs,B1,click,_Data,_Arg} -> % button 1 pressed
            io:format("Button 1 pressed!~n",[]),
            loop(B1,B2);
        {gs,B2,click,_Data,_Arg} -> % button 2 pressed
            io:format("Button 2 pressed!~n",[]),
            loop(B1,B2)
    end.

4.5  Matching Events Against Object Names

Another solution is to name the objects using the create/4 function. In this way, the program does not have to pass any parameters which contain object identifiers for each function call made.


-module(ex4).
-copyright('Copyright (c) 1991-97 Ericsson Telecom AB').
-vsn('$Revision: /main/release/2 $ ').

-export([init/0]).

init() ->
    S = gs:start(),
    gs:create(window,win1,S,[{width,300},{height,200}]),
    gs:create(button,b1,win1,[{label, {text,"Button1"}},{y,0}]),
    gs:create(button,b2,win1,[{label, {text,"Button2"}},{y,40}]),
    gs:config(win1, {map,true}),
    loop(). %% look, no args!

loop() ->
    receive
        {gs,b1,click,_,_} -> % button 1 pressed
            io:format("Button 1 pressed!~n",[]),
            loop();
        {gs,b2,click,_,_} -> % button 2 pressed
            io:format("Button 2 pressed!~n",[]),
            loop()
    end.

4.6  Matching Events Against the Data Field

A third solution is to set the data option to some value and then match against this value. All built-in objects have an option called data which can be set to any Erlang term. For example, we could set the data field to a tuple {Mod, Fun,Args} and have the receiving function make an apply on the contents of the data field whenever certain events arrive.


-module(ex5).
-copyright('Copyright (c) 1991-97 Ericsson Telecom AB').
-vsn('$Revision: /main/release/2 $ ').

-export([start/0, init/0, b1/0, b2/0]).

start() ->
    spawn(ex5, init, []).

init() ->
    S = gs:start(),
    W = gs:create(window,S,[{map,true}]),       
    gs:create(button,W,[{label,{text,"Button1"}},{data,{ex5,b1,[]}},{y,0}]),
    gs:create(button,W,[{label,{text,"Button2"}},{data,{ex5,b2,[]}},{y,40}]),
    loop().

loop()->
    receive
        {gs,_,click,{M,F,A},_} -> % any button pressed
            apply(M,F,A),
            loop()
    end.

b1() ->
    io:format("Button 1 pressed!~n",[]).
b2() ->
    io:format("Button 2 pressed!~n",[]).                

4.7  Experimenting with Events

A good way of learning how events work is to write a short demo program like the one shown below and test how different events work.


-module(ex6).
-copyright('Copyright (c) 1991-97 Ericsson Telecom AB').
-vsn('$Revision: /main/release/2 $ ').

-export([start/0,init/0]).

start() ->
    spawn(ex6,init,[]).

init() ->
    S = gs:start(),
    W = gs:create(window,S,[{map,true},{keypress,true},
                            {buttonpress,true},{motion,true}]),
    gs:create(button,W,[{label,{text,"PressMe"}},{enter,true},
                        {leave,true}]),
    event_loop().

event_loop() ->
    receive
        X ->
            io:format("Got event: ~w~n",[X]),
            event_loop()
    end.