1. Tutorial on the Iup Graphical User Interface toolkit
    1. Iup's design principles
    2. Hello World
      1. hello0.c
      2. hello0.scm
      3. hello1.c
      4. hello11.scm
      5. hello12.scm
    3. The LED resource language
      1. hello.led
      2. hello13.scm
    4. Visualize your design
      1. inspector.scm
    5. Porting some examples from the Iup Distribution
      1. fill.scm
      2. hbox.scm
      3. menu.scm
      4. filedlg.scm
      5. multiline.scm
      6. progressbar.scm
      7. webbrowser.scm
    6. Concluding remark
  2. Author
  3. Initial version
  4. Last updated

Tutorial on the Iup Graphical User Interface toolkit

Iup is a small and easy to use GUI toolkit, originally written in pure C, but accompanied by an optional resource language LED, to make life easier. It has later been ported to Lua and other scripting languages, and nowadays it is considered Lua's native GUI-toolkit. Thanks to Thomas Chust Chicken and Racket ports exist as well.

Unfortunately, the documentation is somewhat scattered in the internet:

Chicken port of the Iup GUI library

the official API docs.

IUP "portable user interface" GUI library.

Hence you need to consult the original C documentation very often. So it seems appropriate to start with an example (Hello World, you guess it) showing how a C program is translated to Scheme. This way you'll become comfortable writing Scheme programs while using the original C documentation. But before that let me recapitulate some design principles of Iup.

Iup's design principles

Iup programs consist of dialogs, which communicate with each other. A dialog can contain only one widget, usually a container, which in turn can contain other widgets, containers included. For widgets to be visible, they must transparently be mapped to native widgets of the system: Gtk+ or Motif under X11, WinAPI under Windows or Cocoa with a third party extension under Mac OS X.

The mapping happens only once a dialog is shown. Destroying a dialog after its use destroys the included widgets as well. Hence, only dialogs need to be destroyed at the end of a program.

Like other GUI-toolkits, interaction with the user happens within a main loop. But what makes Iup stand out of the crowd are two principles:

First: No widget is ever positioned with explicit coordinates. Everything is done logically by nesting containers and positioning widgets like fill. This facilitates matters considerably.

Second: The appearance of all widgets and dialogs is controlled by attributes, their behaviour by callbacks. This makes the library small.

In C, attribute names as well as values are strings and attribute setting is a separate step from object construction. In Scheme a little more syntactic sugar is available: Attribute names are usually keywords, which can be passed directly to widget constructors. Attribute values can be almost anything you want, including handles and anything that can sensibly be converted to a string.

When you get or set an attribute explicitly, you can also use strings or symbols as attribute names. Getting is done (in Scheme) by the attribute function, setting by the attribute-set! procedure or a generalized set!, i.e. either by

(attribute-set! widget name: val) or (set! (attribute widget name:) val)

whichever you prefer. The same applies to callbacks, which are get and set by callback and callback-set! respectively.

In Scheme's Iup callbacks are functions of one or more arguments, including self, which can return a symbol, for example 'close, to close a dialog, or 'default, to keep it open. In C, on the other hand, the return values are #define'd integers.

Hello World

Let's start with the most trivial version, in C and in Chicken:

hello0.c

#include <iup.h>
#include <stdlib.h>

int main (int argc, char *argv[]) {
  IupOpen(&argc, &argv);
  IupShow(IupDialog(IupLabel ("Hello, world!")));
  IupMainLoop();
  IupClose();
  return 0;
}

hello0.scm

(use iup)
(show (dialog (label "Hello, world!")))
(main-loop)
(exit 0)

Note, that initializing and closing iup disappears in Chicken, since it is done when inporting iup. Note also, that the Chicken names are much friendlier than the C ones: Chicken's module system makes Iup prefixes superfluous, they can be added, if needed, with appropriate import clauses.

Now a version which shows, how to use attributes and callbacks, again in C, and in two Chicken versions. The first is almost a literal translation of the C version, the second a condensed version, where all attributes are set in the creation process.

hello1.c

#include <iup.h>
#include <stdlib.h>

