What is a property in X11?

 

In X11, a property simply has a name, a type, a format, a value, and is associated with a window.

There is a set of functions, provided by X11, which allows manipulating a property, and they are : XChangeProperty, XGetWindowProperty, XDeleteProperty, XListProperties, XRotateWindowProperties.

In the next sections of this tutorial, we will be discussing these functions, and this will make clearer what a property really is.

XChangeProperty : Create or update a property

The XChangeProperty function, is used to create a property, or to update its value.

Once a property has been created or updated, the XChangeProperty function, will generate a PropertyNotify event, which indicates, that a property associated with this window, has been changed.

The signature of the XChangeProperty function is:

XChangeProperty
     (Display* display, 
      Window window, 
      Atom property_name, 
      Atom property_type, 
      int format, 
      int operation_mode, 
      unsigned char* property_data, 
      int number_elements_data )

display

display, is just a reference, to a connection to the server, a connection can be gotten, by using the XOpenDisplay function.

window

window is the id of the window, to which the property will be associated. A window is where your application, is going to do the drawing, and is created using either of, the XCreateSimpleWindow, or the XCreateWindow functions. Both of these functions, return the id of the created window.

The window id, has a type of Window, which is defined as typedef XID Window;, where XID is defined as typedef unsigned long XID; .

property_name

property_name , is of type atom. An atom , is an integral value, as in 1 or 2, which uniquely identify a string, any string, to be, or which is stored on the server. An atom is 32 bits.

So the idea is that an atom, is used in function calls, instead of a string, to minimize data transfer.

This being said, the property_name , is actually a string, which is stored on the server, and which has an atom , meaning an integral value, associated with it. To create an atom from a string, and to get a string from an atom, is explained in this tutorial .

This being said, there are some predefined property names, such as WM_COMMAND, as in, this property is named WM_COMMAND, or WM_ICON_NAME , or RESOURCE_MANAGER … which are stored on the server.

These predefined property names, are there to be used, for example, the window manager, will query a window, for a property named WM_HINTS, in order to identify, the window icon.

This being said, a property can be used for inter process, and inter windows communication.

An application can create a property, associate it with any window, even if that window, was not created by it, and store data into it, and any other application, can read properties, which are associated with any window, and can subscribe to the property change notify event, in order to be notified, when this property has been changed.

This being said, an application can also define, its own property names, and property types.

property_type

property_type , is of type atom, so as described earlier, a property_type, is a string stored on the server, and which has an atom associated with it. This atom is used in function calls.

Some predefined property types, also exist on the server, for example XA_WM_HINTS, is an atom, which is defined as #define XA_WM_HINTS ((Atom) 35), in the <X11/Xatom.h> header.

The <X11/Xatom.h> header, contains some predefined atoms, associated with strings, stored on the server, the strings are used, for example, for property names, property types, font properties …

To get the string, associated with these predefined atoms, you can use the XGetAtomName function, or you can just remove the XA_ part. So for example, the atom XA_WM_HINTS , is associated with the string WM_HINTS, which is stored on the server.

The WM_HINTS string, is used as both a name, and a type, for a property. The type is actually a structure, the structure XWMHints, which is defined as follows:

typedef struct {
    long flags; 
    Bool input;
    int initial_state;
    Pixmap icon_pixmap; 
    Window icon_window;
    int icon_x;
    int icon_y;
    Pixmap icon_mask;
    XID window_group;} XWMHints;

Other examples of a property types, are the string POINT, which is associated with the atom XA_POINT, and which is actually the structure :

typedef struct {
    short x, y;
} XPoint;

This being said, knowing the type of a property, you can interpret its data.

format

format , is of type int, and it must be one of the three values, 8, 16, or 32.

A property has data, the format represents, how many bits, a unit of data, is formed of, so a unit of data, can be formed of, 8, 16, or 32 bits.

If the format specified is 8, then the property data, must be a char array, if it is 16, then the property data, must be, a short array, and if it is 32, then the property data, must be a long array.

Why long you might ask, for 32 bits? Simply because, int is defined by the C standard, as having a minimum of 16 bits, and long, as having a minimum of 32 bits. So whatever what long actually is, so even if it is 64 bits on your client, the server handles, the data as 32 bits. You can check the example for XGetWindowProperty, in the next section, to understand this more.

The data format, is mainly related to, little and big endian, which is how bits are stored into a computer, like is the most significant bit stored first, or last?

