Guides/Window Driver/Event Handling

From J Wiki
Jump to navigation Jump to search

Event Handlers

An event handler is a verb that is invoked when a Windows event occurs, for example, when a button is pressed. The standard event handling mechanism in J supports four levels of event handler for a given form. If the form name is abc, these are:

  • abc_handler (the override handler)
  • abc_id_class (the child handler, e.g. abc_pressme_button)
  • abc_formevent (the form event handler, e.g. abc_close)
  • abc_default (the default handler)

When an event occurs, the system searches for an event handler in the order given, and executes the first one it finds.

Typically, most forms will be written using only the second level of event handler. The other levels allow the programmer to deal easily with special cases.

Note that these event handlers are ordinary verbs and can be defined and modified as required. The search for an appropriate event handler takes place at the time the event occurs.

wdhandler

When a Windows event occurs, the system invokes the following sentence (but does not show it in the session):

   wdhandler ''

wdhandler:

  • defines public names describing the event
  • finds the appropriate event handler
  • executes the event handler with a right argument of ''

Names defined for the event handler

wdhandler issues wd'q' to get event information, and assigns the result to the public name wdq in the locale of the form. wdq is a table of boxed strings:

   wdq
+------------+---------------------------------+
|syshandler  |mywin_handler                    |
+------------+---------------------------------+
|sysevent    |mywin_pressme_button             |
+------------+---------------------------------+
|sysdefault  |mywin_default                    |
+------------+---------------------------------+
|sysparent   |mywin                            |
+------------+---------------------------------+
|syschild    |pressme                          |
+------------+---------------------------------+
|systype     |button                           |
+------------+---------------------------------+
|syslocalep  |base                             |
+------------+---------------------------------+
|syslocalec  |                                 |
+------------+---------------------------------+
|syshwndp    |1388                             |
+------------+---------------------------------+
|syshwndc    |1388                             |
+------------+---------------------------------+
|sysfocus    |pressme                          |
+------------+---------------------------------+
|syslastfocus|pressme                          |
+------------+---------------------------------+
|sysmodifiers|0                                |
+------------+---------------------------------+

You should not execute wd'q' yourself. wd'q' only returns information about the last event that occurred. Re-running it will provide new information only if another event has occurred.

The first three rows of wdq correspond to the override handler, the child handler/form event handler, and the default handler. wdhandler checks whether any of these event handlers exist, and also defines each name in the first column with the corresponding value from the second column. (Note that all of these values are lists of characters, even though some look like numbers.)

Many child controls have a value that is included in wdq, and therefore also assigned to a public name (which has the same name as the child control).

Seeing what events are signaled

Sometimes you wonder what events are occurring but not being processed because you don't have handlers for them. Following the rules given above, you can set

formname_default =: 3 : 'smoutput wdq'

and you will see the events as they happen.

Child-control Events

Most child controls have associated events, which are reported by calling the child handler. See the description of each child control for details.

All event handlers are invoked with a right argument of ''. Data associated with the event, such as which key was pressed or where the cursor was, is assigned to public names before the handler is called. The description of each child control lists the names that are defined for each event.

Events for Function Keys and Control Keys

Function and control keys are reported to the parent control when the parent has focus. The sentence

parentname_keyname_fkey ''

is executed, where parentname is the name of the parent control and keyname is

  • xctrl for CTRL+x. x may be alphabetic, numeric, or one of the special names home end pgup pgdn up down left right corresponding to those keys.
  • xctrlshift for CTRL+SHIFT+x. x may be alphabetic or one of the special names home end pgup pgdn up down left right corresponding to those keys.
  • fn for function key n, n=1 through 35
  • fnctrl for CTRL+function key n, n=1 through 35
  • fnshift for SHIFT+function key n, n=1 through 35
  • fnctrlshift for CTRL+SHIFT+function key n, n=1 through 35

keyname is always lowercase.

Pressing the ESCAPE key produces one event, which may be signaled in the child control or in the form, depending on what type of child control has focus.

  • If the child control is one that creates a formname_childname_char event, that is used.
  • Otherwise, if the escclose style is set, the event is signaled by

parentname_close ''

  • Otherwise the event is signaled by the sentence

parentname_cancel ''

Timer Events

When working with a form (created by wd 'pc ...) it's generally wise to use the wd 'ptimer' mechanism instead of the system timer mechanism defined below. You can have multiple ptimer events scheduled - one for each form. There's only one system timer event and when multiple forms use the system timer the consequences can be... comedic.

But it's perhaps best to use the system timer when learning and experimenting, as it does not require you create a form.

The timer is a heartbeat timer that generates events at a programmable rate, by executing the sentence

sys_timer_z_ ''

(Or, for ptimer events, replace sys with the parent name of the form, and replace z with the locale of the form.)

when the programmed time has elapsed. You must define the handler sys_timer_z_ and then start the heartbeat by issuing

wd 'timer nnn'

Where nnn is the timer period, in milliseconds.

(Or, for ptimer events, you must have your form selected and instead use wd 'ptimer nnn'.)

Example: define a handler for a timer event that writes the current time to the session, then set the timer to be 1000 milliseconds. The timestamps when Windows signals the timer event are written to the current session

   sys_timer_z_=: (6!:0) (1!:2) 2:

   wd 'timer 1000'

1996 1 3 10 15 37.72

1996 1 3 10 15 38.76

1996 1 3 10 15 39.81

1996 1 3 10 15 40.9

To switch off the timer, run

   wd 'timer 0'

(Or, for ptimer events, you must have your form selected and use wd'ptimer 0'.)

Socket Event

In Windows if sdasync has been done on a socket, then the sentence

   socket_handler_z_ EVENT, ERROR

is run whenever the socket state has changed.

EVENT is an integer event code, eg. FD_ACCEPT FD_READ FD_WRITE ERROR is an integer error code, eg. WSAECONNREFUSED WSAENETUNREACH WSAETIMEDOUT

SK=: 0 pick sdcheck sdsocket''
sdcheck sdasync SK

Make a socket non-blocking and cause the system to run sentence socket_handler whenever the state of the socket has changed.