int exit_cb (void) {
  return IUP_CLOSE;
}

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

  // declare widgets
  Ihandle *btn, *lbl, *vb, *dlg;

  // initialize iup
  IupOpen(&argc, &argv);
  
  // create widgets and set their attributes
  btn=IupButton("&Ok", "");
  IupSetCallback(btn,"ACTION", (Icallback) exit_cb);
  IupSetAttribute(btn, "EXPAND", "Yes");
  IupSetAttribute(btn, "TIP", "Exit button");
  
  lbl=IupLabel("Hello,world!");

  vb=IupVbox(lbl, btn, NULL);
  IupSetAttribute(vb, "GAP", "10");
  IupSetAttribute(vb, "MARGIN", "10x10");
  IupSetAttribute(vb, "ALIGNMENT", "ACENTER");

  dlg=IupDialog(vb);
  IupSetAttribute(dlg, "TITLE", "Hello");

  // Map widgets and show dialog
  IupShow(dlg);

  // Wait for user interaction
  IupMainLoop();

  // Clean up
  IupDestroy(dlg);
  IupClose();
  return EXIT_SUCCESS;
}

hello11.scm

Here is a translation to Scheme ...

(use iup)

(define (cb-exit self) 'close)

(define btn (button))
(set! (callback btn action:) cb-exit)
(set! (attribute btn title:) '&Ok)
(set! (attribute btn expand:) 'Yes)
(set! (attribute btn tip:) "Close button")

(define lbl (label "Hello World!")) 

(define vb (vbox lbl btn))
(attribute-set! vb gap: 10)
(attribute-set! vb margin: '15x15)
(attribute-set! vb alignment: 'ACENTER)

(define dlg (dialog vb))
(attribute-set! dlg title: 'Hello)

(show dlg)
(main-loop)
(destroy! dlg)
(exit 0)

Note, how the upper-case C-names of attributes change to lower-case Chicken-keywords (by the way, Chicken-keywords can be either written with a trailing colon or a leading hash-colon, but I prefere the former, which looks nicer).

Note also, that attribute values can be numbers or symbols as well. The Iup convention is to use "Yes" and "No" as boolean values. In Scheme you can use symbols as well. And last, but not least, note the tips: attribute, which defines tooltips, and the ampersand in the button's title, which defines a shortcut, Alt+underlined-character, to execute the callback.

hello12.scm

... and here a condensed version.

(use iup)

(define dlg
  (dialog
    (vbox
      (label "Hello, World!")
      (button title: '&Ok
              expand: 'Yes
              tip: "Close button"
              action: (lambda (self) 'close))
      gap: 10
      alignment: 'ACENTER
      margin: '15x15)
    title: 'IUP))

(show dlg)
(main-loop)
(destroy! dlg)
(exit 0)

Note, how smoothly the indentation reflects the logical representation of the dialog.

The LED resource language

The condensed version above can be almost literally transformed into a LED resource file, which can be either loaded into a C program or a Scheme program. Those resources are interpreted at runtime, so that even in a compiled program the resource can be changed afterwards, provided the identifiers remain consistent.

The form of a LED-declaration looks as follows

widget=widget-type[attribute-name=attribute-value, ...](arg, ...)

where the attributes' names and values are written without enclosing quotes, but interpreted as strings.

Here is an example and its use in Chicken.

hello.led

btn = button[TIP = "Close window", EXPAND = Yes]("&Ok", 0)

dlg = dialog[TITLE = Hello](
            vbox[GAP = 10, MARGIN = 15x15, ALIGNMENT = ACENTER](
                label("Hello world!"),
                btn))

hello13.scm

(use iup)

(load/led "hello.led")

(callback-set! (handle-ref "btn")
               action: (lambda (self) 'close))

(define dlg (handle-ref "dlg"))

(show dlg)
(main-loop)
(destroy! dlg)
(exit 0)

Note, that the LED-import is done via load/led and the identification of LED-names with Chicken-variables via handle-ref.

Note also, that btn must have a name, because we need to set the callback, which can not be a string.

You can compile hello13.scm with csc and change hello.led afterwards. Try it out and play with some attributes, for example, remove the EXPAND attribute from btn and set it for label, ...

Visualize your design

Iup has two dialogs which you can use to visualize your design, layout-dialog and element-properties-dialog. They can be used to inspect what you have already done and what's still available. For example, if you replace the line (show dlg) by (show (layout-dialog dlg)) you can see the tree structure of your design and inspect the properties of each widget by right-clicking on it and choosing the appropriate menu-item of the popped-up context-menu.

The layout-dialog menu has an export item as well, whith which you can export your design to C, Lua or LED. Of course, for Chicken only the LED export is of any value. But be warned, the dialog is not stable and exporting will crash the program very often, in particular, if the target LED file doesn't already exist. But if the export makes the program crash while overwriting an existing LED file, your work is lost. So, you better won't use layout-dialog to create your design, but only to inspect it. That's not that bad, because visual design doesn't help much in a language like Scheme, where the syntax-tree of a program is already visible in its source code, and a good text editor is much faster than a visual editor ....

The other dialog, element-properties-dialog, can conveniently be used for documentation purposes, by applying it to each existing widget with attributes set to the most primitive values. This is done in the following example.

inspector.scm

When writing an Iup application, you have always the problem, that you don't know in advance, what are the available widgets and what are the registered attributes and callbacks of each widget. To help with problems like this you can, of course, study the original documentation, and as a method of last resort you have to do that. But for a quick overview you can start the program "inspector" below. It's rather ugly. But that's not important here.

Note, that there are no libraries iup-web, iup-pplot and iup-glcanvas. They are all contained in the iup library. Hence we have to separate the installation of the library iup from the import of its sublibraries.

(require-library srfi-4 iup)
(import srfi-4 iup iup-web iup-pplot iup-glcanvas)

(define (popup dlg . args)
  (apply show dlg #:modal? 'yes args)
  (destroy! dlg))

(define (properties ih)
  (popup (element-properties-dialog ih))
  'default)

(define dlg
  (dialog
    (vbox
      (hbox ; headline
        (fill)
        (frame (label " Inspect control and dialog classes "
                      fontsize: 15))
        (fill)
        margin: '0x0)

      (label "") 
      (label "Dialogs" fontsize: 12)
      (hbox
        (button "dialog"
                action: (lambda (self) (properties (dialog (vbox)))))
        (button "color-dialog"
                action: (lambda (self) (properties (color-dialog))))
        (button "file-dialog"
                action: (lambda (self) (properties (file-dialog))))
        (button "font-dialog"
                action: (lambda (self) (properties (font-dialog))))
        (button "message-dialog"
                action: (lambda (self) (properties (message-dialog))))
        (fill)
        margin: '0x0)
      (hbox
        (button "layout-dialog"
                action: (lambda (self) (properties (layout-dialog))))
        (button "element-properties-dialog"
                action: (lambda (self)
                          (properties
                            (element-properties-dialog (create 'user)))))
        (fill)
        margin: '0x0)

      (label "") 
      (label "Composition widgets" fontsize: 12)
      (hbox
        (button "fill"
                action: (lambda (self) (properties (fill))))
        (button "hbox"
                action: (lambda (self) (properties (hbox))))
        (button "vbox"
                action: (lambda (self) (properties (vbox))))
        (button "zbox"
                action: (lambda (self) (properties (zbox))))
        (button "radio"
                action: (lambda (self) (properties (radio (vbox)))))
        (button "normalizer"
                action: (lambda (self) (properties (normalizer))))
        (button "cbox"
                action: (lambda (self) (properties (cbox))))
        (button "sbox"
                action: (lambda (self) (properties (sbox (vbox)))))
        (button "split"
                action: (lambda (self) (properties (split (vbox) (vbox)))))
        (fill)
        margin: '0x0)

      (label "") 
      (label "Standard widgets" fontsize: 12)
      (hbox
        (button "button"
                action: (lambda (self) (properties (button))))
        (button "canvas"
                action: (lambda (self) (properties (canvas))))
        (button "frame"
                action: (lambda (self) (properties (frame))))
        (button "label"
                action: (lambda (self) (properties (label))))
        (button "listbox"
                action: (lambda (self) (properties (listbox))))
        (button "progress-bar"
                action: (lambda (self) (properties (progress-bar))))
        (button "spin"
                action: (lambda (self) (properties (spin))))
        (fill)
        margin: '0x0)
      (hbox
        (button "tabs"
                action: (lambda (self) (properties (tabs))))
        (button "textbox"
                action: (lambda (self) (properties (textbox))))
        (button "toggle"
                action: (lambda (self) (properties (toggle))))
        (button "treebox"
                action: (lambda (self) (properties (treebox))))
        (button "valuator"
                action: (lambda (self) (properties (valuator ""))))
        (fill)
        margin: '0x0)

      (label "") 
      (label "Additional widgets" fontsize: 12)
      (hbox
        (button "cells"
                action: (lambda (self) (properties (cells))))
        (button "color-bar"
                action: (lambda (self) (properties (color-bar))))
        (button "color-browser"
                action: (lambda (self) (properties (color-browser))))
        (button "dial"
                action: (lambda (self) (properties (dial ""))))
        (button "matrix"
                action: (lambda (self) (properties (matrix))))
        (fill)
        margin: '0x0)
      (hbox
        (button "pplot"
                action: (lambda (self) (properties (pplot))))
        (button "glcanvas"
                action: (lambda (self) (properties (glcanvas))))
        (button "web-browser"
                action: (lambda (self) (properties (web-browser))))
        (fill)
        margin: '0x0)

      (label "") 
      (label "Menu widgets" fontsize: 12)
      (hbox
        (button "menu"
                action: (lambda (self) (properties (menu))))
        (button "menu-item"
                action: (lambda (self) (properties (menu-item))))
        (button "menu-separator"
                action: (lambda (self) (properties (menu-separator))))
        (fill)
        margin: '0x0)

      (label "") 
      (label "Images" fontsize: 12)
      (hbox
        (button "image/palette"
                action: (lambda (self)
                          (properties
                            (image/palette 1 1 (u8vector->blob (u8vector 0))))))
        (button "image/rgb"
                action: (lambda (self)
                          (properties
                            (image/rgb 1 1 (u8vector->blob (u8vector 0))))))
        (button "image/rgba"
                action: (lambda (self)
                          (properties
                            (image/rgba 1 1 (u8vector->blob (u8vector 0))))))
        (button "image/file"
                action: (lambda (self)
                          (properties
                            ;; same attributes as image/palette
                            (image/palette 1 1 (u8vector->blob (u8vector 0))))))
                            ;; needs a file in current directory
                            ;(image/file "chicken.ico")))) ; ok
                            ;(image/file "chicken.png")))) ; doesn't work
        (fill)
        margin: '0x0)

      (label "") 
      (label "Other widgets" fontsize: 12)
      (hbox
        (button "clipboard"
                action: (lambda (self) (properties (clipboard))))
        (button "timer"
                action: (lambda (self) (properties (timer))))
        (button "spinbox"
                action: (lambda (self) (properties (spinbox (vbox)))))
        (fill)
        margin: '0x0)

      (fill)
      (button "E&xit"
              expand: 'horizontal
              action: (lambda (self) 'close))
      )
    margin: '15x15
    title: "Iup inspector"))

(show dlg)
(main-loop)
(exit 0)

When you compile this program and use it, you will note, that some attributes can be inherited. That means, that all children of an object inherit those attributes. To override this, you must set those attributes in the children anew (look at the margin: attribute above). Moreover, you'll notice that there are tabs not only on attributes and callbacks, but on a hash-table as well. This is how Iup handles class extension. If you want a widget to have an attribute "foo", you can simply use (attribute-set! widget "foo" "value"). Those hash-table attributes are inherited as well. By the way, you are not obliged, to use strings only in their definition.

 

Porting some examples from the Iup Distribution

Now we'll show, how some predefined widgets work. For that we'll port some of the C-examples.

fill.scm

The following dialog shows, how different positions of the fill widget change the dialog's appearance.

(use iup)

;;; Create frame with left aligned button
(define frame-left
  (frame
    (hbox
      (button title: '&Left
              tip: "Left button"
              action: (lambda (self)
                        (print "Stay in with Left button")
                        'default))
      (fill))))
(set! (attribute frame-left title:) "Left aligned")

;;; Create frame with centered button
(define frame-center
  (frame
    (hbox
      (fill)
      (button title: '&Center
              tip: "Central button"
              action: (lambda (self)
                        (print "Way out with Central button")
                        'close))
      (fill))))
(attribute-set! frame-center title: "Centered")

;;; Create frame with right aligned button
(define frame-right
  (frame
    (hbox
      (fill)
      (button title: '&Right
              tip: "Right button"
              action: (lambda (self)
                        (print "Way out with Right button")
                        'close)))
    title: "Right aligned"))

; Note, that callbacks should return a symbol.
; Only the symbol 'close of the action: callbacks closes the dialog!

;;; Create dialog with these three frames
(define dlg
  (dialog
    (vbox
      frame-left
      frame-center
      frame-right)))
(set! (attribute dlg size:) 120)
(set! (attribute dlg title:) 'Fill)

(show dlg)
(main-loop)
(destroy! dlg)
(exit 0)

hbox.scm

This dialog shows, how horizontal boxes within vertical ones work.

(use iup)

(define dlg
  (dialog
    (vbox
      (frame
        (hbox (fill)
              (button "1" "" size: "30x30")
              (button "2" "" size: "30x40")
              (button "3" "" size: "30x50")
              (fill)
              alignment: "ATOP"
              gap: 10
              size: 200)
        title: "alignment: ATOP gap: 10 size: 200"
        gap: 10
        size: 200)
      (frame
        (hbox (fill)
              (button "1" "" size: "30x30")
              (button "2" "" size: "30x40")
              (button "3" "" size: "30x50")
              (fill)
              alignment: "ACENTER"
              gap: 20)
        title: "alignment: ACENTER gap: 20"
        gap: 20)
      (frame
        (hbox (fill)
              (button "1" "" size: "30x30")
              (button "2" "" size: "30x40")
              (button "3" "" size: "30x50")
              (fill)
              alignment: "ABOTTOM"
              size: 150)
        title: "alignment: ABOTTOM size: 150"
        size: 150))
    title: "Hbox"))

(show dlg x: 'center y: 'center)
(main-loop)
(destroy! dlg)

Now a dialog with a menu and submenus, one of whose items call a predefined message-dialog.

(require-library iup)
(import iup iup-dialogs)

(define item-open (menu-item "&Open"))

(define item-save (menu-item "&Save"))

(define item-undo (menu-item "&Undo" active: "NO"))

(define item-exit (menu-item "E&xit"))
(set! (callback item-exit action:)
      (lambda (self) 'close))

(define file-menu
  (menu item-open 
        item-save 
        (menu-separator)
        item-undo
        item-exit))

(define item-about (menu-item "&About"))
(define (about-cb self)
  (show (message-dialog value: "Information goes here ...") modal?: #t)
  'default)
(callback-set! item-about action: about-cb)

(define help-menu
  (menu item-about))

(define mnu
  (menu (menu-item "&File" file-menu)
        (menu-item "&Help" help-menu)))
;(set! (handle-name mnu) "mymenu")

(define dlg (dialog (canvas "")))
(set! (attribute dlg menu:) mnu);"mymenu")
(set! (attribute dlg title:) "Menu")

(show dlg)
(main-loop)
(exit 0)

Note, that the underlined characters behave differently in menus and submenus. In the former they execute their callbacks with the Alt prefix, in the latter without.

Note also, that predefined dialogs can only be shown with the modal?: attribute set.

filedlg.scm

Now we use two predefined dialogs, file-dialag and message-dialog. You should note, that they are mapped to gtk-dialogs!

(use iup)

(define (popup dlg . args)
  (apply show dlg #:modal? #t  args)
  (destroy dlg))

(define dlg (file-dialog title: "File save"
                         dialogtype: 'SAVE
                         filter: "*.led"
                         filterinfo: "Iup resource file"))

(popup dlg x: 'center y: 'center)

(let ((status (attribute dlg status:)))
  (cond
    ((string=? status "1")
     (popup
       (message-dialog value: (string-append  "New File: "
                                              (attribute dlg value:)))))
    ((string=? status "0")
     (popup
       (message-dialog value: (string-append  "File already exists: "
                                              (attribute dlg value:)))))
    ((string=? status "-1")
     (popup
       (message-dialog value: "File-dialog: Operation Canceled")))))

(destroy! dlg)
(exit 0) 

Note, that the status: attribute of the file-dialog is a string!

multiline.scm

Now, we'll show how widgets within a dialog can communicate whith each other. The trick is, that Iup can reference widgets by name, or, to be more precise, by string name. You have seen this in the LED example above: In LED a widget is named by a string and constructed by the LED interpreter. To use it in Scheme, you reference the widget proper from its name via handle-ref. This can be done without LED as well. But then you must give the widget a string name via handle-name-set! or the generalized version (set! (handle-name widget) name).

The following dialog contains two textboxes, one of them multiline, a dropdown listbox and some buttons, allowing to get or set the multiline attributes. Please take note, how handle-ref and handle-name is used.

(use iup)

(define (message title value)
  (show (message-dialog title: title value: value) #:modal? #t))

;; setter and getter
;; widgets are referenced by name
(define (set-attribute keyword)
  (let (
    (msg (sprintf "Attribute ~A set with value ~A"
                  keyword (attribute single value:)))
    (multi (handle-ref "multi"))
    (single (handle-ref "single"))
    )
    (attribute-set! multi keyword (attribute single value:))
    (message "Set attribute" msg)
    'default))

(define (get-attribute keyword)
  (let (
    (msg (sprintf "Attribute ~A get with value ~A"
                  keyword (attribute multi keyword)))
    (multi (handle-ref "multi"))
    (single (handle-ref "single"))
    )
    (attribute-set! single value: (attribute multi keyword))
    (message "Get attribute" msg)
    'default))

;; callback generator
(define (cb keyword)
  (lambda (self)
    (let ((lb (handle-ref "lb")))
      (if (eqv? (string->number (attribute lb value:)) 1)
        (set-attribute keyword)
        (get-attribute keyword))
      'default)))

;; buttons
(define btn-append
  (button title: '&Append
          action: (cb append:)))

(define btn-insert
  (button title: '&Insert
          action: (cb insert:)))

(define btn-border
  (button title: '&Border
          action: (cb boarder:)))

(define btn-caret
  (button title: '&Caret
          action: (cb caret:)))

(define btn-readonly
  (button title: '&Readonly
          action: (cb readonly:)))

(define btn-selection
  (button title: '&Selection
          action: (cb selection:)))

(define btn-selectedtextbox
  (button title: 'Selected&textbox
          action: (cb selectedbox:)))

(define btn-nc
  (button title: '&Nc
          action: (cb nc:)))

(define btn-value
  (button title: '&Value
          action: (cb value:)))

;; other widgets
(define lb (listbox #:1 'set #:2 'get value: 1 dropdown: 'Yes))
(define multi (textbox expand: 'Yes multiline: 'Yes))
(define single (textbox expand: 'horizontal))

;; name those other widgets, so that they can be referenced by name
(set! (handle-name lb) "lb")
(set! (handle-name multi) "multi")
(set! (handle-name single) "single")

;; the dialog
(define dlg
  (dialog (vbox multi
                (hbox lb
                      single)
                (hbox btn-append btn-insert btn-border btn-caret
                      btn-readonly btn-selection)
                (hbox btn-selectedtextbox btn-nc btn-value))
          title: "Multiline example"
          size: 'HALFxQUARTER))

(show dlg x: 'center y: 'center)
(main-loop)
(destroy! dlg)
(exit 0)

There is one caveat in the listbox: The lines must be denoted with prefixed keyword notation, 1: will not work, for example!

progressbar.scm

Now a dialog with a progressbar and a timer as well as some pixmap images for the control buttons. The latter are constructed with image/palette, which accepts two dimension parameters and a blob with the pixmap data. The color of each pixel is described by a number and translated by that very number attribute to an rgb-string.

(use iup srfi-4)

;; button images

(define pause-img
  (image/palette 22 22
    (u8vector->blob
      (u8vector
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 1 1 2 2 1 1 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 1 1 2 2 1 1 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 1 1 2 2 1 1 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 1 1 2 2 1 1 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 1 1 2 2 1 1 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 1 1 2 2 1 1 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 1 1 2 2 1 1 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 1 1 2 2 1 1 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 1 1 2 2 1 1 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2))
    #:1 "0 0 0"
    #:2 "220 218 213"))

(define play-img
  (image/palette 22 22
    (u8vector->blob
      (u8vector
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2
        2 2 2 2 2 2 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2))
    #:1 "0 0 0"
    #:2 "220 218 213"))
 
(define reset-img
  (image/palette 22 22
    (u8vector->blob
      (u8vector
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 1 1 2 2 2 2 2 2 2 2 1 1 2 2 2 2 2 2
        2 2 2 2 1 1 2 2 2 2 2 2 1 1 1 1 2 2 2 2 2 2
        2 2 2 2 1 1 2 2 2 2 1 1 1 1 1 1 2 2 2 2 2 2
        2 2 2 2 1 1 2 2 1 1 1 1 1 1 1 1 2 2 2 2 2 2
        2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2
        2 2 2 2 1 1 2 2 1 1 1 1 1 1 1 1 2 2 2 2 2 2
        2 2 2 2 1 1 2 2 2 2 1 1 1 1 1 1 2 2 2 2 2 2
        2 2 2 2 1 1 2 2 2 2 2 2 1 1 1 1 2 2 2 2 2 2
        2 2 2 2 1 1 2 2 2 2 2 2 2 2 1 1 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2))
    #:1 "0 0 0"
    #:2 "220 218 213"))

(define decelerate-img
  (image/palette 22 22
    (u8vector->blob
      (u8vector
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 1 2 2 2 2 1 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 1 1 2 2 2 1 1 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 1 1 1 2 2 1 1 1 2 2 2 2 2 2 2
        2 2 2 2 2 2 1 1 1 1 2 1 1 1 1 2 2 2 2 2 2 2
        2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2
        2 2 2 2 2 2 1 1 1 1 2 1 1 1 1 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 1 1 1 2 2 1 1 1 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 1 1 2 2 2 1 1 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 1 2 2 2 2 1 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2))
    #:1 "0 0 0"
    #:2 "220 218 213"))

(define forward-img
  (image/palette 22 22
    (u8vector->blob
      (u8vector
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 1 2 2 2 2 1 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 1 1 2 2 2 1 1 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 1 1 1 2 2 1 1 1 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 1 1 1 1 2 1 1 1 1 2 2 2 2 2 2
        2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2
        2 2 2 2 2 2 2 1 1 1 1 2 1 1 1 1 2 2 2 2 2 2
        2 2 2 2 2 2 2 1 1 1 2 2 1 1 1 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 1 1 2 2 2 1 1 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 1 2 2 2 2 1 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
        2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2))
    #:1 "0 0 0"
    #:2 "220 218 213"))

(define speed 0.01)

;; button callbacks

(define (ok-cb self) 'close)

(define (idle-cb self)
  (set! (attribute progressbar value:)
        (+ (string->number (attribute progressbar value:)) speed))
        ; will internally be transformed to a string
  (if (>= (string->number (attribute progressbar value:)) 1)
    (set! (attribute progressbar value:) 0))
  'default)

(define (pause-cb self)
  (if (string= (attribute tmr run:) "YES")
    (begin
      (attribute-set! tmr run: 'no) ; internally transformed to "NO"
      (attribute-set! pause-btn image: pause-img))
    (begin
      (attribute-set! tmr run: 'yes) ; internally transformed to "YES"
      (attribute-set! pause-btn image: play-img)))
  'default)

(define (reset-cb self)
  (attribute-set! progressbar value: 0)
  'default)

(define (accelerate-cb self)
  (set! speed (* 2 speed))
  (if (> speed 1.0) (set! speed 1.0))
  'default)

(define (decelerate-cb self)
  (set! speed (/ speed 2))
  'default)

;; buttons

(define reset-btn
  (button "&Reset"
          action: reset-cb
          tip: "Reset timer"
          image: reset-img))

(define pause-btn
  (button "&Pause"
          action: pause-cb
          tip: "Pause timer"
          image: play-img))

(define accelerate-btn
  (button "&Accelerate"
          action: accelerate-cb
          tip: "Accelerate timer"
          image: forward-img))

(define decelerate-btn
  (button "&Decelerate"
          action: decelerate-cb
          tip: "Decelerate timer"
          image: decelerate-img))

(define ok-btn
  (button "&OK"
          action: ok-cb
          tip: "Close window"
          expand: 'yes))

;; timer

(define tmr (timer time: 100 action-cb: idle-cb))
(attribute-set! tmr run: 'yes) ; doesn't work in constructor
; 'yes or any of the values 1, 'true, "TRUE", #t ... will internally
; be transformed to "YES"

;; progressbar

(define progressbar
  (progress-bar value: "0.34"
                ;dashed: 'yes ; doesn't work
                expand: 'yes))

;; dialog

(define dlg
  (dialog
    (vbox progressbar
          (hbox
            (fill)
            pause-btn
            reset-btn
            decelerate-btn
            accelerate-btn
            (fill))
          (fill)
          (hbox
            (fill)
            ok-btn
            (fill)))
    title: 'Progressbar
    resize: 'no))

;; run

(show dlg)
(main-loop)
(destroy! dlg)
(exit 0)

webbrowser.scm

Now an example which shows how mighty iup-web is, a Chicken implementation of the webkit engine. With some lines of code we can write a fully functional webbrowser. But there is one caveat with it: For reasons I don't understand, the program will crash unless you load libiupweb.so via the LD_PRELOAD environment variable.

Another Warning: Running this file in the interpreter with the readline module loaded will cause a segfault. To be on the safe side use csi -n.

(require-library iup)
(import iup iup-web data-structures)

;;; webbrowser callbacks

(define (on-history self)
  (let (
    (back (attribute (handle-ref "dlg-web") backcount:))
    (fwrd (attribute (handle-ref "dlg-web") forwardcount:))
    )
    (print "History items")
    (let loop ((i (if (string? back) (- (string->number back)) 0)))
      (unless (zero? i)
        (printf "Backward ~a: ~a~%"
                i (attribute (handle-ref "dlg-web")
                             (string->keyword (sprintf "itemhistory~a" i))))
        (loop (+ i 1))))
    (printf "Current ~a: ~a~%"
            0 (attribute (handle-ref "dlg-web") itemhistory0:))
    (let ((fwd (if (string? fwrd) (string->number fwrd) 0)))
      (let loop ((i 1))
        (unless (> i fwd)
          (printf "Forward ~a: ~a~%"
                  i (attribute (handle-ref "dlg-web")
                               (string->keyword (sprintf "itemhistory~a" i))))
          (loop (+ i 1)))))
    'default))

(define (on-navigation self url)
  (print "web-browser navigate-cb: url " url)
  (if (substring-index "download" url)
    'ignore
    'default))

(define (on-error self url)
  (print "web-browser error-cb: url " url)
  'default)
      
(define (on-completion self); url)
  (print "web-browser completed-cb: value: " (attribute self value:)); url)
  (attribute-set! (handle-ref "dlg-listbox") insertitem1:
                  (attribute self value:))
  (attribute-set! (handle-ref "dlg-listbox") value:
                  (attribute self value:))
  'default)
; note, that the documentation required two arguments, but the attribute
; completed-cb: accepted only one.
      
(define (on-newwindow self url)
  (print "web-browser newwindow-cb: url " url)
  (attribute-set! self value: url)
  'default)

;;; button callbacks

(define (on-back self)
  (attribute-set! (handle-ref "dlg-web") backforward: -1)
  'default)
      
(define (on-forward self)
  (attribute-set! (handle-ref "dlg-web") backforward: 1)
  'default)
      
(define (on-stop self)
  (attribute-set! (handle-ref "dlg-web") stop: 'no)
  'default)
      
(define (on-reload self)
  (attribute-set! (handle-ref "dlg-web") reload: 'no)
  'default)
      
(define (on-load self)
  (attribute-set! (handle-ref "dlg-web") value:
                  (attribute (handle-ref "dlg-listbox") value:))
  'default)

(define (on-search self)
  (attribute-set! (handle-ref "dlg-web") value:
                  "https://eu.ixquick.com")
  'default)

(define (on-home self)
  (attribute-set! (handle-ref "dlg-web") value:
                  "http://www.call-cc.org")
  'default)

;;; listbox callbacks

(define (on-valuechanged self)
  (print "listbox valuechanged-cb: value: "(attribute self value:))
  (attribute-set! (handle-ref "dlg-web") value:
                  (attribute self value:))
  'default)

(define (on-dropdown self state)
  (print "listbox dropdown-cb: state " state " value: " (attribute self value:))
  'default)

(define (on-action self text item state)
  (print "listbox action: text " text " item " item " state " state)
  (if (= state 1)
    (attribute-set! (handle-ref "dlg-web") value: text))
  'default)

(define (on-key self key)
  (print "listbox k-any: key " key)
  (if (= key (char->integer #\return))
    (attribute-set! (handle-ref "dlg-web") value:
                    (attribute self value:)))
  'continue)

;;;;;;;;;;;;;;;;; WebBrowserTest

;(define dlg-textbox
;  (textbox value: "http://wiki.call-cc.org";www.tecgraf.puc-rio.br/iup"
;           expand: 'horizontal))
;(handle-name-set! dlg-textbox "dlg-textbox")

(define dlg-listbox
  (listbox value: "http://www.tecgraf.puc-rio.br/iup";call-cc.org"
           expand: 'horizontal
           dropdown: 'yes
           editbox: 'yes
           #:1 "http://api.call-cc.org"
           action: on-action
           k-any: on-key
           dropdown-cb: on-dropdown
           ;valuechanged-cb: on-valuechanged
           ;edit-cb: on-edit
           ))
(handle-name-set! dlg-listbox "dlg-listbox")

(define dlg-web
  (web-browser value: (attribute dlg-listbox value:)
               ;value: (attribute dlg-textbox value:)
               newwindow-cb: on-newwindow
               navigate-cb: on-navigation
               error-cb: on-error
               completed-cb: on-completion))
                             ;(lambda (self)
                             ;  (on-completion self (attribute self value:)))))
                             ;  ;; wrapper replaces the C typecast (Icallback)
(handle-name-set! dlg-web "dlg-web")

(define dlg
  (dialog
    (vbox
      (hbox
        (button "&Home" action: on-home)
        (button "&Back" action: on-back)
        (button "&Forward" action: on-forward)
        (button "&Load" action: on-load)
        (button "&Reload" action: on-reload)
        (button "S&top" action: on-stop)
        (button "H&istory" action: on-history)
        (button "&Search" action: on-search)
        (fill)
        (button "E&xit" action: (lambda (self) 'close))
        )
      dlg-listbox
      dlg-web)
    rastersize: '800x600
    title: 'WebBrowser))
    ;defaultenter: btn-load
    ;margin: '10x10
    ;gap: 10))

(show dlg)
(main-loop)
(destroy! dlg)
(exit 0)

Concluding remark

All these examples show, that Iup is really easy to use ...

Author

Juergen Lorenz

Initial version

Nov 25, 2011

Last updated

Apr 04, 2012