Interaction with the X Window System can be achieved by using the Xlib interface f03xlib for Fortran 2003. Creating windows using raw Xlib works on all systems that have an X server running. A widget toolkit, like GTK+, Qt, or Tk, is not required, albeit no visual components (buttons, labels, menus, …) are part of the Xlib standard.

For this reason, Xt was often used in the past, but a Fortran interface to it is not available yet. As a consequence, programmers only have access to simple drawing functions for text, geometric shapes, and images. The GTK+ bindings for Fortran can be used instead to facilitate the implementation of full-featured graphical user interfaces.

Fortran Xlib
Fig. 1: The example Xlib program written in Fortran 2003

Xlib programming in Fortran does not differ from one in other programming languages, as xlib.f90 is solely an ISO C binding interface.


The example draws text and fills a rectangle (fig. 1).

! xwin.f90
program main
    use, intrinsic :: iso_c_binding, only: c_null_char, c_ptr
    use :: xlib
    implicit none

    integer,          parameter :: WIDTH      = 320             ! Window width.
    integer,          parameter :: HEIGHT     = 200             ! Window height.
    character(len=*), parameter :: TITLE      = 'Fortran X11'   ! Window title.
    character(len=*), parameter :: COLOR_NAME = 'Sea Green'     ! X11 colour name.
    character(len=*), parameter :: FONT_NAME  = '-*-helvetica-bold-r-*-*-14-*-*-*-*-*-*-*'
    character(len=*), parameter :: TEXT       = 'X11 window with Fortran'

    type(c_ptr)         :: display
    type(c_ptr)         :: gc
    type(x_color)       :: color
    type(x_event)       :: event
    type(x_font_struct) :: font
    type(x_gc_values)   :: values
    type(x_size_hints)  :: size_hints
    integer             :: screen
    integer             :: rc
    integer(kind=8)     :: root
    integer(kind=8)     :: colormap
    integer(kind=8)     :: black
    integer(kind=8)     :: white
    integer(kind=8)     :: window
    integer(kind=8)     :: wm_delete_window
    integer(kind=8)     :: long(5)

    ! Open display.
    display  = x_open_display(c_null_char)
    screen   = x_default_screen(display)
    root     = x_default_root_window(display)
    colormap = x_default_colormap(display, screen)

    ! Define colours.
    black = x_black_pixel(display, screen)
    white = x_white_pixel(display, screen)
    rc    = x_alloc_named_color(display, &
                                colormap, &
                                COLOR_NAME // c_null_char, &
                                color, &

    ! Create window.
    window = x_create_simple_window(display, root, 0, 0, WIDTH, HEIGHT, 0, black, white)
    call x_store_name(display, window, TITLE // c_null_char)

    ! Register closing of window.
    wm_delete_window = x_intern_atom(display, &
                                     'WM_DELETE_WINDOW' // c_null_char, &
    rc = x_set_wm_protocols(display, window, wm_delete_window, 1)

    ! Set hints to prevent resizing.
    size_hints%flags      = ior(P_MIN_SIZE, P_MAX_SIZE)
    size_hints%min_width  = WIDTH
    size_hints%min_height = HEIGHT
    size_hints%max_width  = WIDTH
    size_hints%max_height = HEIGHT

    call x_set_wm_normal_hints(display, window, size_hints)

    ! Create graphics context.
    gc = x_create_gc(display, window, 0, values)

    ! Load and set font.
    font = x_load_query_font(display, FONT_NAME // c_null_char)
    call x_set_font(display, gc, font%fid)

    ! Show window.
    call x_select_input(display, window, ior(EXPOSURE_MASK, STRUCTURE_NOTIFY_MASK))
    call x_map_window(display, window)

    ! Event loop.
        call x_next_event(display, event)

        select case (event%type)
            case (EXPOSE)
                ! Redraw the window.
                call draw(display, window, gc, font, 25, 25, TEXT // c_null_char)
            case (CLIENT_MESSAGE)
                ! Quit if window is closed.
                long = transfer(event%x_client_message%data, long)

                if (long(1) == wm_delete_window) &
        end select
    end do

    ! Clean up and quit.
    call x_free_font(display, font)
    call x_free_colors(display, colormap, [ color%pixel ], 1, int(0, kind=8))
    call x_free_gc(display, gc)
    call x_destroy_window(display, window)
    call x_close_display(display)
    subroutine draw(display, window, gc, font, x, y, string)
        use :: xlib_types
        type(c_ptr),         intent(in) :: display
        integer(kind=8),     intent(in) :: window
        type(c_ptr),         intent(in) :: gc
        type(x_font_struct), intent(in) :: font
        integer,             intent(in) :: x
        integer,             intent(in) :: y
        character(len=*),    intent(in) :: string
        integer                         :: direction, ascent, descent
        type(x_char_struct)             :: overall

        call x_text_extents(font, string, len(string), direction, &
                            ascent, descent, overall)
        call x_set_foreground(display, gc, color%pixel)

        ! Draw text.
        call x_draw_string(display, window, gc, x, y, string, len(string))
        ! Fill rectangle.
        call x_fill_rectangle(display, window, gc, x, y + 25, 100, 100)
    end subroutine draw
end program main

Compile the Xlib binding and the example program with:

$ gfortran9 -c xlib.f90
$ gfortran9 -I/usr/local/include/ -L/usr/local/lib/ -o xwin xwin.f90 xlib.o -lX11
$ ./xwin

For further examples, see the GitHub repository (Mandelbrot set, wire-frame graphics, images).