A property is stored on the server, hence its data is stored on the server, this being said, the endianness of the server, can be different from that of the client, hence it is important to specify the format correctly, as such the library would handle, the conversion between client and server endianness automatically.

operation_mode

operation_mode , is of type int, and it can take one of three values, defined in the <X11/X.h> header, and which are:

PropModeAppend , is defined as #define PropModeAppend 2, so basically the newly sent data, is appended, to the old data, and the data type and format, must match the old ones, or else, a BadMatch error is generated.

PropModePrepend , is defined as #define PropModePrepend 1, in this case, the newly sent data, is prepended, to the old data, and the type and format, of the new data, must match, the old ones, or a BadMatch error is generated.

PropModeReplace , is defined as #define PropModeReplace 0, the old data is discarded, and replaced with the new data, having the passed in format, and data type.

property_data

property_data , is of type unsigned char*, so basically, this is just a pointer to the data, that you want to be stored in this property.

number_elements_data

number_elements_data , is of type int, this is the number of units, so the number of units of, 8 or 16 or 32 bits data, which is being sent.

Example

In this example, the XChangeProperty method, is used to set a property named WM_NAME. A property with such a name, tells the window manager, that its data, is to be used, as the title, for the window, the window being a top level window.

#include <X11/Xlib.h>
#include <X11/Xatom.h>

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int
        main
            (int argc , char* argv[ ] ){

    // get a connection to the server
    char* connection_string = NULL;
    Display* display;
    display =  XOpenDisplay (connection_string  );
    if (display == NULL ){
        fprintf (stderr, "Error: XOpenDisplay (%s )\n", connection_string == NULL ? "NULL" :  connection_string );
        exit (EXIT_FAILURE );}

    // create a top level window
    Window top_level_window;
    top_level_window = XCreateSimpleWindow (
        display ,
        XDefaultRootWindow (display ), //parent window
            // set the parent of top_level_window to the root window
        0, //x position
        0, //y position
        300, //width
        300, //height
        0, //border width
        0xFFFFFFFF , //border color
        0xFF112233 /*background color .*/ );

    XChangeProperty (display, //The connection
        top_level_window, //The window
        XA_WM_NAME, //atom, Property name
        XA_STRING, //atom, Property type
        8, //Property format
        PropModeAppend, //Property mode
        (unsigned char *) "Hey world", //Property data
        strlen ("Hey world") ); //Number of data units

    XSelectInput (display, top_level_window, ExposureMask );
    //select the event, we want to handle
    //In this case, the window being exposed

    XMapWindow (display, top_level_window );
    //Register a window to be
    //exposed later on.

    XEvent xevent;
    while (true ){ //handle events
        XNextEvent (display, &xevent );
        switch (xevent .type ){
            case Expose:
        default:
            break; }}

    XCloseDisplay(display);
    return 0;}

Errors that can be caused by the function

The XChangeProperty function, can cause a BadWindow error, if the window is not recognized, by the server, as being defined.

It can also cause a BadAtom error, if the atom is not valid, hence undefined on the server.

A BadMatch error, is generated, as explained earlier, when for example, you specify a mode of PropModeAppend, or PropModePrepend, and the type and format of the new data, do not match those of the old data.

A BadValue error, is generated, if a numerical value, is not within the required range.

A BadAlloc error, is generated, when the server is not able, to allocate, the requested memory.

XGetWindowProperty : Retrieve a property

What is XGetWindowProperty ?

As explained earlier, a window can have a property, a property has a name, a type, a format, and data, hence the aim of this function is to enable us, to retrieve all information, related to a property, so its type, its format, its data.

Function signature ?

The signature of the XGetWindowProperty function is:

