Targeting your displays

The display is typically controlled by a window manager. However, an application can choose a display in the absence of a window manager.

Enumerating displays

You need to start by finding out what, and how many displays are available to you in your context.

  1. After having connected Screen, get the number of displays available in your context using the SCREEN_PROPERTY_DISPLAY_COUNT property.

    Use the screen_get_context_property_iv() function with the SCREEN_PROPERTY_DISPLAY_COUNT property in your context.

    int ndisplays = 0;
    screen_context_t screen_ctx;
    ...
    screen_get_context_property_iv(screen_ctx, SCREEN_PROPERTY_DISPLAY_COUNT, &ndisplays);
    ...
                
  2. Next, using the number of displays returned from your query, allocate enough memory to retrieve an array of pointers to screen_display_t.

    screen_display_t *screen_dpy = calloc(ndisplays, sizeof(screen_display_t));
                
  3. Once you have the number of displays available in your context, retrieve the array of displays.

    screen_get_context_property_pv(screen_ctx, SCREEN_PROPERTY_DISPLAYS, (void **)screen_dpy);
                       

    If you're using multiple displays, you may wish to use some sort of data structure to track your displays and the state of each display. For example:

    struct {
    	pthread_mutex_t mutex;
    	pthread_cond_t cond;
    	enum { detached, attached, focused } state;
    } *displays;
    
    displays = calloc(ndisplays, sizeof(*displays));
                        

    In the above example, the structure for each display tracks, in addition to the state of the display, a mutex, and a condition object. This example tracks three states for the display: detached, attached, and focused. Your application affects which display states you need to keep track of. The display property SCREEN_PROPERTY_ATTACHED gives you the attached or detached state of the display, but you must determine from your application which display is the one in focus, if you're tracking that state as well.

  4. Iterate over this display list to determine the state of each display. The state of a display can be determined by its SCREEN_PROPERTY_ATTACHED property. This property indicates whether the display is currently attached. Although the display may exist in the context, it might not be attached (connected, and usable) (e.g., a display that uses a HDMI cable).

    For each attached display, this example initializes a mutex and calls pthread_create() to create a new thread to process the rendering specific to that display. It passes in a start routine that handles the graphics operations for that display to the new thread. This approach used in this example helps when you're using multiple displays. If you spawn a child thread to handle the processing for each of your displays, each display will be written to and updated within its own process. This allows the graphics processor to handle intensive operations, and ensures that if an error occurs or the display becomes detached, the application itself will not fail.

    int i, idx = -1;
    for (i = 0; i < ndisplays; i++) {
        int active = 0;
        screen_get_display_property_iv(screen_dpy[i], SCREEN_PROPERTY_ATTACHED, &active);
        if (active) {
            if (idx == -1) {
                displays[i].state = focused;
                idx = i;
            } else {
                displays[i].state = attached;
            }
        } else {
            displays[i].state = detached;
        }
        pthread_mutex_init(&displays[i].mutex, NULL);
        pthread_cond_init(&displays[i].cond, NULL);
    
        pthread_t thread;
        pthread_create(&thread, NULL, display, (void *)i);
    }
                        

    In the above example, the display() function is passed in as the thread's start routine. This function is responsible for setting up the display and the window, then locking the mutex to determine whether the current display is active and has focus. The function also posts the window so that its contents are displayed.

Targeting displays

The example we're using runs without a window manager, and targets displays. This example is useful to a window manager, or to an application that's running in a system without a window manager.

After having enumerated your displays, and having created your application windows, you can associate each window to a display. You must call screen_set_window_property_pv() using the SCREEN_PROPERTY_DISPLAY property to make this association. And by "you", we mean whoever is the manager (which could be the parent) of the window. SCREEN_PROPERTY_DISPLAY is a property that can't normally be set by the owner of the window.

screen_context_t screen_ctx;
screen_create_context(&screen_ctx, SCREEN_INPUT_PROVIDER_CONTEXT);

int ndisplays = 0;
screen_get_context_property_iv(screen_ctx, SCREEN_PROPERTY_DISPLAY_COUNT, &ndisplays);

screen_display_t *screen_dlist = calloc(ndisplays, sizeof(*screen_dlist));
screen_get_context_property_pv(screen_ctx, SCREEN_PROPERTY_DISPLAYS, (void **)screen_dlist);

screen_display_t disp = screen_dlist[0]; // any screen_display_t from screen_dlist
screen_set_window_property_pv(screen_win, SCREEN_PROPERTY_DISPLAY, (void **)&disp);
            

Child windows inherit the SCREEN_PROPERTY_DISPLAY property from their parent, but ultimately, it's the window manager that's targeting the display. Sometimes, window managers have to manage the illusion of one window appearing on multiple displays. Refer to the Windows section from the "Cloning" chapter. The function screen_share_window_buffers() can be used to achieve this effect.