Xlib

Interaction with the X Window System can be achieved by using the Xlib interface fortran-xlib for Fortran 2003. Creating windows using raw Xlib works on all systems that have an X server running.

A widget toolkit, like Motif, GTK+, Qt, or Tk, is not required, albeit no visual components (buttons, labels, menus, …) are part of the Xlib standard. As a consequence, programmers only have access to simple drawing functions for text, geometric shapes, and images. The Motif or GTK+ bindings for Fortran can be used instead to facilitate the implementation of full-featured graphical user interfaces.

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

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

fortran-xlib

Clone the GitHub repository and simply run make to build the static library libfortran-xlib.a:

$ git clone https://github.com/interkosmos/fortran-xlib
$ cd fortran-xlib/
$ make

Link your Fortran application with libfortran-xlib.a -lX11 and the fortran-xlib module files.

Example

The program draws some text and fills a rectangle (fig. 1) on the root window. The X application xfontsel(1) lets us select an X font name string (other than -*-helvetica-bold-r-*-*-14-*-*-*-*-*-*-* used in the example). The colour name may be changed to any valid X11 colour name.

! demo.f90
program main
    use, intrinsic :: iso_c_binding, only: c_bool, 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, &
                                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, &
                                     .false._c_bool)
    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.
    do
        call x_next_event(display, event)

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

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

    ! Clean up and quit.
    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)
contains
    subroutine draw(display, window, gc, font, color, x, y, string)
        type(c_ptr),         intent(inout) :: display
        integer(kind=8),     intent(inout) :: window
        type(c_ptr),         intent(inout) :: gc
        type(x_font_struct), intent(inout) :: font
        type(x_color),       intent(inout) :: color
        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)                    ! Set colour.
        call x_draw_string(display, window, gc, x, y, string, len(string)) ! Draw text.
        call x_fill_rectangle(display, window, gc, x, y + 25, 100, 100)    ! Fill rectangle.
    end subroutine draw
end program main

Link the example program with libfortran-xlib.a -lX11:

$ gfortran10 -I/usr/local/include/ -L/usr/local/lib/ -o demo demo.f90 libfortran-xlib.a -lX11
$ ./demo

For further examples, see the fortran-xlib source code repository.

References