int XGetWindowProperty 
    (Display* display, 
     Window window, 
     Atom  property_name, 
     long data_offset_32, 
     long data_length_32, 
     Bool delete,
     Atom requested_property_type, 
     Atom* returned_property_type, 
     int* returned_property_format, 
     unsigned long* returned_number_data_units, 
     unsigned long* number_of_bytes_remaining_after_return, 
     unsigned char** retrieved_data ); 
  • Display, is a reference to a connection to the server.
  • window is a window, for which, you want to get the named property.
  • property_name, is an atom, identifying, the name of the property, for which information, and data is to be retrieved.

    If property_name, is not valid, for the specified window, then the delete argument is ignored, the *returned_property_type, is set to the value None, which is defined as #define None 0L, in the <X11/Xatom.h> header, *returned_property_format is set to 0, and *number_of_bytes_remaining_after_return, is also set to 0.

    So in other words, the request for information, for this property, has failed, hence its important, to set a property name, correctly.

  • data_offset_32 is an offset, which is a multiple of 32, as in 32 bits. For example, it can be 0, so the offset is 0, it can be 1, so the offset is 32, it can be 2, so the offset is 64

    The offset, is calculated from the start of the data, associated with this property, so in other words, it is from this offset, that the property data will be retrieved.

    If the offset, is larger than the number of bytes, for this property data, than this will generate, a BadValue error.

  • data_length_32, is the number of 32 bits, block of data, that you wish to retrieve.

    If you pass in, a data length, which is larger, than the number of remaining bytes of data, then data length is set to, the number of remaining bytes of data.

    if data_length_32, is set to a negative value, then this will generate, a BadValue error.

  • delete if true, means that this property, is going to be deleted, if and only if number_of_bytes_remaining_after_return is 0 .

    If the property is deleted, then a PropertyNotify event, is generated, for the window, associated with this property.

  • property_type_requested is the requested type of the property .

    If the property does exist on the window, but its type does not match the requested type, then it is like querying the property, for its information, so what is going to happen is that, the delete argument is ignored, the returned_property_type, will be a reference to the actual property type, the returned_property_format will be a reference to the actual property format, and number_of_bytes_remaining_after_return, will be a reference to the number of bytes of data, for this property.

    If the type does match, or if it is set to the value AnyPropertyType , then all the values to be returned are set, the *returned_property_type, is set to the actual property type, the *returned_property_format is set to the actual property format, *returned_number_data_units is set to the number of units, as defined in property format, as in 8 or 16 or 32, that have been returned, *number_of_bytes_remaining_after_return, is set, to the number of bytes, remaining in the property data, after the returned data, and *retrieved_data is a reference to the retrieved data.

  • property_type_returned, is a reference, to the actual type of the property, as the server sees it.
  • property_format_returned, is a reference, to the actual format, of the data property, so it can be one of, 8, 16, or 32 bit.
  • number_units_data_returned, is a reference, to the number of 8 or 16 or 32 bits, which is the format of the data, that is returned.
  • number_of_bytes_remaining_after_return , is a reference, to the number of bytes remaining, after having gotten, the requested data.
  • retrieved_data, is of type unsigned char** , so this means, that you provide the address, of an unsigned char*.

    The library will allocate memory, to the property data, returned by the server, and will make your passed in pointer, point to the first element, of this memory. It is your job, to free this memory, once done using it, using XFree .

    If the property data format, is 8 bits, then the memory pointed, by your pointer, will be of type char , if the property data format is 16 bits, then your pointer, is pointing to a memory of type short, and if it is 32 bits, then your pointer is pointing to a memory, of type long.

    The data which has been retrieved, is always padded with a zero byte.

The function, returns the value Success, defined in the header <X11/Xlib.h> as #define Success 0, on success.

Example

In the following example, a property with an invalid name, is first gotten, and after that, all the data, related to a property, with a valid name, is retrieved .

