Create the main() function

The main function calls the window creation functions, defines the size of the application window, and defines the event loop that controls the flow of the sample application.

Call the window functions

First, create a context of type SCREEN_APPLICATION_CONTEXT. The SCREEN_APPLICATION_CONTEXT type can be created by any process, regardless of permission level. The context sets up a connection with Screen that lets you create windows and control some of their properties. This parent window type (SCREEN_APPLICATION_CONTEXT) can only control a child window that was created by the same context.

int main(int argc, char **argv)
{
    int pos[2], size[2];
    int vis = 0;
    int type;

    screen_context_t screen_ctx;
    screen_create_context(&screen_ctx, SCREEN_APPLICATION_CONTEXT);
	...
				

Next,you must specify the dimensions for each child window. Remember that the parent window should always determine the size and position of each child window; otherwise, the application may face potential race conditions. For example, the video mode might change during the initialization phase, or the display can be rotated. Either of these situations can lead to bad layouts. Instead, the parent should choose a layout and provide the dimensions when creating each child window.

After each window has joined the group and posted, the parent can request that each window resize if the layout changed. Once everything is perfect, each window can be made visible.

Below, each display that is associated with the context is queried. The application chooses the first display to run the sample application on.

int count = 0;
screen_get_context_property_iv(screen_ctx, SCREEN_PROPERTY_DISPLAY_COUNT, &count);
screen_display_t *screen_disps = calloc(count, sizeof(screen_display_t));
screen_get_context_property_pv(screen_ctx, SCREEN_PROPERTY_DISPLAYS, (void **)screen_disps);

screen_display_t screen_disp = screen_disps[0];
free(screen_disps);
    		

Next, query the size of the display. The display size parameters (dims) are used as the dimensions for our windows and are passed into each window creation function. Note that the display size changes when it is rotated. The display size may also change when the video mode is changed.

int dims[2] = { 0, 0 };
screen_get_display_property_iv(screen_disp, SCREEN_PROPERTY_SIZE, dims);
    		

Next, in the main() function, create a String by calling getpid() to return the process ID. This ID is passed into the create_bg_window(), create_bar_window(), and create_hg_window() functions where it is used as the window group ID. Even though process IDs are unique, there is no guarantee that this string will be unique. An application must check error codes for EEXIST and change its group name if it encounters such an error.

The bar_id_string and hg_id_string variables are passed into the child window functions. These ID strings are used to identify a window during the event loop.

char str[16];
snprintf(str, sizeof(str), "%d", getpid());
screen_bg_win = create_bg_window(str, rotation, dims, screen_ctx);

create_bar_window(str, bar_id_string, rotation, dims);
create_hg_window(str, hg_id_string, rotation, dims);
    		

Create the event loop

The event loop defines how the application responds to and processes events. The sample application listens for window events to determine when to display a window, when to close a window, and when to carry on with normal application processing.

screen_event_t screen_ev;
screen_create_event(&screen_ev);
while (1) {
    do {
         ...
    }
}

In the event loop, a screen_event_t object is instantiated and used to capture an event. There is no need to repeatedly create and destroy event objects. The same event can be reused several times. You should avoid passing an event object to another thread for processing. Calling screen_get_event() with a 0 timeout returns immediately. The event type will be SCREEN_EVENT_NONE if there were no events in the queue. Calling screen_get_event() with a timeout of -1, or ~0 will block until an event is put into the event queue.

A handle to the event is returned by calling the screen_get_event_property_iv() function.

Next, an if...else clause is set up to process the event. The application traps the SCREEN_EVENT_POST event type to ensure that the window has been properly created and initialized before it is made visible. This event is sent when a child window posts for the first time, or when a child window joins our group after having successfully posted at least once. Remember that the screen_post_window() function was called as the last step in the create_hg_window(), create_bar_window(), and create_bg_window() functions.

Once the event is trapped, the screen_get_event_property_pv() function is called to return a handle to the window that dispatched the event. The screen_get_window_property_cv() is then called to return the ID String of the child window. In theory, any process could choose to join our group, or do so accidentally. You can kick unwanted windows out of your group simply by calling screen_leave_group() on those window handles.

screen_get_event(screen_ctx, screen_ev, vis ? 0 : ~0);
screen_get_event_property_iv(screen_ev, SCREEN_PROPERTY_TYPE, &type);

if (type == SCREEN_EVENT_POST) {
    screen_window_t screen_win;
    screen_get_event_property_pv(screen_ev, SCREEN_PROPERTY_WINDOW, (void **)&screen_win);
    screen_get_window_property_cv(screen_win, SCREEN_PROPERTY_ID_STRING, sizeof(str), str);

    if (!screen_bar_win && !strcmp(str, bar_id_string)) {
        screen_bar_win = screen_win;
    } else if (!screen_hg_win && !strcmp(str, hg_id_string)) {
        screen_hg_win = screen_win;
}
	       

When all windows have been posted, the window properties are set by the parent window. Remember that all properties are relative to the parent. This includes the size, position, and z-order of each window. All these changes will be atomic, so the user won't see frames without the bar or the hourglass. The following code simply sets the screen size to fullscreen, except for the hourglass window which will be 100x100, positioned at 10,10. The z-order is set to 0 for the background, 1 for the vertical bar, and 2 for the hourglass.

if (screen_bar_win && screen_hg_win) {
    vis = 1;

    screen_get_window_property_iv(screen_hg_win, SCREEN_PROPERTY_BUFFER_SIZE, size);
    screen_set_window_property_iv(screen_hg_win, SCREEN_PROPERTY_SIZE, size);

    pos[0] = pos[1] = 10;
    screen_set_window_property_iv(screen_hg_win, SCREEN_PROPERTY_POSITION, pos);

    pos[0] = pos[1] = 0;
    screen_set_window_property_iv(screen_bar_win, SCREEN_PROPERTY_POSITION, pos);
    screen_set_window_property_iv(screen_bg_win, SCREEN_PROPERTY_POSITION, pos);

    size[0] = barwidth;
    size[1] = dims[1];
    screen_set_window_property_iv(screen_bar_win, SCREEN_PROPERTY_SIZE, size);

    size[0] = dims[0];
    screen_set_window_property_iv(screen_bg_win, SCREEN_PROPERTY_SIZE, size);

    int zorder = 0;
    screen_set_window_property_iv(screen_bg_win, SCREEN_PROPERTY_ZORDER, &zorder);
    
    zorder++;
    screen_set_window_property_iv(screen_bar_win, SCREEN_PROPERTY_ZORDER, &zorder);
    
    zorder++;
    screen_set_window_property_iv(screen_hg_win, SCREEN_PROPERTY_ZORDER, &zorder);
}
      	   

Finally, the child windows are set to visible by calling the screen_set_window_property_iv() function specifying the SCREEN_PROPERTY_VISIBLE constant. Since this function is called within the while loop, we know that all windows will appear on the screen. As usual, all the requests that were made so far have been batched in a command buffer. To ensure that those commands are flushed out and applied, you must call screen_flush_context(). The SCREEN_WAIT_IDLE flag is passed in to make sure that at least one vsync or refresh period has elapsed, with the bar at 0,0 before moving it by 1 to the right.

screen_set_window_property_iv(screen_bg_win, SCREEN_PROPERTY_VISIBLE, &vis);
screen_set_window_property_iv(screen_hg_win, SCREEN_PROPERTY_VISIBLE, &vis);
screen_set_window_property_iv(screen_bar_win, SCREEN_PROPERTY_VISIBLE, &vis);
screen_flush_context(screen_ctx, SCREEN_WAIT_IDLE);
      	

Next, the SCREEN_EVENT_CLOSE event is trapped in order to process window close events. When a window is closed, the handle to the window is set to NULL and the window is destroyed by calling the screen_destroy_window() function. The application keeps track of which window leaves the group so that it can start again if the missing window were to join the group and post.

else if (type == SCREEN_EVENT_CLOSE) {
    screen_window_t screen_win;
    screen_get_event_property_pv(screen_ev, SCREEN_PROPERTY_WINDOW, (void **)&screen_win);
    if (screen_win == screen_bar_win) {
        screen_bar_win = NULL;
    } else if (screen_win == screen_hg_win) {
        screen_hg_win = NULL;
    }
    screen_destroy_window(screen_win);

    if (!screen_bar_win || !screen_hg_win) {
        vis = 0;
    }
}
        	

While no close window events are trapped, the X position of the screen_bar_win child window is incremented by one and the screen_set_window_property_iv() function is called to update the position of the bar. When the bar reaches the right side of the screen, it automatically starts over from the left. Note that this sample produces an animation without actually rendering anything.

To prevent the animation from moving the bar too fast, the screen_flush_context() function is called with flags set to SCREEN_WAIT_IDLE. This will rate-limit the animation to the refresh rate of the display.

if (vis) {
    if (++pos[0] > dims[0] - barwidth) {
        pos[0] = 0;
    }
    screen_set_window_property_iv(screen_bar_win, SCREEN_PROPERTY_POSITION, pos);
    screen_flush_context(screen_ctx, SCREEN_WAIT_IDLE);
}
        	

The windowing system has termination handlers that will release any resource created by a process when it exits, whether it exits normally or abruptly. Although any instances created are destroyed when the application exits, it is best practice to destroy any window, pixmap, and context instances that you created but no longer require.

screen_destroy_event(screen_ev);
screen_destroy_context(screen_ctx);
return EXIT_SUCCESS;