Set up Screen

Window management requires a connection to Screen, configuration of a window, and creation of a window buffer.

Before you can manage application windows, you need to set up your window manager by doing the following:
  1. Connect to Screen.
  2. Create a window for your window manager.
  3. Set the properties for your window.
  4. Create a window buffer for your window.
  5. Post your window and flush your context.

Connect to Screen

The first step is to establish a connection between your window manager and the underlying windowing system, Screen. To set up this connection, you need to create a Screen context.

There are different context types. A standard application would use SCREEN_APPLICATION_CONTEXT. Because you're writing a window manager, you need a context type that lets you modify all the windows in the system. Specifically, you need to use SCREEN_WINDOW_MANAGER_CONTEXT; this context type enables the receipt of events when application windows are created and destroyed and when applications change their window properties.

Note that root permission is required to use the SCREEN_WINDOW_MANAGER_CONTEXT context type.

int rc = 0;
screen_context_t screen_ctx; /* connection to Screen */
rc = screen_create_context(&screen_ctx, SCREEN_WINDOW_MANAGER_CONTEXT);

Create a window for your window manager

Without a window for your window manager, you can still receive SCREEN_EVENT_CREATE and SCREEN_EVENT_CLOSE window manager events, but you can't receive any input events.

You need to create a window for your window manager so that you can receive and handle these input events:
screen_window_t screen_win;    /* native handle for our window */
rc = screen_create_window(&screen_win, screen_ctx);

Set the properties for your window

Although many window properties are available, you don't need to set them all because most have defaults that are appropriate. For a window manager, however, you need to set some particular window properties:

The intended usage for the buffers associated with the window. You need to ensure that these buffers can be written to, which means you need to set the SCREEN_USAGE_WRITE flag in the bitfield for this property.
The width and height, in pixels, of the window. By default, windows are fullscreen. You may not want your window manager's window to be fullscreen; for example, if you still want to see this window when you run multiple applications at the same time.
The window's display coordinates. You want to set this so that the position of your window manager's window isn't obscuring an application window's area of interest.
This property indicates the level from the bottom, which is used to order window groups among each other. Your window manager needs to examine the z-order settings of its own window and of all application windows so that it can display them in the correct order.
int val = 0;
rc = screen_set_window_property_iv(screen_win, SCREEN_PROPERTY_USAGE, &val);
int size[2] = { 64, 64 };      /* size of the window on screen */
rc = screen_set_window_property_iv(screen_win, SCREEN_PROPERTY_SIZE, size);
int pos[2] = { 0, 0 };         /* position of the window on screen */
rc = screen_set_window_property_iv(screen_win, SCREEN_PROPERTY_POSITION, pos);
int zorder = 0;
rc = screen_set_window_property_iv(screen_win, SCREEN_PROPERTY_ZORDER, &zorder);

Create a window buffer for your window

You need at least one buffer to hold the contents of your window so that your window will be visible.

In the simplest case, you can fill your window with a solid color so that you can see the window. Before you can do this, you'll need to query some properties of the window buffer.

The pointer to the window buffer available for rendering. It's best to first query SCREEN_PROPERTY_RENDER_BUFFER_COUNT to determine the number of window buffers you have. But in this case, there's only one, so you can simply query SCREEN_PROPERTY_RENDER_BUFFERS.
The pointer that can be used to read from and/or write to the window buffer. When you set the SCREEN_PROPERTY_USAGE to include SCREEN_USAGE_WRITE, you enable write access to this buffer. Therefore, this pointer will reference memory that you can write to.
The size, in bytes, of each line of the window buffer. This value is the number of bytes between the same pixel on adjacent rows.

For the sake of simplicity, you can just fill the window buffer with a solid color pattern. To do this, you can use memset():

screen_buffer_t screen_buf;    /* renderable buffer for the window */
rc = screen_create_window_buffers(screen_win, 1);
rc = screen_get_window_property_pv(screen_win,
                                   (void **)&screen_buf);
rc = screen_get_buffer_property_pv(screen_buf, SCREEN_PROPERTY_POINTER, &pointer);
int stride;                    /* size of each window line in bytes */
memset(pointer, 0x80, stride * size[1]);

Post your window and flush your context

To make the content rendered on your window visible, you need to post your changes to Screen. Posting to Screen indicates that you have completed drawing to your render buffer and you wish to have the changes made visible. When you post, you need to specify which area of your buffer has changed so that Screen will redraw only the parts of the framebuffer that need updating. When posting your first frame, you must post the entire buffer.

To ensure that any delayed Screen commands are processed, flush the command queue of your context after you post:

int rect[4] = { 0, 0, size[0], size[1] };
rc = screen_post_window(screen_win, screen_buf, 1, rect, 0);
rc = screen_flush_context(screen_ctx, SCREEN_WAIT_IDLE);