#include <X11/Xlib.h>
#include <X11/Xatom.h>

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int
        main
            (int argc , char* argv[ ] ){

    // get a connection to the server
    char* connection_string = NULL;
    Display* display;
    display =  XOpenDisplay (connection_string  );
    if (display == NULL ){
        fprintf (stderr, "Error: XOpenDisplay (%s )\n", connection_string == NULL ? "NULL" :  connection_string );
        exit (EXIT_FAILURE );}


    // create a top level window
    Window top_level_window;
    top_level_window = XCreateSimpleWindow (
        display ,
        XDefaultRootWindow (display ), //parent window
            // set the parent of top_level_window to the root window
        0, //x position
        0, //y position
        300, //width
        300, //height
        0, //border width
        0xFFFFFFFF , //border color
        0xFF112233 /*background color .*/ );


    /*Declare the values, to be returned
        by a call to the XGetWindowProperty
        function. */
    Atom returned_property_type;
    int returned_property_format;
    unsigned long returned_number_data_units;
    unsigned long number_of_bytes_remaining_after_return;
    long* retrieved_data;


    //Getting a property with an invalid name
    int status = XGetWindowProperty (display, //The connection
                    top_level_window, //The window
                    XA_WM_NAME, //The property name
                    0, //offset in 32 bits
                    1, //number of elements in 32 bits
                    False, //delete
                    XA_INTEGER, //requested_property_type
                    &returned_property_type,  //returned_property_type
                    &returned_property_format, //returned_property_format
                    &returned_number_data_units, //returned_number_data_units
                    &number_of_bytes_remaining_after_return, //number_of_bytes_remaining_after_return
                    (unsigned char** ) &retrieved_data /*retrieved_data */ );

    printf("Results of getting a property, with an invalid name: \n" );
    printf("\tstatus is : %d\n", status );
    /*If returned_property_type is None,
        and both of returned_property_format
        and returned_number_data_units are
        0, then the property name is
        invalid.*/
    printf("\treturned_property_type is : %lu\n", returned_property_type );
    printf("\treturned_property_format is : %d\n", returned_property_format );
    printf("\treturned_number_data_units is : %lu\n\n\n", returned_number_data_units );
    /* Output:
    Results of getting a property, with an invalid name:
        status is : 0
        returned_property_type is : 0
        returned_property_format is : 0
        returned_number_data_units is : 0 */


    //Create an atom for a string
    //which will be used, as our
    //own property name.
    Atom my_prop_atom = XInternAtom (display, "MY_PROPERTY", false );
    int my_prop_data_len = 5;
    long my_prop_data [ ] = {4294967297, 5, 6, 8, 10 };

    //Define a property, on the
    //top level window.
    XChangeProperty (display, //The connection
        top_level_window, //The window
        my_prop_atom, //atom, Property name
        XA_INTEGER, //atom, Property type
        32, //Property format
        PropModeAppend, //Property mode
        (unsigned char *) my_prop_data, //Property data
        my_prop_data_len ); //Number of data units


    printf("Results of getting a property, with a valid name: \n" );
    long offset = 0;
    long length = 1;
    do{
        status = XGetWindowProperty (display, //The connection
                        top_level_window, //The window
                        my_prop_atom, //The property name
                        offset, //offset in 32 bits
                        length, //number of elements in 32 bits
                        False, //delete
                        XA_INTEGER, //requested_property_type
                        &returned_property_type,  //returned_property_type
                        &returned_property_format, //returned_property_format
                        &returned_number_data_units, //returned_number_data_units
                        &number_of_bytes_remaining_after_return, //number_of_bytes_remaining_after_return
                        (unsigned char** ) &retrieved_data /*retrieved_data */ );

        printf("\tstatus is : %d\n", status );
        printf("\treturned_property_type is : %lu\n", returned_property_type );
        printf("\treturned_property_format is : %d\n", returned_property_format );
        printf("\treturned_number_data_units is : %lu\n", returned_number_data_units );
        printf("\tnumber_of_bytes_remaining_after_return is : %lu\n", number_of_bytes_remaining_after_return );
        printf("\tretrieved property data is: %ld\n\n", *retrieved_data );
        offset++;
        XFree (retrieved_data );
    }while (number_of_bytes_remaining_after_return != 0 );
    /* Output:
    Results of getting a property, with a valid name:
        status is : 0
        returned_property_type is : 19
        returned_property_format is : 32
        returned_number_data_units is : 1
        number_of_bytes_remaining_after_return is : 16
        retrieved property data is: 1

        status is : 0
        returned_property_type is : 19
        returned_property_format is : 32
        returned_number_data_units is : 1
        number_of_bytes_remaining_after_return is : 12
        retrieved property data is: 5

        status is : 0
        returned_property_type is : 19
        returned_property_format is : 32
        returned_number_data_units is : 1
        number_of_bytes_remaining_after_return is : 8
        retrieved property data is: 6

        status is : 0
        returned_property_type is : 19
        returned_property_format is : 32
        returned_number_data_units is : 1
        number_of_bytes_remaining_after_return is : 4
        retrieved property data is: 8

        status is : 0
        returned_property_type is : 19
        returned_property_format is : 32
        returned_number_data_units is : 1
        number_of_bytes_remaining_after_return is : 0
        retrieved property data is: 10 */

    XSelectInput (display, top_level_window, ExposureMask );
    //select the event, we want to handle
    //In this case, the window being exposed

    XMapWindow (display, top_level_window );
    //Register a window to be
    //exposed later on.

    XEvent xevent;
    while (true ){ //handle events
        XNextEvent (display, &xevent );
        switch (xevent .type ){
            case Expose:
        default:
            break; }}

    XCloseDisplay(display);
    return 0;}

Errors that can be caused by the function

