Set up Screen

Window management requires a connection to Screen.

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

Create a connection 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. But since you're writing a window manager, you need a context type that will let you modify all the windows in the system. You need to use the 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 windowing system */
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 receive window manager events such as SCREEN_EVENT_CREATE and SCREEN_EVENT_CLOSE. However, you won't be able to 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 on your window

Many window properties are available, but you don't need to set them all because most have defaults that are already appropriate. But for a window manager, there are some particular window properties that you'll need to set:

The intended usage for the buffer(s) associated with the window. You need to ensure that the buffer(s) associated with the window can be written to. The bitfield you need set for this property is SCREEN_USAGE_WRITE.
The width and height, in pixels, of the window. By default, the windows are fullscreen. You may not necessarily want your window manager's window to be fullscreen because when you run multiple applications at the same time, you'll also want to see your window manager's window.
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 that is used when ordering window groups among each other. Your window manager will need to manage the z-order for its own window along with the application windows so that the windows are displayed 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 that is 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 enabled write access to this buffer. Therefore, this pointer will be to memory that you can write to.
The size, in bytes, of each line of the window buffer (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 a plain 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 you render 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 the area of your buffer that has changed so that Screen will redraw only the parts of the framebuffer that need updating. When you're posting your first frame, you'll want to 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);