Create the child windows

In the sample application, the hourglass and bar are implemented as child windows. This short walkthrough describes how to create the child windows for the hourglass and bar. Like the background window, the bar and hourglass windows never change; we need to fill a single buffer and only post it once.

Create the child window for the bar

First, create and initialize variables to store the hourglass and bar child windows. Also, create variables to store IDs for each of the window types. The IDs are used to identify each window during the event loop.

screen_window_t screen_hg_win = NULL;
screen_window_t screen_bar_win = NULL;

const char *hg_id_string = "hourglass";
const char *bar_id_string = "bar";
      

Next, create the create_bar_window() function. This will be used to create the child window for the bar. The function takes a string that is used as a window group name, a char that defines the ID of the window, and an array of integers that define the size of the window.

Note that you create a new screen_context_t instance for each window. Creating a separate context for each child window allows us to go over the steps required to deal with child windows that are created by other processes. Note that window permissions are handled per context, and not per process.

void create_bar_window(const char *group, const char *id, int dims[2])
{
	screen_context_t screen_ctx;
	screen_create_context(&screen_ctx, SCREEN_APPLICATION_CONTEXT);
}
      

Next, in the create_bar_window() function, create the child window by calling the screen_create_window_type() function and by specifying the SCREEN_CHILD_WINDOW constant. This establishes the window as a child window. Each child window is passed an ID (created earlier) so that it can be identified by the event loop.

After the window is created, the screen_join_window_group() function is called by specifying the ID of the main window group to which this child window will belong. Remember that the group ID was passed into the create_bg_window() function and used as the group ID for the parent window.

screen_window_t screen_win;
screen_create_window_type(&screen_win, screen_ctx, SCREEN_CHILD_WINDOW);
screen_join_window_group(screen_win, group);
screen_set_window_property_cv(screen_win, SCREEN_PROPERTY_ID_STRING, strlen(id), id);
        

Next, in the create_bar_window() function, set the window visibility to 0, making the window invisible. Setting the visibility is a responsibility of the parent. Parent windows must always set the visibility of each child window to true when appropriate.

int vis = 0;
screen_set_window_property_iv(screen_win, SCREEN_PROPERTY_VISIBLE, &vis);
	

Next, in the create_bar_window() function, use the trick from the previous tutorial to set the background color of the bar.

int color = 0xff0000ff;
screen_set_window_property_iv(screen_win, SCREEN_PROPERTY_COLOR, &color);

int rect[4] = { 0, 0, 1, 1 };
screen_set_window_property_iv(screen_win, SCREEN_PROPERTY_BUFFER_SIZE, rect+2);

int pos[2] = { -rect[2], -rect[3] };
screen_set_window_property_iv(screen_win, SCREEN_PROPERTY_SOURCE_POSITION, pos);

screen_buffer_t screen_buf;
screen_create_window_buffers(screen_win, 1);
screen_get_window_property_pv(screen_win, SCREEN_PROPERTY_RENDER_BUFFERS, (void **)&screen_buf);
screen_post_window(screen_win, screen_buf, 1, rect, 0);
        

Create the child window for the hourglass

The child window for the hourglass is created in much the same manner as the child window that contains the bar. You can see the complete code sample later on in the tutorial. The main differences are described below.

First, because the window will never change, use the static window property to tell Screen that the contents of the buffer won't change and aren't ever expected to post. This allows Screen to optimize the work required to put this window on the display.

int flag = 1;
screen_set_window_property_iv(screen_win, SCREEN_PROPERTY_STATIC, &flag);
        

Next, in the create_hg_window() function, set the pixel format. Because the hourglass shape will use transparency, you must use a pixel format with an alpha channel. Below, RGBA8888 is used.

int format = SCREEN_FORMAT_RGBA8888;
screen_set_window_property_iv(screen_win, SCREEN_PROPERTY_FORMAT, &format);
            

Next, in the create_hg_window() function, set the transparency property so that the hourglass window buffer source rectangle will appear over top of the background window. By default, RGBA8888 formats will have the transparency mode set to SCREEN_TRANSPARENCY_SOURCE_OVER. The windowing system assumes that if an application chooses RGBA over RGBX, it intends to do at least some blending. Note that it is always good practice to set the transparency mode.

int transparency = SCREEN_TRANSPARENCY_SOURCE_OVER;
screen_set_window_property_iv(screen_win, SCREEN_PROPERTY_TRANSPARENCY, &transparency);
            

Next, set the buffer size. Since the hourglass shape is 100x100, simply set the buffer size to match those dimensions. The source rectangle will default to 100x100 once the buffer size is set, so there is no need to set it. The on-screen dimensions of the child window will also default to 100x100.

Remember that parent windows are responsible for setting the position and size of each child window. Do not set those properties here. Instead, let the event loop do that once all windows are ready to be made visible.

Memory is allocated for the buffer, then a handle to the buffer is returned by calling the screen_get_window_property_pv() function and specifying the SCREEN_PROPERTY_RENDER_BUFFERS constant. A pointer to the buffer is returned by calling the screen_get_buffer_property_pv() property and specifying the buffer handle. This pointer will be used to fill the hourglass shape.

int rect[4] = { 0, 0, 100, 100 };
screen_set_window_property_iv(screen_win, SCREEN_PROPERTY_BUFFER_SIZE, rect+2);

screen_buffer_t screen_buf;
screen_create_window_buffers(screen_win, 1);
screen_get_window_property_pv(screen_win, SCREEN_PROPERTY_RENDER_BUFFERS, (void **)&screen_buf);

char *ptr = NULL;
screen_get_buffer_property_pv(screen_buf, SCREEN_PROPERTY_POINTER, (void **)&ptr);
            

Next, in the create_hg_window() function, draw the shape of the hourglass in the buffer.

The stride is the number of bytes between pixels on different rows. That is, if a pixel at position (x,y) is at ptr, the pixel at location (x,y+1) will be at ptr+stride. There is no guarantee that each line is 400 bytes in this case. Drivers often have constraints that will require the stride to be larger than the width in pixels times the number of bytes per pixel.

The hourglass shape is simple enough that it can be calculated. Below, the alpha channel is adjusted to be transparent or opaque based on a test that determines if a pixel is inside or outside of the hourglass shape.

int stride = 0;
screen_get_buffer_property_iv(screen_buf, SCREEN_PROPERTY_STRIDE, &stride);

for (i = 0; i < rect[3]; i++, ptr += stride) {
       for (j = 0; j < rect[2]; j++) {
            ptr[j*4] = 0xa0;
            ptr[j*4+1] = 0xa0;
            ptr[j*4+2] = 0xa0;
            ptr[j*4+3] = ((j >= i && j <= rect[3]-i) 
               || (j <= i && j >= rect[3]-i)) ? 0xff : 0;
        }    
}
        

Finally, call the screen_post_window() function to post the buffer. This will allow the hourglass child window to become visible when the event loop decides to make it visible. It is customary for the first post to have a single dirty rect that covers the entire buffer.

screen_post_window(screen_win, screen_buf, 1, rect, 0);
            

Now that you've created functions to create a parent window and two child windows, you can implement the logic of the sample application. The logic, which creates the windows and sets up an event loop, is defined in the main() function.