This function can cause a BadAtom error, if a passed atom, is not identified by the server. It can also cause a BadValue error, if a numerical value, for an argument, is not within a required range, and finally, it can cause a BadWindow error, if a window id, is not identified by the server.

XListProperties : List window properties

What is XListProperties ?

The XListProperties function, can be used, to list the properties of a window.

The signature of the XListProperties function, is:

Atom* XListProperties
    (Display* display, 
     Window window, 
     int* number_returned_properties )

display is a reference, to a connection to the server. The window is the id of a window, which properties are to be retrieved. A BadWindow error, is generated, if the window id is not valid. number_returned_properties, is a reference, to the number of properties, which are returned.

If no properties are defined for this window, then the XListProperties function, will return NULL, otherwise, it will return a pointer, to the first element, of an array of atoms, which size is referenced, by number_returned_properties . In other words, you can loop through this array, in order to check the details for each property. Once done using, the returned pointer, it can be freed using XFree.

Another way of getting a window property, is by using the xprop utility, so you just execute the xprop command, and click on a window, which you want its properties displayed.

$ xclock &
# Launch the xclock
# program.

$ xprop 
# after clicking on xclock 
# the following is displayed
WM_STATE(WM_STATE):
                window state: Normal
                icon window: 0x0
_NET_WM_PID(CARDINAL) = 5208
_WINDOWSWM_NATIVE_HWND(INTEGER) = 2689576, 0
WM_PROTOCOLS(ATOM): protocols  WM_DELETE_WINDOW
WM_CLIENT_LEADER(WINDOW): window id # 0x80000a
WM_LOCALE_NAME(STRING) = "en_US.UTF-8"
WM_CLASS(STRING) = "xclock", "XClock"
WM_HINTS(WM_HINTS):
                Client accepts input or input focus: False
                Initial state is Normal State.
                bitmap id # to use for icon: 0x800001
                bitmap id # of mask for icon: 0x800003
WM_NORMAL_HINTS(WM_SIZE_HINTS):
                program specified size: 164 by 164
                window gravity: NorthWest
WM_CLIENT_MACHINE(STRING) = "DSKTP-TOAPK1I"
WM_COMMAND(STRING) = { "xclock" }
WM_ICON_NAME(STRING) = "xclock"
WM_NAME(STRING) = "xclock"

xprop can also be used to set, or remove, a window’s property.

Example

In this example, the properties of the root window, have their property names, and their related atom numbers, printed.

#include <X11/Xlib.h>

#include <stdio.h>
#include <stdlib.h>

int
        main
            (int argc , char* argv[ ] ){

    // get a connection to the server
    char* connection_string = NULL;
    Display* display;
    display =  XOpenDisplay (connection_string  );
    if (display == NULL ){
        fprintf (stderr, "Error: XOpenDisplay (%s )\n", connection_string == NULL ? "NULL" :  connection_string );
        exit (EXIT_FAILURE );}

    //List the properties of the root window
    Atom* root_window_properties;
    int number_returned_properties;
    root_window_properties = XListProperties (display, //The display
                                              DefaultRootWindow (display), //The root window
                                              &number_returned_properties ); //The number of returned properties.

    if (root_window_properties != NULL ){
        printf ("atom\tname\n");
        for (int i = 0; i < number_returned_properties; i++ ){
            printf("%4lu\t%4s\t\n", root_window_properties [i ], XGetAtomName (display, root_window_properties[i ] ));}}
    XCloseDisplay(display);
    return 0;}
/* Output:
atom    name
 241    __NET_DESKTOP_NAMES
 240    _NET_NUMBER_OF_DESKTOPS
 239    _NET_CURRENT_DESKTOP
 243    _NET_SUPPORTED
  38    WM_ICON_SIZE
 232    _XKB_RULES_NAMES
*/

XDeleteProperty : Delete a property

Overview

A property can be created, updated, or read, by any client, on any window. A property life time, is not tied, to the client who has created it, but to the window hosting it, hence even, if the client who has created a window is destroyed, the property stays alive, until the window, it is associated with, is destroyed.

If a window is deleted, all its properties are deleted, and if a server is reset, all the properties are deleted.

Additionally a property can be deleted explicitly, as described earlier, using the XGetWindowProperty function, besides that, a property can also be deleted explicitly, by using the XDeleteProperty function.

The signature of the XDeleteProperty function, is :

XDeleteProperty
    (Display* display, 
     Window window, 
     Atom property );

display is a reference, to a connection to the server, window , is the id of the window, to delete its property, property , is an atom, identifying the property to be deleted.

