Event Handling

Updated: April 19, 2023

Screen events, which include both input and general events, are associated with a given context and are generally handled in your main application's loop.

Screen manages an event queue per context on the server side. Screen creates event queues for each context with a maximum size of queue-start-size that's defaulted to 200. This value can be configured in graphics.conf in the globals section. This queue size doesn't grow if there are more events than the queue size that you've specified. This means that applications may lose events if they are not handled before more events are queued beyond the queue's capacity.

An event exists in the context's queue, on the server, until the application calls screen_get_event(). After this call, the event is transferred from the event queue to the application (content of the event is copied from the server to the client's event object). Once transferred to the application, the event can then be used with screen_get_event_property_*() in the application's event-handling routine to handle the event accordingly.

Before an application can retrieve an event from the server, it must create an event object to store the event, by calling screen_create_event(). This is the event object that your application uses when calling screen_get_event() to get the event from the server. Once the application creates the event, and has a copy of it from the server, the application can determine the lifetime of the event. The event no longer exists on the server once it's been retrieved by the application.



Figure 1. Event handling

Several variations of screen_get_event_property_*() and screen_set_event_property_*() are available to get and set event properties respectively. It's important to note that different types of events permit a different selection of properties that can be queried or set. The exception to this rule is the SCREEN_PROPERTY_TYPE property, which indicates the type of event. Therefore, if you have an event to handle, it's common practice in your event-handling routine to first query the type of event using screen_get_event_property_iv() with SCREEN_PROPERTY_TYPE. Once you know which type of event you're handling, you can proceed to call the appropriate get and set functions for the event's properties.

If you have a dedicated thread to get events from the server, use an infinite timeout (-1) when calling screen_get_event(). An infinite timeout will block the call, and it won't return until there is an event ready. A timeout of 0 will return an event if there's one available, or an event of type SCREEN_EVENT_NONE if none is ready.

You can use the same application loop for both rendering and event handling if you wish. The following is an example of an event-handling loop with a few of the common events that you may handle; the example is not exhaustive and its purpose isn't to demonstrate how you necessarily need to handle the events. The actions to take upon receipt of an event are entirely dependent on your application. The list of events may seem long, but you really need to handle only the events that your application is interested in.

