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 specifier.

! 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=32)   :: str
    type(c_ptr)         :: display
    type(c_ptr)         :: gc
    type(x_char_struct) :: overall
    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             :: direction, ascent, descent
    integer             :: screen
    integer             :: rc, x, y
    integer(kind=8)     :: root
    integer(kind=8)     :: colormap
    integer(kind=8)     :: black, 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)

    ! Set text and text position.
    str = 'X11 window with Fortran'; x = 25; y = 25

    ! Event loop.
    do
        call x_next_event(display, event)

        select case (event%type)
            case (EXPOSE)
                ! Redraw the window.
                call x_text_extents(font, string, len_trim(str), direction, ascent, descent, overall)
                call x_set_foreground(display, gc, color%pixel)                    ! Set colour.
                call x_draw_string(display, window, gc, x, y, str, len_trim(str))  ! Draw text.
                call x_fill_rectangle(display, window, gc, x, y + 25, 100, 100)    ! Fill rectangle.

            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)
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