If the window, and property are defined, and the property belongs to the window, the property is deleted, and a PropertyNotify event for the window is generated.

Errors that can arise, from using this function, are BadAtom, if the server does not know about the atom, and BadWindow, if the server does not know about the window.

Example

In this example, a property is created on the root window, and later on it is deleted using the XDeleteProperty function.

#include <X11/Xlib.h>
#include <X11/Xatom.h>

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int
        main
            (int argc , char* argv[ ] ){

    // get a connection to the server
    char* connection_string = NULL;
    Display* display;
    display =  XOpenDisplay (connection_string  );
    if (display == NULL ){
        fprintf (stderr, "Error: XOpenDisplay (%s )\n", connection_string == NULL ? "NULL" :  connection_string );
        exit (EXIT_FAILURE );}


    Atom atom = XInternAtom (display, "a_property_name", false );

    XChangeProperty (display, //The connection
        DefaultRootWindow (display ), //The window
        atom, //atom, Property name
        XA_STRING, //atom, Property type
        8, //Property format
        PropModeReplace, //Property mode
        (unsigned char *) "Property data", //Property data
        strlen ("Property data" ) ); //Number of data units

    XDeleteProperty(
      display,
      DefaultRootWindow(display),
      atom );


    XCloseDisplay(display);
    return 0;}

After compiling, and running the program, and to check if the created property, was successfully deleted, you can use xprop, on the root window, and the result should be empty.

$ xprop.exe -root | grep a_property_name

XRotateWindowProperties : Circular rotation of properties values

what is XRotateWindowProperties ?

As the name of this function implies, it is used to rotate the values, of some properties, which are placed in an array, in a circular manner.

To explain it better, let us start, by defining the XRotateWindowProperties function signature:

XRotateWindowProperties
    (Display* display, 
     Window window, 
     Atom* properties, 
     int number_of_properties, 
     int  number_of_positions );

display, is a reference, to a connection to the server, window is the id of the window, which has some properties, which values are to be rotated, properties is an array of atoms, indicating the window properties, to be rotated, number_of_properties, is the number of properties, which are stored in the array. number_of_positions, is by how much, you are rotating each individual value.

In other words, given a property, at an array index, you subtract from its index, the number_of_positions, and you take its modulo, with relation to the number_of_properties, in order to know the index of the property, that it will take its value.

As an example, if the number_of_positions is 1, and the number_of_properties is 5, then the property at index 0, will take the value, of the property at index 0 - 1 % 5 = 4 .

And if the number_of_positions is -1, and the number_of_properties is 5, then the property at index 0, will take the value, of the property at index 0 - (-1) % 5 = 1 .

If rotation actually happens, so if the value of each property changes, then a PropertyNotify event, is generated for the window, for each of the rotated properties, in the order, they appear in the array.

Errors that can be generated by this function, are BadWindow, if the server, does not know about the window, BadAtom if the server, does not know about the atom, BadMatch , if the property is specified more than once, in the array, or if it does not belong to the window. If an error occurs, properties, are not rotated.

Example

In this example , three properties were created, and using XRotateWindowProperties, their values have been rotated.

#include <X11/Xlib.h>

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>