...
int val, object_type, property_name;    /* used for property queries */
screen_context_t screen_ctx;            /* connection to screen windowing system */
screen_event_t screen_ev;               /* handle used to retrieve events from our context's queue */
screen_display_t disp;                  /* native handle for our display */
screen_window_t win;                    /* window contained in an event */
screen_device_t input_dev;              /* an input device */
...
screen_create_event(&screen_ev);
while (1) {
    while (!screen_get_event(screen_ctx, screen_ev, ~0L)) {
        screen_get_event_property_iv(screen_ev, SCREEN_PROPERTY_OBJECT_TYPE, &object_type);
        screen_get_event_property_iv(screen_ev, SCREEN_PROPERTY_TYPE, &val);
        if (rc || val == SCREEN_EVENT_NONE) {
            break;
        }
        switch (val) {
            case SCREEN_EVENT_DISPLAY:
                {
                    /* Here, you may wish to query the display type, to identify the display who's
                     * sending this event and check its SCREEN_PROPERTY_ATTACHED property to see if it's
                     * a new display being connected, or a display that's been disconnected.
                     */
                     ...
                }
                break;
            case SCREEN_EVENT_IDLE:
                {
                    /* This event type likely indicates that there hasn't been any input received by a
                     * display within its SCREEN_PROPERTY_IDLE_TIMEOUT period. At this point, you
                     * may want to tally your keep-awakes and determine whether or not to put the display into
                     * an idle mode.
                     */
                    ...
                }
            break;
            case SCREEN_EVENT_CREATE:
                {
                    /* Decide what actions need to be performed based on the object type you queried. If the
                     * object type is a window, then it's likely that a window was created, or a child window joined a
                     * window group, or a window joined a group. Only window managers, parents, or group owners will
                     * receive a create event for windows being created or joining. Another reason why a create event
                     * can be received is if you're being granted access to a window through permissions.
                     */
                    switch (object_type) {
                        case SCREEN_OBJECT_TYPE_WINDOW:
                        {
                            screen_get_event_property_pv(screen_ev, SCREEN_PROPERTY_WINDOW, (void **)&win);
                            /* When you are done using this window, call screen_destroy_window() to free resources
                             * that are locally allocated by Screen to track this window in your local context.
                             */
                            ...
                        }
                        break;
                        ...
                    }
                 }
                 break;
            case SCREEN_EVENT_PROPERTY:
                {
                    screen_get_event_property_iv(screen_ev, SCREEN_PROPERTY_NAME, &property_name);
                    /* You can retrieve the name of the property that was changed from the object
                     * (once you know what object this event was for) and decide how to act accordingly.
                     */
                    switch (object_type) {
                        case SCREEN_OBJECT_TYPE_WINDOW:
                        {
                            screen_get_event_property_pv(screen_ev, SCREEN_PROPERTY_WINDOW, (void **)&win);
                            /* When you are done using this window, call screen_destroy_window() to free resources
                             * that are locally allocated by Screen to track this window in your local context.
                             */
                            switch (property_name) {
                                case SCREEN_PROPERTY_STATUS:
                                {
                                    screen_get_window_property_iv(win, SCREEN_PROPERTY_STATUS, &val);
                                    ...
                                }
                                break; 
                                ...
                             }
                        }
                        break;
                        ...
                    }
                }
                break;
            case SCREEN_EVENT_CLOSE:
                 {
                    /* Decide what actions need to be performed based on the object type you queried. If the
                     * object type is a window, then likely the window is destroyed, or a child window left a window
                     * group, or a window left a group. Only window managers, parents, or group owners will receive a
                     * close event for windows leaving or being destroyed.
                     * When a window is destroyed, you need to call screen_destroy_window() to destroy your local
                     * handle. However, the appropriate screen_destroy_* API that you need to call depends on the
                     * object type of this event.
                     *
                     * Another reason why a close event can be received is if you're being denied access to a window
                     * through permissions. 
                     */
                    switch (object_type) {
                        case SCREEN_OBJECT_TYPE_WINDOW:
                        {
                            screen_get_event_property_pv(screen_ev, SCREEN_PROPERTY_WINDOW, (void **)&win);
                            if (win == NULL) {
                                /* Local resources that were allocated by Screen to track this object have been
                                 * released, you have no further action to free resources.
                                 */
                            }
                            else {
                                screen_destroy_window(win);
                                /* You must free the local resources that were allocated by Screen to track this object. */
                            }
                        }
                        /* These following objects require that you free the local resources that were allocated
                         * by Screen on a close event or when you are done using them:
                         * - group
                         * - pixmap
                         * - session
                         * - stream
                         * - window
                         */
                        break;
                        ...
                    }
                 }
                 break;
            case SCREEN_EVENT_POST:
                {
                    /* A window or stream requested to have its content displayed for the first time. In the case
                     * of a window, the window manager or parent window needs to determine the layout and adjust other
                     * windows' sizes and positions accordingly.
                     */
                     switch (object_type) {
                        case SCREEN_OBJECT_TYPE_WINDOW:
                        {
                            screen_get_event_property_pv(screen_ev, SCREEN_PROPERTY_WINDOW, (void **)&win);
                            /* When you are done using this window, call screen_destroy_window() to free resources
                             * that are locally allocated by Screen to track this window in your local context.
                             */
                            ...
                        }
                        break;
                        ...
                    }
                 }
                break;
            case SCREEN_EVENT_POINTER:
                {
                    /* A pointer event was received. You can retrieve the position of the cursor and the
                     * status of the buttons to determine what user action was taken. For example, your
                     * application can track the position and button status from a series of pointer
                     * events to determine when the user selects an exit button. At this point,
                     * your application can take appropriate action to gracefully exit.
                     */
                     ...
                     screen_get_event_property_iv(screen_ev, SCREEN_PROPERTY_MOUSE_HORIZONTAL_WHEEL, &val);
                     screen_get_event_property_iv(screen_ev, SCREEN_PROPERTY_MOUSE_WHEEL, &val);
                     ...
                }
                break;
            case SCREEN_EVENT_KEYBOARD:
                {
                    /* A keyboard event was received. You can retrieve the pertinent properties from this event
                     * to take appropriate action. For example, if the SCREEN_PROPERTY_KEY_SYM indicates that the
                     * 'esc' key was pressed, then you can exit the application or window.
                     */
                    screen_get_event_property_iv(screen_ev, SCREEN_PROPERTY_FLAGS, &val);
                    if (val & KEY_DOWN) {
                        screen_get_event_property_iv(screen_ev, SCREEN_PROPERTY_SYM, &val);
                        switch (val) {
                            case KEYCODE_ESCAPE:
                                ...
                                return EXIT_SUCCESS;
                            default:
                                break;
                        }
                    }
                    ...
                }
                break;
            case SCREEN_EVENT_MTOUCH_TOUCH:
            case SCREEN_EVENT_MTOUCH_PRETOUCH:
            case SCREEN_EVENT_MTOUCH_MOVE:
            case SCREEN_EVENT_MTOUCH_RELEASE:
                {
                    /* A touch event was received. You can retrieve the pertinent properties from this event
                     * to take appropriate action.  For example, your application can track the position from a series
                     * of touch events (e.g., a touch, then move, then release) to determine that the user
                     * has dragged an object, so you'll need to re-render an image with changes in its position.
                     */
                     screen_get_event_property_iv(screen_ev, SCREEN_PROPERTY_TOUCH_ID, &val);
                     screen_get_event_property_iv(screen_ev, SCREEN_PROPERTY_TOUCH_ORIENTATION, &val);
                     ...
                }
                break;
        }
    }
}
...
        

A SCREEN_EVENT_CREATE, SCREEN_EVENT_CLOSE, and SCREEN_EVENT_PROPERTY event is not sent to the context that caused the event. For example, if a context creates a window, it will not receive a SCREEN_EVENT_CREATE event. For more information on when these events are dispatched, see the Screen event types section.

