{"id":13442685,"url":"https://github.com/lispnik/iup","last_synced_at":"2026-01-29T19:01:16.933Z","repository":{"id":138193306,"uuid":"169189967","full_name":"lispnik/iup","owner":"lispnik","description":"Common Lisp CFFI bindings to the IUP Portable User Interface library (pre-ALPHA)","archived":false,"fork":false,"pushed_at":"2025-02-03T03:39:00.000Z","size":9299,"stargazers_count":155,"open_issues_count":14,"forks_count":7,"subscribers_count":10,"default_branch":"master","last_synced_at":"2025-12-09T13:51:12.569Z","etag":null,"topics":["cffi","common-lisp","gui","iup"],"latest_commit_sha":null,"homepage":"","language":"Common Lisp","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lispnik.png","metadata":{"files":{"readme":"README.org","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2019-02-05T04:22:54.000Z","updated_at":"2025-10-20T14:58:31.000Z","dependencies_parsed_at":"2024-01-27T08:41:49.590Z","dependency_job_id":"4163a5b9-3121-4d21-a419-64b138bba06b","html_url":"https://github.com/lispnik/iup","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/lispnik/iup","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lispnik%2Fiup","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lispnik%2Fiup/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lispnik%2Fiup/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lispnik%2Fiup/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lispnik","download_url":"https://codeload.github.com/lispnik/iup/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lispnik%2Fiup/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28882609,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-29T16:41:59.663Z","status":"ssl_error","status_checked_at":"2026-01-29T16:39:39.641Z","response_time":59,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cffi","common-lisp","gui","iup"],"created_at":"2024-07-31T03:01:49.132Z","updated_at":"2026-01-29T19:01:16.902Z","avatar_url":"https://github.com/lispnik.png","language":"Common Lisp","funding_links":[],"categories":["Common Lisp"],"sub_categories":[],"readme":"#+STARTUP: showall\n#+TITLE: Common Lisp CFFI bindings to the IUP Portable User Interface library\n\n[[./docs/screenshots/sample-01.png]]\n\n[[./docs/screenshots/sample-02.png]]\n\n* Compatibility\n\nIUP 3.29\n\n* Introduction\n\n* Shared Libraries\n\nGet the Tecgraf shared libraries (.so and .dll files) from the Tecgraf\nproject pages and install them per your operating system. Usually this\nmeans setting ~LD_LIBRARY_PATH~ on Linux or ~PATH~ on Windows.\n\n- [[https://www.tecgraf.puc-rio.br/iup/]]\n- [[https://www.tecgraf.puc-rio.br/cd/]]\n- [[https://www.tecgraf.puc-rio.br/im/]]\n\nThere is, however, a [[https://github.com/lispnik/tecgraf-libs][companion project]] which can be used to download\nand install the Tecgraf IUP, CD and IM shared libraries for x86/64-bit\nLinux and Windows automatically:\n\n#+begin_src lisp :results silent \n  (ql:quickload \"tecgraf-libs\")\n#+end_src\n\nThis downloads the archives from each of the project pages according\nto the platform your lisp is running on (Linux or Windows). It unpacks\nthe archives, and copies all of the shared libraries to a single\ndirectory.\n\nIt is a one-off step is is not needed subsequently.\n\n*NOTE:* On Linux, the ~tecgraf-libs~ system needs the ~patchelf~\ncommand available (usually available via ~sudo apt install\npatchelf~). ~patchelf~ is used to set the shared library .so files\norigin to the library's location so that the multiple shared libraries\nloaded via CFFI uses are found relative to each other.\n\n*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:\n\n#+begin_src\nSET PATH=C:\\mylisp\\projects\\tecgraf-libs\\libs;\nwx86cl64.exe\n#+end_src\n\nTo tell CFFI to find these libraries, use:\n\n#+begin_src lisp\n  (ql:quickload \"cffi\")\n\n  (pushnew (asdf:system-relative-pathname :tecgraf-libs #p\"libs/\")\n           cffi:*foreign-library-directories*)\n#+end_src\n\n#+RESULTS:\n: (#P\"/home/mkennedy/.roswell/local-projects/lispnik/tecgraf-libs/libs/\")\n\n* Loading IUP\n\n(First, read the previous part on getting the Tecgraf libraries and installing).\n\nRequirements:\n\nDownload/clone the following systems:\n\n- lispnik/`tecgraf-base`\n\nThose are required by Iup. \n\nThen load iup.asd in the usual way (e.g. `(ql:quickload \"iup\")`\n\n*NOTE:* For SBCL, you need to set a larger heap size to compile the\nbindings, e.g. ~--dynamic-space-size 2048~\n\n* Running GUI Applications on Windows using SLIME or SLY\n\nOn Windows, it is necessary to start your Lisp indrectly via ~CMD.EXE~, e.g. \nfor SLY, in your Emacs configuration, include something like:\n\n#+begin_src lisp :results silent :export none\n  (setq sly-lisp-implementations\n           '((ccl (\"cmd\" \"/c\" \"wx86cl64\"))\n             (sbcl (\"cmd\" \"/c\" \"sbcl.exe\" \"--dynamic-space-size\" \"2048\"))))\n#+end_src\n\nSimilarly use ~slime-lisp-implementations~ if your are using SLIME.\n\nThis is not specific to IUP or the bindings. It also works for running other \ngraphical application libraries like CL-SDL2.\n\n* Hello, World!\n\n#+begin_src lisp :results silent :export none :tangle examples/hello.lisp\n  ;;; Generated from org-mode, do not edit\n\n  (eval-when (:compile-toplevel :load-toplevel :execute)\n    (ql:quickload \"iup\"))\n\n  (defpackage #:iup-examples.hello\n    (:use #:common-lisp)\n    (:export #:hello))\n\n  (in-package #:iup-examples.hello)\n#+end_src\n\n#+begin_src lisp :results silent :tangle examples/hello.lisp\n  (defun hello ()\n    (iup:with-iup ()\n      (let* ((label (iup:label :title (format nil \"Hello, World!~%IUP ~A~%~A ~A\"\n                                              (iup:version)\n                                              (lisp-implementation-type)\n                                              (lisp-implementation-version))))\n             (dialog (iup:dialog label :title \"Hello, World!\")))\n        (iup:show dialog)\n        (iup:main-loop))))\n#+end_src\n\n#+begin_src lisp :results silent :tangle examples/hello.lisp\n  #-sbcl (hello)\n\n  ,#+sbcl\n  (sb-int:with-float-traps-masked\n      (:divide-by-zero :invalid)\n    (hello))\n#+end_src\n\n[[./docs/screenshots/helloworld.png]] [[./docs/screenshots/helloworld-2.png]]\n\n* Callbacks\n\n** Simple Example\n\n#+begin_src lisp :results silent :export none :tangle examples/callback.lisp\n  ;;; Generated from org-mode, do not edit\n\n  (eval-when (:compile-toplevel :load-toplevel :execute)\n    (ql:quickload \"iup\"))\n\n  (defpackage #:iup-examples.callback\n    (:use #:common-lisp)\n    (:export #:callback))\n\n  (in-package #:iup-examples.callback)\n#+end_src\n\n#+begin_src lisp :results silent :tangle examples/callback.lisp\n  (defun callback ()\n    (iup:with-iup ()\n      (let* ((button1\n               (iup:button :title \"Test \u00261\"\n                           :expand :yes\n                           :tip \"Callback inline at control creation\"\n                           :action (lambda (handle)\n                                     (message \"button1's action callback\")\n                                     iup:+default+)))\n             (button2\n               (iup:button :title \"Test \u00262\"\n                           :expand :yes\n                           :tip \"Callback set later using (SETF (IUP:CALLBACK ..) ..)\"))\n             (button3\n               (iup:button :title \"Test \u00263\"\n                           :expand :yes\n                           :tip \"Callback example using symbol-referenced function at control creation\"\n                           :action 'test3-callback))\n             (button4\n               (iup:button :title \"Test \u00264\"\n                           :expand :yes\n                           :tip \"Callback example using symbol-referenced function later using (SETF (IUP:CALLBACK ..) ..)\"))\n             (vbox\n               (iup:vbox (list button1 button2 button3 button4)\n                         :gap \"10\"\n                         :margin \"10x10\"\n                         :alignment :acenter))\n             (dialog\n               (iup:dialog vbox :title \"Callback Example\")))\n        (setf (iup:callback button2 :action)\n              (lambda (handle)\n                (message \"button2's action callback\")\n                iup:+default+))\n        (setf (iup:callback button4 :action) 'test4-callback)\n        (iup:show dialog)\n        (iup:main-loop))))\n\n  (defun test3-callback (handle)\n    (message \"button3's action callback\")\n    iup:+default+)\n\n  (defun test4-callback (handle)\n    (message \"button4's action callback\")\n    iup:+default+)\n\n  (defun message (message)\n    (iup:message \"Callback Example\" message))\n#+end_src\n\n#+begin_src lisp :results silent :tangle examples/callback.lisp\n  #-sbcl (callback)\n\n  ,#+sbcl\n  (sb-int:with-float-traps-masked\n      (:divide-by-zero :invalid)\n    (callback))\n#+end_src\n\n[[./docs/screenshots/callback-1.png]] [[./docs/screenshots/callback-2.png]]\n\n[[./docs/screenshots/callback-3.png]] [[./docs/screenshots/callback-4.png]]\n\n** Color Mixer\n\nConsider capturing state by creating a closure over the controls that\nmake up state:\n\n#+begin_src lisp :results silent :export none :tangle examples/mixer.lisp\n  ;;; Generated from org-mode, do not edit\n\n  (eval-when (:compile-toplevel :load-toplevel :execute)\n    (ql:quickload \"iup\"))\n\n  (defpackage #:iup-examples.mixer\n    (:use #:common-lisp)\n    (:export #:mixer))\n\n  (in-package #:iup-examples.mixer)\n\n  (defun make-mixer-action (r g b button label)\n    (lambda (handle)\n      (declare (ignore handle))\n      (let ((color (format nil \"~A ~A ~A\"\n                           (floor (iup:attribute r :value 'number))\n                           (floor (iup:attribute g :value 'number))\n                           (floor (iup:attribute b :value 'number)))))\n        (setf (iup:attribute button :fgcolor) color\n              (iup:attribute label :title) color)\n        (iup:refresh button))\n      iup:+default+))\n\n  (defun mixer ()\n    (iup:with-iup ()\n      (let* ((button (iup:flat-button :expand :yes :canfocus :no))\n             (label (iup:label :expand :horizontal :title \"#x00000\" :alignment \"ACENTER:ACENTER\"))\n             (r (iup:val :expand :horizontal :min 0 :max 255))\n             (g (iup:val :expand :horizontal :min 0 :max 255))\n             (b (iup:val :expand :horizontal :min 0 :max 255))\n             (vbox (iup:vbox\n                    (list (iup:grid-box\n                           (list (iup:label :title \"\u0026Red\")   r\n                                 (iup:label :title \"\u0026Green\") g\n                                 (iup:label :title \"\u0026Blue\")  b)\n                           :numdiv 2\n                           :cgapcol 10\n                           :cgaplin 5)\n                          button\n                          label)\n                    :cmargin 5\n                    :cgap 5\n                    :margin \"x5\"))\n             (dialog (iup:dialog vbox :title \"Color Mixer Example\" :size \"QUARTERxQUARTER\")))\n        (loop :with action := (make-mixer-action r g b button label)\n              :for handle :in (list r g b)\n              :do (setf (iup:callback handle :valuechanged_cb)  action))\n        (iup:show dialog)\n        (iup:main-loop))))\n\n  #-sbcl (mixer)\n\n  ,#+sbcl\n  (sb-int:with-float-traps-masked\n      (:divide-by-zero :invalid)\n    (mixer))\n#+end_src\n\n[[./docs/screenshots/mixer-01.png]]\n\n[[./docs/screenshots/mixer-02.png]]\n\n* Idle Action\n\n#+begin_src lisp :results silent :export none :tangle examples/idle.lisp\n  ;;; Generated from org-mode, do not edit\n\n  (eval-when (:compile-toplevel :load-toplevel :execute)\n    (ql:quickload \"iup\"))\n\n  (defpackage #:iup-examples.idle\n    (:use #:common-lisp)\n    (:export #:idle))\n\n  (in-package #:iup-examples.idle)\n#+end_src\n\nThere is a global callback for running functions when IUP event loop\nis idle. See also [[https://webserver2.tecgraf.puc-rio.br/iup/en/call/iup_idle_action.html][IUP: Idle Action]]. In Lisp, it can be set using\n~(SETF IUP:IDLE-ACTION)~. Note: Idle actions run a lot more often than\nyou'd expect. Try the following example application to get an idea for\njust how often.\n\nAn idle action function should return ~IUP:+DEFAULT+~, ~IUP:+CLOSE+~\nor ~IUP:+IGNORE+~. ~IUP:+DEFAULT~ will cause the idle action function\nto be called repeatedly. ~IUP:+IGNORE+~ will run idle action\nonce. ~IUP:+CLOSE+~ will exit the event loop.\n\n#+begin_src lisp :results silent :tangle examples/idle.lisp\n  (defun idle ()\n    (iup:with-iup ()\n      (let* ((counter (iup:label :fontsize 24\n                                 :title 0\n                                 :expand :yes\n                                 :alignment :acenter))\n             (start-button (iup:button :title \"\u0026Start\" :expand :horizontal))\n             (stop-button (iup:button :title \"S\u0026top\" :expand :horizontal))\n             (do-nothing nil)\n             (do-nothing-toggle (iup:toggle :title \"Do nothing\"\n                                            :action (lambda (handle state)\n                                                      (setf do-nothing (not do-nothing))\n                                                      iup:+default+)))\n             (vbox (iup:vbox (list counter\n                                   (iup:hbox (list start-button stop-button do-nothing-toggle)\n                                             :cgap 5))\n                             :margin \"5x5\"))\n             (dialog (iup:dialog vbox\n                                 :title (format nil \"Idle Example on ~A\" (lisp-implementation-type))\n                                 :size \"QUARTERxQUARTER\")))\n        (setf (iup:callback start-button :action)\n              (lambda (handle)\n                (setf (iup:idle-action)\n                      (lambda ()\n                        (unless do-nothing\n                          (setf (iup:attribute counter :title)\n                                (1+ (iup:attribute counter :title 'number))))\n                        iup:+default+))\n                iup:+default+))\n        (setf (iup:callback stop-button :action)\n              (lambda (handle)\n                (setf (iup:idle-action) nil)\n                iup:+default+))\n        (iup:show dialog)\n        (iup:main-loop))))\n#+end_src\n\n#+begin_src lisp :results silent :export none :tangle examples/idle.lisp\n  #-sbcl (idle)\n\n  ,#+sbcl\n  (sb-int:with-float-traps-masked\n      (:divide-by-zero :invalid)\n    (idle))\n#+end_src\n\n[[./docs/screenshots/idle-01.png]]\n\n[[./docs/screenshots/idle-02.png]]\n\n* Canvas\n\nIn this example, we'll port the Sierpinski Carpet fractal that\nappeared the chapter on graphics in [[https://www.apress.com/us/book/9781484211779][Common Lisp Recipes: A\nProblem-Solution Approach]].\n\nWe need a spinner (an up and down arrow-controlled number field) and a\ncanvas to draw on to get started. In this example, rather than specify\nthe callbacks inline, as anonymous lamba forms, we will create\nseparate functions and set them later using ~(SETF\nIUP:CALLBACK)~. ~*LEVELS*~ will keep track how deep to draw the\nfractal.\n\n#+begin_src lisp :results silent :export none :tangle examples/sierpinski.lisp\n  ;;; Generated from org-mode, do not edit\n\n  (eval-when (:compile-toplevel :load-toplevel :execute)\n    (ql:quickload '(\"iup\" \"iup-cd\" \"cd\")))\n\n  (defpackage #:iup-examples.sierpinksi\n    (:use #:common-lisp)\n    (:export #:sierpinksi))\n\n  (in-package #:iup-examples.sierpinksi)\n#+end_src\n\n#+begin_src lisp :results silent :tangle examples/sierpinski.lisp\n  (defparameter *levels* 0)\n\n  (defun sierpinski ()\n    (iup:with-iup ()\n      (let* ((canvas (iup:canvas :rastersize \"200x200\"))\n             (spin (iup:text :spin \"YES\" :spinmin 0 :spinmax 4))\n             (vbox (iup:vbox (list canvas spin) :alignment \"ACENTER\"))\n             (dialog (iup:dialog vbox :title \"Sierpinski Carpet\")))\n        (setf (iup:callback canvas :map_cb) 'canvas-map\n              (iup:callback canvas :unmap_cb) 'canvas-unmap\n              (iup:callback canvas :action) 'canvas-redraw\n              (iup:callback spin :spin_cb) 'canvas-spin\n              ,*levels* 0)\n        (iup:show-xy dialog iup:+center+ iup:+center+)\n        (iup:main-loop))))\n#+end_src\n\n** Notes on Callback Naming\n\nEach IUP widget supports a number of callbacks. In IUP, these are\nstrings. In the Lisp bindings, they can be specified as keywords. For\nexample, ~:UNMAP_CB~. These are rather unlispy names, but do come from\nIUP via its introspection mechanism. In a future version of these\nbindings, it might be possible to have lispier\nnames. e.g. ~:UNMAP-CALLBACK~.\n\n** CD, a 2D Graphics Library\n\nIUP has support for CD, a cross platform 2D Graphics Library. We have\nsupport in Lisp via [[https://github.com/lispnik/cd][CD]] bindings.\n\nThe following code is entirely CD dependent and can be used in non-IUP\ncanvas applications.\n\n#+caption: Adapted from Edi's LTK example in Common Lisp Recipes: A Problem-Solution Approach\n#+begin_src lisp :results silent :tangle examples/sierpinski.lisp\n  (defun sierpinski-draw (canvas level)\n    (multiple-value-bind\n          (w h)\n        (cd:size canvas)\n      (labels ((square (x y x-size y-size)\n                 (cd:box canvas x (+ x x-size) y (+ y y-size)))\n               (recurse (x y x-size y-size level)\n                 (let ((x-step (/ x-size 3))\n                       (y-step (/ y-size 3)))\n                   (square (+ x x-step) (+ y y-step) x-step y-step)\n                   (when (plusp level)\n                     (dolist (x-next (list x (+ x x-step) (+ x x-step x-step)))\n                       (dolist (y-next (list y (+ y y-step) (+ y y-step y-step)))\n                         (recurse x-next y-next x-step y-step (1- level))))))))\n        (recurse 0 0 w h level))))\n#+end_src\n\nFor example, we can write it to [[./docs/sierpinski.pdf][PDF]] and print out to hang on your\nwall:\n\n#+begin_src lisp :results silent\n  (ql:quickload \"cd-pdf\")\n\n  (let ((canvas (cd:create-canvas (cd-pdf:context-pdf) \"docs/sierpinski.pdf\")))\n    (unwind-protect\n         (sierpinski-draw canvas 4)\n      (cd:kill canvas)))\n#+end_src\n\nIn our IUP example however, we'll use it with IUP's CD support and\narrange for the canvas to be draw on via ~CANVAS-REDRAW~ which will be\ntriggered by the canvas widget's action callback.\n\n#+begin_src lisp :results silent :tangle examples/sierpinski.lisp\n  (defparameter *canvas* nil)\n\n  (defun canvas-redraw (handle x y)\n    (cd:activate *canvas*)\n    (cd:clear *canvas*)\n    (setf (cd:foreground *canvas*) cd:+red+)\n    (sierpinski-draw *canvas* *levels*)\n    (cd:flush *canvas*)\n    iup:+default+)\n#+end_src\n\nWe can ignore ~HANDLE~, ~X~, and ~Y~ in our callback handler in this\nexample. Those are IUP widget that triggered the callback and location\non the canvas.\n\nFirst we activate the canvas to draw on, clear whatever was there, set\na drawing color for the the foreground of the canvas, then draw to the\ncanvas using ~SIERPINSKI-DRAW~.\n\nThe last step is to flush the canvas. This triggers a backing buffer\nswap, so all of the drawing appears instantly. If we don't do this, we\ndon't see anything on the screen because it will still be in the\noff-screen drawing buffer.\n\n** Attributes\n\nIt is a good idea to separate your UI presentation from its undelying\nmodel. In our case, the UI \"model\" is a special variable ~*LEVELS*~\nwhich holds the depth to draw the fractal as an integer. We need this\nupdated when the user clicks on the spinner widget.\n\n#+begin_src lisp :results silent :tangle examples/sierpinski.lisp\n  (defun canvas-spin (handle pos)\n    (setf *levels* (iup:attribute handle :value 'number))\n    (canvas-redraw nil nil nil)\n    iup:+default+)\n#+end_src\n\nWe can get the number from the spinner widget and assign it to\n~*LEVELS*~ using ~IUP:ATTRIBUTE~. It takes a IUP handle from which to\nget the ~:VALUE~ attribute. \n\nIUP widget value attributes are mostly strings. The third argument,\n~'INTEGER~ converts the string to an integer for convenience, rather\nthan having to ~PARSE-INTEGER~ ourselves.\n\n** Fiddly bits\n\nLastly, we need to associate the CD canvas with a IUP canvas, but we\ncan't do this until we have the handle of the IUP canvas, so we can't\nset it up in the ~LET*~ form in our main function like we did with\neverything else. \n\nLuckily IUP provides callbacks for when the component is \"mapped\" onto\nthe user's display which allow us to deal with this dependency in an\nelegant manner.\n\n#+begin_src lisp :results silent :tangle examples/sierpinski.lisp\n  (defun canvas-map (handle)\n    (setf *canvas* (cd:create-canvas (iup-cd:context-iup-dbuffer) handle))\n    iup:+default+)\n\n  (defun canvas-unmap (handle)\n    (cd:kill *canvas*)\n    iup:+default+)\n#+end_src\n\n#+begin_src lisp :results silent :tangle examples/sierpinski.lisp\n  #-sbcl (sierpinski)\n\n  ,#+sbcl\n  (sb-int:with-float-traps-masked\n      (:divide-by-zero :invalid)\n    (sierpinski))\n#+end_src\n\n[[./docs/screenshots/sierpinski.png]] [[./docs/screenshots/sierpinski-02.png]]\n\n* Using IUP Additional Controls\n\nThe [[https://www.tecgraf.puc-rio.br/iup/en/ctrl/iupcells.html][cells control]] \"creates a grid widget (set of cells) that enables\nseveral application-specific drawing, such as: chess tables, tiles\neditors, degrade scales, drawable spreadsheets and so forth\".\n\nIt's included in the standard IUP distribution downloads, but it's not\nautomatically loaded. The Lisp bindings do the same thing, so to use\nit, we need to depend on ~IUP-CONTROLS~.\n\n#+begin_src lisp :results silent :export none :tangle examples/cells.lisp\n  ;;; Generated from org-mode, do not edit\n\n  (eval-when (:compile-toplevel :load-toplevel :execute)\n    (ql:quickload '(\"iup\" \"iup-controls\" \"cd\")))\n\n  (defpackage #:iup-examples.cells-checkerboard\n    (:use #:common-lisp)\n    (:export #:cells-checkerboard))\n\n  (in-package #:iup-examples.cells-checkerboard)\n#+end_src\n\nWe start with the same boiler plate, but this time we need to call\n~IUP-CONTROLS:OPEN~ ahead of using the cells control.\n\n#+begin_src lisp :results silent :tangle examples/cells.lisp\n  (defun cells-checkerboard ()\n    (iup:with-iup ()\n      (iup-controls:open)\n      (let* ((cells (iup-controls:cells\n                     :draw_cb 'draw\n                     :width_cb 'width\n                     :height_cb 'height\n                     :nlines_cb 'nlines\n                     :ncols_cb 'ncols\n                     :mouseclick_cb 'click))\n             (vbox (iup:vbox (list cells)))\n             (dialog (iup:dialog vbox :title \"Cells Checkerboard\" :rastersize \"440x480\" :shrink \"YES\")))\n        (iup:show-xy dialog iup:+center+ iup:+center+)\n        (iup:main-loop))))\n#+end_src\n\nCells has a number of callbacks related rows, columns, sizing etc.\n\n#+begin_src lisp :results silent :tangle examples/cells.lisp\n  (defun nlines (handle) 8)\n  (defun ncols (handle) 8)\n  (defun height (handle i) 50)\n  (defun width (handle j) 50)\n#+end_src\n\nWhen ~DRAW~ is called, we get a canvas on which to draw:\n\n#+begin_src lisp :results silent :tangle examples/cells.lisp\n  (defun draw (handle i j xmin xmax ymin ymax canvas)\n    (if (or (and (oddp i) (oddp j)) (and (oddp (1+ i)) (oddp (1+ j))))\n        (setf (cd:foreground canvas) cd:+black+)\n        (setf (cd:foreground canvas) cd:+white+))\n    (cd:box canvas xmin xmax ymin ymax)\n    iup::+default+)\n#+end_src\n\nWhen out click callback is called:\n\n#+begin_src lisp :results silent :tangle examples/cells.lisp\n  (defun click (handle button pressed line column x y status)\n    (iup:message\n     \"Clicked!\"\n     (format nil \"Callback arguments~%~S\"\n      (list :button button\n            :pressed pressed\n            :line line\n            :column column\n            :x x\n            :y y\n            :status (iup:status-plist status))))\n       iup:+default+)\n#+end_src\n\n#+begin_src lisp :results silent :export none :tangle examples/cells.lisp\n  #-sbcl (cells-checkerboard)\n\n  ,#+sbcl\n  (sb-int:with-float-traps-masked\n      (:divide-by-zero :invalid)\n    (cells-checkerboard))\n#+end_src\n\n[[./docs/screenshots/checkerboard-01.png]] \n[[./docs/screenshots/checkerboard-02.png]]\n\n[[./docs/screenshots/checkerboard-03.png]] \n[[./docs/screenshots/checkerboard-04.png]] \n\n(lol button 49)\n\n* Detachable Box\n\n#+begin_src lisp :results silent :export none :tangle examples/detached.lisp\n  ;;; Generated from org-mode, do not edit\n\n  (eval-when (:compile-toplevel :load-toplevel :execute)\n    (ql:quickload \"iup\"))\n\n  (defpackage #:iup-examples.detached\n    (:use #:common-lisp)\n    (:export #:detached))\n\n  (in-package #:iup-examples.detached)\n#+end_src\n\n#+begin_src lisp :results silent :tangle examples/detached.lisp\n  (defun detached ()\n    (iup:with-iup ()\n      (let* ((button1 (iup:button :title \"Detach Me!\"\n                                  :action 'button-detach-callback\n                                  :expand :yes\n                                  :handlename \"detach\"))\n             (multi-line (iup:multi-line :expand :yes\n                                         :visiblelines 5))\n             (hbox (iup:hbox (list button1 multi-line) :margin \"10x0\"))\n             (dbox (iup:detach-box hbox :orientation :vertical\n                                        :detached_cb 'detached-callback\n                                        :handlename \"dbox\"))\n             (label (iup:label :title \"Label\"\n                               :expand :vertical))\n             (button2 (iup:button :title \"Restore me!\"\n                                  :expand :yes\n                                  :active :no\n                                  :action 'button-restore-callback\n                                  :handlename \"restore\"))\n             (text (iup:text :expand :horizontal))\n             (dialog (iup:dialog (iup:vbox (list dbox label button2 text)\n                                           :margin \"10x10\"\n                                           :gap 10)\n                                 :title \"IupDetachBox Example\"\n                                 :rastersize \"300x300\")))\n\n        (iup:show dialog)\n        (iup:main-loop))))\n#+end_src\n\n** Handle Names\n\nInstead of accessing other elements via lexical scope, it's sometimes\nuseful to refer to them by name. This example uses the ~HANDLENAME~\nattribute to associate a name with an IUP handle.\n\n#+begin_src lisp :results silent :tangle examples/detached.lisp\n  (defun detached-callback (handle new-parent x y)\n    (setf (iup:attribute new-parent :title) \"New Dialog\"\n          (iup:attribute (iup:handle \"restore\") :active) :yes\n          (iup:attribute (iup:handle \"detach\") :active) :no)\n    iup:+default+)\n\n  (defun button-restore-callback (button)\n    (setf (iup:attribute (iup:handle \"dbox\") :restore) nil\n          (iup:attribute button :active) :no\n          (iup:attribute (iup:handle \"detach\") :active) :yes)\n    iup:+default+)\n\n  (defun button-detach-callback (button)\n    (setf (iup:attribute (iup:handle \"dbox\") :detach) nil\n          (iup:attribute button :active) :no\n          (iup:attribute (iup:handle \"restore\") :active) :yes)\n    iup:+default+)\n#+end_src\n\n#+begin_src lisp :results silent :export none :tangle examples/detached.lisp\n  #-sbcl (detached)\n\n  ,#+sbcl\n  (sb-int:with-float-traps-masked\n      (:divide-by-zero :invalid)\n    (detached))\n#+end_src\n\n[[./docs/screenshots/detach-01.png]] [[./docs/screenshots/detach-02.png]]\n\n# FIXME look into problem with restore not being active after detach\n# FIXME insert example of using restart to recover from error in callback\n\n* Tabs Example\n\nDemonstrates the use of ~(SETF IUP:ATTRIBUTE)~ for setting attributes\nnot available via control's constructor function.\n\n#+begin_src lisp :results silent :export none :tangle examples/tabs.lisp\n  ;;; Generated from org-mode, do not edit\n\n  (eval-when (:compile-toplevel :load-toplevel :execute)\n    (ql:quickload \"iup\"))\n\n  (defpackage #:iup-examples.tabs\n    (:use #:common-lisp)\n    (:export #:tabs))\n\n  (in-package #:iup-examples.tabs)\n#+end_src\n\n#+begin_src lisp :results silent :tangle examples/tabs.lisp\n  (defun tabs ()\n    (iup:with-iup ()\n      (let* ((vbox1 (iup:vbox\n                     (list (iup:label :title \"Inside Tab A\")\n                           (iup:button :title \"Button A\"))))\n             (vbox2 (iup:vbox\n                     (list (iup:label :title \"Inside Tab B\")\n                           (iup:button :title \"Button B\"))))\n             (tabs1 (iup:tabs (list vbox1 vbox2)))\n             (vbox3 (iup:vbox\n                     (list (iup:label :title \"Inside C\")\n                           (iup:button :title \"Button C\"))))\n             (vbox4 (iup:vbox\n                     (list (iup:label :title \"Inside D\")\n                           (iup:button :title \"Button D\"))))\n             (tabs2 (iup:tabs (list vbox3 vbox4)))\n             (box (iup:hbox (list tabs1 tabs2) :margin \"10x10\" :gap \"10\"))\n             (dialog (iup:dialog box :title \"IUP Tabs\" :size \"200x80\")))\n        (setf (iup:attribute vbox1 :tabtitle) \"Tab A\"\n              (iup:attribute vbox2 :tabtitle) \"Tab B\"\n              (iup:attribute vbox3 :tabtitle) \"Tab C\"\n              (iup:attribute vbox4 :tabtitle) \"Tab D\")\n        (iup:show dialog)\n        (iup:main-loop))))\n#+end_src\n\n#+begin_src lisp :results silent :export none :tangle examples/tabs.lisp\n  #-sbcl (tabs)\n\n  ,#+sbcl\n  (sb-int:with-float-traps-masked\n      (:divide-by-zero :invalid)\n    (tabs))\n#+end_src \n\n[[./docs/screenshots/tabs-01.png]] [[./docs/screenshots/tabs-02.png]]\n\n* Plotting\n\nExample [[./examples/plot.lisp][./examples/plot.lisp]]\n\n[[./docs/screenshots/plot-01.png]]\n\n[[./docs/screenshots/plot-02.png]]\n\n* OpenGL\n\nFor this example, we'll take advantage for [[https://github.com/3b/cl-opengl][cl-opengland and\ncl-glu]]. Don't forget to depend on iup-gl (part of these bindings) as\nwell.\n\nMuch of this example is tedious old-style OpenGL. We'll only highlight\nthe IUP/OpenGL integration points here. It suffices to say, we've got\na function ~CUBE~ which draws OpenGL things to the current buffer.\n\n#+begin_src lisp :export none :results silent :tangle examples/cube.lisp\n  ;;; Generated from org-mode, do not edit\n\n  (eval-when (:compile-toplevel :load-toplevel :execute)\n    (ql:quickload '(\"iup\" \"iup-gl\" \"cl-opengl\" \"cl-glu\")))\n\n  (defpackage #:iup-examples.cube\n    (:use #:common-lisp)\n    (:export #:cube))\n\n  (in-package #:iup-examples.cube)\n#+end_src\n\n#+begin_src lisp :results silent :tangle examples/cube.lisp\n  (defvar *canvas* nil)\n  (defvar *tt* 0.0)\n\n  (defvar *vertices*\n    #((-1 -1 1) (-1 1 1)\n      (1 1 1) (1 -1 1)\n      (-1 -1 -1) (-1 1 -1)\n      (1 1 -1) (1 -1 -1)))\n\n  (defun polygon (a b c d)\n    (gl:begin :polygon)\n    (apply #'gl:vertex (aref *vertices* a))\n    (apply #'gl:vertex (aref *vertices* b))\n    (apply #'gl:vertex (aref *vertices* c))\n    (apply #'gl:vertex (aref *vertices* d))\n    (gl:end))\n\n  (defun color-cube ()\n    (gl:color 1 0 0)\n    (gl:normal 1 0 0)\n    (polygon 2 3 7 6)\n    (gl:color 0 1 0)\n    (gl:normal 0 1 0)\n    (polygon 1 2 6 5)\n    (gl:color 0 0 1)\n    (gl:normal 0 0 1)\n    (polygon 0 3 2 1)\n    (gl:color 1 0 1)\n    (gl:normal 0 -1 0)\n    (polygon 3 0 4 7)\n    (gl:color 1 1 0)\n    (gl:normal 0 0 -1)\n    (polygon 4 5 6 7)\n    (gl:color 0 1 1)\n    (gl:normal -1 0 0)\n    (polygon 5 4 0 1))\n#+end_src\n\n#+begin_src lisp :results silent :tangle examples/cube.lisp\n  (defun cube ()\n    (iup:with-iup ()\n      (iup-gl:open)\n      (setf *canvas*\n            (iup-gl:canvas :rastersize \"640x480\"\n                           :buffer \"DOUBLE\"\n                           :action 'repaint\n                           :resize_cb 'resize))\n      (let* ((dialog (iup:dialog *canvas* :title \"IUP OpenGL\")))\n        ;; FIXME      (iup-cffi::%iup-set-function :idle_action 'idle)\n        (setf (iup:attribute *canvas* :depthsize) \"16\")\n        (iup:show dialog)\n        (iup:main-loop))))\n#+end_src\n\nOur example has three callbacks: repaint, resize and a global idle\nfunction callback which we'll use to rotate a cube relative to time\nvariable ~*TT*~.\n\n#+begin_src lisp :results silent :tangle examples/cube.lisp\n  (defun repaint (handle posx posy)\n    (iup-gl:make-current handle)\n    (gl:clear-color 0.3 0.3 0.3 1.0)\n    (gl:clear :color-buffer-bit :depth-buffer-bit)\n    (gl:enable :depth-test)\n    (gl:matrix-mode :modelview)\n    (gl:with-pushed-matrix\n      (gl:translate 0 0 0)\n      (gl:scale 1 1 1)\n      (gl:rotate *tt* 0 0 1)\n      (color-cube))\n    (iup-gl:swap-buffers handle)\n    iup::+default+)\n\n  (defun resize (handle width height)\n    (iup-gl:make-current handle)\n    (gl:viewport 0 0 width height)\n    (gl:matrix-mode :modelview)\n    (gl:load-identity)\n    (gl:matrix-mode :projection)\n    (gl:load-identity)\n    (glu:perspective 60 (/ 4 3) 1 15)\n    (glu:look-at 3 3 3 0 0 0 0 0 1)\n    iup::+default+)\n#+end_src\n\n#+begin_src lisp :results silent :tangle examples/cube.lisp\n  ;;; FIXME\n  ;; (cffi:defcallback idle-cb :int ()\n  ;;   (incf tt)\n  ;;   (iup-gl:make-current canvas)\n  ;;   (repaint canvas)\n  ;;   iup::+default+)\n#+end_src\n\n#+begin_src lisp :results silent :tangle examples/cube.lisp\n  #-sbcl (cube)\n\n  ,#+sbcl\n  (sb-int:with-float-traps-masked\n      (:divide-by-zero :invalid)\n    (cube))\n#+end_src\n\n[[./docs/screenshots/opengl.png]]\n\n[[./docs/screenshots/opengl-01.png]]\n\n* Trees\n\nThis is a port of the [[http://webserver2.tecgraf.puc-rio.br/iup/en/basic/index.html#Trees][Lua tree example from the IUP documentation]]. It\ngoes one step further by allowing the tree to be expanded recursively\nas branches open.\n\n#+begin_src lisp :results silent :export none :tangle examples/tree.lisp\n  ;;; Generated from org-mode, do not edit\n\n  (eval-when (:compile-toplevel :load-toplevel :execute)\n    (ql:quickload '(\"iup\" \"iup-controls\" \"uiop\")))\n\n  (defpackage #:iup-examples.tree\n    (:use #:common-lisp)\n    (:export #:tree))\n\n  (in-package #:iup-examples.tree)\n#+end_src\n\n#+begin_src lisp :results silent :tangle examples/tree.lisp\n  (defun get-dir (pathname)\n    (assert (uiop:directory-pathname-p pathname))\n    (loop for pathname in (uiop:directory* (make-pathname :name :wild :defaults pathname))\n          if (uiop:directory-pathname-p pathname)\n            collect pathname into dirs\n          else\n            collect pathname into files\n          finally (return (values dirs files))))\n\n  (defun fill-tree (tree id pathname)\n    (multiple-value-bind\n          (dirs files)\n        (get-dir pathname)\n      (dolist (file files)\n        (setf (iup:attribute tree :addleaf) (namestring file)))\n      (dolist (dir dirs)\n        (setf (iup:attribute tree :addbranch) (namestring dir)))\n      (setf (iup:attribute tree :title) (namestring pathname))))\n#+end_src\n\n#+begin_src lisp :results silent :tangle examples/tree.lisp\n  (defun map-callback (handle)\n    (fill-tree handle 0 \"/\")\n    iup:+default+)\n\n  (defun branchopen-callback (handle id)\n    (setf (iup:attribute handle (format nil \"DELNODE~A\" id)) \"CHILDREN\")\n    (fill-tree handle id (iup:attribute handle (format nil \"TITLE~A\" id))) \n    iup:+default+)\n\n  (defun tree ()\n    (iup:with-iup ()\n      (let* ((tree (iup:tree :minsize \"200x300\"\n                             :map_cb 'map-callback\n                             :branchopen_cb 'branchopen-callback))\n             (dialog (iup:dialog tree :title \"Tree Example\")))\n        (iup:show dialog)\n        (iup:main-loop))))\n#+end_src\n\n#+begin_src lisp :results silent :export none :tangle examples/tree.lisp\n  #-sbcl (tree)\n\n  ,#+sbcl\n  (sb-int:with-float-traps-masked\n      (:divide-by-zero :invalid)\n    (tree))\n#+end_src\n\n[[./docs/screenshots/tree-02.png]]\n\n* Built-in Dialogs\n\nIUP includes a number of dialogs, including one that embeds the\n[[https://www.scintilla.org/][Scintilla]] editor control.\n\n#+begin_src lisp :results silent :tangle examples/dialogs.lisp\n  ;;; Generated from org-mode, do not edit\n\n  (eval-when (:compile-toplevel :load-toplevel :execute)\n    (ql:quickload '(\"iup\" \"iup-scintilla\")))\n\n  (defpackage #:iup-examples.dialogs\n    (:use #:common-lisp)\n    (:export #:dialogs))\n\n  (in-package #:iup-examples.dialogs)\n#+end_src\n\n#+begin_src lisp :results silent :tangle examples/dialogs.lisp\n  (defun dialogs ()\n    (iup:with-iup ()\n      (iup-scintilla:open)\n      (flet ((button (title callback)\n               (iup:button :title title\n                           :action callback\n                           :expand :horizontal)))\n        (let* ((dialog (iup:dialog\n                        (iup:vbox (list (button \"File Dialog\" 'file-dialog)\n                                        (button \"Message Dialog\" 'message-dialog)\n                                        (button \"Color Dialog\" 'color-dialog)\n                                        (button \"Font Dialog\" 'font-dialog)\n                                        (button \"Scintilla Dialog\" 'scintilla-dialog)\n                                        (button \"Layout Dialog\" 'layout-dialog)))\n                        :title \"IUP Predefined Dialogs\")))\n          (iup:show dialog)\n          (iup:main-loop)))))\n#+end_src\n\n[[./docs/screenshots/dialogs-01.png]] [[./docs/screenshots/dialogs-02.png]]\n\n** Using ~IUP:POPUP~ for Modal Dialogs\n\nOften a UI designs for grabbing the user's attention via modal dialogs\nwhere the dialog is shown above the rest of the application and\nprevents interaction with the rest of the application. ~IUP:POPUP~\nlets you achive this.\n\n** File Dialog\n\n#+begin_src lisp :results silent :tangle examples/dialogs.lisp\n  (defun file-dialog (handle)\n    (let ((dialog (iup:file-dialog)))\n      (unwind-protect\n           (progn\n             (iup:popup dialog iup:+center+ iup:+center+)\n             (iup:message \"File Dialog Example\"\n                          (format nil \"Selected ~A\" (iup:attribute dialog :value))))\n        (iup:destroy dialog)))\n    iup:+default+)\n#+end_src\n\n*NOTE:* Because modal dialogs are often created over the course of a\nprogram's runtime, they need to be destroyed after use, via\n~IUP:DESTROY~.\n\n[[./docs/screenshots/filedialog-01.png]] [[./docs/screenshots/filedialog-02.png]]\n\n[[./docs/screenshots/filedialog-03.png]] [[./docs/screenshots/filedialog-04.png]]\n\n** Message Dialog\n\nMessage dialogs are like ~IUP:MESSAGE~ except that they allow for more\nconfiguration (result buttons, etc.).\n\n#+begin_src lisp :results silent :tangle examples/dialogs.lisp\n  (defun message-dialog (handle)\n    (let ((dialog (iup:message-dialog \n                   :dialogtype :warning\n                   :buttons :retrycancel)))\n      (unwind-protect\n           (progn\n             (setf (iup:attribute dialog :value) \"Heap exhausted, game over.\")\n             (iup:popup dialog iup:+center+ iup:+center+)\n             (iup:message \"Message Dialog\"\n                          (format nil \"Got button response ~S\"\n                                  (iup:attribute dialog :buttonresponse))))\n        (iup:destroy dialog)))\n    iup:+default+)\n#+end_src\n\n[[./docs/screenshots/messagedialog-01.png]] [[./docs/screenshots/messagedialog-02.png]]\n\n[[./docs/screenshots/messagedialog-03.png]] [[./docs/screenshots/messagedialog-04.png]]\n\n** Color Dialog\n\n#+begin_src lisp :results silent :tangle examples/dialogs.lisp\n  (defun color-dialog (handle)\n    (let ((dialog (iup:color-dialog\n                   :title \"IUP Color Dialog\"\n                   :showhex \"YES\"\n                   :showcolortable \"YES\"\n                   :showalpha \"YES\")))\n      (unwind-protect\n           (progn\n             (iup:popup dialog iup:+center+ iup:+center+)\n             (iup:message \"Result\"\n                          (format nil \"Got button response ~S~%Got color ~A RGB (~A HSI, ~A)\"\n                                  (iup:attribute dialog :status)\n                                  (iup:attribute dialog :value)\n                                  (iup:attribute dialog :valuehsi)\n                                  (iup:attribute dialog :valuehex))))))\n    iup:+default+)\n#+end_src\n\n[[./docs/screenshots/colordialog-01.png]] [[./docs/screenshots/colordialog-02.png]]\n\n[[./docs/screenshots/colordialog-03.png]] [[./docs/screenshots/colordialog-04.png]]\n\n** Font Dialog\n\n#+begin_src lisp :results silent :tangle examples/dialogs.lisp\n  (defun font-dialog (handle)\n    (let ((dialog (iup:font-dialog :title \"IUP Font Dialog\")))\n      (unwind-protect\n           (progn\n             (iup:popup dialog iup:+center+ iup:+center+)\n             (iup:message \"Result\"\n                          (format nil \"Got button response ~S~%Got font ~S\"\n                                  (iup:attribute dialog :status)\n                                  (iup:attribute dialog :value))))\n        (iup:destroy dialog)))\n    iup:+default+)\n#+end_src\n\n[[./docs/screenshots/fontdialog-01.png]] [[./docs/screenshots/fontdialog-02.png]]\n\n[[./docs/screenshots/fontdialog-03.png]] [[./docs/screenshots/fontdialog-04.png]]\n** Scintilla Dialog\n\n#+begin_src lisp :results silent :tangle examples/dialogs.lisp\n  (defun scintilla-dialog (handle)\n    (let ((dialog (iup-scintilla:scintilla-dialog :title \"IUP Scintilla Dialog\")))\n      (unwind-protect\n           (iup:popup dialog iup:+center+ iup:+center+)\n        (iup:destroy dialog))))\n#+end_src\n\n[[./docs/screenshots/scintilladialog-01.png]]\n\n[[./docs/screenshots/scintilladialog-02.png]]\n\n(There is also a separate, more customizable Scintilla control:\n~IUP-SCINTILLA:SCINTILLA~.)\n\n** IUP Layout Dialog\n\nThe layout dialog lets you visually inspect and edit an existing\ndialog and it's children or create a new dialog from scretch. It is\nextremely useful for experimenting and iterating on UI design.\n\nYou can use it as a visual GUI builder, similar to Glade in GTK+.\n\nYou can export a dialog and load it from file via ~IUP:LOAD~. The\nexport format is a [[https://www.tecgraf.puc-rio.br/iup/en/led.html][IUP LED file]].\n\n#+begin_src lisp :results silent :tangle examples/dialogs.lisp\n  (defun layout-dialog (handle)\n    (let ((dialog (iup:layout-dialog nil)))\n      (unwind-protect\n           (iup:popup dialog iup:+center+ iup:+center+)\n        (iup:destroy dialog)))\n    iup:+default+)\n#+end_src\n\n[[./docs/screenshots/layoutdialog-01.png]]\n\n[[./docs/screenshots/layoutdialog-02.png]]\n\n** Get Text Dialog\n\n#+begin_src lisp :results silent :tangle examples/dialogs.lisp\n#+end_src\n\n** List Dialog\n\n#+begin_src lisp :results silent :tangle examples/dialogs.lisp\n#+end_src\n\n** Get Param Dialog\n\n#+begin_src lisp :results silent :tangle examples/dialogs.lisp\n#+end_src\n\n** Alarm Dialog\n\n#+begin_src lisp :results silent :tangle examples/dialogs.lisp\n#+end_src\n\n#+begin_src lisp :results silent :tangle examples/dialogs.lisp\n  #-sbcl (dialogs)\n\n  ,#+sbcl\n  (sb-int:with-float-traps-masked\n      (:divide-by-zero :invalid)\n    (dialogs))\n#+end_src \n\n* Application Icons and IUP-IM\n\nIn this example, we'll set the application icon via an arbitrary image\nfile, demonstrating the use of the ~IUP-IM~ system. The ~IM-IUP~\nsystem provides support in our IUP bindings for interoperability with\n[[http://webserver2.tecgraf.puc-rio.br/im/][IM]], an imaging toolkit, also by Tecgraf.\n\n#+begin_src lisp :results silent :export none :tangle examples/icon.lisp\n  ;;; Generated from org-mode, do not edit\n\n  (eval-when (:compile-toplevel :load-toplevel :execute)\n    (ql:quickload '(\"iup\" \"iup-im\")))\n\n  (defpackage #:iup-examples.icon\n    (:use #:common-lisp)\n    (:export #:icon))\n\n  (in-package #:iup-examples.icon)\n#+end_src\n\nHere we load an image from the filesystem using ~IUP-IM:LOAD-IMAGE~\nand associate the global handle name ~lispalien~ with it. We can use\nthis handle name in labels, buttons, etc. to set the image that should\nbe used. In the case of dialogs however, we can use the handle name to\nspecify the image that should be displayed in the application's title\nbar.\n\nIM supports a large number of formats. Here we use a .ICO file.\n\n#+begin_src lisp :results silent :tangle examples/icon.lisp\n  (defun icon ()\n    (iup:with-iup ()\n      (let ((icon (iup-im:load-image (asdf:system-relative-pathname \"iup\" \"examples/lispalien.ico\"))))\n        (setf (iup:handle \"lispalien\") icon))\n      (let* ((label (iup:flat-label :image \"lispalien\" :expand :yes))\n             (dialog (iup:dialog label :title \"Icon from File\"\n                                       :icon \"lispalien\"\n                                       :size \"THIRDxTHIRD\")))\n        (iup:show dialog)\n        (iup:main-loop))))\n#+end_src\n\n#+begin_src lisp :results silent :export none :tangle examples/icon.lisp\n  #-sbcl (icon)\n\n  ,#+sbcl\n  (sb-int:with-float-traps-masked\n      (:divide-by-zero :invalid)\n    (icon))\n#+end_src\n\n[[./docs/screenshots/icon-01.png]] [[./docs/screenshots/icon-02.png]]\n\n* Drag and Drop\n\n** File Drag and Drop from Applications\n\n#+begin_src lisp :results silent :export none :tangle examples/drophash.lisp\n  ;;; Generated from org-mode, do not edit\n\n  (eval-when (:compile-toplevel :load-toplevel :execute)\n    (ql:quickload '(\"iup\" \"ironclad\")))\n\n  (defpackage #:iup-examples.drophash\n    (:use #:common-lisp)\n    (:export #:drophash))\n\n  (in-package #:iup-examples.drophash)\n#+end_src\n\nThis example demonstrates how to receive files via a drag and drop\noperation from another other application as well as sliding box layout\nand techniques for keeping the UI responsive during computationally\nintensive operations.\n\nWe'll create a simple GUI wrapper for computing file digests using\n[[https://github.com/sharplispers/ironclad][Ironclad]], a cryptographic toolkit written in Common Lisp. The drop\ndown lists all the digest algorithms Ironclad supports, and a label\nwill be used to receive file drops. Once the digest is computed, they\nare appended to a result panel.\n\n#+begin_src lisp :results silent :tangle examples/drophash.lisp\n  (defun drophash ()\n    (iup:with-iup ()\n      (let* ((list (iup:list :dropdown :yes\n                             :expand :horizontal\n                             :handlename \"list\"))\n             (label (iup:flat-label :title \"Drop files for hash\"\n                                    :alignment \"ACENTER:ACENTER\"\n                                    :font \"Helvetica, 24\"\n                                    :dropfilestarget :yes\n                                    :dropfiles_cb 'drop-files-callback\n                                    :expand :yes))\n             (frame (iup:frame label))\n             (results (iup:multi-line :expand :yes\n                                      :readonly :yes\n                                      :visiblelines 7\n                                      :handlename \"results\"))\n             (vbox (iup:vbox (list list\n                                   frame\n                                   (iup:sbox results :direction :north))\n                             :margin \"10x10\"\n                             :cgap 5))\n             (dialog (iup:dialog vbox\n                                 :title \"Drop Hash\"\n                                 :size \"HALFxHALF\")))\n        (loop for digest in (ironclad:list-all-digests)\n              for i from 1\n              do (setf (iup:attribute list i) digest)\n              finally (setf (iup:attribute list :valuestring) 'ironclad:sha256))\n        (iup:show dialog)\n        (iup:main-loop))))\n#+end_src \n\nWhen files are dropped onto the drop target (in this case, an IUP flat\nlabel), the drop files callback is called. If multiple files are\ndropped at the same time, then the callback will be invoked for each\nfile.\n\n#+begin_src lisp :results silent :tangle examples/drophash.lisp\n  (defun drop-files-callback (handle filename num x y)\n    (let* ((digest\n            (intern (iup:attribute (iup:handle \"list\") :valuestring) \"IRONCLAD\"))\n          (digest-hex \n            (ironclad:byte-array-to-hex-string \n             (ironclad:digest-file digest\n              filename))))\n      (setf (iup:attribute (iup:handle \"results\") :append)\n            (format nil \"~A     ~A\" filename digest-hex)))\n    (iup:flush)\n    iup:+default+)\n#+end_src\n\nWhen multiple files are dropped, the callback will be invoked in rapid\nsuccession and the UI will seem unresponsive. This is why the example\ncalls ~IUP:FLUSH~ after each file is processed. ~IUP:FLUSH~ will run\nany pending UI operations (such as the append to the results text\nbox).\n\nThis helps, and indeed the results pane updates in real-time as files\nare processed, however the UI will become unresponsive again when the\ndigest of large files are computed.\n\nIt is best not to do any computationally expensive operations in the\nUI thread. We'll cover off-loading from the UI thread as well as\nrevisit this example for better responsiveness later.\n\n#+begin_src lisp :results silent :tangle examples/drophash.lisp\n  #-sbcl (drophash)\n\n  ,#+sbcl\n  (sb-int:with-float-traps-masked\n      (:divide-by-zero :invalid)\n    (drophash))\n#+end_src \n\n[[./docs/screenshots/drophash-01.png]]\n\n[[./docs/screenshots/drophash-02.png]]\n\n* Examples\n\nCheckout the [[./examples][examples]] directory for the examples in this document as\nwell as these other examples.\n\n** LTK Demonstration Port\n\nIncludes example usage of ~IUP:TIMER~ for canvas animations.\n\n[[./docs/screenshots/ltkdemo-01.png]]\n\n[[./docs/screenshots/ltkdemo-02.png]]\n\n* Bindings Generation Internals\n\nThere are dozens of IUP controls and each control has dozens of\ncallbacks and attributes. Fortunately IUP controls can be introspected\nto gain information on what the control is, what its callbacks and\nattributes are (and their arguments and types). \n\nThe ~iup-classesdb~ system uses this information to to automatically\ngenerate binding metadata from which the bindings are generated. This\nprovides for a much nicer development experience:\n\n[[./docs/screenshots/generation-01.png]]\n\nThe following sections describe how this works in more detail.\n\n** Maintainer\n\nThe maintainer is typically someone with access to the Git repository\nfor these bindings. When a new release of IUP comes out, the\nmaintainer needs to update the metadata so that any new or removed\ncontrols, attributes or callbacks are reflected in the Lisp bindings:\n\n#+begin_src plantuml :file docs/binding-maintainer.png :results silent\n  (*) --\u003e \"(asdf:load-system :iup-classesdb)\" as Load\n  Load --\u003e \"(iup-classesdb:regenerate)\" as Regen\n  Regen --\u003e \"classesdb.lisp-sexp\" as Sexp\n  Sexp --\u003e (*)\n#+end_src\n\n[[./docs/binding-maintainer.png]]\n\n[[file:classesdb.lisp-sexp][~classesdb.lisp-sexp~]] is the output metadata. The maintainer typically\ncommits this file to version control so the metadata is available for\neveryone.\n\n** User\n\nThe first time the user compiles the IUP bindings,\n~classesdb.lisp-sexp~ is processed by macros at compile time and\ngenerates all function definitions for IUP controls. Note, that\n~classesdb.lisp-sexpr~ is not actually needed when the user loads the\nsystem.\n\nFor the curious, the generation looks like the following, for each IUP\nsystem: ~IUP~, ~IUP-CONTROLS~, ~IUP-GL~, ~IUP-GLCONTROLS~, ~IUP-PLOT~,\n~IUP-MGLPLOT~, ~IUP-OLECONTROL~, ~IUP-SCINTILLA~, ~IUP-WEB~ and\n~IUP-TUIO~.\n\n#+begin_src lisp :results silent :export none\n  (iup::defiupclasses \"IUP\")\n#+end_src\n\nThe process is roughly: \n\n1. load each shared library\n2. introspect for the available IUP classes (i.e. metadata about\n   controls) availabe\n3. For each class, generate the bindings in its own package.\n\n#+begin_src plantuml :file docs/binding-generation.png :results silent\n  (*) --\u003e \"(asdf:compile-system :iup)\" as Load\n  \"classesdb.lisp-sexp\" as Sexpr --\u003e Load\n  Load --\u003e (*)\n#+end_src\n\n#+begin_src plantuml :file docs/binding-generation-2.png :results silent\n  (*) --\u003e \"(asdf:load-system :iup)\" as Load\n  Load --\u003e (*)\n#+end_src\n\n [[./docs/binding-generation.png]][[./docs/binding-generation-2.png]]\n\n** Why ~classesdb.lisp-sexp~?\n\nExtracting the metadata actually requires a complete GUI stack\nrunning. On Linux, this means having an X11 display available. This\nturns out to be a bit of a problem for continuous integration\nsystems.\n\nAlthough there are embedded X11 servers that can be used, I didn't\nknow what might be necessary for Windows or even macOS (when it's\nsupported) for CI/CD. Hence the ~classesdb.lisp-sexp~ is the\nmaintainer's job to regenerate when necessary.\n\n** Example IUP 3.25 to 3.26\n\nAmong other changes, IUP 3.26 introduced [[http://webserver2.tecgraf.puc-rio.br/iup/en/elem/iupmultibox.html][IupMultiBox]] as a new control\ncontainer with 19 attributes and defaults. Regenerating\n~classesdb.lisp-sexp~ automatically collected these changes so that\nthe corresponding Lisp function ~IUP:MULTIBOX~ is created and exported\nautomatically from the ~IUP~ package.\n\n* Interactive Development\n\nTBD\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flispnik%2Fiup","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flispnik%2Fiup","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flispnik%2Fiup/lists"}