int
        main
            (int argc , char* argv[ ] ){

    // get a connection to the server
    char* connection_string = NULL;
    Display* display;
    display =  XOpenDisplay (connection_string  );
    if (display == NULL ){
        fprintf (stderr, "Error: XOpenDisplay (%s )\n", connection_string == NULL ? "NULL" :  connection_string );
        exit (EXIT_FAILURE );}


    Atom atom_prop_1 = XInternAtom (display, "prop_1", false );
    Atom atom_prop_2 = XInternAtom (display, "prop_2", false );
    Atom atom_prop_3 = XInternAtom (display, "prop_3", false );

    int number_properties = 3;
    Atom properties[ ] = {atom_prop_1, atom_prop_2, atom_prop_3 };

    Atom atom_type_int = XInternAtom (display, "type_int", false );

    //Store some properties, on the root window
    for  (long i = 0; i < number_properties; i++ )
        XChangeProperty (display, //The connection
            DefaultRootWindow (display ), //The window
            properties[i ], //atom, Property name
            atom_type_int, //atom, Property type
            32, //Property format
            PropModeReplace, //Property mode
            (unsigned char *) &i, //Property data
            1 ); //Number of data units

    XRotateWindowProperties
        (display,
         DefaultRootWindow (display ),
         properties,
         number_properties,
         1 ); //rotate properties by 1

    /* Extract the rotated property values */
    Atom returned_property_type;
    int returned_property_format;
    unsigned long returned_number_data_units;
    unsigned long number_of_bytes_remaining_after_return;
    long* retrieved_data;

    printf("%-18s%-18s%-18s\n", "Property", "Old Value" , "New Value" ); 
    for  (long i = 0; i < number_properties; i++ ){
        XGetWindowProperty (display, //The connection
                            DefaultRootWindow (display ), //The window
                            properties[i ], //The property name
                            0, //offset in 32 bits
                            1, //number of elements in 32 bits
                            False, //delete
                            atom_type_int, //requested_property_type
                            &returned_property_type,  //returned_property_type
                            &returned_property_format, //returned_property_format
                            &returned_number_data_units, //returned_number_data_units
                            &number_of_bytes_remaining_after_return, //number_of_bytes_remaining_after_return
                            (unsigned char** ) &retrieved_data /*retrieved_data */ );
        printf("atom_prop_%-8ld  %-18d %-18d\n", i, i, *retrieved_data );}
    /* Output
        Property          Old Value         New Value
        atom_prop_0         0                  2
        atom_prop_1         1                  0
        atom_prop_2         2                  1 */

    XRotateWindowProperties
        (display,
         DefaultRootWindow (display ),
         properties,
         number_properties,
         -1 ); //Reset the previous rotation


    XRotateWindowProperties
        (display,
         DefaultRootWindow (display ),
         properties,
         number_properties,
         2 ); //Rotate by 2

    printf("\n\n%-18s%-18s%-18s\n", "Property", "Old Value" , "New Value" ); 
    for  (long i = 0; i < number_properties; i++ ){
        XGetWindowProperty (display, //The connection
                            DefaultRootWindow (display ), //The window
                            properties[i ], //The property name
                            0, //offset in 32 bits
                            1, //number of elements in 32 bits
                            False, //delete
                            atom_type_int, //requested_property_type
                            &returned_property_type,  //returned_property_type
                            &returned_property_format, //returned_property_format
                            &returned_number_data_units, //returned_number_data_units
                            &number_of_bytes_remaining_after_return, //number_of_bytes_remaining_after_return
                            (unsigned char** ) &retrieved_data /*retrieved_data */ );
        printf("atom_prop_%-8ld  %-18d %-18d\n", i, i, *retrieved_data );}
    /* Output:
        Property          Old Value         New Value
        atom_prop_0         0                  1
        atom_prop_1         1                  2
        atom_prop_2         2                  0*/


    for  (long i = 0; i < number_properties; i++ )
        XDeleteProperty(
        display,
        DefaultRootWindow (display ),
        properties[i ] );


    XCloseDisplay(display);
    return 0;}

The PropertyNotify event

what is the PropertyNotify event?

An event has a mask, used to register for the event, it has a type, used to query the type of the received event, and it has a structure, containing information, about the received event, of the given type.

PropertyNotify is the type of the event, which is generated for a window, when one of its properties, is modified.

In other words, this might happen, when a call to XChangeProperty is made, and is successful , or when a property has been deleted, by either a call to XGetWindowProperty , or XDeleteProperty .

The PropertyNotify event, also happens, once for each rotated property, when a call to XRotateWindowProperties, causes the properties values, to be rotated.

This being said, the PropertyChangeMask, is set on a window, in such a case, the server will notify the client, when this window’s properties, have been changed.

The PropertyChangeMask can be set on a window, using various functions, such as XSelectInput, XChangeWindowAttributes, or XCreateWindow.

Multiple clients, can register for the PropertyNotify event, of a given window, and once a property on this window, is changed, they will all be notified by the server.

The structure associated with the PropertyNotify event, is the XPropertyEvent structure.

Example

To illustrate, what has been said till now, let us give a concrete example:

