Code Monkey home page Code Monkey logo

iup's Introduction

Common Lisp CFFI bindings to the IUP Portable User Interface library

./docs/screenshots/sample-01.png

./docs/screenshots/sample-02.png

Compatibility

IUP 3.29

Introduction

Shared Libraries

Get the Tecgraf shared libraries (.so and .dll files) from the Tecgraf project pages and install them per your operating system. Usually this means setting LD_LIBRARY_PATH on Linux or PATH on Windows.

There is, however, a companion project which can be used to download and install the Tecgraf IUP, CD and IM shared libraries for x86/64-bit Linux and Windows automatically:

(ql:quickload "tecgraf-libs")

This downloads the archives from each of the project pages according to the platform your lisp is running on (Linux or Windows). It unpacks the archives, and copies all of the shared libraries to a single directory.

It is a one-off step is is not needed subsequently.

NOTE: On Linux, the tecgraf-libs system needs the patchelf command available (usually available via sudo apt install patchelf). patchelf is used to set the shared library .so files origin to the library’s location so that the multiple shared libraries loaded via CFFI uses are found relative to each other.

NOTE: On Windows, the environment variable `PATH` should be modified so it points to the directory where the libs are downloaded. This has to be done before starting your Lisp implementation. Example of doing it and starting CCL:

SET PATH=C:\mylisp\projects\tecgraf-libs\libs;
wx86cl64.exe

To tell CFFI to find these libraries, use:

(ql:quickload "cffi")