Multithreaded applications

If your application is multithreaded, you need to be careful when you're passing events between threads. For example, thread A calls screen_create_event() to create an event object. It uses this event object to get an event from the server by calling screen_get_event(). The event is now transferred and stored in the event object created by thread A. Thread A is responsible for getting the event, checking its type, and then passing it to another thread (thread B) to process. If thread A simply passes the event object to thread B, and then continues on to retrieve another event from the server, the event object that thread B receives may not be the same event that it was intended to process. By the time thread B gets to processing the initial event, thread A might have retrieved another event and that new event has been copied into the event object. Make sure that if you're passing events between threads, that you aren't using the same event object to retrieve events from the server.

Also keep in mind that the screen_get_event_property_*() functions, unlike most other screen_get_*_property_*() functions, are immediate execution types. What this means is that calling screen_get_event_property_*() won't flush the context, so any delayed commands that are batched won't have been processed.

Locally allocated resources

Screen locally allocates resources to track the object that's associated with a SCREEN_EVENT_CREATE event for the following Screen API objects:

Screen allocates these resources based on the object type that's associated with the SCREEN_EVENT_CREATE event when you call screen_get_event(). These resources are freed when you receive the corresponding SCREEN_EVENT_CLOSE event.

However, if at one time in your context, you acquire a handle to the object that's associated with this event (e.g., by calling screen_get_*_property_pv()), Screen won't free the local resources. You must call the appropriate screen_destroy_*() function to free these resources when you no longer need to use the object in your local context.

Note:

For objects that have buffers associated with them (i.e., pixmaps, windows, streams), you can't access their buffer-related properties with the handle you acquired from the event. That is, calls to screen_get_*() and/or screen_set_*() functions using this handle for the following properties will be unsuccessful or not useful for your application:

  • SCREEN_PROPERTY_RENDER_BUFFER_COUNT
  • SCREEN_PROPERTY_FRONT_BUFFER_COUNT
  • SCREEN_PROPERTY_BUFFER_COUNT
  • SCREEN_PROPERTY_BUFFERS
  • SCREEN_PROPERTY_FRONT_BUFFERS
  • SCREEN_PROPERTY_RENDER_BUFFERS

Therefore, we recommend the approach of always ensuring that any locally allocated resources are released when you receive a SCREEN_EVENT_CLOSE event for these aforementioned objects.

For example, a typical event handler handles SCREEN_EVENT_CREATE events as follows:

...
int val, type;                  /* used for property queries */
screen_context_t screen_ctx;    /* connection to screen windowing system */
screen_event_t screen_ev;       /* handle used to retrieve events from our context's queue */
screen_window_t win;            /* window contained in an event */
...
screen_create_event(&screen_ev);
while (1) {
    while (!screen_get_event(screen_ctx, screen_ev, ~0L)) {
        screen_get_event_property_iv(screen_ev, SCREEN_PROPERTY_TYPE, &val);
        switch (val) {
            ...
            case SCREEN_EVENT_CREATE:
                {
                    screen_get_event_property_iv(screen_ev, SCREEN_PROPERTY_OBJECT_TYPE, &type);
                    switch (type) {
                        case SCREEN_OBJECT_TYPE_WINDOW:
                        {
                            screen_get_event_property_pv(screen_ev, SCREEN_PROPERTY_WINDOW, (void **)&win);
                            ...
                        }
                        break;
                        ...
                    }
                 }
                 break;
...
            

By calling screen_get_event_property_pv() with the SCREEN_PROPERTY_WINDOW property, you retrieve a handle to a window object (win). Screen allocates resources in your local context to track this window. Because you now have a reference to this window, Screen won't automatically free the resources when you receive a corresponding SCREEN_EVENT_CLOSE event for that window. It's up to your application to free the resources on receipt of the SCREEN_EVENT_CLOSE event for this window. For example:

...
case SCREEN_EVENT_CLOSE:
{
    screen_get_event_property_iv(screen_ev, SCREEN_PROPERTY_OBJECT_TYPE, &type);
    switch (type) {
        case SCREEN_OBJECT_TYPE_WINDOW:
        {
            screen_get_event_property_pv(screen_ev, SCREEN_PROPERTY_WINDOW, (void **)&win);
            if (win == NULL) {
            /* Local resources that were allocated by Screen to track this object have been
             * released, you have no further action to free resources.
             */
             }
             else {
                screen_destroy_window(win);
                /* You must free the local resources that were allocated by Screen to track this object. */
             }
         }
         break;
...
            

In some cases, you may not receive a SCREEN_EVENT_CLOSE event. An example is when the SCREEN_EVENT_CREATE event is the result of a gain in permissions access to the object. The owner may grant, but not take away, permissions to the object. In this case, you would receive only a SCREEN_EVENT_CREATE event. For the types of objects that Screen tracks in your local context, you must explicitly call the appropriate screen_destroy_*() function when you no longer have use for the object.