#include <X11/Xlib.h>
#include <X11/Xatom.h>

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int
        main
            (int argc , char* argv[ ] ){

    // get a connection to the server
    char* connection_string = NULL;
    Display* display;
    display =  XOpenDisplay (connection_string  );
    if (display == NULL ){
        fprintf (stderr, "Error: XOpenDisplay (%s )\n", connection_string == NULL ? "NULL" :  connection_string );
        exit (EXIT_FAILURE );}


    // create a top level window
    Window top_level_window;
    top_level_window = XCreateSimpleWindow (
        display ,
        XDefaultRootWindow (display ), //parent window
            // set the parent of top_level_window to the root window
        0, //x position
        0, //y position
        300, //width
        300, //height
        0, //border width
        0xFFFFFFFF , //border color
        0xFF112233 /*background color .*/ );


    XSelectInput (display, top_level_window, ExposureMask|PropertyChangeMask );
    /* Using XSelectInput, register to receive
        notifications, about the Expose and
        PropertyNotify events, using the
        event masks ExposureMask and
        PropertyChangeMask .*/

    XMapWindow (display, top_level_window );
    /* Map the window to be exposed
        later on .*/

    XEvent xevent;
    while (true ){ //handle events
        XNextEvent (display, &xevent );
        switch (xevent .type ){
            case Expose:
                /*When the window is shown,
                    change its title .*/
                XChangeProperty (display, //The connection
                    top_level_window, //The window
                    XA_WM_NAME, //atom, Property name, set the title
                    XA_STRING, //atom, Property type
                    8, //Property format
                    PropModeAppend, //Property mode
                    (unsigned char *) "Hey world", //Property data
                    strlen ("Hey world") ); //Number of data units
                break;
            case PropertyNotify:
                /*Print the name of the
                    property, which is being
                    modified .*/
                printf ("Property modified is : %s\n", XGetAtomName (display, xevent .xproperty .atom));
                break;
        default:
            break; }}

    XCloseDisplay(display);
    return 0;}
/* Output:
Property modified is : WM_NAME
Property modified is : _WINDOWSWM_NATIVE_HWND
Property modified is : WM_STATE */

The XSelectInput function

There are multiple functions, that can be used to inform an X11 server, that you are interested in it, reporting events, happening on a window, and they are the XSelectInput, XChangeWindowAttributes, and XCreateWindow functions.

The signature of the XSelectInput function is:

XSelectInput
    (Display* display, 
     Window window, 
     long event_mask)

display, is a reference, to a connection to the server, window, is the id of a window, which you are interested in one or more of its events, event_mask, is one or more events, related to the specified window, which you are interested in.

So from the previous example, the function was used as such:

XSelectInput 
    (display, 
     top_level_window, 
     ExposureMask|PropertyChangeMask );
/*Using XSelectInput, register to receive
  notifications, about the Expose and
  PropertyNotify events, using the 
  event masks ExposureMask and 
  PropertyChangeMask .*/

The XPropertyEvent structure

As it can be seen from the example,

XEvent xevent;
while (true ){ //handle events
    XNextEvent (display, &xevent );
    switch (xevent .type ){
        case Expose:
            ..
            break;
        case PropertyNotify:
            .. XGetAtomName (display, xevent .xproperty .atom);

To retrieve events, you must use the XNextEvent function, and you pass in a reference, to a structure of type XEvent. This will set the value, for the variable xevent, which is of type XEvent.

At this stage, you can query for the type of event, using xevent .type, and if it is of type PropertyNotify, then you can access a structure of type XPropertyEvent , from xevent .

In this example, this was used to get the name of the property, that has changed, by accessing the atom field, of the xproperty structure.

This being said, the signature of the xproperty structure is:

typedef struct {
    int type;
    unsigned long serial;
    Bool send_event;
    Display* display;
    Window window;
    Atom atom;
    Time time;
    int state;} XPropertyEvent;
  • type: is the type of the event, so in this case, it will be set to the value PropertyNotify.
  • serial: is of type unsigned long. Each request, is given a serial by the server, so serial, is just a number generated by the server.
  • send_event: is of type bool, it is set to true, if the event was sent by an application, using the XSendEvent function, or else it is set to false.
  • display: is of type Display*, so this is a reference to the connection, from which the event came.
  • window: is of type Window, so this is the id of the window, which generated this event. So in this case, a property has been changed.
  • atom: is of type Atom, so this is the atom identifying, the property name, on which there was a change.
  • time: is of type Time, which is defined as, typedef unsigned long Time;, in the <X11/X.h> header. It is the server time, as in a timestamp, when the property has been changed.
  • state: is of type int. It can take, one of the two values, PropertyNewValue or PropertyDeleted, and which are defined in the <X11/X.h> header.

    PropertyNewValue, is set, when a call to XChangeProperty is successful , or when a call to XRotateWindowProperties, results in the properties values, being rotated.

    PropertyDeleted, is set, when a call to XDeleteProperty, or XGetWindowProperty, results in the deletion of a property, associated with a window.