(pushnew (asdf:system-relative-pathname :tecgraf-libs #p"libs/")
         cffi:*foreign-library-directories*)

Loading IUP

(First, read the previous part on getting the Tecgraf libraries and installing).

Requirements:

Download/clone the following systems:

  • lispnik/`tecgraf-base`
  • lispnik/`pffft`

Those are required by Iup.

Then load iup.asd in the usual way (e.g. `(ql:quickload “iup”)`

NOTE: For SBCL, you need to set a larger heap size to compile the bindings, e.g. --dynamic-space-size 2048

Running GUI Applications on Windows using SLIME or SLY

On Windows, it is necessary to start your Lisp indrectly via CMD.EXE, e.g. for SLY, in your Emacs configuration, include something like:

(setq sly-lisp-implementations
         '((ccl ("cmd" "/c" "wx86cl64"))
           (sbcl ("cmd" "/c" "sbcl.exe" "--dynamic-space-size" "2048"))))

Similarly use slime-lisp-implementations if your are using SLIME.

This is not specific to IUP or the bindings. It also works for running other graphical application libraries like CL-SDL2.

Hello, World!

;;; Generated from org-mode, do not edit

(eval-when (:compile-toplevel :load-toplevel :execute)
  (ql:quickload "iup"))

(defpackage #:iup-examples.hello
  (:use #:common-lisp)
  (:export #:hello))

(in-package #:iup-examples.hello)
(defun hello ()
  (iup:with-iup ()
    (let* ((label (iup:label :title (format nil "Hello, World!~%IUP ~A~%~A ~A"
                                            (iup:version)
                                            (lisp-implementation-type)
                                            (lisp-implementation-version))))
           (dialog (iup:dialog label :title "Hello, World!")))
      (iup:show dialog)
      (iup:main-loop))))
#-sbcl (hello)

#+sbcl
(sb-int:with-float-traps-masked
    (:divide-by-zero :invalid)
  (hello))

./docs/screenshots/helloworld.png ./docs/screenshots/helloworld-2.png

Callbacks

Simple Example

;;; Generated from org-mode, do not edit

(eval-when (:compile-toplevel :load-toplevel :execute)
  (ql:quickload "iup"))

(defpackage #:iup-examples.callback
  (:use #:common-lisp)
  (:export #:callback))

(in-package #:iup-examples.callback)
(defun callback ()
  (iup:with-iup ()
    (let* ((button1
             (iup:button :title "Test &1"
                         :expand :yes
                         :tip "Callback inline at control creation"
                         :action (lambda (handle)
                                   (message "button1's action callback")
                                   iup:+default+)))
           (button2
             (iup:button :title "Test &2"
                         :expand :yes
                         :tip "Callback set later using (SETF (IUP:CALLBACK ..) ..)"))
           (button3
             (iup:button :title "Test &3"
                         :expand :yes
                         :tip "Callback example using symbol-referenced function at control creation"
                         :action 'test3-callback))
           (button4
             (iup:button :title "Test &4"
                         :expand :yes
                         :tip "Callback example using symbol-referenced function later using (SETF (IUP:CALLBACK ..) ..)"))
           (vbox
             (iup:vbox (list button1 button2 button3 button4)
                       :gap "10"
                       :margin "10x10"
                       :alignment :acenter))
           (dialog
             (iup:dialog vbox :title "Callback Example")))
      (setf (iup:callback button2 :action)
            (lambda (handle)
              (message "button2's action callback")
              iup:+default+))
      (setf (iup:callback button4 :action) 'test4-callback)
      (iup:show dialog)
      (iup:main-loop))))

(defun test3-callback (handle)
  (message "button3's action callback")
  iup:+default+)

(defun test4-callback (handle)
  (message "button4's action callback")
  iup:+default+)

(defun message (message)
  (iup:message "Callback Example" message))
#-sbcl (callback)

#+sbcl
(sb-int:with-float-traps-masked
    (:divide-by-zero :invalid)
  (callback))

./docs/screenshots/callback-1.png ./docs/screenshots/callback-2.png

./docs/screenshots/callback-3.png ./docs/screenshots/callback-4.png

Color Mixer

Consider capturing state by creating a closure over the controls that make up state:

;;; Generated from org-mode, do not edit

(eval-when (:compile-toplevel :load-toplevel :execute)
  (ql:quickload "iup"))

(defpackage #:iup-examples.mixer
  (:use #:common-lisp)
  (:export #:mixer))

(in-package #:iup-examples.mixer)

(defun make-mixer-action (r g b button label)
  (lambda (handle)
    (declare (ignore handle))
    (let ((color (format nil "~A ~A ~A"
                         (floor (iup:attribute r :value 'number))
                         (floor (iup:attribute g :value 'number))
                         (floor (iup:attribute b :value 'number)))))
      (setf (iup:attribute button :fgcolor) color
            (iup:attribute label :title) color)
      (iup:refresh button))
    iup:+default+))

(defun mixer ()
  (iup:with-iup ()
    (let* ((button (iup:flat-button :expand :yes :canfocus :no))
           (label (iup:label :expand :horizontal :title "#x00000" :alignment "ACENTER:ACENTER"))
           (r (iup:val :expand :horizontal :min 0 :max 255))
           (g (iup:val :expand :horizontal :min 0 :max 255))
           (b (iup:val :expand :horizontal :min 0 :max 255))
           (vbox (iup:vbox
                  (list (iup:grid-box
                         (list (iup:label :title "&Red")   r
                               (iup:label :title "&Green") g
                               (iup:label :title "&Blue")  b)
                         :numdiv 2
                         :cgapcol 10
                         :cgaplin 5)
                        button
                        label)
                  :cmargin 5
                  :cgap 5
                  :margin "x5"))
           (dialog (iup:dialog vbox :title "Color Mixer Example" :size "QUARTERxQUARTER")))
      (loop :with action := (make-mixer-action r g b button label)
            :for handle :in (list r g b)
            :do (setf (iup:callback handle :valuechanged_cb)  action))
      (iup:show dialog)
      (iup:main-loop))))

#-sbcl (mixer)

#+sbcl
(sb-int:with-float-traps-masked
    (:divide-by-zero :invalid)
  (mixer))

./docs/screenshots/mixer-01.png

./docs/screenshots/mixer-02.png

Idle Action

;;; Generated from org-mode, do not edit

(eval-when (:compile-toplevel :load-toplevel :execute)
  (ql:quickload "iup"))

(defpackage #:iup-examples.idle
  (:use #:common-lisp)
  (:export #:idle))

(in-package #:iup-examples.idle)

There is a global callback for running functions when IUP event loop is idle. See also IUP: Idle Action. In Lisp, it can be set using (SETF IUP:IDLE-ACTION). Note: Idle actions run a lot more often than you’d expect. Try the following example application to get an idea for just how often.

An idle action function should return IUP:+DEFAULT+, IUP:+CLOSE+ or IUP:+IGNORE+. IUP:+DEFAULT will cause the idle action function to be called repeatedly. IUP:+IGNORE+ will run idle action once. IUP:+CLOSE+ will exit the event loop.

(defun idle ()
  (iup:with-iup ()
    (let* ((counter (iup:label :fontsize 24
                               :title 0
                               :expand :yes
                               :alignment :acenter))
           (start-button (iup:button :title "&Start" :expand :horizontal))
           (stop-button (iup:button :title "S&top" :expand :horizontal))
           (do-nothing nil)
           (do-nothing-toggle (iup:toggle :title "Do nothing"
                                          :action (lambda (handle state)
                                                    (setf do-nothing (not do-nothing))
                                                    iup:+default+)))
           (vbox (iup:vbox (list counter
                                 (iup:hbox (list start-button stop-button do-nothing-toggle)
                                           :cgap 5))
                           :margin "5x5"))
           (dialog (iup:dialog vbox
                               :title (format nil "Idle Example on ~A" (lisp-implementation-type))
                               :size "QUARTERxQUARTER")))
      (setf (iup:callback start-button :action)
            (lambda (handle)
              (setf (iup:idle-action)
                    (lambda ()
                      (unless do-nothing
                        (setf (iup:attribute counter :title)
                              (1+ (iup:attribute counter :title 'number))))
                      iup:+default+))
              iup:+default+))
      (setf (iup:callback stop-button :action)
            (lambda (handle)
              (setf (iup:idle-action) nil)
              iup:+default+))
      (iup:show dialog)
      (iup:main-loop))))
#-sbcl (idle)

#+sbcl
(sb-int:with-float-traps-masked
    (:divide-by-zero :invalid)
  (idle))

./docs/screenshots/idle-01.png

./docs/screenshots/idle-02.png

Canvas

In this example, we’ll port the Sierpinski Carpet fractal that appeared the chapter on graphics in Common Lisp Recipes: A Problem-Solution Approach.

We need a spinner (an up and down arrow-controlled number field) and a canvas to draw on to get started. In this example, rather than specify the callbacks inline, as anonymous lamba forms, we will create separate functions and set them later using (SETF IUP:CALLBACK). *LEVELS* will keep track how deep to draw the fractal.

;;; Generated from org-mode, do not edit

(eval-when (:compile-toplevel :load-toplevel :execute)
  (ql:quickload '("iup" "iup-cd" "cd")))

(defpackage #:iup-examples.sierpinksi
  (:use #:common-lisp)
  (:export #:sierpinksi))

(in-package #:iup-examples.sierpinksi)
(defparameter *levels* 0)

(defun sierpinski ()
  (iup:with-iup ()
    (let* ((canvas (iup:canvas :rastersize "200x200"))
           (spin (iup:text :spin "YES" :spinmin 0 :spinmax 4))
           (vbox (iup:vbox (list canvas spin) :alignment "ACENTER"))
           (dialog (iup:dialog vbox :title "Sierpinski Carpet")))
      (setf (iup:callback canvas :map_cb) 'canvas-map
            (iup:callback canvas :unmap_cb) 'canvas-unmap
            (iup:callback canvas :action) 'canvas-redraw
            (iup:callback spin :spin_cb) 'canvas-spin
            *levels* 0)
      (iup:show-xy dialog iup:+center+ iup:+center+)
      (iup:main-loop))))

Notes on Callback Naming

Each IUP widget supports a number of callbacks. In IUP, these are strings. In the Lisp bindings, they can be specified as keywords. For example, :UNMAP_CB. These are rather unlispy names, but do come from IUP via its introspection mechanism. In a future version of these bindings, it might be possible to have lispier names. e.g. :UNMAP-CALLBACK.

CD, a 2D Graphics Library

IUP has support for CD, a cross platform 2D Graphics Library. We have support in Lisp via CD bindings.

The following code is entirely CD dependent and can be used in non-IUP canvas applications.

(defun sierpinski-draw (canvas level)
  (multiple-value-bind
        (w h)
      (cd:size canvas)
    (labels ((square (x y x-size y-size)
               (cd:box canvas x (+ x x-size) y (+ y y-size)))
             (recurse (x y x-size y-size level)
               (let ((x-step (/ x-size 3))
                     (y-step (/ y-size 3)))
                 (square (+ x x-step) (+ y y-step) x-step y-step)
                 (when (plusp level)
                   (dolist (x-next (list x (+ x x-step) (+ x x-step x-step)))
                     (dolist (y-next (list y (+ y y-step) (+ y y-step y-step)))
                       (recurse x-next y-next x-step y-step (1- level))))))))
      (recurse 0 0 w h level))))

For example, we can write it to PDF and print out to hang on your wall:

(ql:quickload "cd-pdf")

(let ((canvas (cd:create-canvas (cd-pdf:context-pdf) "docs/sierpinski.pdf")))
  (unwind-protect
       (sierpinski-draw canvas 4)
    (cd:kill canvas)))

In our IUP example however, we’ll use it with IUP’s CD support and arrange for the canvas to be draw on via CANVAS-REDRAW which will be triggered by the canvas widget’s action callback.

(defparameter *canvas* nil)

(defun canvas-redraw (handle x y)
  (cd:activate *canvas*)
  (cd:clear *canvas*)
  (setf (cd:foreground *canvas*) cd:+red+)
  (sierpinski-draw *canvas* *levels*)
  (cd:flush *canvas*)
  iup:+default+)

We can ignore HANDLE, X, and Y in our callback handler in this example. Those are IUP widget that triggered the callback and location on the canvas.

First we activate the canvas to draw on, clear whatever was there, set a drawing color for the the foreground of the canvas, then draw to the canvas using SIERPINSKI-DRAW.

The last step is to flush the canvas. This triggers a backing buffer swap, so all of the drawing appears instantly. If we don’t do this, we don’t see anything on the screen because it will still be in the off-screen drawing buffer.

Attributes

It is a good idea to separate your UI presentation from its undelying model. In our case, the UI “model” is a special variable *LEVELS* which holds the depth to draw the fractal as an integer. We need this updated when the user clicks on the spinner widget.

(defun canvas-spin (handle pos)
  (setf *levels* (iup:attribute handle :value 'number))
  (canvas-redraw nil nil nil)
  iup:+default+)

We can get the number from the spinner widget and assign it to *LEVELS* using IUP:ATTRIBUTE. It takes a IUP handle from which to get the :VALUE attribute.

IUP widget value attributes are mostly strings. The third argument, ~’INTEGER~ converts the string to an integer for convenience, rather than having to PARSE-INTEGER ourselves.

Fiddly bits

Lastly, we need to associate the CD canvas with a IUP canvas, but we can’t do this until we have the handle of the IUP canvas, so we can’t set it up in the LET* form in our main function like we did with everything else.

Luckily IUP provides callbacks for when the component is “mapped” onto the user’s display which allow us to deal with this dependency in an elegant manner.

(defun canvas-map (handle)
  (setf *canvas* (cd:create-canvas (iup-cd:context-iup-dbuffer) handle))
  iup:+default+)

(defun canvas-unmap (handle)
  (cd:kill *canvas*)
  iup:+default+)
#-sbcl (sierpinski)

#+sbcl
(sb-int:with-float-traps-masked
    (:divide-by-zero :invalid)
  (sierpinski))

./docs/screenshots/sierpinski.png ./docs/screenshots/sierpinski-02.png

Using IUP Additional Controls

The cells control “creates a grid widget (set of cells) that enables several application-specific drawing, such as: chess tables, tiles editors, degrade scales, drawable spreadsheets and so forth”.

It’s included in the standard IUP distribution downloads, but it’s not automatically loaded. The Lisp bindings do the same thing, so to use it, we need to depend on IUP-CONTROLS.

;;; Generated from org-mode, do not edit

(eval-when (:compile-toplevel :load-toplevel :execute)
  (ql:quickload '("iup" "iup-controls" "cd")))

(defpackage #:iup-examples.cells-checkerboard
  (:use #:common-lisp)
  (:export #:cells-checkerboard))

(in-package #:iup-examples.cells-checkerboard)

We start with the same boiler plate, but this time we need to call IUP-CONTROLS:OPEN ahead of using the cells control.

(defun cells-checkerboard ()
  (iup:with-iup ()
    (iup-controls:open)
    (let* ((cells (iup-controls:cells
                   :draw_cb 'draw
                   :width_cb 'width
                   :height_cb 'height
                   :nlines_cb 'nlines
                   :ncols_cb 'ncols
                   :mouseclick_cb 'click))
           (vbox (iup:vbox (list cells)))
           (dialog (iup:dialog vbox :title "Cells Checkerboard" :rastersize "440x480" :shrink "YES")))
      (iup:show-xy dialog iup:+center+ iup:+center+)
      (iup:main-loop))))

Cells has a number of callbacks related rows, columns, sizing etc.

(defun nlines (handle) 8)
(defun ncols (handle) 8)
(defun height (handle i) 50)
(defun width (handle j) 50)

When DRAW is called, we get a canvas on which to draw:

(defun draw (handle i j xmin xmax ymin ymax canvas)
  (if (or (and (oddp i) (oddp j)) (and (oddp (1+ i)) (oddp (1+ j))))
      (setf (cd:foreground canvas) cd:+black+)
      (setf (cd:foreground canvas) cd:+white+))
  (cd:box canvas xmin xmax ymin ymax)
  iup::+default+)

When out click callback is called:

(defun click (handle button pressed line column x y status)
  (iup:message
   "Clicked!"
   (format nil "Callback arguments~%~S"
    (list :button button
          :pressed pressed
          :line line
          :column column
          :x x
          :y y
          :status (iup:status-plist status))))
     iup:+default+)
#-sbcl (cells-checkerboard)

#+sbcl
(sb-int:with-float-traps-masked
    (:divide-by-zero :invalid)
  (cells-checkerboard))

./docs/screenshots/checkerboard-01.png ./docs/screenshots/checkerboard-02.png

./docs/screenshots/checkerboard-03.png ./docs/screenshots/checkerboard-04.png

(lol button 49)

Detachable Box

;;; Generated from org-mode, do not edit

(eval-when (:compile-toplevel :load-toplevel :execute)
  (ql:quickload "iup"))

(defpackage #:iup-examples.detached
  (:use #:common-lisp)
  (:export #:detached))

(in-package #:iup-examples.detached)
(defun detached ()
  (iup:with-iup ()
    (let* ((button1 (iup:button :title "Detach Me!"
                                :action 'button-detach-callback
                                :expand :yes
                                :handlename "detach"))
           (multi-line (iup:multi-line :expand :yes
                                       :visiblelines 5))
           (hbox (iup:hbox (list button1 multi-line) :margin "10x0"))
           (dbox (iup:detach-box hbox :orientation :vertical
                                      :detached_cb 'detached-callback
                                      :handlename "dbox"))
           (label (iup:label :title "Label"
                             :expand :vertical))
           (button2 (iup:button :title "Restore me!"
                                :expand :yes
                                :active :no
                                :action 'button-restore-callback
                                :handlename "restore"))
           (text (iup:text :expand :horizontal))
           (dialog (iup:dialog (iup:vbox (list dbox label button2 text)
                                         :margin "10x10"
                                         :gap 10)
                               :title "IupDetachBox Example"
                               :rastersize "300x300")))

      (iup:show dialog)
      (iup:main-loop))))

Handle Names

Instead of accessing other elements via lexical scope, it’s sometimes useful to refer to them by name. This example uses the HANDLENAME attribute to associate a name with an IUP handle.

(defun detached-callback (handle new-parent x y)
  (setf (iup:attribute new-parent :title) "New Dialog"
        (iup:attribute (iup:handle "restore") :active) :yes
        (iup:attribute (iup:handle "detach") :active) :no)
  iup:+default+)

(defun button-restore-callback (button)
  (setf (iup:attribute (iup:handle "dbox") :restore) nil
        (iup:attribute button :active) :no
        (iup:attribute (iup:handle "detach") :active) :yes)
  iup:+default+)

(defun button-detach-callback (button)
  (setf (iup:attribute (iup:handle "dbox") :detach) nil
        (iup:attribute button :active) :no
        (iup:attribute (iup:handle "restore") :active) :yes)
  iup:+default+)
#-sbcl (detached)

#+sbcl
(sb-int:with-float-traps-masked
    (:divide-by-zero :invalid)
  (detached))

./docs/screenshots/detach-01.png ./docs/screenshots/detach-02.png

Tabs Example

Demonstrates the use of (SETF IUP:ATTRIBUTE) for setting attributes not available via control’s constructor function.

;;; Generated from org-mode, do not edit

(eval-when (:compile-toplevel :load-toplevel :execute)
  (ql:quickload "iup"))

(defpackage #:iup-examples.tabs
  (:use #:common-lisp)
  (:export #:tabs))

(in-package #:iup-examples.tabs)
(defun tabs ()
  (iup:with-iup ()
    (let* ((vbox1 (iup:vbox
                   (list (iup:label :title "Inside Tab A")
                         (iup:button :title "Button A"))))
           (vbox2 (iup:vbox
                   (list (iup:label :title "Inside Tab B")
                         (iup:button :title "Button B"))))
           (tabs1 (iup:tabs (list vbox1 vbox2)))
           (vbox3 (iup:vbox
                   (list (iup:label :title "Inside C")
                         (iup:button :title "Button C"))))
           (vbox4 (iup:vbox
                   (list (iup:label :title "Inside D")
                         (iup:button :title "Button D"))))
           (tabs2 (iup:tabs (list vbox3 vbox4)))
           (box (iup:hbox (list tabs1 tabs2) :margin "10x10" :gap "10"))
           (dialog (iup:dialog box :title "IUP Tabs" :size "200x80")))
      (setf (iup:attribute vbox1 :tabtitle) "Tab A"
            (iup:attribute vbox2 :tabtitle) "Tab B"
            (iup:attribute vbox3 :tabtitle) "Tab C"
            (iup:attribute vbox4 :tabtitle) "Tab D")
      (iup:show dialog)
      (iup:main-loop))))
#-sbcl (tabs)

#+sbcl
(sb-int:with-float-traps-masked
    (:divide-by-zero :invalid)
  (tabs))

./docs/screenshots/tabs-01.png ./docs/screenshots/tabs-02.png

Plotting

Example ./examples/plot.lisp

./docs/screenshots/plot-01.png

./docs/screenshots/plot-02.png

OpenGL

For this example, we’ll take advantage for cl-opengland and cl-glu. Don’t forget to depend on iup-gl (part of these bindings) as well.

Much of this example is tedious old-style OpenGL. We’ll only highlight the IUP/OpenGL integration points here. It suffices to say, we’ve got a function CUBE which draws OpenGL things to the current buffer.

;;; Generated from org-mode, do not edit

(eval-when (:compile-toplevel :load-toplevel :execute)
  (ql:quickload '("iup" "iup-gl" "cl-opengl" "cl-glu")))

(defpackage #:iup-examples.cube
  (:use #:common-lisp)
  (:export #:cube))

(in-package #:iup-examples.cube)
(defvar *canvas* nil)
(defvar *tt* 0.0)

(defvar *vertices*
  #((-1 -1 1) (-1 1 1)
    (1 1 1) (1 -1 1)
    (-1 -1 -1) (-1 1 -1)
    (1 1 -1) (1 -1 -1)))

(defun polygon (a b c d)
  (gl:begin :polygon)
  (apply #'gl:vertex (aref *vertices* a))
  (apply #'gl:vertex (aref *vertices* b))
  (apply #'gl:vertex (aref *vertices* c))
  (apply #'gl:vertex (aref *vertices* d))
  (gl:end))

(defun color-cube ()
  (gl:color 1 0 0)
  (gl:normal 1 0 0)
  (polygon 2 3 7 6)
  (gl:color 0 1 0)
  (gl:normal 0 1 0)
  (polygon 1 2 6 5)
  (gl:color 0 0 1)
  (gl:normal 0 0 1)
  (polygon 0 3 2 1)
  (gl:color 1 0 1)
  (gl:normal 0 -1 0)
  (polygon 3 0 4 7)
  (gl:color 1 1 0)
  (gl:normal 0 0 -1)
  (polygon 4 5 6 7)
  (gl:color 0 1 1)
  (gl:normal -1 0 0)
  (polygon 5 4 0 1))
(defun cube ()
  (iup:with-iup ()
    (iup-gl:open)
    (setf *canvas*
          (iup-gl:canvas :rastersize "640x480"
                         :buffer "DOUBLE"
                         :action 'repaint
                         :resize_cb 'resize))
    (let* ((dialog (iup:dialog *canvas* :title "IUP OpenGL")))
      ;; FIXME      (iup-cffi::%iup-set-function :idle_action 'idle)
      (setf (iup:attribute *canvas* :depthsize) "16")
      (iup:show dialog)
      (iup:main-loop))))

Our example has three callbacks: repaint, resize and a global idle function callback which we’ll use to rotate a cube relative to time variable *TT*.

(defun repaint (handle posx posy)
  (iup-gl:make-current handle)
  (gl:clear-color 0.3 0.3 0.3 1.0)
  (gl:clear :color-buffer-bit :depth-buffer-bit)
  (gl:enable :depth-test)
  (gl:matrix-mode :modelview)
  (gl:with-pushed-matrix
    (gl:translate 0 0 0)
    (gl:scale 1 1 1)
    (gl:rotate *tt* 0 0 1)
    (color-cube))
  (iup-gl:swap-buffers handle)
  iup::+default+)

(defun resize (handle width height)
  (iup-gl:make-current handle)
  (gl:viewport 0 0 width height)
  (gl:matrix-mode :modelview)
  (gl:load-identity)
  (gl:matrix-mode :projection)
  (gl:load-identity)
  (glu:perspective 60 (/ 4 3) 1 15)
  (glu:look-at 3 3 3 0 0 0 0 0 1)
  iup::+default+)
;;; FIXME
;; (cffi:defcallback idle-cb :int ()
;;   (incf tt)
;;   (iup-gl:make-current canvas)
;;   (repaint canvas)
;;   iup::+default+)
#-sbcl (cube)

#+sbcl
(sb-int:with-float-traps-masked
    (:divide-by-zero :invalid)
  (cube))

./docs/screenshots/opengl.png

./docs/screenshots/opengl-01.png

Trees

This is a port of the Lua tree example from the IUP documentation. It goes one step further by allowing the tree to be expanded recursively as branches open.

;;; Generated from org-mode, do not edit

(eval-when (:compile-toplevel :load-toplevel :execute)
  (ql:quickload '("iup" "iup-controls" "uiop")))

(defpackage #:iup-examples.tree
  (:use #:common-lisp)
  (:export #:tree))

(in-package #:iup-examples.tree)
(defun get-dir (pathname)
  (assert (uiop:directory-pathname-p pathname))
  (loop for pathname in (uiop:directory* (make-pathname :name :wild :defaults pathname))
        if (uiop:directory-pathname-p pathname)
          collect pathname into dirs
        else
          collect pathname into files
        finally (return (values dirs files))))

(defun fill-tree (tree id pathname)
  (multiple-value-bind
        (dirs files)
      (get-dir pathname)
    (dolist (file files)
      (setf (iup:attribute tree :addleaf) (namestring file)))
    (dolist (dir dirs)
      (setf (iup:attribute tree :addbranch) (namestring dir)))
    (setf (iup:attribute tree :title) (namestring pathname))))
(defun map-callback (handle)
  (fill-tree handle 0 "/")
  iup:+default+)

(defun branchopen-callback (handle id)
  (setf (iup:attribute handle (format nil "DELNODE~A" id)) "CHILDREN")
  (fill-tree handle id (iup:attribute handle (format nil "TITLE~A" id))) 
  iup:+default+)

(defun tree ()
  (iup:with-iup ()
    (let* ((tree (iup:tree :minsize "200x300"
                           :map_cb 'map-callback
                           :branchopen_cb 'branchopen-callback))
           (dialog (iup:dialog tree :title "Tree Example")))
      (iup:show dialog)
      (iup:main-loop))))
#-sbcl (tree)

#+sbcl
(sb-int:with-float-traps-masked
    (:divide-by-zero :invalid)
  (tree))

./docs/screenshots/tree-02.png

Built-in Dialogs

IUP includes a number of dialogs, including one that embeds the Scintilla editor control.

;;; Generated from org-mode, do not edit

(eval-when (:compile-toplevel :load-toplevel :execute)
  (ql:quickload '("iup" "iup-scintilla")))

(defpackage #:iup-examples.dialogs
  (:use #:common-lisp)
  (:export #:dialogs))

(in-package #:iup-examples.dialogs)
(defun dialogs ()
  (iup:with-iup ()
    (iup-scintilla:open)
    (flet ((button (title callback)
             (iup:button :title title
                         :action callback
                         :expand :horizontal)))
      (let* ((dialog (iup:dialog
                      (iup:vbox (list (button "File Dialog" 'file-dialog)
                                      (button "Message Dialog" 'message-dialog)
                                      (button "Color Dialog" 'color-dialog)
                                      (button "Font Dialog" 'font-dialog)
                                      (button "Scintilla Dialog" 'scintilla-dialog)
                                      (button "Layout Dialog" 'layout-dialog)))
                      :title "IUP Predefined Dialogs")))
        (iup:show dialog)
        (iup:main-loop)))))

./docs/screenshots/dialogs-01.png ./docs/screenshots/dialogs-02.png

Using IUP:POPUP for Modal Dialogs

Often a UI designs for grabbing the user’s attention via modal dialogs where the dialog is shown above the rest of the application and prevents interaction with the rest of the application. IUP:POPUP lets you achive this.

File Dialog

(defun file-dialog (handle)
  (let ((dialog (iup:file-dialog)))
    (unwind-protect
         (progn
           (iup:popup dialog iup:+center+ iup:+center+)
           (iup:message "File Dialog Example"
                        (format nil "Selected ~A" (iup:attribute dialog :value))))
      (iup:destroy dialog)))
  iup:+default+)

NOTE: Because modal dialogs are often created over the course of a program’s runtime, they need to be destroyed after use, via IUP:DESTROY.

./docs/screenshots/filedialog-01.png ./docs/screenshots/filedialog-02.png

./docs/screenshots/filedialog-03.png ./docs/screenshots/filedialog-04.png

Message Dialog

Message dialogs are like IUP:MESSAGE except that they allow for more configuration (result buttons, etc.).

(defun message-dialog (handle)
  (let ((dialog (iup:message-dialog 
                 :dialogtype :warning
                 :buttons :retrycancel)))
    (unwind-protect
         (progn
           (setf (iup:attribute dialog :value) "Heap exhausted, game over.")
           (iup:popup dialog iup:+center+ iup:+center+)
           (iup:message "Message Dialog"
                        (format nil "Got button response ~S"
                                (iup:attribute dialog :buttonresponse))))
      (iup:destroy dialog)))
  iup:+default+)

./docs/screenshots/messagedialog-01.png ./docs/screenshots/messagedialog-02.png

./docs/screenshots/messagedialog-03.png ./docs/screenshots/messagedialog-04.png

Color Dialog

(defun color-dialog (handle)
  (let ((dialog (iup:color-dialog
                 :title "IUP Color Dialog"
                 :showhex "YES"
                 :showcolortable "YES"
                 :showalpha "YES")))
    (unwind-protect
         (progn
           (iup:popup dialog iup:+center+ iup:+center+)
           (iup:message "Result"
                        (format nil "Got button response ~S~%Got color ~A RGB (~A HSI, ~A)"
                                (iup:attribute dialog :status)
                                (iup:attribute dialog :value)
                                (iup:attribute dialog :valuehsi)
                                (iup:attribute dialog :valuehex))))))
  iup:+default+)

./docs/screenshots/colordialog-01.png ./docs/screenshots/colordialog-02.png

./docs/screenshots/colordialog-03.png ./docs/screenshots/colordialog-04.png

Font Dialog

(defun font-dialog (handle)
  (let ((dialog (iup:font-dialog :title "IUP Font Dialog")))
    (unwind-protect
         (progn
           (iup:popup dialog iup:+center+ iup:+center+)
           (iup:message "Result"
                        (format nil "Got button response ~S~%Got font ~S"
                                (iup:attribute dialog :status)
                                (iup:attribute dialog :value))))
      (iup:destroy dialog)))
  iup:+default+)

./docs/screenshots/fontdialog-01.png ./docs/screenshots/fontdialog-02.png

./docs/screenshots/fontdialog-03.png ./docs/screenshots/fontdialog-04.png

Scintilla Dialog

(defun scintilla-dialog (handle)
  (let ((dialog (iup-scintilla:scintilla-dialog :title "IUP Scintilla Dialog")))
    (unwind-protect
         (iup:popup dialog iup:+center+ iup:+center+)
      (iup:destroy dialog))))

./docs/screenshots/scintilladialog-01.png

./docs/screenshots/scintilladialog-02.png

(There is also a separate, more customizable Scintilla control: IUP-SCINTILLA:SCINTILLA.)

IUP Layout Dialog

The layout dialog lets you visually inspect and edit an existing dialog and it’s children or create a new dialog from scretch. It is extremely useful for experimenting and iterating on UI design.

You can use it as a visual GUI builder, similar to Glade in GTK+.

You can export a dialog and load it from file via IUP:LOAD. The export format is a IUP LED file.

(defun layout-dialog (handle)
  (let ((dialog (iup:layout-dialog nil)))
    (unwind-protect
         (iup:popup dialog iup:+center+ iup:+center+)
      (iup:destroy dialog)))
  iup:+default+)

./docs/screenshots/layoutdialog-01.png

./docs/screenshots/layoutdialog-02.png

Get Text Dialog

List Dialog

Get Param Dialog

Alarm Dialog

#-sbcl (dialogs)

#+sbcl
(sb-int:with-float-traps-masked
    (:divide-by-zero :invalid)
  (dialogs))

Application Icons and IUP-IM

In this example, we’ll set the application icon via an arbitrary image file, demonstrating the use of the IUP-IM system. The IM-IUP system provides support in our IUP bindings for interoperability with IM, an imaging toolkit, also by Tecgraf.

;;; Generated from org-mode, do not edit

(eval-when (:compile-toplevel :load-toplevel :execute)
  (ql:quickload '("iup" "iup-im")))

(defpackage #:iup-examples.icon
  (:use #:common-lisp)
  (:export #:icon))

(in-package #:iup-examples.icon)

Here we load an image from the filesystem using IUP-IM:LOAD-IMAGE and associate the global handle name lispalien with it. We can use this handle name in labels, buttons, etc. to set the image that should be used. In the case of dialogs however, we can use the handle name to specify the image that should be displayed in the application’s title bar.

IM supports a large number of formats. Here we use a .ICO file.

(defun icon ()
  (iup:with-iup ()
    (let ((icon (iup-im:load-image (asdf:system-relative-pathname "iup" "examples/lispalien.ico"))))
      (setf (iup:handle "lispalien") icon))
    (let* ((label (iup:flat-label :image "lispalien" :expand :yes))
           (dialog (iup:dialog label :title "Icon from File"
                                     :icon "lispalien"
                                     :size "THIRDxTHIRD")))
      (iup:show dialog)
      (iup:main-loop))))
#-sbcl (icon)

#+sbcl
(sb-int:with-float-traps-masked
    (:divide-by-zero :invalid)
  (icon))

./docs/screenshots/icon-01.png ./docs/screenshots/icon-02.png

Drag and Drop

File Drag and Drop from Applications

;;; Generated from org-mode, do not edit

(eval-when (:compile-toplevel :load-toplevel :execute)
  (ql:quickload '("iup" "ironclad")))

(defpackage #:iup-examples.drophash
  (:use #:common-lisp)
  (:export #:drophash))

(in-package #:iup-examples.drophash)

This example demonstrates how to receive files via a drag and drop operation from another other application as well as sliding box layout and techniques for keeping the UI responsive during computationally intensive operations.

We’ll create a simple GUI wrapper for computing file digests using Ironclad, a cryptographic toolkit written in Common Lisp. The drop down lists all the digest algorithms Ironclad supports, and a label will be used to receive file drops. Once the digest is computed, they are appended to a result panel.

(defun drophash ()
  (iup:with-iup ()
    (let* ((list (iup:list :dropdown :yes
                           :expand :horizontal
                           :handlename "list"))
           (label (iup:flat-label :title "Drop files for hash"
                                  :alignment "ACENTER:ACENTER"
                                  :font "Helvetica, 24"
                                  :dropfilestarget :yes
                                  :dropfiles_cb 'drop-files-callback
                                  :expand :yes))
           (frame (iup:frame label))
           (results (iup:multi-line :expand :yes
                                    :readonly :yes
                                    :visiblelines 7
                                    :handlename "results"))
           (vbox (iup:vbox (list list
                                 frame
                                 (iup:sbox results :direction :north))
                           :margin "10x10"
                           :cgap 5))
           (dialog (iup:dialog vbox
                               :title "Drop Hash"
                               :size "HALFxHALF")))
      (loop for digest in (ironclad:list-all-digests)
            for i from 1
            do (setf (iup:attribute list i) digest)
            finally (setf (iup:attribute list :valuestring) 'ironclad:sha256))
      (iup:show dialog)
      (iup:main-loop))))

When files are dropped onto the drop target (in this case, an IUP flat label), the drop files callback is called. If multiple files are dropped at the same time, then the callback will be invoked for each file.

(defun drop-files-callback (handle filename num x y)
  (let* ((digest
          (intern (iup:attribute (iup:handle "list") :valuestring) "IRONCLAD"))
        (digest-hex 
          (ironclad:byte-array-to-hex-string 
           (ironclad:digest-file digest
            filename))))
    (setf (iup:attribute (iup:handle "results") :append)
          (format nil "~A     ~A" filename digest-hex)))
  (iup:flush)
  iup:+default+)

When multiple files are dropped, the callback will be invoked in rapid succession and the UI will seem unresponsive. This is why the example calls IUP:FLUSH after each file is processed. IUP:FLUSH will run any pending UI operations (such as the append to the results text box).

This helps, and indeed the results pane updates in real-time as files are processed, however the UI will become unresponsive again when the digest of large files are computed.

It is best not to do any computationally expensive operations in the UI thread. We’ll cover off-loading from the UI thread as well as revisit this example for better responsiveness later.

#-sbcl (drophash)

#+sbcl
(sb-int:with-float-traps-masked
    (:divide-by-zero :invalid)
  (drophash))

./docs/screenshots/drophash-01.png

./docs/screenshots/drophash-02.png

Examples

Checkout the examples directory for the examples in this document as well as these other examples.

LTK Demonstration Port

Includes example usage of IUP:TIMER for canvas animations.

./docs/screenshots/ltkdemo-01.png

./docs/screenshots/ltkdemo-02.png

Bindings Generation Internals

There are dozens of IUP controls and each control has dozens of callbacks and attributes. Fortunately IUP controls can be introspected to gain information on what the control is, what its callbacks and attributes are (and their arguments and types).

The iup-classesdb system uses this information to to automatically generate binding metadata from which the bindings are generated. This provides for a much nicer development experience:

./docs/screenshots/generation-01.png

The following sections describe how this works in more detail.

Maintainer

The maintainer is typically someone with access to the Git repository for these bindings. When a new release of IUP comes out, the maintainer needs to update the metadata so that any new or removed controls, attributes or callbacks are reflected in the Lisp bindings:

(*) --> "(asdf:load-system :iup-classesdb)" as Load
Load --> "(iup-classesdb:regenerate)" as Regen
Regen --> "classesdb.lisp-sexp" as Sexp
Sexp --> (*)

./docs/binding-maintainer.png

~classesdb.lisp-sexp~ is the output metadata. The maintainer typically commits this file to version control so the metadata is available for everyone.

User

The first time the user compiles the IUP bindings, classesdb.lisp-sexp is processed by macros at compile time and generates all function definitions for IUP controls. Note, that classesdb.lisp-sexpr is not actually needed when the user loads the system.

For the curious, the generation looks like the following, for each IUP system: IUP, IUP-CONTROLS, IUP-GL, IUP-GLCONTROLS, IUP-PLOT, IUP-MGLPLOT, IUP-OLECONTROL, IUP-SCINTILLA, IUP-WEB and IUP-TUIO.

(iup::defiupclasses "IUP")

The process is roughly:

  1. load each shared library
  2. introspect for the available IUP classes (i.e. metadata about controls) availabe
  3. For each class, generate the bindings in its own package.
(*) --> "(asdf:compile-system :iup)" as Load
"classesdb.lisp-sexp" as Sexpr --> Load
Load --> (*)
(*) --> "(asdf:load-system :iup)" as Load
Load --> (*)

./docs/binding-generation.png./docs/binding-generation-2.png

Why classesdb.lisp-sexp?

Extracting the metadata actually requires a complete GUI stack running. On Linux, this means having an X11 display available. This turns out to be a bit of a problem for continuous integration systems.

Although there are embedded X11 servers that can be used, I didn’t know what might be necessary for Windows or even macOS (when it’s supported) for CI/CD. Hence the classesdb.lisp-sexp is the maintainer’s job to regenerate when necessary.

Example IUP 3.25 to 3.26

Among other changes, IUP 3.26 introduced IupMultiBox as a new control container with 19 attributes and defaults. Regenerating classesdb.lisp-sexp automatically collected these changes so that the corresponding Lisp function IUP:MULTIBOX is created and exported automatically from the IUP package.

Interactive Development

TBD

iup's People

Contributors

defunkydrummer avatar lispnik avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

iup's Issues

[IUP question] No treeview widget with (sortable) columns?

Hello,

This is a question about IUP, not the bindings. I already asked on the iup-users mailing list but I'll hopefully get more ideas here…

My concern is: is there no widget to display columns? And columns that we can sort?

Like GTK's Treeview or ltk't Treeview (or an html table):

(https://python-gtk-3-tutorial.readthedocs.io/en/latest/_images/treeview_filter_example.png)

The answer I received is: "Take a look at the IupMatrix and IupMatrixEx controls."

However, while IupMatrixEx looks quite nice and advanced, it is a spreadsheet and not a table :S Users would be surprised with this.

IUP Matrix Test_028

My goal is to display tabular data: a book title, its author, its price, a cover image we can toggle,…

What does that inspire you all?

Thanks.

run main-loop on a thread

Would like to be able to interactively build a gui from the REPL like you can with CAPI. main-loop blocks until the last dialog is closed though. Main consideration is that UI operations should run on the UI thread, otherwise bad thing happen (just like in swing, and maybe others).

Idea sketch:

(iup:open) ;; initialize manual

(defvar *idle-queue* '())

(defun idle-callback () 
   ;; lock
   (dolist (func *idle-queue*) (funcall func)
   (setf *idle-queue* '()))

(with-gui-thread ()
   (setf (iup:attribute foo :title) "new title"))
;;; ...
(call-with-gui-thread (func)
   ;; lock idle queue 
   (push func *idle-queue*))

notes:

  • also need to support the original purpose of the idle callback

  • set global var lockloop (so last dialog doesn't exit main-loop)

  • make sure it's not a multithreaded shit show

  • document

  • check that the CPU usage of idle callback isn't too high (iup docs explain it's called more than you think).

  • should enable this kind of interactive coding (like in TCL wish):

CL-USER> (in-package #:iup-user)
IUP-USER> (start-gui-thread)
IUP-USER> (setq dialog (dialog nil))
IUP-USER> (setq button (button :title "hi"))
IUP-USER> (show dialog)
IUP-USER> (iup:append dialog button)
IUP-USER> (setf (attribute button :title )"new title")

suggestion: tecgraf-libs

(if (tecgraf-libs is working OK)
(Include on README that tecgraf-libs can be used to download the libs))

CCL: LTK-demo: The value #<IHANDLE "canvas" 32EE41F0> is not of the expected type

CCL64 bits v1.12, Windows 10

On loading ltk-demo.lisp:

The value #<IHANDLE "canvas" 32EE41F0> is not of the expected type (OR
                                                                    STRING
                                                                    CFFI-SYS:FOREIGN-POINTER).
   [Condition of type TYPE-ERROR]

Restarts:
 0: [USE-DEFAULT] Continue by returning IUP:+DEFAULT+
 1: [LOAD-SOURCE] Load "c:/Users/fegoavil010/Documents/instalados/lisp-portacle/portacle/projects/iup/examples/ltk-demo.lisp" instead of "c:/Users/fegoavil010/Documents/instalados/lisp-portacle/portacle/projects/iup/examples/ltk-demo.wx64fsl"
 2: [RECOMPILE] Compile "c:/Users/fegoavil010/Documents/instalados/lisp-portacle/portacle/projects/iup/examples/ltk-demo.lisp" into "c:/Users/fegoavil010/Documents/instalados/lisp-portacle/portacle/projects/iup/examples/ltk-demo.wx64fsl" then load "c:/Users/fegoavil010/Documents/instalados/lisp-portacle/portacle/projects/iup/examples/ltk-demo.wx64fsl" again
 3: [RETRY-LOAD] Retry loading #P"c:/Users/fegoavil010/Documents/instalados/lisp-portacle/portacle/projects/iup/examples/ltk-demo.wx64fsl"
 4: [SKIP-LOAD] Skip loading #P"c:/Users/fegoavil010/Documents/instalados/lisp-portacle/portacle/projects/iup/examples/ltk-demo.wx64fsl"
 5: [LOAD-OTHER] Load other file instead of #P"c:/Users/fegoavil010/Documents/instalados/lisp-portacle/portacle/projects/iup/examples/ltk-demo.wx64fsl"
 --more--

Backtrace:
  0: (CD:CREATE-CANVAS #<A Foreign Pointer #x7FFDD8757658> #<IHANDLE "canvas" 32EE41F0>)
  1: (CANVAS-MAP #<IHANDLE "canvas" 32EE41F0>)
  2: (IUP::INVOKE-CALLBACK CANVAS-MAP (#<IHANDLE "canvas" 32EE41F0>) (#<IHANDLE "canvas" 32EE41F0>))
  3: (CFFI-CALLBACKS::|KEYWORD::CANVAS-MAP_CB| 109428248)
  4: (CCL::%PASCAL-FUNCTIONS% 104 109428248)
  5: (NIL #<Unknown Arguments>)
  6: (IUP-CFFI::%IUP-SHOW-XY #<IHANDLE "dialog" 32EE4E70> 65535 65535)
  7: ((:INTERNAL LTK-DEMO))
  8: (IUP::CALL-WITH-IUP #<Compiled-function (:INTERNAL LTK-DEMO) (Non-Global)  #x21035136AF>)
  9: (LTK-DEMO)
      [No Locals]
 10: (#<Anonymous Function #x21035129EF>)
 11: (CCL::$FASL-LFUNCALL #<CCL::FASLSTATE #x2A710F9D>)
 12: (CCL::%FASLOAD "c:/Users/fegoavil010/Documents/instalados/lisp-portacle/portacle/projects/iup/examples/ltk-demo.wx64fsl" #(#<Compiled-function CCL::$FASL-NOOP (Non-Global)  #x10012135F> ..)))
 13: (CCL::%LOAD #P"c:/Users/fegoavil010/Documents/instalados/lisp-portacle/portacle/projects/iup/examples/ltk-demo.wx64fsl" NIL NIL :ERROR :DEFAULT NIL)
 14: (LOAD #P"c:/Users/fegoavil010/Documents/instalados/lisp-portacle/portacle/projects/iup/examples/ltk-demo.wx64fsl" :VERBOSE NIL :PRINT NIL :IF-DOES-NOT-EXIST :ERROR :EXTERNAL-FORMAT :DEFAULT :PRESERVE-..
 --more--

not sure how to load IUP now

Loading IUP now fails because System "tecgraf-base" not found.

We need, in the documentation, a clear way to show how to load IUP, which LISPNIK projects one needs to fetch, etc. I didn't know anything about "tecgraf-base" until now; my IUP system used to load just fine.

For IUP to succeed, it must be easy to load in the first place!

BTW, There are also so many ASD files, one can be baffled on how to proceed.

SBCL explodes after loading IUP

Hello Lispnik, i'm in Windows x64 under SBCL 1.3.10 and get the following:

Session:

CL-USER> (ql:quickload
          "tecgraf-libs")
To load "tecgraf-libs":
  Load 1 ASDF system:
    tecgraf-libs
; Loading "tecgraf-libs"
[package tecgraf-libs].
Downloading https://sourceforge.net/projects/iup/files/3.26/Windows%20Libraries/Dynamic/iup-3.26_Win64_dll15_lib.zip...
....
Downloading https://sourceforge.net/projects/canvasdraw/files/5.12/Windows%20Libraries/Dynamic/cd-5.12_Win64_dll15_lib.zip...
Downloading https://sourceforge.net/projects/imtoolkit/files/3.13/Windows%20Libraries/Dynamic/im-3.13_Win64_dll15_lib.zip...
.
Unpacked to #P"c:/Users/fegoavil010/Documents/instalados/lisp-portacle/portacle/projects/tecgraf-libs/libs/"

("tecgraf-libs")
CL-USER> cffi:*foreign-library-directories*
NIL
CL-USER> (push #P"c:/Users/fegoavil010/Documents/instalados/lisp-portacle/portacle/projects/tecgraf-libs/libs/" cffi:*foreign-library-directories*)
(#P"c:/Users/fegoavil010/Documents/instalados/lisp-portacle/portacle/projects/tecgraf-libs/libs/")
CL-USER> (ql:quickload "iup")
To load "iup":
  Load 1 ASDF system:
    iup
; Loading "iup"
.To load "genhash":
  Load 1 ASDF system:
    asdf
  Install 1 Quicklisp release:
    genhash
; Fetching #<URL "http://beta.quicklisp.org/archive/genhash/2018-12-10/genhash-20181210-git.tgz">
; 4.11KB
==================================================
4,210 bytes in 0.04 seconds (108.19KB/sec)
; Loading "genhash"
[package net.hexapodia.hashtables]....
; Loading "iup"
To load "trivial-arguments":
  Load 1 ASDF system:
    asdf
  Install 1 Quicklisp release:
    trivial-arguments
; Fetching #<URL "http://beta.quicklisp.org/archive/trivial-arguments/2018-08-31/trivial-arguments-20180831-git.tgz">
; 5.88KB
==================================================
6,024 bytes in 0.03 seconds (217.88KB/sec)
; Loading "trivial-arguments"
[package trivial-arguments]
; Loading "iup"
[package iup-cffi]................................
[package iup-utils]...............................
[package iup].....................................
..................................................
..................................................
..................

inferior lisp says:

Heap exhausted during garbage collection: 160 bytes available, 528 requested.
 Gen StaPg UbSta LaSta LUbSt Boxed Unboxed LB   LUB  !move  Alloc  Waste   Trig    WP  GCs Mem-age
   0:     0     0     0     0     0     0     0     0     0        0     0 10737418    0   0  0.0000
   1:     0     0     0     0     0     0     0     0     0        0     0 10737418    0   0  0.0000
   2: 30001 30000     0     0 18148  4445    27    18   170 736677312 5124672 375740794    0   1  1.2492
   3: 32767 32752     0     0  7469  1092    98   134    14 284015296 4113728  2000000    0   0  0.0000
   4:     0     0     0     0     0     0     0     0     0        0     0  2000000    0   0  0.0000
   5:     0     0     0     0     0     0     0     0     0        0     0  2000000    0   0  0.0000
   6:     0     0     0     0  1109   210     0     0     0 43220992     0  2000000  930   0  0.0000
   Total bytes allocated    = 1063913600
   Dynamic-space-size bytes = 1073741824
GC control variables:
   *GC-INHIBIT* = true
   *GC-PENDING* = true
   *STOP-FOR-GC-PENDING* = false
fatal error encountered in SBCL pid 18748(tid 74267088):
Heap exhausted, game over.

The system is too badly corrupted or confused to continue at the Lisp
level. If the system had been compiled with the SB-LDB feature, we'd drop
into the LDB low-level debugger now. But there's no LDB in this build, so
we can't really do anything but just exit, sorry.

Process inferior-lisp exited abnormally with code 1

All your base are belong to us. There's no chance to escape make your time.

images

In iup.lisp, the code is commented out for image-rgb, image-rgba, etc. Uncomment, test and doc example.

SBCL needs to trap division by zero errors

It's all documented since you do document to run with

#-sbcl (hello)

#+sbcl
(sb-int:with-float-traps-masked
    (:divide-by-zero :invalid)
  (callback))

but this is not ideal, hence the issue.
Do you know the reason ?

setup CI/CD for Windows

Use appveyor: should get the tecgraf-libs, im, cd, iup, pffft and ensure that quickload works for all the iup modules (iup-cd, iup-im, iup-gl, etc)

Cut down on library deps

Wow I installed it from scratch and there's a ton of stuff it depends on. Go through the deps for the bindings and check if all that stuff is necessary.

wrap the global attributes

Most of them return a string, some return a pointer (e.g. to a win32 hinstance, or xdisplay), a lot of them are true/false values (maybe nice to return t/nil for those)

A workflow that leads to a memory fault: re-defining a missing callback

It's been twice or more that I get an unhandled memory fault with the following workflow.

I quickload iup, iup-scintilla, I define the dialogs function and I run it (with the sb-int division by zero trap on sbcl) . I select "message-dialog", which doesn't exist yet, so I get a nice error. Now I copy-paste it (style warning: handle is unused), I re-run (dialogs) but I get a memory error:

Unhandled memory fault at #x0.
   [Condition of type SB-SYS:MEMORY-FAULT-ERROR]

Restarts:
 0: [RETRY] Retry SLIME REPL evaluation request.
 1: [*ABORT] Return to SLIME's top level.
 2: [REMOVE-FD-HANDLER] Remove #<SB-IMPL::HANDLER INPUT on descriptor 4: #<CLOSURE (LABELS SWANK/SBCL::RUN :IN SWANK/BACKEND:ADD-FD-HANDLER) {1004BF542B}>>
 3: [ABORT] Exit debugger, returning to top level.

Backtrace:
  0: (SB-SYS:MEMORY-FAULT-ERROR #<unused argument> #.(SB-SYS:INT-SAP #X00000000))
  1: ("foreign function: call_into_lisp")
  2: ("foreign function: funcall2")
  3: ("foreign function: handle_trap")
  4: ("foreign function: #x41AFC0")
  5: (IUP:OPEN)
  6: (IUP::CALL-WITH-IUP #<FUNCTION (LAMBDA NIL :IN DIALOGS) {1001B881EB}>)
  7: ((LAMBDA ()))
  8: (SB-INT:SIMPLE-EVAL-IN-LEXENV (SB-INT:WITH-FLOAT-TRAPS-MASKED (:DIVIDE-BY-ZERO :INVALID) (DIALOGS)) #<NULL-LEXENV>)
  9: (EVAL (SB-INT:WITH-FLOAT-TRAPS-MASKED (:DIVIDE-BY-ZERO :INVALID) (DIALOGS)))
 --more--

and I must restart SBCL.
Doing it on the right order works fine.

SBCL 1.4.5 Debian.

IUP:IMAGE duplicated function

The IupImage control is discovered by the bindings generator, but it's defined as taking three arguments (width, height, pixels). IUP:IMAGE is created first with zero args but with all the keywords for attributes and then it's redefined later on as IUP:IMAGE with width height pixels arguments.

IupImage needs some special treatment therefore.

tecgraf-libs now requires SSL libraries

I see tecgraf-libs has changed and now requires chipz which in turn asks me for having the SSL DLLs installed.

This makes the installation far more cumbersome.

Is this really necessary?

Where I can find the documentation?

Hello!
II have been looking for a GUI library for Lisp for a long time. Found IUP. There is a tutorial for it, but did not find a consistent description of all the functions. Even in some over language. Could you help my? Where can I find documentation?
Thanks!

Can't load iup-controls from SBCL

Hello! I really appreciate your work and really like the bindings so far. But I have a problem:

I use Windows and SBCL 2.3.1, and the IUP bindings work well but when trying to load iup-controls SBCL gives an error:

* (ql:quickload :iup-controls)
To load "iup-controls":
  Load 1 ASDF system:
    iup-controls
; Loading "iup-controls"
[package iup-controls]INFO: Caught stack overflow exception (sp=0x0000000000411fa0); proceed with caution.

debugger invoked on a SB-KERNEL::CONTROL-STACK-EXHAUSTED in thread
#<THREAD "main thread" RUNNING {1007868113}>:
Control stack exhausted (no more space for function call frames).
This is probably due to heavily nested or infinitely recursive function
calls, or a tail call that SBCL cannot or has not optimized away.

PROCEED WITH CAUTION.

Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.

restarts (invokable by number or by possibly-abbreviated name):
  0: [RETRY                        ] Retry
                                     compiling #<CL-SOURCE-FILE "iup-controls" "controls">.
  1: [ACCEPT                       ] Continue, treating
                                     compiling #<CL-SOURCE-FILE "iup-controls" "controls">
                                     as having been successful.
  2:                                 Retry ASDF operation.
  3: [CLEAR-CONFIGURATION-AND-RETRY] Retry ASDF operation after resetting the
                                     configuration.
  4:                                 Retry ASDF operation.
  5:                                 Retry ASDF operation after resetting the
                                     configuration.
  6: [ABORT                        ] Give up on "iup-controls"
  7: [REGISTER-LOCAL-PROJECTS      ] Register local projects and try again.
  8:                                 Exit debugger, returning to top level.

(SB-INT:HASHSET-FIND #<SB-IMPL::ROBINHOOD-HASHSET SB-INT:LIST-ELTS-EQ 1595/8192 keys, psl=8 {1000133483}> (#<SB-KERNEL:NAMED-TYPE T>))

Note that iup-controls works on CCL, and loading any IUP package (in SBCL) works e.g. iup-gl, iup-imglib, iup-scintilla.

Also note that I tried different --dynamic-space-size arguments ranging from 2048 to 8192 and I tried SBCL version 2.3.0 and 2.3.2 but to no avail.

Thanks for reading!

IUP:CALLBACK not creating right key for *REGISTERED-CALLBACKS* lookups

(with-iup ()
  (let* ((button (button :title "Interesting" :action 'foo))
	 (callback (callback button :action)))
    callback))

Should return 'FOO, but doesn't because key used to lookup doesn't match.

It's a bit of a bummer, this one, because the key includes the package name of the control (e.g. IUP::TREE-MAP_CB and IUP::CONTROLS::CELLS-MOUSECLICK_CB), but there's no non-hacky way to get that package information from the control.

Can't run iup from SLIME REPL

Hi! I'm using Windows and SBCL and I have a problem when executing functions that have iup code in them.

I get different results when running the hello world example from the README from different environments:

  1. When running SBCL from the terminal and running the example it works and shows me a window
  2. When running from emacs:
    a. running the function using C-x C-e works and shows a window
    b. but when running from the slime REPL it just blocks the REPL and never shows a window

Mind you I tried every combination of sly/slime (even emacs' run-lisp) and SBCL/CCL. Thanks!

creating an executable

Mostly not a feature of this library, but should be documented:

  • asdf:program-op
  • determining which IUP dlls should be also distributed

questions

  • windows installer op?
  • what to do with so paths on Linux: patchelf + set origin root? create a .deb/.rpm?

IupScintilla attributes ending in numbers missing

I've been really enjoying using this library, and started working on a text editor component of my software. Here I ran into a bit of an issue:

Pretty much the title. Certain IUP attributes don't exist as lisp keywords in the definition of (iup-scintilla:scintilla), whereas keywords like :lexerlanguage or :value do exist.

For instance, "KEYWORDS0" sets a list of keywords for Scintilla to highlight, but there is no :keywords0 in (iup-scintilla:scintilla). And attempting something like (setf (iup:attribute *editor* :keywords0) "defun defclass") silently does nothing, as well as trying it as "KEYWORDS0".

This seems to be the case for any iup (scintilla) attributes that end with a number, such as "KEYWORDS0", "STYLEFONT32", "STYLEFGCOLOR5", etc.

IupScintilla documentation: https://www.tecgraf.puc-rio.br/iup/en/ctrl/iup_scintilla.html

CCL: [windows] iup-controls fails loading the DLL

I'm on Windows x64 because Bill Gates loves us. CCL 64-bits.

Steps:

  1. I have the tecgraf libs, were loaded using your "tecgraf-libs" system.

  2. CFFI foreign lib path is set correctly.

  3. I have iupcontrols.dll there.

  4. simple IUP example "buttons.lisp" works OK

  5. However, when trying to load iup-controls (system), i get:

"the specified module could not be found"

Unable to load foreign library (IUP-CONTROLS).
  Error opening shared library c:/Users/fegoavil010/Documents/instalados/lisp-portacle/portacle/projects/tecgraf-libs/libs/iupcontrols.dll : The specified module could not be found. .
   [Condition of type CFFI:LOAD-FOREIGN-LIBRARY-ERROR]

Restarts:
 0: [RETRY] Try loading the foreign library again.
 1: [USE-VALUE] Use another library instead.
 2: [LOAD-SOURCE] Load "home:Documents;instalados;lisp-portacle;portacle;projects;iup-master;controls;controls-cffi.lisp" instead of "C:/Users/fegoavil010/AppData/Local/cache/common-lisp/ccl-1.11-f96-win-x64/C/Users/fegoavil010/Documents/instalados/lisp-portacle/portacle/projects/iup-master/controls/controls-cffi.wx64fsl"
 3: [RECOMPILE] Compile "home:Documents;instalados;lisp-portacle;portacle;projects;iup-master;controls;controls-cffi.lisp" into "C:/Users/fegoavil010/AppData/Local/cache/common-lisp/ccl-1.11-f96-win-x64/C/Users/fegoavil010/Documents/instalados/lisp-portacle/portacle/projects/iup-master/controls/controls-cffi.wx64fsl" then load "C:/Users/fegoavil010/AppData/Local/cache/common-lisp/ccl-1.11-f96-win-x64/C/Users/fegoavil010/Documents/instalados/lisp-portacle/portacle/projects/iup-master/controls/controls-cffi.wx64fsl" again
 4: [RETRY-LOAD] Retry loading #P"C:/Users/fegoavil010/AppData/Local/cache/common-lisp/ccl-1.11-f96-win-x64/C/Users/fegoavil010/Documents/instalados/lisp-portacle/portacle/projects/iup-master/controls/controls-cffi.wx64fsl"
 5: [SKIP-LOAD] Skip loading #P"C:/Users/fegoavil010/AppData/Local/cache/common-lisp/ccl-1.11-f96-win-x64/C/Users/fegoavil010/Documents/instalados/lisp-portacle/portacle/projects/iup-master/controls/controls-cffi.wx64fsl"
 --more--

Backtrace:
  0: (CFFI::FL-ERROR "Unable to load foreign library (~A).~%  ~A" IUP-CONTROLS-CFFI::IUP-CONTROLS "Error opening shared library c:/Users/fegoavil010/Documents/instalados/lisp-portacle/portacle/projects/tec..
  1: (CFFI::REPORT-SIMPLE-ERROR IUP-CONTROLS-CFFI::IUP-CONTROLS #<SIMPLE-ERROR #x21029E987D>)
  2: (CFFI::LOAD-FOREIGN-LIBRARY-PATH IUP-CONTROLS-CFFI::IUP-CONTROLS "iupcontrols.dll" NIL)
  3: (CFFI::LOAD-FOREIGN-LIBRARY-HELPER IUP-CONTROLS-CFFI::IUP-CONTROLS "iupcontrols.dll" NIL)
  4: ((:INTERNAL CFFI::%DO-LOAD CFFI::%DO-LOAD-FOREIGN-LIBRARY) #<FOREIGN-LIBRARY IUP-CONTROLS> IUP-CONTROLS-CFFI::IUP-CONTROLS "iupcontrols.dll")
  5: (CFFI::%DO-LOAD-FOREIGN-LIBRARY IUP-CONTROLS-CFFI::IUP-CONTROLS NIL)
  6: (CFFI:LOAD-FOREIGN-LIBRARY IUP-CONTROLS-CFFI::IUP-CONTROLS :SEARCH-PATH NIL)
  7: (#<Anonymous Function #x21029E9F7F>)
  8: (CCL::$FASL-LFUNCALL #<CCL::FASLSTATE #x2727F71D>)
  9: (CCL::%FASLOAD "C:/Users/fegoavil010/AppData/Local/cache/common-lisp/ccl-1.11-f96-win-x64/C/Users/fegoavil010/Documents/instalados/lisp-portacle/portacle/projects/iup-master/controls/controls-cffi.wx6..
 10: (CCL::%LOAD #P"C:/Users/fegoavil010/AppData/Local/cache/common-lisp/ccl-1.11-f96-win-x64/C/Users/fegoavil010/Documents/instalados/lisp-portacle/portacle/projects/iup-master/controls/controls-cffi.wx64..
 11: (LOAD #P"C:/Users/fegoavil010/AppData/Local/cache/common-lisp/ccl-1.11-f96-win-x64/C/Users/fegoavil010/Documents/instalados/lisp-portacle/portacle/projects/iup-master/controls/controls-cffi.wx64fsl" :..
 12: (UIOP/UTILITY:CALL-WITH-MUFFLED-CONDITIONS #<COMPILED-LEXICAL-CLOSURE (:INTERNAL UIOP/LISP-BUILD:LOAD*) #x21029EAFFF> ("Overwriting already existing readtable ~S." ..))
 13: (CCL::%%BEFORE-AND-AFTER-COMBINED-METHOD-DCODE ((NIL) #<STANDARD-METHOD ASDF/ACTION:PERFORM (ASDF/LISP-ACTION:LOAD-OP ASDF/LISP-ACTION:CL-SOURCE-FILE)> #<LOAD-OP> ..))
 14: (CCL::%CALL-NEXT-METHOD ((NIL) #<STANDARD-METHOD ASDF/ACTION:PERFORM (ASDF/LISP-ACTION:LOAD-OP ASDF/LISP-ACTION:CL-SOURCE-FILE)> #<LOAD-OP> #<CL-SOURCE-FILE "iup-controls-cffi" "controls-cffi">))
 15: ((:INTERNAL ASDF/ACTION:CALL-WHILE-VISITING-ACTION))
 16: (CCL::%%STANDARD-COMBINED-METHOD-DCODE (#<STANDARD-METHOD ASDF/ACTION:PERFORM :AROUND (ASDF/OPERATION:OPERATION ASDF/COMPONENT:COMPONENT)> (#<STANDARD-METHOD ASDF/ACTION:PERFORM :BEFORE #>) ..)) 81768..
 17: (NIL #<Unknown Arguments>)
 18: (CCL::%CALL-NEXT-METHOD (NIL #<STANDARD-METHOD ASDF/ACTION:PERFORM-WITH-RESTARTS (T T)> . 81768756))
 19: (#<STANDARD-METHOD ASDF/ACTION:PERFORM-WITH-RESTARTS (ASDF/LISP-ACTION:LOAD-OP ASDF/LISP-ACTION:CL-SOURCE-FILE)> #<LOAD-OP> #<CL-SOURCE-FILE "iup-controls-cffi" "controls-cffi">)
 --more--

fix OpenGL example's idle callback.

Thank you for great Lisp GUI toolkit which can use OpenGL.

I make it animated by fixing idle callback at the OpenGL example of README.

      ;; FIXME      (iup-cffi::%iup-set-function :idle_action 'idle)
      ;; into
      (iup-cffi::%iup-set-function :idle_action (cffi:callback idle-cb))
;;; FIXME
;; (cffi:defcallback idle-cb :int ()
;; ...
;; into
(cffi:defcallback idle-cb :int ()
  (incf *tt*)
  (iup-gl:make-current *canvas*)
  (repaint *canvas* 0 0) ;; FIXME: true posx posy. or simply, call canvas action.
  iup::+default+)

referenced : iup-examples/examples/C/gl/opengl3D.c at master · LuaDist/iup-examples

Sometimes we can't cancel (C-c C-c) the IUP loop

I am exploring these bindings, and as a beginner I'll make errors. One of them is to call the main-loop at the wrong place, which leaves me to a state where I loose control over Slime. I can't C-c C-c the running IUP process:

(defun bookslist ()
  (iup:with-iup ()
    (let* ((frame (iup:frame
                   (iup:vbox (loop for list in (list (iup:list :value 1 :tip "List 1" :multiple :yes)
                                                     (iup:list :value 2 :tip "list 2" :dropdown :yes)
                                                     (iup:list :value 3 :tip "List 3" :editbox :yes))
                                do (loop for i from 1 upto 3
                                      do (setf (iup:attribute list i)
                                               (format nil "Item ~A" i)))
                                collect list))
                   :title "IUP List"))
           (dialog (iup:dialog frame :menu "menu" :title "a title")))
      (iup:map dialog)
      (iup:show dialog)
      ;; (iup:main-loop)
      )))

(defun main ()
  #-sbcl (hello)
  #+sbcl
  (sb-int:with-float-traps-masked
      (:divide-by-zero :invalid)
    (bookslist)
    (iup:main-loop)))

Leaves me in:

IUP> (main)

[no C-c C-c here]

I only escape from there by restarting the lisp process :/

SBCL 1.4.5

Iup-im depends on im and im-cffi, which don't seem to exist

When trying to load iup-im with:

(ql:quickload '(:iup iup-im))

Quicklisp fails with error "System im was not found". If I comment out the dependency, then im-iup-cffi will similarly fail to find dependency "im-cffi". It seems only to require this dependency for im-cffi::im-image. I have been unable to find references to either of these systems either in this repository, or any of your related repositories.

not happy with plot bindings

(imported from TODO)

plot

  • Look at with-plot - change to with-datasource?
  • also add the ds_* keywords as part of the with-datasource?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.