[Xcb] Win32 XCB wiki

Jeetu Golani jeetu.golani at gmail.com
Sat Mar 28 05:14:09 PDT 2009


Hi guys,

I've written a basic document on the XCB Win32 port which I am
attaching. As per the suggestion of Barton and Vincent I thought I
would put this up on the wiki however I think I need admin privileges
or something to add new content (have registered under the name Jatin
however I can only edit pages). Sorry am new here would appreciate if
you could go through this text file and show me how I can add this to
the wiki.

Vincent, since you mentioned you use Windows to port other libraries,
I would appreciate if you could go through this and do an independent
review of whether the port works for you within Win32 so we can
include this in the main tree.

Bye for now
-------------- next part --------------
XCB out of Windows
-jatin golani (aka jeetu aka topcat) <jeetu.golani at gmail.com> 


XCB has now been ported to the Win32 platform. This mess shall soon be unleashed on the world so here is the headsup on it :)

Background
----------

The Windows operating system no longer includes the POSIX subsystem out of the box. Using POSIX/Unix applications under Windows therefore requires an additional layer such as Microsoft's Interix Posix/Unix subsystem or the Cygwin libraries. Cygwin consists of a library that implements the POSIX system call API in terms of Win32 system calls. Unfortunately, an additional translation layer causes performance degradation. While there are a few X Servers that have been ported to Windows, there has been no port of the X Client library (Xlib or XCB), thereby preventing writing native Windows based X clients. The Win32 XCB port aims at allowing native Windows X Clients to be developed. These native X applications would provide performance superior to applications using libraries such as Cygwin.


What's changed between the *nix and Windows ports?
--------------------------------------------------

Essentially the following:

- The following header files used under *nix development environments are absent or redundant in Windows. 

sys/socket.h
netinet/in.h
sys/un.h
sys/select.h
sys/uio.h
netdb.h

Our code has been changed to skip inclusion of these header files when compiled under WIN32.

- Source code files effected:

xcb_auth.c

xcb_conn.c
	- set_fd_flags() : Win32 doesn't have file descriptors and the fcntl function. Made changes that set the socket in non-blocking mode.
	- write_vec() : Win32 version using the send() function instead of writev.
	- xcb_disconnect() : Calls WSACleanup for Win32 build.

xcb_in.c
	- read_block() : Win32 version using the recv() function instead of read.
	- _xcb_in_read() : Win32 version using the recv() function instead of read.
	
xcb_util.c
	- _xcb_open_unix() : Preprocessor directives prevent this from being compiled while building for Win32.
	- _xcb_open() : Does not call _xcb_open_unix for Win32 build.
	- _xcb_open_tcp() : Calls initWSA to initialize the Windows Socket Architecture. 
	- initWSA() : New function added to initialize the Win32 Sockets layer.

xcb.h
	- Declaration for the initWSA function added.

windefs.h (new header created specifically for the Win32 port)

- Windefs.h

windefs.h defines all headers and other structures,datatypes,etc required for win32. A key point to note within this header file is the following:

>> #define WINVER 0x0501 since getaddrinfo/freeaddrinfo are defined only for WinXP and above. Without this linker complains of undefined references.

Currently, therefore, the code may not compile (untested) on versions prior to XP.



Creating the XCB DLL
--------------------

The following lists the process I used to compile XCB under MinGW on my Debian system :

- compiled and installed x11proto under standard linux ./autogen && make && make install....then copied the headers from /usr/local/include/X11 to /use/i586-mingw32msvc/include physically.

- Compiled the DLL version of libXau: 
 ./autogen.sh --host=i586-mingw32msvc
make
make install

- Compiled and installed the pthread-win32 library (http://sourceware.org/pthreads-win32/)

Make options
- make CC=i586-mingw32msvc-gcc \
       RC=i586-mingw32msvc-windres \
       RANLIB=i586-mingw32msvc-ranlib \
       DLLTOOL=i586-mingw32msvc-dlltool \
       clean GC

- Made the following changes (thanks Vincent) - 

>1) in configure.ac, before AC_PROG_LIBTOOL, add AC_LIBTOOL_WIN32_DLL

>2) add, in src/Makefile.am, the value -no-undefined to *_LDFLAGS:

>libxcb_la_LDFLAGS = -no-undefined -version-info 1:0:0

- set the environment variable LIBS to include the ws2_32 and pthreadGC2 library - export LIBS = -lws2_32 -lpthreadGC2

- Configured and built using - 
./autogen.sh --host=i586-mingw32msvc
make

If all goes well the DLL is created within the src/.libs directory.

Using XCB within Windows
------------------------

The XCB DLL has been tested predominantly under Windows XP (32 bit). The development environment has been Linux (Debian) using MinGW. However there is no reason you can't use Visual Studio or other IDEs. There are plenty of online resources that show how to create the required .def and .lib files and that speak of differences in name decoration of functions generated by MinGW as opposed to other compilers.

Apart from the XCB DLL (e.g. libxcb-1.dll) you will also need the DLL for libXau (e.g. libXau-6.dll) and Pthreads (e.g. pthreadGC2.dll) to be in your path or along with your source code.

The simplest way to use the DLL under MinGW is to include it during compiling your code. For e.g. :

>> i586-mingw32msvc-gcc xcbclient.c libxcb-1.dll -o xcbclient.exe

X Servers
---------

For testing purposes I have used the following X Servers within Windows. My source code has been connecting locally to these X Servers. I have also connected remotely to my X.org server on Linux. 

XMing (http://www.straightrunning.com/XmingNotes)
WinAxe (http://www.labf.com/winaxe/index.html)

Test Code
---------

The following is a test code from the XCB tutorial. Copy all of the XCB headers within your source code directory or somewhere in your path. If all goes well this should work. 


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

#include "xcb.h"

#define WIDTH 300
#define HEIGHT 150

static xcb_gc_t
    getFontGC (xcb_connection_t *connection,
               xcb_screen_t     *screen,
               xcb_window_t      window,
               const char       *fontName );
    static void
    testCookie (xcb_void_cookie_t cookie,
                xcb_connection_t *connection,
                char *errMessage )
    {
        xcb_generic_error_t *error = xcb_request_check (connection, cookie);
        if (error) {
            fprintf (stderr, "ERROR: %s : %d\n", errMessage , error->error_code);
            xcb_disconnect (connection);
            exit (-1);
        }
    }


    static void
    drawButton (xcb_connection_t *connection,
                xcb_screen_t     *screen,
                xcb_window_t      window,
                int16_t           x1,
                int16_t           y1,
                const char       *label )
    {
        uint8_t length = strlen (label);
        int16_t inset = 2;
        int16_t width = 7 * length + 2 * (inset + 1);
        int16_t height = 13 + 2 * (inset + 1);

        xcb_point_t points[5];
        points[0].x = x1;
        points[0].y = y1;
        points[1].x = x1 + width;
        points[1].y = y1;
        points[2].x = x1 + width;
        points[2].y = y1 - height;
        points[3].x = x1;
        points[3].y = y1 - height;
        points[4].x = x1;
        points[4].y = y1;


        xcb_gcontext_t gc = getFontGC (connection, screen, window, "7x13");


        xcb_void_cookie_t lineCookie = xcb_poly_line_checked (connection,
                                                              XCB_COORD_MODE_ORIGIN,
                                                              window,
                                                              gc,
                                                              5,
                                                              points );
        testCookie (lineCookie, connection, "can't draw lines");


        xcb_void_cookie_t textCookie = xcb_image_text_8_checked (connection,
                                                                 length,
                                                                 window,
                                                                 gc,
                                                                 x1 + inset + 1,
                                                                 y1 - inset - 1,
                                                                 label );
        testCookie (textCookie, connection, "can't paste text");


        xcb_void_cookie_t gcCookie = xcb_free_gc (connection, gc);
        testCookie (gcCookie, connection, "can't free gc");
    }


    static void
    drawText (xcb_connection_t *connection,
              xcb_screen_t     *screen,
              xcb_window_t      window,
              int16_t           x1,
              int16_t           y1,
              const char       *label )
    {

        xcb_gcontext_t gc = getFontGC (connection, screen, window, "7x13");


        xcb_void_cookie_t textCookie = xcb_image_text_8_checked (connection,
                                                                 strlen (label),
                                                                 window,
                                                                 gc,
                                                                 x1,
                                                                 y1,
                                                                 label );
        testCookie(textCookie, connection, "can't paste text");


        xcb_void_cookie_t gcCookie = xcb_free_gc (connection, gc);
        testCookie (gcCookie, connection, "can't free gc");
    }


    static xcb_gc_t
    getFontGC (xcb_connection_t *connection,
               xcb_screen_t     *screen,
               xcb_window_t      window,
               const char       *fontName )
    {

        xcb_font_t font = xcb_generate_id (connection);
        xcb_void_cookie_t fontCookie = xcb_open_font_checked (connection,
                                                              font,
                                                              strlen (fontName),
                                                              fontName );
        testCookie (fontCookie, connection, "can't open font");


        xcb_gcontext_t gc = xcb_generate_id (connection);
        uint32_t  mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_FONT;
        uint32_t value_list[3];
        value_list[0] = screen->black_pixel;
        value_list[1] = screen->white_pixel;
        value_list[2] = font;


        xcb_void_cookie_t gcCookie = xcb_create_gc_checked (connection,
                                                            gc,
                                                            window,
                                                            mask,
                                                            value_list );
        testCookie (gcCookie, connection, "can't create gc");


        fontCookie = xcb_close_font_checked (connection, font);
        testCookie (fontCookie, connection, "can't close font");

        return gc;
    }


    static void
    setCursor (xcb_connection_t *connection,
                xcb_screen_t     *screen,
                xcb_window_t      window,
                int               cursorId )
    {
        uint32_t mask;
	xcb_font_t font = xcb_generate_id (connection);
        xcb_void_cookie_t fontCookie = xcb_open_font_checked (connection,
                                                              font,
                                                              strlen ("cursor"),
                                                              "cursor" );
        testCookie (fontCookie, connection, "can't open font");


        xcb_cursor_t cursor = xcb_generate_id (connection);
        xcb_create_glyph_cursor (connection,
                                 cursor,
                                 font,
                                 font,
                                 cursorId,
                                 cursorId + 1,
                                 0, 0, 0, 0, 0, 0 );


        xcb_gcontext_t gc = xcb_generate_id (connection);

        mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_FONT;
        uint32_t values_list[3];
        values_list[0] = screen->black_pixel;
        values_list[1] = screen->white_pixel;
        values_list[2] = font;

        xcb_void_cookie_t gcCookie = xcb_create_gc_checked (connection, gc, window, mask, values_list);
        testCookie (gcCookie, connection, "can't create gc");


        mask = XCB_CW_CURSOR;
        uint32_t value_list = cursor;
        xcb_change_window_attributes (connection, window, mask, &value_list);


        xcb_free_cursor (connection, cursor);


        fontCookie = xcb_close_font_checked (connection, font);
        testCookie (fontCookie, connection, "can't close font");
    }

    int 
    main ()
    {
       

        int screenNum,i;
        xcb_connection_t *connection = xcb_connect ("127.0.0.1:0.0", &screenNum);
        if (!connection) {
            fprintf (stderr, "ERROR: can't connect to an X server\n");
            return -1;
        }


       

        xcb_screen_iterator_t iter = xcb_setup_roots_iterator (xcb_get_setup (connection));


        for (i = 0; i < screenNum; ++i) {
            xcb_screen_next (&iter);
        }

        xcb_screen_t *screen = iter.data;

        if (!screen) {
            fprintf (stderr, "ERROR: can't get the current screen\n");
            xcb_disconnect (connection);
            return -1;
        }


       

        xcb_window_t window = xcb_generate_id (connection);
        uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
        uint32_t values[2];
        values[0] = screen->white_pixel;
        values[1] = XCB_EVENT_MASK_KEY_RELEASE |
                    XCB_EVENT_MASK_BUTTON_PRESS |
                    XCB_EVENT_MASK_EXPOSURE |
                    XCB_EVENT_MASK_POINTER_MOTION;


        xcb_void_cookie_t windowCookie = xcb_create_window_checked (connection,
                                                  screen->root_depth,
                                                  window,
                                                  screen->root,
                                                  20, 200, WIDTH, HEIGHT,
                                                  0,
                                                  XCB_WINDOW_CLASS_INPUT_OUTPUT,
                                                  screen->root_visual,
                                                  mask, values );
        testCookie (windowCookie, connection, "can't create window");


        xcb_void_cookie_t mapCookie = xcb_map_window_checked (connection, window);
        testCookie (mapCookie, connection, "can't map window");


        setCursor (connection, screen, window, 68);

        xcb_flush(connection);


       

        uint8_t isHand = 0;

        while (1) {
            xcb_generic_event_t *event = xcb_poll_for_event (connection);
            if (event) {
                switch (event->response_type & ~0x80) {
                    case XCB_EXPOSE: {
                        char *text = "click here to change cursor";
                        drawButton (connection, 
                                    screen,
                                    window,
                                    (WIDTH - 7 * strlen(text)) / 2,
                                    (HEIGHT - 16) / 2,
                                    text );

                        text = "Press ESC key to exit...";
                        drawText (connection,
                                  screen,
                                  window,
                                  10,
                                  HEIGHT - 10,
                                  text );
                        break;
                    }
                    case XCB_BUTTON_PRESS: {
                        xcb_button_press_event_t *press = (xcb_button_press_event_t *)event;

                        int length = strlen ("click here to change cursor");
                        if ((press->event_x >= (WIDTH - 7 * length) / 2) &&
                                (press->event_x <= ((WIDTH - 7 * length) / 2 + 7 * length + 6)) &&
                                (press->event_y >= (HEIGHT - 16) / 2 - 19) &&
                                (press->event_y <= ((HEIGHT - 16) / 2))) {
                            isHand = 1 - isHand;
                        }

                        if (isHand) {
                            setCursor (connection, screen, window, 58);
                        }
                        else {
                            setCursor (connection, screen, window, 68);
                        }
                    }
                    case XCB_KEY_RELEASE: {
                        xcb_key_release_event_t *kr = (xcb_key_release_event_t *)event;

                        switch (kr->detail) {
                            
                            case 8:
                                free (event);
                                xcb_disconnect (connection);
                                return 0;
                        }
                    }
                }
                free (event);
            }
        }

        return 0;
}



More information about the Xcb mailing list