From 8366314611bf30a0f31d25bf5f5023186fa87692 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Sat, 24 Mar 2007 18:52:31 +0000 Subject: [PATCH] [svn-inject] Installing original source of ion3 --- ChangeLog | 9624 ++++++++++++++++++++ LICENSE | 510 ++ Makefile | 36 + README | 137 + RELNOTES | 1048 +++ TODO.README | 5 + TODO.riot | 759 ++ build/ac/README.autoconf | 45 + build/ac/aclocal.m4 | 57 + build/ac/configure.ac | 440 + build/ac/system-ac.mk.in | 190 + build/libs.mk | 16 + build/mkman.lua | 306 + build/mkpreload.lua | 32 + build/rules.mk | 204 + build/system-inc.mk | 18 + config.h | 49 + de/Makefile | 26 + de/brush.c | 203 + de/brush.h | 108 + de/colour.c | 74 + de/colour.h | 41 + de/draw.c | 576 ++ de/font.c | 280 + de/font.h | 58 + de/fontset.c | 186 + de/fontset.h | 20 + de/init.c | 369 + de/init.h | 41 + de/private.h | 21 + de/style.c | 325 + de/style.h | 97 + doc/ChangeLog | 667 ++ doc/LICENSE | 340 + doc/Makefile | 136 + doc/README | 23 + doc/artikel3.perl | 12 + doc/conf-bindings.tex | 269 + doc/conf-menus.tex | 120 + doc/conf-winprops.tex | 279 + doc/conf.tex | 178 + doc/confintro.tex | 52 + doc/cstyle.tex | 118 + doc/de.tex | 388 + doc/designnotes.tex | 54 + doc/exact-version | 5 + doc/fnref.tex | 38 + doc/fullhierarchy.tex | 43 + doc/gpl.tex | 416 + doc/hookref.tex | 241 + doc/ionconf.dvi.gz | Bin 0 -> 117932 bytes doc/ionconf.out | 82 + doc/ionconf.ps.gz | Bin 0 -> 238432 bytes doc/ionconf.tex | 73 + doc/ionconf/WARNINGS | 8 + doc/ionconf/contents.png | Bin 0 -> 278 bytes doc/ionconf/images.aux | 19 + doc/ionconf/images.idx | 0 doc/ionconf/images.log | 396 + doc/ionconf/images.out | 0 doc/ionconf/images.pl | 6 + doc/ionconf/images.tex | 371 + doc/ionconf/img1.png | 0 doc/ionconf/img2.png | 0 doc/ionconf/img3.png | 0 doc/ionconf/img4.png | 0 doc/ionconf/img5.png | 0 doc/ionconf/img6.png | 0 doc/ionconf/index.html | 341 + doc/ionconf/index.png | Bin 0 -> 246 bytes doc/ionconf/internals.pl | 1306 +++ doc/ionconf/ionconf.css | 38 + doc/ionconf/ionconf.html | 341 + doc/ionconf/labels.pl | 2613 ++++++ doc/ionconf/next.png | Bin 0 -> 245 bytes doc/ionconf/next_g.png | Bin 0 -> 272 bytes doc/ionconf/node1.html | 276 + doc/ionconf/node10.html | 377 + doc/ionconf/node11.html | 1091 +++ doc/ionconf/node12.html | 77 + doc/ionconf/node2.html | 163 + doc/ionconf/node3.html | 543 ++ doc/ionconf/node4.html | 1369 +++ doc/ionconf/node5.html | 720 ++ doc/ionconf/node6.html | 351 + doc/ionconf/node7.html | 6232 +++++++++++++ doc/ionconf/node8.html | 597 ++ doc/ionconf/node9.html | 103 + doc/ionconf/prev.png | Bin 0 -> 279 bytes doc/ionconf/prev_g.png | Bin 0 -> 327 bytes doc/ionconf/up.png | Bin 0 -> 211 bytes doc/ionnotes.dvi.gz | Bin 0 -> 27907 bytes doc/ionnotes.out | 22 + doc/ionnotes.ps.gz | Bin 0 -> 109113 bytes doc/ionnotes.tex | 60 + doc/ionnotes/WARNINGS | 8 + doc/ionnotes/contents.png | Bin 0 -> 278 bytes doc/ionnotes/crossref.png | Bin 0 -> 147 bytes doc/ionnotes/index.html | 172 + doc/ionnotes/index.png | Bin 0 -> 246 bytes doc/ionnotes/internals.pl | 34 + doc/ionnotes/ionnotes.css | 37 + doc/ionnotes/ionnotes.html | 172 + doc/ionnotes/labels.pl | 69 + doc/ionnotes/next.png | Bin 0 -> 245 bytes doc/ionnotes/next_g.png | Bin 0 -> 272 bytes doc/ionnotes/node1.html | 123 + doc/ionnotes/node2.html | 444 + doc/ionnotes/node3.html | 121 + doc/ionnotes/node4.html | 310 + doc/ionnotes/node5.html | 188 + doc/ionnotes/node6.html | 285 + doc/ionnotes/node7.html | 597 ++ doc/ionnotes/node8.html | 139 + doc/ionnotes/node9.html | 77 + doc/ionnotes/prev.png | Bin 0 -> 279 bytes doc/ionnotes/prev_g.png | Bin 0 -> 327 bytes doc/ionnotes/up.png | Bin 0 -> 211 bytes doc/luaif.tex | 148 + doc/macros.tex | 151 + doc/objects.tex | 246 + doc/objectsimpl.tex | 20 + doc/predist.sh | 39 + doc/prelim.tex | 58 + doc/rapport3.perl | 38 + doc/statusd.tex | 81 + doc/tricks.tex | 104 + etc/Makefile | 27 + etc/cfg_dock.lua | 51 + etc/cfg_ion.lua | 56 + etc/cfg_ioncore.lua | 377 + etc/cfg_kludges.lua | 46 + etc/cfg_menu.lua | 32 + etc/cfg_modules.lua | 11 + etc/cfg_panews.lua | 57 + etc/cfg_query.lua | 117 + etc/cfg_statusbar.lua | 87 + etc/cfg_tiling.lua | 146 + etc/look_brownsteel.lua | 108 + etc/look_clean.lua | 102 + etc/look_cleanios.lua | 85 + etc/look_cleanviolet.lua | 100 + etc/look_dusky.lua | 105 + etc/look_greyviolet.lua | 98 + etc/look_ios.lua | 104 + etc/look_simpleblue.lua | 112 + etc/lookcommon_clean.lua | 60 + etc/lookcommon_emboss.lua | 57 + exact-version | 5 + install-sh | 251 + ion/Makefile | 61 + ion/ion.c | 293 + ioncore/Makefile | 53 + ioncore/activity.c | 172 + ioncore/activity.h | 31 + ioncore/attach.c | 165 + ioncore/attach.h | 59 + ioncore/basicpholder.c | 122 + ioncore/basicpholder.h | 46 + ioncore/binding.c | 745 ++ ioncore/binding.h | 92 + ioncore/bindmaps.c | 200 + ioncore/bindmaps.h | 46 + ioncore/classes.h | 47 + ioncore/clientwin.c | 1403 +++ ioncore/clientwin.h | 115 + ioncore/colormap.c | 249 + ioncore/colormap.h | 28 + ioncore/common.h | 31 + ioncore/conf-bindings.c | 505 + ioncore/conf-bindings.h | 27 + ioncore/conf.c | 245 + ioncore/conf.h | 17 + ioncore/cursor.c | 38 + ioncore/cursor.h | 28 + ioncore/dummywc.h | 55 + ioncore/event.c | 239 + ioncore/event.h | 52 + ioncore/eventh.c | 534 ++ ioncore/eventh.h | 32 + ioncore/exec.c | 286 + ioncore/exec.h | 47 + ioncore/extlconv.c | 115 + ioncore/extlconv.h | 34 + ioncore/extlrx.c | 55 + ioncore/float-placement.c | 170 + ioncore/float-placement.h | 27 + ioncore/focus.c | 435 + ioncore/focus.h | 64 + ioncore/frame-draw.c | 481 + ioncore/frame-draw.h | 38 + ioncore/frame-pointer.c | 388 + ioncore/frame-pointer.h | 28 + ioncore/frame.c | 927 ++ ioncore/frame.h | 128 + ioncore/framedpholder.c | 217 + ioncore/framedpholder.h | 60 + ioncore/framep.h | 39 + ioncore/fullscreen.c | 212 + ioncore/fullscreen.h | 32 + ioncore/global.h | 93 + ioncore/gr.c | 443 + ioncore/gr.h | 165 + ioncore/grab.c | 289 + ioncore/grab.h | 43 + ioncore/group-cw.c | 302 + ioncore/group-cw.h | 45 + ioncore/group-ws.c | 430 + ioncore/group-ws.h | 49 + ioncore/group.c | 1361 +++ ioncore/group.h | 133 + ioncore/groupedpholder.c | 182 + ioncore/groupedpholder.h | 39 + ioncore/grouppholder.c | 199 + ioncore/grouppholder.h | 49 + ioncore/infowin.c | 275 + ioncore/infowin.h | 47 + ioncore/ioncore.c | 662 ++ ioncore/ioncore.h | 37 + ioncore/ioncore_bindings.lua | 233 + ioncore/ioncore_efbb.lua | 37 + ioncore/ioncore_ext.lua | 94 + ioncore/ioncore_luaext.lua | 92 + ioncore/ioncore_menudb.lua | 354 + ioncore/ioncore_misc.lua | 53 + ioncore/ioncore_wd.lua | 167 + ioncore/ioncore_winprops.lua | 129 + ioncore/kbresize.c | 380 + ioncore/kbresize.h | 34 + ioncore/key.c | 279 + ioncore/key.h | 24 + ioncore/llist.c | 100 + ioncore/llist.h | 56 + ioncore/manage.c | 284 + ioncore/manage.h | 93 + ioncore/modules.c | 352 + ioncore/modules.h | 28 + ioncore/mplex.c | 2083 +++++ ioncore/mplex.h | 245 + ioncore/mplexpholder.c | 313 + ioncore/mplexpholder.h | 67 + ioncore/mwmhints.c | 60 + ioncore/mwmhints.h | 63 + ioncore/names.c | 614 ++ ioncore/names.h | 51 + ioncore/navi.c | 327 + ioncore/navi.h | 52 + ioncore/netwm.c | 255 + ioncore/netwm.h | 34 + ioncore/pholder.c | 210 + ioncore/pholder.h | 63 + ioncore/pointer.c | 349 + ioncore/pointer.h | 36 + ioncore/presize.c | 177 + ioncore/presize.h | 22 + ioncore/property.c | 441 + ioncore/property.h | 32 + ioncore/rectangle.c | 48 + ioncore/rectangle.h | 41 + ioncore/regbind.c | 337 + ioncore/regbind.h | 49 + ioncore/reginfo.c | 92 + ioncore/reginfo.h | 43 + ioncore/region-iter.h | 37 + ioncore/region.c | 914 ++ ioncore/region.h | 193 + ioncore/resize.c | 853 ++ ioncore/resize.h | 150 + ioncore/rootwin.c | 625 ++ ioncore/rootwin.h | 50 + ioncore/saveload.c | 304 + ioncore/saveload.h | 43 + ioncore/screen.c | 732 ++ ioncore/screen.h | 83 + ioncore/selection.c | 187 + ioncore/selection.h | 24 + ioncore/sizehint.c | 311 + ioncore/sizehint.h | 54 + ioncore/sizepolicy.c | 340 + ioncore/sizepolicy.h | 78 + ioncore/stacking.c | 617 ++ ioncore/stacking.h | 108 + ioncore/strings.c | 470 + ioncore/strings.h | 37 + ioncore/tags.c | 134 + ioncore/tags.h | 25 + ioncore/window.c | 247 + ioncore/window.h | 57 + ioncore/xic.c | 97 + ioncore/xic.h | 24 + ioncore/xwindow.c | 140 + ioncore/xwindow.h | 37 + libextl/LICENSE | 510 ++ libextl/Makefile | 42 + libextl/README | 116 + libextl/build/system-inc.mk | 2 + libextl/exact-version | 81 + libextl/extl.h | 26 + libextl/install-sh | 251 + libextl/libextl-mkexports.in | 803 ++ libextl/luaextl.c | 2439 +++++ libextl/luaextl.h | 171 + libextl/misc.c | 23 + libextl/private.h | 115 + libextl/readconfig.c | 505 + libextl/readconfig.h | 51 + libextl/types.h | 32 + libmainloop/Makefile | 31 + libmainloop/defer.c | 198 + libmainloop/defer.h | 35 + libmainloop/exec.c | 308 + libmainloop/exec.h | 37 + libmainloop/hooks.c | 423 + libmainloop/hooks.h | 67 + libmainloop/rx.mk | 11 + libmainloop/select.c | 110 + libmainloop/select.h | 36 + libmainloop/signal.c | 529 ++ libmainloop/signal.h | 60 + libtu/LICENSE | 640 ++ libtu/Makefile | 58 + libtu/README | 31 + libtu/README.rb | 113 + libtu/build/system-inc.mk | 2 + libtu/debug.h | 19 + libtu/dlist.h | 124 + libtu/errorlog.c | 127 + libtu/errorlog.h | 38 + libtu/exact-version | 56 + libtu/install-sh | 251 + libtu/iterable.c | 51 + libtu/iterable.h | 29 + libtu/locale.h | 27 + libtu/map.c | 47 + libtu/map.h | 27 + libtu/minmax.h | 27 + libtu/misc.c | 211 + libtu/misc.h | 43 + libtu/np-conv.h | 121 + libtu/np/np-conv.h | 121 + libtu/np/numparser2.h | 272 + libtu/numparser2.h | 272 + libtu/obj.c | 297 + libtu/obj.h | 69 + libtu/objlist.c | 326 + libtu/objlist.h | 60 + libtu/objp.h | 72 + libtu/optparser.c | 471 + libtu/optparser.h | 78 + libtu/output.c | 405 + libtu/output.h | 76 + libtu/parser.c | 717 ++ libtu/parser.h | 54 + libtu/pointer.h | 16 + libtu/private.h | 27 + libtu/ptrlist.c | 202 + libtu/ptrlist.h | 57 + libtu/rb-test.c | 98 + libtu/rb.c | 626 ++ libtu/rb.h | 143 + libtu/setparam.c | 60 + libtu/setparam.h | 27 + libtu/snprintf_2.2/INSTALL | 24 + libtu/snprintf_2.2/LICENSE.txt | 121 + libtu/snprintf_2.2/Makefile.unused | 43 + libtu/snprintf_2.2/README | 283 + libtu/snprintf_2.2/README.html | 382 + libtu/snprintf_2.2/snprintf-orig.c | 1025 +++ libtu/snprintf_2.2/snprintf.c | 1032 +++ libtu/snprintf_2.2/snprintf.h | 26 + libtu/snprintf_2.2/test.c | 689 ++ libtu/stringstore.c | 98 + libtu/stringstore.h | 22 + libtu/tester.c | 54 + libtu/tester2.c | 69 + libtu/tester3.c | 72 + libtu/tokenizer.c | 958 ++ libtu/tokenizer.h | 212 + libtu/types.h | 86 + libtu/util.c | 37 + libtu/util.h | 23 + man/Makefile | 70 + man/ion3.cs.in | 169 + man/ion3.de.in | 164 + man/ion3.fi.in | 169 + man/ion3.in | 167 + man/pwm3.cs.in | 110 + man/pwm3.de.in | 111 + man/pwm3.fi.in | 107 + man/pwm3.in | 108 + man/welcome.cs.head | 26 + man/welcome.de.head | 22 + man/welcome.fi.head | 25 + man/welcome.head | 26 + mod_dock/LICENSE | 504 + mod_dock/Makefile | 35 + mod_dock/README.dock | 98 + mod_dock/dock.c | 1701 ++++ mod_menu/Makefile | 27 + mod_menu/grabmenu.c | 89 + mod_menu/main.c | 73 + mod_menu/main.h | 23 + mod_menu/menu.c | 1375 +++ mod_menu/menu.h | 95 + mod_menu/mkmenu.c | 127 + mod_menu/mkmenu.h | 23 + mod_menu/mod_menu.lua | 108 + mod_mgmtmode/Makefile | 26 + mod_mgmtmode/main.c | 71 + mod_mgmtmode/main.h | 22 + mod_mgmtmode/mgmtmode.c | 222 + mod_mgmtmode/mgmtmode.h | 33 + mod_panews/Makefile | 27 + mod_panews/main.c | 141 + mod_panews/main.h | 25 + mod_panews/mod_panews.lua | 562 ++ mod_panews/panews.c | 466 + mod_panews/panews.h | 47 + mod_panews/placement.c | 320 + mod_panews/placement.h | 55 + mod_panews/splitext.c | 617 ++ mod_panews/splitext.h | 55 + mod_panews/unusedwin.c | 211 + mod_panews/unusedwin.h | 29 + mod_query/Makefile | 29 + mod_query/complete.c | 206 + mod_query/complete.h | 42 + mod_query/edln.c | 779 ++ mod_query/edln.h | 75 + mod_query/fwarn.c | 67 + mod_query/fwarn.h | 21 + mod_query/history.c | 207 + mod_query/history.h | 26 + mod_query/input.c | 206 + mod_query/input.h | 42 + mod_query/inputp.h | 28 + mod_query/listing.c | 529 ++ mod_query/listing.h | 50 + mod_query/main.c | 191 + mod_query/main.h | 28 + mod_query/mod_query.lua | 1217 +++ mod_query/mod_query_chdir.lua | 26 + mod_query/query.c | 94 + mod_query/query.h | 24 + mod_query/wedln-wrappers.c | 278 + mod_query/wedln.c | 1072 +++ mod_query/wedln.h | 81 + mod_query/wmessage.c | 231 + mod_query/wmessage.h | 32 + mod_sm/Makefile | 27 + mod_sm/sm.c | 158 + mod_sm/sm_matchwin.c | 309 + mod_sm/sm_matchwin.h | 47 + mod_sm/sm_session.c | 413 + mod_sm/sm_session.h | 23 + mod_sp/Makefile | 27 + mod_sp/cfg_sp.lua | 19 + mod_sp/main.c | 206 + mod_sp/main.h | 25 + mod_statusbar/Makefile | 34 + mod_statusbar/draw.c | 183 + mod_statusbar/draw.h | 21 + mod_statusbar/ion-statusd/Makefile | 40 + mod_statusbar/ion-statusd/exec.c | 39 + mod_statusbar/ion-statusd/extlrx.c | 42 + mod_statusbar/ion-statusd/ion-statusd.c | 291 + mod_statusbar/ion-statusd/statusd_date.lua | 41 + mod_statusbar/ion-statusd/statusd_load.lua | 99 + mod_statusbar/ion-statusd/statusd_mail.lua | 156 + mod_statusbar/main.c | 312 + mod_statusbar/main.h | 23 + mod_statusbar/mod_statusbar.lua | 340 + mod_statusbar/statusbar.c | 1092 +++ mod_statusbar/statusbar.h | 94 + mod_tiling/Makefile | 30 + mod_tiling/main.c | 157 + mod_tiling/main.h | 26 + mod_tiling/ops.c | 190 + mod_tiling/panehandle.c | 139 + mod_tiling/panehandle.h | 34 + mod_tiling/placement.c | 113 + mod_tiling/placement.h | 38 + mod_tiling/split-stdisp.c | 747 ++ mod_tiling/split-stdisp.h | 24 + mod_tiling/split.c | 2004 ++++ mod_tiling/split.h | 221 + mod_tiling/splitfloat.c | 1009 ++ mod_tiling/splitfloat.h | 56 + mod_tiling/tiling.c | 1764 ++++ mod_tiling/tiling.h | 127 + modulelist.mk | 11 + po/Makefile | 96 + po/README | 14 + po/cs.po | 1716 ++++ po/de.po | 1667 ++++ po/fi.po | 1760 ++++ po/ru.po | 1637 ++++ pwm/Makefile | 73 + pwm/cfg_pwm.lua | 97 + pwm/pwm.c | 241 + system.mk | 195 + utils/Makefile | 33 + utils/ion-completefile/Makefile | 30 + utils/ion-completefile/ion-completefile.c | 711 ++ utils/ion-completeman.in | 123 + utils/ion-runinxterm | 43 + version.h | 2 + 507 files changed, 125037 insertions(+) create mode 100644 ChangeLog create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README create mode 100644 RELNOTES create mode 100644 TODO.README create mode 100644 TODO.riot create mode 100644 build/ac/README.autoconf create mode 100644 build/ac/aclocal.m4 create mode 100644 build/ac/configure.ac create mode 100644 build/ac/system-ac.mk.in create mode 100644 build/libs.mk create mode 100644 build/mkman.lua create mode 100644 build/mkpreload.lua create mode 100644 build/rules.mk create mode 100644 build/system-inc.mk create mode 100644 config.h create mode 100644 de/Makefile create mode 100644 de/brush.c create mode 100644 de/brush.h create mode 100644 de/colour.c create mode 100644 de/colour.h create mode 100644 de/draw.c create mode 100644 de/font.c create mode 100644 de/font.h create mode 100644 de/fontset.c create mode 100644 de/fontset.h create mode 100644 de/init.c create mode 100644 de/init.h create mode 100644 de/private.h create mode 100644 de/style.c create mode 100644 de/style.h create mode 100644 doc/ChangeLog create mode 100644 doc/LICENSE create mode 100644 doc/Makefile create mode 100644 doc/README create mode 100644 doc/artikel3.perl create mode 100644 doc/conf-bindings.tex create mode 100644 doc/conf-menus.tex create mode 100644 doc/conf-winprops.tex create mode 100644 doc/conf.tex create mode 100644 doc/confintro.tex create mode 100644 doc/cstyle.tex create mode 100644 doc/de.tex create mode 100644 doc/designnotes.tex create mode 100644 doc/exact-version create mode 100644 doc/fnref.tex create mode 100644 doc/fullhierarchy.tex create mode 100644 doc/gpl.tex create mode 100644 doc/hookref.tex create mode 100644 doc/ionconf.dvi.gz create mode 100644 doc/ionconf.out create mode 100644 doc/ionconf.ps.gz create mode 100644 doc/ionconf.tex create mode 100644 doc/ionconf/WARNINGS create mode 100644 doc/ionconf/contents.png create mode 100644 doc/ionconf/images.aux create mode 100644 doc/ionconf/images.idx create mode 100644 doc/ionconf/images.log create mode 100644 doc/ionconf/images.out create mode 100644 doc/ionconf/images.pl create mode 100644 doc/ionconf/images.tex create mode 100644 doc/ionconf/img1.png create mode 100644 doc/ionconf/img2.png create mode 100644 doc/ionconf/img3.png create mode 100644 doc/ionconf/img4.png create mode 100644 doc/ionconf/img5.png create mode 100644 doc/ionconf/img6.png create mode 100644 doc/ionconf/index.html create mode 100644 doc/ionconf/index.png create mode 100644 doc/ionconf/internals.pl create mode 100644 doc/ionconf/ionconf.css create mode 100644 doc/ionconf/ionconf.html create mode 100644 doc/ionconf/labels.pl create mode 100644 doc/ionconf/next.png create mode 100644 doc/ionconf/next_g.png create mode 100644 doc/ionconf/node1.html create mode 100644 doc/ionconf/node10.html create mode 100644 doc/ionconf/node11.html create mode 100644 doc/ionconf/node12.html create mode 100644 doc/ionconf/node2.html create mode 100644 doc/ionconf/node3.html create mode 100644 doc/ionconf/node4.html create mode 100644 doc/ionconf/node5.html create mode 100644 doc/ionconf/node6.html create mode 100644 doc/ionconf/node7.html create mode 100644 doc/ionconf/node8.html create mode 100644 doc/ionconf/node9.html create mode 100644 doc/ionconf/prev.png create mode 100644 doc/ionconf/prev_g.png create mode 100644 doc/ionconf/up.png create mode 100644 doc/ionnotes.dvi.gz create mode 100644 doc/ionnotes.out create mode 100644 doc/ionnotes.ps.gz create mode 100644 doc/ionnotes.tex create mode 100644 doc/ionnotes/WARNINGS create mode 100644 doc/ionnotes/contents.png create mode 100644 doc/ionnotes/crossref.png create mode 100644 doc/ionnotes/index.html create mode 100644 doc/ionnotes/index.png create mode 100644 doc/ionnotes/internals.pl create mode 100644 doc/ionnotes/ionnotes.css create mode 100644 doc/ionnotes/ionnotes.html create mode 100644 doc/ionnotes/labels.pl create mode 100644 doc/ionnotes/next.png create mode 100644 doc/ionnotes/next_g.png create mode 100644 doc/ionnotes/node1.html create mode 100644 doc/ionnotes/node2.html create mode 100644 doc/ionnotes/node3.html create mode 100644 doc/ionnotes/node4.html create mode 100644 doc/ionnotes/node5.html create mode 100644 doc/ionnotes/node6.html create mode 100644 doc/ionnotes/node7.html create mode 100644 doc/ionnotes/node8.html create mode 100644 doc/ionnotes/node9.html create mode 100644 doc/ionnotes/prev.png create mode 100644 doc/ionnotes/prev_g.png create mode 100644 doc/ionnotes/up.png create mode 100644 doc/luaif.tex create mode 100644 doc/macros.tex create mode 100644 doc/objects.tex create mode 100644 doc/objectsimpl.tex create mode 100644 doc/predist.sh create mode 100644 doc/prelim.tex create mode 100644 doc/rapport3.perl create mode 100644 doc/statusd.tex create mode 100644 doc/tricks.tex create mode 100644 etc/Makefile create mode 100644 etc/cfg_dock.lua create mode 100644 etc/cfg_ion.lua create mode 100644 etc/cfg_ioncore.lua create mode 100644 etc/cfg_kludges.lua create mode 100644 etc/cfg_menu.lua create mode 100644 etc/cfg_modules.lua create mode 100644 etc/cfg_panews.lua create mode 100644 etc/cfg_query.lua create mode 100644 etc/cfg_statusbar.lua create mode 100644 etc/cfg_tiling.lua create mode 100644 etc/look_brownsteel.lua create mode 100644 etc/look_clean.lua create mode 100644 etc/look_cleanios.lua create mode 100644 etc/look_cleanviolet.lua create mode 100644 etc/look_dusky.lua create mode 100644 etc/look_greyviolet.lua create mode 100644 etc/look_ios.lua create mode 100644 etc/look_simpleblue.lua create mode 100644 etc/lookcommon_clean.lua create mode 100644 etc/lookcommon_emboss.lua create mode 100644 exact-version create mode 100755 install-sh create mode 100644 ion/Makefile create mode 100644 ion/ion.c create mode 100644 ioncore/Makefile create mode 100644 ioncore/activity.c create mode 100644 ioncore/activity.h create mode 100644 ioncore/attach.c create mode 100644 ioncore/attach.h create mode 100644 ioncore/basicpholder.c create mode 100644 ioncore/basicpholder.h create mode 100644 ioncore/binding.c create mode 100644 ioncore/binding.h create mode 100644 ioncore/bindmaps.c create mode 100644 ioncore/bindmaps.h create mode 100644 ioncore/classes.h create mode 100644 ioncore/clientwin.c create mode 100644 ioncore/clientwin.h create mode 100644 ioncore/colormap.c create mode 100644 ioncore/colormap.h create mode 100644 ioncore/common.h create mode 100644 ioncore/conf-bindings.c create mode 100644 ioncore/conf-bindings.h create mode 100644 ioncore/conf.c create mode 100644 ioncore/conf.h create mode 100644 ioncore/cursor.c create mode 100644 ioncore/cursor.h create mode 100644 ioncore/dummywc.h create mode 100644 ioncore/event.c create mode 100644 ioncore/event.h create mode 100644 ioncore/eventh.c create mode 100644 ioncore/eventh.h create mode 100644 ioncore/exec.c create mode 100644 ioncore/exec.h create mode 100644 ioncore/extlconv.c create mode 100644 ioncore/extlconv.h create mode 100644 ioncore/extlrx.c create mode 100644 ioncore/float-placement.c create mode 100644 ioncore/float-placement.h create mode 100644 ioncore/focus.c create mode 100644 ioncore/focus.h create mode 100644 ioncore/frame-draw.c create mode 100644 ioncore/frame-draw.h create mode 100644 ioncore/frame-pointer.c create mode 100644 ioncore/frame-pointer.h create mode 100644 ioncore/frame.c create mode 100644 ioncore/frame.h create mode 100644 ioncore/framedpholder.c create mode 100644 ioncore/framedpholder.h create mode 100644 ioncore/framep.h create mode 100644 ioncore/fullscreen.c create mode 100644 ioncore/fullscreen.h create mode 100644 ioncore/global.h create mode 100644 ioncore/gr.c create mode 100644 ioncore/gr.h create mode 100644 ioncore/grab.c create mode 100644 ioncore/grab.h create mode 100644 ioncore/group-cw.c create mode 100644 ioncore/group-cw.h create mode 100644 ioncore/group-ws.c create mode 100644 ioncore/group-ws.h create mode 100644 ioncore/group.c create mode 100644 ioncore/group.h create mode 100644 ioncore/groupedpholder.c create mode 100644 ioncore/groupedpholder.h create mode 100644 ioncore/grouppholder.c create mode 100644 ioncore/grouppholder.h create mode 100644 ioncore/infowin.c create mode 100644 ioncore/infowin.h create mode 100644 ioncore/ioncore.c create mode 100644 ioncore/ioncore.h create mode 100644 ioncore/ioncore_bindings.lua create mode 100644 ioncore/ioncore_efbb.lua create mode 100644 ioncore/ioncore_ext.lua create mode 100644 ioncore/ioncore_luaext.lua create mode 100644 ioncore/ioncore_menudb.lua create mode 100644 ioncore/ioncore_misc.lua create mode 100644 ioncore/ioncore_wd.lua create mode 100644 ioncore/ioncore_winprops.lua create mode 100644 ioncore/kbresize.c create mode 100644 ioncore/kbresize.h create mode 100644 ioncore/key.c create mode 100644 ioncore/key.h create mode 100644 ioncore/llist.c create mode 100644 ioncore/llist.h create mode 100644 ioncore/manage.c create mode 100644 ioncore/manage.h create mode 100644 ioncore/modules.c create mode 100644 ioncore/modules.h create mode 100644 ioncore/mplex.c create mode 100644 ioncore/mplex.h create mode 100644 ioncore/mplexpholder.c create mode 100644 ioncore/mplexpholder.h create mode 100644 ioncore/mwmhints.c create mode 100644 ioncore/mwmhints.h create mode 100644 ioncore/names.c create mode 100644 ioncore/names.h create mode 100644 ioncore/navi.c create mode 100644 ioncore/navi.h create mode 100644 ioncore/netwm.c create mode 100644 ioncore/netwm.h create mode 100644 ioncore/pholder.c create mode 100644 ioncore/pholder.h create mode 100644 ioncore/pointer.c create mode 100644 ioncore/pointer.h create mode 100644 ioncore/presize.c create mode 100644 ioncore/presize.h create mode 100644 ioncore/property.c create mode 100644 ioncore/property.h create mode 100644 ioncore/rectangle.c create mode 100644 ioncore/rectangle.h create mode 100644 ioncore/regbind.c create mode 100644 ioncore/regbind.h create mode 100644 ioncore/reginfo.c create mode 100644 ioncore/reginfo.h create mode 100644 ioncore/region-iter.h create mode 100644 ioncore/region.c create mode 100644 ioncore/region.h create mode 100644 ioncore/resize.c create mode 100644 ioncore/resize.h create mode 100644 ioncore/rootwin.c create mode 100644 ioncore/rootwin.h create mode 100644 ioncore/saveload.c create mode 100644 ioncore/saveload.h create mode 100644 ioncore/screen.c create mode 100644 ioncore/screen.h create mode 100644 ioncore/selection.c create mode 100644 ioncore/selection.h create mode 100644 ioncore/sizehint.c create mode 100644 ioncore/sizehint.h create mode 100644 ioncore/sizepolicy.c create mode 100644 ioncore/sizepolicy.h create mode 100644 ioncore/stacking.c create mode 100644 ioncore/stacking.h create mode 100644 ioncore/strings.c create mode 100644 ioncore/strings.h create mode 100644 ioncore/tags.c create mode 100644 ioncore/tags.h create mode 100644 ioncore/window.c create mode 100644 ioncore/window.h create mode 100644 ioncore/xic.c create mode 100644 ioncore/xic.h create mode 100644 ioncore/xwindow.c create mode 100644 ioncore/xwindow.h create mode 100644 libextl/LICENSE create mode 100644 libextl/Makefile create mode 100644 libextl/README create mode 100644 libextl/build/system-inc.mk create mode 100644 libextl/exact-version create mode 100644 libextl/extl.h create mode 100644 libextl/install-sh create mode 100644 libextl/libextl-mkexports.in create mode 100644 libextl/luaextl.c create mode 100644 libextl/luaextl.h create mode 100644 libextl/misc.c create mode 100644 libextl/private.h create mode 100644 libextl/readconfig.c create mode 100644 libextl/readconfig.h create mode 100644 libextl/types.h create mode 100644 libmainloop/Makefile create mode 100644 libmainloop/defer.c create mode 100644 libmainloop/defer.h create mode 100644 libmainloop/exec.c create mode 100644 libmainloop/exec.h create mode 100644 libmainloop/hooks.c create mode 100644 libmainloop/hooks.h create mode 100644 libmainloop/rx.mk create mode 100644 libmainloop/select.c create mode 100644 libmainloop/select.h create mode 100644 libmainloop/signal.c create mode 100644 libmainloop/signal.h create mode 100644 libtu/LICENSE create mode 100644 libtu/Makefile create mode 100644 libtu/README create mode 100644 libtu/README.rb create mode 100644 libtu/build/system-inc.mk create mode 100644 libtu/debug.h create mode 100644 libtu/dlist.h create mode 100644 libtu/errorlog.c create mode 100644 libtu/errorlog.h create mode 100644 libtu/exact-version create mode 100644 libtu/install-sh create mode 100644 libtu/iterable.c create mode 100644 libtu/iterable.h create mode 100644 libtu/locale.h create mode 100644 libtu/map.c create mode 100644 libtu/map.h create mode 100644 libtu/minmax.h create mode 100644 libtu/misc.c create mode 100644 libtu/misc.h create mode 100644 libtu/np-conv.h create mode 100644 libtu/np/np-conv.h create mode 100644 libtu/np/numparser2.h create mode 100644 libtu/numparser2.h create mode 100644 libtu/obj.c create mode 100644 libtu/obj.h create mode 100644 libtu/objlist.c create mode 100644 libtu/objlist.h create mode 100644 libtu/objp.h create mode 100644 libtu/optparser.c create mode 100644 libtu/optparser.h create mode 100644 libtu/output.c create mode 100644 libtu/output.h create mode 100644 libtu/parser.c create mode 100644 libtu/parser.h create mode 100644 libtu/pointer.h create mode 100644 libtu/private.h create mode 100644 libtu/ptrlist.c create mode 100644 libtu/ptrlist.h create mode 100644 libtu/rb-test.c create mode 100644 libtu/rb.c create mode 100644 libtu/rb.h create mode 100644 libtu/setparam.c create mode 100644 libtu/setparam.h create mode 100644 libtu/snprintf_2.2/INSTALL create mode 100644 libtu/snprintf_2.2/LICENSE.txt create mode 100644 libtu/snprintf_2.2/Makefile.unused create mode 100644 libtu/snprintf_2.2/README create mode 100644 libtu/snprintf_2.2/README.html create mode 100644 libtu/snprintf_2.2/snprintf-orig.c create mode 100644 libtu/snprintf_2.2/snprintf.c create mode 100644 libtu/snprintf_2.2/snprintf.h create mode 100644 libtu/snprintf_2.2/test.c create mode 100644 libtu/stringstore.c create mode 100644 libtu/stringstore.h create mode 100644 libtu/tester.c create mode 100644 libtu/tester2.c create mode 100644 libtu/tester3.c create mode 100644 libtu/tokenizer.c create mode 100644 libtu/tokenizer.h create mode 100644 libtu/types.h create mode 100644 libtu/util.c create mode 100644 libtu/util.h create mode 100644 man/Makefile create mode 100644 man/ion3.cs.in create mode 100644 man/ion3.de.in create mode 100644 man/ion3.fi.in create mode 100644 man/ion3.in create mode 100644 man/pwm3.cs.in create mode 100644 man/pwm3.de.in create mode 100644 man/pwm3.fi.in create mode 100644 man/pwm3.in create mode 100644 man/welcome.cs.head create mode 100644 man/welcome.de.head create mode 100644 man/welcome.fi.head create mode 100644 man/welcome.head create mode 100644 mod_dock/LICENSE create mode 100644 mod_dock/Makefile create mode 100644 mod_dock/README.dock create mode 100644 mod_dock/dock.c create mode 100644 mod_menu/Makefile create mode 100644 mod_menu/grabmenu.c create mode 100644 mod_menu/main.c create mode 100644 mod_menu/main.h create mode 100644 mod_menu/menu.c create mode 100644 mod_menu/menu.h create mode 100644 mod_menu/mkmenu.c create mode 100644 mod_menu/mkmenu.h create mode 100644 mod_menu/mod_menu.lua create mode 100644 mod_mgmtmode/Makefile create mode 100644 mod_mgmtmode/main.c create mode 100644 mod_mgmtmode/main.h create mode 100644 mod_mgmtmode/mgmtmode.c create mode 100644 mod_mgmtmode/mgmtmode.h create mode 100644 mod_panews/Makefile create mode 100644 mod_panews/main.c create mode 100644 mod_panews/main.h create mode 100644 mod_panews/mod_panews.lua create mode 100644 mod_panews/panews.c create mode 100644 mod_panews/panews.h create mode 100644 mod_panews/placement.c create mode 100644 mod_panews/placement.h create mode 100644 mod_panews/splitext.c create mode 100644 mod_panews/splitext.h create mode 100644 mod_panews/unusedwin.c create mode 100644 mod_panews/unusedwin.h create mode 100644 mod_query/Makefile create mode 100644 mod_query/complete.c create mode 100644 mod_query/complete.h create mode 100644 mod_query/edln.c create mode 100644 mod_query/edln.h create mode 100644 mod_query/fwarn.c create mode 100644 mod_query/fwarn.h create mode 100644 mod_query/history.c create mode 100644 mod_query/history.h create mode 100644 mod_query/input.c create mode 100644 mod_query/input.h create mode 100644 mod_query/inputp.h create mode 100644 mod_query/listing.c create mode 100644 mod_query/listing.h create mode 100644 mod_query/main.c create mode 100644 mod_query/main.h create mode 100644 mod_query/mod_query.lua create mode 100644 mod_query/mod_query_chdir.lua create mode 100644 mod_query/query.c create mode 100644 mod_query/query.h create mode 100644 mod_query/wedln-wrappers.c create mode 100644 mod_query/wedln.c create mode 100644 mod_query/wedln.h create mode 100644 mod_query/wmessage.c create mode 100644 mod_query/wmessage.h create mode 100644 mod_sm/Makefile create mode 100644 mod_sm/sm.c create mode 100644 mod_sm/sm_matchwin.c create mode 100644 mod_sm/sm_matchwin.h create mode 100644 mod_sm/sm_session.c create mode 100644 mod_sm/sm_session.h create mode 100644 mod_sp/Makefile create mode 100644 mod_sp/cfg_sp.lua create mode 100644 mod_sp/main.c create mode 100644 mod_sp/main.h create mode 100644 mod_statusbar/Makefile create mode 100644 mod_statusbar/draw.c create mode 100644 mod_statusbar/draw.h create mode 100644 mod_statusbar/ion-statusd/Makefile create mode 100644 mod_statusbar/ion-statusd/exec.c create mode 100644 mod_statusbar/ion-statusd/extlrx.c create mode 100644 mod_statusbar/ion-statusd/ion-statusd.c create mode 100644 mod_statusbar/ion-statusd/statusd_date.lua create mode 100644 mod_statusbar/ion-statusd/statusd_load.lua create mode 100644 mod_statusbar/ion-statusd/statusd_mail.lua create mode 100644 mod_statusbar/main.c create mode 100644 mod_statusbar/main.h create mode 100644 mod_statusbar/mod_statusbar.lua create mode 100644 mod_statusbar/statusbar.c create mode 100644 mod_statusbar/statusbar.h create mode 100644 mod_tiling/Makefile create mode 100644 mod_tiling/main.c create mode 100644 mod_tiling/main.h create mode 100644 mod_tiling/ops.c create mode 100644 mod_tiling/panehandle.c create mode 100644 mod_tiling/panehandle.h create mode 100644 mod_tiling/placement.c create mode 100644 mod_tiling/placement.h create mode 100644 mod_tiling/split-stdisp.c create mode 100644 mod_tiling/split-stdisp.h create mode 100644 mod_tiling/split.c create mode 100644 mod_tiling/split.h create mode 100644 mod_tiling/splitfloat.c create mode 100644 mod_tiling/splitfloat.h create mode 100644 mod_tiling/tiling.c create mode 100644 mod_tiling/tiling.h create mode 100644 modulelist.mk create mode 100644 po/Makefile create mode 100644 po/README create mode 100644 po/cs.po create mode 100644 po/de.po create mode 100644 po/fi.po create mode 100644 po/ru.po create mode 100644 pwm/Makefile create mode 100644 pwm/cfg_pwm.lua create mode 100644 pwm/pwm.c create mode 100644 system.mk create mode 100644 utils/Makefile create mode 100644 utils/ion-completefile/Makefile create mode 100644 utils/ion-completefile/ion-completefile.c create mode 100644 utils/ion-completeman.in create mode 100644 utils/ion-runinxterm create mode 100644 version.h diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..43a323e --- /dev/null +++ b/ChangeLog @@ -0,0 +1,9624 @@ +2006-12-23 14:59 UTC Tuomo Valkonen + tagged ion-3ds-20061223 + +2006-12-23 14:59 UTC Tuomo Valkonen + * Release notes + +2006-12-23 11:06 UTC Tuomo Valkonen + * Bound 'toggle tag' (Mod1+T) in WMPlex.toplevel instead of WFrame.toplevel + - Works for FS stuff too now + +2006-12-23 11:06 UTC Tuomo Valkonen + * Added info window for tagging state of FS stuff + +2006-12-22 14:56 UTC Tuomo Valkonen + * More mysterious focus tuning + (Seems like the focus shit working varies by the sunspots.) + +2006-12-21 19:07 UTC Tuomo Valkonen + * Returned to old totally-out-of-order Focus/EnterWindow event processing. + - It seems the sucky Xlib doesn't let you do it otherwise. + +2006-12-20 15:32 UTC Tuomo Valkonen + * Yet more focus tracking hacks. + (Will one ever work?) + +2006-12-14 16:46 UTC Tuomo Valkonen + * The "float" winprop works on transients too now + +2006-12-14 16:33 UTC Tuomo Valkonen + * If sizehint winprops have been set, correct requested geometry to match these. + +2006-12-09 21:29 UTC Tuomo Valkonen + * Place new regions after the current and those with activity after it + - Controlled with frame_default_index parameter of ioncore.set. + To get the old default behaviour, set this to 'next'. + - This option obsoletes the frame_add_last option + +2006-12-09 11:24 UTC Tuomo Valkonen + * Added next/prev-always alternatives to cycle parameter to mod_query.complete. + - If these are used instead of next/prev, cycling occurs despite + history/normal completion mode switch. + +2006-12-02 18:43 UTC Tuomo Valkonen + * Fixed query history duplicate elimination code + +2006-11-23 22:17 UTC Tuomo Valkonen + * Increased query history size from 256 to 1024 + +2006-11-19 23:13 UTC Tuomo Valkonen + * Don't unmanage stdisp when switching to region that can't manage it + +2006-11-12 15:46 UTC Tuomo Valkonen + * Changed region_rqgeom parametrisation + - Gravity can now be passed down to the root relative ("absolute") + version, allowing removal of WClientWin specific checks/hacks. + +2006-11-12 12:47 UTC Tuomo Valkonen + * Detach tries to keep root-relative geometry unchanged + (For framed detach, the geometry of the frame is tried + to be kept unchanged from the origianl geomery of the + detached object.) + +2006-11-12 12:37 UTC Tuomo Valkonen + * Detach works on transients now + +2006-11-12 12:22 UTC Tuomo Valkonen + * Readjust cursor position for infobox + +2006-11-12 10:38 UTC Miroslav Kure + * Updated Czech translation + +2006-11-11 18:20 UTC Tuomo Valkonen + * statusd startup timeout tunning + (incl. typo fix) + +2006-11-11 15:39 UTC Tuomo Valkonen + * Updated Finnish translation + +2006-11-11 15:33 UTC Tuomo Valkonen + * Menu name translation hack + (These are hidden in strings in the configuration files.) + +2006-11-11 13:52 UTC Tuomo Valkonen + * Added info box for history completion mode into queries + +2006-11-11 12:01 UTC Tuomo Valkonen + * Warp in WMPlex.set_hidden + +2006-11-04 14:58 UTC Tuomo Valkonen + * Clear a few more flags of frame size hints + +2006-11-03 21:46 UTC Tuomo Valkonen + * Fixed mod_query.query_attachclient to attach the containing WGroupCW. + Also, if one does not exist (e.g. when attaching transients), one is + created. + +2006-11-03 21:35 UTC Tuomo Valkonen + * Made pholder_do_attach return region instead of boolean. + +2006-11-03 07:52 UTC Tuomo Valkonen + * Fixed switchto/hidden WMPlex attach parameter interaction. + +2006-11-09 18:09 UTC Tuomo Valkonen + * Do not duplicate entries already in history when pushing. + Instead move the first already existing first. + +2006-11-09 18:00 UTC Tuomo Valkonen + * strcoll instead of strcmp for completion sorting + +2006-11-09 17:59 UTC Tuomo Valkonen + * Complete history in history order instead of character set + +2006-11-01 20:13 UTC Tuomo Valkonen + * Remanage stdisp when bottom attached to group + +2006-10-31 16:19 UTC Tuomo Valkonen + * Fixed coding style + (Remember the coding style, folks!) + +2006-10-31 12:03 UTC David Smith + * Handle mods in submapgrab_handler + +2006-10-31 16:19 UTC Tuomo Valkonen + * Doc. fixes + +2006-10-30 21:07 UTC Tuomo Valkonen + * tiling_placement_alt fixes + +2006-10-30 21:05 UTC Tuomo Valkonen + * Comments about moronic kernels and improved workaround + +2006-10-29 13:09 UTC Tuomo Valkonen + * Respect REGION_SKIP_FOCUS more often + +2006-10-29 13:07 UTC Tuomo Valkonen + * Oops + +2006-10-28 23:15 UTC Tuomo Valkonen + tagged ion-3ds-20061029 + +2006-10-28 23:15 UTC Tuomo Valkonen + * Some release notes + +2006-10-28 23:08 UTC Tuomo Valkonen + * mod_menu.grabmenu uses current key event. + - It is no longer necessary to specify the cycling key separately. + +2006-10-28 23:03 UTC Tuomo Valkonen + * Query activation key now cycles completions + +2006-10-28 22:47 UTC Tuomo Valkonen + * Fixed defmenu for callbacks + +2006-10-28 22:47 UTC Tuomo Valkonen + * Added routine to get current key event, if not in a submap. + +2006-10-28 19:45 UTC Tuomo Valkonen + * Fixed sizepolicies used for fullsize-stdisp + +2006-10-28 19:41 UTC Tuomo Valkonen + * Context menu code now knows to use 'Foo.bar' for Foo in mode 'bar-baz'. + +2006-10-28 19:17 UTC Tuomo Valkonen + * Removed unused/broken WFrame-as-scratchpad bindmap + +2006-10-27 18:56 UTC Tuomo Valkonen + * Set SIZEPOLICY_FULL_EXACT for group bottom if unspecified. + +2006-10-27 18:29 UTC Tuomo Valkonen + * Oops, incomplete backward cycle... + +2006-10-27 18:25 UTC Tuomo Valkonen + * Changed parametrisation of WEdln.complete + +2006-10-27 18:16 UTC Tuomo Valkonen + * Added support for history completion + - Mod1+R in the default bindings completes in history. + +2006-10-22 18:11 UTC Tuomo Valkonen + * create_frame parametrisation in mod_scratchpad was wrong. + (Weak typing, bah.) + +2006-10-21 22:06 UTC Tuomo Valkonen + * Fixed rotation support. + (Application of size policies did not pass through the information.) + +2006-10-21 20:41 UTC Tuomo Valkonen + * Fixed layout backwards compatibility hack. + - It wasn't setting SIZEPOLICY_FULL_EXACT for WTiling. + +2006-10-21 17:20 UTC Tuomo Valkonen + * Fixed shading code after the mode stuff had broken it + +2006-10-21 17:08 UTC Tuomo Valkonen + * Resizing code fixes and other changes + +2006-10-20 23:41 UTC Tuomo Valkonen + * Allow frames attached to WTiling to have tiled-alt mode + +2006-10-20 17:47 UTC Tuomo Valkonen + * Oops. Workspace switch warp had become disabled. + +2006-10-20 15:43 UTC Tuomo Valkonen + tagged ion-3ds-20061020 + +2006-10-20 15:43 UTC Tuomo Valkonen + * Updated RELNOTES + +2006-10-20 15:41 UTC Tuomo Valkonen + * Added README for po/ + +2006-10-20 15:35 UTC Tuomo Valkonen + * Restored client window unmap fs_pholder return goto + (Which could do in theory do the wrong thing now under some + strange settings and scripts. TODO: better and more general + "return lists".) + +2006-10-20 15:34 UTC Tuomo Valkonen + * Fixes to previous focus fixes + +2006-10-19 21:46 UTC Tuomo Valkonen + * Separate update_$LANG targets in Makefile instead of update_translations + +2006-10-19 18:10 UTC Tuomo Valkonen + * Removed unnecessary test file + +2006-10-19 16:46 UTC Tuomo Valkonen + * Updated Finnish translation + +2006-10-19 16:35 UTC Tuomo Valkonen + * Use lua-xgettext + +2006-10-18 18:04 UTC Tuomo Valkonen + * mplex_current_node falls back to mx_current. + (Kludge to get around problems with deferred destroy and groups.) + +2006-10-18 17:54 UTC Tuomo Valkonen + * WMPlex focus code changes + +2006-10-17 21:57 UTC Tuomo Valkonen + * Added boolean 'float' winprop. + - If it is set, groups don't pass prepare_manage to 'bottom'. + +2006-10-17 21:47 UTC Tuomo Valkonen + * Don't set REGION_PLEASE_WARP on groups + +2006-10-16 22:32 UTC Tuomo Valkonen + * predist.sh fix/redundancy removal + +2006-10-16 22:20 UTC Tuomo Valkonen + * Don't install cfg_panews.lua, as the module is disabled. + +2006-10-16 19:55 UTC Tuomo Valkonen + * Makefile preload hack fixes + +2006-10-16 19:49 UTC Tuomo Valkonen + * -typo + +2006-10-16 12:17 UTC Miroslav Kure + * Updated Czech manpage + +2006-10-16 12:16 UTC Miroslav Kure + * Updated Czech translation + +2006-10-02 11:57 UTC Tuomo Valkonen + * README update + +2006-10-15 22:35 UTC Tuomo Valkonen + * Fixed a typo in workspace query + (Stupid dynamic typing.) + +2006-10-15 18:00 UTC Tuomo Valkonen + tagged ion-3ds-20061015 + +2006-10-15 17:59 UTC Tuomo Valkonen + * Doc fix + +2006-10-15 17:52 UTC Tuomo Valkonen + * Some final release note tuning + +2006-10-15 17:02 UTC Tuomo Valkonen + * Use the faster direct url in predist.sh + +2006-10-15 17:01 UTC Tuomo Valkonen + * predist.sh update + +2006-10-15 16:58 UTC Tuomo Valkonen + * Oops. Do not enforce floating style for transient frames.. + +2006-10-15 15:27 UTC Tuomo Valkonen + * Improved layout backwards compatibility hack + +2006-10-15 14:36 UTC Tuomo Valkonen + * Doc fix + +2006-10-15 14:16 UTC Tuomo Valkonen + * WFrame@WTiling bindmap and menu was actually redundant now. + +2006-10-14 23:11 UTC Tuomo Valkonen + * Added some release notes + +2006-10-14 23:01 UTC Tuomo Valkonen + * Actually, disable tab-bar by deafult for FRAME_MODE_TILED_ALT. + +2006-10-14 22:49 UTC Tuomo Valkonen + * Use "@" instead of "-on-" for bindings and menus dependent on manager + (for more consistency with "." for mode-dependent stuff). + +2006-10-14 22:46 UTC Tuomo Valkonen + * Context menus also support modes now. + - The context menu "Class.mode" is included, if Class:mode exists and + returns "mode". + +2006-10-14 22:45 UTC Tuomo Valkonen + * Improvements to mode stuff + +2006-10-14 22:28 UTC Tuomo Valkonen + * Removed superfluous frame drawing routine dynamism. + +2006-10-14 22:21 UTC Tuomo Valkonen + * Fixed mkbottom + +2006-10-14 22:17 UTC Tuomo Valkonen + * Groups and tilings now enforce frame mode. + +2006-10-14 22:10 UTC Tuomo Valkonen + * Added frame mode-switching code. + - Frames without a tab bar can be obtained with + + frame:set_mode('frame-tiled-alt') + + in the default styles (or by entirely disabling the tabbar with + `bar = 'none'` in the 'frame-tiled' style). + +2006-10-14 21:29 UTC Tuomo Valkonen + * Removed WFloatFrame + - Frame modes are used instead. + - Tabbar-toggling is also gone, and must be reproduced with + modes (TODO). + +2006-10-14 17:58 UTC Tuomo Valkonen + * Replace frame style parameter with mode parameter. + +2006-10-10 22:26 UTC Tuomo Valkonen + * Added guards for _rawsub and renamed it _chld. + +2006-10-07 21:06 UTC Tuomo Valkonen + * Fixed stdisp unmanage + +2006-10-07 16:03 UTC Tuomo Valkonen + * Added detach entries into menus + +2006-10-07 16:00 UTC Tuomo Valkonen + * Added 'append' option for menus. + +2006-10-07 14:29 UTC Tuomo Valkonen + * Added support for WFoo-on-WBar context menus. + +2006-10-07 14:17 UTC Tuomo Valkonen + * Manual page binding listing generation improvements etc. + +2006-10-06 15:50 UTC Tuomo Valkonen + * Added context menu for screens. + - Entries to create workspace with default template or an empty one, and + close current workspace (if permitted). + - Mod1+M bound at WMPlex level now, to display this menu on empty + workspaces. + +2006-10-05 18:15 UTC Tuomo Valkonen + * Updated (English and Finnish) man pages. + +2006-10-05 17:46 UTC Tuomo Valkonen + * Floatframe shading fixes. + +2006-10-04 18:01 UTC Tuomo Valkonen + * Float placement code ignore group bottom now. + +2006-10-04 17:56 UTC Tuomo Valkonen + * Fixed and cleaned up bitrot in region binding registration code. + +2006-10-03 16:00 UTC Tuomo Valkonen + * Fixed mod_query.show_clientwin by expanding it into mod_query.show_tree. + +2006-10-03 15:39 UTC Tuomo Valkonen + * Simplified group attach stuff with separate WFramedPHolder + +2006-09-30 20:57 UTC Tuomo Valkonen + * mod_menu was still referring to WMPlex.llist + +2006-09-30 20:43 UTC Tuomo Valkonen + * Group attach size fix (quick&dirty version). + +2006-09-30 12:23 UTC Tuomo Valkonen + * Oops. Reparent/weave were in wrong order in group_fitrep. + +2006-09-29 19:17 UTC Tuomo Valkonen + * Put floating client windows as well in groups (for now). + - Also use frames with special style for floating transients as well. + +2006-09-29 19:05 UTC Tuomo Valkonen + * WGroupedPHolder and other fixes. + +2006-09-29 17:48 UTC Tuomo Valkonen + * Hacks to ignore size hints when in client-requested full screen mode. + +2006-09-28 18:13 UTC Tuomo Valkonen + * Fixed uninitialised structures. + (Stupid C...) + +2006-09-28 09:15 UTC Tuomo Valkonen + * Restored NotifyPointer focus event ignorance. + +2006-09-27 09:57 UTC Tuomo Valkonen + * Changed email in convirus script to nobody@nowhere.invalid + (Stupid piece of shit telling users to report its own idiocy to + that address.) + +2006-09-27 09:52 UTC Tuomo Valkonen + * Use NGROUPS_MAX if NGROUPS is not defined. + +2006-09-24 16:15 UTC Tuomo Valkonen + * Removed useless rescue code. + +2006-09-24 16:14 UTC Tuomo Valkonen + * Oops. + +2006-09-24 15:59 UTC Tuomo Valkonen + * Use WGroupedPHolder for grouped attach of client windows to frames. + +2006-09-24 15:58 UTC Tuomo Valkonen + * Fixed mplex placeholder rearrangements. + +2006-09-23 15:39 UTC Tuomo Valkonen + * Removed completed #warning TODO + +2006-09-23 15:27 UTC Tuomo Valkonen + * Lowering a stacked-above object (transient) lowers the o'parent' object as well now. + +2006-09-22 18:11 UTC Tuomo Valkonen + * Fixed group prepare_manage policy. + +2006-09-19 17:10 UTC Tuomo Valkonen + * Changes in EnterWindow event handling. + +2006-09-19 07:00 UTC Tuomo Valkonen + * Added zero size check to Xinerama sanity check. + +2006-09-17 16:03 UTC Tuomo Valkonen + * Transient etc. size fixes + +2006-09-16 19:27 UTC Tuomo Valkonen + * Sizehint code tuning for requirements of WGroupCW. + +2006-09-16 19:25 UTC Tuomo Valkonen + * Oops, statusbar attach stuff hadn't been brought up-to-date. + +2006-09-16 17:19 UTC Tuomo Valkonen + * Removed redundant size hint correction wrapper code. + +2006-09-16 15:09 UTC Tuomo Valkonen + * WGroupWS.attach_framed supports arbitrary regions (and not just WClientWins). + +2006-09-16 15:08 UTC Tuomo Valkonen + * Use just "frame-floating" style instead of "frame-floating-groupws". + +2006-09-16 15:08 UTC Tuomo Valkonen + * WFloatFrame wasn't using region_displayname yet. + +2006-09-16 14:36 UTC Tuomo Valkonen + * Cleaned up SPLIT_NONE/ANY + +2006-09-16 11:15 UTC Tuomo Valkonen + * Marked some entries as 'done' on the TODO list. + +2006-09-15 12:01 UTC Tuomo Valkonen + * Removed superfluous ion-completeman code + +2006-09-15 16:19 UTC Tuomo Valkonen + * Focus fixes + +2006-09-10 19:28 UTC Tuomo Valkonen + * Fixed focusing when focus gets restored to root window and we want to focus something else. + +2006-09-10 12:44 UTC Tuomo Valkonen + * Transient initial positioning fixed + +2006-09-03 13:20 UTC Tuomo Valkonen + * Fixes to activity propagation code + +2006-09-03 12:26 UTC Tuomo Valkonen + * Oops, forgot to update bindings earlier. + +2006-09-03 11:03 UTC Tuomo Valkonen + * Oops. + +2006-09-03 10:59 UTC Tuomo Valkonen + * Replaced region_activity_hook with generic region_notify_hook, which + is also used to notify of name, tag and other changes, with a string + parameter indicating the actual change that has taken place. + +2006-09-03 08:47 UTC Tuomo Valkonen + * The activity notification window is now managed normally. + +2006-08-31 19:36 UTC Tuomo Valkonen + * Added basic detach/mkbottom code. + TODO: placeholders for reattach, etc. Maybe put into separate + module (instead of mod_tiling)? + +2006-08-31 19:33 UTC Tuomo Valkonen + * Various minor fixes and clean-up. + +2006-08-25 02:06 UTC Tuomo Valkonen + * Moved commented-out transpose_words as Control+K T. + +2006-08-24 20:52 UTC Tibor Csögör + * Added transpose_chars and transpose_words to mod_query. + +2006-08-31 17:20 UTC Tuomo Valkonen + * Some attach mechanism improvements. + +2006-08-21 17:02 UTC Tuomo Valkonen + * Navigation code fixes + +2006-08-21 16:34 UTC Tuomo Valkonen + * WMPlex attach code always uses WMPlexPHolders. + - The attach callbacks should then be able to safely remove stuff + from the mplex, for detach stuff etc. + +2006-08-19 17:25 UTC Tuomo Valkonen + * Fixed initial window order in frame. + +2006-08-19 17:12 UTC Tuomo Valkonen + * Improvements related to navigation and stacking + - Added WRegion.rqorder as an abstract raise/lower request. + +2006-08-19 16:48 UTC Tuomo Valkonen + * Removed unused stacking code + +2006-08-18 17:59 UTC Tuomo Valkonen + * More navigation stuff. + - In particular, rebound configuration files to use ioncore.goto_next + with appropriate parametrisation. + +2006-08-18 17:46 UTC Tuomo Valkonen + * Some improvements in binding handler compilation code + +2006-08-17 17:01 UTC Tuomo Valkonen + * Added no_ascend/no_descend parameters to the navi functions. + +2006-08-17 16:57 UTC Tuomo Valkonen + * Some fixes binding graph hacks for windowless regions. + +2006-08-16 18:55 UTC Tuomo Valkonen + * Generic navigation code improvements. + - Also removed some redundant tiling code. + +2006-08-11 16:46 UTC Tuomo Valkonen + * Fixed an assert trigger + +2006-08-11 16:45 UTC Tuomo Valkonen + * Some drawing engine code tuning + +2006-08-14 15:57 UTC Tuomo Valkonen + * Added a missing check for nil table. + +2006-08-14 18:57 UTC Tuomo Valkonen + * Fixed some undefined references in mod_dock. + +2006-08-09 17:57 UTC Tuomo Valkonen + * Use redblack tree for reg->stacking lookup. + +2006-08-07 16:39 UTC Tuomo Valkonen + * Some documentation updates. + +2006-08-07 16:29 UTC Tuomo Valkonen + * Changes in WMPlex structure. + - l1/l2 lists are gone, and instead there just a single list of all + managed regions using WStacking nodes, and another list of the + “layer 1” or “mutually exclusive” regions. + +2006-08-05 18:39 UTC Tuomo Valkonen + * Removed mplex_lcount stuff. + +2006-08-02 14:56 UTC Tuomo Valkonen + * Possible fixes to (initial) effect of stdisp on tiling llayout. + +2006-08-03 21:13 UTC Tuomo Valkonen + * Updated to predist.sh to reflect changed paths of *.mk. + +2006-08-03 21:03 UTC Tuomo Valkonen + * Changes in predist.sh to reflect _darcs/ having been changed. + +2006-08-03 15:56 UTC Tuomo Valkonen + * Simplifications in mplex/group focus code. + +2006-08-02 20:02 UTC Tuomo Valkonen + * Combined WStacking and WLListNode. + +2006-08-01 22:11 UTC Tuomo Valkonen + * Some field renames for further changes. + +2006-07-16 15:33 UTC Tuomo Valkonen + * WGroupCW uses the bottom_last_close option now (instead of reinventing it). + +2006-07-16 15:28 UTC Tuomo Valkonen + * Added option to auto-destroy groups when 'bottom' is destroyed + when nothing "essential" is left after it. + +2006-07-16 15:12 UTC Tuomo Valkonen + * Improved handling of stdisp managed by destroyed 'bottom' of a group. + +2006-07-15 19:09 UTC Tuomo Valkonen + * PWM config updates. + +2006-07-15 18:48 UTC Tuomo Valkonen + * Added default_ws_params to ioncore.set. + - cfg_tiling.lua sets a horizontally 1:1 splitted tiled layout as default + +2006-07-15 18:01 UTC Tuomo Valkonen + * Renamed mod_ionws as mod_tiling and WIonWS as WTiling. + +2006-07-14 12:46 UTC Tuomo Valkonen + * mod_floatws is no more. + - WFloatWS was moved into ioncore as the WGroupWS base for all workspaces. + - Backwards compatibility hack included for saved layouts, but not + configuration files. + +2006-07-14 11:21 UTC Tuomo Valkonen + * Moved some region_register calls to safer places. + +2006-07-14 11:07 UTC Tuomo Valkonen + * Changed remaining lua-side references to WGenWS to WGroupWS. + +2006-07-10 20:25 UTC pfeifer@wait.de + * pass-table-fix + This patch fixes a bug in mod_panes that would prevent a caller for successfully passing + a table as template to the module + +2006-07-14 10:26 UTC Tuomo Valkonen + * Removed redundant code by using weave for raise/lower. + +2006-07-12 12:34 UTC Tuomo Valkonen + * Removed WGenWS entirely. + +2006-07-08 10:13 UTC Tuomo Valkonen + * mod_dock was still referring to mplex_layer. + +2006-07-12 11:59 UTC Tuomo Valkonen + * Temporarily disabled mod_panews. + +2006-07-12 05:00 UTC Etan Reisner + * Remove an extra space in the Lua query prompt. + +2006-07-08 08:48 UTC Tuomo Valkonen + * Fixes in initial stacking of regions. + +2006-07-08 07:54 UTC Tuomo Valkonen + * floatws redirects prepare_manage to 'bottom' if active. + +2006-07-07 18:19 UTC Tuomo Valkonen + * Added beginnings of more generic navigation code. + +2006-07-07 17:13 UTC Tuomo Valkonen + * Fixed 'bottom' initial stacking. + +2006-07-07 14:42 UTC Tuomo Valkonen + * Redirect stdisp to WGroup(WS) 'bottom'. + +2006-07-05 15:13 UTC Tuomo Valkonen + * Ugly Makefile hacks. + +2006-07-02 17:24 UTC Tuomo Valkonen + * Some improvements in mplex focusing code and policies. + +2006-07-02 15:31 UTC Tuomo Valkonen + * Some cleanup. + +2006-07-02 15:27 UTC Tuomo Valkonen + * WGroupCW no longer loads if empty. + +2006-07-01 19:13 UTC Tuomo Valkonen + * Some mplex/group integration. + +2006-07-01 18:19 UTC Tuomo Valkonen + * Further improvements in focus code. + +2006-07-01 16:28 UTC Tuomo Valkonen + * region_managed_goto improvements. + +2006-06-27 22:10 UTC Tuomo Valkonen + * Oops. + +2006-06-27 21:15 UTC Tuomo Valkonen + * Some more changes, fixes, and damage done in key binding dispatch code. + +2006-06-25 20:00 UTC Tuomo Valkonen + * Fixes and simplifications to key handling code. + +2006-06-25 17:36 UTC Tuomo Valkonen + * Minor fixes to group stuff. + +2006-06-25 16:03 UTC Tuomo Valkonen + * Reduced use of WGenWS. + (Only WIonWS and WPaneWS still refer to it.) + +2006-06-25 09:37 UTC Tuomo Valkonen + * WGroupCW passes on region_managed_notify for the 'bottom'. + +2006-06-25 09:04 UTC Tuomo Valkonen + * Stupid C and dependencies.. + +2006-06-24 18:20 UTC Tuomo Valkonen + * Some more release notes. + +2006-06-24 18:09 UTC Tuomo Valkonen + * Groups use minimum size hint from 'bottom'. + +2006-06-24 17:54 UTC Tuomo Valkonen + * Check that WGenWS actually implements genws_manage_stdisp before calling it. + +2006-06-24 17:41 UTC Tuomo Valkonen + * Full-screening code updates. + +2006-06-24 17:40 UTC Tuomo Valkonen + * Fix in group_fitrep. + +2006-06-24 17:33 UTC Tuomo Valkonen + * Ignore stuff in groups for primitive stacking code. + +2006-06-24 17:32 UTC Tuomo Valkonen + * Fixed stacking_weave. + +2006-06-24 16:24 UTC Tuomo Valkonen + * Added binding set for WClientWin. + +2006-06-24 16:16 UTC Tuomo Valkonen + * New mechanism for grabs of windowless regions' bindings. + +2006-06-23 09:26 UTC Tuomo Valkonen + * Added stuff to RELNOTES. + +2006-06-23 09:19 UTC Tuomo Valkonen + * Added region_displayname stuff. + - Needed to display the main client window's name in the tab-bar for + client window groups (WGroupCW). + +2006-06-23 09:09 UTC Tuomo Valkonen + * WGroupCW gets destroyed when empty. + +2006-06-23 08:41 UTC Tuomo Valkonen + * Temporarily removed sticky floating frame support. + - To be re-implemented in a completely different manner. + +2006-06-23 08:36 UTC Tuomo Valkonen + * Added group modality stuff. + +2006-06-23 07:15 UTC Tuomo Valkonen + * Some cleanup. + +2006-06-22 23:38 UTC Tuomo Valkonen + * First steps of client window groups. + +2006-06-21 16:46 UTC Tuomo Valkonen + * There were unused fields in WClientWin. + +2006-06-21 16:42 UTC Tuomo Valkonen + * Some dates were wrong.. + +2006-06-21 16:39 UTC Tuomo Valkonen + * Fixed group attach geometry stuff. + +2006-06-21 08:10 UTC Tuomo Valkonen + * Typo fixes. + +2006-06-20 19:05 UTC Tuomo Valkonen + * Inherited WFloatWS from WGroup. + +2006-06-20 18:37 UTC Tuomo Valkonen + * Moved part of WFloatWS as WGroup in ioncore. + +2006-06-20 18:36 UTC Tuomo Valkonen + * Removed unused local variable. + +2006-06-20 18:32 UTC Tuomo Valkonen + * Moved some more stuff. + +2006-06-20 18:13 UTC Tuomo Valkonen + * Moved framed attach stuff from floatws.c to placement.c. + +2006-06-20 17:54 UTC Tuomo Valkonen + * Improved floatws pholder. + +2006-06-19 16:21 UTC Tuomo Valkonen + * Added SIZEPOLICY_SHRUNK modifier. + +2006-06-19 16:13 UTC Tuomo Valkonen + * Added generic floatws attach routines. + +2006-06-19 12:36 UTC Tuomo Valkonen + * Allow override of xterm with the XTERM Lua-side variable. + +2006-06-18 01:00 UTC Tuomo Valkonen + * Improved and extended floatws sizepolicy usage. + +2006-06-17 22:17 UTC Tuomo Valkonen + * stacking_unweave/weave + +2006-06-17 22:03 UTC Tuomo Valkonen + * Oops. + +2006-06-17 18:18 UTC Tuomo Valkonen + * FloatWS special-case stuff points to the stacking structures instead of the regions. + +2006-06-17 17:36 UTC Tuomo Valkonen + * Added manager specific list to WStacking. + +2006-06-17 10:45 UTC Tuomo Valkonen + * Cleaned up the top directory of building related files. + - In particular, autovirus stuff is in build/ac now and must be run there. + +2006-06-15 02:51 UTC Etan Reisner + * Sun Xinerama support, also fix the --help text to accurately indicate whether we were built with xinerama support or not. + +2006-06-13 15:08 UTC Norbert Tretkowski + * Use META instead MOD1. + +2006-06-07 11:54 UTC Tuomo Valkonen + * Missing assignment fixed. + +2006-06-07 09:47 UTC Tuomo Valkonen + * Export ioncore.tags_first. + +2006-06-11 17:30 UTC Tuomo Valkonen + * Improvements to floatws bottom support + use size policies. + +2006-06-11 17:30 UTC Tuomo Valkonen + * Added SIZEPOLICY_UNCONSTRAINED. + +2006-06-10 21:45 UTC Tuomo Valkonen + * Preliminary floatws "bottom" support. + +2006-06-09 14:37 UTC Tuomo Valkonen + * Exclude USECS_IN_SEC itself from valid values too.. + +2006-06-09 14:12 UTC Tuomo Valkonen + * Ensure that tv_usec is less than 1000000 (usecs in sec). + - Some kernels are a bit picky. + +2006-05-29 21:00 UTC Tuomo Valkonen + * Size hints weren't being applied on client windows on Ion startup startup. + (Now they're applied if we do not modify the old client window size.) + +2006-05-29 16:05 UTC Tuomo Valkonen + * Added levels to stacking code. + +2006-05-28 20:17 UTC Tuomo Valkonen + * Removed some redundancy from stacking code. + +2006-05-19 21:36 UTC Etan Reisner + * s/kpress_waitrel/kpress_wait/ and some s/waitrel/wait/ since I believe waitrel is the old name and this makes things a little cleaner. + The switch_bindings.lua script currently has an ugly if/elseif section to do + all the binding removal stuff, with this change that whole section can be + replaced with one line. + +2006-05-28 17:52 UTC Tuomo Valkonen + * Some more stacking code changes. + +2006-05-28 11:36 UTC Tuomo Valkonen + * Some more stacking code cleanup etc. + +2006-05-27 15:25 UTC Tuomo Valkonen + * Some improvements/cleanup in floatws stacking code. + +2006-05-24 16:22 UTC Tuomo Valkonen + tagged ion-3ds-20060524 + +2006-05-23 06:12 UTC Tuomo Valkonen + * An assert had moved to the wrong place. + +2006-05-20 09:10 UTC Tuomo Valkonen + * One more gsub update... + +2006-05-19 16:34 UTC Tuomo Valkonen + * More Lua 5.1 stuff: use # instead of table.getn. + +2006-05-19 07:12 UTC René van Bevern + * take ioncore_bindings.lua of ion3 to lua 5.1 + +2006-05-19 06:48 UTC Tuomo Valkonen + * Few more backticks. + +2006-05-18 23:06 UTC Tuomo Valkonen + tagged ion-3ds-20060519 + +2006-05-18 23:05 UTC Tuomo Valkonen + * Added new release notes. + +2006-05-18 23:05 UTC Tuomo Valkonen + * Updated README.autoconf. + +2006-05-18 23:01 UTC Tuomo Valkonen + * Added a few more backticks to RELNOTES and README. + +2006-05-19 02:19 UTC Matthieu.Moy@imag.fr + * Use -lm and -ldl in the test for -llua + +2006-05-18 13:21 UTC Tuomo Valkonen + * Markdownized README as well. + +2006-05-18 13:04 UTC Tuomo Valkonen + * Small changes in RELNOTES for markdown processing for web. + +2006-05-17 15:26 UTC Tuomo Valkonen + * Updated TODO.riot. + +2006-05-17 16:51 UTC Matthieu.Moy@imag.fr + * bugfix in lua 5.1 + other usefull checks + The test for the liblua version was completely buggy. Sorry. + + Tests added for other headers: + X11/SM/SMlib.h, X11/Xresource.h and libintl.h + (with error messages pointing to the name of Debian packages) + + Test for xmessage (with a warning only, since ion can probably still + work without xmessage) + + + +2006-05-17 13:39 UTC Matthieu.Moy@imag.fr + * Update configure.ac for lua 5.1 + +2006-05-16 18:39 UTC Sergej Pupykin + * sigusr2 + + Allows to create lua hook for SIGUSR2. + Can be usefull (for example) for status line changing from media player... + + Usage example: + ioncore.get_hook("ioncore_sigusr2_hook"):add( + function(reg) + mod_statusbar.inform("status_name", "SIGUSR2 catched") + mod_statusbar.update() + end + ) + +2006-05-16 17:32 UTC Tuomo Valkonen + * README etc. updates. + +2006-05-16 17:24 UTC Tuomo Valkonen + * Fixed string.gsub issues with Lua 5.1. + + Lua 5.0: + "... If the value returned by this function is a string, then it is + used as the replacement string; otherwise, the replacement string is the + empty string." + + Lua 5.1: + "If the value returned by the table query or by the function call is a string + or a number, then it is used as the replacement string; otherwise, if it is + false or nil, then there is no replacement (that is, the original match is + kept in the string)." + + +2006-05-16 16:20 UTC Tuomo Valkonen + * Few more missing table-iteration changes. + +2006-03-24 00:19 UTC Etan Reisner + * lua5.1 incompatible changes + The _LOADED variable became package.loaded. + The contents of lualib were merged into lua itself, so remove -llualib from the build line. + Debian now uses pkg-config for the lua stuff not lua-config. + +2006-03-24 00:09 UTC Etan Reisner + * lua5.1 backwards compatible changes + Whether intentional or not lua 5.1 no longer allows + tab={} + for k,v in tab do + ... + end + and requires + for k,v in pairs(tab) do + ... + end + instead. + +2006-05-06 21:18 UTC Tuomo Valkonen + * Do not spew out stack traces all the time when warn() is called. + +2006-04-26 17:49 UTC Tuomo Valkonen + * framed_transients is on by default now. + +2006-04-22 16:08 UTC Tuomo Valkonen + * Added toggle for showing tab numbers. + - WFrame:set_numbers with the usual 'set'/'unset'/'toggle' parametrisation. + +2006-04-20 19:19 UTC Tuomo Valkonen + * Removed obsolete references to -i18n flag. + +2006-04-26 17:45 UTC Tuomo Valkonen + * Added WFrame.toplevel and WMPlex.toplevel binding maps. + - These are not used by frames for transients. + - Changed the default bindings accordingly. + +2006-04-13 22:05 UTC Tuomo Valkonen + * The frame_add_last option had been broken. + - Since switching to use of placeholders for initial management + setup, the flag was no longer checked. + +2006-04-02 19:36 UTC Tuomo Valkonen + * Added stuff for accessing regions with activity/urgency bit set. + +2006-04-02 19:33 UTC Tuomo Valkonen + * Extended Mod1+K K binding. + - Go to first region demanding attention or previously active region: + "ioncore.activity_goto() or ioncore.goto_previous()". + +2006-04-01 14:38 UTC Tuomo Valkonen + * Separated and cleaned up some stacking code from mod_floatws. + +2006-03-31 18:14 UTC Tuomo Valkonen + * Changes in default_ws_type lookup. + +2006-03-28 20:18 UTC Tuomo Valkonen + * Use SIZEPOLICY_FULL_BOUNDS for client windows in mplexes. + - Needed to communicate available area for transients. + +2006-03-26 09:37 UTC Tuomo Valkonen + tagged ion-3ds-20060326 + +2006-03-23 16:15 UTC Tuomo Valkonen + * Included a TODO list. + + The TODO.riot file has been created with the riot outliner available from + . The file is, however, just an mbox file, so + you can read it with your favourite threading mail user agent. For example: + mutt -f TODO.riot + +2006-03-20 23:08 UTC Tuomo Valkonen + * Enforce win_gravity=NorthWestGravity for client windows. + - Fixes issues with OO.org's transients in framed mode. + +2006-03-18 22:19 UTC Tuomo Valkonen + * Changes in default appmenu. + - Removed firefox (won't promote gtk shit). + - Added opera, konqueror, dillo, w3m, links, rxvt. + +2006-03-18 12:58 UTC Tuomo Valkonen + * Ah, -Wl,-whole-archive is a better hack. + +2006-03-18 12:48 UTC Tuomo Valkonen + * Link PWM also with the -u ptrlist_iter gcc lameness hack. + +2006-03-17 20:49 UTC Tuomo Valkonen + * Some more winprop/sizepolicy changes. + +2006-03-17 19:42 UTC Tuomo Valkonen + tagged ion-3ds-20060317 + +2006-03-17 10:22 UTC Tuomo Valkonen + * Oops. Previous change incomplete. + +2006-03-17 10:07 UTC Tuomo Valkonen + * default_ws_type is no longer set by config files and could confuse things. Fixed. + +2006-03-15 23:00 UTC Tuomo Valkonen + * Changes in look file fonts. + - Refer directly to -*-fixed-medium-r-normal-*-13-*-*-*-*-*-*-* as + the 'fixed' alias has problems with encodings. + +2006-03-15 18:59 UTC Tuomo Valkonen + * Timer signal object passing to Lua side was broken. + (Weak typing...) + +2006-03-15 18:23 UTC Tuomo Valkonen + * Changes in the set of configuration files. + - cfg_ion.lua now simply load cfg_ioncore.lua and cfg_modules.lua + with the default set of modules, and includes some of the most + commonly changed options commented-out. This deprecates cfg_user.lua + - cfg_menus.lua and cfg_bindings.lua are now cfg_ioncore.lua. This + better matches how things for different modules are in their own + files. + +2006-03-12 12:29 UTC Tuomo Valkonen + * Embedded dock initilisation code had been broken. Fixed. + +2006-03-11 09:05 UTC Tuomo Valkonen + * Oops. The stretch size policy used wrong variables. + +2006-03-11 08:58 UTC Tuomo Valkonen + * Different query-menus use different history context. + +2006-03-08 20:03 UTC Tuomo Valkonen + * Oops. The new client window code used wrong "llist" iterator. + This could cause segfaults. + +2006-03-08 19:46 UTC Tuomo Valkonen + * Oops. Closing a transient had started warping to remaining transient(s). + +2006-03-07 10:32 UTC Tuomo Valkonen + * Fixed binding lookup code to be able to deal with zero keycodes. + - This may happen if X keycode to keysym mapping is changed + while Ion is running. + +2006-03-06 23:20 UTC Tuomo Valkonen + * Oops. If there were winprops set, SIZEPOLICY_DEFAULT was used for transients. + +2006-03-05 13:44 UTC Tuomo Valkonen + tagged ion-3ds-20060305 + +2006-03-05 13:42 UTC Tuomo Valkonen + * 'ru' was missing from list of translations in po/Makefile. + +2006-03-05 13:39 UTC Tuomo Valkonen + * frame_rqgeom_clientwin passes rqflags as-is. + +2006-03-05 13:30 UTC Tuomo Valkonen + * SIZE_POLICY_FREE_GLUE is now properly used for transients. + +2006-03-05 13:28 UTC Tuomo Valkonen + * Added -u ptrlist_iter linking hack to gcc/ld. + - Otherwise ptrlist.o from libtu won't get linked as it will no longer + used by the main binary, and thus the modules won't have access to it. + +2006-03-05 10:59 UTC Tuomo Valkonen + * Improved free_glue and stretch size policies. + +2006-03-02 18:49 UTC Tuomo Valkonen + * Added SIZEPOLICY_FREE_GLUE and other size policy code changes. + (TODO: client windows need to store state for this for transients.) + +2006-02-26 16:25 UTC Tuomo Valkonen + * Client windows use the same (mplex) size policies for transients now. + +2006-02-26 00:17 UTC Tuomo Valkonen + * Oops. MPlex attach code changes had broken mgd. region ordering on load. + +2006-02-25 17:31 UTC Tuomo Valkonen + * Added some size policies with gravity. + +2006-02-24 19:23 UTC Tuomo Valkonen + * Separated mplex size policy in new file, and independent of mplex. + +2006-02-22 13:34 UTC Tuomo Valkonen + * Append '/' to submenu entries in query_menu. + +2006-02-19 16:37 UTC Tuomo Valkonen + * Some mplex attach code cleanup. + +2006-02-19 16:07 UTC Tuomo Valkonen + * Reduced usage of REGION_FIT_BOUNDS a little. + +2006-02-19 00:41 UTC Tuomo Valkonen + * Updated dock to reflect previous mplex size policy changes. + +2006-02-18 20:39 UTC Tuomo Valkonen + * Removed the WScratchpad class. + - Scratchpads are now simply normal WFrames with MPLEX_SIZEPOLICY_FREE. + +2006-02-18 20:31 UTC Tuomo Valkonen + * Added WMPlex managed region size policy support. + +2006-02-18 18:53 UTC Tuomo Valkonen + * Fixed "./" being included in completed file name in query_exec. + - Also brought mod_query.popen_completions documentation up-to-date. + +2006-02-15 07:29 UTC Tuomo Valkonen + * Fixed load_hint. + - Stupid unsafe dynamically-typed languages... + +2006-02-12 16:03 UTC Tuomo Valkonen + * Let's call it statusbar_ instead of status_ after all... + +2006-02-12 15:59 UTC Tuomo Valkonen + * Oops. Should check for sb meter value being null when shortening it. + +2006-02-12 15:30 UTC Tuomo Valkonen + * mod_statusbar tries to load scripts before falling back to statusd. + - If status_foobar(.lua|.lc) is found on the search path, -m foobar + is not passed to ion-statusd. + +2006-02-12 15:19 UTC Tuomo Valkonen + * Added region_activity_hook. + - Called when the activity flag of a region is changed with the + region as parameter. + +2006-02-12 15:07 UTC Tuomo Valkonen + * Framed transients' size requests get handled now. + +2006-02-11 15:35 UTC dev@triphoenix.de + * completefile-groupexec + + Although ion-completefile does check if any matching executable is + world-executable or user-executable, it doesn't check for + group-executable. This is problematic for example on systems, where + games are installed with r-xr-x--- and root:games (which seems to be a + common method). This introduces a check for the group id, basically it + gets all groups for the current user and checks if any of them is + applicable. This is only done when the user and world checks already + have failed and the group-executable bit is set. + + +2006-02-08 07:42 UTC Tuomo Valkonen + * ion-statusd catches SIGCHLD. + +2006-02-07 21:37 UTC Tuomo Valkonen + * Improved comments in cfg_statusbar.lua. + +2006-02-06 20:56 UTC Tuomo Valkonen + * Changed query_menu title transformation. + * Lua's regexp routines don't understand utf-8 or other multibyte encodings. + * TODO: conversion to lower case of non-ascii letters. + +2006-02-01 18:05 UTC Vassily Leushin + * russian_locale + +2006-01-29 15:27 UTC Tuomo Valkonen + * Statusbar meter template is respected as maximum size for meter. + +2006-01-25 23:57 UTC Tuomo Valkonen + * Added dummy gettext hack for those labels. + +2006-01-25 23:54 UTC Tuomo Valkonen + * Added context menu label support. + +2006-01-25 16:30 UTC Tuomo Valkonen + * Do not try to resize the statusbar unnecessarily. + +2006-01-21 23:47 UTC ludal@logilab.fr + * fixes random segfaults + With ion-3ds-20060107 I see random segfaults I can reproduce navigating + some website with galeon. This patch seems to fix the problem (and the + original code looked wrong) + +2006-01-21 21:35 UTC Tuomo Valkonen + * Oops. A function wasn't marked local. + +2006-01-21 20:57 UTC Tuomo Valkonen + * Oops. Counter wasn't incremented in statusbar list building. + +2006-01-20 21:16 UTC Tuomo Valkonen + * Updated/fixed PWM bindings configuration. + +2006-01-20 17:36 UTC Tuomo Valkonen + * Statusbar uses PtrLists instead of ObjLists. + (Stuff gets removed from ObjLists before the manager's removal handler + is called.) + +2006-01-19 19:24 UTC Etan Reisner + * Use the table values we are iterating over. + +2006-01-14 20:10 UTC Tuomo Valkonen + * Previous completion behaviour change broke something.. + +2006-01-11 17:12 UTC Tuomo Valkonen + * Fixed a problem in statusbar winprop usage. + +2006-01-07 21:03 UTC Tuomo Valkonen + tagged ion-3ds-20060107 + +2006-01-07 21:01 UTC Tuomo Valkonen + * Added systray window height limiting. + +2006-01-07 19:06 UTC Tuomo Valkonen + * Added support for multiple systray items in statusbar. + Use %systray_whatever in the template, and set the 'statusbar' winprop + to "systray_whatever" to put any window at that point the template. + +2006-01-01 01:26 UTC Tuomo Valkonen + * Year changed. + +2006-01-01 01:17 UTC Tuomo Valkonen + * Fixed mod_menu.grabmenu documentation. + +2005-12-31 23:44 UTC Tuomo Valkonen + * Display transients of systray icons somewhere else. + +2005-12-29 22:10 UTC Tuomo Valkonen + * The statusbar supports system tray windows now. + These are windows that have the _KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR + property set. + +2005-12-26 19:32 UTC Tuomo Valkonen + * Moved some statusbar code to the C side and removed old (backcompat) kludges. + +2005-12-25 17:06 UTC Tuomo Valkonen + * Added noautoexpand option for mod_query.query_menu. + This option should be used to suppress automatic expansion + of huge menus e.g. as follows: + + submenu("Debian", "Debian", {noautoexpand=true}), + + +2005-12-23 18:39 UTC Tuomo Valkonen + * Some improvements and fixes in style files. + +2005-12-21 22:14 UTC Tuomo Valkonen + * Fixed status display mapping on destroyal of ws and switch fs cwin. + +2005-12-19 18:44 UTC Tuomo Valkonen + * frame_brushes_updated wasn't in the WFrame dynfuntab. + +2005-12-18 16:32 UTC Tuomo Valkonen + * Redefine 'print' in query_lua execution environment, + and display the printed strings, if there are any, after execution + in a message box. + +2005-12-18 16:00 UTC Tuomo Valkonen + * Moved fullscreen winprop handling to default clientwin_do_manage_alt handler. + Other handlers may now override it, but return-from-fs spaceholder is now + obtained. Also, setting the property to false will override initial + fullscreen request from the application. + +2005-12-18 14:43 UTC Tuomo Valkonen + * Fixed mod_query.query_menu failing if a submenu could not be found. + +2005-12-17 23:43 UTC Tuomo Valkonen + * Don't warp to newly opened transient, only focus it. + +2005-12-17 14:14 UTC Tuomo Valkonen + * Changes in transient geometry change request handling. + +2005-12-16 23:35 UTC Tuomo Valkonen + * Forced new completion run (Control+Tab) in auto-show-completions mode doesn't cycle to first alternative. + +2005-12-16 23:30 UTC Tuomo Valkonen + * Meter names etc. may be enclosed in braces in statusbar template. + +2005-12-14 16:19 UTC Tuomo Valkonen + * Documented mod_query.query low-level query routine. + +2005-12-13 16:53 UTC Tuomo Valkonen + * Updated documentation comment. + +2005-12-12 21:13 UTC Tuomo Valkonen + * Fixed space deletion in query_exec completion. + +2005-12-10 20:37 UTC Tuomo Valkonen + tagged ion-3ds-20051210 + +2005-12-10 20:35 UTC Tuomo Valkonen + * Removed kludges from client window resize code. + +2005-12-10 20:20 UTC Tuomo Valkonen + * Changes in transient window management setup code. + +2005-12-08 15:16 UTC Tuomo Valkonen + * Floatws config file was out-of-date. + +2005-12-05 23:18 UTC Tuomo Valkonen + * Added a metatable kludge for MOD1/MOD2 vs. META/ALTMETA compatibility. + - All mixes of old and new configuration files should work correctly. + +2005-12-05 23:17 UTC Tuomo Valkonen + * Renamed MOD1/MOD2 to META/ALTMETA. + - Too many newbies are confused between the MOD1/MOD2 variables and the + Mod1/Mod2 X modifiers. + +2005-12-03 17:32 UTC Tuomo Valkonen + * mod_query.query_attachclient (Mod1+A) now activates client already in target mplex. + +2005-12-03 17:15 UTC Tuomo Valkonen + * Empty dock uses (w, h)=tile_size instead of (1, 1). + +2005-12-02 07:50 UTC Tuomo Valkonen + * mod_statusbar.create should pass fullsize option to WMPlex.set_stdisp. + +2005-11-13 22:24 UTC Tuomo Valkonen + * Experimental: keybindings open query-menus instead of normal menus. + +2005-11-26 11:36 UTC Tuomo Valkonen + * Oops. Failing to open error log file could cause extra trouble.. + +2005-11-26 11:30 UTC Tuomo Valkonen + * Sigh. The default FD_CLOEXEC setting of false is brain-damaged. + +2005-11-25 19:54 UTC Miroslav Kure + * Updated Czech translation + +2005-11-15 17:04 UTC Tuomo Valkonen + * Another temporary clientwin resize hack. + +2005-11-20 13:00 UTC Tuomo Valkonen + * Improved previous active region tracking. + - Replaced explicit and cumbersome previous active object saving + at particular points of the code with a simple activity history + list/stack. + +2005-11-20 12:31 UTC Tuomo Valkonen + * Hmm.. active_screen was still in ioncore_g. + +2005-11-15 07:20 UTC Tuomo Valkonen + * Fixed a typo; wairel -> waitrel. + +2005-11-13 22:38 UTC Tuomo Valkonen + * Oops. Forgot to set FRAME_SZH_USEMINMAX on floatframes now. + +2005-11-13 22:34 UTC Tuomo Valkonen + * Small improvement in date monitor. + +2005-11-13 22:19 UTC Tuomo Valkonen + * Changes in floatframe size hint adjustment. + +2005-11-13 21:59 UTC Tuomo Valkonen + * Changes in mod_query.query_menu name conversion. + +2005-11-13 21:55 UTC Tuomo Valkonen + * Fixed mod_query.query_menu submenu support. + +2005-11-10 19:44 UTC Tuomo Valkonen + * Removed active screen tracking. + - Focused region tracking does the job. + +2005-11-10 19:37 UTC Tuomo Valkonen + * Added ioncore.current(). + - Can be used to find the currently focused region. + +2005-11-06 15:51 UTC Tuomo Valkonen + * Removed stray debug print statement. + +2005-11-03 19:45 UTC Tuomo Valkonen + * Command line completor understands pipes now. + +2005-11-02 19:40 UTC Tuomo Valkonen + * Fixed empty frame minimum size hint. + - Stupid C ! vs. & operator precedence. + +2005-11-01 17:59 UTC Tom Payne + * Include in mod_statusbar/ion-statusd/ion-statusd.c for call to setlocale. + Gentoo bug https://bugs.gentoo.org/show_bug.cgi?id=110860 + +2005-11-01 21:54 UTC Tuomo Valkonen + * Removed PWM from restart menu. + - Because the default menu file is shared by PWM and Ion. + +2005-11-01 21:53 UTC Tuomo Valkonen + * Fixed floatframe size hints' base height. + - For resize indicator to display the current client window's size + instead of something else. + +2005-10-31 20:15 UTC Tuomo Valkonen + * ion-statusd init code checks CF_NO_LOCALE. + +2005-10-29 12:32 UTC Tuomo Valkonen + tagged ion-3ds-20051029 + +2005-10-25 18:31 UTC Tuomo Valkonen + * Fixed (unframed) nested transient size issue. + +2005-10-13 20:57 UTC Tuomo Valkonen + * Fixed -sessionname to -session in manual pages. + +2005-10-24 20:47 UTC Tuomo Valkonen + * Oops. %filler update had removed stretching space constant part. + +2005-10-24 15:37 UTC Tuomo Valkonen + * Oops. mod_statusbar was also lagging behind the mplex_get_stdisp parametrisation change. + +2005-10-23 16:14 UTC Tuomo Valkonen + tagged ion-3ds-20051023 + +2005-10-23 16:13 UTC Tuomo Valkonen + * Experimental: added option to turn framed transients on/off. + - Use ioncore.set{framed_transients=true} to turn them on (off by default). + +2005-10-23 16:12 UTC Tuomo Valkonen + * Experimental: framed transients. + +2005-10-23 15:55 UTC Tuomo Valkonen + * Fixed dummy size hint setup. + +2005-10-18 16:22 UTC Tuomo Valkonen + * Oops. mod_dock hadn't been updated to reflect changes in mplex_set/get_stdisp. + +2005-10-15 18:11 UTC Tuomo Valkonen + * Changes in frame autodestroy code. + +2005-10-07 21:07 UTC Tuomo Valkonen + * Fixed 180 degree rotation. + +2005-10-07 18:23 UTC Tuomo Valkonen + * Changes in ionws rotation support code. + +2005-10-05 22:23 UTC Sadrul H Chowdhury + * attach_transient crash fix + +2005-10-05 03:55 UTC Sadrul H Chowdhury + * introduce ioncore.tagged_list() to get a list of tagged regions + +2005-09-20 18:05 UTC Tuomo Valkonen + * Fixes related to removal of region from an mplex. + +2005-09-18 16:50 UTC Tuomo Valkonen + * Exported WClientWin.attach_transient. + +2005-09-18 16:47 UTC Tuomo Valkonen + * Moved cfg files from module directories to etc/. + +2005-09-18 16:43 UTC Tuomo Valkonen + * Moved some common look settings to lookcommon_*.lua from look_*.lua. + +2005-09-18 15:32 UTC Tuomo Valkonen + * Documentation comment fixes. + +2005-09-13 18:04 UTC Tuomo Valkonen + * Some de cleanup. + +2005-09-08 18:00 UTC Tuomo Valkonen + * Some clean-up. + +2005-09-06 16:29 UTC Tuomo Valkonen + * Added fullsize option for space-wasting status displays. + +2005-09-06 15:16 UTC Tuomo Valkonen + * Removed -c from msgfmt arguments. + +2005-09-05 20:20 UTC Tuomo Valkonen + * Removed mail monitor from default statusbar template. + +2005-08-29 05:43 UTC Tuomo Valkonen + * Fixed floatws_backcirculate (copy-paste bug..) + +2005-08-31 10:19 UTC Tuomo Valkonen + * Removed unused parameter of ioncore.x_get_atom_name. + +2005-08-27 23:01 UTC Per Olofsson + * Some updates to README.dock. + +2005-08-27 22:56 UTC Per Olofsson + * README.dock updates from Debian (removing references to ion-devel etc.) + +2005-08-28 12:30 UTC Tuomo Valkonen + * (Probably) fixed move/resize indicator positioning on xinerama screens not at (0, 0). + +2005-08-27 16:45 UTC Tuomo Valkonen + * Replaced stray \a0's with spaces. + +2005-08-27 16:36 UTC Tuomo Valkonen + * Added %filler statusbar element. + +2005-08-26 17:34 UTC Tuomo Valkonen + * Added oneshot winprop option. + +2005-08-22 11:31 UTC Tuomo Valkonen + * Added missing #include. + +2005-08-21 08:04 UTC Tuomo Valkonen + * Fixed splitting an ancestor of the stdisp. + +2005-08-21 07:36 UTC Tuomo Valkonen + * mod_query.query_menu improvements. + +2005-08-20 14:44 UTC Tuomo Valkonen + * Oops. Pane handle style name change wasn't supposed to be recorded. + - frame-tiled-pane breaks handle drawing completely. + +2005-08-20 11:33 UTC Tuomo Valkonen + tagged ion-3ds-20050820 + +2005-08-20 11:32 UTC Tuomo Valkonen + * Oops. ionws_current_nostdisp was never added after all... + +2005-08-19 12:34 UTC Tuomo Valkonen + * Oops. ioncore_efbb was still referenced as ioncore-efbb. + +2005-08-09 08:24 UTC Tuomo Valkonen + * Fixed statusbar filler drawing. + +2005-08-09 08:06 UTC Tuomo Valkonen + * statusd_load interval parameter should be update_interval. + +2005-08-03 08:50 UTC Tuomo Valkonen + * Fixed indentation in cfg_statusbar.lua. + +2005-08-15 16:16 UTC Tuomo Valkonen + * Keep track of non-fs placement of initially fullscreen windows. + - region_manage_clientwin was replaced with region_prepare_manage that + instead of starting to manage the client window should return a placeholder + for it. If the window is not put in fs mode, this placeholder is then used + to do the placement. Otherwise the placeholder is stored as the fs return + placeholder. + +2005-08-14 18:12 UTC Tuomo Valkonen + * Added a basic placeholder for transients and the dock. + +2005-08-11 21:00 UTC Tuomo Valkonen + * Improved transpose stdisp handling. + +2005-08-11 18:09 UTC Tuomo Valkonen + * Improvements for better Xrandr support. + +2005-08-11 16:07 UTC Tuomo Valkonen + * README updates. + +2005-08-11 15:38 UTC Tuomo Valkonen + * Initialise new splits' current field point to the splitted split. + +2005-08-11 15:37 UTC Tuomo Valkonen + * Fixed stdisp focusing on switch to fresh ionws's. + - Initialise stdisp's parent split's 'current' field point to the + other child. + +2005-08-08 21:54 UTC Tuomo Valkonen + * Removed a few compiler warnings... + +2005-08-08 21:41 UTC Tuomo Valkonen + * Moved menu database stuff from mod_menu to ioncore. + - Configuration files for other modules can now safely define menus without + checking for or loading mod_menu, and + - mod_query.query_menu doesn't depend on mod_menu having been loaded. + +2005-08-07 12:16 UTC Tuomo Valkonen + * Added WDock.attach. + +2005-08-07 11:49 UTC Tuomo Valkonen + * Windows can now be added last in frames. + - Enable with ioncore.set{frame_add_last=true}. + +2005-08-07 11:34 UTC Tuomo Valkonen + * Encoding check only warns if no encoding is given at all in LC_CTYPE. + - If wrong encoding is given (vs. nl_langinfo), localisation is still refused. + - UTF-8 check is also case-insensitive now. + +2005-07-28 18:53 UTC Norbert Tretkowski + * Use WCOREDUMP() only if it's available + +2005-07-28 17:39 UTC Tuomo Valkonen + tagged ion-3ds-20050728 + +2005-07-28 17:39 UTC Tuomo Valkonen + * Small fix in set_text_property. + +2005-07-22 09:21 UTC Tuomo Valkonen + * Fixed transient EnterWindow focus. + +2005-07-22 08:31 UTC Tuomo Valkonen + * Changed autoshowcompl mode tab behaviour. + - If hitting tab triggers a new list of completions, the first + entry is selected, as would happen when hitting tab after a + delayed modification-triggered completion already finished. + +2005-07-21 16:18 UTC Tuomo Valkonen + * Added "Failed to load fallback font" error message. + +2005-07-21 16:16 UTC Tuomo Valkonen + * Fixed segfault if no font could be loaded. + +2005-07-20 21:20 UTC Tuomo Valkonen + * Fixed screen_managed_changed_hook calling when fs clientwin is closed. + +2005-07-20 22:51 UTC Tuomo Valkonen + * Changed next/prev screen bindings. + - They are now consistently with next/prev workspace bindings + MOD1+Shift+comma/period. + +2005-07-20 21:06 UTC fshahriar@gmail.com + * ion-completeman -- Compatibility issue with FreeBSD's sed + + Originally, after tab complete wasn't working I tried to do + ion-completeman -mkusercache but it was generating an empty file. + Tracked down the problem to the sed regex. FreeBSD doesn't like the + "\+". Example: + $ echo /usr/share/man/man1/ls.1.gz|sed 's:^.*/\([^/]\+\.[0-9].*\)$:\1:p; d' + $ + (No output) + + Not sure if it will break if used with GNU sed. + + +2005-07-20 20:02 UTC Tuomo Valkonen + * Improved(?) size selection of docked non-dockapps and enabled d&d to dock. + +2005-07-17 19:36 UTC Tuomo Valkonen + * Improvements to statusd launch error logging code. + +2005-07-17 19:20 UTC Tuomo Valkonen + * Oops. Control could reach end of non-void function. + +2005-07-15 20:31 UTC Tuomo Valkonen + * ion-statusd startup errors are reported in ion startup errorlog now. + +2005-07-15 20:30 UTC Tuomo Valkonen + * Some changes in libmainloop popen routines. + +2005-07-12 16:40 UTC Tuomo Valkonen + * statusd_load should work on fbsd now. + +2005-07-10 19:39 UTC Tuomo Valkonen + * Fixed some variables to be local (on the Lua side). + +2005-07-10 09:40 UTC kurem@debian.cz + * Updated Czech translation of Ion3 + +2005-07-09 20:08 UTC Tuomo Valkonen + * Improved started program stderr catching hack. + - If the program terminates with an error code under 10s, + stderr is displayed. + - If the program terminates under 2s, stderr is displayed despite + errorcode or signal. + +2005-07-09 19:04 UTC Tuomo Valkonen + * Changes in menu opening function parametrisation. + - mod_menu.bigmenu should no longer be used, but remains for + compatibility at least for a while. Instead, set big=true + in the extra parameter to mod_menu.menu. + +2005-07-04 20:14 UTC Tuomo Valkonen + * Added optional directory checking for 'cd'. + * If the Lua POSIX library can be loaded, parameters to 'cd' in the + run query are checked to be directories. (cd is interpreted as a + special case to change workspace working dir.) + +2005-07-03 20:42 UTC Tuomo Valkonen + * Scratchpad toggle now creates a new one on screens if none is found. + +2005-07-03 20:17 UTC Tuomo Valkonen + * Oops. Updated statusd.popen_bgread. + +2005-07-02 20:23 UTC Tuomo Valkonen + * Improved statusd_mail error message. + +2005-07-02 20:11 UTC Tuomo Valkonen + * Added stderr handler parameter to ioncore.popen_bgread. + +2005-06-29 09:11 UTC Tuomo Valkonen + * Fixes to goto-when-scratchpad-is-active patch. + +2005-06-25 15:12 UTC Tuomo Valkonen + tagged ion-3ds-20050625 + +2005-06-25 15:11 UTC Tuomo Valkonen + * Updates to Finnish translation. + +2005-06-24 19:15 UTC Tuomo Valkonen + * The scratchpad can now be hidden automatically when going to another region. + +2005-06-24 10:33 UTC Tuomo Valkonen + * Fixed mplex layer1 initial stacking while there were regions visible on layer2. + +2005-06-22 10:32 UTC Tuomo Valkonen + * Don't set template in default cfg_statusbar.lua so that translated version gets used from mod_statusbar.lua. + +2005-06-22 10:31 UTC Tuomo Valkonen + * Set up locale in ion-statusd. + +2005-06-20 18:03 UTC Tuomo Valkonen + * Man-page completion script optimisation for full list. + +2005-06-20 17:53 UTC Tuomo Valkonen + * Invalidate old completion list when timed completion is set up. + +2005-06-18 09:15 UTC Tuomo Valkonen + * Improved encoding check error message. + +2005-06-16 09:37 UTC Stephan Wendt + * Replacement of indenting tabs by spaces in the ssh-hostnickname-completion-patch + +2005-06-14 05:35 UTC Stephan Wendt + * Hostnickname-completion for ssh + + Adds the feature to get hostnicknames, defined in ~/.ssh/config, presented + as possible completions for the ion-ssh-functionality. + + +2005-06-15 10:01 UTC Norbert Tretkowski + * Fixes for german po file from Jens Seidel. + +2005-06-14 19:16 UTC Tuomo Valkonen + * Initially show first instead of last completions of first column of them. + +2005-06-14 19:13 UTC Tuomo Valkonen + * Scroll completion list as selected entry is changed. + +2005-06-14 18:05 UTC Tuomo Valkonen + * Improved manual completion in auto-show-completions mode. + +2005-06-10 16:49 UTC Tuomo Valkonen + * Better tracking of multiple pending completions. + Completor routines now get a WComplProxy object as first parameter and + should use WComplProxy.set_completions instead of WEdln.set_completions + to set list of completions. Much of old code should be compatible since + completors were called in protected mode and only WEdln.set_completions + was callable. + +2005-06-09 22:32 UTC Tuomo Valkonen + * Size hints are now ignored in client-requested full screen mode. + +2005-06-09 22:13 UTC Tuomo Valkonen + * Changes in fullscreen focus policy. + - Inactive screens may switch to showing a client window when it requests + full screen mode even the window isn't focused (which is required of + client windows on the active screen). + +2005-06-07 13:05 UTC Tuomo Valkonen + tagged ion-3ds-20050607 + +2005-06-07 13:05 UTC Tuomo Valkonen + * Added some release notes. + +2005-06-06 20:56 UTC Tuomo Valkonen + * ioncore_goto_previous related fix. + - clientwin_managed_goto was calling ioncore_protect_previous instead + of ioncore_set_previous_of. + +2005-06-05 19:35 UTC Tuomo Valkonen + * Fixed _NET_SUPPORTING_WM_CHECK and _NET_SUPPORTED property setup. + +2005-06-05 19:33 UTC Tuomo Valkonen + * Call region_notify_change in ioncore_clear_tags. + +2005-05-31 16:12 UTC Tuomo Valkonen + * Do not automatically run autoconf in predist.sh. + (Maybe one or two more people will RTF README now.) + +2005-05-27 13:38 UTC Tuomo Valkonen + * Ignore _NET_ACTIVE_WINDOW request by default. + - Can be enabled by setting the ignore_net_active_window winprop to false. + +2005-05-27 13:35 UTC Tuomo Valkonen + * Exec-in-xterm (::) and command line completion had been broken by auto-show changes. + +2005-05-27 11:29 UTC Tuomo Valkonen + * Experimental auto-show-completions support. + +2005-05-27 11:27 UTC Tuomo Valkonen + * Minor improvement to binding compilation code. + +2005-05-19 14:07 UTC Tuomo Valkonen + * Added object parameter support to C-side timers. + +2005-05-19 13:32 UTC Tuomo Valkonen + * Updated mod_statusbar.set_sb to not use set_date. + +2005-05-19 10:01 UTC Tuomo Valkonen + * Encoding sanity check ignores dashes and case. + +2005-05-18 13:07 UTC Tuomo Valkonen + * Moved date monitor to ion-statusd. + +2005-05-15 19:54 UTC Tuomo Valkonen + * Fixed floatws iteration code. + +2005-05-14 11:46 UTC imadil@gmail.com + * mod_statusbar.get/set_sb() + +2005-05-13 07:47 UTC Tuomo Valkonen + * Miscellaneous fixes to recent changes. + +2005-05-12 22:19 UTC Tuomo Valkonen + * Added zeropad to WStatusBar.get_template. + +2005-05-12 19:29 UTC imadil@gmail.com + * WStatusBar:get_template() + +2005-05-12 18:12 UTC Tuomo Valkonen + * Fixed message box wrapping. + +2005-05-12 17:31 UTC Tuomo Valkonen + * Trap SIGCHLD earlier in startup. + +2005-05-12 17:19 UTC Tuomo Valkonen + * Fixed dummy implementation of mbrlen... + +2005-05-12 17:16 UTC Tuomo Valkonen + * Some focusing improvements. + +2005-05-12 15:04 UTC Tuomo Valkonen + * Added load_1min, load_5min and load_15min meters to the ion-statusd load script. + +2005-05-12 15:02 UTC Tuomo Valkonen + * Oops. Mblen did something else... + - Added str_len to ioncore/strings.c + +2005-05-12 14:32 UTC Tuomo Valkonen + * The statusbar support zero-padding of meters now. + The syntax is %[alignment][0count]. + For example: %02mail_total + +2005-05-12 14:20 UTC Tuomo Valkonen + * Organised WIonWS context menu possibly better. + +2005-05-11 21:40 UTC Tuomo Valkonen + * Select events earlier in client window init code. + +2005-05-10 18:08 UTC Tuomo Valkonen + * Added WIonWS.split_at and transpose_at for easier binding. + +2005-05-10 17:52 UTC Tuomo Valkonen + * Save state on SIGTERM if not running under a session manager. + +2005-05-10 16:21 UTC Tuomo Valkonen + * Load mod_sp by default. + +2005-05-09 19:53 UTC Tuomo Valkonen + * Don't shell-escape arguments to man-page viewer. + +2005-05-06 20:48 UTC Tuomo Valkonen + UNDO: The CF_DE_USE_XUTF8 option is now on by default. + This will cause Xutf8 routines to be used instead Xmb routines for UTF-8. + As of XFree86 4.3.0.1, the Xmb routines only use the iso10646-1 font in + a fontset and thus can't well display international text, while the Xutf8 + routines inconsitently use the iso10646 font only as a fallback. The + setting was differently previously because the Xutf8 routines were having + other problems that seem to be gone now... + +2005-05-08 16:14 UTC Jeremy Hankins + * Don't break old user configs in statusd_mail.lua + +2005-05-08 14:12 UTC Jeremy Hankins + * Fixed the logic triggering the retry interval in statusd_mail.lua + +2005-05-07 21:30 UTC nowan@nowan.org + * Oops, fix statusd_mail.lua + +2005-05-07 21:24 UTC nowan@nowan.org + * Added support for multiple mailboxes to statusd_mail.lua + +2005-05-08 15:07 UTC Tuomo Valkonen + * Moved ioncore_set_previous_of calls to region_managed_goto. + +2005-05-07 15:39 UTC Tuomo Valkonen + * Possibly better submenu placement in in-frame mode. + +2005-05-06 20:48 UTC Tuomo Valkonen + * The CF_DE_USE_XUTF8 option is now on by default. + This will cause Xutf8 routines to be used instead Xmb routines for UTF-8. + As of XFree86 4.3.0.1, the Xmb routines only use the iso10646-1 font in + a fontset and thus can't well display international text, while the Xutf8 + routines inconsitently use the iso10646 font only as a fallback. The + setting was differently previously because the Xutf8 routines were having + other problems that seem to be gone now... + +2005-05-06 20:46 UTC Tuomo Valkonen + * Faster fontset kludge for UTF-8 locales. + - Try to substitute only with -misc-fixed-... This works in some common + cases just like a more comprehensive but slower pattern would, but some + users will have to provide extra fonts to the fontset manually. + +2005-05-04 20:26 UTC Tuomo Valkonen + * Dock initial height (temporary) height was uninitialised. + +2005-05-02 14:55 UTC Tuomo Valkonen + tagged ion-3ds-20050502 + +2005-05-01 20:13 UTC Tuomo Valkonen + * Some changes in stock style files. + +2005-05-01 18:23 UTC Tuomo Valkonen + * Added some node-on-ws checks. + +2005-05-01 08:53 UTC Tuomo Valkonen + * Improved/fixed/updated WRegion.rqclose* documentation. + +2005-05-01 08:49 UTC Tuomo Valkonen + * WFloatWS client window rescue code ignores the status display. + +2005-04-30 14:49 UTC Tuomo Valkonen + * Bound left/right arrows in menus. + +2005-04-29 16:54 UTC Tuomo Valkonen + * Execution and file viewing queries catch stderr. + +2005-04-29 16:34 UTC Tuomo Valkonen + * Added stderr piping support to spawning routines. + +2005-04-29 13:31 UTC Tuomo Valkonen + * Maybe fixed activity notification. + +2005-04-29 10:06 UTC Tuomo Valkonen + * Changes in ionws context menu. + - Replaced floating split menu with floating toggle menu. + +2005-04-29 10:03 UTC Tuomo Valkonen + * Added routines for toggling floating of splits. + - WIonWS.set_floating for splits directly, and + - WIonWS.set_floating_at for frames. Takes a direction parameter. + +2005-04-23 12:55 UTC Tuomo Valkonen + * Added popen_bgread and exec to ion-statusd. + +2005-04-23 12:52 UTC Tuomo Valkonen + * Moved exec code to libmainloop. + +2005-04-22 18:32 UTC Tuomo Valkonen + * Oops. Oops. Oops. + +2005-04-22 18:23 UTC Tuomo Valkonen + * Oops. mainloop_defer was no longer being exported due to prefix re-export change. + +2005-04-21 22:38 UTC Tuomo Valkonen + * More maintainable re-exporting of libmainloop routines to Lua side. + +2005-04-21 14:09 UTC Tuomo Valkonen + * Ion-statusd no longer quits if no meters are loaded with -q. + +2005-04-20 13:51 UTC imadil@gmail.com + * cwin namelist + +2005-04-10 23:37 UTC Tuomo Valkonen + * Oops. + +2005-04-10 23:35 UTC Tuomo Valkonen + * Added clientwin_property_change_hook. + + - On the Lua side, the parameters to hook handlers are (WClientWin, atomid). + + +2005-04-06 11:02 UTC Tuomo Valkonen + tagged ion-3ds-20050406 + +2005-04-06 10:27 UTC Tuomo Valkonen + * Use maximum seen value width for statusbar field width. + +2005-04-05 12:19 UTC Tuomo Valkonen + * Frame maximize fixes and improvements. + +2005-04-03 12:27 UTC Tuomo Valkonen + * Changes (crash fix?) in name allocation code. + +2005-04-02 15:38 UTC Tuomo Valkonen + * Menu scrolling improvements. + +2005-04-02 14:33 UTC Tuomo Valkonen + * Improved menu positioning patch etc. + +2005-04-02 12:36 UTC imadil@gmail.com + * repositioning pmenus that appear at the bottom of the screen + +2005-04-01 22:59 UTC Tuomo Valkonen + * Fixed new transients being attempted to be stacked over themselves. + +2005-04-01 17:58 UTC Tuomo Valkonen + * Fixed table.join to check for 'entry==nil' instead of 'not entry'. + +2005-04-01 17:57 UTC Tuomo Valkonen + * Fixed a typo. + +2005-03-29 22:01 UTC Tuomo Valkonen + * Example in cfg_sp.lua was missing a comma. + +2005-03-29 14:07 UTC Tuomo Valkonen + * Removed remainig statusd_load debug message. + +2005-03-28 20:27 UTC Tuomo Valkonen + * Oops. New regions within screens were added after current instead of at end. + +2005-03-27 18:40 UTC Tuomo Valkonen + * Some statusbar and line editor flicker reduction (?). + +2005-03-27 13:57 UTC Tuomo Valkonen + * Drawing engine api improvements. + +2005-03-26 13:27 UTC Tuomo Valkonen + * Brush drawing routines are no longer passed the window. + +2005-03-26 12:35 UTC Tuomo Valkonen + * Some WIonWS routines now accept "any" as direction. + +2005-03-26 09:24 UTC Tuomo Valkonen + * WFrame.set_tabbar parameters were inverted. + +2005-03-25 19:20 UTC Tuomo Valkonen + * Display again ?? when statusbar meter is unset. + +2005-03-25 16:48 UTC Tuomo Valkonen + * Added WMPlex.l2_is/set_passive routines. + +2005-03-25 11:50 UTC Tuomo Valkonen + * Oops. Menu entry for tagging hadn't been updated. + +2005-03-22 14:30 UTC Tuomo Valkonen + tagged ion-3ds-20050322 + +2005-03-22 14:30 UTC Tuomo Valkonen + * Prepared release notes for a release. + +2005-03-21 10:26 UTC Tuomo Valkonen + * Added some release notes. + +2005-03-21 08:52 UTC Tuomo Valkonen + * Use input-menu-pmenu and tab-menuentry-pmenu styles for drop-down menus. + +2005-03-21 08:34 UTC Tuomo Valkonen + * Lessened string length recalculation in query listing code. + +2005-03-20 15:03 UTC Tuomo Valkonen + * Added ::cmd syntax for running commands with ion-runinxterm -w. + +2005-03-20 14:54 UTC Tuomo Valkonen + * ion-runinxterm script improvements. + +2005-03-20 13:30 UTC Tuomo Valkonen + * Statusbar substyle background colour gets used now. + +2005-03-20 13:10 UTC Tuomo Valkonen + * Also changed region activity exports. + + - WRegion.clear_activity and WRegion.notify_activity were replaced with + WRegion.set_activity with a set/unset/toggle parameter. + +2005-03-20 09:44 UTC Tuomo Valkonen + * Added some release notes (on the toggle export changes). + +2005-03-19 22:06 UTC Tuomo Valkonen + * More changes/fixes in layer2 focus policy. + +2005-03-19 21:09 UTC Tuomo Valkonen + * Many exported toggle functions were changed and renamed. + + The following functions now take a string parameter indicating + whether to toggle, set, or unset the property. + + - WClientwin.set_fullsreen (replaces WClientWin.toggle_fullscreen) + - WRegion.set_tagged (replaces WRegion.tag/untag/toggle_tag) + - WFrame.set_tabbar (replaces WFrame.toggle_tabbar) + - WFrame.set_shaded (replaces WFrame.toggle_shade) + - WFloatFrame.set_sticky (replaces WFloatFrame.toggle_sticky) + - WMPlex.l2_set_hidden (replaces WMPlex.l2_hide/show) + - mod_sp.set_shown(_on) (replaces mod_sp.toggle(_on)) + - mod_dock.set_floating_shown_on (replaces mod_dock.toggle_floating_on) + + Many missing is_ exports corresponding to the above were also + added. + +2005-03-19 20:40 UTC Tuomo Valkonen + * Oops. CLIENTWIN_IS_FULLSCREEN macro didn't work anymore. + +2005-03-19 19:47 UTC Tuomo Valkonen + * Oops. exports.c didn't have dependencies anymore. + +2005-03-19 19:13 UTC Tuomo Valkonen + * Use exports.h generated by libextl-mkexports. + +2005-03-19 18:26 UTC Tuomo Valkonen + * Client windows can now be un-fullscreened without prior frame. + +2005-03-19 00:34 UTC Tuomo Valkonen + * ioncore.popen_bgread also returns pid. + +2005-03-18 18:35 UTC Tuomo Valkonen + * Added ioncore_sigchld_hook. + +2005-03-18 18:31 UTC Tuomo Valkonen + * Exec routines return PID (or -1 on error). + +2005-03-18 18:30 UTC Tuomo Valkonen + * Moved hook code to libmainloop. + +2005-03-17 08:57 UTC Tuomo Valkonen + * Attempts to fix problems with gcc/ld export-dynamic option varying. + + - Configurable in system.mk as the EXPORT_DYNAMIC variable + + - Defaults to '-Xlinker --export-dynamic' now instead of '-export-dynamic' + +2005-03-16 22:56 UTC Tuomo Valkonen + * Fixed coding style in contribution. + + * Contributors _please_ follow my coding style in the future!! + +2005-03-16 22:20 UTC Edwin Steiner + * winprop_gravity + add 'gravity' and 'transient_gravity' winprops + +2005-03-16 19:10 UTC Tuomo Valkonen + * Oops. Modules should be built before ioncore for PRELOAD_MODULES. + +2005-03-16 18:07 UTC Tuomo Valkonen + * Some more layer list scanning optimisations. + +2005-03-16 18:04 UTC Tuomo Valkonen + * Split out some code from mplex.c to llist.c. + +2005-03-15 23:32 UTC Tuomo Valkonen + * Some WMPlex switch code optimisations/simplifications. + +2005-03-15 22:45 UTC Tuomo Valkonen + * WMPlex layer2 visiblity synchronisation fixes and focus policy changes. + +2005-03-15 21:08 UTC Tuomo Valkonen + * Ionws flip/transpose fixes. + +2005-03-15 14:25 UTC Tuomo Valkonen + * mod_query.query_menu can now display context menus. + +2005-03-14 18:27 UTC Tuomo Valkonen + * Dummy regions are no longer given name/registered. + + - WPaneHandles, WInfoWins and such are not accessible to the scripter + (from ioncore.region_list). + +2005-03-14 16:16 UTC Tuomo Valkonen + * Fixed goto_previous and tab switch (pointer) interaction. + +2005-03-13 21:22 UTC Tuomo Valkonen + * Alternative fix to bindmap precedence. + + - "owned" bindmaps are added last on list, while region's own bindmaps + are listed first. + +2005-03-13 20:58 UTC Tuomo Valkonen + UNDO: Changed region bindmap linking order. + + - Frame bindmaps should now take precedence over workspace bindmaps + (that are grabbed on frames as workspaces don't have normal windows + associated to them). + +2005-03-13 21:18 UTC Tuomo Valkonen + * Panehandles (floating splits) are now reparented correctly. + +2005-03-13 20:58 UTC Tuomo Valkonen + * Changed region bindmap linking order. + + - Frame bindmaps should now take precedence over workspace bindmaps + (that are grabbed on frames as workspaces don't have normal windows + associated to them). + +2005-03-13 20:41 UTC Tuomo Valkonen + * Removed #warnings (and fixed a few). + +2005-03-13 13:18 UTC Tuomo Valkonen + * Removed ioncore.root_windows export. + + - ioncore.region_list("WRootWin") does the task. + +2005-03-13 13:06 UTC Tuomo Valkonen + * ion-runinxterm uses the whole command as title now. + +2005-03-13 13:04 UTC Tuomo Valkonen + * PWM menu definition updates and fixes. + +2005-03-13 13:02 UTC Tuomo Valkonen + * Removed the ion-man script. + + - ioncore.exec_on now interprets the ':cmd' ion-runinxterm syntax. + + - mod_query.query_man accepts the man command to use as parameter, + defaulting to ':man'. + + - The default for query_ssh is also ':ssh' now. + +2005-03-11 14:44 UTC Tuomo Valkonen + * clientwin_unmapped_hook parameter was wrong. + (Stupid semi-weakly typed languages...) + +2005-03-10 08:29 UTC Tuomo Valkonen + * Removed ion-ssh wrapper script. + + - mod_query.query_ssh defaults to running ssh with ion-runinxterm, but + accepts additional parameter specifying another program. + +2005-03-09 21:58 UTC Tuomo Valkonen + * Added field alignment code to statusbar module. + +2005-03-09 12:10 UTC Tuomo Valkonen + * Updated README information on F5/F6 keys. + +2005-03-09 12:06 UTC Tuomo Valkonen + * Changes in mod_query.query_editfile/viewfile. + + - These functions take the script (and prompt) as optional parameter now, + defaulting to run-mailcap. + + - Removed the ion-edit and ion-view scripts. + +2005-03-09 11:36 UTC Tuomo Valkonen + * More mplex region indexing fixes. + After the changes in layer list code recently and following attach index fix, + regions were now loaded in wrong order at startup. + +2005-03-02 12:03 UTC Tuomo Valkonen + UNDO: Removed WScreen.set_managed_offset. + It seems unncessary now that there's the statusbar, and it's just an ugly + kludge for dubious uses anyway. + +2005-03-08 14:32 UTC Tuomo Valkonen + * Scratchpad and dock default toggle bindings changed. + + - Mod1+space now toggles the scratchpad, and + + - Mod1+D toggles the dock. + +2005-03-08 14:10 UTC Tuomo Valkonen + * The dock is floating by default now. + + - New users shouldn't at firs try be bewildered by the dock not being able + to coexist with the statusbar. + + +2005-03-08 08:25 UTC Tuomo Valkonen + * Exported classes are now marked with EXTL_EXPORT. + +2005-03-08 07:31 UTC Tuomo Valkonen + * Stdisp was being destroyed along with floatws. + +2005-03-07 17:17 UTC Tuomo Valkonen + * Fixed open quote handling in cmdline completion improvements. + +2005-03-07 17:04 UTC Tuomo Valkonen + * Default attach index was wrong for WMPlex.attach*. + + - Should be after current instead of last. + + +2005-03-07 08:45 UTC Tuomo Valkonen + * Fixed focusingn of non-passive mplex layer2 regions. + +2005-03-06 10:35 UTC Tuomo Valkonen + * Check that reparenting can be done before trying to use a placeholder. + + - Added pholder_(do_)target and pholder_(do_)check_reparent + + +2005-03-06 10:24 UTC Tuomo Valkonen + * More helper routines from attach.c are globally available. + +2005-03-05 16:25 UTC Tuomo Valkonen + * Removed pholder_stale as useless given placeholder redirection. + +2005-03-05 12:42 UTC Tuomo Valkonen + * Changed how query and menu cancel and finish routines destroy the region. + +2005-03-05 09:15 UTC Tuomo Valkonen + * Paths in mod_statusbar file headers were wrong. + +2005-03-04 08:59 UTC Tuomo Valkonen + tagged ion-3ds-20050304-1 + +2005-03-04 08:59 UTC Tuomo Valkonen + * predist.sh improvements. + +2005-03-04 08:54 UTC Tuomo Valkonen + * Oops. Completion improvements had been broken by further improvements. + +2005-03-04 08:38 UTC Tuomo Valkonen + tagged ion-3ds-20050304 + +2005-03-04 08:38 UTC Tuomo Valkonen + * Added some release notes. + +2005-03-04 08:24 UTC Tuomo Valkonen + * Floating split load routine now adds handle widths to given sizes by default. + + - Override by setting tls_brs_incl_handles to true. + + +2005-03-03 21:00 UTC Tuomo Valkonen + * Improved mod_query.query_exec completion. + + - Arguments are now parsed, completed and escaped. + + +2005-03-03 10:15 UTC Tuomo Valkonen + * Removed reference to svn from version.h. + +2005-03-02 11:55 UTC Tuomo Valkonen + * Call region_do_warp_alt also in protected mode. + +2005-03-02 11:48 UTC Tuomo Valkonen + * Changes to ionws_placement_alt hook to make it callable in protected mode. + +2005-03-01 23:53 UTC Tuomo Valkonen + * New export: ioncore.defer. + +2005-03-01 22:59 UTC Tuomo Valkonen + * Many hook calls are now made in protected mode. + +2005-03-02 12:03 UTC Tuomo Valkonen + * Removed WScreen.set_managed_offset. + It seems unncessary now that there's the statusbar, and it's just an ugly + kludge for dubious uses anyway. + +2005-03-01 22:54 UTC Tuomo Valkonen + * WIonWS.current and WFloatWS.current were unnecessarily exported. + + - WRegion.current is already exported. + +2005-02-28 21:07 UTC Tuomo Valkonen + * Rescue code simplification/clean-up. + +2005-02-28 19:36 UTC Tuomo Valkonen + * Updated Finnish translation. + +2005-02-28 19:26 UTC Tuomo Valkonen + * Changes and unification in rqclose code. + +2005-02-28 17:01 UTC Tuomo Valkonen + * The statusd_mail handles missing mailbox more gracefully. + - The retry_interval parameter (default: 10min) controls for how long + it waits for next update attempt on error condition. + +2005-02-28 06:50 UTC Tuomo Valkonen + * Fixed a stupid omission in new client window rescue code. + +2005-02-28 06:08 UTC Tuomo Valkonen + * Fixed floatws drag&drop segfault. + - There was a leftover floatws_add_managed call in floatws_handle_drop after + creation of floatws_create_frame. + +2005-02-28 06:07 UTC Tuomo Valkonen + * Oops. Removed a leftover debug printf. + +2005-02-27 13:16 UTC Tuomo Valkonen + tagged ion-3ds-20050227 + +2005-02-27 13:16 UTC Tuomo Valkonen + * Updated release notes. + +2005-02-27 11:47 UTC Tuomo Valkonen + * Session management module now uses placeholders. + - Order of windows in mplexes is now remembered under SM. + +2005-02-27 11:46 UTC Tuomo Valkonen + * WMPlexPHolder reorganisation fixes. + +2005-02-27 08:38 UTC Tuomo Valkonen + * Added routine to go to placeholder location. + - Active client window being closed in full screen mode now returns + to original position. + +2005-02-27 07:17 UTC Tuomo Valkonen + * Most source files don't need region-iter.h anymore. + +2005-02-27 07:16 UTC Tuomo Valkonen + * Oops. WMPlex layer list code changes were incomplete. + +2005-02-26 23:09 UTC Tuomo Valkonen + * WMPlex layer list code clean-up. + +2005-02-26 21:11 UTC Tuomo Valkonen + * Changes to work with new libtu list routines. + +2005-02-26 19:01 UTC Tuomo Valkonen + * Placeholder code improvements. + - Client window rescue code now uses placeholders. + - Added client window rescue placeholders; returning from full screen mode + now works on floatws. + +2005-02-26 12:02 UTC Tuomo Valkonen + * Added simple floatws pholder. + +2005-02-26 13:30 UTC Tuomo Valkonen + * Added file mtime/ctime checking to ion-statusd. + - Mailboxes can be checked a bit more often now without significant load. + + +2005-02-26 09:03 UTC Tuomo Valkonen + * Client window fullscreen toggle uses placeholders now. + The code does not yet do anything if the target of the initial placeholder + dies, and thus does not work on floatws's that also do not yet provide + placeholders. + + +2005-02-26 08:40 UTC Tuomo Valkonen + * Added a routine for checking whether a placeholder is stale. + +2005-02-26 08:31 UTC Tuomo Valkonen + * Added region_managed_get_pholder for acquiring a placeholder. + +2005-02-26 08:21 UTC Tuomo Valkonen + * Added basic placeholder support. + - Basic placeholder class WPHolder. + - WMPlexPHolder for holding place in mplexes. + +2005-02-24 09:28 UTC Tuomo Valkonen + * Removed traces of old managed lists. + Regions no longer contain link pointers for those lists etc. The + region_rescue_clientwins stuff is currently disabled and needs to be + rewritten when placeholder stuff is implemented. + +2005-02-24 09:22 UTC Tuomo Valkonen + * WRootWin no longer uses a screen_list. + +2005-02-24 09:09 UTC Tuomo Valkonen + * WIonWS now uses symlist for managed list. + +2005-02-24 08:49 UTC Tuomo Valkonen + * WDock no longer uses managed_list. + +2005-02-24 08:45 UTC Tuomo Valkonen + * WFloatWS doesn't use a special managed_list. + The stacking list is enough. + +2005-02-24 08:12 UTC Tuomo Valkonen + * WClientWin uses symlist for transient_list. + +2005-02-24 08:34 UTC Tuomo Valkonen + * Oops. All mplex stuff was being now saved on layer 2. + +2005-02-23 19:40 UTC Tuomo Valkonen + * WMPlex managed list changed to use proxy nodes. + This should help implementing placeholders. + +2005-02-23 06:29 UTC Tuomo Valkonen + * Updated predist.sh for darcs. + +2005-02-23 06:28 UTC Tuomo Valkonen + * Set install script to "sh install-sh" + +2005-02-23 06:12 UTC Tuomo Valkonen + tagged ion-3-svn2darcs + +2005-02-22 21:30 UTC tuomov + * trunk: changeset 1974 + Another autoconf hack. + +2005-02-20 14:42 UTC tuomov + * trunk: changeset 1973 + WMPlex layer 2 region geometry is now remembered. + +2005-02-20 14:38 UTC tuomov + * trunk: changeset 1972 + Added missing include. + +2005-02-20 12:09 UTC tuomov + * trunk: changeset 1970 + Autoconf script fixes (by matthieu moy). + +2005-02-20 09:59 UTC tuomov + * trunk: changeset 1969 + Fixed names of workspace-specific frame bindmaps. + +2005-02-20 08:43 UTC tuomov + * trunk: changeset 1968 + Updated cfg_ionws.lua. + +2005-02-19 08:09 UTC tuomov + * trunk: changeset 1967 + Fixed tab switch focus change when a non-passive layer2 region was + being displayed. + +2005-02-18 21:45 UTC tuomov + * trunk: changeset 1966 + Fixed a typo (?) that could cause floatws stacking code to lock up. + +2005-02-16 05:04 UTC tuomov + * trunk: changeset 1965 + Update WMPlex.l2_hide/show docs. + +2005-02-15 06:06 UTC tuomov + * trunk: changeset 1963 + Added null checks in mplex_l2_hide/show. + +2005-02-14 18:34 UTC tuomov + * trunk: changeset 1962 + removed redundant ionws stacking code, and fixed a possible problem. + +2005-02-14 18:08 UTC tuomov + * trunk: changeset 1961 + Added -lintl comment in system.mk + +2005-02-14 13:59 UTC tuomov + * trunk: changeset 1960 + Added null check; not a member function. + +2005-02-14 09:07 UTC tuomov + * trunk: changeset 1959 + kludgeconf sun f1x remap detection fix. + +2005-02-14 00:04 UTC tuomov + * trunk: changeset 1958 + Oops. + +2005-02-13 23:36 UTC tuomov + * trunk: changeset 1957 + Oops. pwm3.de.in wasn't svn add'ed. + +2005-02-13 22:59 UTC tuomov + * trunk: changeset 1956 + Added mod_sp.toggle. + +2005-02-13 21:32 UTC tuomov + * trunk: changeset 1955 + Added German translation by Schott Robert. + +2005-02-12 18:23 UTC tuomov + * trunk: changeset 1951 + Event mask is now stored for WWindows. + +2005-02-11 18:10 UTC tuomov + * trunk: changeset 1950 + ion-statusd now communicates width templates to mod_statusbar. + +2005-02-11 13:35 UTC tuomov + * trunk: changeset 1949 + - Parameters can now be passed ion-statusd. + + - Some cfg_statusbar usage changes. + +2005-02-06 10:13 UTC tuomov + * trunk: changeset 1947 + Added WRegion.is_activity. + +2005-02-03 21:44 UTC tuomov + * trunk: changeset 1946 + Some info window fixes. + +2005-02-01 12:01 UTC tuomov + * trunk: changeset 1945 + Fixed loading of small frames with hidden tab-bar. + +2005-01-31 17:45 UTC tuomov + * trunk: changeset 1944 + Only use resize increment and base size from current client window + size hints when deciding frame size hints. + +2005-01-31 11:32 UTC tuomov + * trunk: changeset 1941 + Added support for coloured statusbar elements. + +2005-01-31 09:17 UTC tuomov + * trunk: changeset 1940 + - Moved some infowin code to mod_statusbar. + + - mod_statusbar template processing/passing status to c side changes. + +2005-01-29 23:43 UTC tuomov + * trunk: changeset 1939 + - Fixed docked window resize handling. + + - WDock needs to implement region_rqgeom_clientwin, as otherwise the + WClientWin configure request code uses the previously available + space and makes no request. + +2005-01-27 19:00 UTC tuomov + * trunk: changeset 1938 + Fixed WMPlex.l2_hidden segfault when called with nil region. + +2005-01-24 21:59 UTC tuomov + * trunk: changeset 1937 + Fixed hook copy-paste bug. + +2005-01-24 20:58 UTC tuomov + * trunk: changeset 1936 + Added DL_LIBS to ion-statusd linking flags (although it doesn't itself + depend on it) thanks to broken lua installs. + +2005-01-16 11:52 UTC tuomov + tagged ion-3ds-20050116 + +2005-01-16 11:07 UTC tuomov + * trunk: changeset 1929 + Added documentation for X property access exports. + +2005-01-15 21:22 UTC tuomov + * trunk: changeset 1927 + Oops. Broken rename. + +2005-01-15 21:20 UTC tuomov + * trunk: changeset 1925 + tabularx header changes. + +2005-01-15 16:28 UTC tuomov + * trunk: changeset 1921 + Fixed table in doc. comment. + +2005-01-14 14:28 UTC tuomov + * trunk: changeset 1919 + Fixed doc comment. + +2005-01-13 15:02 UTC tuomov + * trunk: changeset 1916 + Fixed clientwin_unmapped_hook. + +2005-01-03 22:24 UTC tuomov + * trunk: changeset 1911 + Added experimental dock (drag&)drop support. It must be enabled by + defining CF_EXPERIMENTAL_DOCK_DROP. + +2005-01-02 13:59 UTC tuomov + * trunk: changeset 1905 + Year changed. + +2004-12-27 21:30 UTC tuomov + * trunk: changeset 1903 + Made complete_name available in mod_query. + +2004-12-27 21:30 UTC tuomov + * trunk: changeset 1902 + Fixed mod_statusbar Makefile to rm before ln. + +2005-02-23 00:41 UTC tuomov + * trunk: changeset 1901 + Made ext_statusbar into a partially C-side module containing a + WStatusBar class that can have a bindmap. + +2004-12-17 00:02 UTC tuomov + * trunk: changeset 1898 + oops. + +2004-12-16 23:56 UTC tuomov + * trunk: changeset 1897 + Created directory for ion-knewt branch. + +2004-12-11 20:06 UTC tuomov + * trunk: changeset 1896 + Added mod_mgmtmode management mode module. + +2004-12-07 15:42 UTC tuomov + * trunk: changeset 1895 + Fixed (?) tabdrag focus. + +2004-11-26 17:41 UTC tuomov + * trunk: changeset 1894 + Do not show activity/urgency notification for docked and other client + windows that can not be easily focused. + +2004-11-26 08:18 UTC tuomov + * trunk: changeset 1893 + PWM bindings setup script now simply loads Ion bindings and unbinds + stuff that would use mod_query. + +2004-11-25 08:40 UTC tuomov + * trunk: changeset 1892 + Control modifier now is required for N/P in menus to not interfere + with typeahead. + +2004-11-17 18:46 UTC tuomov + * trunk: changeset 1891 + Applied patch with some minor menu improvements. + +2004-11-17 18:45 UTC tuomov + * trunk: changeset 1890 + Typo fixes. + +2004-11-16 23:01 UTC tuomov + * trunk: changeset 1888 + Removed another dupe call. + +2004-11-16 12:51 UTC tuomov + * trunk: changeset 1887 + Menu definition code and documentation fixes. + +2004-11-15 21:32 UTC tuomov + * trunk: changeset 1886 + Removed duplicate mod_ionws_register_exports call. + +2004-11-13 12:40 UTC tuomov + * trunk: changeset 1884 + mod_query runfile and viewfile use workspace working directory. + +2004-11-13 00:52 UTC tuomov + * trunk: changeset 1883 + Oops. + +2004-11-12 20:34 UTC tuomov + * trunk: changeset 1881 + Oops. + +2004-11-12 20:12 UTC tuomov + * trunk: changeset 1880 + Changed - to _ in ioncore-*.(lua|lc). + +2004-11-12 20:12 UTC tuomov + * trunk: changeset 1879 + Fixed floatws reparent. + +2004-11-10 14:41 UTC tuomov + * trunk: changeset 1878 + Added support for workspace-specific working directories. These can be + changed and viewed with the internally overridden commands 'cd' and + 'pwd' in the F3 execution query. + +2004-11-10 01:48 UTC tuomov + * trunk: changeset 1877 + Set REGION_SKIP_FOCUS on pane handles. + +2004-11-07 18:18 UTC tuomov + * trunk: changeset 1876 + More misc. fixes. + +2004-11-07 18:00 UTC tuomov + * trunk: changeset 1875 + Oops. Fixed a potential segfault. + +2004-11-05 17:35 UTC tuomov + * trunk: changeset 1874 + Winprop name matching now available as ioncore.match_winprop_name. + +2004-11-05 16:38 UTC tuomov + * trunk: changeset 1873 + %% works in statusbar template. + +2004-11-05 16:22 UTC tuomov + * trunk: changeset 1872 + ion-statusd parameters are now automatically deduced by ext_statusbar + from the template. + +2004-11-04 21:34 UTC tuomov + * trunk: changeset 1870 + Load cfg_user.lua at end of cfg_ion.lua. + +2004-11-04 21:30 UTC tuomov + * trunk: changeset 1869 + Documented ext_statusbar.update. + +2004-11-04 21:20 UTC tuomov + * trunk: changeset 1868 + Fixed winprop name matching. + +2004-11-04 14:59 UTC tuomov + tagged ion-3ds-20041104 + +2004-11-04 14:56 UTC tuomov + * trunk: changeset 1866 + OOps. + +2004-11-04 14:38 UTC tuomov + * trunk: changeset 1865 + Removed changelog files. + +2004-11-04 14:32 UTC tuomov + * trunk: changeset 1864 + Remove autom4te.cache in predist.sh + +2004-11-04 14:30 UTC tuomov + * trunk: changeset 1863 + Added some new release notes. + +2004-11-04 14:19 UTC tuomov + * trunk: changeset 1862 + Some more minor statusbar improvements. + +2004-11-04 12:05 UTC tuomov + * trunk: changeset 1861 + Some clean-up. + +2004-11-04 11:23 UTC tuomov + * trunk: changeset 1860 + Added type of menus that grab input and allow cycling through the menu + with a given key until all modifiers are released. + +2004-11-04 04:16 UTC tuomov + * trunk: changeset 1859 + Still problems with timers being gc'd in statusd; the local timer + references need to be used instead of the timer parameter to the + handler. + +2004-11-04 04:02 UTC tuomov + * trunk: changeset 1858 + Some more statusbar/statusd improvements. + +2004-11-03 22:10 UTC tuomov + * trunk: changeset 1857 + Status meters should retain a local reference to their timers or it + can be collected as garbage. + +2004-11-03 19:56 UTC tuomov + * trunk: changeset 1856 + Moved potentially blocking statusbar meters to ion-statusd. + +2005-02-23 00:39 UTC tuomov + * trunk: changeset 1855 + Moved some mainloop stuff to libmainloop. + +2004-10-30 14:58 UTC tuomov + * trunk: changeset 1853 + Some README updates. + +2004-10-30 14:50 UTC tuomov + * trunk: changeset 1852 + Added WIonWS.split for splitting at a node instead of just frame/root. + +2004-10-23 11:05 UTC kurem + * trunk: changeset 1851 + Update Czech translation + +2004-10-23 10:39 UTC tuomov + * trunk: changeset 1850 + Arbitrary winprop matching criteria can now be used by setting the + 'match' field to a matching function with parameters: (winprop, cwin) + and a boolean return value. + +2004-10-23 00:39 UTC tuomov + * trunk: changeset 1849 + Removed _ION_KLUDGES property support as the Lua side now has direct + access to window properties. + +2004-10-23 00:33 UTC tuomov + * trunk: changeset 1848 + Added exports to access window properties from Lua side. + +2004-10-23 00:14 UTC tuomov + * trunk: changeset 1847 + Added table.map to lua table library extensions. + +2004-10-19 16:04 UTC tuomov + * trunk: changeset 1846 + Changed order of some libs linking commands due to gcc's brain-damaged + handling of .a files. (-llib only loads those parts of liblib.a + required by objects that have appeared before it on the command line.) + +2004-10-19 15:08 UTC tuomov + * trunk: changeset 1845 + Added raise delay to help with an occasional slight annoyance of + floating splits. + +2004-10-16 10:35 UTC tuomov + * trunk: changeset 1841 + predist.sh wasn't making modifications to system.mk + +2004-10-16 08:43 UTC tuomov + * trunk: changeset 1840 + Stdisp parallel movement size calculations were reversed and an old + size swap had not been removed. + +2004-10-15 16:50 UTC tuomov + * trunk: changeset 1839 + Updated cs.po. + +2004-10-15 08:45 UTC tuomov + * trunk: changeset 1838 + ion-completefile Makefile had not been updated for new lib build + dependency handling. + +2004-10-15 08:41 UTC tuomov + * trunk: changeset 1837 + ... + +2004-10-15 08:34 UTC tuomov + * trunk: changeset 1836 + libs.mk.dist fix. + +2004-10-14 12:21 UTC tuomov + * trunk: changeset 1835 + Removed the file distdep. + +2004-10-14 10:55 UTC tuomov + * trunk: changeset 1834 + Added -snapshot option to predist.sh. + +2004-10-11 15:34 UTC tuomov + * trunk: changeset 1827 + Added new ionws context menu entries for splitting. + +2004-10-11 13:44 UTC tuomov + * trunk: changeset 1820 + Floating splits can now be had on WIonWSs as well. + +2004-10-11 13:44 UTC tuomov + * trunk: changeset 1819 + Parallel stdisp rotations had not been updated to calculate geometries + properly now that we switch the places of the splits. + +2004-10-11 11:39 UTC tuomov + * trunk: changeset 1818 + Indentation fix. + +2004-10-09 16:27 UTC tuomov + * trunk: changeset 1817 + Added history search to line editor. (Scrolling through history + entries the beginnings of which match bol-point.) + +2004-10-09 16:00 UTC tuomov + * trunk: changeset 1816 + Moved WSplitFloat to another file from the other panews split + extensions. + +2004-10-09 15:51 UTC tuomov + * trunk: changeset 1815 + - Added some strings from mkman.lua to be translated for improved + translation of manual page bindings reference. + + - Finnish translation updates. + +2004-10-09 12:54 UTC tuomov + * trunk: changeset 1812 + Minor libextl-related rename. + +2004-10-09 11:02 UTC tuomov + * trunk: changeset 1808 + libextl-related 'make pot' fix. + +2004-10-09 06:13 UTC tuomov + * trunk: changeset 1803 + Moved also mkexports.lua to libextl. + +2004-10-09 05:33 UTC tuomov + * trunk: changeset 1797 + Added predist.sh. + +2004-10-09 05:05 UTC tuomov + * trunk: changeset 1795 + Separated libextl from the Ion source tree. + +2004-10-09 04:37 UTC tuomov + * trunk: changeset 1792 + Workspaces no longer propagate close requests. + +2004-10-09 04:30 UTC tuomov + * trunk: changeset 1791 + Removed obsolete autoconf note from README. + +2004-10-06 16:27 UTC tuomov + * trunk: changeset 1790 + Moved part of ioncore/ to libextl/. + +2004-10-06 14:33 UTC tuomov + * trunk: changeset 1788 + Fixed line editor display update on history selection. + +2004-10-06 10:58 UTC tuomov + * trunk: changeset 1787 + Some WIonWS fields were not being initialised. + +2004-10-06 10:17 UTC tuomov + * trunk: changeset 1786 + ion-completeman now understands manual sections. + +2004-10-06 09:39 UTC tuomov + * trunk: changeset 1785 + ioncore_g.screen_notify was not being initialised. + +2004-10-06 09:35 UTC tuomov + * trunk: changeset 1784 + Don't complain if no charset is given in LC_CTYPE if it is "C" or + "POSIX". + +2004-10-06 01:34 UTC tuomov + * trunk: changeset 1783 + Fixed floatws restack. + +2004-10-05 09:03 UTC tuomov + * trunk: changeset 1782 + comma and period were reversed inworkspace cycling. + +2004-10-05 07:38 UTC tuomov + * trunk: changeset 1781 + Changed some set_focus calls to warp calls. + +2004-10-05 07:30 UTC tuomov + * trunk: changeset 1780 + -V and -h options were not being supported after libtu optparser + simplifications. Fixed that. + +2004-10-05 05:07 UTC tuomov + tagged ion-3ds-20041005 + +2004-10-05 05:04 UTC tuomov + * trunk: changeset 1778 + Minor history fix. + +2004-10-05 04:47 UTC tuomov + * trunk: changeset 1777 + - Added _NET_ACTIVE_WINDOW (request and property) support. + + - Moved netwm atoms away from ioncore_g. + +2004-10-05 03:54 UTC tuomov + * trunk: changeset 1776 + Opening a new window on a panews focuses it if an unused area had the + focus. + +2004-10-05 00:41 UTC tuomov + * trunk: changeset 1775 + Moved region_set_await_focus call from xwindow_do_set_focus to where + it is called. + +2004-10-04 22:50 UTC tuomov + * trunk: changeset 1774 + Minor goto_previous fix. (It had been broken at some point.) + +2004-10-03 14:11 UTC tuomov + * trunk: changeset 1773 + Warp propagation improvements. + +2004-10-02 02:29 UTC tuomov + * trunk: changeset 1772 + Translation updates. + +2004-10-02 02:22 UTC tuomov + * trunk: changeset 1771 + Oops, history.h was missing. + +2004-10-02 02:13 UTC tuomov + * trunk: changeset 1770 + Added split and transpose bindings to WIonWS context menu. + +2004-10-02 02:07 UTC tuomov + * trunk: changeset 1769 + - Added WSplitSplit.flip. + + - WSplitFloat should properly transpose now. + +2004-09-30 17:24 UTC tuomov + * trunk: changeset 1768 + Added context-specific query histories. + +2004-09-28 16:42 UTC tuomov + * trunk: changeset 1767 + Added ioncore.set_selection and ioncore.request_selection (with a + continuation function as parameter to the latter) to deal with + selections from Lua code. + +2004-09-28 15:11 UTC tuomov + * trunk: changeset 1763 + Binding changes: Mod1+K comma/period moves tab left/right. + Mod1+comma/period switches to previous/next workspace. Corresponding + bindings for Left/Right keys were removed. + +2004-09-28 14:58 UTC tuomov + * trunk: changeset 1762 + Improved support for context menus. + +2004-09-17 20:17 UTC tuomov + * trunk: changeset 1761 + Renamed clientwin_added_hook to clientwin_mapped_hook and added + clientwin_unmapped_hook with the X window id as parameter. + +2004-09-17 20:09 UTC tuomov + * trunk: changeset 1760 + Replaced frame_activated_hook (and frame_inactivated_hook) with + region_activated_hook (region_inactivated_hook) called when the region + is get focus (loses focus). + +2004-09-17 19:55 UTC tuomov + * trunk: changeset 1759 + Some eventmask macro changes/cleanup. + +2004-09-17 19:43 UTC tuomov + * trunk: changeset 1758 + Moved hook parameter structures from source to header files and added + notes of parameters of all hooks to headers. + +2004-09-16 23:42 UTC tuomov + * trunk: changeset 1757 + Oops. Workspaces were being destroyed when stdisp was removed. + +2004-09-16 23:39 UTC tuomov + * trunk: changeset 1756 + REGION_PARENT macro changes. + +2004-09-16 23:09 UTC tuomov + * trunk: changeset 1755 + Improved handling of small unused spaces in panews. + +2004-09-16 19:32 UTC tuomov + * trunk: changeset 1753 + Some minor clean-up. + +2004-09-16 18:44 UTC tuomov + * trunk: changeset 1752 + Some region_set/unset/detach_manager/parent code simplifications. + +2004-09-16 17:06 UTC tuomov + * trunk: changeset 1751 + Use 'default' layout by default. + +2004-09-16 17:05 UTC tuomov + * trunk: changeset 1750 + - Added mod_panews.set/get + + - Moved loading of module configuration file to end of Lua code if the + module has such, as otherwise not everything may be available to the + configuration file. + +2004-09-16 16:27 UTC tuomov + * trunk: changeset 1749 + WSplitPane should now update markers on transpose. + +2004-09-07 18:45 UTC tuomov + * trunk: changeset 1748 + Changed some exec calls to exec_on calls in mod_query. + +2004-09-07 01:15 UTC tuomov + * trunk: changeset 1747 + Oops, warp setting was no longer used. + +2004-09-06 18:29 UTC tuomov + tagged ion-3ds-20040906 + +2004-09-06 18:28 UTC tuomov + * trunk: changeset 1745 + Fixed a typo in release notes. + +2004-09-06 18:25 UTC tuomov + * trunk: changeset 1744 + Removed POSIX_SOURCE setting from system-ac.mk.in + +2004-09-06 18:21 UTC tuomov + * trunk: changeset 1743 + Added release notes for a new release. + +2004-09-06 17:58 UTC tuomov + * trunk: changeset 1742 + Status display node loading fixes. + +2004-09-06 17:44 UTC tuomov + * trunk: changeset 1741 + - WPaneWS:s can be closed now. + + - Pane initial size calculation fix. + +2005-02-23 00:34 UTC tuomov + * trunk: changeset 1740 + Renamed autows to panews. + +2004-09-05 22:26 UTC tuomov + * trunk: changeset 1739 + Added compatibility conversion from old frame style names. + +2004-09-05 21:55 UTC tuomov + * trunk: changeset 1738 + Some focusing code simplifications/fixes/new bugs. + +2004-09-05 15:58 UTC tuomov + * trunk: changeset 1737 + Fixed an mplex layer2 focus issue. + +2004-09-05 15:30 UTC tuomov + * trunk: changeset 1736 + Fixed split tree ws_if_root pointer management bug. + +2004-09-05 15:12 UTC tuomov + * trunk: changeset 1735 + Added 'save' option for dock. + +2004-09-05 13:53 UTC tuomov + * trunk: changeset 1734 + Added frame_managed_changed_hook (improved from + frame_content_switched_hook). + +2004-09-02 15:20 UTC tuomov + * trunk: changeset 1733 + Border line drawing fix. + +2004-09-01 21:26 UTC tuomov + * trunk: changeset 1732 + WSplitPanes should now when appropriate recreate a WSplitUnused when + emptied. + +2004-09-01 18:45 UTC tuomov + * trunk: changeset 1731 + AutoWS now supports dropping stuff on unused areas. + +2004-09-01 13:31 UTC tuomov + * trunk: changeset 1730 + - Autows unused region focusing improvements. + + - Install cfg_autows.lua. + +2004-08-31 18:51 UTC tuomov + * trunk: changeset 1729 + Fixes to previous changes (coded without a monitor to test the code + on). + +2004-08-31 11:26 UTC tuomov + * trunk: changeset 1728 + Fixed a potential minor memory leak. + +2004-08-29 15:33 UTC tuomov + * trunk: changeset 1727 + Autoconf should work again. (Applied patch to move from + libtool/libltdl to plain libdl.) + +2004-08-29 07:18 UTC tuomov + * trunk: changeset 1726 + Some pane handle changes. + +2004-08-27 14:54 UTC tuomov + * trunk: changeset 1725 + Tiled workspace navigation code changes. + +2004-08-26 16:23 UTC tuomov + * trunk: changeset 1724 + - Moved resize code so that WUnusedWin can use it. + + - Added resize bindings for WUnusedWin. + +2004-08-26 14:16 UTC tuomov + * trunk: changeset 1723 + Added WUnusedWin for WSplitUnused. + +2004-08-23 03:27 UTC tuomov + * trunk: changeset 1722 + WAutoWS layout is now initialised when the workspace is created + instead of when the first frame is created. + +2004-08-21 15:54 UTC tuomov + * trunk: changeset 1721 + Added Czech translations of Ion messages. + +2004-08-19 15:16 UTC tuomov + * trunk: changeset 1720 + Added bindmap for WPaneWins. + +2004-08-18 14:12 UTC tuomov + * trunk: changeset 1719 + - AutoWS pane window handling improvements. + + - Changes in frame style names. + +2004-08-17 01:24 UTC tuomov + * trunk: changeset 1718 + Changed stdisp adaptation code to be more destructive on the split + tree to preserve special nodes for autows. + +2004-08-15 01:40 UTC tuomov + * trunk: changeset 1717 + Fixed a reference to mod_menu. + +2004-08-09 23:58 UTC tuomov + * trunk: changeset 1716 + Added beginnings of a Czech translation (by Miroslav Kure). + +2004-08-09 23:57 UTC tuomov + * trunk: changeset 1715 + Simplified regexp for 'uptime' load average method as some locales + show it differently. + +2004-08-06 02:55 UTC tuomov + * trunk: changeset 1714 + Some more makefile fixes. + +2004-08-06 00:05 UTC tuomov + * trunk: changeset 1713 + $(INTALLDIR) wasn't being called for translated manual page + directories. + +2004-08-03 18:40 UTC tuomov + * trunk: changeset 1712 + Added routine to drawing engine to draw just a borderline and not a + full border. + +2004-08-03 16:37 UTC tuomov + * trunk: changeset 1711 + updated cfgfile too. + +2004-08-02 19:35 UTC tuomov + * trunk: changeset 1710 + Submaps now default to AnyModifier. + +2004-08-02 16:01 UTC tuomov + * trunk: changeset 1709 + nroff instead of groff. + +2004-08-02 14:51 UTC tuomov + * trunk: changeset 1708 + Oops, calcmail was global. + +2004-08-02 14:49 UTC tuomov + * trunk: changeset 1707 + - Some more binding documentation and translation improvements. + + - Use proper welcome.lang.txt. + +2004-08-02 05:16 UTC tuomov + * trunk: changeset 1706 + Oops. + +2004-08-02 03:31 UTC tuomov + * trunk: changeset 1705 + - Removed share directory from the source tree and moved stuff therein + to ioncore and utils directories. + + - Some rules.mk improvements. + +2004-08-02 00:51 UTC tuomov + * trunk: changeset 1704 + - Improved the 'basic concepts' section of the manual page. + + - Added a Finnish translation of the manual page and welcome message. + (Correct version of the welcome message is not yet used.) + +2004-08-01 21:51 UTC tuomov + * trunk: changeset 1703 + ... and also gettext menuentry and submenu. + +2004-08-01 21:46 UTC tuomov + * trunk: changeset 1702 + Removed 'TR' from menu configuration files; include 'submenu' and + 'menuentry' in keywords to lxgettext instead. + +2004-08-01 21:44 UTC tuomov + * trunk: changeset 1701 + Fixed type in documentation string. + +2004-08-01 21:43 UTC tuomov + * trunk: changeset 1700 + Added translations for binding documentation. + +2004-08-01 19:48 UTC tuomov + * trunk: changeset 1699 + Oops, previous commit was a bit broken. + +2004-08-01 19:06 UTC tuomov + * trunk: changeset 1698 + Binding documentation is now included in the configuration files and + the documentation for manual pages is generated from there. + +2004-07-31 22:00 UTC tuomov + * trunk: changeset 1696 + Minor fixes to previous error reporting and other changes. + +2004-07-31 20:55 UTC tuomov + * trunk: changeset 1695 + Added Finnish translation. + +2004-07-31 20:53 UTC tuomov + * trunk: changeset 1694 + - Some more error and other reporting changes for easier and better + translations. + + - Added menu configuration files to be translated. + +2004-07-31 20:12 UTC tuomov + * trunk: changeset 1693 + s/corner/pos/g in statusbar creation code. + +2004-07-31 19:11 UTC tuomov + * trunk: changeset 1691 + - Removed some redundant error reporting that is of no help to the + user. + + - Changes in error reporting policy: trust the source of the problem + do it, if it is our code. + +2005-02-23 00:02 UTC tuomov + * trunk: changeset 1690 + Added basic framework for localisation/language translations. + +2004-07-30 00:08 UTC tuomov + tagged ion-3ds-20040730 + +2004-07-29 23:32 UTC tuomov + * trunk: changeset 1687 + Oops DIST: PREFIX line was broken. + +2004-07-29 23:30 UTC tuomov + * trunk: changeset 1686 + Added new release notes and other preparation for the release of ion- + 3ds-20040730. + +2004-07-29 23:24 UTC tuomov + * trunk: changeset 1685 + - Changes in default installation paths and binary names: ion->ion3, + etc. + + - Added ioncore.progname so that scripts that want to show the manual + page don't need to know if we're running as ion or pwm. + +2004-07-29 23:19 UTC tuomov + * trunk: changeset 1684 + Manual page updates. + +2004-07-29 19:58 UTC tuomov + * trunk: changeset 1683 + Added old release notes to the package. + +2004-07-29 19:47 UTC tuomov + * trunk: changeset 1682 + WIonWS: don't load WSplitST, because we don't save position and + orientation and thus think the saved stdispnode was bl/horizontal. + +2004-07-29 19:41 UTC tuomov + * trunk: changeset 1681 + Drawing engine configuration files now also use the underscored + "look_" prefix instead of "look-". + +2004-07-29 19:36 UTC tuomov + * trunk: changeset 1680 + Added styles for status displays. + +2004-07-29 18:52 UTC tuomov + * trunk: changeset 1679 + - Changed WFloatWS.circulate and backcirculate to be stacking based. + + - Fixes to previous stacking code changes. + +2004-07-29 18:27 UTC tuomov + * trunk: changeset 1678 + Floatws stacking code improvements. + +2004-07-29 02:18 UTC tuomov + * trunk: changeset 1672 + All savefiles are now also prefixed with "saved_". + +2004-07-29 02:02 UTC tuomov + * trunk: changeset 1671 + Removed WIonWS.resize_tree and added WSplit.rqgeom. + +2004-07-29 01:52 UTC tuomov + * trunk: changeset 1670 + Renamed redundantly named ioncore.create_new_ws to ioncore.create_ws. + +2004-07-29 01:50 UTC tuomov + * trunk: changeset 1669 + Added WWindow.xid export. + +2004-07-29 01:47 UTC tuomov + * trunk: changeset 1668 + Statically linking modules had been broken with the module stub loader + change, and make depend had never worked in this case. + +2004-07-29 01:32 UTC tuomov + * trunk: changeset 1667 + - Removed generic stacking code; stacking is now handled fully by + managers, making it simpler and better-working. + + - Some fixes to WMPlex layer2 new focus policy. + +2004-07-28 21:58 UTC tuomov + * trunk: changeset 1666 + Reorganisation autows code to match the new lua/c division and stub + loaders. + +2004-07-28 20:57 UTC tuomov + * trunk: changeset 1665 + Changes in WMPlex passive layer 2 object focus policy. + +2004-07-28 02:02 UTC tuomov + * trunk: changeset 1664 + Oops. Search path order had been reversed. + +2004-07-28 01:57 UTC tuomov + * trunk: changeset 1663 + Style file lookup was broken. + +2004-07-28 01:46 UTC tuomov + * trunk: changeset 1662 + The status display is now skipped by WIonWS navigational routines. + +2004-07-28 01:30 UTC tuomov + * trunk: changeset 1661 + Forgot to update resize_delay->kbresize_delay to config files. + +2004-07-28 01:25 UTC tuomov + * trunk: changeset 1660 + Oops. Forgot to remove references to DEFAULT_WS_TYPE. + +2004-07-28 01:17 UTC tuomov + * trunk: changeset 1659 + - Changes in functions to set some basic settings of ioncore and some + modules: for the most part, a single 'set' function now. Also added + the 'get' counterpart. + + - Removed extl_globals as DEFAULT_WS_TYPE is part of ioncore.set/get + now. + +2004-07-27 21:06 UTC tuomov + * trunk: changeset 1658 + Added WSplitInner.current dynfun. + +2004-07-27 21:01 UTC tuomov + * trunk: changeset 1657 + Restore extl_loadstring as mod_ionflux needs it. + +2004-07-27 19:23 UTC tuomov + * trunk: changeset 1656 + - Statusbar mail checker died if $MAIL didn't exist. + + - Added mailbox file setting. + +2004-07-27 18:03 UTC tuomov + * trunk: changeset 1655 + Some statusbar config tuning + +2004-07-27 17:49 UTC tuomov + * trunk: changeset 1654 + - Better cfg_dock.lua; moved code to mod_dock. + + - Status display is now _not_ saved to layout savefile, for easier + switching between different status displays. + + - Some mplex layer2 access improvements. + +2004-07-27 13:48 UTC tuomov + * trunk: changeset 1653 + Fixed some typos etc. + +2004-07-27 13:42 UTC tuomov + * trunk: changeset 1652 + - Improved ext_statusbar update time calculation. + + - Added ext_statusbar to list of subdirectories to build in the top- + level Makefile. + +2004-07-27 04:11 UTC tuomov + * trunk: changeset 1651 + Oops, mail_last_check wasn't being update. + +2004-07-27 04:10 UTC tuomov + * trunk: changeset 1650 + Added ext_statusbar statusbar script. + +2004-07-27 00:43 UTC tuomov + * trunk: changeset 1649 + - Put ext_misc stuff in ioncore anyway; removed close_current_ws. + + - Fixed some PWM stuff. + +2004-07-27 00:26 UTC tuomov + * trunk: changeset 1648 + Status display removal is now properly notified by the screen to + workspaces over restarts. + +2004-07-26 22:03 UTC tuomov + * trunk: changeset 1647 + Oops, welcome message was no longer installed. + +2004-07-26 21:57 UTC tuomov + * trunk: changeset 1645 + Some documentation fixes and documentation generation changes. + +2004-07-26 21:57 UTC tuomov + * trunk: changeset 1644 + Oops, there was still a reference to draw.lua + +2004-07-26 21:48 UTC tuomov + * trunk: changeset 1643 + ioncore-extras.lua was still lying around after barfed commit. + +2004-07-26 21:29 UTC tuomov + * trunk: changeset 1642 + Fixed a sed vomit in ion-completeman (around sed's brain-damaged + refusal to support escaping of square brackets) after GNU sed stopped + accepting the previous vomit. + +2004-07-26 20:50 UTC tuomov + * trunk: changeset 1641 + Fixes to previous failed commit. + +2004-07-26 20:46 UTC tuomov + * trunk: changeset 1640 + - United ioncore and ioncorelib; mod_query and querylib; and mod_menu + and menulib routines into the single namespace of the first of each + to present the user with less different code units. + + - Changed all configuration files the user might want to edit to be + prefixed with 'cfg_'. + +2004-07-26 19:17 UTC tuomov + * trunk: changeset 1639 + Renamed 'include' 'dopath' (to look similar to Lua's 'dofile' that + expects complete file name). + +2004-07-26 18:58 UTC tuomov + * trunk: changeset 1638 + Added stub loaders for modules, so users only need a single command to + load scripts or modules. + +2004-07-26 17:43 UTC tuomov + * trunk: changeset 1637 + Some minor infowin, stdisp and timer fixes and changes. + +2004-07-26 16:13 UTC tuomov + * trunk: changeset 1636 + Exported timers to Lua side and some other changes in timer code. + +2004-07-26 14:38 UTC tuomov + * trunk: changeset 1633 + Added some code to WInfoWin to eventually allow it to be used as an + stdisp. + +2004-07-25 19:18 UTC tuomov + * trunk: changeset 1632 + Some autows overlap and new OO split code fixes. + +2004-07-24 14:09 UTC tuomov + * trunk: changeset 1631 + Some fixes to previous changes in name allocation. + +2004-07-24 11:58 UTC tuomov + * trunk: changeset 1630 + Added still incomplete support for partial floating of WSplitPanes on + WAutoWS:s. + +2004-07-23 22:27 UTC tuomov + * trunk: changeset 1629 + Resizing code fixes and simplifications. + +2004-07-23 20:50 UTC tuomov + * trunk: changeset 1628 + Some more name allocation improvements and simplifications. + +2004-07-23 19:34 UTC tuomov + * trunk: changeset 1627 + Fixed problem allocating names that already contain something that + looks like an instance number. + +2004-07-22 22:31 UTC tuomov + * trunk: changeset 1626 + - Moved WSplitUnused code to mod_autows from mod_ionws. + + - Added initial version of WSplitPane split tree node for better + organisation of different application classes in WAutoWS split tree. + +2004-07-18 18:59 UTC tuomov + * trunk: changeset 1625 + The split tree now uses inheritance within the Ion object system, so + it is easier to add special inner nodes to it. + +2004-07-18 02:25 UTC tuomov + * trunk: changeset 1624 + Some dock changes+fixes. + +2004-07-17 00:48 UTC tuomov + * trunk: changeset 1623 + Autows template code is now aware of stdisp. + +2004-07-16 23:54 UTC tuomov + * trunk: changeset 1622 + Autows classification heuristics improvements. + +2004-07-15 15:29 UTC tuomov + * trunk: changeset 1621 + SM segfault fix. + +2004-07-15 13:21 UTC tuomov + * trunk: changeset 1620 + Fixed config file help. + +2004-07-15 01:59 UTC tuomov + * trunk: changeset 1619 + Removed stdisp from lists of normal managed objects on workspaces. + +2004-07-13 16:05 UTC tuomov + * trunk: changeset 1618 + Fixed WRegion.size_hints export. + +2004-07-13 16:04 UTC tuomov + * trunk: changeset 1617 + Always leave a SPLIT_UNUSED when stacking. + +2004-07-13 16:03 UTC tuomov + * trunk: changeset 1616 + Changes in split tree resize code to better handle SPLIT_UNUSED. + +2004-07-12 20:11 UTC tuomov + * trunk: changeset 1615 + AutoWS layout initialisation code improvements. + +2004-07-11 22:57 UTC tuomov + * trunk: changeset 1614 + Beginnings of a new template-based autows implementation. + +2004-07-08 00:18 UTC tuomov + * trunk: changeset 1613 + Some frame->style handling changes. + +2004-07-08 00:17 UTC tuomov + * trunk: changeset 1612 + Possibly fixed scratchpad focusing (again). + +2004-07-03 18:04 UTC tuomov + tagged ion-3ds-20040703 + +2004-07-03 18:01 UTC tuomov + * trunk: changeset 1610 + Added a note on ./configure being broken in README. + +2004-07-03 17:57 UTC tuomov + * trunk: changeset 1609 + Default to no dock. + +2004-07-03 17:56 UTC tuomov + * trunk: changeset 1608 + Added code in dock.lua to create dock of either flavour. + +2004-07-03 17:56 UTC tuomov + * trunk: changeset 1607 + Do not automagically redirect region_manage_clientwin requests to + passive layer 2 objects. + +2004-07-03 17:55 UTC tuomov + * trunk: changeset 1606 + Added table.join. + +2004-07-03 17:54 UTC tuomov + * trunk: changeset 1605 + Changes in menu stacking. + +2004-07-03 17:25 UTC tuomov + * trunk: changeset 1604 + Implemented WRegion.rqclose for WDock (and removed WDock.destroy), so + that empty docks can now be closed in a consistent manner. + +2004-07-03 17:21 UTC tuomov + * trunk: changeset 1603 + Added some region dynfuns. + +2004-06-27 17:43 UTC tuomov + * trunk: changeset 1602 + Dock supports bindings now. + +2004-06-27 17:12 UTC tuomov + * trunk: changeset 1601 + - Removed orientation from stdisp parameters; it is got from the + region itself through region_orientation. + + - WDock.set can now properly request changes when working as WMPlex + stdisp. + +2004-06-26 20:13 UTC tuomov + * trunk: changeset 1600 + floatws fix: don't save stdisp configuration. + +2004-06-26 20:05 UTC tuomov + * trunk: changeset 1599 + Ensure FD_CLOEXEC is set. + +2004-06-26 19:53 UTC tuomov + * trunk: changeset 1598 + - Removed generic stacking exports. Raise/lower is only available + through WFloatWS.raise/lower now. + + - Removed WRegion.active_sub export. + +2004-06-26 19:35 UTC tuomov + * trunk: changeset 1597 + Renamed 'reference' field in load/info tables to 'reg'. + +2004-06-26 18:40 UTC tuomov + * trunk: changeset 1596 + Guarantee maximum size to be set in region_size_hints along with base + and minimum size. + +2004-06-26 18:37 UTC tuomov + * trunk: changeset 1595 + Removed relw and relh from region_size_hints; use + XSizeHints.base_width/height instead. + +2004-06-26 16:07 UTC tuomov + * trunk: changeset 1594 + - Added support for passive mplex layer 2 objects. + + - Unified generic stdisp and floating dock position parametrisation. + +2004-06-23 15:56 UTC tuomov + * trunk: changeset 1593 + Improved mplex managed object attribute storage. + +2004-06-22 00:16 UTC tuomov + * trunk: changeset 1592 + Added XID to querylib.show_clientwin. + +2004-06-21 15:02 UTC tuomov + * trunk: changeset 1591 + Fixed drawing engine style loading code when neither font nor based_on + was set. + +2004-06-21 08:18 UTC tuomov + * trunk: changeset 1590 + Previous "fixes" broken dock resize on workspace change if previous + docknode geometry was a good fit. + +2004-06-21 07:37 UTC tuomov + * trunk: changeset 1589 + Some status display/dock support code fixes and improvements. + +2004-06-21 00:30 UTC tuomov + * trunk: changeset 1588 + Support dragging tab of current window by clicking anywhere in a + frame. + +2004-06-20 16:45 UTC tuomov + * trunk: changeset 1587 + Initial conversion of mod_dock to work with WMPlex sticky auto-sizing + status display mechanisms. + +2004-06-20 11:47 UTC tuomov + * trunk: changeset 1586 + Don't register WRegionSimpleCreateFn as it is not actually needed. + Thus only register WRegionLoadCreateFn. + +2004-06-20 11:45 UTC tuomov + * trunk: changeset 1584 + Don't duplicate libtu/stringintmap_key as value2str. + +2004-06-20 11:10 UTC tuomov + * trunk: changeset 1582 + Fixed folds. + +2004-06-19 16:39 UTC tuomov + * trunk: changeset 1581 + - Use of block auto-indentation had broken indentation after some + EXTL_EXPORT statements. + + - Added documentation for WMPlex.set_stdisp and WMPlex.get_stdisp. + +2004-06-19 16:26 UTC tuomov + * trunk: changeset 1580 + Improved support for auto-sizing status display/dock. + +2004-06-19 13:26 UTC tuomov + * trunk: changeset 1579 + Export WScreen.set_managed_offset. + +2004-06-18 22:14 UTC tuomov + * trunk: changeset 1576 + Use libtu's new stringstore for storing frame style names. + +2004-06-17 20:42 UTC tuomov + * trunk: changeset 1573 + Fixed handling of requested geometry in floatws_attach_clientwin. + +2004-06-17 17:14 UTC tuomov + * trunk: changeset 1572 + Added some split-tree manipulation algorithms needed to eventually + support an automagically properly sized dock. + +2004-06-17 13:58 UTC tuomov + * trunk: changeset 1571 + Resize primary node fix. + +2004-06-14 15:09 UTC tuomov + * trunk: changeset 1570 + Finished converting (still crappy) autows penalty code to Lua and some + tuning. + +2004-06-12 20:51 UTC tuomov + * trunk: changeset 1569 + Moved autows penalty calculation code to Lua side for easier + customisation. + +2004-06-12 18:10 UTC tuomov + * trunk: changeset 1568 + - Added WRegion.size_hints to exports + + - ... and renamed region_resize_hints to region_size_hints. + +2004-06-12 14:16 UTC tuomov + * trunk: changeset 1567 + Fixed querylib.query_exit and renamed it to querylib.query_shutdown. + +2004-06-11 19:03 UTC tuomov + * trunk: changeset 1566 + Changed penalties to be based on proportional shrinkage/growth/free + space after split and other autows placement code changes. + +2004-06-11 11:03 UTC tuomov + * trunk: changeset 1565 + Put back WIonWS.create_frame_fn. + +2004-06-10 22:48 UTC tuomov + * trunk: changeset 1564 + - Session saving is no longer disabled on exit if there was an error + loading layout. Instead a backup of the layout savefile is made. + + - The behaviour of ioncore.resign was changed not to save session, but + instead just quit and, when running under a session manager, + instruct the session manager to not restart the WM again. + +2004-06-10 21:13 UTC tuomov + * trunk: changeset 1563 + Fixed focusing on frame destroy. + +2004-06-10 21:01 UTC tuomov + * trunk: changeset 1562 + Bracing error caused split unused fusing not to work in all cases. + +2004-06-10 15:33 UTC tuomov + * trunk: changeset 1561 + Changes in the set of exported WSplit methods and WIonWS:split_tree(). + +2004-06-10 15:15 UTC tuomov + * trunk: changeset 1560 + Added code to fuse adjacent SPLIT_UNUSED nodes. + +2004-06-09 16:12 UTC tuomov + * trunk: changeset 1559 + Removed WIonFrame and WAutoFrame. There's just WFrame and WFloatFrame + (also to be unified?) now. + +2004-06-08 20:52 UTC tuomov + * trunk: changeset 1558 + Some minor autows placement tuning. + +2004-06-08 20:26 UTC tuomov + * trunk: changeset 1557 + Moved p_move from WFloatFrame to WFrame. + +2004-06-08 17:23 UTC tuomov + * trunk: changeset 1556 + Binding to nil should again remove the binding. + +2004-06-08 17:21 UTC tuomov + * trunk: changeset 1555 + Fixes in navigation code handling of SPLIT_UNUSED. + +2004-06-07 13:41 UTC tuomov + * trunk: changeset 1554 + Some more unused space information storage changes. + +2004-06-07 11:09 UTC tuomov + * trunk: changeset 1553 + load_autows hand't been updated to new ionws_load_node parameters. + +2004-06-06 19:39 UTC tuomov + * trunk: changeset 1552 + Added querylib.show_clientwin to display information on a client + window and put it in the context menu. + +2004-06-06 19:27 UTC tuomov + * trunk: changeset 1551 + Documentation fixes. + +2004-06-06 18:32 UTC tuomov + * trunk: changeset 1550 + Some fixes in split tree load code changes. + +2004-06-06 17:58 UTC tuomov + * trunk: changeset 1549 + Added penalties to attach to an existing frame. + +2004-06-06 17:09 UTC tuomov + * trunk: changeset 1548 + Added querylib.query_menu that can be used to create a query of a + menu. + +2004-06-06 16:16 UTC tuomov + * trunk: changeset 1547 + Moved Fx queries to WMPlex level so they're available on empty + workspaces and full screen client windows as well. + +2004-06-06 16:08 UTC tuomov + * trunk: changeset 1546 + Added 'lazy' split attribute. If a horizontal/vertical split is + "lazy", and a direct child is removed, the split won't be collapsed + unless the other child is SPLIT_UNUSED. + +2004-06-06 15:47 UTC tuomov + * trunk: changeset 1545 + Few minor fixes. + +2004-06-05 19:24 UTC tuomov + * trunk: changeset 1543 + - Moved workspace dummy window code from WFloatWS to WGenWS to be + available to WAutoWS as well. + + - Removed unused managed_splits field from WIonWS. + +2004-06-05 15:14 UTC tuomov + * trunk: changeset 1542 + WAutoWS placement code clean-up. + +2004-06-05 09:42 UTC tuomov + * trunk: changeset 1541 + Oops. Fixed sanity check. + +2004-06-04 23:15 UTC tuomov + * trunk: changeset 1539 + Added Xinerama sanity check. + +2004-06-04 17:55 UTC tuomov + * trunk: changeset 1537 + Changed WAutoWS placement code to use new unused space storage and + added framework code to handle calculating penalties based on + "immediate" available unused space. + +2004-06-04 17:29 UTC tuomov + * trunk: changeset 1536 + Changed storage of used/unused space within + SPLIT_VERTICAL/SPLIT_HORIZONTAL. + +2004-06-04 15:49 UTC tuomov + * trunk: changeset 1535 + Fixed WMPlex.l2_show focus. + +2004-06-03 11:25 UTC tuomov + * trunk: changeset 1534 + Added very preliminary WAutoWS layout code. + +2004-06-03 11:23 UTC tuomov + * trunk: changeset 1533 + Code that creates regions from tables now supports a 'reference' to + use existing regions. + +2004-06-03 11:21 UTC tuomov + * trunk: changeset 1532 + Fixed some indentation. + +2004-06-02 19:15 UTC tuomov + * trunk: changeset 1531 + Added support for "static" splits. + +2004-05-31 10:40 UTC tuomov + * trunk: changeset 1526 + Splitting fixes. + +2004-05-31 10:39 UTC tuomov + * trunk: changeset 1525 + Added nil check to hook_add_extl. + +2004-05-30 16:01 UTC tuomov + * trunk: changeset 1524 + Fixed a serious brainfart in region name allocation. (Temporary hack; + need to do this more efficiently.) + +2004-05-30 14:13 UTC tuomov + * trunk: changeset 1523 + Oops. Alloc one byte too small. + +2004-05-29 19:13 UTC tuomov + * trunk: changeset 1522 + Check that client window is not already managed when loading saved + configuration (on restart). + +2004-05-29 19:12 UTC tuomov + * trunk: changeset 1521 + Added -noerrorlog startup option. + +2004-05-29 12:03 UTC tuomov + * trunk: changeset 1520 + Fixed dec/inc_index bindings. + +2004-05-29 11:26 UTC tuomov + * trunk: changeset 1519 + Bindmap binding count was not maintained correctly if a key/button was + re-bound. + +2004-05-28 14:41 UTC tuomov + * trunk: changeset 1518 + Added SPLIT_UNUSED nodes and stopped allowing frames that do not use + full space of their SPLIT_REGNODE: + +2004-05-26 19:26 UTC tuomov + * trunk: changeset 1517 + querylib.query_renameworkspace had been broken at some point. + +2004-05-26 19:24 UTC tuomov + * trunk: changeset 1516 + Unified module and script search paths. There's only one search path + now. + +2004-05-26 19:17 UTC tuomov + * trunk: changeset 1515 + *sigh* install-sh seems to require -c to copy. + +2004-05-26 17:24 UTC tuomov + * trunk: changeset 1514 + Changed INSTALL to install-sh + +2004-05-26 17:10 UTC tuomov + * trunk: changeset 1513 + Build rules fixes. + +2004-05-26 16:57 UTC tuomov + * trunk: changeset 1512 + Libtool is no longer used: problems with one module depending on + another. + +2004-05-26 14:13 UTC tuomov + * trunk: changeset 1511 + Added unviewable check in mplex init. + +2004-05-26 13:57 UTC tuomov + * trunk: changeset 1510 + - WMPlex layer2 object hide improved. + + - Unified WMPlex layer list access functions. + +2004-05-21 21:04 UTC tuomov + * trunk: changeset 1509 + Removed 'exec' from /bin/sh call to execute command so that more + complex commands can be entered. + +2004-05-21 19:46 UTC tuomov + * trunk: changeset 1507 + Removed os.execute block. + +2004-05-21 18:49 UTC tuomov + * trunk: changeset 1506 + Improved querylib.query_exec completion to break at spaces. + +2004-05-21 18:33 UTC tuomov + * trunk: changeset 1505 + SSH host completion now understands usernames. (Code taken from the + Ion Wiki.) + +2004-05-21 18:26 UTC tuomov + * trunk: changeset 1504 + - Improved integration of exit/restart/save state with session + management. + + - Removed ioncore.exit(); in most cases use ioncore.shutdown() instead + (and ioncore.resign() in others). + +2004-05-20 21:48 UTC tuomov + * trunk: changeset 1503 + Added comment headers to the files. + +2004-05-20 21:35 UTC tuomov + * trunk: changeset 1502 + - Added mod_sm.request_save. + + - Removed support for whatever (older?) session management system + defines the macro "XSM". + +2004-05-20 18:26 UTC tuomov + * trunk: changeset 1501 + - Automatically load mod_sm of the SESSION_MANAGER environment + variable is set. + + - Moved session directory setup code to mod_sm. + +2004-05-20 16:03 UTC tuomov + * trunk: changeset 1500 + Added WClientWin.xid export to get X window id. + +2004-05-20 15:04 UTC tuomov + * trunk: changeset 1499 + Improved WAutoFrame "lazy" move/resize behaviour. + +2004-05-20 14:38 UTC tuomov + * trunk: changeset 1498 + Split resizing code now first tries to use any unused space before + shrinking a region. + +2004-05-19 08:35 UTC tuomov + * trunk: changeset 1497 + - Do not clear client window activity status when receiving focus if + the urgency hint is still set. + + - Exported WRegion.notify_activity and WRegion.clear_activity. + +2004-05-18 20:02 UTC tuomov + * trunk: changeset 1496 + Fixed exec bindings to correctly set the root window and changed + ioncore.exec_on to accept any region as parameter. + +2004-05-18 17:55 UTC tuomov + * trunk: changeset 1495 + Changes in save directory under a session manager; use ~/.ion3/gnome- + session-$GNOME_DESKTOP_SESSION_ID (with potentially unallowed + characters replaced) if this environment variable is set. + +2004-05-17 22:52 UTC tuomov + * trunk: changeset 1494 + Merged dock kde systray support patch and fixes from stable branch. + +2004-05-17 22:41 UTC tuomov + * trunk: changeset 1491 + Added missing include. + +2004-05-15 17:45 UTC tuomov + * trunk: changeset 1490 + Removed extl_call_named as it is no longer needed and useless bloat. + +2004-05-15 17:43 UTC tuomov + * trunk: changeset 1489 + Converted ionws_placement_method to ionws_placement_alt hook. Removed + the hook from WAutoWS code temporarily. + +2004-05-15 09:47 UTC tuomov + * trunk: changeset 1488 + Fixed attempt to free memory at middle of allocated area in menu + typeahead find. + +2004-05-14 14:14 UTC tuomov + * trunk: changeset 1487 + - Changed mplex l2 hide/show to warp (if enabled) to new active + region. + + - Some other focus handling fixes/changes. + +2004-05-14 13:13 UTC tuomov + * trunk: changeset 1486 + WMPlex contents are unmapped when either width or height of client + managed area becomes at most 1 instead of resizing them. Previously + this was only done vertically for frames (shade). + +2004-05-14 10:14 UTC tuomov + * trunk: changeset 1483 + Fixed shade on WIonWS. + +2004-05-13 12:01 UTC tuomov + * trunk: changeset 1482 + Minor cleanup. + +2004-05-13 09:02 UTC tuomov + * trunk: changeset 1481 + Removed some scratch code. + +2004-05-12 15:39 UTC tuomov + * trunk: changeset 1480 + Oops. There was a typo that was causing region flags to be cleared + when children were being rescued. + +2004-05-12 13:49 UTC tuomov + * trunk: changeset 1479 + hook_add_extl was missing extl_ref_fn. + +2004-05-12 13:30 UTC tuomov + * trunk: changeset 1478 + Documented hook exports. + +2004-05-12 12:56 UTC tuomov + * trunk: changeset 1477 + Removed deprecated references to defcmd. + +2004-05-12 12:50 UTC tuomov + * trunk: changeset 1476 + New hook system. Both Lua and C-side now have the same hooks except + for ioncore_handle_event_alt. + +2004-05-12 10:31 UTC tuomov + * trunk: changeset 1475 + Added functions to test ExtlTab:s and ExtlFn:s for equality. + +2004-05-08 23:38 UTC tuomov + * trunk: changeset 1474 + WM_COLORMAP_WINDOWS handling fixes. (Does something still use + this/bother supporting indexed colour models at all?) + +2004-05-06 21:01 UTC tuomov + * trunk: changeset 1472 + Oops. nodecor wan't being initialised if mwm hints are not set. + +2004-05-06 09:35 UTC tuomov + * trunk: changeset 1470 + Space wasn't being reclaimed when unsplitting. + +2004-05-03 15:22 UTC tuomov + * trunk: changeset 1469 + Added extra check. Fixed indentation. + +2004-05-03 15:21 UTC tuomov + * trunk: changeset 1468 + Fixed dynamic function call. + +2004-05-03 14:18 UTC tuomov + * trunk: changeset 1466 + Fixed stippled tab font when -i18n was not set. + +2004-05-02 15:57 UTC tuomov + * trunk: changeset 1464 + Path fixes in file headings. + +2004-05-02 15:46 UTC tuomov + * trunk: changeset 1463 + Added some basic framework for autows. No functionality essentially + different from ionws yet. + +2004-05-01 11:32 UTC tuomov + * trunk: changeset 1460 + Transparency configuration reading fix. + +2004-04-30 19:45 UTC tuomov + * trunk: changeset 1459 + Minor session management support improvements; should work with gnome- + session now. + +2004-04-28 11:34 UTC tuomov + * trunk: changeset 1458 + Only kill active grab when esc is pressed, not released. + +2004-04-24 22:45 UTC tuomov + * trunk: changeset 1456 + WIonWS supports alternative frame creation routine (for inheritance). + +2004-04-20 18:01 UTC tuomov + * trunk: changeset 1455 + Split size calculation TODOs handled. + +2004-04-20 15:03 UTC tuomov + * trunk: changeset 1454 + Some fixes to new split tree code. + +2004-04-17 17:44 UTC tuomov + * trunk: changeset 1453 + - Added the WARN_FUNC macros. + + - WIonWS code checks that split_tree!=NULL more often so that it + should be possible to inherit it by a workspace class that doesn't + always have such a tree. + +2004-04-17 16:45 UTC tuomov + * trunk: changeset 1452 + Fixed split bindings to attach currently displayed region of old frame + to new frame. + +2004-04-17 15:03 UTC tuomov + * trunk: changeset 1451 + Added code to transpose splits. + +2004-04-16 22:34 UTC tuomov + * trunk: changeset 1450 + Slightly improved splitting code. + +2004-04-11 10:15 UTC tuomov + * trunk: changeset 1448 + Oops, there was an off-by-one bug in _NET_VIRTUAL_ROOTS setting. + +2004-04-05 12:33 UTC tuomov + * trunk: changeset 1444 + Added a kludge to deal with waitrelease when the modifiers has already + been released. + +2004-04-05 07:39 UTC tuomov + * trunk: changeset 1442 + Oops, LCDIR and SHAREDIR were on path in wrong order. + +2004-04-04 11:48 UTC tuomov + * trunk: changeset 1441 + Improved split data structures. + +2004-04-02 19:59 UTC tuomov + * trunk: changeset 1440 + WMPlex is now a proper non-virtual class and objects of this type can + be created and nested like any others. + +2004-04-02 08:19 UTC tuomov + * trunk: changeset 1439 + Also put querylib.lc and menulib.lc in LCDIR. + +2004-04-02 08:04 UTC tuomov + * trunk: changeset 1437 + Changed compiled .lc files' path to LIBDIR/ion/lc. + +2004-03-27 09:01 UTC tuomov + * trunk: changeset 1435 + Fixed floatframe client window size issue when shaded. + +2004-03-26 22:51 UTC tuomov + * trunk: changeset 1434 + Split brush and style code in separate files. + +2004-03-26 22:37 UTC tuomov + * trunk: changeset 1433 + Removed object abstraction from drawing engine brushes to help + creating drawing engines with other font routines. + +2004-03-26 22:12 UTC tuomov + * trunk: changeset 1432 + Stacking fix(?). + +2004-03-24 20:45 UTC tuomov + * trunk: changeset 1431 + Moved some root position notification code from WRegion to WWindow. + +2004-03-24 20:37 UTC tuomov + * trunk: changeset 1430 + Removed duplicate fitrep code. + +2004-03-24 18:55 UTC tuomov + * trunk: changeset 1429 + Some WIonWS and split tree code reorganisation. + +2004-03-24 17:33 UTC tuomov + * trunk: changeset 1428 + Don't put input method failure complaints in startup error log. + +2004-03-23 11:28 UTC tuomov + * trunk: changeset 1426 + querylib.query_restart and query_exit were not yet converted to the + Ion3 scheme of things. + +2004-03-23 02:15 UTC tuomov + * trunk: changeset 1424 + Client window unmap handling fixes (?). + +2004-03-22 12:29 UTC tuomov + * trunk: changeset 1423 + String freeing fix. + +2004-03-21 16:58 UTC tuomov + * trunk: changeset 1422 + Fixed a colourmap update crash. + +2004-03-21 16:43 UTC tuomov + * trunk: changeset 1421 + Binaries were still being stripped. + +2004-03-20 21:31 UTC tuomov + * trunk: changeset 1420 + mod_sm wasn't registering exported functions. + +2004-03-20 21:23 UTC tuomov + * trunk: changeset 1419 + Added very preliminary (and partially broken) session management + support based on the 'sm' module for Ion1. + +2004-03-20 14:39 UTC tuomov + * trunk: changeset 1418 + Moved activity notification window code from scratchpad to screen; + also works for normal workspaces now. + +2004-03-18 21:11 UTC tuomov + * trunk: changeset 1416 + Improvements and fixes in keyboard mapping changes handling. + +2004-03-18 09:39 UTC tuomov + * trunk: changeset 1413 + Made drawing engine objects inheritable and initialisation code + reusable. + +2004-03-17 20:17 UTC tuomov + * trunk: changeset 1412 + Some scratchpad activity notification window changes/fixes. + +2004-03-16 17:24 UTC tuomov + tagged ion-3ds-20040316 + +2004-03-16 17:23 UTC tuomov + * trunk: changeset 1406 + Added commented-out line to load mod_sp. + +2004-03-16 16:42 UTC tuomov + * trunk: changeset 1405 + Fixed typo Wegion->WRegion. + +2004-03-16 16:40 UTC tuomov + * trunk: changeset 1404 + - Added rectangle_constrain and changed code that did the same thing + to use the function. + + - Some clientwin WFitParams usage improvements and fixes. + +2004-03-16 16:09 UTC tuomov + * trunk: changeset 1403 + Indentation fixes. + +2004-03-15 16:32 UTC tuomov + * trunk: changeset 1401 + Renamed module directories. + +2004-03-15 16:02 UTC tuomov + * trunk: changeset 1399 + Added scratchpad activity notification bubble. + +2004-03-15 08:34 UTC tuomov + * trunk: changeset 1397 + Documentation generation fixes and improvements. + +2004-03-15 08:10 UTC tuomov + * trunk: changeset 1396 + Some function name shortenings and other changes. + +2004-03-14 23:40 UTC tuomov + * trunk: changeset 1395 + Fixed some exec calls to ioncore.exec. + +2004-03-14 23:29 UTC tuomov + * trunk: changeset 1394 + Fixed getbindings for buttons. + +2004-03-14 22:53 UTC tuomov + * trunk: changeset 1393 + Made some more minor changes to binding configuration and added + ioncore.getbindings and ioncorelib.getbindings that can be used to get + a table of made bindings for all contexts. + +2004-03-14 19:04 UTC tuomov + * trunk: changeset 1392 + Minor glitch introduced by previous changes fixed. + +2004-03-14 18:59 UTC tuomov + * trunk: changeset 1391 + Multiplexers now save layer2 objects in layout savefile. + +2004-03-13 21:37 UTC tuomov + * trunk: changeset 1390 + Removed superfluous region_same_rootwin check. + +2004-03-13 21:27 UTC tuomov + * trunk: changeset 1389 + Added tentative mod_sp scratchpad module. + +2004-03-13 18:47 UTC tuomov + * trunk: changeset 1388 + Region fitting, reparting and creating routines now receive a + bounds/exact specification along with geometry to allow managers to + specify whether managed regions should take full space available or + are they allowed to use only part of it. + +2004-03-12 19:06 UTC tuomov + * trunk: changeset 1387 + Added functions to hide/show mplex layer 2 objects. + +2004-03-12 17:58 UTC tuomov + * trunk: changeset 1386 + Region closing method unifications. + +2004-03-12 16:27 UTC tuomov + * trunk: changeset 1385 + Exported functions can now accept nil objects as parameters except for + the first parameter. (Most exported functions have just that first + object parameter.) + +2004-03-11 17:20 UTC tuomov + * trunk: changeset 1384 + Unnamed regions can now also bee seen on lists of regions. + +2004-03-10 21:12 UTC tuomov + * trunk: changeset 1381 + GCC linking order brain-damagedness workaround fixes. + +2004-03-10 21:03 UTC tuomov + * trunk: changeset 1379 + Slightly simpler and faster Lua object cache. + +2004-03-10 20:41 UTC tuomov + * trunk: changeset 1378 + - Moved X window code to xwindow.c. + + - Some other minor clean-up operations. + +2004-03-10 18:12 UTC tuomov + * trunk: changeset 1376 + Slightly reduce flicker with apps that update title too often by + removing a redundant change notify call. + +2004-03-10 18:01 UTC tuomov + * trunk: changeset 1375 + Handle bindmaps using red-black trees for simpler code (before + bindings were in arrays sorted and searched with qsort and bsearch). + +2004-03-10 16:49 UTC tuomov + * trunk: changeset 1372 + Also handle name allocation using red-black trees instead of Lua + tables now that such are available in libtu. + +2004-03-10 16:48 UTC tuomov + * trunk: changeset 1371 + Use red-black trees added to libtu instead of a Lua tables to store + split_of pointers of frames on WIonWS:s to avoid problems with object + cache clean-up on frame deletion. + +2004-03-10 10:47 UTC tuomov + * trunk: changeset 1367 + names.h wasn't being included. + +2004-03-07 21:46 UTC tuomov + * trunk: changeset 1364 + Bound backspace in menus to clear typeahead buffer. + +2004-03-07 21:45 UTC tuomov + * trunk: changeset 1363 + Moved strcasestr to libtu. + +2004-03-07 21:45 UTC tuomov + * trunk: changeset 1361 + Menus now support typeahead find. + +2004-03-07 17:37 UTC tuomov + * trunk: changeset 1359 + Removed ioncore_g.cwin_list. + +2004-03-07 17:29 UTC tuomov + * trunk: changeset 1358 + Removed completion functions from ioncore, moving them to querylib and + replacing with listing functions (ioncore.clientwin_list, + ioncore.region_list). + +2004-03-07 17:28 UTC tuomov + * trunk: changeset 1357 + Restored missing workspacelist menu entry. + +2004-03-07 15:35 UTC tuomov + * trunk: changeset 1356 + Fixed pmenu stacking. + +2004-03-07 11:13 UTC tuomov + * trunk: changeset 1355 + Added ioncore_post_layout_setup_hook. + +2004-03-07 10:41 UTC tuomov + * trunk: changeset 1354 + All modules are now 'mod_something'. + +2004-03-07 10:39 UTC tuomov + * trunk: changeset 1353 + Oops, some NULL checks were missing from new stacking code. + +2004-03-07 00:26 UTC tuomov + * trunk: changeset 1352 + Merged recent changes from the stable branch (WFloatWS.attach, better + gravity handling, goto_* routines return target). + +2004-03-07 00:24 UTC tuomov + * trunk: changeset 1351 + Brought PWM binding and menu configuration files up to date. + +2004-03-06 23:59 UTC tuomov + * trunk: changeset 1350 + - WMPlex now supports a full "second layer" of objects in addition to + a single input. + + - A lot of WIonFrame code was removed and moved to WFrame. + + - New rescue and initial management handling code. + +2004-03-06 23:53 UTC tuomov + * trunk: changeset 1349 + region_stack_above wasn't working. + +2004-03-06 23:12 UTC tuomov + * trunk: changeset 1348 + Stacking code relies on XQueryTree instead of maintaining our internal + state that might not agree with X. + +2004-03-06 23:08 UTC tuomov + * trunk: changeset 1347 + Fixed dock configuration file for new binding system. + +2004-02-18 03:00 UTC tuomov + * trunk: changeset 1336 + Merged tab drag&drop fixes and size guard additions from the stable + branch. + +2004-02-17 23:16 UTC tuomov + * trunk: changeset 1330 + Oops, some old class names were used in the merge. + +2004-02-17 18:40 UTC tuomov + * trunk: changeset 1329 + Merged fixes from the stable branch. + +2004-02-17 01:32 UTC tuomov + * trunk: changeset 1326 + Autosave disabling was missing. + +2004-02-16 23:27 UTC tuomov + * trunk: changeset 1324 + - Changed layout save mechanism to construct configuration tables + instead of directly writing data to files. + + - Some more mainloop changes. + +2004-02-16 20:33 UTC tuomov + * trunk: changeset 1322 + Documentation and documentation generation updates. + +2004-02-16 19:22 UTC tuomov + * trunk: changeset 1321 + Moved SunOS F11->SunF36, F12->SunF37 kludge to build time. + +2004-02-16 19:02 UTC tuomov + * trunk: changeset 1320 + Mainloop X flushing fix. + +2004-02-16 17:50 UTC tuomov + * trunk: changeset 1317 + Switched to using spaces only for indentation. Simple automatic "\t" + -> " " conversion; may need more tuning. + +2004-02-16 17:08 UTC tuomov + * trunk: changeset 1315 + Oops. errorlog code was still here. + +2004-02-16 17:05 UTC tuomov + * trunk: changeset 1314 + - Moved object system and other generic code to libtu. + + - Changed the mainloop to only handle file descriptors with the X + connection just a case among other inputfd:s. + +2004-02-16 04:42 UTC tuomov + * trunk: changeset 1311 + Moved stuff to the C side. + +2004-02-16 04:29 UTC tuomov + * trunk: changeset 1310 + Some minor fixes to stuff broken by namespace change. + +2004-02-16 04:14 UTC tuomov + * trunk: changeset 1309 + classes.h was missing from previous commmit. + +2004-02-16 04:13 UTC tuomov + * trunk: changeset 1308 + - Major namespace clean-up, both C and Lua-side. + + - Yet another binding configuration scheme. + +2004-02-15 19:06 UTC tuomov + * trunk: changeset 1300 + Added function to return line editor "mark". + +2004-02-14 23:31 UTC tuomov + * trunk: changeset 1299 + - Removed gr_get_brush_values and the possibility obtain brush + parameters before creating a window. + + - Added "above" and "below" as supported directions with some ionws + functions. + +2004-02-14 22:11 UTC tuomov + * trunk: changeset 1298 + Merged floatframe tab toggle and man page fixes from the stable + branch. + +2004-02-14 22:11 UTC tuomov + * trunk: changeset 1297 + Removed delib.lua and moved the stuff on the C side. + +2004-02-12 12:40 UTC tuomov + * trunk: changeset 1294 + Added manual page caching setup help in README. + +2004-02-12 11:42 UTC tuomov + * trunk: changeset 1293 + Removed superfluous navigation functions from WIonWS. + +2004-02-11 21:12 UTC tuomov + * trunk: changeset 1292 + Lua interface improvements. + +2004-02-11 09:56 UTC tuomov + * trunk: changeset 1290 + - Portability fixes merged from stable branch. + + - Fixed ion-completeman to use = insteaf == with 'test'. + + - Minor binding changes. + +2004-02-11 03:02 UTC tuomov + * trunk: changeset 1288 + Slightly better pipe stall check. + +2004-02-11 02:48 UTC tuomov + * trunk: changeset 1287 + Added line counter reset. + +2004-02-11 02:45 UTC tuomov + * trunk: changeset 1286 + Querylib tries to waste a little less memory when reading completions + from a pipe. + +2004-02-11 01:50 UTC tuomov + * trunk: changeset 1285 + Makefile sed fix. + +2004-02-11 01:41 UTC tuomov + * trunk: changeset 1284 + Merged fixes from the "stable" Ion2 branch. + +2004-02-11 01:28 UTC tuomov + * trunk: changeset 1282 + Forgot to upgrade querylib to new script parameters. + +2004-02-11 01:25 UTC tuomov + * trunk: changeset 1281 + Man-page completion is now implemented with the external script 'ion- + completeman'. The script supports cacheng names of known manual pages + (as a cronjob) for much faster completion. It can also use the + 'manpath' program in addition to the MANPATH or ION_MC_MANPATH + environment variables to figure out the paths containing man pages. + +2004-02-11 00:59 UTC tuomov + * trunk: changeset 1280 + Completion display wasn't being refreshed, if its size didn't change + between completions. + +2004-02-10 23:12 UTC tuomov + * trunk: changeset 1277 + - New binding configuration scheme based on strings instead of direct + passing of Lua functions. + + - Some WMPlex managed object indexing function changes. + +2004-02-10 19:47 UTC tuomov + * trunk: changeset 1276 + Move/resize mode is common to all types of frames. + +2004-02-10 19:35 UTC tuomov + * trunk: changeset 1274 + There were still some WGENFRAME macros. + +2004-02-10 18:29 UTC tuomov + * trunk: changeset 1273 + Changed version and API version. + +2004-02-10 10:42 UTC tuomov + * trunk: changeset 1271 + File name changes and a couple of fixes to the WGenFrame -> WFrame + change. + +2004-02-10 10:36 UTC tuomov + * trunk: changeset 1270 + Renamed WGenFrame to WFrame. + +2004-02-10 10:35 UTC tuomov + * trunk: changeset 1269 + Changed default installation path to /usr/local/ion-3 and user + configuration file path to ~/.ion3. + +2004-02-07 01:56 UTC tuomov + * trunk: changeset 1256 + Brought WClientWin.get_ident documentation up-to-date. + +2004-02-05 22:09 UTC tuomov + * trunk: changeset 1252 + Implemented a kludge to track "awaiting focus" state. This will allow, + for example, windows changing to full screen mode immediately after + mapping (instead of doing it right and setting the properties before + mapping) to be switched to. + +2004-02-05 16:30 UTC tuomov + * trunk: changeset 1250 + Added extra include for dock. + +2004-02-05 16:29 UTC tuomov + * trunk: changeset 1249 + WIonWS.newframe wasn't adjusting sizes of existing frames nicely. + +2004-02-05 09:10 UTC tuomov + * trunk: changeset 1248 + Removed 'const' from 'spec' entry. + +2004-02-03 18:45 UTC tuomov + * trunk: changeset 1246 + - Minor memory leak removed: drawing engine colour group name. + + - Some extra safety checks added in line editor. + +2004-02-02 20:10 UTC tuomov + * trunk: changeset 1245 + Changed the autoconf script not to use \" that not all shells + apparently support. + +2004-02-01 20:58 UTC tuomov + * trunk: changeset 1244 + Fixed line breaking in really narrow listings. + +2004-02-01 11:54 UTC tuomov + * trunk: changeset 1243 + Man page fixes. + +2004-01-30 15:34 UTC tuomov + * trunk: changeset 1241 + - Unblock signals at startup as GDM in its great wisdom initially + blocks the window manager from receiving SIGCHLD resulting in + zombies. + + - More changes to (floatws) stacking policy. + +2004-01-29 20:07 UTC tuomov + * trunk: changeset 1240 + - Transient size calculation fixed. + + - Creating frame for a transient on a floatws will raise the frame + containing the transient_for window. + + - Added notes on Lua 5.0 in README. + +2004-01-28 16:02 UTC tuomov + * trunk: changeset 1238 + - Minor man page improvements. + + - Typo fix in system.mk. + +2004-01-28 06:41 UTC tuomov + * trunk: changeset 1237 + Changed libtool minimum version requirement back to safe 1.4.3. + +2004-01-27 22:38 UTC tuomov + * trunk: changeset 1236 + Documentation comment for ioncore_userdir was broken. + +2004-01-27 18:10 UTC tuomov + * trunk: changeset 1233 + Ooops, testing changes were left in development install path. + +2004-01-27 18:09 UTC tuomov + * trunk: changeset 1232 + Removed superfluous call to wedln_draw_completions. + +2004-01-27 13:49 UTC tuomov + * trunk: changeset 1229 + Install documentation and manual pages under $PREFIX/share as per the + FHS. + +2004-01-26 14:49 UTC tuomov + * trunk: changeset 1228 + - Added autoconf script support for -DCF_NO_MB_SUPPORT + + - Remove system-ac.mk in 'make realclean'. + +2004-01-26 14:48 UTC tuomov + * trunk: changeset 1227 + Fixed an 'err' variable to be local. + +2004-01-25 23:35 UTC tuomov + * trunk: changeset 1226 + Trap signals at an earlier stage so that processes forked at startup + don't become temporarily defunct. + +2004-01-25 18:19 UTC tuomov + * trunk: changeset 1224 + There was still a reference to ~/.ion/ + +2004-01-25 18:12 UTC tuomov + * trunk: changeset 1223 + Class lookup is no longer case-insensitive. (There's no need for that + since the workspace creation query was changed.) + +2004-01-25 18:05 UTC tuomov + * trunk: changeset 1221 + Typo and spelling fixes. + +2004-01-25 11:07 UTC tuomov + * trunk: changeset 1219 + Some XX_SOURCE flag changes. + +2004-01-24 21:54 UTC tuomov + * trunk: changeset 1218 + Renames. + +2004-01-24 21:49 UTC tuomov + * trunk: changeset 1217 + - Added dummy multibyte/widechar routines for retarded platforms + without even such dummy support. + + - Some Cygwin note modifications. + +2004-01-24 20:28 UTC tuomov + * trunk: changeset 1216 + Some extra safety checks added. + +2004-01-22 21:25 UTC tuomov + * trunk: changeset 1214 + Fixed LaTeX in dock documentation comments. + +2004-01-21 19:32 UTC tuomov + * trunk: changeset 1212 + Some more polish on floatws focus policy. + +2004-01-21 17:32 UTC tuomov + * trunk: changeset 1211 + - Moved session directory creation to a proper place, so that the + directory will exist for the style menu save feature. + + - Fill _NET_SUPPORTED root window property with the supported features + (_NET_WM_FULLSCREEN, _NET_WM_NAME, _NET_VIRTUAL_ROOTS). + +2004-01-20 21:11 UTC tuomov + * trunk: changeset 1209 + Module preloading support was broken. + +2004-01-20 19:50 UTC tuomov + * trunk: changeset 1208 + Message cleaned up. + +2004-01-20 18:37 UTC tuomov + * trunk: changeset 1207 + Binaries are now again stripped when installed. + +2004-01-20 18:35 UTC tuomov + * trunk: changeset 1206 + Fixed a man page typo. + +2004-01-19 18:26 UTC tuomov + * trunk: changeset 1205 + Fixed tab drop on a floatws. + +2004-01-19 16:03 UTC tuomov + * trunk: changeset 1204 + Old submaps were ignored when new entries were added. + +2004-01-19 12:22 UTC tuomov + * trunk: changeset 1203 + Fixed libtool version requirements. + +2004-01-19 12:22 UTC tuomov + * trunk: changeset 1202 + - Autoconf script Xinerama check fixed. + + - README.autoconf simplified. + +2004-01-19 12:20 UTC tuomov + * trunk: changeset 1201 + Fixed references to ioncore-*.lua. + +2004-01-16 18:39 UTC tuomov + * trunk: changeset 1200 + There were still some references to frame-tab (how tab-frame), causing + initial frame sizes on floatws:s to be miscalculated. + +2004-01-16 18:38 UTC tuomov + * trunk: changeset 1199 + Oops. Removed some important code earlier. + +2004-01-16 16:58 UTC tuomov + * trunk: changeset 1198 + - Minor client window management set up fixes. + + - Pass _ION_KLUDGES (XA_STRING) property in WClientwin.get_ident as + .kludges. + +2004-01-16 15:35 UTC tuomov + * trunk: changeset 1197 + - Fixed goto_previous after switching between windows in the same + frame with a query. + + - The dock no longer gets focused when the pointer enters it. + +2004-01-15 18:34 UTC tuomov + * trunk: changeset 1196 + Changed order of mplex old/new selected object map/unmap to reduce + flicker. + +2004-01-15 16:10 UTC tuomov + * trunk: changeset 1195 + - A couple manual page and README bugs were fixed. + + - Removed Mod1+K T rebinding in ionframe_bindings to + toggle_transients_pos (it is supposed to be clear_tags). + +2004-01-14 22:43 UTC tuomov + * trunk: changeset 1194 + Style selection saving wasn't working. + +2004-01-14 19:37 UTC tuomov + * trunk: changeset 1191 + Removed extra character in comment header. + +2004-01-14 18:03 UTC tuomov + * trunk: changeset 1190 + - Floating workspace now give focus to highest-stacked frame when the + active one is destroyed. + + - Mod1+N/P were bound to raise/lower on floating workspaces. + +2004-01-14 16:55 UTC tuomov + * trunk: changeset 1189 + Changed version string to ION_VERSION. + +2004-01-14 16:39 UTC tuomov + * trunk: changeset 1188 + - Added optional autoconf script. + + - Fixed etc/Makefile. + +2004-01-13 23:52 UTC tuomov + * trunk: changeset 1187 + - Renamed do_move_pointer_to do_warp and made redefinable through the + do_warp_alt hook. + + - Fixed reparent_region->region_reparent. + + - Added some missing includes. + +2004-01-13 16:31 UTC tuomov + * trunk: changeset 1186 + Frame maximize takes active client window size limits into account. + +2004-01-13 16:12 UTC tuomov + * trunk: changeset 1185 + Keyboard move/resize warps (if enabled) pointer back to manipulated + frame after finishing. + +2004-01-08 23:16 UTC tuomov + * trunk: changeset 1184 + Fixed some typos in documentation. + +2004-01-08 22:05 UTC tuomov + * trunk: changeset 1182 + Changed winprop selection by title to longest match. + +2004-01-08 21:51 UTC tuomov + * trunk: changeset 1181 + - Support changes in X keyboard map. + + - Some keymap deinitialisation fixes and bindmap clean-up. + +2004-01-06 19:48 UTC tuomov + * trunk: changeset 1180 + Minor man page fixes. + +2004-01-06 12:30 UTC tuomov + * trunk: changeset 1179 + Removed introduction of a non-existent function. + +2004-01-03 00:27 UTC tuomov + * trunk: changeset 1178 + Updated most of the copyright notices to 2004. + +2003-12-29 20:26 UTC tuomov + * trunk: changeset 1177 + - stylemenu properly saves draw.lua if querylib is not installed. + + - Separate binding configuration file for PWM. + +2003-12-29 20:14 UTC tuomov + * trunk: changeset 1176 + - Some default menu configuration changes. PWM no longer uses the same + menu configuration file. + + - Removed client window check code mismatch complaint on startup. + + - PWM startup errorlog was incorrectly referring to Ion. + +2003-12-29 20:01 UTC tuomov + * trunk: changeset 1175 + Removed debug message. + +2003-12-29 18:42 UTC tuomov + * trunk: changeset 1174 + Workspace query asks for type of workspace when creating new. + +2003-12-29 15:53 UTC tuomov + * trunk: changeset 1173 + Previous frame geometry (for maximize and shade) is saved in the + workspaces save file. + +2003-12-23 21:14 UTC tuomov + * trunk: changeset 1172 + Added a note on the dock module authors to README. + +2003-12-23 21:13 UTC tuomov + * trunk: changeset 1171 + - Added dock to list of modules to build. + + - Updated to newer version of dock. + + - Dock position and growing direction was changed to old PWM defaults. + + - PWM stock configuration files load the dock module by default. + +2003-12-23 20:55 UTC tuomov + * trunk: changeset 1169 + - Included the dock module with Ion. + + - Fixed dock_get_tile_size and modified dock module Makefile for + inclusion in Ion. + +2003-12-22 20:45 UTC tuomov + * trunk: changeset 1100 + Use also C99_SOURCE where XOPEN_SOURCE is used. + +2003-12-22 20:11 UTC tuomov + * trunk: changeset 1099 + include stdio.h + +2003-12-20 13:08 UTC tuomov + * trunk: changeset 1098 + XFreeGC(xor_gc) on deinit. + +2003-12-18 18:08 UTC tuomov + * trunk: changeset 1097 + Focusing on return from submenu had been broken by previous changes. + +2003-12-18 17:57 UTC tuomov + * trunk: changeset 1096 + look-clean "bigmenu" font changed. + +2003-12-18 17:52 UTC tuomov + * trunk: changeset 1095 + WScreens no longer accept tab drag&drop. + +2003-12-18 17:48 UTC tuomov + * trunk: changeset 1094 + Better error message in querylib.query_workspace. + +2003-12-18 17:45 UTC tuomov + * trunk: changeset 1093 + Message line counting fix. + +2003-12-18 17:42 UTC tuomov + * trunk: changeset 1092 + Class lookup is now case-insensitive. + +2003-12-18 17:28 UTC tuomov + * trunk: changeset 1091 + Added some object destroy safety checks. + +2003-12-18 17:22 UTC tuomov + * trunk: changeset 1090 + Fixed winprop lookup when window name is nil. + +2003-12-18 12:08 UTC tuomov + * trunk: changeset 1089 + Use lua-config50 in Debian settings. + +2003-12-16 20:54 UTC tuomov + * trunk: changeset 1088 + Re-created the file. + +2003-12-16 20:52 UTC tuomov + * trunk: changeset 1087 + Less verbosity. + +2003-12-16 18:29 UTC tuomov + * trunk: changeset 1086 + Another old reference. + +2003-12-14 11:52 UTC tuomov + * trunk: changeset 1085 + Fixed (?) grab release focusing when warping is disabled. + +2003-12-11 22:16 UTC tuomov + * trunk: changeset 1084 + Man pages still had references to old user configuration file + directory. + +2003-12-11 21:31 UTC tuomov + * trunk: changeset 1083 + Fixed querylib.query_man default value. + +2003-12-11 18:03 UTC tuomov + * trunk: changeset 1082 + Fixed .welcome_msg_displayed permissions. + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20031211 + +2003-12-11 17:11 UTC tuomov + * trunk: changeset 1081 + Fixed a potential segfault when window disappears while being set up + to be managed. + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20031210 + +2003-12-10 09:17 UTC tuomov + * trunk: changeset 1080 + Updates. + +2003-12-09 20:39 UTC tuomov + * trunk: changeset 1079 + Removed compat.lua + +2003-12-09 19:48 UTC tuomov + * trunk: changeset 1078 + Removed mention of development branch. + +2003-12-09 19:48 UTC tuomov + * trunk: changeset 1077 + Minor size calculation fix. + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20031209 + +2003-12-09 19:28 UTC tuomov + * trunk: changeset 1076 + Module compatibility is checked against ION_API_VERSION (2) instead of + ION_VERSION. + +2003-12-09 19:13 UTC tuomov + * trunk: changeset 1075 + Refer to ~/.ion2/. + +2003-12-09 19:07 UTC tuomov + * trunk: changeset 1074 + Removed configuration file conversion utilities; people following the + development branch should already have converted their files. + +2003-12-09 19:05 UTC tuomov + * trunk: changeset 1073 + - Removed '-devel' from path names. + + - Changed user configuration file directories to ~/.ion2/ and + ~/.pwm2/. + + - Renamed all ioncore*.lua configuration files to ion*.lua. + + - The main configuration file for Ion is ion.lua and for PWM pwm.lua. + +2003-12-09 00:32 UTC tuomov + * trunk: changeset 1072 + Changed LUA_PATH to LUA_DIR to avoid conflicts with Lua itself. + +2003-12-07 22:05 UTC tuomov + * trunk: changeset 1071 + Oops. + +2003-12-07 22:01 UTC tuomov + * trunk: changeset 1070 + Use string.shell_safe to encapsulate file names. + +2003-12-07 22:01 UTC tuomov + * trunk: changeset 1069 + Added string.shell_safe function. + +2003-12-06 22:20 UTC tuomov + * trunk: changeset 1068 + Insert new bindmaps at beginning of region bindmap list. + +2003-12-06 20:43 UTC tuomov + * trunk: changeset 1067 + Initial workspace creation code checks default_ws_type. + +2003-12-06 20:36 UTC tuomov + * trunk: changeset 1066 + Changed ion-completefile path. + +2003-12-06 17:45 UTC tuomov + * trunk: changeset 1065 + Added WClientWin.toggle_transients_pos export and transients_at_top + winprop. + +2003-12-06 17:44 UTC tuomov + * trunk: changeset 1064 + Bound WClientWin.toggle_transients_pos to DEFAULT_MOD+K T. + +2003-12-06 17:43 UTC tuomov + * trunk: changeset 1063 + Require Lua 5.0.1. + +2003-12-06 17:43 UTC tuomov + * trunk: changeset 1062 + Removed extra whitespace at ends of some lines. + +2003-12-06 16:17 UTC tuomov + * trunk: changeset 1061 + Fixed previous change. + +2003-12-06 12:25 UTC tuomov + * trunk: changeset 1060 + Some transient initial size calculation changes. + +2003-12-06 11:44 UTC tuomov + * trunk: changeset 1059 + Changed export names and added one more. + +2003-12-06 11:42 UTC tuomov + * trunk: changeset 1058 + Added "stylemenu" that displays all look-*.lua files on search path. + +2003-12-06 11:42 UTC tuomov + * trunk: changeset 1057 + Added some search path exports. + +2003-12-06 11:15 UTC tuomov + * trunk: changeset 1056 + ion-completefile directory changed and extended to support multiple + paths. + +2003-12-05 00:46 UTC tuomov + * trunk: changeset 1055 + - Submenus can now be generated by functions when needed. + + - Added "windowlist" and "workspacelist" default menus. + +2003-12-04 22:10 UTC tuomov + * trunk: changeset 1054 + Removed a tab. + +2003-12-04 21:56 UTC tuomov + * trunk: changeset 1053 + In-mplex menus that do not fully fit within the space available now + scroll the visible entries. + +2003-12-03 23:43 UTC tuomov + * trunk: changeset 1052 + aboutmsg was missing. + +2003-12-03 23:43 UTC tuomov + * trunk: changeset 1051 + Comment change. + +2003-12-03 22:27 UTC tuomov + * trunk: changeset 1050 + Closing a frame initially contaning transient window will switch focus + to the frame that contained the transient_for window. + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20031203 + +2003-12-03 20:33 UTC tuomov + * trunk: changeset 1049 + Removed mentions of ion-devel in the release tarball; default + installation target is now /usr/local with configuration files in + /usr/local/etc/ion and ~/.ion. (CVS snapshots will still use + /usr/local/ion-devel.) + +2003-12-03 20:31 UTC tuomov + * trunk: changeset 1048 + Just ditch old $ETCDIR/draw.lua. + +2003-12-03 20:29 UTC tuomov + * trunk: changeset 1047 + Added pwm.1 man page. + +2003-12-03 20:22 UTC tuomov + * trunk: changeset 1046 + Use PWM_ETCDIR if set. + +2003-12-03 20:14 UTC tuomov + * trunk: changeset 1045 + Load the query module. + +2003-12-03 20:06 UTC tuomov + * trunk: changeset 1044 + - The ion and pwm scripts were removed and are instead now binaries + linked against ioncore.a. + + - The 'ion' binary uses ~/.ion (no longer ~/.ion-devel) for user's + configuration files and the 'pwm' binary uses ~/.pwm for user's + configuration files. + +2003-12-03 17:59 UTC tuomov + * trunk: changeset 1043 + Script search path changes; calling file's directly is now only + considered if explicitly indicated in the file name. (So now normally + "included" files in user's directories will always be preferred over + those in system directories.) + +2003-12-03 11:37 UTC tuomov + * trunk: changeset 1042 + etc/ make install changes; ioncore.lua is now always overwritten and + draw.lua link restored to a working file. + +2003-12-02 23:23 UTC tuomov + * trunk: changeset 1041 + Display a welcome message and manual page to new users. + +2003-12-02 23:22 UTC tuomov + * trunk: changeset 1040 + - Display a welcome message and manual page to new users. + + - Some path setting function changes. + +2003-12-02 23:20 UTC tuomov + * trunk: changeset 1039 + - Renamed manual page to ion.1. + + - Added ion.1.in.txt + +2003-12-02 23:11 UTC tuomov + * trunk: changeset 1038 + Minor modification. + +2003-12-02 23:01 UTC tuomov + * trunk: changeset 1037 + Removed mention of "messages" to reduce chance of confusion. + +2003-12-02 18:31 UTC tuomov + * trunk: changeset 1036 + Added a "basic concepts" section in the manual page. + +2003-12-02 18:05 UTC tuomov + * trunk: changeset 1035 + Fixed message display line copying bug. + +2003-12-02 13:37 UTC tuomov + * trunk: changeset 1034 + Fixed -onescreen to -oneroot. + +2003-12-01 20:18 UTC tuomov + * trunk: changeset 1033 + Button6 and Button7 might be recognised. + +2003-12-01 20:18 UTC tuomov + * trunk: changeset 1032 + Ignore unknown bits in keypress event state. + +2003-12-01 19:09 UTC tuomov + * trunk: changeset 1031 + Minor clean-up. + +2003-12-01 18:23 UTC tuomov + * trunk: changeset 1030 + The line editor and region_set_name strip white space from the + beginning and end of strings. + +2003-12-01 01:34 UTC tuomov + * trunk: changeset 1029 + Some more acceleration changes. + +2003-11-30 16:51 UTC tuomov + * trunk: changeset 1028 + - Implemented move/resize edge snapping. + + - Smoother square root based move/resize acceleration curve. + +2003-11-30 16:49 UTC tuomov + * trunk: changeset 1027 + Uncommented CF_EDGE_RESISTANCE. + +2003-11-30 00:51 UTC tuomov + * trunk: changeset 1026 + Save sticky state in session savefile. + +2003-11-30 00:48 UTC tuomov + * trunk: changeset 1025 + Implemented sticky frames on WFloatWS:s. + +2003-11-30 00:34 UTC tuomov + * trunk: changeset 1024 + Implemented WWatch-node based WObjList and changed tag list to that + instead of wasting space in WRegion. + +2003-11-29 22:46 UTC tuomov + * trunk: changeset 1023 + Only redraw menu entries that need redrawing when selected entry is + changed. + +2003-11-29 16:27 UTC tuomov + * trunk: changeset 1022 + Some focus handling changes, fixes and clean-up. + +2003-11-29 15:35 UTC tuomov + * trunk: changeset 1021 + "Close" key and menu binding changes. + +2003-11-29 15:34 UTC tuomov + * trunk: changeset 1019 + Minor clean-up. + +2003-11-29 14:36 UTC tuomov + * trunk: changeset 1018 + Fixed selection request when multibyte support is not enabled. + +2003-11-29 14:31 UTC tuomov + * trunk: changeset 1017 + Small documentation fix. + +2003-11-29 14:29 UTC tuomov + * trunk: changeset 1016 + Do not display a query_message error when failing to open + ~/.ssh/known_hosts. + +2003-11-29 14:21 UTC tuomov + * trunk: changeset 1015 + Improved documentation. + +2003-11-29 14:21 UTC tuomov + * trunk: changeset 1014 + Fixed underscore and tiled escapes. + +2003-11-27 22:26 UTC tuomov + * trunk: changeset 1013 + Added note on known_hosts in documentation comment. + +2003-11-27 22:26 UTC tuomov + * trunk: changeset 1012 + Keyboard is no longer grabbed in submap mode if normal bindmap is not + grabed. + +2003-11-27 19:58 UTC tuomov + * trunk: changeset 1011 + Oops. + +2003-11-27 16:18 UTC tuomov + * trunk: changeset 1010 + Minor clean-up operations. + +2003-11-27 15:56 UTC tuomov + * trunk: changeset 1009 + Added some stacking setup and mplex attach sanity checks. + +2003-11-27 15:42 UTC tuomov + * trunk: changeset 1008 + Added clear_tags ctxmenu entry. + +2003-11-27 15:38 UTC tuomov + * trunk: changeset 1007 + Region rescue code was cleaned up a little and the code is no longer + called from deinit handlers. + +2003-11-25 20:37 UTC tuomov + * trunk: changeset 1006 + Added WIonWS.farthest, next_to and goto_dir exports. + +2003-11-25 19:44 UTC tuomov + * trunk: changeset 1005 + CF_STR_EMPTY added. + +2003-11-24 16:52 UTC tuomov + * trunk: changeset 1004 + Oops, 16pt helvetica may not be available. Use 17pt, 18pt is bold even + as medium. + +2003-11-24 16:32 UTC tuomov + * trunk: changeset 1003 + Fonts are chosen more consistently in provided look-* style files. + +2003-11-24 16:07 UTC tuomov + * trunk: changeset 1002 + look-cleanios was missing from Makefile. + +2003-11-24 16:02 UTC tuomov + * trunk: changeset 1001 + Removed "for" from get_savefile_for, read_config_for, etc. + +2003-11-24 16:02 UTC tuomov + * trunk: changeset 1000 + - Added 'jumpto' winprop and 'clientwin_added' hook. + + - 'switchto' is passed as parameter to fullscreen check functions. + + - Exported 'get_savefile'. + + - Removed "for" from get_savefile_for, read_config_for, etc. + + - Moved winprop lookup to the proper point in code after having again + moved it where name is not available. + +2003-11-24 01:14 UTC tuomov + * trunk: changeset 999 + Fixed -libdir to -moduledir. + +2003-11-23 19:45 UTC tuomov + * trunk: changeset 998 + Changes in stipple pattern usage in drawing a dragged tab. + +2003-11-23 18:37 UTC tuomov + * trunk: changeset 997 + Added WIonWS.resize_tree function that can be used to resize whole + subtrees of the workspace split hierarchy. + +2003-11-23 13:19 UTC tuomov + * trunk: changeset 996 + Removed the styles. + +2003-11-23 13:18 UTC tuomov + * trunk: changeset 995 + Added new style look-cleanios. + +2003-11-23 04:54 UTC tuomov + * trunk: changeset 994 + 'pmenu' entry index could be too big by one. Fixed. + +2003-11-23 04:53 UTC tuomov + * trunk: changeset 993 + Look configuration files properly ported the drawing engine model and + proper menu styles added to those styles that need it. + +2003-11-23 03:12 UTC tuomov + * trunk: changeset 992 + String shortening rules can now be specified always-on so that the + rule is applied even when no shortening is necessary. + +2003-11-23 03:10 UTC tuomov + * trunk: changeset 991 + The same region is not to be passed twice as a _key_ binding handler + argument; two regions are only passed when e.g. a workspace is + selecting keys on a frame. + +2003-11-23 03:08 UTC tuomov + * trunk: changeset 990 + Changed make_mplex_sub_or_self_fn behaviour. + +2003-11-23 02:45 UTC tuomov + * trunk: changeset 989 + Completable hosts for SSH query are parsed from ~/.ssh/known_hosts + (instead of requiring the user to a list of them). + +2003-11-22 15:44 UTC tuomov + * trunk: changeset 988 + Added Button3->rootmenu binding. + +2003-11-21 16:45 UTC tuomov + * trunk: changeset 987 + Added mention of modulelist.mk. + +2003-11-21 16:40 UTC tuomov + * trunk: changeset 986 + Modified the default drawing engine to be more usable as a basis for + alternative drawing engines that need per-window data. + +2003-11-21 15:09 UTC tuomov + * trunk: changeset 985 + Title shortening rules are now used always even if the title would fit + in the available space without modification. + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20031121 + +2003-11-21 14:43 UTC tuomov + * trunk: changeset 984 + clean-up variable rename. + +2003-11-21 09:11 UTC tuomov + * trunk: changeset 983 + Invalid multibyte strings could cause the line editor to go to an + endless loop. + +2003-11-21 09:00 UTC tuomov + * trunk: changeset 982 + Listing (completions, messages) line breaking wasn't yet multibyte- + aware. + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20031119 + +2003-11-21 04:10 UTC tuomov + * trunk: changeset 981 + New string shortening code could segfault. Fixed it. + +2003-11-19 21:39 UTC tuomov + * trunk: changeset 980 + Replaced UTF-8 support with (almost) general multibyte encoding + support. + +2003-11-17 21:03 UTC tuomov + * trunk: changeset 978 + mplex_fit didn't update region geometry data. + +2003-11-17 21:03 UTC tuomov + * trunk: changeset 977 + A minor clean-up operation. + +2003-11-16 21:40 UTC tuomov + * trunk: changeset 976 + Updated documentation. + +2003-11-16 17:16 UTC tuomov + * trunk: changeset 975 + Load querylib. + +2003-11-16 17:05 UTC tuomov + * trunk: changeset 974 + make_mplex_sub_or_self_fn was broken. + +2003-11-16 17:05 UTC tuomov + * trunk: changeset 973 + A variable name was wrong. + +2003-11-16 16:51 UTC tuomov + * trunk: changeset 972 + Added support for _NET_WM_STATE_FULLSCREEN request. + +2003-11-16 15:19 UTC tuomov + * trunk: changeset 971 + Reset font to NULL on deinit. + +2003-11-16 14:58 UTC tuomov + * trunk: changeset 970 + Bindings can now be removed on the fly by passing nil as callback + function. + +2003-11-16 13:56 UTC tuomov + * trunk: changeset 969 + Complain of invalid binding table. + +2003-11-16 13:48 UTC tuomov + * trunk: changeset 968 + Updated the Makefile. + +2003-11-16 13:48 UTC tuomov + * trunk: changeset 967 + Renamed ioncore-mplexfns.lua to ioncorelib-mplexfns.lua and moved all + mplex-related functions from ioncorelib.lua there. + +2003-11-16 01:44 UTC tuomov + * trunk: changeset 966 + Fixed GC font setup. + +2003-11-16 01:43 UTC tuomov + * trunk: changeset 965 + Don't compiled some code if CF_UTF8 is not set. + +2003-11-16 00:29 UTC tuomov + * trunk: changeset 964 + Updated the man page. + +2003-11-15 23:51 UTC tuomov + * trunk: changeset 963 + UTF8 is not used if locale is C/POSIX (or broken) even if Ion was + compiled with UTF8 support. + +2003-11-15 23:49 UTC tuomov + * trunk: changeset 962 + Added font caching/ref.counting so that font loading would take little + less time when pattern guessing is required to fullfill locale's + requirements. + +2003-11-14 20:35 UTC tuomov + * trunk: changeset 961 + Changed the order in which brushes are freed. + +2003-11-14 20:31 UTC tuomov + * trunk: changeset 960 + The original source of the the fontset code was discovered. + +2003-11-14 16:00 UTC tuomov + * trunk: changeset 959 + The functions to create binding wrappers to operate on WMPlexs' + children were extended and given better names, although the old ones + are still available. + +2003-11-14 15:56 UTC tuomov + * trunk: changeset 958 + nothing special. + +2003-11-14 15:49 UTC tuomov + * trunk: changeset 957 + Removed fallback font check. + +2003-11-14 15:40 UTC tuomov + * trunk: changeset 956 + Added FontSet guessing code that should be able to figure out enough + fonts for XCreateFontSet to fullfill locales' requirements so font + loading shouldn't fail so often when UTF8 support is enabled. + +2003-11-14 00:42 UTC tuomov + * trunk: changeset 955 + At least load "de" engine if no engine has been loaded while executing + draw.lua. + +2003-11-13 22:37 UTC tuomov + * trunk: changeset 954 + Added some _LOADED checks. + +2003-11-13 18:45 UTC tuomov + * trunk: changeset 953 + Lua files in share/ (but not etc/) are precompiled. + +2003-11-13 18:26 UTC tuomov + * trunk: changeset 952 + 'ioncore-startup.lua' kludge was removed and replaced by the loading + of 'ioncore-efbb.lua' in case of empty bindmaps from the C side. + +2003-11-13 18:04 UTC tuomov + * trunk: changeset 951 + Changes in default configuration files to make them more legible and + to add menu configuration. + +2003-11-13 18:03 UTC tuomov + * trunk: changeset 950 + Call XClearWindow when toggling tab to avoid clutter. + +2003-11-13 18:02 UTC tuomov + * trunk: changeset 949 + Added ioncore_aboutmsg export. + +2003-11-13 12:07 UTC tuomov + * trunk: changeset 948 + Added disabled XMMS kludge. + +2003-11-12 17:50 UTC tuomov + * trunk: changeset 947 + Extension definition changes. + +2003-11-12 17:50 UTC tuomov + * trunk: changeset 946 + - C-side module configuration file loading function also look for + compiled .lc files. + + - Lua-side include() automatically also looks for .lc and .lua files + if neither extension nor path component is given. + +2003-11-10 17:58 UTC tuomov + * trunk: changeset 945 + Made find_suitable_screen global. + +2003-11-09 16:09 UTC tuomov + * trunk: changeset 944 + Added WRegion.is_active and is_mapped exports. + +2003-11-06 17:50 UTC tuomov + * trunk: changeset 943 + Some mplex_managed_changed calls were wrong, causing trouble with + transparency. + +2003-11-05 22:45 UTC tuomov + * trunk: changeset 942 + include fixes. + +2003-11-05 22:45 UTC tuomov + * trunk: changeset 941 + Stack management fixes. + +2003-11-05 12:23 UTC tuomov + * trunk: changeset 940 + Removed mentions of xft. + +2003-11-04 20:27 UTC tuomov + * trunk: changeset 939 + Fixed a potential segfault point. + +2003-11-04 20:26 UTC tuomov + * trunk: changeset 938 + Quick&dirty stacking fix. + +2003-10-30 10:00 UTC tuomov + * trunk: changeset 937 + Removed CURRENT_FILE kludge for include handling; use the Lua debug + interface instead to get the file the calling function was defined in. + +2003-10-30 08:34 UTC tuomov + * trunk: changeset 936 + Some changes in default menus. + +2003-10-30 08:34 UTC tuomov + * trunk: changeset 935 + libtool 1.4.3->1.4.x. + +2003-10-28 13:00 UTC tuomov + * trunk: changeset 934 + submenus weren't being handled correctly. + +2003-10-27 16:59 UTC tuomov + * trunk: changeset 933 + about_msg should be local. + +2003-10-27 16:58 UTC tuomov + * trunk: changeset 932 + Changed styles are automatically translated (and complained of). + +2003-10-27 12:26 UTC tuomov + * trunk: changeset 931 + Fixed cursor and selection substyles. + +2003-10-27 12:05 UTC tuomov + * trunk: changeset 930 + Updated the script to use the new style names. + +2003-10-27 12:05 UTC tuomov + * trunk: changeset 929 + Some style name changes. + +2003-10-27 12:05 UTC tuomov + * trunk: changeset 928 + Changes in menus. + +2003-10-27 12:05 UTC tuomov + * trunk: changeset 927 + Updated the look-* files. + +2003-10-27 12:05 UTC tuomov + * trunk: changeset 926 + look-cleanviolet is now the default style. + +2003-10-25 20:23 UTC tuomov + * trunk: changeset 925 + Check fallback font at startup with XCreateFontSet instead of + XLoadQueryFont. + +2003-10-24 17:09 UTC tuomov + * trunk: changeset 924 + Moved list of modules to build from system.mk to modulelist.mk + +2003-10-04 14:52 UTC tuomov + * trunk: changeset 923 + va_list usage changes due to problems on some architechtures. + +2003-10-04 12:41 UTC tuomov + * trunk: changeset 922 + Added genframe_(in)activated hooks. + +2003-09-15 18:15 UTC tuomov + * trunk: changeset 921 + Some target and fullscreen winprop handling fixes. + +2003-09-15 18:15 UTC tuomov + * trunk: changeset 920 + Some fixes to client window rescuing. + +2003-09-15 18:15 UTC tuomov + * trunk: changeset 919 + Some fixes to client window rescuing. + +2003-09-15 18:14 UTC tuomov + * trunk: changeset 918 + Fixed a FALSE to NULL. + +2003-09-09 20:07 UTC tuomov + * trunk: changeset 917 + Fixed an event-missing problem. + +2003-09-04 08:22 UTC tuomov + * trunk: changeset 916 + Menu configuration changes. + +2003-09-04 08:22 UTC tuomov + * trunk: changeset 915 + Ungrab keyboard before calling dispatch_binding (except with submap). + +2003-08-31 16:15 UTC tuomov + * trunk: changeset 914 + Added a note on .xinitrc/.xsession to README. + +2003-08-28 20:59 UTC tuomov + * trunk: changeset 913 + Some border drawing fixes. + +2003-08-25 15:25 UTC tuomov + * trunk: changeset 912 + restart_other_wm fixes. + +2003-08-25 15:05 UTC tuomov + * trunk: changeset 911 + Better grabbing checks. + +2003-08-25 15:04 UTC tuomov + * trunk: changeset 910 + XSync() before fork() -- xlock should work a bit better now. + +2003-08-24 14:51 UTC tuomov + * trunk: changeset 909 + Menu config changes; use submenus. + +2003-08-24 14:51 UTC tuomov + * trunk: changeset 908 + Removed empty_tab area. + +2003-08-24 14:51 UTC tuomov + * trunk: changeset 907 + Moved minof/maxof elsewhere, removed empty_tab. + +2003-08-24 14:50 UTC tuomov + * trunk: changeset 906 + A little clean-up. + +2003-08-24 14:50 UTC tuomov + * trunk: changeset 905 + Added ''pmenu'' off-screen scrolling support as in PWM. + +2003-08-24 14:49 UTC tuomov + * trunk: changeset 904 + Some WTimer enhancements. + +2003-08-22 16:37 UTC tuomov + * trunk: changeset 903 + Fixed a crash problem in case of invalid use of WGenFrame.p_tabdrag. + +2003-08-21 18:29 UTC tuomov + * trunk: changeset 902 + - More changes to pointer handling code for better menu support. + + - genframe_bindings knows of frame areas. + +2003-08-21 18:28 UTC tuomov + * trunk: changeset 901 + Enhanced menu support. + +2003-08-21 18:24 UTC tuomov + * trunk: changeset 900 + Added a brush that handles submenu entries in menus specially. + +2003-08-21 18:22 UTC tuomov + * trunk: changeset 899 + 'make depend' fixed to work with modules. + +2003-08-20 16:46 UTC tuomov + * trunk: changeset 898 + Don't strip white space from query results. + +2003-08-19 12:54 UTC tuomov + * trunk: changeset 897 + Fixed tab drop on floatws:s (was putting clients to full screen mode + on occasion). + +2003-08-19 09:11 UTC tuomov + * trunk: changeset 896 + uname SunOS check complaint fix with newer gnu utils. + +2003-08-15 17:14 UTC tuomov + * trunk: changeset 895 + Some changes to pointer event handling code so menus can set handlers + immediately on button press. + +2003-08-15 13:59 UTC tuomov + * trunk: changeset 894 + Some files were missing.. again.. *sigh* + +2003-08-14 18:16 UTC tuomov + * trunk: changeset 893 + Added very preliminary and primitive menu support (only query-like + embedded-in-an-mplex menus). + +2003-08-14 18:08 UTC tuomov + * trunk: changeset 892 + Added ioncore_version export. + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20030814 + +2003-08-14 18:07 UTC tuomov + * trunk: changeset 891 + Some file location reorganisation. + +2003-08-14 18:07 UTC tuomov + * trunk: changeset 890 + Some file location reorganisation and header cleanup. + +2003-08-13 13:09 UTC tuomov + * trunk: changeset 889 + Call region_notify_change when name is unset. + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20030811-1 + +2003-08-13 13:08 UTC tuomov + * trunk: changeset 888 + Dragging a tab with no title could cause a segfault. + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20030811 + +2003-08-12 00:06 UTC tuomov + * trunk: changeset 887 + Changed location for X shape extension shape.h include. + +2003-08-11 23:05 UTC tuomov + * trunk: changeset 886 + There was an indexing bug in rootwin.c that caused stack corruption + and crash when Xinerama was enabled. + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20030810-1 + +2003-08-11 05:21 UTC tuomov + * trunk: changeset 885 + Winprop lookup had been broken. + +2003-08-10 17:02 UTC tuomov + * trunk: changeset 884 + make_exec_fn was broken when the parameter to created function was a + frame. + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20030810 + +2003-08-10 16:16 UTC tuomov + * trunk: changeset 883 + Removed duplicate variable. + +2003-08-10 16:15 UTC tuomov + * trunk: changeset 882 + Changes in tab reordering were not being updated to screen correctly. + +2003-08-10 13:46 UTC tuomov + * trunk: changeset 881 + Added some more functions to manipulate object indices within a + WMPlex. + +2003-08-08 15:40 UTC tuomov + * trunk: changeset 880 + close_sub_or_self is now WRegion.close_sub_or_self. + +2003-08-07 21:32 UTC tuomov + * trunk: changeset 879 + Modified the winprop patch to use the numerical zero field instead of + " ! " to store winprops with no name regexp specified. + +2003-08-07 20:49 UTC tuomov + * trunk: changeset 878 + Applied a patch to add title matching field 'name' (Lua regexp) in + winprops. + +2003-08-06 18:23 UTC tuomov + * trunk: changeset 877 + Fixed a typo in UTF8 font code. + +2003-08-06 18:21 UTC tuomov + * trunk: changeset 876 + Changed where get_winprop is called to a later time where the client + window's name has been set. + +2003-08-06 14:58 UTC tuomov + * trunk: changeset 875 + foreground colour was wrong. + +2003-08-06 14:35 UTC tuomov + * trunk: changeset 874 + Moved lookconv.lua to utils. + +2003-08-06 14:35 UTC tuomov + * trunk: changeset 873 + - Wrote a conversion script from older .lua workspaces savefiles. + + - Moved lookconv.lua to utils. + +2003-08-06 13:57 UTC tuomov + * trunk: changeset 872 + Updated style configuration files to draw tabs with the 'activity' + attribute set in white on red. 'lookconv.lua' uses these same colours + in converted files. + +2003-08-06 13:55 UTC tuomov + * trunk: changeset 871 + Implemented 'activity' display cue that is set when a newly created + client window is not displayed or when the urgency hint is set by the + client application. + +2003-08-06 13:46 UTC tuomov + * trunk: changeset 870 + Backslash wasn't being escaped in saved strings. + +2003-08-05 14:13 UTC tuomov + * trunk: changeset 869 + Improved handling of substyles with based_on. + +2003-08-05 14:09 UTC tuomov + * trunk: changeset 868 + Removed WRegion/mgr_data; use object-indexed tables instead. + +2003-08-05 14:07 UTC tuomov + * trunk: changeset 867 + Use a table instead of WRegion/mgr_data to store managed regions' + immediate splits. + +2003-08-05 13:30 UTC tuomov + * trunk: changeset 866 + Added functions to index tables by any supported type. + +2003-08-04 22:15 UTC tuomov + * trunk: changeset 865 + Wrong file name info in file. + +2003-08-04 21:30 UTC tuomov + * trunk: changeset 864 + Some table accessing routine unifications. + +2003-08-04 16:59 UTC tuomov + * trunk: changeset 863 + ionframe_bar_inside_border instead of bar_inside_frame + +2003-08-04 16:54 UTC tuomov + * trunk: changeset 862 + Enchanced WRegion.close documentation. + +2003-08-04 14:58 UTC tuomov + * trunk: changeset 861 + mdblclick(Button1) bound to shade on ionframes. + +2003-08-04 14:56 UTC tuomov + * trunk: changeset 860 + Ionframe shade fixed. + +2003-08-04 10:03 UTC tuomov + * trunk: changeset 859 + Added table entry type info to a documentation comment for + WMPlex.attach_*. + +2003-08-03 20:35 UTC tuomov + * trunk: changeset 858 + Added options to set floatframe bar width limits and tab bar location + for ionframes(fields floatframe_bar_max_w_q/floatframe_tab_min_w and + ionframe_bar_inside_borderin frame style config). + +2003-08-01 16:49 UTC tuomov + * trunk: changeset 857 + preliminary code to handle changing transient_for property... special + interface needed to handle floatws:s. + +2003-08-01 16:32 UTC tuomov + * trunk: changeset 856 + Added some line editing and history exports. + +2003-08-01 05:52 UTC tuomov + * trunk: changeset 855 + Added ignore_cfgrq winprop. + +2003-07-31 16:52 UTC tuomov + * trunk: changeset 854 + added spacing = 1 to input style. + +2003-07-31 16:52 UTC tuomov + * trunk: changeset 853 + listing fitting fixed. + +2003-07-31 14:16 UTC tuomov + * trunk: changeset 852 + Fixed some problems with selections in queries and added + wedln_clear_mark function. + +2003-07-31 14:16 UTC tuomov + * trunk: changeset 851 + Bound Control+G to end a query and Control+K G to clear mark in + queries. + +2003-07-31 13:55 UTC tuomov + * trunk: changeset 850 + Added some more querylib documentation and simplified a couple + functions. + +2003-07-31 13:49 UTC tuomov + * trunk: changeset 849 + Added gr_refresh and renamed reread_draw_config to gr_read_config. + +2003-07-31 13:49 UTC tuomov + * trunk: changeset 848 + Style configuration files now call de_reset and gr_refresh. + +2003-07-30 21:56 UTC tuomov + * trunk: changeset 847 + look-cleanviolet.lua was missing from set of installed files. + +2003-07-30 19:37 UTC tuomov + * trunk: changeset 846 + Added some ugly workarounds to some random Lua API functions not + checking the types of objects on stack. + +2003-07-30 19:33 UTC tuomov + * trunk: changeset 845 + Implemented keyboard resize acceleration. + +2003-07-30 15:44 UTC tuomov + * trunk: changeset 844 + Updated manpage with -sessionname. + +2003-07-30 15:27 UTC tuomov + * trunk: changeset 843 + WRectangle passing as argument changed to const WRectangle* mostly. + +2003-07-30 14:23 UTC tuomov + * trunk: changeset 842 + Added "deinit" hook. + +2003-07-30 14:21 UTC tuomov + * trunk: changeset 841 + Updated read_config_for_args call. + +2003-07-30 14:17 UTC tuomov + * trunk: changeset 840 + - Removed support for screen-specific configuration files. + + - Savefiles now go in ~/.ion-devel/sessionname (instead of ~/.ion- + devel/saves) where sessionname is 'default-session-displayname' by + default (with colon in display name converted to a dash) but can be + changed from the command line. + + - Workspaces are now saved in a single 'workspaces.lua' file in + session directory and screens are also now set with a single + 'initialise_screen_id' call. + +2003-07-30 14:14 UTC tuomov + * trunk: changeset 839 + Removed CF_SECOND_RATE_OS_FS comment as the setting is no longer + necessary. + +2003-07-28 18:01 UTC tuomov + * trunk: changeset 838 + Added --mode=link to libtool parameters. + +2003-07-28 13:43 UTC tuomov + * trunk: changeset 837 + - Fixed a bug in the new split resizing algorithm that caused + bottom/right regions in a split to be misplaced if both bottom and + top or left and right border were moved of another region. + + - Added some comments documenting the resizing algorithm. + +2003-07-27 18:49 UTC tuomov + * trunk: changeset 836 + Added some libtool options to system.mk + +2003-07-27 18:42 UTC tuomov + * trunk: changeset 835 + Fixed a problem with the split resizing algorithm and keyboard resize + by almost completely rewriting the algorithm. + +2003-07-27 18:41 UTC tuomov + * trunk: changeset 834 + Removed a comment. + +2003-07-27 00:15 UTC tuomov + * trunk: changeset 833 + Added keys for manipulating tags and attaching tagged objects (Mod+T: + toggle tag, Mod+K T: clear tags, Mod+K A: attach tagged). + +2003-07-27 00:00 UTC tuomov + * trunk: changeset 832 + Fixed transient_mode = "current" in full-screen mode. + +2003-07-26 23:44 UTC tuomov + * trunk: changeset 831 + Added some documentation comments. + +2003-07-26 23:07 UTC tuomov + * trunk: changeset 830 + Transparency mode switching fixed. + +2003-07-26 22:58 UTC tuomov + * trunk: changeset 829 + Some cleanup. + +2003-07-26 22:37 UTC tuomov + * trunk: changeset 828 + Some colour group initialisation changes. + +2003-07-25 20:31 UTC tuomov + * trunk: changeset 827 + CVS barfed again and refused to remove these files. + +2003-07-25 20:24 UTC tuomov + * trunk: changeset 826 + Ion now supports drawing engines as loadable modules! + +2003-07-25 20:13 UTC tuomov + * trunk: changeset 825 + Colour scheme configuration files were converted to the new format. + +2003-07-25 19:34 UTC tuomov + * trunk: changeset 824 + WRootWin changed to be of type WWindow (instead of just WRegion). + +2003-07-23 13:43 UTC tuomov + * trunk: changeset 823 + Fixed tag pixmap and textbox spacings. + +2003-07-22 18:09 UTC tuomov + * trunk: changeset 822 + Drawing engine Lua library was added. + +2003-07-22 18:06 UTC tuomov + * trunk: changeset 821 + Drawing engine interface code was added although not yet used. + +2003-07-22 18:04 UTC tuomov + * trunk: changeset 820 + The prospective default drawing engine was added. + +2003-07-22 16:57 UTC tuomov + * trunk: changeset 819 + Added a script (etc/lookconv.lua) to convert the old .lua colour + schemes to the upcoming format. + +2003-07-21 05:25 UTC tuomov + * trunk: changeset 818 + Added a note on *BSD libtool version brain-damagedness. + +2003-07-19 10:50 UTC tuomov + * trunk: changeset 817 + submap had been broken by previous simplifications. + +2003-07-19 10:50 UTC tuomov + * trunk: changeset 816 + Some va_list passing changes. + +2003-07-18 15:30 UTC tuomov + * trunk: changeset 815 + The fact that there is a configuration manual was made much better + visible in the README. + +2003-07-16 14:16 UTC tuomov + * trunk: changeset 814 + Nested workspace handling had been broken by the add managed/attach + interface change. Fixed. + +2003-07-13 21:14 UTC tuomov + * trunk: changeset 813 + The function 'exec_on_wm_display' was renamed 'exec'. + +2003-07-13 21:10 UTC tuomov + * trunk: changeset 812 + Lua function binding for classes put into class-tables thus making the + bindings more object-oriented in spirit. + +2003-07-10 22:56 UTC tuomov + * trunk: changeset 811 + AnyModifier handling fixes; Xlib was crashing when lock ignore kludge + was applied on AnyModifier grabs. + +2003-07-08 18:48 UTC tuomov + * trunk: changeset 810 + Some documentation updates. + +2003-07-08 18:22 UTC tuomov + * trunk: changeset 809 + removed deprecated definitions + +2003-07-08 18:08 UTC tuomov + * trunk: changeset 808 + The 'have region A manage region B' interface was heavily revamped. + The generic region_manage(_new) functions are gone and only WMPlexes + now export the equivalent interfaces mplex_attach(_new). Only client + windows' are now set up with a generic interface that is a lot simpler + than the old. + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20030628 + +2003-07-04 14:04 UTC tuomov + * trunk: changeset 807 + Set default_ws_type in pwm-ioncore-example.lua. + +2003-06-28 17:40 UTC tuomov + * trunk: changeset 806 + Fixed a crash when the same key was bound as both submap and normal + action on an object. + +2003-06-27 21:16 UTC tuomov + * trunk: changeset 805 + Added more obsolete functions. + +2003-06-27 21:15 UTC tuomov + * trunk: changeset 804 + Fixed some comments. + +2003-06-27 21:15 UTC tuomov + * trunk: changeset 803 + Implemented region_close on WFloatWS:s and renamed floatws_destroy to + floatws_relocate_and_close to be consistent with the naming of similar + functions on WIonFrames. + +2003-06-27 21:15 UTC tuomov + * trunk: changeset 802 + Fixed documentation in querylib. + +2003-06-27 18:55 UTC tuomov + * trunk: changeset 801 + The CF_LT_DL_ANCIENT option was removed as much more extra code would + have been needed to support ancient versions of libtool. Version 1.4.3 + or newer is now required. + +2003-06-27 18:47 UTC tuomov + * trunk: changeset 800 + The '-or' flag to find apparently was a GNU extension; '-o' seems to + work. + +2003-06-27 18:40 UTC tuomov + * trunk: changeset 799 + - The Mod1+F1 Ion man page display binding was broken. + + - The function exec_in_frame was renamed exec_in. + +2003-06-27 14:05 UTC tuomov + * trunk: changeset 798 + Manual page fixed. + +2003-06-25 23:37 UTC tuomov + * trunk: changeset 797 + FloatWS:s don't warp to new frames. + +2003-06-25 23:14 UTC tuomov + * trunk: changeset 796 + XOR resize rubberand had been broken by previous changes. + +2003-06-25 13:37 UTC tuomov + * trunk: changeset 795 + The default bindings for the F-keys now use the modifier from + SECOND_MOD (defaults to the empty string i.e. no modifier). + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20030623 + +2003-06-25 05:36 UTC tuomov + * trunk: changeset 794 + Tabs' grab area extended to include frame's top border when the + frame's y coordinate is zero. + +2003-06-23 23:47 UTC tuomov + * trunk: changeset 793 + A minor fix + +2003-06-23 13:14 UTC tuomov + * trunk: changeset 792 + Resize display was showing incorrect values for keyboard resize. + +2003-06-23 13:13 UTC tuomov + * trunk: changeset 791 + A bug in grab handler calling code could crash Ion when leaving + keyboard resize mode manually. + +2003-06-23 01:20 UTC tuomov + * trunk: changeset 790 + Fixed pointer warping on screen change. + +2003-06-22 11:12 UTC tuomov + * trunk: changeset 789 + Removed false comments. + +2003-06-21 20:07 UTC tuomov + * trunk: changeset 788 + Screen lookup had been broken for windows that are not properly on any + screen. + +2003-06-21 20:06 UTC tuomov + * trunk: changeset 787 + As the number of dynamic functions has been getting bigger, the + functions are now sorted on first use and then binary-searched instead + of naive linear searching. + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20030620 + +2003-06-21 12:50 UTC tuomov + * trunk: changeset 786 + Return from full screen mode to floatws had been broken. + +2003-06-21 10:56 UTC tuomov + * trunk: changeset 785 + Client window last height request bookkeeping code had been lost when + configure request policy was changed. This caused transient sizes to + be calculated incorrectly. + +2003-06-20 09:19 UTC tuomov + * trunk: changeset 784 + Nothing! + +2003-06-20 09:19 UTC tuomov + * trunk: changeset 783 + Constrain frames to their workspaces. + +2003-06-20 08:52 UTC tuomov + * trunk: changeset 782 + Oops. + +2003-06-20 08:50 UTC tuomov + * trunk: changeset 781 + Some initial focus policy changes. + +2003-06-20 08:50 UTC tuomov + * trunk: changeset 780 + The split functions now return the newly created frame. + +2003-06-20 07:40 UTC tuomov + * trunk: changeset 779 + Added a comment. + +2003-06-19 22:06 UTC tuomov + * trunk: changeset 778 + Put new client windows in innermost/deepest nested active workspace, + if any, instead of limiting to those attached directly to screens. + +2003-06-19 22:05 UTC tuomov + * trunk: changeset 777 + Split recalculation on WS resize fixed and made proportional. + +2003-06-19 20:51 UTC tuomov + * trunk: changeset 776 + Changes to client window move request handling on WFloatWS:s; while + the current behaviour may not be correct, a greater number of apps' + requests should work almost as expected even in nested workspaces. + +2003-06-19 18:12 UTC tuomov + * trunk: changeset 775 + New windows weren't being placed on correct screen in Xinerama mode. + +2003-06-19 18:11 UTC tuomov + * trunk: changeset 774 + Removed unnecessary flags from create/init. + +2003-06-19 16:16 UTC tuomov + * trunk: changeset 773 + Tab-bar state wasn't being applied correctly from savefiles. + +2003-06-18 18:49 UTC tuomov + * trunk: changeset 772 + Some transient size management changes. + +2003-06-18 18:14 UTC tuomov + * trunk: changeset 771 + - Added min_size winprop. + + - Transients weren't properly unattached when the managing client + window died. This could cause segfault e.g. at exit. + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20030617 + +2003-06-18 13:31 UTC tuomov + * trunk: changeset 770 + Some title shortening rules were defined in wrong order in ioncore- + example.lua and the rules App: doc -> doc... and App: doc -> + doc... were missing. + +2003-06-18 12:12 UTC tuomov + * trunk: changeset 769 + Use libtool for make clean. + +2003-06-17 20:29 UTC tuomov + * trunk: changeset 768 + Added Galeon find dialog randomly missing transient_for hint + workaround winprop to kludges.lua. + +2003-06-17 20:27 UTC tuomov + * trunk: changeset 767 + Fixed the workaround. + +2003-06-17 20:09 UTC tuomov + * trunk: changeset 766 + The function close_sub_or_self was added. + +2003-06-17 20:09 UTC tuomov + * trunk: changeset 765 + The function region_get_active_leaf was removed and the export + region_active_sub added. + +2003-06-17 20:08 UTC tuomov + * trunk: changeset 764 + The Mod1+C binding had been broken by the removal if + make_active_leaf_fn: The function close_sub_or_self (not same as + make_active_leaf_fn(region_close)) was added and the key bound to this + function. + +2003-06-17 19:45 UTC tuomov + * trunk: changeset 763 + Added workaround to the XFree86 textprop bug that caused starting + Opera to crash Ion when UTF8 support was enabled. + +2003-06-17 19:32 UTC tuomov + * trunk: changeset 762 + Mention XFree86 version number in the bug comment. + +2003-06-17 19:28 UTC tuomov + * trunk: changeset 761 + Mention WinXP in cygwin comments. + +2003-06-17 16:57 UTC tuomov + * trunk: changeset 760 + Added commented-out options to system.mk for compiling Ion with the + Debian Lua package as the paths and file names differ greatly from the + official distribution. + +2003-06-17 15:56 UTC tuomov + * trunk: changeset 759 + Added a note to system.mk about the Xlib UTF8 bug. + +2003-06-17 09:16 UTC tuomov + * trunk: changeset 758 + More Cygwin notes. + +2003-06-17 08:26 UTC tuomov + * trunk: changeset 757 + Added some Cygwin installation notes to system.mk. + +2003-06-17 08:17 UTC tuomov + * trunk: changeset 756 + Scripts in share/ still weren't being built. + +2003-06-15 18:22 UTC tuomov + * trunk: changeset 755 + Winprop lookup was cluttering globals. + +2003-06-15 10:40 UTC tuomov + * trunk: changeset 754 + Some more stack trace cleanup. + +2003-06-15 10:24 UTC tuomov + * trunk: changeset 753 + Stack traces are ordered better when there are nested calls with + errors and calls to C functions for which no name is known are + compressed in the output. + +2003-06-15 09:09 UTC tuomov + * trunk: changeset 752 + make_active_leaf_fn in compat.lua was broken. + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20030614 + +2003-06-14 20:28 UTC tuomov + * trunk: changeset 751 + Some WMPlex/query focusing fixes. + +2003-06-14 20:12 UTC tuomov + * trunk: changeset 750 + The man page query completor also looks for symbolic links. + +2003-06-14 15:10 UTC tuomov + * trunk: changeset 749 + Added more documentation to *frame_begin_resize. + +2003-06-14 12:05 UTC tuomov + * trunk: changeset 748 + Focus was being incorrectly changed when an inactive full screen + client window was destroyed. + +2003-06-14 11:49 UTC tuomov + * trunk: changeset 747 + Some fullscreen focus kludges + +2003-06-13 23:01 UTC tuomov + * trunk: changeset 746 + Use luaL_(g|s)etn instead of looking upthe functions from the table + library. + +2003-06-13 19:36 UTC tuomov + * trunk: changeset 745 + - Some changes to grab and drag handler setup functions. + + - Escape key was harcoded to kill any active grab (so that + misconfigured resize modes and such can't do harm). + +2003-06-12 22:53 UTC tuomov + * trunk: changeset 744 + Fixed doc comment + +2003-06-12 18:48 UTC tuomov + * trunk: changeset 743 + Some generic resize code simplifications; timers moved to keyboard + resize mode code(s). + +2003-06-12 18:28 UTC tuomov + * trunk: changeset 742 + Resize timeout timer was being set up only after some resize action + had been performed, not when entering the mode. + +2003-06-12 18:14 UTC tuomov + * trunk: changeset 741 + More error-tolerant configuration reading setup: 1. If Lua fails to + load a configuration file (syntax error etc.), the next on path is + tried instead of failing. (If the configuration file dies in an error + during execution, the next one, however, is not attempted.) 2. The + main configuration file (ioncore.lua) is executed through ioncore- + startup.lua. This file redefines some of the binding setup functions + to monitor binding settings. If some of the binding groups have been + left empty by failing configuration files, minimal bindings are + created. + +2003-06-12 18:04 UTC tuomov + * trunk: changeset 740 + Some error display format changes. + +2003-06-12 18:04 UTC tuomov + * trunk: changeset 739 + Some error displays were broken. + +2003-06-12 18:03 UTC tuomov + * trunk: changeset 738 + The binding setup functions no return FALSE if no bindings were + defined at all. + +2003-06-12 18:01 UTC tuomov + * trunk: changeset 737 + Better commented configuration files. + +2003-06-12 15:41 UTC tuomov + * trunk: changeset 736 + Comments in system.mk were out of place + +2003-06-12 00:37 UTC tuomov + * trunk: changeset 735 + Error log should be somewhat easier to read now. + +2003-06-10 18:18 UTC tuomov + * trunk: changeset 734 + waitrelease fixed. + +2003-06-10 16:21 UTC tuomov + * trunk: changeset 733 + bindmaps.c and bindmaps.h were missing from previous commit. + +2003-06-10 16:17 UTC tuomov + * trunk: changeset 732 + - Binding configuration (hopefully) simplified: bindings previously + defined in common-frame-bindings.lua were moved to ioncore- + bindings.lua and functions for defining bindings common to all + WMPlexes and WGenFrames were added. + + - The confusing 'make_active_leaf_fn' was also removed (can still be + found in compat.lua) and instead client window bindings are defined + in mplex_bindings with the help of the perhaps a little less + confusing 'make_current_clientwin_fn'. + +2003-06-10 16:14 UTC tuomov + * trunk: changeset 731 + Binding configuration (hopefully) simplified: bindings previously + defined in common-frame-bindings.lua were moved to ioncore- + bindings.lua and functions for defining bindings common to all + WMPlexes and WGenFrames were added. + +2003-06-10 11:07 UTC tuomov + * trunk: changeset 730 + Manual page updates. + +2003-06-10 11:05 UTC tuomov + * trunk: changeset 729 + The functions extl_dofile/string' were removed and + extl_loadfile/string added. + +2003-06-09 22:24 UTC tuomov + * trunk: changeset 728 + Warping on workspace switch had been broken by addition of + multiplexes. + +2003-06-09 21:13 UTC tuomov + * trunk: changeset 727 + The Lua interfacing code now uses a unique (cached in a weak table) + WWatch for Ion's objects instead of creating a new userdata/watch + every time an object is passed to Lua. This allows using the objects + as indexes in tables. + +2003-06-09 20:28 UTC tuomov + * trunk: changeset 726 + Some client window initial focus policy unification. + +2003-06-09 16:14 UTC tuomov + * trunk: changeset 725 + Added some definitions in header + +2003-06-09 15:17 UTC tuomov + * trunk: changeset 724 + 'QueryLib.query_workspace' new creates workspaces of the type set in + the variable 'default_ws_type' if no type is otherwise specified and + Mod1+F9 was bound to create workspaces of this type without asking for + a name. (The default name of default_ws_type is used.) + +2003-06-09 15:14 UTC tuomov + * trunk: changeset 723 + WRegions (except WClientWins) are now given names of the form + 'ClassName' by default. + +2003-06-09 14:24 UTC tuomov + * trunk: changeset 722 + - Don't complain of disappeared windows at startup phase. + + - Some client window management setup cleanup. + +2003-06-08 19:06 UTC tuomov + * trunk: changeset 721 + Added 'warn' export. + +2003-06-08 18:22 UTC tuomov + * trunk: changeset 720 + Added -noxinerama command line option. + +2003-06-08 17:55 UTC tuomov + * trunk: changeset 719 + Some small changes. + +2003-06-08 17:38 UTC tuomov + * trunk: changeset 718 + Fixed some documentation comments. + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20030606 + +2003-06-08 17:28 UTC tuomov + * trunk: changeset 717 + Added mplex_managed_count, mplex_managed_index and mplex_current_index + functions. The latter two are in ioncore-mplexfns.lua that must + specifically be loaded if the functions are needed. + +2003-06-08 12:44 UTC tuomov + * trunk: changeset 716 + Workspace swithing while dragging tabs had been broken by the mplex + change. + +2003-06-06 13:23 UTC tuomov + * trunk: changeset 715 + Changed comments. + +2003-06-06 13:13 UTC tuomov + * trunk: changeset 714 + Added 'screen_set_managed_offset' function that statusbars and such + should use to allocate space. + +2003-06-06 12:42 UTC tuomov + * trunk: changeset 713 + Some cleanup. + +2003-06-05 23:19 UTC tuomov + * trunk: changeset 712 + Don't execute 'arg' setup code when there are no parameters. + +2003-06-05 06:10 UTC tuomov + * trunk: changeset 711 + Makefile was missing share/ from SUBDIRS: + +2003-06-04 16:12 UTC tuomov + * trunk: changeset 710 + WScreen and WGenFrame now have a common WMPlex base class. + +2003-06-02 22:30 UTC tuomov + * trunk: changeset 709 + Ion-ssh and ion-man scripts were updated to use $SHAREDIR/ion- + runinxterm. + +2003-06-02 19:52 UTC tuomov + * trunk: changeset 708 + CF_NO_XINERAMA had been broken at some point. + +2003-06-02 17:50 UTC tuomov + * trunk: changeset 707 + Added -DCF_LTLD_ANCIENT kludge so that it might be possible to use + some systems' ancient libltdl. + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20030602-1 + +2003-06-02 14:15 UTC tuomov + * trunk: changeset 706 + Remaining sprintf calls replaced with snprintf. + +2003-06-02 14:08 UTC tuomov + * trunk: changeset 705 + Most of module management code removed as libltdl can handle it. + +2003-06-02 10:34 UTC tuomov + * trunk: changeset 704 + Oops. + +2003-06-02 10:32 UTC tuomov + * trunk: changeset 703 + More ugly-fontixes + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20030602 + +2003-06-02 10:04 UTC tuomov + * trunk: changeset 702 + Changed $SHAREDIR before $ETCDIR on configuration file/script search + path because people weren't removing their old *lib.lua files. + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20030601 + +2003-06-02 09:27 UTC tuomov + * trunk: changeset 701 + Ugly-font support was broken. + +2003-06-02 05:40 UTC tuomov + * trunk: changeset 700 + Some libltdl search path setting changes. + +2003-06-01 13:21 UTC tuomov + * trunk: changeset 699 + Some minor clarifications and fixes. + +2003-06-01 12:15 UTC tuomov + * trunk: changeset 698 + More fixes. + +2003-06-01 12:11 UTC tuomov + * trunk: changeset 697 + Another extl_l1_finalize fix. + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20030531 + +2003-06-01 02:02 UTC tuomov + * trunk: changeset 696 + Fixed a memory leak. + +2003-06-01 01:44 UTC tuomov + * trunk: changeset 695 + An off-by-one error in extl_l1_finalize caused references to some Lua + tables (including large completions) never to be released. + +2003-05-31 15:33 UTC tuomov + * trunk: changeset 694 + Ion-runinxterm was not found. + +2003-05-31 15:04 UTC tuomov + * trunk: changeset 693 + License changed: LGPL. + +2003-05-31 14:49 UTC tuomov + * trunk: changeset 692 + The -libdir option aws changed to -moduledir. + +2003-05-31 14:48 UTC tuomov + * trunk: changeset 691 + Manual page updated. + +2003-05-31 13:20 UTC tuomov + * trunk: changeset 690 + Oops. + +2003-05-31 13:19 UTC tuomov + * trunk: changeset 689 + Added CF_ALWAYS_VIRTUAL_ROOT + +2003-05-31 13:11 UTC tuomov + * trunk: changeset 688 + Compatibility wrappers for old resize functions were added. + +2003-05-31 13:11 UTC tuomov + * trunk: changeset 687 + The functions *frame_do_resize were changed to receive four + parameters, one for each border/direction. + +2003-05-31 13:10 UTC tuomov + * trunk: changeset 686 + More changes in move/resize mode bindings to be more consistent and + predictable: Left/Right/Up/Down and F/B/P/N grow the frame in the + specific direction, Shift+keys shrink and in case of floating frames, + DEFAULT_MOD+keys move. + +2003-05-31 12:45 UTC tuomov + * trunk: changeset 685 + mkexports.pl => mkexports.lua + +2003-05-31 12:43 UTC tuomov + * trunk: changeset 684 + Transient reparent stack "fix". + +2003-05-31 12:35 UTC tuomov + * trunk: changeset 683 + Removed unused settings. + +2003-05-31 11:09 UTC tuomov + * trunk: changeset 682 + Some drawing fixes. + +2003-05-31 10:58 UTC tuomov + * trunk: changeset 681 + Some (part bad) name allocation fixes. + +2003-05-31 00:40 UTC tuomov + * trunk: changeset 680 + Some reordering of initilisation code. + +2003-05-30 21:17 UTC tuomov + * trunk: changeset 679 + Changes in installation directories: The ion-* shell scripts are + installed in $SHAREDIR and are ioncorelib.lua, querylib.lua and + compat.lua. 'ion-completefile' is installed in $EXTRABINDIR + (=$MODULEDIR) being a binary. + +2003-05-30 21:17 UTC tuomov + * trunk: changeset 678 + - Changes in installation directories: The ion-* shell scripts are + installed in $SHAREDIR and are ioncorelib.lua, querylib.lua and + compat.lua. 'ion-completefile' is installed in $EXTRABINDIR + (=$MODULEDIR) being a binary. + + - QueryLib functions search the script directories (~/.ion-devel/, + $ETCDIR, $SHAREDIR, $EXTRABINDIR; in that order) for the ion-* + helper programs instead of assuming them being on $PATH. + +2003-05-30 21:15 UTC tuomov + * trunk: changeset 677 + - The *DIR settings in system.mk are now more detailed. + + - Changes in installation directories: The ion-* shell scripts are + installed in $SHAREDIR and are ioncorelib.lua, querylib.lua and + compat.lua. 'ion-completefile' is installed in $EXTRABINDIR + (=$MODULEDIR) being a binary. + +2003-05-29 22:22 UTC tuomov + * trunk: changeset 676 + Removed note on C99 requirement as that is no longer true. + +2003-05-29 13:11 UTC tuomov + * trunk: changeset 675 + Removed deprecated definitions. + +2003-05-29 12:32 UTC tuomov + * trunk: changeset 674 + All object destroys should now be handled safely. + +2003-05-28 22:21 UTC tuomov + * trunk: changeset 673 + Name management wasn't updating the changes to frames. + +2003-05-28 21:37 UTC tuomov + * trunk: changeset 672 + Floatframe_handle_drop should now set geometry correctly in nested + workspaces. + +2003-05-28 18:48 UTC tuomov + * trunk: changeset 671 + Small global environment cluttering fix. + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20030528 + +2003-05-28 18:40 UTC tuomov + * trunk: changeset 670 + Small UTF8 fix. + +2003-05-28 16:31 UTC tuomov + * trunk: changeset 669 + Examples of query_man_path and query_ssh_hosts were added to the + default ioncore.lua main configuration file. + +2003-05-27 19:14 UTC tuomov + * trunk: changeset 668 + WClientWins now save the last height request of transients (and other + managed objects) so a decent size should always be restored when the + window is in a big enough frame. + +2003-05-27 17:17 UTC tuomov + * trunk: changeset 667 + Mkexports.lua document generation updated to understand + EXTL_EXPORT_AS. + +2003-05-27 17:07 UTC tuomov + * trunk: changeset 666 + A minor check added. + +2003-05-27 17:02 UTC tuomov + * trunk: changeset 665 + Added a comment. + +2003-05-27 16:59 UTC tuomov + * trunk: changeset 664 + Some clean-up and region_full_name reference removed. + +2003-05-27 16:59 UTC tuomov + * trunk: changeset 663 + complete/lookup_clientwin removed. + +2003-05-27 16:57 UTC tuomov + * trunk: changeset 662 + New name allocation code: client windows are now in a separate + namespace from other objects and "short names" without appended + instance number are gone. + +2003-05-27 16:55 UTC tuomov + * trunk: changeset 661 + Some tab bar drawing fixes. + +2003-05-27 16:47 UTC tuomov + * trunk: changeset 660 + Added functions to clear Lua table entries. + +2003-05-26 22:13 UTC tuomov + * trunk: changeset 659 + Some changes in system.mk Lua settings. + +2003-05-26 22:06 UTC tuomov + * trunk: changeset 658 + Changed DATA_MODE. + +2003-05-26 22:06 UTC tuomov + * trunk: changeset 657 + Some client window resize/move request handling changes. + +2003-05-25 11:07 UTC tuomov + * trunk: changeset 656 + Some clean-up. + +2003-05-25 11:07 UTC tuomov + * trunk: changeset 655 + Added the boolean 'fullscreen' winprop. + +2003-05-25 11:04 UTC tuomov + * trunk: changeset 654 + Floatws circulate bindings should not clutter global environment. + +2003-05-25 11:03 UTC tuomov + * trunk: changeset 653 + QueryLib should now remember last directory for file view and edit + queries. + +2003-05-24 20:37 UTC tuomov + * trunk: changeset 652 + Fixed extl_globals. + +2003-05-24 16:45 UTC tuomov + * trunk: changeset 651 + Minor bugs fixed. + +2003-05-24 15:06 UTC tuomov + * trunk: changeset 650 + Added query_message. + +2003-05-24 14:50 UTC tuomov + * trunk: changeset 649 + Some documentation was missing. + +2003-05-24 12:32 UTC tuomov + * trunk: changeset 648 + Client window rescue fixed. + +2003-05-24 11:25 UTC tuomov + * trunk: changeset 647 + Removed an unnecessary XClear. + +2003-05-23 23:57 UTC tuomov + * trunk: changeset 646 + More attempts at fixing take_focus. + +2003-05-23 23:09 UTC tuomov + * trunk: changeset 645 + Removed extra space in querylib prompts. + +2003-05-23 22:46 UTC tuomov + * trunk: changeset 644 + Fixed lookup_region. + +2003-05-23 22:35 UTC tuomov + * trunk: changeset 643 + Fixed obj_exists. + +2003-05-23 18:00 UTC tuomov + * trunk: changeset 642 + Removed unnuecessary (?) XClear(Area|Window) calls. + +2003-05-23 15:43 UTC tuomov + * trunk: changeset 641 + Removed rootwin->current_screen redundancy. + +2003-05-23 15:30 UTC tuomov + * trunk: changeset 640 + Some minor clean-up. + +2003-05-23 15:19 UTC tuomov + * trunk: changeset 639 + Xinerama screens now always have a virtual root window for better + separation. + +2003-05-23 15:14 UTC tuomov + * trunk: changeset 638 + Removed grab_released. + +2003-05-23 14:38 UTC tuomov + * trunk: changeset 637 + Grab handling code simplified. + +2003-05-23 14:38 UTC tuomov + * trunk: changeset 636 + Fixed UTF8 include stuff. + +2003-05-21 16:09 UTC tuomov + * trunk: changeset 635 + The exports region_set_w/h were replaced with region_request_geom. + +2003-05-21 16:08 UTC tuomov + * trunk: changeset 634 + Added EXTL_EXPORT_AS + +2003-05-21 16:07 UTC tuomov + * trunk: changeset 633 + The exported function specification generation script was extended + with EXTL_EXPORT_AS(...). + +2003-05-21 05:48 UTC tuomov + * trunk: changeset 632 + Use get_text_property in clientwin_get_ident. + +2003-05-20 18:37 UTC tuomov + * trunk: changeset 631 + Better (?) frame shading and maximizing code that should also + eventually work on tiled workspaces (only partially implemented; + better region_request_geom neeeded). + +2003-05-20 14:13 UTC tuomov + * trunk: changeset 630 + Oops. + +2003-05-20 13:29 UTC tuomov + * trunk: changeset 629 + Simpler implementation of 'goto_previous' using watches. + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20030520 + +2003-05-20 11:45 UTC tuomov + * trunk: changeset 628 + Don't add non-strings in a table to list of completions. + +2003-05-20 11:44 UTC tuomov + * trunk: changeset 627 + The exports generation script was ignoring constness of string and + could therefore cause Ion to crash or corrupt strings. + +2003-05-19 22:46 UTC tuomov + * trunk: changeset 626 + Removed #include + +2003-05-19 22:45 UTC tuomov + * trunk: changeset 625 + Some more mouse resize tuning. + +2003-05-19 16:51 UTC tuomov + * trunk: changeset 624 + Some clean-up and optimisation. + +2003-05-19 15:34 UTC tuomov + * trunk: changeset 623 + Kludges and bloat to a proper (but not necessarily correct) X server + time in WM_TAKE_FOCUS messages to get around problems with some + programs. + +2003-05-19 14:32 UTC tuomov + * trunk: changeset 622 + Unnecessary function region_request_geom_constrain was removed. + +2003-05-19 14:32 UTC tuomov + * trunk: changeset 621 + Changes to accommodate for changes in lookup_region. + +2003-05-19 14:31 UTC tuomov + * trunk: changeset 620 + Unnecessary function wobj_cast was removed. + +2003-05-19 14:30 UTC tuomov + * trunk: changeset 619 + The functions lookup/complete_region now expect a string class + parameter (or null for "WRegion") and the functions + complete/lookup_workspace/clientwin were moved to ioncorelib.lua as + they are not needed on the C side. + +2003-05-18 13:41 UTC tuomov + * trunk: changeset 618 + The resize angle limits are now universal; borders and tabs are not a + special case. + +2003-05-18 13:35 UTC tuomov + * trunk: changeset 617 + Mouse resize fine-tuning: if the angle between the vector from the + centre of the frame to the point where the initial mouse press occured + and the unit vector pointing (from the centre) to a border of the + frame is less than 55 degrees, we allow resize to change that border. + +2003-05-18 13:03 UTC tuomov + * trunk: changeset 616 + Some more focusing policy changes/fixes. + +2003-05-18 13:01 UTC tuomov + * trunk: changeset 615 + Oops. + +2003-05-18 12:39 UTC tuomov + * trunk: changeset 614 + Some more cleanup. + +2003-05-18 12:31 UTC tuomov + * trunk: changeset 613 + Some cleanup. + +2003-05-18 12:31 UTC tuomov + * trunk: changeset 612 + Fix around problems with macros with some (possibly broken?) versions + of gcc. + +2003-05-17 13:53 UTC tuomov + * trunk: changeset 611 + Previous modifications had broken tabdrag workspace switch. + +2003-05-17 13:37 UTC tuomov + * trunk: changeset 610 + add_to_viewport should not have been renamed add_to_screen. + +2003-05-17 13:31 UTC tuomov + * trunk: changeset 609 + Fixed the place of a comma. + +2003-05-17 13:13 UTC tuomov + * trunk: changeset 608 + CVS fscked up screen.* + +2003-05-17 13:11 UTC tuomov + * trunk: changeset 607 + Removed viewport.c and viewport.h. + +2003-05-17 13:08 UTC tuomov + * trunk: changeset 606 + - Changes in object and function names to be closer to what users see + and think: what previously were screens (WScreen) are now called + root windows (WRootWin) and viewport (WViewport) have become screens + (WScreen). These changes are so big that no wrappers were added to + compat.lua. + + - Some old root window (old screen) functions were removed. + + - Bindings in global_bindings should get a WScreen (old WViewport) as + an argument instead of WRootWin. + + - It is possible to build Ion with the CF_WINDOWED_SCREENS flag so + that WScreens have "virtual" root windows to better separate + Xinerama screens. + +2003-05-17 13:09 UTC tuomov + * trunk: changeset 605 + - Changes in object and function names to be closer to what users see + and think: what previously were screens (WScreen) are now called + root windows (WRootWin) and viewport (WViewport) have become screens + (WScreen). These changes are so big that no wrappers were added to + compat.lua. + + - Some old root window (old screen) functions were removed. + + - Bindings in global_bindings should get a WScreen (old WViewport) as + an argument instead of WRootWin. + +2003-05-17 12:16 UTC tuomov + * trunk: changeset 604 + Default configuration uses XOR-rubberband move/resize (instead of + opaque) to be nicer on slower systems. + +2003-05-17 11:49 UTC tuomov + * trunk: changeset 603 + Fixed FloatWS initial focus. + +2003-05-16 19:53 UTC tuomov + * trunk: changeset 602 + Some clean-up. + +2003-05-16 18:48 UTC tuomov + * trunk: changeset 601 + Some changes in property getting functions. + +2003-05-16 18:02 UTC tuomov + * trunk: changeset 600 + Removed unnecessary references to screens. + +2003-05-16 16:08 UTC tuomov + * trunk: changeset 599 + region_list_to_table renamed to managed_list_to_table. + +2003-05-16 15:59 UTC tuomov + * trunk: changeset 598 + Signals are not trapped until the initialisation is at a point where + we have found some screens to manage. + +2003-05-16 15:56 UTC tuomov + * trunk: changeset 597 + Removed erroneous cast. + +2003-05-16 15:56 UTC tuomov + * trunk: changeset 596 + Added the extl_globals function for accessing globals. + +2003-05-16 15:49 UTC tuomov + * trunk: changeset 595 + Enchancements in client window "rescueing" and some unifications with + return from full screen mode. + +2003-05-16 15:48 UTC tuomov + * trunk: changeset 594 + Changed char* to const char* in ExtlL2Param. + +2003-05-16 15:42 UTC tuomov + * trunk: changeset 593 + Moved fullscreen stuff into a separate file. + +2003-05-15 23:05 UTC tuomov + * trunk: changeset 592 + Added a check for nil functions. + +2003-05-15 22:27 UTC tuomov + * trunk: changeset 591 + Modified mkexports.lua to generate a little less code. + +2003-05-15 22:26 UTC tuomov + * trunk: changeset 590 + Fixed extl_table_is_bool_set. + +2003-05-15 21:05 UTC tuomov + * trunk: changeset 589 + Restored old extl_cpcall interface. + +2003-05-15 15:45 UTC tuomov + * trunk: changeset 588 + Added extl_table_is_bool_set. + +2003-05-15 15:45 UTC tuomov + * trunk: changeset 587 + Some l_st usage cleanup in the Lua code and added extl_cpcallx. + +2003-05-15 06:01 UTC tuomov + * trunk: changeset 586 + Some minor changes in the Lua interface. + +2003-05-15 06:00 UTC tuomov + * trunk: changeset 585 + QueryLib.query_lib should properly return on error. + +2003-05-14 21:32 UTC tuomov + * trunk: changeset 584 + Fixed a problem with bsearch() and Solaris. + +2003-05-14 20:13 UTC tuomov + * trunk: changeset 583 + Client window management setup code simplified by attaching transients + the their transient_for by default and having floatws:s override this + behaviour by hooking to add_clientwin_alt. + +2003-05-14 19:59 UTC tuomov + * trunk: changeset 582 + Changes in how parameters are passed to Lua code loaded as string or + from a file. + +2003-05-14 16:49 UTC tuomov + * trunk: changeset 581 + Return from full screen mode should work with floatws:s now. + +2003-05-14 16:40 UTC tuomov + * trunk: changeset 580 + Added a warning for _NET_WM broken transient_for hint. + +2003-05-14 16:38 UTC tuomov + * trunk: changeset 579 + 'ionws_do_clientwin' now calls the Lua function + 'ionws_placement_method' with parameters (ws, cwin, pos_given_by_user) + to determine in which frame to place a window. This can be used to + e.g. experiment with placement heuristics. + +2003-05-14 14:21 UTC tuomov + * trunk: changeset 578 + Mouse resize changed to only resize along one coordinate axis when the + window is grabbed far enough from borders. + +2003-05-13 19:20 UTC tuomov + * trunk: changeset 577 + Renamed geom.* extlconv.* + +2003-05-13 18:07 UTC tuomov + * trunk: changeset 576 + The 'include' function didn't handle absolute paths. + +2003-05-13 18:06 UTC tuomov + * trunk: changeset 575 + There was a problem getting return values from Lua functions. + +2003-05-13 16:09 UTC tuomov + * trunk: changeset 574 + Added parameter count check and complaint when va_copy is not + available. + +2003-05-13 16:05 UTC tuomov + * trunk: changeset 573 + The new Lua calling code allowed removing dependency on C99 va_copy a + little more easily than the old so Ion no longer depends on it and + should be easier to compile on older systems (apparently including gcc + 2.9x.x). + +2003-05-13 14:46 UTC tuomov + * trunk: changeset 572 + Transient resizing when the managing WClientWin was resized had been + broken at some point. + +2003-05-13 05:40 UTC tuomov + * trunk: changeset 571 + Removed redundant comment. + +2003-05-13 05:37 UTC tuomov + * trunk: changeset 570 + Vertical keyboard resize binding swapped. + +2003-05-13 05:38 UTC tuomov + * trunk: changeset 569 + Keyboard resize should not "cumulate" size increments that do not + affect the frame size. + +2003-05-12 23:17 UTC tuomov + * trunk: changeset 568 + Fixed a minor resize glitch. + +2003-05-12 19:57 UTC tuomov + * trunk: changeset 567 + fixed a typo + +2003-05-12 17:02 UTC tuomov + * trunk: changeset 566 + ionws_split_of synopsis changed. + +2003-05-12 16:59 UTC tuomov + * trunk: changeset 565 + Enhanced comments. + +2003-05-12 16:38 UTC tuomov + * trunk: changeset 564 + Region name instances are saved in the workspace savefiles. (However, + client windows do not use the saved title because it may have changed + so client windows' instance numbers may change over restarts.) + +2003-05-12 16:31 UTC tuomov + * trunk: changeset 563 + QueryLib goto still had a debug complaint in it. + +2003-05-12 16:23 UTC tuomov + * trunk: changeset 562 + Save client window id as %lu instead of %.32f. + +2003-05-12 16:20 UTC tuomov + * trunk: changeset 561 + The wrapper function generated by "obsolete" didn't return any values. + +2003-05-12 16:18 UTC tuomov + * trunk: changeset 560 + Do not add IMPLOBJ(WObj) in exports.c + +2003-05-12 16:18 UTC tuomov + * trunk: changeset 559 + The exports region_get_(x|y|w|h) were replaced with region_geom. + +2003-05-12 16:17 UTC tuomov + * trunk: changeset 558 + Added new exports to get information on splits on WIonWs:s. This + should help writing alternative navigation functions. + +2003-05-12 16:18 UTC tuomov + * trunk: changeset 557 + Added wrappers to obsolete functions region_get_(x|y|w|h). + +2003-05-11 16:17 UTC tuomov + * trunk: changeset 556 + Client window check codes are not reset so they're not lost so easily. + +2003-05-11 14:24 UTC tuomov + * trunk: changeset 555 + Cleaned up. + +2003-05-11 13:33 UTC tuomov + * trunk: changeset 554 + Added checks in region_add_managed to prevent from attachinging + parent's or manager's to their (grand)children or managed regions. + +2003-05-11 00:05 UTC tuomov + * trunk: changeset 553 + Added a missing newline. + +2003-05-10 23:43 UTC tuomov + * trunk: changeset 552 + Another broken documentation comment found. + +2003-05-10 23:42 UTC tuomov + * trunk: changeset 551 + 'ionws_load' no longer requires 'split_tree' to be specified so that + new workspaces can be created with region_manage_new without + specifying the contents. + +2003-05-10 23:41 UTC tuomov + * trunk: changeset 550 + The rest of the queries (goto/create workspace, attach client) are now + finally implemented in Lua as the function region_manage(_new) are + available. + +2003-05-10 23:15 UTC tuomov + * trunk: changeset 549 + Documentation was missing for exports in screen.c. + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20030510 + +2003-05-10 20:34 UTC tuomov + * trunk: changeset 548 + Removed efence reference from system.mk. + +2003-05-10 19:56 UTC tuomov + * trunk: changeset 547 + Oops.. region_manage documentation had an extra space that caused it + not to be parsed. + +2003-05-10 02:16 UTC tuomov + * trunk: changeset 546 + Some changes in comments. + +2003-05-10 02:12 UTC tuomov + * trunk: changeset 545 + Fixed a bug in check_input_fds. + +2003-05-10 02:11 UTC tuomov + * trunk: changeset 544 + Wiser loading of querylib. + +2003-05-10 02:13 UTC tuomov + * trunk: changeset 543 + The Lua interface code now uses lua_cpcall extensively to make it more + tolerant to Lua's longjmp error handling. + +2003-05-09 22:21 UTC tuomov + * trunk: changeset 542 + 'extl_dofile' and 'extl_dostring' now pass arguments in the local + instead of global variable 'arg'. + +2003-05-09 22:19 UTC tuomov + * trunk: changeset 541 + Setfenv is not necessary. Fixed error reporting. + +2003-05-09 21:43 UTC tuomov + * trunk: changeset 540 + Viewport names are now saved and other changes in savefile format. Old + 'add_to_viewport' function was kept for compatibility but will be + removed eventually. + +2003-05-09 17:39 UTC tuomov + * trunk: changeset 539 + Bleah. + +2003-05-09 17:34 UTC tuomov + * trunk: changeset 538 + Some more README changes. + +2003-05-09 17:32 UTC tuomov + * trunk: changeset 537 + Fixed transient size/position problems and split the + REGION_ATTACH_GEOMRQ flag into separate POSRQ and SIZERQ. + +2003-05-09 16:28 UTC tuomov + * trunk: changeset 536 + Updated the README. + +2003-05-09 14:57 UTC tuomov + * trunk: changeset 535 + Fixed a tabularx + +2003-05-09 14:36 UTC tuomov + * trunk: changeset 534 + Added the exports 'region_manage' and 'region_manage_new'. + +2003-05-09 14:35 UTC tuomov + * trunk: changeset 533 + Fixed a bug in extl_table_get that caused it to succeed for NULL + WObjs. + +2003-05-09 13:33 UTC tuomov + * trunk: changeset 532 + Query module listings could hang Ion if there was not enough space for + a single visible row. + +2003-05-08 18:38 UTC tuomov + * trunk: changeset 531 + Some bindings were still using Mod1 instead of DEFAULT_MOD. + +2003-05-08 18:36 UTC tuomov + * trunk: changeset 530 + Some minor fixes in bindings + +2003-05-08 06:05 UTC tuomov + * trunk: changeset 529 + Fixed a va_arg problem with luaextl.c and strange architechtures. + +2003-05-08 06:05 UTC tuomov + * trunk: changeset 528 + Oops. + +2003-05-08 05:25 UTC tuomov + * trunk: changeset 527 + Updated documentation comments. + +2003-05-07 18:49 UTC tuomov + * trunk: changeset 526 + Fixed a typo. + +2003-05-07 18:42 UTC tuomov + * trunk: changeset 525 + The Lua parser in mkexports.lua now parses for function arguments. + +2003-05-07 18:42 UTC tuomov + * trunk: changeset 524 + Removed make_screen_switch_nth_fn. + +2003-05-07 18:41 UTC tuomov + * trunk: changeset 523 + Added documentation to ioncorelib.lua. + +2003-05-07 18:41 UTC tuomov + * trunk: changeset 522 + Removed Makefile; function reference .texes are now generated in the + ion-doc package. + +2003-05-07 18:36 UTC tuomov + * trunk: changeset 521 + Changes in function documentation Makefile. + +2003-05-07 18:21 UTC tuomov + * trunk: changeset 520 + 'mkexports.lua' can now parse documentation from Lua code. + +2003-05-07 18:20 UTC tuomov + * trunk: changeset 519 + Added documentation to querylib.lua. + +2003-05-07 16:08 UTC tuomov + * trunk: changeset 518 + The functions 'ionws_split', 'ionws_split_empty' and 'ionws_split_top' + were renamed to the more consistent 'ionframe_split', + 'ionframe_split_empty' and 'ionws_newframe'. As usual, 'compat.lua' + has wrappers to the old functions. + +2003-05-07 15:57 UTC tuomov + * trunk: changeset 517 + - The completion handler for QueryLib.query_lua can now descend into + tables and complete subexpressions. + + - QueryLib.query_lua sets the variable '_' in the local environment of + the string to be called to point to the frame the query was opened + in. The variable 'arg' is also now set in the local environment + instead of global. + +2003-05-07 14:54 UTC tuomov + * trunk: changeset 516 + Client window size hints were not used when Ion was restarted. Fixed + that. + +2003-05-07 14:42 UTC tuomov + * trunk: changeset 515 + 'viewport_display_managed' was calling just 'set_focus' instead of + 'warp' as it should. + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20030506 + +2003-05-07 00:14 UTC tuomov + * trunk: changeset 514 + FIND_PARENT1 renamed GET_PARENT_CHK. FIND_PARENT removed. + +2003-05-06 18:28 UTC tuomov + * trunk: changeset 513 + Fixed a memory allocation problem discovered by valgrind. + +2003-05-06 17:02 UTC tuomov + * trunk: changeset 512 + x => x!=NULL + +2003-05-06 17:02 UTC tuomov + * trunk: changeset 511 + Added some \hlines in tables + +2003-05-06 05:20 UTC tuomov + * trunk: changeset 510 + Fixed a bug in the title shortening routine. + +2003-05-06 05:20 UTC tuomov + * trunk: changeset 509 + Fixed a bug in the new transient management setup code. + +2003-05-05 22:58 UTC tuomov + * trunk: changeset 508 + A small change in documentation comment. + +2003-05-05 22:58 UTC tuomov + * trunk: changeset 507 + Changes in the Makefile to build the exports.texes. + +2003-05-05 22:50 UTC tuomov + * trunk: changeset 506 + Line editor history is now saved when Ion exits. + +2003-05-05 17:40 UTC tuomov + * trunk: changeset 505 + Fixed extl_stack_get 'double' code. + +2003-05-05 17:37 UTC tuomov + * trunk: changeset 504 + - Removed target_id code. + + - Client windows are now saved over restarts in saves/workspaces-*.lua + instead of using target_ids. A special check code property is added + to each window so that we don't incorrectly reparent windows when + initially starting Ion. + +2003-05-04 16:01 UTC tuomov + * trunk: changeset 503 + Added hyperlinks in the exported function documentation. + +2003-05-04 02:18 UTC tuomov + * trunk: changeset 502 + Minor fix. + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20030504 + +2003-05-04 02:18 UTC tuomov + * trunk: changeset 501 + Sort functions in documentation alphabetically. + +2003-05-04 00:59 UTC tuomov + * trunk: changeset 500 + minor fixes + +2003-05-03 22:34 UTC tuomov + * trunk: changeset 499 + fix + +2003-05-03 22:30 UTC tuomov + * trunk: changeset 498 + typo fixed + +2003-05-03 22:30 UTC tuomov + * trunk: changeset 497 + typo fixed + +2003-05-03 22:04 UTC tuomov + * trunk: changeset 496 + doc/Makefile added and list_exports.sh removed + +2003-05-03 22:04 UTC tuomov + * trunk: changeset 495 + Documentation was added to the source for exported functions. + +2003-05-03 22:03 UTC tuomov + * trunk: changeset 494 + 'mkexports.lua' now parses for documentation of the form /*EXTL_DOC + ... */. + +2003-05-03 16:21 UTC tuomov + * trunk: changeset 493 + The 'mkexports' script was rewritten in Lua (was an unmaintainable + vomit of Perl). + +2003-05-02 19:41 UTC tuomov + * trunk: changeset 492 + Made 'obsolete' local + +2003-05-02 19:39 UTC tuomov + * trunk: changeset 491 + Added some comments + +2003-05-02 19:18 UTC tuomov + * trunk: changeset 490 + WFloatFrames can now be resized from the keyboard. + +2003-05-02 19:17 UTC tuomov + * trunk: changeset 489 + Simplified resize interface to have just ionframe_do_resize + +2003-05-02 18:52 UTC tuomov + * trunk: changeset 488 + IonFrame keyboard resize mode changed to allow resizing in both + directions without leaving and re-entering resize mode. Compatibility + functions for the old mode are provided in compat.lua. + +2003-05-02 18:52 UTC tuomov + * trunk: changeset 487 + - IonFrame keyboard resize mode changed to allow resizing in both + directions without leaving and re-entering resize mode. + Compatibility functions for the old mode are provided in compat.lua. + + - The compatibility functions in compat.lua now complain of + obsoleteness to stderr. + +2003-05-02 17:32 UTC tuomov + * trunk: changeset 486 + Added some comments + +2003-05-02 17:27 UTC tuomov + * trunk: changeset 485 + Added the Lua-side hooks genframe_managed_switched and + viewport_workspace_switched. + +2003-05-02 17:27 UTC tuomov + * trunk: changeset 484 + Added support for hooks Lua code can hook on to with add_to_hook(hook, + fn). + +2003-05-02 16:18 UTC tuomov + * trunk: changeset 483 + Fixed (floatws) focus problem that reoccured after previous attempt at + fixing other focus problems. + +2003-05-01 14:23 UTC tuomov + * trunk: changeset 482 + Added 'eq' metamethod for WObj:s. + +2003-05-01 14:12 UTC tuomov + * trunk: changeset 481 + Added new exports that should e.g. enable writing workspace navigation + functions that can also be used to move between viewports or other + workspaces instead of just wrapping around. + +2003-05-01 13:14 UTC tuomov + * trunk: changeset 480 + Some unifications in add_clientwin/region_add_managed interface. + +2003-04-28 22:42 UTC tuomov + * trunk: changeset 479 + Fixed CURRENT_FILE maintenance in include(). + +2003-04-28 16:47 UTC tuomov + * trunk: changeset 478 + minor fix + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20030428 + +2003-04-28 15:12 UTC tuomov + * trunk: changeset 477 + The innermost window grabbing on a mouse button should now get to + handle the event as is the case with key grabs. + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20030427-2 + +2003-04-28 14:53 UTC tuomov + * trunk: changeset 476 + Fixed WIonFrame subregion load problem + +2003-04-28 05:21 UTC tuomov + * trunk: changeset 475 + Fixed the region destroy focus fix. + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20030427-1 + +2003-04-27 21:29 UTC tuomov + * trunk: changeset 474 + QueryLib.query_exec fixed to use the correct handler. + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20030427 + +2003-04-27 21:26 UTC tuomov + * trunk: changeset 473 + Fixed a warning + +2003-04-27 17:44 UTC tuomov + * trunk: changeset 472 + ion-completefile Makefile fixed. + +2003-04-27 11:54 UTC tuomov + * trunk: changeset 471 + minor fix + +2003-04-27 01:15 UTC tuomov + * trunk: changeset 470 + doh! + +2003-04-27 01:10 UTC tuomov + * trunk: changeset 469 + QueryLib file and man page completors use 'popen_bgread' so the + queries can not block the WM from processing other events (or even + hang it). + +2003-04-27 01:09 UTC tuomov + * trunk: changeset 468 + Moved file completetion code from the query module into a separate + external program ('ion-completefile'). + +2003-04-27 01:07 UTC tuomov + * trunk: changeset 467 + Added 'popen_bgread(cmd, lua_fn)' to open read mode pipes that are + selected() in the main event loop and the given function called with + received data. + +2003-04-25 17:32 UTC tuomov + * trunk: changeset 466 + Most Ion functions should be null-string safe now except for some low- + level functions and functions that also receive string length as an + argument. This allows Lua scripts to pass nil to functions that have + special meaning for NULL strings. + +2003-04-25 17:11 UTC tuomov + * trunk: changeset 465 + Fixed do_complete_region. + +2003-04-25 16:59 UTC tuomov + * trunk: changeset 464 + Some changes on how region close/destroy is handled and how focus is + handled when an active region with non-window manager is destroyed. + +2003-04-25 16:46 UTC tuomov + * trunk: changeset 463 + Fixed extl_verify_wobj. + +2003-04-24 17:27 UTC tuomov + * trunk: changeset 462 + Some minor TODOs completed and some minor fixes. + +2003-04-23 22:16 UTC tuomov + * trunk: changeset 461 + Removed string free in extl_stack_push + +2003-04-23 16:17 UTC tuomov + * trunk: changeset 460 + foo + +2003-04-23 16:16 UTC tuomov + * trunk: changeset 459 + The floatws module is now aware of window gravities. + +2003-04-22 19:08 UTC tuomov + * trunk: changeset 458 + Some comments added + +2003-04-22 19:05 UTC tuomov + * trunk: changeset 457 + - Added window stacking management code. + + - Fixed region_notify_subregions_move. + +2003-04-22 19:05 UTC tuomov + * trunk: changeset 456 + Added window stacking management code. + +2003-04-22 19:05 UTC tuomov + * trunk: changeset 455 + The (exported) functions floatframe_raise/lower are obsolete and + replaced with region_raise/lower. The file 'compat.lua' can be + included to define these functions. + +2003-04-20 17:45 UTC tuomov + * trunk: changeset 454 + No longer complain of missing workspace savefiles. + +2003-04-19 20:11 UTC tuomov + * trunk: changeset 453 + minor fixes + +2003-04-19 20:10 UTC tuomov + * trunk: changeset 452 + Makefile also changed to note change of name in ioncorelib.lua + +2003-04-19 20:09 UTC tuomov + * trunk: changeset 451 + 'ioncore-lib.lua' renamed 'ioncorelib.lua' to be consistent with + 'querylib.lua'. + +2003-04-19 19:36 UTC tuomov + * trunk: changeset 450 + removed debug messages + +2003-04-19 19:35 UTC tuomov + * trunk: changeset 449 + Extl_init enables Lua loadlib. + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20030416 + +2003-04-19 19:34 UTC tuomov + * trunk: changeset 448 + Winprop management is now implemented in Lua. + +2003-04-16 21:44 UTC tuomov + * trunk: changeset 447 + QueryLib.mancache fixed to contain weak references. + +2003-04-16 17:16 UTC tuomov + * trunk: changeset 446 + Include correct version of libtu. Old version could cause crashes. + +2003-04-16 17:14 UTC tuomov + * trunk: changeset 445 + misc signal.c changes + +2003-04-16 17:13 UTC tuomov + * trunk: changeset 444 + WFloatWS placement code should now handle shaded frames correctly. + +2003-04-16 17:11 UTC tuomov + * trunk: changeset 443 + Added man-page completion to QueryLib. + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20030412-3 + +2003-04-16 17:09 UTC tuomov + * trunk: changeset 442 + Don't waitpid() in the SIGCHLD handler but in the main loop after this + handler has been called. For some reason Lua's io.popen() didn't like + the old behaviour. + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20030412-2 + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20030412-1 + +2003-04-13 19:07 UTC tuomov + * trunk: changeset 441 + FloatWS module honours window positions when starting up the WM. + +2003-04-12 20:08 UTC tuomov + * trunk: changeset 440 + - Some transient handling fixes. + + - region_do_add_managed wasn't passing enough parameters which could + cause a crash. + +2003-04-12 19:27 UTC tuomov + * trunk: changeset 439 + Old upvalue syntax removed from Lua code as the just-released Lua 5.0 + does not support it anymore by default. + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20030412 + +2003-04-12 19:06 UTC tuomov + * trunk: changeset 438 + Added the flag -std=c99 to options to compile luaextl.c as it needs + va_copy from C99 and some versions of GCC seem to disable this macro + otherwise. + +2003-04-12 18:44 UTC tuomov + * trunk: changeset 437 + Include stdarg.h in luaextl.c + +2003-04-12 18:19 UTC tuomov + * trunk: changeset 436 + minor changes + +2003-04-12 17:27 UTC tuomov + * trunk: changeset 435 + Yet another attempt at perfecting focus handling (before resorting to + separate displayed and actual activity states and update delays or + similar another kludge in counteracting X's lame key grab focus + policy). + +2003-04-12 16:44 UTC tuomov + * trunk: changeset 434 + Added CF_XMESSAGE + +2003-04-12 16:44 UTC tuomov + * trunk: changeset 433 + minor changes + +2003-04-12 16:43 UTC tuomov + * trunk: changeset 432 + flags + +2003-04-12 16:43 UTC tuomov + * trunk: changeset 431 + Prefer _NET_WM_NAME, if it exists, over WM_NAME as apps no longer seem + to use WM_NAME for UTF-8 titles. + +2003-04-12 16:10 UTC tuomov + * trunk: changeset 430 + QueryLib.query_lua displays all error messages. + +2003-04-12 16:08 UTC tuomov + * trunk: changeset 429 + The standard modules no longer fail on partially broken configuration + files unless no bindings have been configured before the error + occured. + +2003-04-12 16:07 UTC tuomov + * trunk: changeset 428 + misc + +2003-04-12 16:06 UTC tuomov + * trunk: changeset 427 + Full error log is displayed with xmessage after startup whether it is + possible to continue or not. + +2003-04-12 16:05 UTC tuomov + * trunk: changeset 426 + Added collect_errors(fn, params) for Lua code to be able to e.g. + display encountered errors with query_fwarn. + +2003-04-11 19:19 UTC tuomov + * trunk: changeset 425 + key changes + +2003-04-11 18:48 UTC tuomov + * trunk: changeset 424 + Added some XFree(prop.value):s + +2003-04-11 18:38 UTC tuomov + * trunk: changeset 423 + Inconsistently named 'goto_viewport_id' renamed to goto_nth_viewport. + +2003-04-11 18:37 UTC tuomov + * trunk: changeset 422 + misc changes. + +2003-04-11 18:36 UTC tuomov + * trunk: changeset 421 + The line editor's copy-paste features should now at least attempt to + support UTF8. + +2003-04-11 17:02 UTC tuomov + * trunk: changeset 420 + Added placement calculation code to the floatws module. Placement + method can be configured with + 'set_floatws_placement_method("method")'. Available methods are udlr, + lrud and random. (Maybe placement methods should be implemented in + Lua?) + +2003-04-11 16:06 UTC tuomov + * trunk: changeset 419 + Client windows are unmapped when frames are unmapped. This was an + overlooked (but lame and redundant) requirement of the ICCCM and + fullfilling it might fix some apps. (Ion probably still is far from + ICCCM-compliant, but so are most of the badly behaving apps.) + +2003-04-11 15:24 UTC tuomov + * trunk: changeset 418 + If UTF8 support is enabled, Ion will attempt to load CF_FALLBACK_FONT + ("fixed" by default) at startup. If this fails (or XSupporsLocale() + fails, which it seldom seems to do), it will reset locale back to + "POSIX" so that there's a better chance that some fonts can be loaded + although non-ASCII (7-bit) characters will be crippled. (If UTF8 + support is disabled, 8-bit character sets should usually work.) + +2003-04-11 14:08 UTC tuomov + * trunk: changeset 417 + Double-click shades + +2003-04-11 14:07 UTC tuomov + * trunk: changeset 416 + WFloatFrames can now be shaded. + +2003-04-11 05:32 UTC tuomov + * trunk: changeset 415 + minor fixes + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20030410 + +2003-04-11 05:19 UTC tuomov + * trunk: changeset 414 + complete_function fixed. + +2003-04-10 23:28 UTC tuomov + * trunk: changeset 413 + Transient_mode winprop fixed. + +2003-04-10 18:31 UTC tuomov + * trunk: changeset 412 + Removed some upvalue-marks + +2003-04-10 18:26 UTC tuomov + * trunk: changeset 411 + Remaining "goto_*_name" functions were removed as Lua code will + probably mostly use "reg=lookup_*() ... region_goto(reg)" + +2003-04-10 18:08 UTC tuomov + * trunk: changeset 410 + QueryLib.query_yesno fixed. + +2003-04-10 16:34 UTC tuomov + * trunk: changeset 409 + moved obj_exists to ioncore-lib.lua + +2003-04-10 16:33 UTC tuomov + * trunk: changeset 408 + Removed it + +2003-04-10 16:32 UTC tuomov + * trunk: changeset 407 + Added 'obj_exists' export for checking whether an object referenced in + Lua still exists in Ion. + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20030409 + +2003-04-10 16:03 UTC tuomov + * trunk: changeset 406 + Updated web page address and my email + +2003-04-10 06:01 UTC tuomov + * trunk: changeset 405 + complete_function implemented in Lua. + +2003-04-09 22:51 UTC tuomov + * trunk: changeset 404 + - A lot of the query code was converted to Lua. + + - QueryLib.query_ssh query was added. This will tab-complete hosts + from the table "query_ssh_hosts" and run the script "ion-ssh" on the + entered host. + +2003-04-09 21:07 UTC tuomov + * trunk: changeset 403 + A number of bugs in the Lua interface were fixed. + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20030408 + +2003-04-09 19:04 UTC tuomov + * trunk: changeset 402 + Some fixes + +2003-04-09 18:46 UTC tuomov + * trunk: changeset 401 + Added functions to add entries into Lua tables (for completion + handlers). + +2003-04-08 17:49 UTC tuomov + * trunk: changeset 400 + Code to create ~/.ion-devel/saves/ if it didn't exist had been lost at + some point. + +2003-04-08 17:35 UTC tuomov + * trunk: changeset 399 + added luaextl to SUBDIRS + +2003-04-08 17:33 UTC tuomov + * trunk: changeset 398 + foo + +2003-04-08 17:25 UTC tuomov + * trunk: changeset 397 + Use libtool and libltdl for module support. + +2003-04-07 19:35 UTC tuomov + * trunk: changeset 396 + Added obj_is and obj_typename exports. + +2003-04-07 19:16 UTC tuomov + * trunk: changeset 395 + Multi-line capability added to WMessage. + +2003-04-07 19:16 UTC tuomov + * trunk: changeset 394 + Added quite useless stack trace displayed when C function called from + Lua calls warn(). + +2003-04-07 19:15 UTC tuomov + * trunk: changeset 393 + Added verbosity to conf-bindings error messages. + +2003-04-07 17:15 UTC tuomov + * trunk: changeset 392 + Function renames. Most functions that can be considered member + functions of some WObj are now rather consistently (although + unnaturally) named. + +2003-04-07 17:14 UTC tuomov + * trunk: changeset 391 + Updated configuration files + +2003-04-07 17:14 UTC tuomov + * trunk: changeset 390 + Removed out-of-date documentation + +2003-04-07 15:21 UTC tuomov + * trunk: changeset 389 + Focusing code: iteration n. + +2003-04-07 14:59 UTC tuomov + * trunk: changeset 388 + Defer mechanism supports multiple lists. + +2003-04-07 05:47 UTC tuomov + * trunk: changeset 387 + Added extl_dostring and better extl_dofile + +2003-04-07 05:36 UTC tuomov + * trunk: changeset 386 + Added some checks to conf-draw.c + +2003-04-07 05:25 UTC tuomov + * trunk: changeset 385 + Added wedln-wrappers.c + +2003-04-07 05:23 UTC tuomov + * trunk: changeset 384 + Added extl.h + +2003-04-07 00:05 UTC tuomov + * trunk: changeset 383 + Added luaextl Makefile + +2003-04-06 21:47 UTC tuomov + * trunk: changeset 382 + Added some stack checks + +2003-04-06 16:30 UTC tuomov + * trunk: changeset 381 + Implemented include() support for the lua files + +2003-04-06 15:20 UTC tuomov + * trunk: changeset 380 + Use Lua as extension language. + +2003-04-05 15:00 UTC tuomov + * trunk: changeset 379 + MODULE_CFLAGS fixed. + +2003-04-02 18:24 UTC tuomov + * trunk: changeset 378 + Some code cleanup. + +2003-03-30 17:20 UTC tuomov + * trunk: changeset 377 + Some minor cleanup. + +2003-03-30 14:29 UTC tuomov + * trunk: changeset 376 + More minor object model changes + +2003-03-30 14:16 UTC tuomov + * trunk: changeset 375 + Some simplifications to the object model: WThing removed and + functionality split between WObj (watches) and WRegion (child<->parent + linking). + +2003-03-28 20:48 UTC tuomov + * trunk: changeset 374 + The region_add_managed mechanism was simplified and generalised. + +2003-03-28 16:01 UTC tuomov + * trunk: changeset 373 + Possible key binding setup bug fixed. + +2003-03-28 16:01 UTC tuomov + * trunk: changeset 372 + Modules are removed by 'make realclean'. + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20030327 + +2003-03-28 16:00 UTC tuomov + * trunk: changeset 371 + foo + +2003-03-28 15:59 UTC tuomov + * trunk: changeset 370 + Scripts are build using ETCDIR and LIBDIR instead of just PREFIX. + +2003-03-27 19:40 UTC tuomov + * trunk: changeset 369 + Don't strip modules + +2003-03-27 19:32 UTC tuomov + * trunk: changeset 368 + fix + +2003-03-27 19:30 UTC tuomov + * trunk: changeset 367 + Simple ioncore startup check added. + +2003-03-27 19:26 UTC tuomov + * trunk: changeset 366 + More minor fixes + +2003-03-27 19:16 UTC tuomov + * trunk: changeset 365 + Minor fix in clientwin_deinit. + +2003-03-27 19:01 UTC tuomov + * trunk: changeset 364 + A minor nested WS fix. + +2003-03-27 18:12 UTC tuomov + * trunk: changeset 363 + - Renamed the main binary 'ioncore'. + + - Added 'ion' shell script to run 'ioncore' with correct configuration + and module file directory parameters. A 'pwm' script to run ioncore + in PWM mode was also added but this is not installed by 'make + install' at the moment. + +2003-03-22 20:22 UTC tuomov + * trunk: changeset 362 + Applied the toggle_tab patch. + +2003-03-20 21:01 UTC tuomov + * trunk: changeset 361 + - Frames save their saveable contents + + - EnterWindow event handling changed so that embedded workspaces work + as expected. + +2003-03-17 18:49 UTC tuomov + * trunk: changeset 360 + system.mk mods + +2003-03-17 18:29 UTC tuomov + * trunk: changeset 359 + Client window (esp. transient) resize request fixes. + +2003-03-17 18:28 UTC tuomov + * trunk: changeset 358 + Double-click fixed. + +2003-03-17 18:28 UTC tuomov + * trunk: changeset 357 + stuff moved to system.mk + +2003-03-17 18:27 UTC tuomov + * trunk: changeset 356 + Tabdrag detects attempts to drop on oneself + +2003-03-15 19:21 UTC tuomov + * trunk: changeset 355 + ASCII control characters (ch&0x7f<32) are now presented as escaped + octals in saved region name strings. + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20030311-2 + +2003-03-15 18:15 UTC tuomov + * trunk: changeset 354 + The functions region_add_bindmap* no longer have the grab argument but + instead REGION_BINDINGS_ARE_GRABBED flag is to be set. + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20030311 + +2003-03-15 18:15 UTC tuomov + * trunk: changeset 353 + - The functions region_add_bindmap* no longer have the grab argument + but instead REGION_BINDINGS_ARE_GRABBED flag is to be set. + + - CF_PLACEMENT_GEOM check added in find_suitable_viewport. + +2003-03-11 20:29 UTC tuomov + * trunk: changeset 352 + Fixed a stupid mistake in creating an initial workspace when there is + no workspaces.conf. + +2003-03-11 19:03 UTC tuomov + * trunk: changeset 351 + fix + +2003-03-11 18:57 UTC tuomov + * trunk: changeset 350 + cleanup + +2003-03-11 18:52 UTC tuomov + * trunk: changeset 349 + foobar + +2003-03-11 18:52 UTC tuomov + * trunk: changeset 348 + Function lookup order changed from region->parent to region->manager. + +2003-03-10 22:01 UTC tuomov + * trunk: changeset 347 + Tabs can now be dropped on WFloatWS workspaces to create a new frames + containing the region corresponding to the dragged tab. + +2003-03-10 19:12 UTC tuomov + * trunk: changeset 346 + query_workspace should now set workspace name + +2003-03-10 16:58 UTC tuomov + * trunk: changeset 345 + Added the compile time option CF_UNDERSCORED_MODULE_SYMBOLS for some + strange systems whose libdl insists on the calling program prefixing + module symbol names with an underscore. + +2003-03-09 22:50 UTC tuomov + * trunk: changeset 344 + Resize size hint handling properly (?) implemented. + +2003-03-09 12:41 UTC tuomov + * trunk: changeset 343 + *sigh* + +2003-03-09 12:40 UTC tuomov + * trunk: changeset 342 + Support for compiling modules statically in the Ion core binary + +2003-03-09 12:24 UTC tuomov + * trunk: changeset 341 + Added IonWs main.h + +2003-03-09 11:46 UTC tuomov + * trunk: changeset 340 + README updates + +2003-03-09 11:43 UTC tuomov + * trunk: changeset 339 + ioncore-example.conf was missing + +2003-03-09 11:41 UTC tuomov + * trunk: changeset 338 + main.h was missing + +2003-03-09 11:39 UTC tuomov + * trunk: changeset 337 + Resize code was missing + +2003-03-09 11:34 UTC tuomov + * trunk: changeset 336 + Files were missing from last commit + +2003-03-09 11:24 UTC tuomov + * trunk: changeset 335 + - The query module was removed of dependencies to WIonFrame code and + is now a loadable module (query.so). + + - The 'query_workspace' command by default creates workspaces of the + first registered (module loaded) kind. Other kinds of workspaces can + be created by prefixing workspace name with the class name (WIonWS, + WFloatWS) and a colon, e.g. 'WFloatWS:foo'. + +2003-03-09 11:20 UTC tuomov + * trunk: changeset 334 + Tiled workspace and frame code (WIonWS, WIonFrame) modularised + (ionws.so) and generic frame and worksapce code moved to Ioncore. + Dependencies on the query module were also removed. + +2003-03-09 11:17 UTC tuomov + * trunk: changeset 333 + - Renamed wmcore ioncore. + + - Main configuration file name changed to 'ioncore.conf'. + + - Added to ioncore generic workspace and frame classes (WGenWS, + WGenFrame) on which modules' implementations are to be based. + + - Module initialization and deinitialization functions are now named + modulename_module_init and -deinit. + + - Added module version checking. Modules are now supposed to contain + the variable 'char modulename_module_ion_version[]=ION_VERSION;', + where ION_VERSION can be found in the top-level directory version.h. + Ioncore will refuse to load modules which have not set this variable + or the version is incorrect. + + - Resize size calculation should be fixed now. + + - The 'region_register_load_create_fn' interface was removed and + replaced with 'region_register_class'. + + - Region create and reparent functions now have parent type WWindow + because everything expected that anyway. + + - Workspace setups are now saved in ~/.ion-devel/saves/ to remove + clutter from ~/.ion-devel. + + - Some minor bugs were fixed. + +2003-03-09 11:06 UTC tuomov + * trunk: changeset 332 + Very preliminary and experimental (a lot is still missing) support for + PWM-like workspaces and frames: the floatws module. + +2003-03-09 11:05 UTC tuomov + * trunk: changeset 331 + Updated default configuration files. Binding configuration is now + divided into multiple module-specific files with some common bindings + in common-frame-bindins.conf. Some look configuration files were added + and the rest were also changed to reflect changes in the order frame + border colours and sizes are specified. + +2003-03-09 11:02 UTC tuomov + * trunk: changeset 330 + Removed ETCDIR setup + +2003-03-08 13:53 UTC tuomov + * trunk: changeset 329 + 'make install' code moved from the toplevel Makefile to Makefiles in + subdirectories (etc, man, scripts). + +2003-03-08 13:53 UTC tuomov + * trunk: changeset 328 + 'make install' code moved from the toplevel Makefile to Makefiles in + subdirectories (etc, man, scripts) + +2003-03-08 13:52 UTC tuomov + * trunk: changeset 327 + 'make install' code moved from the toplevel Makefile to Makefiles in + subdirectories (etc, man, scripts). + +2003-03-06 19:22 UTC tuomov + * trunk: changeset 326 + Fixed a bug in do_fit_clientwin + +2003-03-06 19:20 UTC tuomov + * trunk: changeset 325 + Don't grab buttons that are only bound to an area (border, tab) of the + frame instead of the whole frame. + +2003-03-05 06:25 UTC tuomov + * trunk: changeset 324 + handle_configure_event fixed + +2003-03-02 15:43 UTC tuomov + * trunk: changeset 323 + Fixed a typo + +2003-03-02 15:21 UTC tuomov + * trunk: changeset 322 + Renamings and stuff + +2003-03-02 15:21 UTC tuomov + * trunk: changeset 321 + Some renamings that will break configuration files again. + +2003-03-02 15:20 UTC tuomov + * trunk: changeset 320 + Some clean-up + +2003-03-01 23:42 UTC tuomov + * trunk: changeset 319 + Screen setup sets manager for viewports + +2003-03-01 23:02 UTC tuomov + * trunk: changeset 318 + More focusing kludges + +2003-03-01 23:02 UTC tuomov + * trunk: changeset 317 + Comments added + +2003-03-01 22:08 UTC tuomov + * trunk: changeset 316 + foo + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20030301 + +2003-03-01 17:13 UTC tuomov + * trunk: changeset 315 + Added some locale checks + +2003-03-01 15:02 UTC tuomov + * trunk: changeset 314 + More utf8 kludges + +2003-03-01 12:08 UTC tuomov + * trunk: changeset 313 + Use iconv instead of libunicode + +2003-03-01 12:08 UTC tuomov + * trunk: changeset 312 + Use iconv instead of libunicode + +2003-02-28 23:17 UTC tuomov + * trunk: changeset 311 + missing font assert + +2003-02-28 19:15 UTC tuomov + * trunk: changeset 310 + FONT_HEIGHT -> MAX_FONT_HEIGHT + +2003-02-28 18:47 UTC tuomov + * trunk: changeset 309 + The 'transparent_background' draw.conf option now only applies to + empty frames. For client windows with a transparent background the + 'transparent' winprop should be set to true for transparent frame + background. + +2003-02-28 18:17 UTC tuomov + * trunk: changeset 308 + More focus kludging + +2003-02-28 16:47 UTC tuomov + * trunk: changeset 307 + Misc cleanup + +2003-02-28 16:24 UTC tuomov + * trunk: changeset 306 + Preliminary support for UTF8. XFree86 (4.x) and libunicode are + required. + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20030225 + +2003-02-28 15:27 UTC tuomov + * trunk: changeset 305 + Maybe focusing would work this time... + +2003-02-26 23:40 UTC tuomov + * trunk: changeset 304 + do_fit_clientwin fixed + +2003-02-25 19:29 UTC tuomov + * trunk: changeset 303 + More kludges in an attempt to fix focus handling + +2003-02-25 19:16 UTC tuomov + * trunk: changeset 302 + Support for optional autoconf-generated system-ac.inc. (The configure + script is not finished or included.) + +2003-02-24 14:13 UTC tuomov + * trunk: changeset 301 + Xft support fixed more + +2003-02-24 11:24 UTC tuomov + * trunk: changeset 300 + Xft support fixed + +2003-02-24 06:29 UTC tuomov + * trunk: changeset 299 + Oops + +2003-02-24 06:25 UTC tuomov + * trunk: changeset 298 + Added KP_Enter bound to 'finish' to query bindings + +2003-02-24 06:23 UTC tuomov + * trunk: changeset 297 + Fixed focus and grab handling when warping is not enabled + +2003-02-23 18:55 UTC tuomov + * trunk: changeset 296 + Added the command frame_close_if_empty and bound close command for + frames to this. + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20030223 + +2003-02-23 18:54 UTC tuomov + * trunk: changeset 295 + Implemented "close" command for queries. + +2003-02-23 18:49 UTC tuomov + * trunk: changeset 294 + Fixed frame_close + +2003-02-23 14:25 UTC tuomov + * trunk: changeset 293 + Nested submaps are now fully implemented. + +2003-02-23 14:14 UTC tuomov + * trunk: changeset 292 + Minor mods + +2003-02-23 13:45 UTC tuomov + * trunk: changeset 291 + The command clientwin_toggle_fullscreen now works in both directions. + However, it should be noted that this toggle does not work well along + with client programs' full screen mode toggles. Some means of + communication should be devised. + +2003-02-23 13:16 UTC tuomov + * trunk: changeset 290 + Attempts at more focusing fixes + +2003-02-23 12:35 UTC tuomov + * trunk: changeset 289 + Changes to binding file + +2003-02-23 12:22 UTC tuomov + * trunk: changeset 288 + Added the compile-time option CF_SECOND_RATE_OS_FS to change colons to + underscores in display name part of configuration file names. + +2003-02-23 11:58 UTC tuomov + * trunk: changeset 287 + Preliminary support for workspace switching while dragging tabs. + +2003-02-23 02:40 UTC tuomov + * trunk: changeset 286 + Oops + +2003-02-23 02:35 UTC tuomov + * trunk: changeset 285 + Added saveload.h + +2003-02-23 00:38 UTC tuomov + * trunk: changeset 284 + Some changes and (hopefully) fixes to focusing policy + +2003-02-23 00:14 UTC tuomov + * trunk: changeset 283 + Submap with the same key can be specified in multiple sections now + +2003-02-22 21:48 UTC tuomov + * trunk: changeset 282 + Tab width calculation fixed + +2003-02-22 21:08 UTC tuomov + * trunk: changeset 281 + Workspace initialization stuff moved to wmcore + +2003-02-22 20:49 UTC tuomov + * trunk: changeset 280 + close_frame fixed + +2003-02-22 20:36 UTC tuomov + * trunk: changeset 279 + Some function names changed + +2003-02-22 17:44 UTC tuomov + * trunk: changeset 278 + Active client window commands can be accessed from other bindings with + the command 'commands_at_leaf'. + +2003-02-22 17:40 UTC tuomov + * trunk: changeset 277 + - Key binding setup changed. + + - X window -less regions no longer contain children. Instead the + regions "manage" these objects that share the parent object with the + managing object. + + - Removed clientwin_bindings and viewport_bindings sections. + + - More consistent and descriptive command names. + + - New workspace layout saving and loading code that supports arbitrary + objects instead of just frames and workspaces.. + +2003-02-22 17:37 UTC tuomov + * trunk: changeset 276 + - Key binding setup changed. + + - X window -less regions no longer contain children. Instead the + regions "manage" these objects that share the parent object with the + managing object. + +2003-02-22 17:34 UTC tuomov + * trunk: changeset 275 + - Removed clientwin_bindings and viewport_bindings sections. + + - More consistent and descriptive command names. + +2003-02-22 17:32 UTC tuomov + * trunk: changeset 274 + modified system.mk + +2003-02-22 14:24 UTC tuomov + * trunk: changeset 273 + Resize size display should now be properly positioned on Xinerama + screens. + +2003-02-20 17:48 UTC tuomov + * trunk: changeset 272 + load_module searches the directories $LIBDIR and ~/.ion-devel/lib for + the module if the name contains no slashes. + +2003-02-20 14:59 UTC tuomov + * trunk: changeset 271 + Xft default compilation options changed in system.mk + +2003-02-17 20:34 UTC tuomov + * trunk: changeset 270 + Fixed region_do_find_new_home + +2003-02-16 17:30 UTC tuomov + * trunk: changeset 269 + Fixed alloc_defer + +2003-02-12 21:18 UTC tuomov + * trunk: changeset 268 + Colour-freeing fix + +2003-02-09 16:23 UTC tuomov + * trunk: changeset 267 + Free unused colours + +2003-02-09 15:57 UTC tuomov + * trunk: changeset 266 + Addresses updated on the man page + +2003-02-09 12:45 UTC tuomov + * trunk: changeset 265 + Support re-reading draw.conf (reread_draw_config) without restart + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20030131-2 + +2003-02-09 12:44 UTC tuomov + * trunk: changeset 264 + Changed broken_app_resize_kludge a bit. Should work a little better + now. + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20030131 + +2003-02-08 13:41 UTC tuomov + * trunk: changeset 263 + Minor fix + +2003-01-31 21:44 UTC tuomov + * trunk: changeset 262 + Changed hook linking order + +2003-01-31 18:16 UTC tuomov + * trunk: changeset 261 + Year changed to 2003 + +2003-01-26 22:30 UTC tuomov + * trunk: changeset 260 + Changed -pedantic-errors to -pedantic in system.mk to get around + broken glibc headers + +2003-01-17 21:43 UTC tuomov + * trunk: changeset 259 + Transient mapping fix + +2003-01-09 00:29 UTC tuomov + * trunk: changeset 258 + Xft font names are now to be prefixed with 'xft:', otherwise normal + clear fonts are used. Xft support still is not compiled in by default. + +2003-01-09 00:01 UTC tuomov + * trunk: changeset 257 + minor fixes + +2003-01-08 15:25 UTC tuomov + * trunk: changeset 256 + small changes + +2003-01-05 23:48 UTC tuomov + * trunk: changeset 255 + Removed an unnecessary debug fprintf + +2003-01-05 06:53 UTC tuomov + * trunk: changeset 254 + Tab dragging routines can now handle nested frames. + +2003-01-04 22:02 UTC tuomov + * trunk: changeset 253 + Submap handling fix + +2003-01-04 21:39 UTC tuomov + * trunk: changeset 252 + CF_STUBBORN_TRESH fix + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20021229 + +2003-01-03 22:58 UTC tuomov + * trunk: changeset 251 + sample.conf micro->milli + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20021219 + +2002-12-30 23:33 UTC tuomov + * trunk: changeset 250 + Send more ConfigureNotify events to fix slow startup times of some + programs + +2002-12-29 02:22 UTC tuomov + * trunk: changeset 249 + Configurable regular expression based window title shortening rules + +2002-12-18 23:04 UTC tuomov + * trunk: changeset 248 + Fixed a potential memory leak + +2002-12-18 22:51 UTC tuomov + * trunk: changeset 247 + kludges.conf updated + +2002-12-18 22:50 UTC tuomov + * trunk: changeset 246 + Winprop matching improvements: WM_WINDOW_ROLE support and + configuration format changed to 'winprop "class", "role", "instance" { + ... }'. + +2002-12-14 17:55 UTC tuomov + * trunk: changeset 245 + quote_next returns + +2002-12-03 22:32 UTC tuomov + * trunk: changeset 244 + Some extra abstraction to Xft support code + +2002-12-03 22:19 UTC tuomov + * trunk: changeset 243 + Applied Xft support patch + +2002-11-21 17:58 UTC tuomov + * trunk: changeset 242 + Web page and email address updated + +2002-11-14 23:00 UTC tuomov + * trunk: changeset 241 + Transient window height restrictions lifted + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20021104 + +2002-11-13 00:00 UTC tuomov + * trunk: changeset 240 + config.h wasn't included at the proper point in binding.c + +2002-11-08 00:14 UTC tuomov + * trunk: changeset 239 + Fixed pointer warping on workspace change when warps are disabled + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20021103 + +2002-11-04 13:37 UTC tuomov + * trunk: changeset 238 + Fixed a segfault problem with symlists + +2002-11-03 04:33 UTC tuomov + * trunk: changeset 237 + Fixed a drawing problem with ion_bar_inside_frame FALSE + +2002-11-03 02:44 UTC tuomov + * trunk: changeset 236 + Added transparent_background (TRUE/FALSE) draw.conf configuration + option + +2002-11-03 02:33 UTC tuomov + * trunk: changeset 235 + Bound broken_application_resize_kludge to Mod1+L + +2002-11-03 02:26 UTC tuomov + * trunk: changeset 234 + Added broken_app_resize_kludge function + +2002-11-03 02:15 UTC tuomov + * trunk: changeset 233 + Ad hoc fix for clientwin enter window events + +2002-11-02 22:57 UTC tuomov + * trunk: changeset 232 + Other minor fixes + +2002-11-02 22:57 UTC tuomov + * trunk: changeset 231 + Line editor history scrolling fixed + +2002-11-02 22:21 UTC tuomov + * trunk: changeset 230 + Default (black&white) colour scheme changes + +2002-11-02 22:14 UTC tuomov + * trunk: changeset 229 + Fullscreen windows on separate Xinerama screens should be focused + properly now. + +2002-11-02 22:03 UTC tuomov + * trunk: changeset 228 + Fixed a problem concerning resizing of frames containing hidden + "acrobatic" windows + +2002-11-02 15:54 UTC tuomov + * trunk: changeset 227 + Added handle_event_alt "alternative hook" for modules that want to + handle X events directly. + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20020926 + +2002-10-27 22:40 UTC tuomov + * trunk: changeset 226 + Screen-based configuration file selection fixed + +2002-10-15 18:08 UTC tuomov + * trunk: changeset 225 + Fixed a problem with query boxes and wheel mice + +2002-09-25 21:15 UTC tuomov + * trunk: changeset 224 + Added the function goto_named_region (replaces missing + goto_client_name) + +2002-09-25 11:53 UTC tuomov + * trunk: changeset 223 + Added manual tab-ordering functions frame_move_current_tab_left/right + +2002-09-25 07:26 UTC tuomov + * trunk: changeset 222 + Fixed pointer warping on workspace change + +2002-09-15 09:36 UTC tuomov + * trunk: changeset 221 + Minor fixes + +2002-09-15 09:36 UTC tuomov + * trunk: changeset 220 + Added a few missing characters to workspace configuration loading code + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20020819 + +2002-09-13 11:35 UTC tuomov + * trunk: changeset 219 + Minor fix + +2002-08-22 21:13 UTC tuomov + * trunk: changeset 218 + Self-pointing transient_for hint problem fixed + +2002-08-18 17:09 UTC tuomov + * trunk: changeset 217 + Initial focus changes + +2002-08-18 16:51 UTC tuomov + * trunk: changeset 216 + Tagging restored (frame functions: toggle_sub_tag, attach_tagged and + global function clear_tags) + +2002-08-12 20:55 UTC tuomov + * trunk: changeset 215 + Unused code commented out + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20020606 + +2002-07-06 21:59 UTC tuomov + * trunk: changeset 214 + Function completion in query_function restored + +2002-06-14 22:36 UTC tuomov + * trunk: changeset 213 + Tab drag assertion fixed + +2002-06-05 23:10 UTC tuomov + * trunk: changeset 212 + "Watches" added in pointing device code + +2002-06-05 22:51 UTC tuomov + * trunk: changeset 211 + Minor clean-up + +2002-06-05 20:56 UTC tuomov + * trunk: changeset 210 + Target ID table bugfix + +2002-06-05 20:56 UTC tuomov + * trunk: changeset 209 + Split bugfix + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20020604 + +2002-06-05 14:03 UTC tuomov + * trunk: changeset 208 + Old window_press restored: mouse actions directed to the client window + associated with a tab should work now. + +2002-06-03 23:07 UTC tuomov + * trunk: changeset 207 + Some minor clean-up + +2002-06-03 22:50 UTC tuomov + * trunk: changeset 206 + Makefile fix concerning patch and bindings-sun.conf + +2002-06-03 22:39 UTC tuomov + * trunk: changeset 205 + Preliminary Xinerama support + +2002-06-03 15:11 UTC tuomov + * trunk: changeset 204 + Another key binding related segfault fix + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20020531 + +2002-06-01 00:25 UTC tuomov + * trunk: changeset 203 + Fixed segfault problem with multihead displays + +2002-05-31 06:00 UTC tuomov + * trunk: changeset 202 + Fixed lockup when a window's title ends in its only colon (and spaces) + but even just the three dots and instance number are too long to fit + in the tab. + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20020529 + +2002-05-30 19:30 UTC tuomov + * trunk: changeset 201 + Fixed segfault when unbound key was pressed in submap mode + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20020528 + +2002-05-30 17:04 UTC tuomov + * trunk: changeset 200 + Fixed 'switch_tab' + +2002-05-29 06:51 UTC tuomov + * trunk: changeset 199 + - Fixed wscurrent callback handlers + + - Fixed query_function error reporting + +2002-05-28 21:49 UTC tuomov + * trunk: changeset 198 + fixed the fix + +2002-05-28 21:46 UTC tuomov + * trunk: changeset 197 + - Lifted restriction on nested command sequences (now max 32) + + - More fixes and temporary kludges + +2002-05-28 20:08 UTC tuomov + * trunk: changeset 196 + New binding configuration file + +2002-05-28 20:05 UTC tuomov + * trunk: changeset 195 + regbind.* were missing + +2002-05-28 19:59 UTC tuomov + * trunk: changeset 194 + A few comments + +2002-05-28 19:59 UTC tuomov + * trunk: changeset 193 + Added a notice of out-of-date information to documentation + +2002-05-27 21:54 UTC tuomov + * trunk: changeset 192 + Resize problem fixed + +2002-05-26 23:03 UTC tuomov + * trunk: changeset 191 + Binding callbacks are now entirely command sequence based + +2002-05-26 14:40 UTC tuomov + * trunk: changeset 190 + - Key binding management revised: all X server key grabs are made on + the root window and the innermost window with an internal grab gets + to receive the events. + + - Key binding configuration changed: clientwin, screen and frame + sections added. + + - Preliminary full screen client window support: + 'clientwin_enter_fullscreen' function (no toggle yet) and auto + detection (MWM decoration hints set to none when a configure request + with width and height set to those of the screen is received). + +2002-05-26 14:27 UTC tuomov + * trunk: changeset 189 + Query updated to new binding model + +2002-05-26 14:27 UTC tuomov + * trunk: changeset 188 + Bindings updated to new binding model + +2002-05-21 18:49 UTC tuomov + * trunk: changeset 187 + Fixed workspace switching on restart + +2002-05-21 18:39 UTC tuomov + * trunk: changeset 186 + Fixed split_empty + +2002-05-19 22:30 UTC tuomov + * trunk: changeset 185 + Title updating fixed + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20020510 + +2002-05-19 22:26 UTC tuomov + * trunk: changeset 184 + Maximize fixed + +2002-05-11 00:09 UTC tuomov + * trunk: changeset 183 + My email address changed + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20020414 + +2002-05-10 19:33 UTC tuomov + * trunk: changeset 182 + transient_mode winprop returns + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20020411 + +2002-04-20 20:59 UTC tuomov + * trunk: changeset 181 + Main loop select() support for multiple file descriptors. + +2002-04-12 20:01 UTC tuomov + * trunk: changeset 180 + The function query_renameframe was added and names of frames are saved + now. + +2002-04-11 21:31 UTC tuomov + * trunk: changeset 179 + Some minor glitches like missing includes fixed -- strict compiler + warning flags enabled by default again. + +2002-04-11 20:51 UTC tuomov + * trunk: changeset 178 + Command sequence stuff moved to wmcore/ + +2003-12-23 20:17 UTC unknown + tagged ion-devel-20020405 + +2002-04-11 15:24 UTC tuomov + * trunk: changeset 177 + 'target' winprop for specifying named workspaces (or any named object + with region_attach_sub) as attachment targets for client windows. + +2002-04-11 15:06 UTC tuomov + * trunk: changeset 176 + Added the region_ws_attach_clientwin/region_ws_attach_transient + interface that workspace-like objects should implement. + +2002-04-04 23:42 UTC tuomov + * trunk: changeset 175 + oldChangeLog changed + +2005-02-15 18:06 UTC tailor@f281.ttorni.ton.tut.fi + * Tailorization of trunk + Import of the upstream sources from the repository + + http://tao.uab.es/ion/svn/ion/trunk + + as of revision 173 + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..cf9b6b9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,510 @@ + + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations +below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. +^L + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it +becomes a de-facto standard. To achieve this, non-free programs must +be allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. +^L + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control +compilation and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. +^L + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. +^L + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at least + three years, to give the same user the materials specified in + Subsection 6a, above, for a charge no more than the cost of + performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. +^L + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. +^L + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply, and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License +may add an explicit geographical distribution limitation excluding those +countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. +^L + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS +^L + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms +of the ordinary General Public License). + + To apply these terms, attach the following notices to the library. +It is safest to attach them to the start of each source file to most +effectively convey the exclusion of warranty; and each file should +have at least the "copyright" line and a pointer to where the full +notice is found. + + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or +your school, if any, to sign a "copyright disclaimer" for the library, +if necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James + Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..6f7c06a --- /dev/null +++ b/Makefile @@ -0,0 +1,36 @@ +## +## Ion Makefile +## + +# System-specific configuration is in system.mk +include build/system-inc.mk + +# List of modules +include modulelist.mk + +###################################### + +INSTALL_SUBDIRS=\ + $(MODULE_LIST) \ + ioncore ion pwm etc utils man po \ + + +SUBDIRS = $(LIBS_SUBDIRS) $(INSTALL_SUBDIRS) + +DOCS = README LICENSE ChangeLog RELNOTES + +TO_REALCLEAN = build/ac/system-ac.mk + +POTFILE=po/ion.pot + +###################################### + +include build/rules.mk + +###################################### + +_install: + $(INSTALLDIR) $(DOCDIR) + for i in $(DOCS); do \ + $(INSTALL) -m $(DATA_MODE) $$i $(DOCDIR); \ + done diff --git a/README b/README new file mode 100644 index 0000000..62684ee --- /dev/null +++ b/README @@ -0,0 +1,137 @@ + +Ion +=== + +Copyright (c) Tuomo Valkonen 1999-2006. + +tuomov at iki.fi + + + + +Building and installing +----------------------- + +1. Make sure you have the following tools and libraries installed: + + * GNU make + * Lua 5.1 (see ). + +2. a) Edit `system.mk` to suit your system. Most GNU/Linux users should + need very little modifications to `system.mk`. + + b) Alternatively, go to the directory `build/ac`, run `autoreconf` + and then `./configure`. Switch back to the top directory afterwards. + See `build/ac/README.autoconf` for details on parameters, especially + if you are using the Debian Lua packages. If you try this method and + it fails for some reason, please make sure `build/ac/system-ac.mk` + does not exist before falling back to alternative a). Also in such + a case, find someone else to complain to; I (tuomov) will have + nothing to do with autoconf. + +3. If you want to build some extra modules now or do not want to build + some of the standard modules, edit `modulelist.mk`. + +4. Run `make`. Note that `make` here refers to GNU make which is usually + named `gmake` on systems with some other implementation of make as + default. + +5. Run `make install`, as root if you set `$PREFIX` in `system.mk` to a + directory that requires those privileges. + + YOU SHOULD NOT SKIP THIS STEP unless you know what you are doing. Ion + will refuse to start if it can not find all the necessary uncorrupt + configuration files either in `$PREFIX/etc/ion3/` or in `~/.ion3/`. + +6. How to best set up `startx` or whatever to start Ion instead of your + current window manager depends on your system's setup. A good guess + is creating or modifying an executable shell script `.xsession` in your + home directory to start Ion. This should usually (but not always) work + if you're using some X display/login manager. If `~/.xsession` does not + help and you're not using a display manager, modifying `~/.xinitrc` or + creating one based on your system's `xinitrc` (wherever that may be; + use locate) may be what you need to do. Note that unlike `.xsession`, + a `.xinitrc` should usually do much more setup than simply start a few + programs of your choice. + +Please see the file `RELNOTES` for additional release-specific installation +and configuration notes. + + +Some optional installation steps +-------------------------------- + +1. The F5 and F6 keys expect to find the program `run-mailcap` to select + a program to view a file based on its guessed MIME type. Unless you are + using Debian, most likely you don't have it, but any other similar + program (or just plain old text editor) will do as well - just the bindings + in `cfg_bindings.lua`. Of course, if you don't want to use the feature at + this time or never, you may simply skip this step. If you want to use + `run-mailcap`, it can be found from the following address, as a source + tarball as well: + + + +2. Ion supports caching known man-pages in a file for faster man-page + completion in the F1 man page query. To enable this feature, you must + periodically run a cronjob to build this list. To create a system-wide + man page cache, run `crontab -e` (might vary depending on platform) as + root and enter a line such as follows: + + 15 05 * * * $SHAREDIR/ion-completeman -mksyscache + + Replace `$SHAREDIR` with the setting from `system.mk` (or `system-ac.mk` + if you used autoconf). This example runs daily at 05:15, but you may + modify the run times to your needs; see the crontab manual. + + If you can't or do not want to build a system-wide man page cache, run + `crontab -e` as your normal user and replace `-mksyscache` with + `-mkusercache` above. The cache file will be `~/.ion3/mancache`. + + It may also be useful to run `ion-completeman` with the suitable + `-mk*cache` argument once manually to build the initial cache. + + If the `MANPATH` environment variable is not set on your system and it + does not have the `manpath` command (or it does not print anything + sensible), you may also want to set the `ION_MC_MANPATH` environment + variable to the list of paths where the system stores manual pages. + + +Configuration +------------- + +For help on modifying Ion's configuration files, PLEASE READ THE DOCUMENT +"Configuring and extending Ion3 with Lua" available from the Ion web page, +listed at the top of this file. + + +Questions, comments, problems? +------------------------------ + +If the available documentation does not answer your question, please +post it to the mailing list. Details can be found on the Ion web page +listed at the top of this file. + + +Credits +------- + +Ion was written by Tuomo Valkonen. + +The autoconf script written by Tom Payne for the most part. + +The dock module was written by Tom Payne and Per Olofsson. + +`share/ion-completefile/ion-completefile.c` is based on editline, (c) +1992 Simmule Turner and Rich Salz. See the file for details. + +The code that `de/fontset.c` is based on was apparently originally written +by Tomohiro Kubota; see +. + +Various patches have been contributed by other individuals unlisted here. +See the mailing list archives and the darcs source repository history at +. For translators see the individual `.po` +files in `po/`. + +See `libtu/README` for code by others integrated into libtu. diff --git a/RELNOTES b/RELNOTES new file mode 100644 index 0000000..589ab54 --- /dev/null +++ b/RELNOTES @@ -0,0 +1,1048 @@ + +ion-3ds-20061223 +---------------- + +There's nothing major in this release, primarily just some minor fixes +and tuning to the previous release, that it was time to release. + + +ion-3ds-20061029 +---------------- + +Mostly this release still fixes issues in the big 3ds-20061015 release, +but in addition there are some improvements in the query department: + + * Query activation key now cycles completions (So e.g. the + in the context menu activated with META+M, this same key + can be used to cycle through the alternatives.) This does + not work for queries activated by submap bindings. + + * Likewise, it is no longer necessary to specify the key to + use for cycling for `mod_menu.grabmenu`. + + * Control+R can now be used for history completion in queries. + (Currently matching is done for full string up to 'point', but + this may be changed to substring match.) + + * Note that the parametrisation of WEdln.complete has changed, + and the second cycle parameter must be 'next' now instead of + `true`. + + +ion-3ds-20061020 +---------------- + +Fixes some (expected, but minor) issues in the previous release. + + +ion-3ds-20061015 +---------------- + + * WIonWS and WFloatWS and the corresponding modules are gone, and + your custom configuration files will be broken with regard to these. + However, a partial backwards compatibility hack exists for layout + savefiles. + + The F9 and META-F9 bindings now by default create workspaces with + a tiled layout of two frames. To create an "empty" workspace, + corresponding to the old WFloatWS, use the context menu (META-M) + and choose "new-empty-workspace". It is also possible to change + the default layout. + + * Note that if you restart from an old version to this new version of Ion, + transients will stop working as expected for already existing windows. + They will work for newly-created windows, however. + + * There are a few new sets of binding (including one for `WClientWin`!), + and some old bindings may not work exactly as expected anymore. In + particular, those for switching to full-screen mode. + + **It is probably best to start from scratch with your custom + bindings.** + + * `WFrame.set_tabbar` is gone. If you absolutely want to get rid off + the tabs, you must change the frame's "mode" with `WFrame.set_mode`. + The mode "tiled-alt" has been intended for this, and the corresponding + "framed-tiled-alt" style defaults to `bar = "none"`. + + * The rather popular `detach.lua` script from the scripts repository + is obsoleted now, as Ion includes detach functionality in itself. + To detach a window, use META-K D in the default bindings. To tile + an existing frame from a workspace that doesn't have a tiling yet, + use META-K B. + + +ion-3ds-20060519 +---------------- + +Some notable changes in this release include + + * Lua 5.1 is now required. + + * Framed transients on by default now. New binding contexts + "WFrame.toplevel" and "WMPlex.toplevel" were added to allow + for separate sets of bindings for nested transient frames and + top-level frames. Some of the bindings in the default binding + maps that are likely to be unwanted on transient frames were + moved to these contexts. Old custom bindings will continue to + work unless they modify the defaults by unbinding some of the + moved bindings. + + * Pressing Mod1+K K in the default bindings now switches to any + region with the "activity" flag set (indicated by the a box + at a corner of the screen), if there's one, before cycling to + previously active region. The same effect can be achieved in + your custom bindings with + + ioncore.activity_goto() or ioncore.goto_previous() + + +ion-3ds-20050607 +---------------- + +This release mostly features minor bugfixes and other improvements. The most +visible non-bugfix changes are: + + * Faster "fontset" filling kludge: only `-misc-fixed-*` fonts are tried. Ion + should now load faster in UTF-8 environments, and usually with no less + fonts in the fontset than before. (UTF-8 string drawing still does not + fully utilise these fonts under XFree86, but it does under XOrg.) + + * Experimental auto-show-completions support, which is also on by default + now. In this mode the Tab key can be used to cycle forward through the + completions, and Shift+Tab backwards. Modify the settings seen in the + new `mod_query.lua` to get normal Tab-completion, or change the completion + delay. + + * The release scripts do not run automatically autoconf anymore so maybe + a few more people would look into the README first. + + +ion-3ds-20050322 +---------------- + +The most visible changes in this release are: + + * Mod1+space now toggles the scratchpad by default and Mod1+D the dock. + + * The `ion-(ssh|man|view|edit)` wrapper scripts were removed and instead + the programs used can be configured in Ion's configuration files. + + * `ioncore.exec(_on)` also support the `:cmd` notation for "run cmd in + xterm" familiar from the run query. The `::cmd` notation can be used + to ask for enter to be pressed even when the program quit succesfully. + + * Those with custom configuration files should note that many exported toggle + functions were changed and renamed, and now accept a string parameter + incidating whether to toggle, set or unset the property. + + - `WClientwin.set_fullsreen` (replaces `WClientWin.toggle_fullscreen`) + - `WRegion.set_tagged` (replaces `WRegion.tag/untag/toggle_tag`) + - `WFrame.set_tabbar` (replaces `WFrame.toggle_tabbar`) + - `WFrame.set_shaded` (replaces `WFrame.toggle_shade`) + - `WFloatFrame.set_sticky` (replaces `WFloatFrame.toggle_sticky`) + - `WMPlex.l2_set_hidden` (replaces `WMPlex.l2_hide/show`) + - `mod_sp.set_shown(_on)` (replaces `mod_sp.toggle(_on)`) + - `mod_dock.set_floating_shown_on` (replaces `mod_dock.toggle_floating_on`) + - `WRegion.set_activity` (replaces `WRegion.clear_activity` and + `WRegion.notify_activity`) + + For example, `WRegion.set_tagged(_, 'toggle')` should be used in place of + `WRegion.toggle_tagged(_)` now. + +Obviously there are some other changes and fixes too. See the changelog +for details as usual. + + +ion-3ds-20050304 +---------------- + +This is mostly a bug fix release, but also features improved +`mod_query.query_exec` (F3 key) completion support. Script writers should be +aware that As a side effect of one of these bug fixes, many hooks are now +called in "protected mode" and can not call any functions that modify the +internal state of Ion, except ioncore.defer. + + +ion-3ds-20050227 +---------------- + +The most important changes in this release are: + + * So-called "placeholders". With the help of these the positions of + full-screen windows are remembered in their original frames, and don't + just get inserted after currently active window when returning from + full-screen mode. Under a session manager placeholders are also used + to remember the original order of windows. + + * Improved `mod_statusbar` and `ion-statusd` communication, and colouring + of important/critical meters. + + * A number of small fixes. + + +ion-3ds-20041104 +---------------- + +This monthly snapshot adds a few new and improved features. + + * 'Grabbed menus' that have a single cycling key and activate selected + entry when all modifiers have been released. See + + + + for an application. + + * Potentially blocking status meters are now in a separate ion-statusd + program. Please write your additional status meters that do not monitor + the state of Ion itself for ion-statusd (and contribute them in the + Ion3 scripts repository at ). + For help on writing such status meters, see e.g. source for + `statusd_load` in `ext_statusbar/ion-statusd`. + + * Floating splits are now supported on plain tiled workspaces as well as + on pane workspaces. To create such a split, use the workspace context + menu (Mod1+M by default) or write your own bindings. + + * Line editor now supports history search; Control+Up/Down only scrolls + through history entries with matching initial part. + + * Arbitrary winprop matching criteria is supported. Lua scripts have + access to X properties. + +Of course there are some other minor fixes and improvements as well. + + +ion-3ds-20040906 +---------------- + +This release finally includes a usable (yet still incomplete) version +of the `mod_panews` workspace module (that has also bored the name 'autows' +and 'rubberws' previously). The final outcome is not exactly what I +initially planned, as those plans turned out not to be that workable. +Instead, what `mod_panews` does is add to the basic tiled ionws approach +overlappable splits and automatically filled panes that are initially +empty. Each window is classified and assigned to the pane matching that +classification. (The default classifications are T(erminal), V(iewer) +and M(iscellaneous).) + +For a better feel for it, try it out yourself and please give feedback. +The overlapping panes may be a bit confusing (this is one place where +true translucency might actually be useful and not just eyecandy) at +first, but you'll get used to them if you use the feature, and e.g. +the Gimp works quite splendidly with the toolboxes in the 'M' pane and +image windows in the 'V' pane -- although the simple initial size-based +classification heuristics don't always get it right and overriding +winprops (setting: `panews_classification`) should eventually added. + +In addition to the new module, this release adds support for translations +of program messages and the manual page, regardless of whether such is +of any use in a program like Ion or not. (Currently Finnish and Czech +translations are available.) Of course there are some bug fixes and +other minor additions as well, and the `./configure` script is also back, +the abandonment of libtool being final now. + + +ion-3ds-20040730 +---------------- + +The first thing you'll notice when you start up this release of Ion is +that isn't reading your old configuration that. The next thing you +should notice is a neat layout-adapting statusbar at the bottom of the +screen. That's right, Ion now includes an `ext_statusbar` Lua script that +is enabled by default and displays date/time/load/mail count in a +configurable format. + +So why is it not reading your configuration and save files? Firstly, +all of the `.lua` files were renamed to be indicative of their purpose. +Secondly, there have been so many changes that your old files would be +incompatible anyway. + +The `.lua` files are now named as follows: + + cfg_*.lua Configuration file that the user may wish to edit + look_*.lua Drawing engine style file + saved_*.lua Save file + mod_*.lua Module stub loader + ext_*.lua A bigger Lua extension without a C counterpart + so that it is not a module + +The configuration file for `mod_foobar` or `ext_foobar`, if it has one, is, +of course, `cfg_foobar.lua`. In the topic of file names, also note that +the default installation paths and binary names have changed to include +the component '3' to reflect the situation with many binary packages +of Ion. + +You perhaps noticed above that modules have stub loaders now, so the +user has no need to use the `ioncore.load_module` routine. All extensions +and additional configuration files can now be loaded with 'dopath' +(used to be 'include'). Also, the `menulib`, `querylib`, and `ioncorelib` +Lua libraries are gone and instead their contents can be found in the same +`mod_whatever` namespace with the corresponding module (the "stub" loader +for these modules is a bit more than just a stub...). Some may also +want to know that for `mod_*` and `ext_*` only the compiled .lc files are +now installed, and not the source `.lua` files, thus removing redundant +files and making the installation slightly smaller. + +There are few other changes to the contents of the configuration files +as well, so you're probably best off simply rewriting your modified +configuration files based on the new defaults. There quite likely won't +be any more _big_ changes to the configuration files before the release +of final Ion3, wherever that will be. (Most likely we won't see 3rc:s +yet this year.) However, some functions and variables are still likely +to be renamed or changed. + +In addition to being renamed, the layout savefile of this Ion release +is incompatible with older releases. This is due to the changes made +to the `WIonWS` (and `WAutoWS`) split tree code to make it more modularly +extensible with the new kinds of nodes that `WAutoWS` requires. + +The `mod_autows` module has infact been through many changes since the +last release, and I think I have the final form of it finally figured +out. However, it is still far from finished and unlikely to be ready +for use yet. + + +ion-3ds-20040703 +---------------- + +The major new features of this snapshot release are: + + * `WMPlex` support for a sticky status display area to which `WIonWS`s + adjust properly. Modified to the dock module to support this method. + See the new `dock.lua` to set up the dock in the new embedded or old + floating manner (the API for the latter has also changed). + * Primitive session management support. + +There are also some bug fixes and many minor improvements; see the ChangeLog +for details. Work on `mod_autows` has also started, but it isn't ready for +use yet. + + +### Note on ./configure: + +./configure does not exist for the moment as the source autoconf script +is not up-to-date. Ion no longer uses libtool/libltdl due to problems +with inter-module dependencies, and I have not asked Tom Payne to fix +the script yet, as this change may not be final (although that is most +likely the case; those without Linux-compatible libdl and a flexible +binary format such as ELF will just have to link statically against the +modules). + + +### Notes on session management: + +Ion loads `mod_sm` automatically when the SESSION_MANAGER environment +variable that should be set by the session manager is set, so there's +no need load it in `ion.lua`. + +When session management is in use, all entries in the 'Session' menu +(previously 'Exit' menu) actually invoke the session manager to do the +task. 'Save' asks the SM save the whole session and 'Exit' (ioncore.shutdown) +asks it to shut down the session instead of just causing the WM to quite. +To do the latter, use ioncore.resign. + +Unfortunately, all session managers I have tried (from Debian/unstable), +are broken/incomplete in one way or another: + + +xsm: doesn't support any requests from applications. This makes Ion's +session menu complete unfunctional. The only way to restart/exit/save +state is through xsm's window. + + +gnome-session: This seems the most complete of the all the session managers +and works fine until it is requested to shut down the session, when it dumps +core and session state is lost if was not explicitly saved previously. + +A `~/.gnome2/session` file to use with Ion follows: + + [Default] + num_clients=2 + 0,id=default0 + 0,Priority=0 + 0,RestartCommand=gnome-smproxy --sm-client-id default0 + 1,id=default1 + 1,Priority=10 + 1,RestartCommand=/usr/local/ion-3/bin/ion -smclientid default1 + + +ksmserver: Only supports a global shutdown request, so that Ion can not be +restarted or session state saved in the middle of a session. + + +ion-3ds-20040316 +---------------- + +This is the first development snapshot release of what is to be Ion3. +The most visible changes to Ion2 so far are: + + * Default installation directory is `/usr/local/ion-3` while user + configuration files go in `~/.ion3` + + * The `mod_sp` module was added. It creates an extra toggleable + "scratchpad" frame on each screen. Toggle is for now bound by default + to Mod1+section, which should be very conveniently located on most + Nordic keyboards (left of "1"), but you may have to change it. The + scratchpad should be nice for xconsole or similar monitors, xmessage + and other popups and just as temporary holding space for windows. + + * All modules except the drawing engine are now called `mod_something`. + + * Man-page complection supports a cache of known man-pages for faster + completion. See the README for instructions on setting up a cronjob + or manually generating the index if you want to use the feature. + Also `query_man_path` is no longer used. Instead we try the + `ION_MC_MANPATH` and `MANPATH` environment variables and the + 'manpath' program. + + * Exported functions are now separated into tables (namespaces) + instead of cluttering the globals table. Some frequently used + configuration functions are imported into the globals table, though. + Some functions have also been removed or renamed for simpler and + more consistent function set. + + * New binding configuration mechanism. Dozens of `*_bindings` functions + were replaced with a single `[ioncorelib.]defbindings` function that + accepts a context parameter. Callbacks are specified as strings + (although passing functions still works) to make it easier for + external configuration programs to understand the configuration + files and perhaps remove some confusion among users who do not + care to read a tiny bit of Lua documentation and understand the + concept of anonymous functions. + + * It is now also possible to retrieve a list of bindings with + `ioncorelib.getbindings`, for example, for self-documentation. + + * Single move/resize mode bindings instead of separate for both + ionframe and floatframe. + +There are also quite a few internal changes; see the ChangeLog for +details. No documentation tarball is available at the moment as the +documentation is out-of-date except for the function reference. If +you need the reference, just checkout the documentation from the +Subversion repository with + + svn co http://tao.uab.es/ion/svn/ion-doc/trunk ion-doc-3ds + +and build the documentation. + + +ion-2rc-20040114 +---------------- + +This release is finally what can be called "Ion 2 release candidate #1". +No more new features will be added to "Ion 2" after this release, and the +configuration interface has already been frozen for a while. I will wait a +couple of weeks for bug reports, and if nothing serious is found, the new +stable Ion should finally be released then. + +The most notable changes since the previous release are: + + * The dock module is included + * An optional autoconf script was added + * A few minor bugs were fixed + * Some incomplete features were polished, especially focus control on + floatws, and: + * Changes in X keyboard map are supported now (so e.g. switching to + dvorak after Ion has started should update the bindings to match the + locations of symbols in the dvorak layout). + + +About the version numbering scheme: + +Due to a demand of a version numbering scheme more indicative of the status +of the project, I was thinking of various different new version numbering +schemes for this release: ion-2rc1 (then 2r1, 3d1, etc.), ion-2-20040114-rc1, +ion-2.20040114rc1, ion-20040114-2rc1, etc.). It would've been nice if +simple lexicographical sorting could be used to order the packages, but +in the end I decided to stick with a scheme that is consistent with the +'ion-devel' package names: + + project-branch_and_status-release_date + +Therefore this release is 'ion-2rc-20040114', and the "stable" one will be +'ion-2-20040???'. + + +ion-devel-20031211 +------------------ + +This is a big clean-up release. The most noticeable changes are that +'-devel' has been removed from path names, user configuration files go +in `~/.ion2/` and the main configuration file is 'ion.lua'. (The other +`ioncore-*.lua` files have also been renamed.) There have been no notable +changes in the configuration files themselves, so your old files will +work if you move them to the correct directory and rename the changed +files. (`mv ~/.ion-devel ~/.ion2; cd ~/.ion2; mv ioncore.lua ion.lua;` +etc.) + +This release also finally contains a working PWM binary. (The ioncore+ +scripts scheme was replaced with separate binaries statically linked +to ioncore.a.) Floating workspaces now support edge snapping and +sticky windows, but some PWM features are probably still missing. + +Some bugs were also fixed and users are now force-fed the manual page +the first time Ion is started. Transients can be toggled between +top/bottom of the main window with Mod1+K T. The SSH query uses +`~/.ssh/known_hosts` for completion instead of a manually defined list. + +There may be a few additions (e.g. the dock) and bug fixes, of course, +before the stable release, but this release should pretty much be what +the new "stable" Ion should look like. Please upgrade to it to help +weed out the bugs. Note that you need to upgrade Lua to the 5.0.1 +pre-release. + + +ion-devel-20031119 +------------------ + + +It's finally time for a new release on Ion's development branch. Most +likely this will also be the last "big" release before finally, after +almost two years, releasing a new "stable" version of Ion. A few bug +fix and code clean-up releases should appear in between, though. + +The most important additions, changes and non-changes in this release +are: + + * Menu module + * A little less broken extended character set and string encoding + support + * Extended WM hints fullscreen request support + * Hopefully a little clarified configuration file layout + * Quite a few fixes + * And most important of all: + +> Configuration files written for the previous release should +> still work this time! + +(However, Ion will complain of old drawing engine styles.) + +The menu module provides both drop-down menus (in the stock configuration +pressing Button3 on a tab should show a context menu) and query-like +"in-mplex" menus (F12: main menu at screen level; Mod1+M: the same context +menu as above at frame level). + +What was done enhance support for strings (mostly window titles) in +languages that need more than 8-bit character sets is: + + * Remove UTF8 restriction and support almost arbitrary multibyte + encodings instead. (Statefull encodings will not work and combining + characters can cause clutter. Both are a sign of bad encoding, IMHO.) + + * The XCreateFontSet routine that is used to load fonts in the kind + of structure the X utf8 and multibyte routines want apparently wants + to be able to load glyphs for all character sets specified in the + locale and therefore often fails if only single font is specified. + Therefore a kludge was added that tries loading more fonts while + keeping the fonts' size the same. (The ",*" kludge could load huge + fonts). Unfortunately this has a noticeable effect in startup time :(. + + * There is no longer a system.mk option to enable utf8 (now multibyte) + support, but must still specifically be enabled with the -i18n + command line switch, mostly thanks to troublesome utf-8 locales. + +Because Xlib's UTF-8 string drawing code is broken and very unlikely to +be fixed, decent text output in an UTF-8 locale is still unlikely and +dependent on the fonts loaded in the system. The Xmb routines that are now +used always use an iso10646-1 font even if other fonts are loaded and the +unicode font does not contain a particular glyph to be drawn. The use of +the Xutf8 routines in an utf8 locale can be enabled with the +`CF_DE_USE_XUTF8` compile-time option. The advantage of this is that +these routines seem to choose the font to draw particular character in +most cases more sanely. The downside is that there are other more serious +problems with unknown characters. + +All in all, even if I'd like to support a universal move to UTF-8, thanks +to Xlib brokenness I can't really recommend using UTF-8 locales with Ion +and the default drawing engine if there's a decent alternative encoding. +Most if not all other stateless encodings shouldn't have the problems +UTF-8 has. An alternative drawing engine that didn't use the Xlib i18n +string drawing routines might solve the troubles with utf-8 support. + +One more note: + +If you have saved a custom system.mk, you'll need to set LUAC +point to the Lua compiler there as some of the share/ files +are compiled now. + + +ion-devel-20030810 +------------------ + +It's been a while since the previous release as I wanted to freeze the +Lua configuration/scripting interface for this release and therefore +finish work on a few things. Well, on my part the interface is frozen, +but I will still accept constructive complaints for a short time and +after that the implemented parts of the scripting interface will be +frozen. But in all likelihood, if no one has anything else to say, but +"it's ok", "it sucks", the interface will no longer change, only possibly +grow. + +What's new for this release then? + + * Drawing engine module support. + * OO-style exported functions. The old `class_fn` functions are now + `WClass.fn` (an ugly wrapper is provided in `compat.lua`). + * No more screen-specific configuration or savefiles. + * Session name (can be specified on command line) instead of display + name based savefiles. Also affects query history and not just workspace + saves. + * Bug fixes, most small and one bigger (almost complete rewrite of + the split resizing algorithm). + * Some documentation improvements. + +Converting configuration files. + +Old `.lua` colour schemes and workspace save files can be automatically +converted to a format suitable to be loaded by this latest release. +Other files will have to be ported manually. To convert colour schemes, +use the script `utils/lookconv.lua`. The usage is + + lookconv.lua look-old.lua > look-new.lua + +To convert workspaces savefiles, use the script `utils/saveconv.sh`. +The usage is + + saveconf.sh ~/.ion-devel/saves/workspaces-DISPLAY.*.lua \ + > ~/.ion-devel/SESSIONNAME/workspaces.lua + +`DISPLAY` here is the actual display part of `$DISPLAY`, probably just `:0`. +'*' stands for all screens (if you have only one screen you could do with +perhaps just `workspaces-:0.0.lua`). SESSIONNAME is the name of the session +where you want to use the converted savefiles. Default session name for +`DISPLAY` is `default-session-DISPLAY` with the colon in `DISPLAY` converted +to a dash. For most people this is `default-session--0`. In the simplest +case the whole command line is therefore + + saveconv.sh ~/.ion-devel/saves/workspaces-:0.0.lua \ + > ~/.ion-devel/default-session--0/workspaces.lua + +The scripts may or may not work and are only provided as a potential +convenience that will not be maintained and will be removed eventually. + + +ion-devel-20030623 +------------------ + +Quite a few bug fixes and one most likely unnoticeable improvement: + +2003-06-23: + + * Fixed pointer warping on screen change. + * A bug in grab handler calling code could crash Ion when leaving + keyboard resize mode manually. + * Resize display was showing incorrect values for keyboard resize. + +2003-06-21: + + * Client window last height request bookkeeping code had been lost + when configure request policy was changed. This caused transient + sizes to be calculated incorrectly. + * Return from full screen mode to floatws had been broken. + * As the number of dynamic functions has been getting bigger, the + functions are now sorted on first use and then binary-searched + instead of naive linear searching. + * Screen lookup had been broken for windows that are not properly + on any screen. + + +ion-devel-20030620 +------------------ + +Just some bug fixes and minor behavioural changes and improvements in +this release: + + +ion-devel-20030617 +------------------ + +This release fixes some small problems with the previous release and +adds a workaround kludge for the XFree86 textprop bug (it's been fixed +but no release with the fix is available) that could cause Opera to +crash Ion when UTF8 support was enabled. I also added the winprop +needed for galeon's find dialog to default `kludges.lua` and there's +some extra documentation and defaults for some systems in system.mk. + + +ion-devel-20030614 +------------------ + +Most changes in this release centre around making Ion more tolerant +to broken configuration files; for details see the ChangeLog. There +are also changes in binding configuration as I already mentioned in +an earlier poting. Namely `common-frame-bindings.lua` is gone and the +bindings previously set there (into variables that the `*ws.lua` +files later referenced) were moved to `ioncore-bindings.lua` and are +set using the new functions `mplex_bindings` and `genframe_bindings` +are were added. Old modified configuration files should still work, +however, but if you use `make_active_leaf_fn`, you will need to +include `compat.lua` as the `global_bindings` that used this were +replaced by bindings in `mplex_bindings` and the function +`make_current_clientwin_fn`. + +There are also some (minor) bug fixes and a few other improvements +worth a mention. In particular all regions are now given names of +the form `WFoobar` by default and `DEFAULT_MOD.."F9"` was bound to +create a new workspace without asking for a name. This binding and +`QueryLib.query_workspace` use the workspace type defined in the +variable `default_ws_type` instead of being hardcoded to `WIonWS`. + +All objects passed to Lua now have a unique userdata (a `WWatch` +cached in a weak table) so they can be used e.g. as indices to tables. + + +ion-devel-20030606 +------------------ + +This release unifies some parts of `WScreen` and `WGenFrame`, which makes +screen-level queries possible. (Later the queries for small frames might +be changed to be shown at screen level.) The statusbar restriction from +ion-devel-20030531 was also removed by this some change and a few +non-fatal bugs have been fixed. + + +ion-devel-20030601 +------------------ + +A bug was discovered: + +2003-06-01: + + * An off-by-one error in `extl_l1_finalize` caused references to some + Lua tables (including large completions) never to be released. + +It could be that this bug was causing some other errors in Lua as well. +Sometimes QueryLib bindings failed because some function generators +returned nil although they shouldn't. Adding dummy lines (!) in those +functions fixed the problem and for some reason this patch also seemed +to remove those problems. + + +One more note: as `ioncorelib.lua` and `querylib.lua` are now installed +in `$SHAREDIR`, you must remove the old files in `$ETCDIR` if installing +over a previous release or else there will be errors. You should also +not use any possible old copies you have in `~/.ion-devel/`. (One of the +reasons for moving these to `$SHAREDIR` is to stop users from making +copies of them as they are not configuration files.) + + +ion-devel-20030531 +------------------ + +As the subject line says, Ion-devel-20030531 was just released. The short +list of changes is: + + * The license was changed from the Clarified Artistic License to the + GNU Library/Lesser General Public License (LGPL). + * Screen, viewport and root window renames and other changes (see below). + * Some installation directory changes; the ion-* helper programs are now + installed either in `$SHAREDIR` or `$EXTRABINDIR` and QueryLib searches + for them on the script path (`~/.ion-devel/`, `$ETCDIR`, `$SHAREDIR`, + `$EXTRABINDIR`) instead of assuming them being on `$PATH`. + * Client windows are now in a separate namespace. + * Resize/maximize/shade changes: Shading should work on `WIonFrame`s too + and shade mode is automatic when the client area gets too small when + resizing. Maximize toggle restores frame to previous size if shaded + instead of maximizing. The move/resize mode bindings were changed again + to be more consistent and predictable: Left/Right/Up/Down and F/B/P/N + grow the frame in the specific direction, Shift+keys shrink and in + case of floating frames, `DEFAULT_MOD+keys` move + * Returning from full screen mode should work on `WFloatWS`s too. + * Lots of minor fixes, export additions and clean-up. + +As usual, see the (long!) list of new ChangeLog entries at the end of +this message for details. + + +I've finally personally switched from the old "stable" 20020207 to using +the latest development release at home too and I must say that it is +finally starting to look good. There are still things to be written, but +unless you need "proven" stability, I see no reason to sticking to that +old release anymore. Unless, of course, if you find a problem that I have +not encountered or simply have no need for the new features and don't want +to port your configuration files. + + +On screens, viewports, root windows and problems with the new +implementation + +The objects previously called "screens" are now called "root windows" +and what were called "viewports" are called "screens" to better reflect +what the user sees. (When Xinerama is not used there's no difference +between a root window and a screen, but when Xinerama is used a root +window may be split over multiple screens.) + +This release also creates so-called virtual root windows for each +Xinerama screen when there are more than one. This is to better separate +the windows on different screens and thus emulate normal multihead, the +main difference being that windows can be moved between screens. +Especially the virtual roots are there to keep the windows that are on +a floatws on the right screen. + +Virtual root windows will, however, break a few apps: + + * Mozilla -remote won't work because of its crappy method for looking + up existing windows that can't handle multiple levels of WM windows. + Nested workspaces have the same problem. There is, however, a simple + solution to this problem: gnome-moz-remote. It seems to use a saner + method for looking up an existing Gecko browser and also works with + at least Phoenix/MozillaFirebird and Galeon. + +These two are problems only if you use floatws:s (other problems with +which virtual root windows are intended to solve): + + * The background-setting apps I am aware of will require a following + 'xrefresh' for the changes to be updated to the (transparent) virtual + roots. Background setting apps that support the `_NET_VIRTUAL_ROOTS` + property (which, I think, was meant for root window-sized WS + backgrounds) could be used to set a separate background for each + Xinerama screen, though. I am not aware of any such app. + * Some apps' resize and move features will behave erratically on those + Xinerama screens not at (0, 0) on the root window. This is again + because the apps are assuming there's at most one WM window between + them and the root and are requesting windows' positions incorrectly. + The ICCCM is quite vague on this and I think _all_ apps that I have + tried are doing it wrong but the way e.g. XMMS does it certainly is + not an interpretation of the ICCCM. The apps that now behave correctly + request position for the outermost WM window. Nested workspaces have + the same problem. + +There is one more temporary problem: + + * Status bar modules (and the dock module's statusbar mode) will be + *temporarily* broken when virtual roots are _not_ used. This will be + fixed later when parts of screen and frame code are unified (it should + then be possible to run Ion without any workspace modules and have + queries attached to screens). In the meanwhile, if you don't care about + the above-mentioned problems and want a statusbar (which probably needs + to be ported to this release), you can defined `CF_ALWAYS_VIRTUAL_ROOT` + when compiling Ion. + + +ion-devel-20030510 +------------------ + +There are quite a few small fixes and minor enhancements invisible to +the user in this release. The splitting functions were renamed to be +more consistent and there are a couple of enhancements in the Lua code +query that should make it much more usable. First, tab-completion can +now descend into tables and complete subexpressions. Secondly, the local +variable `_` in addition to 'arg[1]' is set to point to the the frame in +which the query is executing. + +I have also written some new documentation that is now available from +the Ion web page. + + +ion-devel-20030506 +------------------ + +The most notable change in this release is that the `target_id` system +was removed and instead client window status is also saved over +restarts in the saves/workspaces-* files. (It shouldn't be too hard +to hook a session management module over this.) Thanks to this +modification, floatframes can also save their status now. Note +that if you restart from older version of Ion to this one, client +window layout will be messed up. + +There are also a few bug fixes and code to save and load line editor +history was added. + + +ion-devel-20030503 +------------------ + +There are lots of minor improvements in this release, see the +new ChangeLog entries for details. + + +ion-devel-20030427 +------------------ + +Some minor feature enhancements and a few fixes in this new release: + +The file system scanning completions are now put in the background +by receiving the data from external programs through pipes and select() +for data in the main event loop. This way long taking completions don't +block Ion from processing other events and NFS problems shouldn't hang +it. + +Basic window stacking management support code was added. Transients +on floatws:s should now be stacked above their parents. If you don't +want to change your modified binding configurations at this point and +need the functions `floatframe_raise/lower`, include `compat.lua`. + + +ion-devel-20030412 +------------------ + +This release again fixes some minor problems and enhances a few +features. Most of the enhancements are related to UTF8 support and +the floatws module. If UTF8 support is enabled, Ion now tries to +load the "fixed" font at startup after setting up locales. If locales +aren't properly set up, this probably fails and Ion will reset locale +back to "POSIX" as this might make fonts loadable although support for +non-ASCII characters will be crippled. It might now be safe for +package maintainers to enable UTF8 support by default + +As apps seem to have switched to using `_NET_WM_NAME` for UTF8 titles +and filling `WM_NAME` with crap, this property is also preferred over +`WM_NAME` if set. Support for some other "extended" WM hints might be +added in the future but I have no intentions of moking Ion NetWM- +compliant. I might have thought of attempting to do so a few years +ago when I last read the specification but it seems that since then +they've filled it with lots of pointless bloat that may even be in +opposition to Ion's goals. Take multi-parent transients, for example. + +Full error log is also displayed with xmessage on startup whether +it is possible to continue or not. + + +ion-devel-20030410 +------------------ + +There was a bug in QueryLib written yesterday, therefore this release. + + +ion-devel-20030409 +------------------ + +I started converting the query code to Lua and discovered some rather +silly bugs in the Lua interface so here's a new release with the new +and improved query code included. The names of queries have changed +(they're all in the table QueryLib defined in `querylib.lua` -- only +`query_query` and a few temporary handlers are implemented in query.so) +so your modified configuration files will be broken again :(. + + +ion-devel-20030408 +------------------ + +Be prepared to completely rewrite your configuration files once +again: a new version of Ion-devel has been released that uses Lua + for all configuration. Version 5.0 of the +language/library is required. Although scripting possibilities +are now much better than before, there is one rather big drawback: +Lua is not nearly as error-tolerant as Ion's old configuration +parser so a syntax error in a file will cause the file not to be +executed and may even cause Ion not to be able to start. More +comments and documentation on scripting will follow at a later time. + +Libtool and libltdl are also now used to implement module support. +Hopefully this will make compiling Ion easier on a wider range of +platforms. (On the other hand, this could also induce new problems.) + +Finally, the preferred linking address for Ion's home page is now +. The pages are still located at the +old address, but this redirected address should be more permanent. + + +ion-devel-20030327 +------------------ + +Just some minor fixes, binary rename and better embedded workspace +support in this release. I'm still contemplating whether to convert +Ion to C++ and haven't written many things that I intended to as +that decision will affect how those are implemented. + +To create an embedded workspaces in a frame, at the moment you have +to modify the appropriate workspace save file (when Ion is not running!) +by adding lines such as + + region "WFloatWS", "testiupotus" { + } + +inside the frame definition after the other options (flags, `target_id`; +those options will be ignored if after any region definitions). + +One more thing to note: debugging infos are no longer automatically +stripped from the ioncore binary or modules by 'make install' so that +I don't have to explain how to get proper backtraces every time a bug +is found. If you you are confident that you can send me proper backtraces +when you find a bug (or maybe can fix it yourself), you can make the +binary and the modules a _lot_ smaller by running 'strip' on them. + + +ion-devel-20030311 +------------------ + +The list of new ChangeLog entries is long this time, but I'm finally +starting to get to what I started working on almost two years ago. Yes, +there is *experimental* support for PWM-like "floating frame" workspaces. +A lot of the functionality is still missing -- no need to complain of +such at the time -- but the floatws.so module and basic functionality +is there. Most notably perhaps a menu module is not yet implemented. +(Porting the PWM menu code should a nice little task for whomever +interested... *wink*.) To create some PWM workspaces, load the floatws.so +module (should be loaded by default) and in `query_workspace` (F9) +prefix workspace name with 'WFloatWS:' (or load the module before +ionws.so). + +This release is also finally fully modularised: In addition to the +ion core binary that can not function alone, there are the modules +ionws.so, floatws.so and query.so. It is also possible statically +compile the modules in the core binary if the system doesn't support +libdl. + +There's also *experimental* UTF8 support that must be specifically +enabled from system.mk. You must have XFree86 (4.x?) and C99 wide +char support available (either libc directly or maybe libutf8+libiconv). +Thanks to bugs (?) in some of the XFree86 Xutf8 functions, your locales +must be properly set up or else X will stop drawing strings at non-ascii +characters instead of ignoring them. See my earlier rants on the mailing +list for reasons on not using the more standard Xmb functions. +To actually see any special characters, you must load the necessary fonts +by specifying a comma-separated list of fonts to the font and `tab_font` +draw.conf options. Multiple font loading does not work when Xft support +is enabled at the time being. + +There has been a lot of changes in the config files, again, and I can +promise there's still more to come. See the ChangeLog for details. +If you want to use your old workspaces configurations, move them to +`~/.ion-devel/saves` and replace the strings "WFrame" and "WWorkspace" +with "WIonFrame" and "WIonWS", respectively. + + +ion-devel-20030225 +------------------ + +I've made the bugfix release as promised. Get it from the usual place. + + +ion-devel-20030223 +------------------ + +There's a new major development release available from Ion web page. +Not many changes are visible to the end user, though, but a lot was +rewritten to be more flexible and simpler; see the ChangeLog for a +full account. The older development release is also still available +because this release can not be considered as stable after major +changes to the code. The most visible changes are + + * Changes in binding configuration + * A (mostly) working full screen mode toggle (see below) + * The ability to switch workspaces while dragging tabs (experimental) + * The ability to re-read draw.conf without restarting Ion + * Some bug fixes. + + +Some notes on full screen mode toggle: + + * Ion doesn't detect programs trying to leave full screen mode--it + should be possible to devise some method using window properties, + however. + * At least Opera unmaps the window also before changing the size to + enter full screen mode, so Ion doesn't remember the last frame. + * Mozilla sometimes has trouble entering full screen mode but when it + succeeds, Ion remembers the last frame. However, if + `clientwin_toggle_fullscreen` is used to leave the full screen mode + started from Mozilla, Mozilla doesn't know that the mode has been + left. + +Clearly there should be some method of communication between Ion and the +programs for full screen toggles to fully work. At the moment I suggest +using `clientwin_toggle_fullscreen` instead of the programs' native toggles +unless the program has some special full screen mode and you really want +it. diff --git a/TODO.README b/TODO.README new file mode 100644 index 0000000..d02825c --- /dev/null +++ b/TODO.README @@ -0,0 +1,5 @@ + +The TODO.riot file containg an Ion TODO list has been created with the +riot outliner available from . The file is, +however, just an mbox file, so you can read it with your favourite +threading mail user agent. For example: mutt -f TODO.riot diff --git a/TODO.riot b/TODO.riot new file mode 100644 index 0000000..efe689f --- /dev/null +++ b/TODO.riot @@ -0,0 +1,759 @@ +From background-static Thu Mar 23 17:30:06 EET 2006 +Message-Id: +Date: Thu Mar 23 17:30:06 EET 2006 +Status: RO +X-Riot-Version: 1ds-yyyymmdd +From: Riot +X-Riot-Edited: Thu Mar 23 17:30:06 EET 2006 +Content-Type: text/plain; charset=utf-8 +Subject: Ion TODO list + +Ion TODO list + +From background-static Thu Mar 23 18:08:00 EET 2006 +Message-Id: +Date: Thu Mar 23 18:08:00 EET 2006 +Status: RO +X-Riot-Version: 1ds-yyyymmdd +From: Riot +X-Riot-Edited: Thu Mar 23 18:08:12 EET 2006 +Content-Type: text/plain; charset=utf-8 +Subject: Core/general +In-Reply-To: + +Core/general + +From background-static Thu Mar 23 17:31:43 EET 2006 +Message-Id: +Date: Thu Mar 23 17:31:43 EET 2006 +Status: RO +X-Riot-Version: 1ds-yyyymmdd +From: Riot +X-Riot-Edited: Thu Mar 23 17:31:46 EET 2006 +Content-Type: text/plain; charset=utf-8 +Subject: Startup sequence support +In-Reply-To: + +Startup sequence support + +Support for the FDO startup notification specification should be +added. Ion should arrange things in the right frame by LAUNCHED_BY. + +The specification is at + + + +From background-static Thu Mar 23 17:41:20 EET 2006 +Message-Id: +Date: Thu Mar 23 17:41:20 EET 2006 +Status: RO +X-Riot-Version: 1ds-yyyymmdd +From: Riot +X-Riot-Edited: Thu Mar 23 17:41:20 EET 2006 +Content-Type: text/plain; charset=utf-8 +Subject: Better support for the few applicable EWMH hints +In-Reply-To: + +Better support for the few applicable EWMH hints + +From background-static Thu Mar 23 17:52:17 EET 2006 +Message-Id: +Date: Thu Mar 23 17:52:17 EET 2006 +Status: RO +X-Riot-Version: 1ds-yyyymmdd +From: Riot +X-Riot-Edited: Sat Apr 22 19:08:23 EEST 2006 +Content-Type: text/plain; charset=utf-8 +Subject: Numbered tabs +In-Reply-To: + +Numbered tabs + +Either togglable, or perhaps the numbers should be shown when +a prefix of the key combo to switch tabs has been entered. +The latter will be quite more complicated to implement. + +Done: basic toggleable numbering. + +From background-static Thu Mar 23 18:00:25 EET 2006 +Message-Id: +Date: Thu Mar 23 18:00:25 EET 2006 +Status: RO +X-Riot-Version: 1ds-yyyymmdd +From: Riot +X-Riot-Edited: Thu Mar 23 18:00:25 EET 2006 +Content-Type: text/plain; charset=utf-8 +Subject: Some way to load/initialise inherited drawing engines properly. +In-Reply-To: + +Some way to load/initialise inherited drawing engines properly. + +From background-static Thu Mar 23 18:04:35 EET 2006 +Message-Id: +Date: Thu Mar 23 18:04:35 EET 2006 +Status: RO +X-Riot-Version: 1ds-yyyymmdd +From: Riot +X-Riot-Edited: Thu Mar 23 18:04:35 EET 2006 +Content-Type: text/plain; charset=utf-8 +Subject: New hooks? +In-Reply-To: + +New hooks? + +From background-static Thu Mar 23 18:10:34 EET 2006 +Message-Id: +Date: Thu Mar 23 18:10:34 EET 2006 +Status: RO +X-Riot-Version: 1ds-yyyymmdd +From: Riot +X-Riot-Edited: Thu Mar 23 18:10:34 EET 2006 +Content-Type: text/plain; charset=utf-8 +Subject: Tiled workspaces +In-Reply-To: + +Tiled workspaces + +From background-static Thu Mar 23 17:40:03 EET 2006 +Message-Id: +Date: Thu Mar 23 17:40:03 EET 2006 +Status: RO +X-Riot-Version: 1ds-yyyymmdd +From: Riot +X-Riot-Edited: Thu Mar 23 17:40:03 EET 2006 +Content-Type: text/plain; charset=utf-8 +Subject: A general solution to transients, queries and menus in tiny frames. +In-Reply-To: + +A general solution to transients, queries and menus in tiny frames. + +From background-static Thu Mar 23 17:40:08 EET 2006 +Message-Id: +Date: Thu Mar 23 17:40:08 EET 2006 +Status: RO +X-Riot-Version: 1ds-yyyymmdd +From: Riot +X-Riot-Edited: Thu Mar 23 17:40:10 EET 2006 +Content-Type: text/plain; charset=utf-8 +Subject: PaneWS +In-Reply-To: + +PaneWS + +From background-static Thu Mar 23 17:40:13 EET 2006 +Message-Id: +Date: Thu Mar 23 17:40:13 EET 2006 +Status: RO +X-Riot-Version: 1ds-yyyymmdd +From: Riot +X-Riot-Edited: Thu Mar 23 17:40:13 EET 2006 +Content-Type: text/plain; charset=utf-8 +Subject: Lots of fine-tuning +In-Reply-To: + +Lots of fine-tuning + +From background-static Thu Mar 23 17:50:02 EET 2006 +Message-Id: +Date: Thu Mar 23 17:50:02 EET 2006 +Status: RO +X-Riot-Version: 1ds-yyyymmdd +From: Riot +X-Riot-Edited: Thu Mar 23 17:50:22 EET 2006 +Content-Type: text/plain; charset=utf-8 +Subject: Pane handles should allow resizing with the rodent. +In-Reply-To: + +Pane handles should allow resizing with the rodent. +(You know, those things on the sides of floating splits.) + +From background-static Thu Mar 23 18:09:58 EET 2006 +Message-Id: +Date: Thu Mar 23 18:09:58 EET 2006 +Status: RO +X-Riot-Version: 1ds-yyyymmdd +From: Riot +X-Riot-Edited: Thu Mar 23 18:10:27 EET 2006 +Content-Type: text/plain; charset=utf-8 +Subject: mod_statusbar and ion-statusd +In-Reply-To: + +mod_statusbar and ion-statusd + +From background-static Thu Mar 23 17:36:11 EET 2006 +Message-Id: +Date: Thu Mar 23 17:36:11 EET 2006 +Status: RO +X-Riot-Version: 1ds-yyyymmdd +From: Riot +X-Riot-Edited: Thu Mar 23 17:36:11 EET 2006 +Content-Type: text/plain; charset=utf-8 +Subject: Multi-line statusbar +In-Reply-To: + +Multi-line statusbar + +From background-static Thu Mar 23 17:36:44 EET 2006 +Message-Id: +Date: Thu Mar 23 17:36:44 EET 2006 +Status: RO +X-Riot-Version: 1ds-yyyymmdd +From: Riot +X-Riot-Edited: Thu Mar 23 17:39:08 EET 2006 +Content-Type: text/plain; charset=utf-8 +Subject: Better control of layout etc. +In-Reply-To: + +Better control of layout etc. + +From background-static Thu Mar 23 17:39:20 EET 2006 +Message-Id: +Date: Thu Mar 23 17:39:20 EET 2006 +Status: RO +X-Riot-Version: 1ds-yyyymmdd +From: Riot +X-Riot-Edited: Thu Mar 23 17:39:20 EET 2006 +Content-Type: text/plain; charset=utf-8 +Subject: mpress support for meters or other areas. +In-Reply-To: + +mpress support for meters or other areas. + +From background-static Thu Mar 23 17:54:07 EET 2006 +Message-Id: +Date: Thu Mar 23 17:54:07 EET 2006 +Status: RO +X-Riot-Version: 1ds-yyyymmdd +From: Riot +X-Riot-Edited: Thu Mar 23 17:54:07 EET 2006 +Content-Type: text/plain; charset=utf-8 +Subject: Communicating configuration to statusd shouldn't use temp file +In-Reply-To: + +Communicating configuration to statusd shouldn't use temp file + +From background-static Thu Mar 23 18:11:44 EET 2006 +Message-Id: +Date: Thu Mar 23 18:11:44 EET 2006 +Status: RO +X-Riot-Version: 1ds-yyyymmdd +From: Riot +X-Riot-Edited: Thu Mar 23 18:11:44 EET 2006 +Content-Type: text/plain; charset=utf-8 +Subject: mod_query +In-Reply-To: + +mod_query + +From background-static Thu Mar 23 17:40:27 EET 2006 +Message-Id: +Date: Thu Mar 23 17:40:27 EET 2006 +Status: RO +X-Riot-Version: 1ds-yyyymmdd +From: Riot +X-Riot-Edited: Thu Mar 23 17:40:35 EET 2006 +Content-Type: text/plain; charset=utf-8 +Subject: mod_query.message should handle tabs properly +In-Reply-To: + +mod_query.message should handle tabs properly + +From background-static Thu Mar 23 18:11:52 EET 2006 +Message-Id: +Date: Thu Mar 23 18:11:52 EET 2006 +Status: RO +X-Riot-Version: 1ds-yyyymmdd +From: Riot +X-Riot-Edited: Thu Mar 23 18:11:52 EET 2006 +Content-Type: text/plain; charset=utf-8 +Subject: mod_dock +In-Reply-To: + +mod_dock + +From background-static Thu Mar 23 17:58:56 EET 2006 +Message-Id: +Date: Thu Mar 23 17:58:56 EET 2006 +Status: RO +X-Riot-Version: 1ds-yyyymmdd +From: Riot +X-Riot-Edited: Thu Mar 23 17:58:56 EET 2006 +Content-Type: text/plain; charset=utf-8 +Subject: There are some problems with user geometries of docked apps. +In-Reply-To: + +There are some problems with user geometries of docked apps. + +From background-static Thu Mar 23 18:12:02 EET 2006 +Message-Id: +Date: Thu Mar 23 18:12:02 EET 2006 +Status: RO +X-Riot-Version: 1ds-yyyymmdd +From: Riot +X-Riot-Edited: Thu Mar 23 18:12:02 EET 2006 +Content-Type: text/plain; charset=utf-8 +Subject: mod_mgmtmode +In-Reply-To: + +mod_mgmtmode + +From background-static Thu Mar 23 18:04:54 EET 2006 +Message-Id: +Date: Thu Mar 23 18:04:54 EET 2006 +Status: RO +X-Riot-Version: 1ds-yyyymmdd +From: Riot +X-Riot-Edited: Thu Mar 23 18:04:54 EET 2006 +Content-Type: text/plain; charset=utf-8 +Subject: An actual management mode for mod_mgmtmode. +In-Reply-To: + +An actual management mode for mod_mgmtmode. + +From background-static Thu Mar 23 18:11:20 EET 2006 +Message-Id: +Date: Thu Mar 23 18:11:20 EET 2006 +Status: RO +X-Riot-Version: 1ds-yyyymmdd +From: Riot +X-Riot-Edited: Thu Mar 23 18:11:20 EET 2006 +Content-Type: text/plain; charset=utf-8 +Subject: Drawing engine(s) +In-Reply-To: + +Drawing engine(s) + +From background-static Thu Mar 23 18:02:32 EET 2006 +Message-Id: +Date: Thu Mar 23 18:02:32 EET 2006 +Status: RO +X-Riot-Version: 1ds-yyyymmdd +From: Riot +X-Riot-Edited: Thu Mar 23 18:02:32 EET 2006 +Content-Type: text/plain; charset=utf-8 +Subject: There should be a sticky attribute for the drawing engine +In-Reply-To: + +There should be a sticky attribute for the drawing engine +for sticky floatws frames. + +From background-static Thu Mar 23 18:06:40 EET 2006 +Message-Id: +Date: Thu Mar 23 18:06:40 EET 2006 +Status: RO +X-Riot-Version: 1ds-yyyymmdd +From: Riot +X-Riot-Edited: Thu Mar 23 18:06:40 EET 2006 +Content-Type: text/plain; charset=utf-8 +Subject: There are some problems with transparent backgrounds of tabs, IIRC. +In-Reply-To: + +There are some problems with transparent backgrounds of tabs, IIRC. + +From background-static Thu Mar 23 18:07:53 EET 2006 +Message-Id: +Date: Thu Mar 23 18:07:53 EET 2006 +Status: RO +X-Riot-Version: 1ds-yyyymmdd +From: Riot +X-Riot-Edited: Thu Mar 23 18:07:53 EET 2006 +Content-Type: text/plain; charset=utf-8 +Subject: Documentation +In-Reply-To: + +Documentation + +From background-static Sat Sep 16 14:14:23 EEST 2006 +Message-Id: +Date: Sat Sep 16 14:14:23 EEST 2006 +Status: RO +X-Riot-Version: 1ds-20060502 +From: Riot +X-Riot-Edited: Sat Sep 16 14:14:23 EEST 2006 +Content-Type: text/plain; charset=utf-8 +Subject: More of it +In-Reply-To: + +More of it + +From background-static Thu Mar 23 17:33:17 EET 2006 +Message-Id: +Date: Thu Mar 23 17:33:17 EET 2006 +Status: RO +X-Riot-Version: 1ds-yyyymmdd +From: Riot +X-Riot-Edited: Thu Mar 23 17:49:42 EET 2006 +Content-Type: text/plain; charset=utf-8 +Subject: Random ideas to try (semi-TODO) + +Random ideas to try (semi-TODO) + +Just some stuff here for interested people to try. No guarantees +that even a good patch will ever be accepted though. + +From background-static Thu Mar 23 18:06:20 EET 2006 +Message-Id: +Date: Thu Mar 23 18:06:20 EET 2006 +Status: RO +X-Riot-Version: 1ds-yyyymmdd +From: Riot +X-Riot-Edited: Thu Mar 23 18:06:20 EET 2006 +Content-Type: text/plain; charset=utf-8 +Subject: Movement threshold would be better for floating splits, raise delay +In-Reply-To: + +Movement threshold would be better for floating splits, raise delay +is just an ugly hack. This is just not implementable in a nice way +without proper translucency support. + +From background-static Thu Mar 23 18:07:15 EET 2006 +Message-Id: +Date: Thu Mar 23 18:07:15 EET 2006 +Status: RO +X-Riot-Version: 1ds-yyyymmdd +From: Riot +X-Riot-Edited: Thu Mar 23 18:07:15 EET 2006 +Content-Type: text/plain; charset=utf-8 +Subject: Composition manager and all the nice stuff possible with it +In-Reply-To: + +Composition manager and all the nice stuff possible with it + +From background-static Thu Mar 23 18:05:04 EET 2006 +Message-Id: +Date: Thu Mar 23 18:05:04 EET 2006 +Status: RO +X-Riot-Version: 1ds-yyyymmdd +From: Riot +X-Riot-Edited: Thu Mar 23 18:05:04 EET 2006 +Content-Type: text/plain; charset=utf-8 +Subject: Key grabs from scripts? +In-Reply-To: + +Key grabs from scripts? + +From background-static Thu Mar 23 18:01:56 EET 2006 +Message-Id: +Date: Thu Mar 23 18:01:56 EET 2006 +Status: RO +X-Riot-Version: 1ds-yyyymmdd +From: Riot +X-Riot-Edited: Thu Mar 23 18:01:56 EET 2006 +Content-Type: text/plain; charset=utf-8 +Subject: Dock resizing improvements? +In-Reply-To: + +Dock resizing improvements? + +From background-static Thu Mar 23 18:01:40 EET 2006 +Message-Id: +Date: Thu Mar 23 18:01:40 EET 2006 +Status: RO +X-Riot-Version: 1ds-yyyymmdd +From: Riot +X-Riot-Edited: Thu Mar 23 18:01:40 EET 2006 +Content-Type: text/plain; charset=utf-8 +Subject: Perhaps dock/statusbar should be able to use a different style +In-Reply-To: + +Perhaps dock/statusbar should be able to use a different style +on floatws. + +From background-static Thu Mar 23 18:01:23 EET 2006 +Message-Id: +Date: Thu Mar 23 18:01:23 EET 2006 +Status: RO +X-Riot-Version: 1ds-yyyymmdd +From: Riot +X-Riot-Edited: Thu Mar 23 18:01:23 EET 2006 +Content-Type: text/plain; charset=utf-8 +Subject: Make lines with other frame splits "sticking" for resize. +In-Reply-To: + +Make lines with other frame splits "sticking" for resize. + +From background-static Thu Mar 23 18:00:54 EET 2006 +Message-Id: +Date: Thu Mar 23 18:00:54 EET 2006 +Status: RO +X-Riot-Version: 1ds-yyyymmdd +From: Riot +X-Riot-Edited: Thu Mar 23 18:00:54 EET 2006 +Content-Type: text/plain; charset=utf-8 +Subject: Better support for nesting workspaces etc. +In-Reply-To: + +Better support for nesting workspaces etc. + +From background-static Thu Mar 23 17:59:40 EET 2006 +Message-Id: +Date: Thu Mar 23 17:59:40 EET 2006 +Status: RO +X-Riot-Version: 1ds-yyyymmdd +From: Riot +X-Riot-Edited: Thu Mar 23 17:59:40 EET 2006 +Content-Type: text/plain; charset=utf-8 +Subject: Maybe parent window should shrink out of the way of transients +In-Reply-To: + +Maybe parent window should shrink out of the way of transients +at least to some threshold. + +From background-static Thu Mar 23 17:57:42 EET 2006 +Message-Id: +Date: Thu Mar 23 17:57:42 EET 2006 +Status: RO +X-Riot-Version: 1ds-yyyymmdd +From: Riot +X-Riot-Edited: Thu Mar 23 17:57:42 EET 2006 +Content-Type: text/plain; charset=utf-8 +Subject: Maybe placeholders should be exported to Lua side, +In-Reply-To: + +Maybe placeholders should be exported to Lua side, +for initial placement setup and so on. + +From background-static Thu Mar 23 17:54:38 EET 2006 +Message-Id: +Date: Thu Mar 23 17:54:38 EET 2006 +Status: RO +X-Riot-Version: 1ds-yyyymmdd +From: Riot +X-Riot-Edited: Thu Mar 23 17:54:38 EET 2006 +Content-Type: text/plain; charset=utf-8 +Subject: Some kind of error reporting system for bindings +In-Reply-To: + +Some kind of error reporting system for bindings +as well while Ion is running. + +From background-static Thu Mar 23 17:51:09 EET 2006 +Message-Id: +Date: Thu Mar 23 17:51:09 EET 2006 +Status: RO +X-Riot-Version: 1ds-yyyymmdd +From: Riot +X-Riot-Edited: Thu Mar 23 17:51:09 EET 2006 +Content-Type: text/plain; charset=utf-8 +Subject: Perhaps transients should restore their sizes +In-Reply-To: + +Perhaps transients should restore their sizes +when moving from a small to big frame, if the user has not resized it. + +From background-static Thu Mar 23 17:48:55 EET 2006 +Message-Id: +Date: Thu Mar 23 17:48:55 EET 2006 +Status: RO +X-Riot-Version: 1ds-yyyymmdd +From: Riot +X-Riot-Edited: Thu Mar 23 17:48:55 EET 2006 +Content-Type: text/plain; charset=utf-8 +Subject: Perhaps / should be stripped from completed directory names. +In-Reply-To: + +Perhaps / should be stripped from completed directory names. +It should however remain in the list of completions as a hint. +Then typing the slash would trigger a new completion run in +auto-show-completions mode. + +From background-static Thu Mar 23 17:49:07 EET 2006 +Message-Id: +Date: Thu Mar 23 17:49:07 EET 2006 +Status: RO +X-Riot-Version: 1ds-yyyymmdd +From: Riot +X-Riot-Edited: Thu Mar 23 17:49:07 EET 2006 +Content-Type: text/plain; charset=utf-8 +Subject: How about pane handles on all splits? +In-Reply-To: + +How about pane handles on all splits? + +From background-static Thu Mar 23 17:32:44 EET 2006 +Message-Id: +Date: Thu Mar 23 17:32:44 EET 2006 +Status: RO +X-Riot-Version: 1ds-yyyymmdd +From: Riot +X-Riot-Edited: Thu Mar 23 17:32:44 EET 2006 +Content-Type: text/plain; charset=utf-8 +Subject: Done + +Done + +From background-static Thu Mar 23 17:53:19 EET 2006 +Message-Id: +Date: Thu Mar 23 17:53:19 EET 2006 +Status: RO +X-Riot-Version: 1ds-yyyymmdd +From: Riot +X-Riot-Edited: Thu Mar 23 17:53:19 EET 2006 +Content-Type: text/plain; charset=utf-8 +Subject: Query activation key cycles completions +In-Reply-To: + +Query activation key cycles completions + +Maybe it should be possible to use a key activating a query to cycle +the completions within the query, a bit like grabmenu. + +From background-static Thu Mar 23 18:13:35 EET 2006 +Message-Id: +Date: Thu Mar 23 18:13:35 EET 2006 +Status: RO +X-Riot-Version: 1ds-yyyymmdd +From: Riot +X-Riot-Edited: Thu Mar 23 18:13:35 EET 2006 +Content-Type: text/plain; charset=utf-8 +Subject: Perhaps the functionality of detach.lua could be implemented +In-Reply-To: + +Perhaps the functionality of detach.lua could be implemented +in a better and more integrated manner. (This could be related +to the problem of transients etc. in tiny frames in the actual +TODO list.) + +From background-static Thu Mar 23 18:03:17 EET 2006 +Message-Id: +Date: Thu Mar 23 18:03:17 EET 2006 +Status: RO +X-Riot-Version: 1ds-yyyymmdd +From: Riot +X-Riot-Edited: Thu Mar 23 18:03:17 EET 2006 +Content-Type: text/plain; charset=utf-8 +Subject: -lintl check +In-Reply-To: + +-lintl check + +From background-static Thu Mar 23 17:32:26 EET 2006 +Message-Id: +Date: Thu Mar 23 17:32:26 EET 2006 +Status: RO +X-Riot-Version: 1ds-yyyymmdd +From: Riot +X-Riot-Edited: Thu Mar 23 17:32:26 EET 2006 +Content-Type: text/plain; charset=utf-8 +Subject: Documentation doesn't build ATM. +In-Reply-To: + +Documentation doesn't build ATM. + +It should be changed to build, or converted to some yet non-existent +latex replacement that doesn't have all the problems of latex. + +From background-static Thu Mar 23 17:57:15 EET 2006 +Message-Id: +Date: Thu Mar 23 17:57:15 EET 2006 +Status: RO +X-Riot-Version: 1ds-yyyymmdd +From: Riot +X-Riot-Edited: Thu Mar 23 17:57:15 EET 2006 +Content-Type: text/plain; charset=utf-8 +Subject: It should be possible to re-attach regions within same manager +In-Reply-To: + +It should be possible to re-attach regions within same manager +at a new location. (Not really that important, though, but +it's ugly that it has to be forbidden ATM.) + +From background-static Thu Mar 23 18:03:33 EET 2006 +Message-Id: +Date: Thu Mar 23 18:03:33 EET 2006 +Status: RO +X-Riot-Version: 1ds-yyyymmdd +From: Riot +X-Riot-Edited: Thu Mar 23 18:03:33 EET 2006 +Content-Type: text/plain; charset=utf-8 +Subject: What else is broken (besides ac itself)? +In-Reply-To: + +What else is broken (besides ac itself)? + +From background-static Thu Mar 23 18:03:25 EET 2006 +Message-Id: +Date: Thu Mar 23 18:03:25 EET 2006 +Status: RO +X-Riot-Version: 1ds-yyyymmdd +From: Riot +X-Riot-Edited: Thu Mar 23 18:03:25 EET 2006 +Content-Type: text/plain; charset=utf-8 +Subject: xmessage check? +In-Reply-To: + +xmessage check? + +From background-static Thu Mar 23 17:37:02 EET 2006 +Message-Id: +Date: Thu Mar 23 17:37:02 EET 2006 +Status: RO +X-Riot-Version: 1ds-yyyymmdd +From: Riot +X-Riot-Edited: Thu Mar 23 17:37:02 EET 2006 +Content-Type: text/plain; charset=utf-8 +Subject: Transition to Lua 5.1 +In-Reply-To: + +Transition to Lua 5.1 + +From background-static Thu Mar 23 17:58:26 EET 2006 +Message-Id: +Date: Thu Mar 23 17:58:26 EET 2006 +Status: RO +X-Riot-Version: 1ds-yyyymmdd +From: Riot +X-Riot-Edited: Thu Mar 23 17:58:33 EET 2006 +Content-Type: text/plain; charset=utf-8 +Subject: A version of ioncore.warn() that doesn't dump stack trace +In-Reply-To: + +A version of ioncore.warn() that doesn't dump stack trace +should be available for Lua-side Ion code. + +From background-static Thu Mar 23 17:54:55 EET 2006 +Message-Id: +Date: Thu Mar 23 17:54:55 EET 2006 +Status: RO +X-Riot-Version: 1ds-yyyymmdd +From: Riot +X-Riot-Edited: Thu Mar 23 17:55:31 EET 2006 +Content-Type: text/plain; charset=utf-8 +Subject: Framed transients should be better +In-Reply-To: + +Framed transients should be better + +It's not really nice that the transient blocks tab-switching bindings +for the containing frame and so on. + +From background-static Thu Mar 23 17:38:29 EET 2006 +Message-Id: +Date: Thu Mar 23 17:38:29 EET 2006 +Status: RO +X-Riot-Version: 1ds-yyyymmdd +From: Riot +X-Riot-Edited: Thu Mar 23 17:38:29 EET 2006 +Content-Type: text/plain; charset=utf-8 +Subject: goto_notification_target +In-Reply-To: + +goto_notification_target + +It should be possible to go to a region with activity flag set in +the order of the flags being set by hitting a key. + +From background-static Thu Mar 23 17:47:26 EET 2006 +Message-Id: +Date: Thu Mar 23 17:47:26 EET 2006 +Status: RO +X-Riot-Version: 1ds-yyyymmdd +From: Riot +X-Riot-Edited: Thu Mar 23 17:47:26 EET 2006 +Content-Type: text/plain; charset=utf-8 +Subject: Should separate stacking code from mod_floatws +In-Reply-To: + +Should separate stacking code from mod_floatws +into a separate independent source file ("module"). + diff --git a/build/ac/README.autoconf b/build/ac/README.autoconf new file mode 100644 index 0000000..9af494e --- /dev/null +++ b/build/ac/README.autoconf @@ -0,0 +1,45 @@ + +Ion autoconf script README + + +CONFIGURE SCRIPT OPTIONS + +The Ion-specific options to ./configure are: + + --disable-xinerama + --disable-xfree86-textprop-bug-workaround + --enable-Xutf8 + --disable-shared + --with-lua* (see DEBIAN NOTES below) + --enable-c99-source + --enable-xopen-source + --disable-locale + --enable-sun-f1x-remap + +Run ./configure --help for more information on these options. The +defaults are chosen to generate a configuration very similar to the +Ion default. + + +DEBIAN NOTES + +If you are using the Lua 5.1 package on Debian then use + + ./configure --with-lua-suffix=5.1 \ + --with-lua-includes=/usr/include/lua5.1 + +This will allow the script to find the Lua headers, binaries and +libraries as renamed and located on Debian. + + +CYGWIN NOTES + +Under Cygwin you must use statically linked modules: + + ./configure --disabled-shared + + +AUTHORS + +Tom Payne + diff --git a/build/ac/aclocal.m4 b/build/ac/aclocal.m4 new file mode 100644 index 0000000..c80e0ac --- /dev/null +++ b/build/ac/aclocal.m4 @@ -0,0 +1,57 @@ + +dnl PKG_CHECK_MODULES(GSTUFF, gtk+-2.0 >= 1.3 glib = 1.3.4, action-if, action-not) +dnl defines GSTUFF_LIBS, GSTUFF_CFLAGS, see pkg-config man page +dnl also defines GSTUFF_PKG_ERRORS on error +AC_DEFUN(PKG_CHECK_MODULES, [ + succeeded=no + + if test -z "$PKG_CONFIG"; then + AC_PATH_PROG(PKG_CONFIG, pkg-config, no) + fi + + if test "$PKG_CONFIG" = "no" ; then + echo "*** The pkg-config script could not be found. Make sure it is" + echo "*** in your path, or set the PKG_CONFIG environment variable" + echo "*** to the full path to pkg-config." + echo "*** Or see http://www.freedesktop.org/software/pkgconfig to get pkg-config." + else + PKG_CONFIG_MIN_VERSION=0.9.0 + if $PKG_CONFIG --atleast-pkgconfig-version $PKG_CONFIG_MIN_VERSION; then + AC_MSG_CHECKING(for $2) + + if $PKG_CONFIG --exists "$2" ; then + AC_MSG_RESULT(yes) + succeeded=yes + + AC_MSG_CHECKING($1_CFLAGS) + $1_CFLAGS=`$PKG_CONFIG --cflags "$2"` + AC_MSG_RESULT($$1_CFLAGS) + + AC_MSG_CHECKING($1_LIBS) + $1_LIBS=`$PKG_CONFIG --libs "$2"` + AC_MSG_RESULT($$1_LIBS) + else + $1_CFLAGS="" + $1_LIBS="" + ## If we have a custom action on failure, don't print errors, but + ## do set a variable so people can do so. + $1_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "$2"` + ifelse([$4], ,echo $$1_PKG_ERRORS,) + fi + + AC_SUBST($1_CFLAGS) + AC_SUBST($1_LIBS) + else + echo "*** Your version of pkg-config is too old. You need version $PKG_CONFIG_MIN_VERSION or newer." + echo "*** See http://www.freedesktop.org/software/pkgconfig" + fi + fi + + if test $succeeded = yes; then + ifelse([$3], , :, [$3]) + else + ifelse([$4], , AC_MSG_ERROR([Library requirements ($2) not met; consider adjusting the PKG_CONFIG_PATH environment variable if your libraries are in a nonstandard prefix so pkg-config can find them.]), [$4]) + fi +]) + + diff --git a/build/ac/configure.ac b/build/ac/configure.ac new file mode 100644 index 0000000..c65e7cb --- /dev/null +++ b/build/ac/configure.ac @@ -0,0 +1,440 @@ +dnl Process this file with autoconf to produce a configure script. +dnl Please report bugs in this autoconf script Tom Payne +dnl $Header: /home/twp/cvsroot/twp/ion/ion-devel-autoconf/configure.ac,v 1.16 2004/01/14 11:32:19 twp Exp $ + +AC_INIT([Ion Window Manager], + [3ds-20061223], + [nobody@nowhere.invalid], + [ion3]) +AC_PREREQ([2.58]) +AC_REVISION([$Revision: 1.16 $]) + +dnl Checks for programs. {{{ +AC_PROG_CC() +AC_PROG_EGREP() +AC_PROG_INSTALL() +AC_PROG_RANLIB() +AC_CHECK_TOOL([STRIP], [strip], [:]) +AC_CHECK_TOOL([AR], [ar], []) +AC_CHECK_PROG(XMESSAGE, xmessage, xmessage) +if test "x${XMESSAGE}" = "x"; then + AC_MSG_WARN([*** ion needs xmessage to run correctly]) +fi +dnl }}} + +dnl Checks for libraries. {{{ +AC_PATH_XTRA() +AC_CHECK_HEADER([libintl.h], [], + [AC_MSG_ERROR([*** libintl.h not found. + *** You probably need to install libc6-dev])]) +dnl Perhaps we should check for -lintl, but on my machine, +dnl the library isn't needed. Only the #include is necessary. + +test "${X_DISPLAY_MISSING+set}" = set && exit 1 +AC_CHECK_HEADER([X11/SM/SMlib.h], [], + [AC_MSG_ERROR([*** SMlib.h not found. + *** You probably need to install libsm-dev])]) +AC_CHECK_HEADER([X11/Xresource.h], [], + [AC_MSG_ERROR([*** Xresource.h.h not found. + *** You probably need to install libx11-dev])]) +AC_CHECK_LIB([Xext], + [XMissingExtension], + [X_LIBS="$X_LIBS -lXext"], + [AC_MSG_ERROR([*** Missing symbol XMissingExtension in Xext])], + [$X_PRE_LIBS -lX11 $X_LIBS $X_EXTRA_LIBS]) +dnl }}} + +dnl XINERAMA_LIBS, _DCF_NO_XINERAMA {{{ + +AC_SUBST([XINERAMA_LIBS]) +AC_SUBST([_DCF_NO_XINERAMA]) + +AC_ARG_ENABLE([xinerama], + [AS_HELP_STRING([--disable-xinerama], + [Disable Xinerama support])]) + +if test "x$enable_xinerama" != xno; then + AC_CHECK_LIB([Xinerama], + [XineramaQueryExtension], + [XINERAMA_LIBS="-lXinerama"], + [AC_MSG_WARN([*** Xinerama disabled (not available)]) + enable_xinerama="no"], + [$X_PRE_LIBS -lX11 $X_LIBS $X_EXTRA_LIBS]) + +fi + +if test "x$enable_xinerama" = xno; then + _DCF_NO_XINERAMA="-DCF_NO_XINERAMA" +fi + +dnl }}} + +dnl _DCF_XFREE86_TEXTPROP_BUG_WORKAROUND {{{ + +AC_SUBST([_DCF_XFREE86_TEXTPROP_BUG_WORKAROUND]) + +AC_ARG_ENABLE([xfree86-textprop-bug-workaround], + [AS_HELP_STRING([--disable-xfree86-textprop-bug-workaround], + [Disable the XFree86 textprop bug workaround])]) + +if test "x$enable_xfree86_textprop_bug_workaround" != xno; then + _DCF_XFREE86_TEXTPROP_BUG_WORKAROUND="-DCF_XFREE86_TEXTPROP_BUG_WORKAROUND" +fi + +dnl }}} + +dnl _DCF_DE_USE_XUTF8 {{{ + +AC_SUBST([_DCF_DE_USE_XUTF8]) + +AC_ARG_ENABLE([Xutf8], + [AS_HELP_STRING([--enable-Xutf8], + [Use the Xutf8 routines (XFree86 extension) + instead of Xmb routines in an UTF-8 locale.])]) + +if test "x$enable_Xutf8" = xyes; then + AC_MSG_CHECKING([for Xutf8*]) + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $X_CFLAGS" + AC_RUN_IFELSE([AC_LANG_SOURCE([[ +#include + +int main() +{ +#ifdef X_HAVE_UTF8_STRING + exit(0); +#else + exit(1); +#endif +} + ]])], + [AC_MSG_RESULT([yes]) + _DCF_DE_USE_XUTF8="-DCF_DE_USE_XUTF8"], + [AC_MSG_RESULT([no]) + exit 1]) + CFLAGS="$save_CFLAGS" +fi + +dnl }}} + +dnl _DCF_SUN_F1X_REMAP {{{ + +AC_SUBST([_DCF_SUN_F1X_REMAP]) + +AC_ARG_ENABLE([sun-f1x-remap], + [AS_HELP_STRING([--enable-sun-f1x-remap], + [Remap F11 key to SunF36 and F12 to SunF37])]) + +if test "x$enable_sun_f1x_remap" == xyes; then + _DCF_SUN_F1X_REMAP="-DCF_SUN_F1X_REMAP" +fi + +dnl }}} + +dnl DL_LIBS {{{ + +AC_SUBST([DL_LIBS]) + +AC_CHECK_HEADER([dlfcn.h], [], [AC_MSG_ERROR([*** missing header dlfcn.h])]) +AC_CHECK_LIB([dl], [dlopen], [DL_LIBS="-ldl"], + [AC_MSG_ERROR([*** missing library dl])]) + +dnl }}} + +dnl PRELOAD_MODULES {{{ + +AC_SUBST(PRELOAD_MODULES) + +AC_ARG_ENABLE([shared], + [AS_HELP_STRING([--disable-shared], + [Do not dynamically load modules (preload modules + instead)])]) + +if test "x$enable_shared" = xno; then + PRELOAD_MODULES="1" +fi + +dnl }}} + +dnl LUA, LUAC, LUA_INCLUDES, LUA_LIBS {{{ + +AC_SUBST([LUA]) +AC_SUBST([LUAC]) +AC_SUBST([LUA_INCLUDES]) +AC_SUBST([LUA_LIBS]) + +dnl Arguments {{{ +AC_ARG_WITH([lua-prefix], + [AS_HELP_STRING([--with-lua-prefix=DIR], + [Lua files are in DIR])]) +AC_ARG_WITH([lua-includes], + [AS_HELP_STRING([--with-lua-includes=DIR], + [Lua include files are in DIR])]) +AC_ARG_WITH([lua-libraries], + [AS_HELP_STRING([--with-lua-libraries=DIR], + [Lua library files are in DIR])]) +AC_ARG_WITH([lua-suffix], + [AS_HELP_STRING([--with-lua-suffix=ARG], + [Lua binary and library files are suffixed with + ARG])]) +dnl }}} + +dnl LUA {{{ +if test "x$with_lua_prefix" = x; then + lua_search_path="$PATH" +else + lua_search_path="$with_lua_prefix/bin" +fi +if test "x$LUA" = x; then + AC_PATH_PROG([LUA], [lua$with_lua_suffix], [], [$lua_search_path]) + test "x$LUA" = x && AC_MSG_ERROR([*** Can't build ion without lua]) +fi +dnl }}} + +dnl lua_version {{{ +AC_MSG_CHECKING([for lua version >= 5.1]) +lua_version=$($LUA -v 2>&1 | head -n 1 | cut -d' ' -f2) +dnl lua_version=$($LUA -v 2>&1 >/dev/null | head -n 1 | $EGREP -o '[0-9]+(\.[0-9]+)+') +case $lua_version in +0.* | 1.* | 2.* | 3.* | 4.* | 5.0) + AC_MSG_RESULT([no (found $lua_version)]) + AC_MSG_ERROR([*** can't build ion with this version of lua]) + ;; +*) + AC_MSG_RESULT([yes (found $lua_version)]) + ;; +esac +dnl }}} + +dnl LUAC {{{ +if test "x$LUAC" = x; then + AC_PATH_PROG([LUAC], [luac$with_lua_suffix], [], [$lua_search_path]) + test "x$LUAC" = x && exit 1 +fi +dnl }}} + +dnl luac_version {{{ +AC_MSG_CHECKING([for lua version = luac version]) +luac_version=$($LUAC -v 2>&1 | head -n 1 | cut -d' ' -f2) +if test "x$lua_version" = "x$luac_version"; then + AC_MSG_RESULT([yes (found $luac_version)]) +else + AC_MSG_RESULT([no (found $luac_version)]) + exit 1 +fi +dnl }}} + +dnl LUA_INCLUDES {{{ +if test "x$with_lua_includes" != x; then + LUA_INCLUDES="-I$with_lua_includes" +elif test "x$with_lua_prefix" != x; then + LUA_INCLUDES="-I$with_lua_prefix/include" +fi +save_CFLAGS="$CFLAGS" +CFLAGS="$CFLAGS $LUA_INCLUDES" +AC_CHECK_HEADERS([lua.h lualib.h], [], + [AC_MSG_ERROR([*** missing headers lua.h or lualib.h. + *** install the lua's -dev package or check --with-lua-includes option.])], []) +CFLAGS="$save_CFLAGS" +dnl }}} + +dnl LUA_LIBS {{{ +if test "x$with_lua_libraries" != x; then + LUA_LIBS="-L$with_lua_libraries" +elif test "x$with_lua_prefix" != x; then + LUA_LIBS="-L$with_lua_prefix/lib" +fi +AC_CHECK_LIB([m], [exp], [lua_extra_libs="$lua_extra_libs -lm"], []) +AC_CHECK_LIB([dl], [dlopen], [lua_extra_libs="$lua_extra_libs -ldl"], []) +AC_CHECK_LIB([lua$with_lua_suffix], + [lua_call], + [LUA_LIBS="$LUA_LIBS -llua$with_lua_suffix $lua_extra_libs"], + [AC_MSG_ERROR([*** Can't find lua_call in lua$with_lua_suffix. + *** Check for liblua installation or --with-lua-libraries or --with-lua-suffix options])], + [$LUA_LIBS $lua_extra_libs]) +dnl }}} + +dnl liblua_version {{{ +AC_MSG_CHECKING([for liblua version >= 5.1]) +save_CFLAGS="$CFLAGS" +save_LIBS="$LIBS" +CFLAGS="$CFLAGS $LUA_INCLUDES" +LIBS="$LIBS $LUA_LIBS" +AC_RUN_IFELSE([AC_LANG_SOURCE([[ + +#include +#include +#include +#include + +int main() +{ + printf("(found %s, %d)... ", LUA_VERSION, LUA_VERSION_NUM); + if(LUA_VERSION_NUM >= 501) + exit(EXIT_SUCCESS); + exit(EXIT_FAILURE); +} + +]])], + [AC_MSG_RESULT([yes])], + [AC_MSG_RESULT([no]) + exit 1]) +CFLAGS="$save_CFLAGS" +LIBS="$save_LIBS" +dnl }}} + +dnl }}} + +dnl HAS_SYSTEM_ASPRINTF {{{ + +AC_SUBST([HAS_SYSTEM_ASPRINTF]) + +AC_CHECK_FUNC([asprintf], + [AC_CHECK_FUNC([vasprintf], + [HAS_SYSTEM_ASPRINTF="1"])]) + +dnl }}} + +dnl XOPEN_SOURCE {{{ + +AC_SUBST([XOPEN_SOURCE]) + +AC_ARG_ENABLE([xopen-source], + [AS_HELP_STRING([--enable-xopen-source], + [Enable X/Open C flags (development use only)])]) + +if test "x$enable_xopen_source" = xyes; then + AC_MSG_CHECKING([for $CC flags to accept X/Open C]) + case $host in + # FIXME: what flags are required for other hosts/compilers? + *-*-solaris* | *-*-sunos*) + XOPEN_SOURCE="-D__EXTENSIONS__" + ;; + *) + XOPEN_SOURCE="-D_XOPEN_SOURCE -D_XOPEN_SOURCE_EXTENDED" + ;; + esac + AC_MSG_RESULT([$XOPEN_SOURCE]) +fi + +dnl }}} + +dnl C99_SOURCE {{{ + +AC_SUBST([C99_SOURCE]) + +AC_ARG_ENABLE([c99-source], + [AS_HELP_STRING([--enable-c99-source], + [Enable C99 source (development use only)])]) + +if test "x$enable_c99_source" = xyes; then + AC_MSG_CHECKING([for $CC flags to accept C99]) + case $CC in + # FIXME: what flags are required for other hosts/compilers? + gcc*) + C99_SOURCE="-std=c99" + ;; + esac + AC_MSG_RESULT([$C99_SOURCE]) +fi + +dnl }}} + +dnl _DCF_HAS_VA_COPY {{{ + +AC_SUBST([_DCF_HAS_VA_COPY]) + +dnl check for va_copy {{{ + +AC_MSG_CHECKING([for va_copy]) +save_CFLAGS="$CFLAGS" +CFLAGS="$CFLAGS $C99_SOURCE" +AC_RUN_IFELSE([AC_LANG_SOURCE([[ + +#include +#include + +void f(void *last, ...) +{ + va_list ap, aq; + va_start(ap, last); + va_copy(aq, ap); + va_end(ap); + exit(va_arg(aq, int)); +} + +int main() +{ + f(NULL, EXIT_SUCCESS); + exit(EXIT_FAILURE); +} + +]])], + [AC_MSG_RESULT([yes]) + _DCF_HAS_VA_COPY="-DCF_HAVE_VA_COPY"], + [AC_MSG_RESULT([no])]) +CFLAGS="$save_CFLAGS" + +dnl }}} + +dnl check whether -ansi breaks va_copy {{{ + +if test "x${XOPEN_SOURCE}" != "x"; then + AC_MSG_CHECKING([whether XOPEN_SOURCE breaks va_copy]) + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $XOPEN_SOURCE $C99_SOURCE" + AC_LINK_IFELSE([AC_LANG_SOURCE([[ + +#include +#include + +void f(void *last, ...) +{ + va_list ap, aq; + va_start(ap, last); + va_copy(aq, ap); + va_end(ap); + va_end(aq); +} + +int main() +{ + f(NULL); + exit(EXIT_SUCCESS); +} + +]])], + [AC_MSG_RESULT([no])], + [AC_MSG_RESULT([yes, clearing XOPEN_SOURCE]) + XOPEN_SOURCE=""]) + CFLAGS="$save_CFLAGS" +fi +dnl }}} + +dnl }}} + +dnl _DCF_NO_LOCALE {{{ + +AC_SUBST([_DCF_NO_LOCALE]) + +AC_ARG_ENABLE([locale], + [AS_HELP_STRING([--disable-locale], + [Disable locale support])]) + +if test "x$enable_locale" == xno; then + _DCF_NO_LOCALE="-DCF_NO_LOCALE" +fi + +dnl }}} + + +AC_OUTPUT([system-ac.mk]) + +if test "x$lua_version" = "x5.0"; then + AC_MSG_NOTICE([**]) + AC_MSG_NOTICE([** lua version 5.0 is buggy, consider upgrading to 5.0.1]) + AC_MSG_NOTICE([** see http://lua-users.org/lists/lua-l/2003-10/msg00039.html]) + AC_MSG_NOTICE([**]) +fi + +dnl vim: foldmethod=marker tabstop=4 shiftwidth=4 diff --git a/build/ac/system-ac.mk.in b/build/ac/system-ac.mk.in new file mode 100644 index 0000000..035a515 --- /dev/null +++ b/build/ac/system-ac.mk.in @@ -0,0 +1,190 @@ +## +## System settings +## + + +## +## Installation paths +## + +prefix=@prefix@ +exec_prefix=@exec_prefix@ + +# Unless you are creating a package conforming to some OS's standards, you +# probably do not want to modify the following directories: + +# Main binaries +BINDIR=@bindir@ +# Configuration .lua files +ETCDIR=@sysconfdir@/@PACKAGE_TARNAME@ +# Some .lua files and ion-* shell scripts +SHAREDIR=@datadir@/@PACKAGE_TARNAME@ +# Manual pages +MANDIR=@datadir@/man +# Some documents +DOCDIR=@datadir@/doc/@PACKAGE_TARNAME@ +# Nothing at the moment +INCDIR=@includedir@ +# Nothing at the moment +LIBDIR=@libdir@ +# Modules +MODULEDIR=@libdir@/@PACKAGE_TARNAME@/mod +# Compiled Lua source code +LCDIR=@libdir@/@PACKAGE_TARNAME@/lc +# ion-completefile (does not belong in SHAREDIR being a binary file) +EXTRABINDIR=@libexecdir@/@PACKAGE_TARNAME@/bin +# For ion-completeman system-wide cache +#VARDIR=@localstatedir@/cache/@PACKAGE_TARNAME@ +VARDIR=/var/cache/@PACKAGE_TARNAME@ +# Message catalogs +LOCALEDIR=@prefix@/share/locale + + +## +## Modules +## + +# Set PRELOAD_MODULES=1 if your system does not support dynamically loaded +# modules through 'libdl' or has non-standard naming conventions. +PRELOAD_MODULES=@PRELOAD_MODULES@ + +# Flags to link with libdl. +DL_LIBS=@DL_LIBS@ + + +## +## Lua +## + +# If you have installed Lua 5.0 from the official tarball without changing +# paths, this should do it. +#LUA_DIR=/usr/local +LUA_LIBS = @LUA_LIBS@ +LUA_INCLUDES = @LUA_INCLUDES@ +LUA=@LUA@ +LUAC=@LUAC@ + +# If you are using the Debian packages, the following settings should be +# what you want. +#LUA_LIBS=`lua-config50 --libs` +#LUA_INCLUDES=`lua-config50 --include` +#LUA=lua50 +#LUAC=luac50 + + +## +## X libraries, includes and options +## + +#X11_PREFIX=/usr/X11R6 +# SunOS/Solaris +#X11_PREFIX=/usr/openwin + +X11_LIBS=@X_EXTRA_LIBS@ @X_PRE_LIBS@ -lX11 @X_LIBS@ +X11_INCLUDES=@X_CFLAGS@ + +# Change commenting to disable Xinerama support +XINERAMA_LIBS=@XINERAMA_LIBS@ +DEFINES += @_DCF_NO_XINERAMA@ + +# XFree86 libraries up to 4.3.0 have a bug that will cause Ion to segfault +# if Opera is used when i18n support is enabled. The following setting +# should work around that situation. +DEFINES += @_DCF_XFREE86_TEXTPROP_BUG_WORKAROUND@ + +# Use the Xutf8 routines (XFree86 extension) instead of Xmb routines in +# an UTF8 locale. +DEFINES += @_DCF_DE_USE_XUTF8@ + +# Remap F11 key to SunF36 and F12 to SunF37? You may want to set this +# on SunOS. +DEFINES += @_DCF_SUN_F1X_REMAP@ + + +## +## libc +## + +# You may uncomment this if you know your system has +# asprintf and vasprintf in the c library. (gnu libc has.) +# If HAS_SYSTEM_ASPRINTF is not defined, an implementation +# in sprintf_2.2/ is used. +HAS_SYSTEM_ASPRINTF=@HAS_SYSTEM_ASPRINTF@ + + +# If you're on an archaic system (such as relatively recent *BSD releases) +# without even dummy multibyte/widechar and localisation support, you may +# have to uncomment the following line: +DEFINES += @_DCF_NO_LOCALE@ + + +## +## C compiler +## + +CC=@CC@ + +# Same as '-Wall -pedantic' without '-Wunused' as callbacks often +# have unused variables. +WARN= -W -Wimplicit -Wreturn-type -Wswitch -Wcomment \ + -Wtrigraphs -Wformat -Wchar-subscripts \ + -Wparentheses -pedantic -Wuninitialized + +CFLAGS=@CFLAGS@ $(WARN) $(DEFINES) $(EXTRA_INCLUDES) $(INCLUDES) +LDFLAGS=@LDFLAGS@ $(EXTRA_LIBS) $(LIBS) +EXPORT_DYNAMIC=-Xlinker --export-dynamic + +# The following options are mainly for development use and can be used +# to check that the code seems to conform to some standards. Depending +# on the version and vendor of you libc, the options may or may not have +# expected results. If you define one of C99_SOURCE or XOPEN_SOURCE, you +# may also have to define the other. + +#C89_SOURCE=-ansi + +#POSIX_SOURCE=@_D_POSIX_SOURCE@ + +# Most systems +#XOPEN_SOURCE=-D_XOPEN_SOURCE -D_XOPEN_SOURCE_EXTENDED +# SunOS, (Irix) +#XOPEN_SOURCE=-D__EXTENSIONS__ + +#C99_SOURCE=-std=c99 -DCF_HAS_VA_COPY + +# The -DCF_HAS_VA_COPY option should allow for some optimisations, and +# in some cases simply defining +C99_SOURCE=@_DCF_HAS_VA_COPY@ +# might allow for those optimisations to be taken without any special +# libc or compiler options. + + +## +## make depend +## + +DEPEND_FILE=.depend +DO_MAKE_DEPEND=$(CC) -MM $(DEFINES) $(EXTRA_INCLUDES) $(INCLUDES) +MAKE_DEPEND=$(DO_MAKE_DEPEND) $(SOURCES) > $(DEPEND_FILE) + +## +## AR +## + +AR=@AR@ +ARFLAGS=cr +RANLIB=@RANLIB@ + + +## +## Install & strip +## + +INSTALL=@INSTALL@ +INSTALLDIR=mkdir -p + +BIN_MODE=755 +DATA_MODE=644 + +STRIP=@STRIP@ + +RM=rm diff --git a/build/libs.mk b/build/libs.mk new file mode 100644 index 0000000..acffc1e --- /dev/null +++ b/build/libs.mk @@ -0,0 +1,16 @@ + +LIBS_SUBDIRS = libtu libextl libmainloop + +LIBTU_DIR = $(TOPDIR)/libtu +LIBTU_INCLUDES = -I$(TOPDIR) +LIBTU_LIBS = -L$(LIBTU_DIR) -ltu + +LIBMAINLOOP_DIR = $(TOPDIR)/libmainloop +LIBMAINLOOP_INCLUDES = -I$(TOPDIR) +LIBMAINLOOP_LIBS = -L$(LIBMAINLOOP_DIR) -lmainloop + +LIBEXTL_DIR = $(TOPDIR)/libextl +LIBEXTL_INCLUDES = -I$(TOPDIR) +LIBEXTL_LIBS = -L$(LIBEXTL_DIR) -lextl + +MKEXPORTS = $(LUA) $(LIBEXTL_DIR)/libextl-mkexports diff --git a/build/mkman.lua b/build/mkman.lua new file mode 100644 index 0000000..bd867c6 --- /dev/null +++ b/build/mkman.lua @@ -0,0 +1,306 @@ +-- +-- build/mkman.lua +-- +-- Translates bindings from Ion configuration into a listing for +-- manual pages. +-- + + +-- Translations {{{ + +local translations={} + +local function gettext(x) + local t=translations[x] + if not t or t=="" then + return x + else + return t + end +end + +local function TR(x, ...) + return string.format(gettext(x), unpack(arg)) +end + +local function read_translations(pofile) + local f, err=io.open(pofile) + if not f then + error(err) + end + + local msgid, msgstr, st, en + + for l in f:lines() do + if string.find(l, "^msgid") then + if msgid then + assert(msgstr) + translations[msgid]=msgstr + msgstr=nil + end + st, en, msgid=string.find(l, '^msgid%s*"(.*)"%s*$') + elseif string.find(l, "^msgstr") then + assert(msgid and not msgstr) + st, en, msgstr=string.find(l, '^msgstr%s*"(.*)"%s*$') + elseif not (string.find(l, "^%s*#") or string.find(l, "^%s*$")) then + local st, en, str=string.find(l, '^%s*"(.*)"%s*$') + assert(msgid or msgstr) + if not msgstr then + msgid=msgid..str + else + msgstr=msgstr..str + end + end + end + + if msgid then + assert(msgstr) + translations[msgid]=msgstr + end + + f:close() +end + +-- }}} + + +-- File parsing {{{ + +local function dobindings(fn, bindings) + local p={} + + p.META="Mod1+" + p.ALTMETA="" + + function p.bdoc(text) + return {action = "doc", text = text} + end + + function p.submap(kcb_, list) + if not list then + return function(lst) + return submap(kcb_, lst) + end + end + return {action = "kpress", kcb = kcb_, submap = list} + end + + local function putcmd(cmd, guard, t) + t.cmd=cmd + t.guard=guard + return t + end + + function p.kpress(keyspec, cmd, guard) + return putcmd(cmd, guard, {action = "kpress", kcb = keyspec}) + end + + function p.kpress_wait(keyspec, cmd, guard) + return putcmd(cmd, guard, {action = "kpress_wait", kcb = keyspec}) + end + + local function mact(act_, kcb_, cmd, guard) + local st, en, kcb2_, area_=string.find(kcb_, "([^@]*)@(.*)") + return putcmd(cmd, guard, { + action = act_, + kcb = (kcb2_ or kcb_), + area = area_, + }) + end + + function p.mclick(buttonspec, cmd, guard) + return mact("mclick", buttonspec, cmd, guard) + end + + function p.mdblclick(buttonspec, cmd, guard) + return mact("mdblclick", buttonspec, cmd, guard) + end + + function p.mpress(buttonspec, cmd, guard) + return mact("mpress", buttonspec, cmd, guard) + end + + function p.mdrag(buttonspec, cmd, guard) + return mact("mdrag", buttonspec, cmd, guard) + end + + function p.defbindings(context, bnd) + if not bindings[context] then + bindings[context]=bnd + else + for _, v in ipairs(bnd) do + table.insert(bindings[context], v) + end + end + end + + local function dummy() + end + + p.defmenu=dummy + p.defctxmenu=dummy + p.menuentry=dummy + p.submenu=dummy + + p.ioncore={ set=dummy } + + local env=setmetatable({}, { + __index=p, + __newindex=function(x) + error("Setting global "..tostring(x)) + end, + }) + setfenv(fn, env) + fn() + return bindings +end + +local function parsefile(f, bindings) + local fn, err=loadfile(f) + if not fn then + error(err) + end + + return dobindings(fn, bindings) +end + +-- }}} + + +-- Binding output {{{ + +local function docgroup_bindings(bindings) + local out={} + local outi=0 + + local function parsetable(t, prefix) + for _, v in ipairs(t) do + if v.kcb then + v.kcb=string.gsub(v.kcb, "AnyModifier%+", "") + end + if v.action=="doc" then + outi=outi+1 + out[outi]={doc=v.text, bindings={}} + elseif v.submap then + parsetable(v.submap, prefix..v.kcb.." ") + else + assert(out[outi]) + v.kcb=prefix..v.kcb + table.insert(out[outi].bindings, v) + end + end + end + + parsetable(bindings, "") + + return out +end + + +local function combine_bindings(v) + local nact={ + ["mpress"]=TR("press"), + ["mclick"]=TR("click"), + ["mdrag"]=TR("drag"), + ["mdblclick"]=TR("double click"), + } + local first=true + local s="" + for _, b in ipairs(v.bindings) do + if not first then + s=s..', ' + end + first=false + if b.action=="kpress" or b.action=="kpress_wait" then + s=s..b.kcb + else + if not b.area then + s=s..TR("%s %s", b.kcb, nact[b.action]) + else + s=s..TR("%s %s at %s", b.kcb, nact[b.action], b.area) + end + end + end + + return s +end + +local function write_bindings_man(db) + local function write_binding_man(v) + return '.TP\n.B '..combine_bindings(v)..'\n'..gettext(v.doc)..'\n' + end + + local s="" + + for _, v in ipairs(db) do + if #(v.bindings)>0 then + s=s..write_binding_man(v) + end + end + + return s +end + +-- }}} + +-- Main {{{ + +local infile +local outfile +local bindings={} +local replaces={} + +local function doargs(a) + local i=1 + while i<=#a do + if a[i]=='-o' then + outfile=a[i+1] + i=i+2 + elseif a[i]=='-i' then + infile=a[i+1] + i=i+2 + elseif a[i]=='-D' then + replaces[a[i+1]]=a[i+2] + i=i+3 + elseif a[i]=='-po' then + read_translations(a[i+1]) + i=i+2 + else + parsefile(a[i], bindings) + i=i+1 + end + end +end + +doargs(arg) + +local f, err=io.open(infile) +if not f then + error(err) +end + +local of, oerr=io.open(outfile, 'w+') +if not of then + error(oerr) +end + +for l in f:lines() do + l=string.gsub(l, '%s*BINDINGS:([%w%.%-]+)%s*', + function(s) + if not bindings[s] then + --error('No bindings for '..s) + return "?" + end + local db=docgroup_bindings(bindings[s]) + return write_bindings_man(db) + end) + + for pat, rep in pairs(replaces) do + l=string.gsub(l, pat, rep) + end + + of:write(l..'\n') +end + +-- }}} + diff --git a/build/mkpreload.lua b/build/mkpreload.lua new file mode 100644 index 0000000..6b51ceb --- /dev/null +++ b/build/mkpreload.lua @@ -0,0 +1,32 @@ + +io.stdout:write([[ +/* Automatically generated. */ + +#include + +]]); + +for _, v in ipairs(arg) do + io.stdout:write(string.format([[ +extern bool %s_init(); +extern void %s_deinit(); +]], v, v)); + +end + +io.stdout:write([[ + +WStaticModuleInfo ioncore_static_modules[]={ +]]) + +for _, v in ipairs(arg) do + io.stdout:write(string.format( + ' {"%s", %s_init, %s_deinit, FALSE},\n', + v, v, v)); +end + +io.stdout:write([[ + {NULL, NULL, NULL, FALSE} +}; +]]) + diff --git a/build/rules.mk b/build/rules.mk new file mode 100644 index 0000000..3f8a42d --- /dev/null +++ b/build/rules.mk @@ -0,0 +1,204 @@ +## +## Some make rules +## + +ifdef MODULE +ifeq ($(PRELOAD_MODULES),1) +MODULE_TARGETS := $(MODULE).a $(MODULE).lc +else +MODULE_TARGETS := $(MODULE).so $(MODULE).lc +endif +TARGETS := $(TARGETS) $(MODULE_TARGETS) +endif + +ifdef LUA_SOURCES +LUA_COMPILED := $(subst .lua,.lc, $(LUA_SOURCES)) +TARGETS := $(TARGETS) $(LUA_COMPILED) +endif + + +# Main targets +###################################### + +.PHONY: subdirs +.PHONY: subdirs-clean +.PHONY: subdirs-realclean +.PHONY: subdirs-depend +.PHONY: subdirs-install +.PHONY: _install +.PHONY: _depend +.PHONY: _exports + +all: subdirs _exports $(TARGETS) + +clean: subdirs-clean _clean + +realclean: subdirs-realclean _clean _realclean + +depend: subdirs-depend _depend + +install: subdirs-install _install + + +# Exports +###################################### + +ifdef MAKE_EXPORTS + +EXPORTS_C = exports.c +EXPORTS_H = exports.h + +DEPEND_DEPENDS += $(EXPORTS_H) + +TO_CLEAN := $(TO_CLEAN) $(EXPORTS_C) $(EXPORTS_H) + +_exports: $(EXPORTS_C) + +$(EXPORTS_H): $(EXPORTS_C) + +$(EXPORTS_C): $(SOURCES) + $(MKEXPORTS) -module $(MAKE_EXPORTS) -o $(EXPORTS_C) -h $(EXPORTS_H) \ + $(SOURCES) $(MKEXPORTS_EXTRAS) + +else # !MAKE_EXPORTS + +EXPORTS_C = +EXPORTS_H = + +endif # !MAKE_EXPORTS + + +# Compilation and linking +###################################### + +OBJS=$(subst .c,.o,$(SOURCES) $(EXPORTS_C)) + +ifdef MODULE + +ifneq ($(PRELOAD_MODULES),1) + +CC_PICFLAGS=-fPIC -DPIC +LD_SHAREDFLAGS=-shared + +%.o: %.c + $(CC) $(CC_PICFLAGS) $(CFLAGS) -c $< -o $@ + +$(MODULE).so: $(OBJS) $(EXT_OBJS) + $(CC) $(LD_SHAREDFLAGS) $(LDFLAGS) $(OBJS) $(EXT_OBJS) -o $@ + + +module_install: module_stub_install + $(INSTALLDIR) $(MODULEDIR) + $(INSTALL) -m $(BIN_MODE) $(MODULE).so $(MODULEDIR) + +else # PRELOAD_MODULES + +PICOPT=-fPIC -DPIC +LINKOPT=-shared + +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ + +$(MODULE).a: $(OBJS) $(EXT_OBJS) + $(AR) $(ARFLAGS) $@ $+ + $(RANLIB) $@ + +module_install: module_stub_install + +endif # PRELOAD_MODULES + +module_stub_install: + $(INSTALLDIR) $(LCDIR) + $(INSTALL) -m $(DATA_MODE) $(MODULE).lc $(LCDIR) + +ifndef MODULE_STUB + +$(MODULE).lc: + echo "ioncore.load_module('$(MODULE)')" | $(LUAC) -o $@ - +else + +LUA_SOURCES += $(MODULE_STUB) + +endif #MODULE_STUB + +else # !MODULE + + +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ + + +endif# !MODULE + + +# Clean rules +###################################### + +_clean: + $(RM) -f $(TO_CLEAN) core $(DEPEND_FILE) $(OBJS) + +_realclean: + $(RM) -f $(TO_REALCLEAN) $(TARGETS) + +# Lua rules +###################################### + +%.lc: %.lua + $(LUAC) -o $@ $< + +lc_install: + $(INSTALLDIR) $(LCDIR) + for i in $(LUA_COMPILED); do \ + $(INSTALL) -m $(DATA_MODE) $$i $(LCDIR); \ + done + +etc_install: + $(INSTALLDIR) $(ETCDIR) + for i in $(ETC); do \ + $(INSTALL) -m $(DATA_MODE) $$i $(ETCDIR); \ + done + +# Dependencies +###################################### + +ifdef SOURCES + +_depend: $(DEPEND_DEPENDS) + $(MAKE_DEPEND) + +ifeq ($(DEPEND_FILE),$(wildcard $(DEPEND_FILE))) +include $(DEPEND_FILE) +endif + +endif + +# Subdirectories +###################################### + +ifdef SUBDIRS + +subdirs: + set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i; done + +subdirs-depend: + set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i depend; done + +subdirs-clean: + set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i clean; done + +subdirs-realclean: + set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i realclean; done + +subdirs-install: + set -e; for i in $(INSTALL_SUBDIRS); do $(MAKE) -C $$i install; done + +endif + +# Localisation +###################################### + +TO_CLEAN += potfiles_c potfiles_lua + +_potfiles: + echo "$(SOURCES)"|tr ' ' '\n' > potfiles_c + echo "$(LUA_SOURCES) $(ETC)"|tr ' ' '\n' > potfiles_lua diff --git a/build/system-inc.mk b/build/system-inc.mk new file mode 100644 index 0000000..39a767a --- /dev/null +++ b/build/system-inc.mk @@ -0,0 +1,18 @@ +# Use system-ac.mk if it exist, system.mk otherwise. + +ifndef TOPDIR + TOPDIR=. +endif + +SYSTEM_MK = $(TOPDIR)/system.mk +AC_SYSTEM_MK = $(TOPDIR)/build/ac/system-ac.mk + +ifeq ($(AC_SYSTEM_MK),$(wildcard $(AC_SYSTEM_MK))) + # Using system-ac.mk + include $(AC_SYSTEM_MK) +else + # Not using system-ac.mk + include $(SYSTEM_MK) +endif + +include $(TOPDIR)/build/libs.mk diff --git a/config.h b/config.h new file mode 100644 index 0000000..23935b7 --- /dev/null +++ b/config.h @@ -0,0 +1,49 @@ +/* + * config.h + * + * Copyright (c) Tuomo Valkonen 1999-2002. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_CONFIG_H +#define ION_CONFIG_H + +/* #define CF_NO_LOCK_HACK */ + +#define CF_FALLBACK_FONT_NAME "fixed" +/*#define CF_FALLBACK_FONT_NAME "-*-helvetica-medium-r-normal-*-12-*-*-*-*-*-*-*"*/ +#define CF_DRAG_TRESHOLD 2 +#define CF_DBLCLICK_DELAY 250 + +#define CF_MAX_MOVERES_STR_SIZE 32 + +#define CF_TAB_TEXT_ALIGN ALIGN_CENTER +#define CF_RESIZE_DELAY 1500 + +#define CF_XMESSAGE "xmessage -file " + +#define CF_EDGE_RESISTANCE 16 + +#define CF_RAISE_DELAY 500 + +#define CF_STATUSBAR_SYSTRAY_HEIGHT 24 + +/* Cursors + */ + +#define CF_CURSOR_DEFAULT XC_left_ptr +#define CF_CURSOR_RESIZE XC_sizing +#define CF_CURSOR_MOVE XC_fleur +#define CF_CURSOR_DRAG XC_cross +#define CF_CURSOR_WAITKEY XC_icon + +#define CF_STR_EMPTY "" +#define CF_STR_EMPTY_LEN 13 + +#define CF_STDISP_MIN_SZ 8 + +#endif /* ION_CONFIG_H */ diff --git a/de/Makefile b/de/Makefile new file mode 100644 index 0000000..6ac4bf0 --- /dev/null +++ b/de/Makefile @@ -0,0 +1,26 @@ +## +## Default drawing engine module Makefile +## + +# System-specific configuration is in system.mk +TOPDIR=.. +include $(TOPDIR)/build/system-inc.mk + +###################################### + +INCLUDES += $(X11_INCLUDES) $(LIBTU_INCLUDES) $(LIBEXTL_INCLUDES) -I.. +CFLAGS += $(XOPEN_SOURCE) $(C99_SOURCE) + +SOURCES=init.c draw.c font.c colour.c brush.c fontset.c style.c + +MODULE=de + +MAKE_EXPORTS=de + +###################################### + +include $(TOPDIR)/build/rules.mk + +###################################### + +_install: module_install diff --git a/de/brush.c b/de/brush.c new file mode 100644 index 0000000..a0576a6 --- /dev/null +++ b/de/brush.c @@ -0,0 +1,203 @@ +/* + * ion/de/brush.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include + +#include +#include + +#include +#include +#include +#include + +#include "brush.h" +#include "style.h" +#include "font.h" +#include "colour.h" +#include "private.h" + + +/*{{{ Brush creation and releasing */ + + +bool debrush_init(DEBrush *brush, Window win, + const char *stylename, DEStyle *style) +{ + brush->d=style; + brush->extras_fn=NULL; + brush->indicator_w=0; + brush->win=win; + brush->clip_set=FALSE; + + style->usecount++; + + if(!grbrush_init(&(brush->grbrush))){ + style->usecount--; + return FALSE; + } + + if(MATCHES("tab-frame", stylename) || MATCHES("tab-info", stylename)){ + brush->extras_fn=debrush_tab_extras; + if(!style->tabbrush_data_ok) + destyle_create_tab_gcs(style); + }else if(MATCHES("tab-menuentry", stylename)){ + brush->extras_fn=debrush_menuentry_extras; + brush->indicator_w=grbrush_get_text_width((GrBrush*)brush, + DE_SUB_IND, + DE_SUB_IND_LEN); + } + + return TRUE; +} + + +DEBrush *create_debrush(Window win, const char *stylename, DEStyle *style) +{ + CREATEOBJ_IMPL(DEBrush, debrush, (p, win, stylename, style)); +} + + +static DEBrush *do_get_brush(Window win, WRootWin *rootwin, + const char *stylename, bool slave) +{ + DEStyle *style=de_get_style(rootwin, stylename); + DEBrush *brush; + + if(style==NULL) + return NULL; + + brush=create_debrush(win, stylename, style); + + /* Set background colour */ + if(brush!=NULL && !slave){ + grbrush_enable_transparency(&(brush->grbrush), + GR_TRANSPARENCY_DEFAULT); + } + + return brush; +} + + +DEBrush *de_get_brush(Window win, WRootWin *rootwin, const char *stylename) +{ + return do_get_brush(win, rootwin, stylename, FALSE); +} + + +DEBrush *debrush_get_slave(DEBrush *master, WRootWin *rootwin, + const char *stylename) +{ + return do_get_brush(master->win, rootwin, stylename, TRUE); +} + + +void debrush_deinit(DEBrush *brush) +{ + destyle_unref(brush->d); + brush->d=NULL; + grbrush_deinit(&(brush->grbrush)); +} + + +void debrush_release(DEBrush *brush) +{ + destroy_obj((Obj*)brush); +} + + +/*}}}*/ + + +/*{{{ Border widths and extra information */ + + +void debrush_get_border_widths(DEBrush *brush, GrBorderWidths *bdw) +{ + DEStyle *style=brush->d; + DEBorder *bd=&(style->border); + uint tmp; + + switch(bd->style){ + case DEBORDER_RIDGE: + case DEBORDER_GROOVE: + tmp=bd->sh+bd->hl+bd->pad; + bdw->top=tmp; bdw->bottom=tmp; bdw->left=tmp; bdw->right=tmp; + break; + case DEBORDER_INLAID: + tmp=bd->sh+bd->pad; bdw->top=tmp; bdw->left=tmp; + tmp=bd->hl+bd->pad; bdw->bottom=tmp; bdw->right=tmp; + break; + case DEBORDER_ELEVATED: + default: + tmp=bd->hl+bd->pad; bdw->top=tmp; bdw->left=tmp; + tmp=bd->sh+bd->pad; bdw->bottom=tmp; bdw->right=tmp; + break; + } + + bdw->tb_ileft=bdw->left; + bdw->tb_iright=bdw->right; + bdw->spacing=style->spacing; + + bdw->right+=brush->indicator_w; + bdw->tb_iright+=brush->indicator_w; +} + + +bool debrush_get_extra(DEBrush *brush, const char *key, char type, void *data) +{ + DEStyle *style=brush->d; + while(style!=NULL){ + if(extl_table_get(style->data_table, 's', type, key, data)) + return TRUE; + style=style->based_on; + } + return FALSE; +} + + + +/*}}}*/ + + +/*{{{ Class implementation */ + + +static DynFunTab debrush_dynfuntab[]={ + {grbrush_release, debrush_release}, + {grbrush_draw_border, debrush_draw_border}, + {grbrush_draw_borderline, debrush_draw_borderline}, + {grbrush_get_border_widths, debrush_get_border_widths}, + {grbrush_draw_string, debrush_draw_string}, + {debrush_do_draw_string, debrush_do_draw_string_default}, + {grbrush_get_font_extents, debrush_get_font_extents}, + {(DynFun*)grbrush_get_text_width, (DynFun*)debrush_get_text_width}, + {grbrush_draw_textbox, debrush_draw_textbox}, + {grbrush_draw_textboxes, debrush_draw_textboxes}, + {grbrush_set_window_shape, debrush_set_window_shape}, + {grbrush_enable_transparency, debrush_enable_transparency}, + {grbrush_clear_area, debrush_clear_area}, + {grbrush_fill_area, debrush_fill_area}, + {(DynFun*)grbrush_get_extra, (DynFun*)debrush_get_extra}, + {(DynFun*)grbrush_get_slave, (DynFun*)debrush_get_slave}, + {grbrush_begin, debrush_begin}, + {grbrush_end, debrush_end}, + END_DYNFUNTAB +}; + + +IMPLCLASS(DEBrush, GrBrush, debrush_deinit, debrush_dynfuntab); + + +/*}}}*/ + + + diff --git a/de/brush.h b/de/brush.h new file mode 100644 index 0000000..a1a6b3f --- /dev/null +++ b/de/brush.h @@ -0,0 +1,108 @@ +/* + * ion/de/brush.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_DE_BRUSH_H +#define ION_DE_BRUSH_H + +#include + +#include +#include +#include + +INTRCLASS(DEBrush); + +#include "style.h" +#include "colour.h" + + +typedef void DEBrushExtrasFn(DEBrush *brush, + const WRectangle *g, DEColourGroup *cg, + GrBorderWidths *bdw, + GrFontExtents *fnte, + const char *a1, const char *a2, + bool pre); + +DECLCLASS(DEBrush){ + GrBrush grbrush; + DEStyle *d; + DEBrushExtrasFn *extras_fn; + int indicator_w; + Window win; + bool clip_set; +}; + +extern DEBrush *de_get_brush(Window win, WRootWin *rootwin, + const char *style); + +extern DEBrush *create_debrush(Window win, + const char *stylename, DEStyle *style); +extern bool debrush_init(DEBrush *brush, Window win, + const char *stylename, DEStyle *style); +extern void debrush_deinit(DEBrush *brush); + +extern DEBrush *debrush_get_slave(DEBrush *brush, WRootWin *rootwin, + const char *style); + +extern void debrush_release(DEBrush *brush); + + +extern DEColourGroup *debrush_get_colour_group2(DEBrush *brush, + const char *attr_p1, + const char *attr_p2); + +extern DEColourGroup *debrush_get_colour_group(DEBrush *brush, + const char *attr); + + +/* Begin/end */ + +extern void debrush_begin(DEBrush *brush, const WRectangle *geom, int flags); +extern void debrush_end(DEBrush *brush); + +/* Information */ + +extern void debrush_get_border_widths(DEBrush *brush, GrBorderWidths *bdw); +extern bool debrush_get_extra(DEBrush *brush, const char *key, char type, + void *data); + +/* Borders & boxes */ + +extern void debrush_draw_border(DEBrush *brush, + const WRectangle *geom, + const char *attrib); +extern void debrush_draw_borderline(DEBrush *brush, const WRectangle *geom, + const char *attrib, GrBorderLine line); + +extern void debrush_draw_textbox(DEBrush *brush, const WRectangle *geom, + const char *text, const char *attr, + bool needfill); + +extern void debrush_draw_textboxes(DEBrush *brush, const WRectangle *geom, + int n, const GrTextElem *elem, + bool needfill, const char *common_attrib); + +extern DEBrushExtrasFn debrush_tab_extras; +extern DEBrushExtrasFn debrush_menuentry_extras; + +/* Misc */ + +extern void debrush_set_window_shape(DEBrush *brush, bool rough, + int n, const WRectangle *rects); + +extern void debrush_enable_transparency(DEBrush *brush, GrTransparency mode); + +extern void debrush_fill_area(DEBrush *brush, const WRectangle *geom, + const char *attr); +extern void debrush_clear_area(DEBrush *brush, const WRectangle *geom); + + +#endif /* ION_DE_BRUSH_H */ diff --git a/de/colour.c b/de/colour.c new file mode 100644 index 0000000..c3797c3 --- /dev/null +++ b/de/colour.c @@ -0,0 +1,74 @@ +/* + * ion/de/colour.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include "colour.h" + + +bool de_alloc_colour(WRootWin *rootwin, DEColour *ret, const char *name) +{ + XColor c; + bool ok=FALSE; + + if(name==NULL) + return FALSE; + + if(XParseColor(ioncore_g.dpy, rootwin->default_cmap, name, &c)){ + ok=XAllocColor(ioncore_g.dpy, rootwin->default_cmap, &c); + if(ok) + *ret=c.pixel; + } + + return ok; +} + + +bool de_duplicate_colour(WRootWin *rootwin, DEColour in, DEColour *out) +{ + XColor c; + c.pixel=in; + XQueryColor(ioncore_g.dpy, rootwin->default_cmap, &c); + if(XAllocColor(ioncore_g.dpy, rootwin->default_cmap, &c)){ + *out=c.pixel; + return TRUE; + } + return FALSE; +} + + +void de_free_colour_group(WRootWin *rootwin, DEColourGroup *cg) +{ + DEColour pixels[5]; + + pixels[0]=cg->bg; + pixels[1]=cg->fg; + pixels[2]=cg->hl; + pixels[3]=cg->sh; + pixels[4]=cg->pad; + + XFreeColors(ioncore_g.dpy, rootwin->default_cmap, pixels, 5, 0); + + if(cg->spec!=NULL){ + free(cg->spec); + cg->spec=NULL; + } +} + + +void de_free_colour(WRootWin *rootwin, DEColour col) +{ + DEColour pixels[1]; + + pixels[0]=col; + + XFreeColors(ioncore_g.dpy, rootwin->default_cmap, pixels, 1, 0); +} + diff --git a/de/colour.h b/de/colour.h new file mode 100644 index 0000000..8886b97 --- /dev/null +++ b/de/colour.h @@ -0,0 +1,41 @@ +/* + * ion/de/colour.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_DE_COLOUR_H +#define ION_DE_COLOUR_H + +#include +#include +#include + + +INTRSTRUCT(DEColourGroup); + + +typedef unsigned long DEColour; + + +DECLSTRUCT(DEColourGroup){ + char *spec; + DEColour bg, hl, sh, fg, pad; +}; + + +#define DE_BLACK(rootwin) BlackPixel(ioncore_g.dpy, rootwin->xscr) +#define DE_WHITE(rootwin) WhitePixel(ioncore_g.dpy, rootwin->xscr) + +bool de_init_colour_group(WRootWin *rootwin, DEColourGroup *cg); +bool de_alloc_colour(WRootWin *rootwin, DEColour *ret, const char *name); +bool de_duplicate_colour(WRootWin *rootwin, DEColour in, DEColour *out); +void de_free_colour_group(WRootWin *rootwin, DEColourGroup *cg); +void de_free_colour(WRootWin *rootwin, DEColour col); + +#endif /* ION_DE_COLOUR_H */ diff --git a/de/draw.c b/de/draw.c new file mode 100644 index 0000000..aae2dae --- /dev/null +++ b/de/draw.c @@ -0,0 +1,576 @@ +/* + * ion/de/draw.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include + +#include +#include +#include +#include "brush.h" +#include "font.h" +#include "private.h" + +#include + + +/*{{{ Colour group lookup */ + + +static DEColourGroup *destyle_get_colour_group2(DEStyle *style, + const char *attr_p1, + const char *attr_p2) +{ + int i, score, maxscore=0; + DEColourGroup *maxg=&(style->cgrp); + + while(style!=NULL){ + for(i=0; in_extra_cgrps; i++){ + score=gr_stylespec_score2(style->extra_cgrps[i].spec, + attr_p1, attr_p2); + if(score>maxscore){ + maxg=&(style->extra_cgrps[i]); + maxscore=score; + } + } + style=style->based_on; + } + + return maxg; +} + + +DEColourGroup *debrush_get_colour_group2(DEBrush *brush, + const char *attr_p1, + const char *attr_p2) +{ + return destyle_get_colour_group2(brush->d, attr_p1, attr_p2); +} + + +DEColourGroup *debrush_get_colour_group(DEBrush *brush, const char *attr) +{ + return destyle_get_colour_group2(brush->d, attr, NULL); +} + + +/*}}}*/ + + +/*{{{ Borders */ + + +/* Draw a border at x, y with outer width w x h. Top and left 'tl' pixels + * wide with color 'tlc' and bottom and right 'br' pixels with colors 'brc'. + */ +static void do_draw_border(Window win, GC gc, int x, int y, int w, int h, + uint tl, uint br, DEColour tlc, DEColour brc) +{ + XPoint points[3]; + uint i=0, a=0, b=0; + + w--; + h--; + + XSetForeground(ioncore_g.dpy, gc, tlc); + + + a=(br!=0); + b=0; + + for(i=0; ix, geom->y, geom->w, geom->h, + tl, br, tlc, brc); + geom->x+=tl; + geom->y+=tl; + geom->w-=tl+br; + geom->h-=tl+br; +} + + +void debrush_do_draw_border(DEBrush *brush, WRectangle geom, + DEColourGroup *cg) +{ + DEBorder *bd=&(brush->d->border); + GC gc=brush->d->normal_gc; + Window win=brush->win; + + switch(bd->style){ + case DEBORDER_RIDGE: + draw_border(win, gc, &geom, bd->hl, bd->sh, cg->hl, cg->sh); + case DEBORDER_INLAID: + draw_border(win, gc, &geom, bd->pad, bd->pad, cg->pad, cg->pad); + draw_border(win, gc, &geom, bd->sh, bd->hl, cg->sh, cg->hl); + break; + case DEBORDER_GROOVE: + draw_border(win, gc, &geom, bd->sh, bd->hl, cg->sh, cg->hl); + draw_border(win, gc, &geom, bd->pad, bd->pad, cg->pad, cg->pad); + draw_border(win, gc, &geom, bd->hl, bd->sh, cg->hl, cg->sh); + break; + case DEBORDER_ELEVATED: + default: + draw_border(win, gc, &geom, bd->hl, bd->sh, cg->hl, cg->sh); + draw_border(win, gc, &geom, bd->pad, bd->pad, cg->pad, cg->pad); + break; + } +} + + +void debrush_draw_border(DEBrush *brush, + const WRectangle *geom, + const char *attrib) +{ + DEColourGroup *cg=debrush_get_colour_group(brush, attrib); + if(cg!=NULL) + debrush_do_draw_border(brush, *geom, cg); +} + + +static void draw_borderline(Window win, GC gc, WRectangle *geom, + uint tl, uint br, DEColour tlc, DEColour brc, + GrBorderLine line) +{ + if(line==GR_BORDERLINE_LEFT && geom->h>0){ + XSetForeground(ioncore_g.dpy, gc, tlc); + XDrawRectangle(ioncore_g.dpy, win, gc, geom->x, geom->y, tl, geom->h); + geom->x+=tl; + }else if(line==GR_BORDERLINE_TOP && geom->w>0){ + XSetForeground(ioncore_g.dpy, gc, tlc); + XDrawRectangle(ioncore_g.dpy, win, gc, geom->x, geom->y, geom->w, tl); + geom->y+=tl; + }else if(line==GR_BORDERLINE_RIGHT && geom->h>0){ + XSetForeground(ioncore_g.dpy, gc, brc); + XDrawRectangle(ioncore_g.dpy, win, gc, geom->x+geom->w-1-br, geom->y, br, geom->h); + geom->w-=br; + }else if(line==GR_BORDERLINE_BOTTOM && geom->w>0){ + XSetForeground(ioncore_g.dpy, gc, brc); + XDrawRectangle(ioncore_g.dpy, win, gc, geom->x, geom->y+geom->h-1-br, geom->w, br); + geom->h-=br; + } +} + + +void debrush_do_draw_borderline(DEBrush *brush, WRectangle geom, + DEColourGroup *cg, GrBorderLine line) +{ + DEBorder *bd=&(brush->d->border); + GC gc=brush->d->normal_gc; + Window win=brush->win; + + switch(bd->style){ + case DEBORDER_RIDGE: + draw_borderline(win, gc, &geom, bd->hl, bd->sh, cg->hl, cg->sh, line); + case DEBORDER_INLAID: + draw_borderline(win, gc, &geom, bd->pad, bd->pad, cg->pad, cg->pad, line); + draw_borderline(win, gc, &geom, bd->sh, bd->hl, cg->sh, cg->hl, line); + break; + case DEBORDER_GROOVE: + draw_borderline(win, gc, &geom, bd->sh, bd->hl, cg->sh, cg->hl, line); + draw_borderline(win, gc, &geom, bd->pad, bd->pad, cg->pad, cg->pad, line); + draw_borderline(win, gc, &geom, bd->hl, bd->sh, cg->hl, cg->sh, line); + break; + case DEBORDER_ELEVATED: + default: + draw_borderline(win, gc, &geom, bd->hl, bd->sh, cg->hl, cg->sh, line); + draw_borderline(win, gc, &geom, bd->pad, bd->pad, cg->pad, cg->pad, line); + break; + } +} + + +void debrush_draw_borderline(DEBrush *brush, const WRectangle *geom, + const char *attrib, GrBorderLine line) +{ + DEColourGroup *cg=debrush_get_colour_group(brush, attrib); + if(cg!=NULL) + debrush_do_draw_borderline(brush, *geom, cg, line); +} + + +/*}}}*/ + + +/*{{{ Boxes */ + + +static void copy_masked(DEBrush *brush, Drawable src, Drawable dst, + int src_x, int src_y, int w, int h, + int dst_x, int dst_y) +{ + + GC copy_gc=brush->d->copy_gc; + + XSetClipMask(ioncore_g.dpy, copy_gc, src); + XSetClipOrigin(ioncore_g.dpy, copy_gc, dst_x, dst_y); + XCopyPlane(ioncore_g.dpy, src, dst, copy_gc, src_x, src_y, w, h, + dst_x, dst_y, 1); +} + + +void debrush_tab_extras(DEBrush *brush, const WRectangle *g, + DEColourGroup *cg, GrBorderWidths *bdw, + GrFontExtents *fnte, + const char *a1, const char *a2, + bool pre) +{ + DEStyle *d=brush->d; + GC tmp; + /* Not thread-safe, but neither is the rest of the drawing code + * with shared GC:s. + */ + static bool swapped=FALSE; + + if(pre){ + if(!MATCHES2("*-*-*-dragged", a1, a2)) + return; + + tmp=d->normal_gc; + d->normal_gc=d->stipple_gc; + d->stipple_gc=tmp; + swapped=TRUE; + XClearArea(ioncore_g.dpy, brush->win, g->x, g->y, g->w, g->h, False); + return; + } + + if(MATCHES2("*-*-tagged", a1, a2)){ + XSetForeground(ioncore_g.dpy, d->copy_gc, cg->fg); + + copy_masked(brush, d->tag_pixmap, brush->win, 0, 0, + d->tag_pixmap_w, d->tag_pixmap_h, + g->x+g->w-bdw->right-d->tag_pixmap_w, + g->y+bdw->top); + } + + if(swapped){ + tmp=d->normal_gc; + d->normal_gc=d->stipple_gc; + d->stipple_gc=tmp; + swapped=FALSE; + } + /*if(MATCHES2("*-*-*-dragged", a1, a2)){ + XFillRectangle(ioncore_g.dpy, win, d->stipple_gc, + g->x, g->y, g->w, g->h); + }*/ +} + + +void debrush_menuentry_extras(DEBrush *brush, const WRectangle *g, + DEColourGroup *cg, GrBorderWidths *bdw, + GrFontExtents *fnte, + const char *a1, const char *a2, + bool pre) +{ + int tx, ty; + + if(pre) + return; + + if(!MATCHES2("*-*-submenu", a1, a2)) + return; + + ty=(g->y+bdw->top+fnte->baseline + +(g->h-bdw->top-bdw->bottom-fnte->max_height)/2); + tx=g->x+g->w-bdw->right; + + debrush_do_draw_string(brush, tx, ty, DE_SUB_IND, DE_SUB_IND_LEN, + FALSE, cg); +} + + +void debrush_do_draw_box(DEBrush *brush, const WRectangle *geom, + DEColourGroup *cg, bool needfill) +{ + GC gc=brush->d->normal_gc; + + if(TRUE/*needfill*/){ + XSetForeground(ioncore_g.dpy, gc, cg->bg); + XFillRectangle(ioncore_g.dpy, brush->win, gc, geom->x, geom->y, + geom->w, geom->h); + } + + debrush_do_draw_border(brush, *geom, cg); +} + + +static void debrush_do_draw_textbox(DEBrush *brush, const WRectangle *geom, + const char *text, DEColourGroup *cg, + bool needfill, + const char *a1, const char *a2) +{ + uint len; + GrBorderWidths bdw; + GrFontExtents fnte; + uint tx, ty, tw; + + grbrush_get_border_widths(&(brush->grbrush), &bdw); + grbrush_get_font_extents(&(brush->grbrush), &fnte); + + if(brush->extras_fn!=NULL) + brush->extras_fn(brush, geom, cg, &bdw, &fnte, a1, a2, TRUE); + + debrush_do_draw_box(brush, geom, cg, needfill); + + do{ /*...while(0)*/ + if(text==NULL) + break; + + len=strlen(text); + + if(len==0) + break; + + if(brush->d->textalign!=DEALIGN_LEFT){ + tw=grbrush_get_text_width((GrBrush*)brush, text, len); + + if(brush->d->textalign==DEALIGN_CENTER) + tx=geom->x+bdw.left+(geom->w-bdw.left-bdw.right-tw)/2; + else + tx=geom->x+geom->w-bdw.right-tw; + }else{ + tx=geom->x+bdw.left; + } + + ty=(geom->y+bdw.top+fnte.baseline + +(geom->h-bdw.top-bdw.bottom-fnte.max_height)/2); + + debrush_do_draw_string(brush, tx, ty, text, len, FALSE, cg); + }while(0); + + if(brush->extras_fn!=NULL) + brush->extras_fn(brush, geom, cg, &bdw, &fnte, a1, a2, FALSE); +} + + +void debrush_draw_textbox(DEBrush *brush, const WRectangle *geom, + const char *text, const char *attr, + bool needfill) +{ + DEColourGroup *cg=debrush_get_colour_group(brush, attr); + if(cg!=NULL){ + debrush_do_draw_textbox(brush, geom, text, cg, needfill, + attr, NULL); + } +} + + +void debrush_draw_textboxes(DEBrush *brush, const WRectangle *geom, + int n, const GrTextElem *elem, + bool needfill, const char *common_attrib) +{ + WRectangle g=*geom; + DEColourGroup *cg; + GrBorderWidths bdw; + int i; + + grbrush_get_border_widths(&(brush->grbrush), &bdw); + + for(i=0; i0 && needfill){ + XClearArea(ioncore_g.dpy, brush->win, g.x, g.y, + brush->d->spacing, g.h, False); + } + g.x+=bdw.spacing; + } +} + + +/*}}}*/ + + +/*{{{ Misc. */ + +#define MAXSHAPE 16 + +void debrush_set_window_shape(DEBrush *brush, bool rough, + int n, const WRectangle *rects) +{ + XRectangle r[MAXSHAPE]; + int i; + + if(n>MAXSHAPE) + n=MAXSHAPE; + + if(n==0){ + /* n==0 should clear the shape. As there's absolutely no + * documentation for XShape (as is typical of all sucky X + * extensions), I don't know how the shape should properly + * be cleared. Thus we just use a huge rectangle. + */ + n=1; + r[0].x=0; + r[0].y=0; + r[0].width=USHRT_MAX; + r[0].height=USHRT_MAX; + }else{ + for(i=0; iwin, + ShapeBounding, 0, 0, r, n, + ShapeSet, Unsorted); +} + + +void debrush_enable_transparency(DEBrush *brush, GrTransparency mode) +{ + XSetWindowAttributes attr; + ulong attrflags=0; + + if(mode==GR_TRANSPARENCY_DEFAULT) + mode=brush->d->transparency_mode; + + if(mode==GR_TRANSPARENCY_YES){ + attrflags=CWBackPixmap; + attr.background_pixmap=ParentRelative; + }else{ + attrflags=CWBackPixel; + attr.background_pixel=brush->d->cgrp.bg; + } + + XChangeWindowAttributes(ioncore_g.dpy, brush->win, attrflags, &attr); + XClearWindow(ioncore_g.dpy, brush->win); +} + + +void debrush_fill_area(DEBrush *brush, const WRectangle *geom, const char *attr) +{ + DEColourGroup *cg=debrush_get_colour_group(brush, attr); + GC gc=brush->d->normal_gc; + + if(cg==NULL) + return; + + XSetForeground(ioncore_g.dpy, gc, cg->bg); + XFillRectangle(ioncore_g.dpy, brush->win, gc, + geom->x, geom->y, geom->w, geom->h); +} + + +void debrush_clear_area(DEBrush *brush, const WRectangle *geom) +{ + XClearArea(ioncore_g.dpy, brush->win, + geom->x, geom->y, geom->w, geom->h, False); +} + + +/*}}}*/ + + +/*{{{ Clipping rectangles */ + +/* Should actually set the clipping rectangle for all GC:s and use + * window-specific GC:s to do this correctly... + */ + +static void debrush_set_clipping_rectangle(DEBrush *brush, + const WRectangle *geom) +{ + XRectangle rect; + + assert(!brush->clip_set); + + rect.x=geom->x; + rect.y=geom->y; + rect.width=geom->w; + rect.height=geom->h; + + XSetClipRectangles(ioncore_g.dpy, brush->d->normal_gc, + 0, 0, &rect, 1, Unsorted); + brush->clip_set=TRUE; +} + + +static void debrush_clear_clipping_rectangle(DEBrush *brush) +{ + if(brush->clip_set){ + XSetClipMask(ioncore_g.dpy, brush->d->normal_gc, None); + brush->clip_set=FALSE; + } +} + + +/*}}}*/ + + +/*{{{ debrush_begin/end */ + + +void debrush_begin(DEBrush *brush, const WRectangle *geom, int flags) +{ + if(flags&GRBRUSH_AMEND) + flags|=GRBRUSH_NO_CLEAR_OK; + + if(!(flags&GRBRUSH_NO_CLEAR_OK)) + debrush_clear_area(brush, geom); + + if(flags&GRBRUSH_NEED_CLIP) + debrush_set_clipping_rectangle(brush, geom); +} + + +void debrush_end(DEBrush *brush) +{ + debrush_clear_clipping_rectangle(brush); +} + + +/*}}}*/ + diff --git a/de/font.c b/de/font.c new file mode 100644 index 0000000..9d80c1e --- /dev/null +++ b/de/font.c @@ -0,0 +1,280 @@ +/* + * ion/de/font.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include + +#include +#include +#include "font.h" +#include "fontset.h" +#include "brush.h" + + +/*{{{ Load/free */ + + +static DEFont *fonts=NULL; + + +DEFont *de_load_font(const char *fontname) +{ + DEFont *fnt; + XFontSet fontset=NULL; + XFontStruct *fontstruct=NULL; + + assert(fontname!=NULL); + + /* There shouldn't be that many fonts... */ + for(fnt=fonts; fnt!=NULL; fnt=fnt->next){ + if(strcmp(fnt->pattern, fontname)==0){ + fnt->refcount++; + return fnt; + } + } + + if(ioncore_g.use_mb){ + fontset=de_create_font_set(fontname); + if(fontset!=NULL){ + if(XContextDependentDrawing(fontset)){ + warn(TR("Fontset for font pattern '%s' implements context " + "dependent drawing, which is unsupported. Expect " + "clutter."), fontname); + } + } + }else{ + fontstruct=XLoadQueryFont(ioncore_g.dpy, fontname); + } + + if(fontstruct==NULL && fontset==NULL){ + if(strcmp(fontname, CF_FALLBACK_FONT_NAME)!=0){ + DEFont *fnt; + warn(TR("Could not load font \"%s\", trying \"%s\""), + fontname, CF_FALLBACK_FONT_NAME); + fnt=de_load_font(CF_FALLBACK_FONT_NAME); + if(fnt==NULL) + warn(TR("Failed to load fallback font.")); + return fnt; + } + return NULL; + } + + fnt=ALLOC(DEFont); + + if(fnt==NULL) + return NULL; + + fnt->fontset=fontset; + fnt->fontstruct=fontstruct; + fnt->pattern=scopy(fontname); + fnt->next=NULL; + fnt->prev=NULL; + fnt->refcount=1; + + LINK_ITEM(fonts, fnt, next, prev); + + return fnt; +} + + +bool de_set_font_for_style(DEStyle *style, DEFont *font) +{ + if(style->font!=NULL) + de_free_font(style->font); + + style->font=font; + font->refcount++; + + if(style->font->fontstruct!=NULL){ + XSetFont(ioncore_g.dpy, style->normal_gc, + style->font->fontstruct->fid); + } + + return TRUE; +} + + +bool de_load_font_for_style(DEStyle *style, const char *fontname) +{ + if(style->font!=NULL) + de_free_font(style->font); + + style->font=de_load_font(fontname); + + if(style->font==NULL) + return FALSE; + + if(style->font->fontstruct!=NULL){ + XSetFont(ioncore_g.dpy, style->normal_gc, + style->font->fontstruct->fid); + } + + return TRUE; +} + + +void de_free_font(DEFont *font) +{ + if(--font->refcount!=0) + return; + + if(font->fontset!=NULL) + XFreeFontSet(ioncore_g.dpy, font->fontset); + if(font->fontstruct!=NULL) + XFreeFont(ioncore_g.dpy, font->fontstruct); + if(font->pattern!=NULL) + free(font->pattern); + + UNLINK_ITEM(fonts, font, next, prev); + free(font); +} + + +/*}}}*/ + + +/*{{{ Lengths */ + + +void debrush_get_font_extents(DEBrush *brush, GrFontExtents *fnte) +{ + if(brush->d->font==NULL){ + DE_RESET_FONT_EXTENTS(fnte); + return; + } + + defont_get_font_extents(brush->d->font, fnte); +} + + +void defont_get_font_extents(DEFont *font, GrFontExtents *fnte) +{ + if(font->fontset!=NULL){ + XFontSetExtents *ext=XExtentsOfFontSet(font->fontset); + if(ext==NULL) + goto fail; + fnte->max_height=ext->max_logical_extent.height; + fnte->max_width=ext->max_logical_extent.width; + fnte->baseline=-ext->max_logical_extent.y; + return; + }else if(font->fontstruct!=NULL){ + XFontStruct *fnt=font->fontstruct; + fnte->max_height=fnt->ascent+fnt->descent; + fnte->max_width=fnt->max_bounds.width; + fnte->baseline=fnt->ascent; + return; + } + +fail: + DE_RESET_FONT_EXTENTS(fnte); +} + + +uint debrush_get_text_width(DEBrush *brush, const char *text, uint len) +{ + if(brush->d->font==NULL || text==NULL || len==0) + return 0; + + return defont_get_text_width(brush->d->font, text, len); +} + + +uint defont_get_text_width(DEFont *font, const char *text, uint len) +{ + if(font->fontset!=NULL){ + XRectangle lext; +#ifdef CF_DE_USE_XUTF8 + if(ioncore_g.enc_utf8) + Xutf8TextExtents(font->fontset, text, len, NULL, &lext); + else +#endif + XmbTextExtents(font->fontset, text, len, NULL, &lext); + return lext.width; + }else if(font->fontstruct!=NULL){ + return XTextWidth(font->fontstruct, text, len); + }else{ + return 0; + } +} + + +/*}}}*/ + + +/*{{{ String drawing */ + + +void debrush_do_draw_string_default(DEBrush *brush, int x, int y, + const char *str, int len, bool needfill, + DEColourGroup *colours) +{ + GC gc=brush->d->normal_gc; + + if(brush->d->font==NULL) + return; + + XSetForeground(ioncore_g.dpy, gc, colours->fg); + + if(!needfill){ + if(brush->d->font->fontset!=NULL){ +#ifdef CF_DE_USE_XUTF8 + if(ioncore_g.enc_utf8) + Xutf8DrawString(ioncore_g.dpy, brush->win, + brush->d->font->fontset, + gc, x, y, str, len); + else +#endif + XmbDrawString(ioncore_g.dpy, brush->win, + brush->d->font->fontset, + gc, x, y, str, len); + }else if(brush->d->font->fontstruct!=NULL){ + XDrawString(ioncore_g.dpy, brush->win, gc, x, y, str, len); + } + }else{ + XSetBackground(ioncore_g.dpy, gc, colours->bg); + if(brush->d->font->fontset!=NULL){ +#ifdef CF_DE_USE_XUTF8 + if(ioncore_g.enc_utf8) + Xutf8DrawImageString(ioncore_g.dpy, brush->win, + brush->d->font->fontset, + gc, x, y, str, len); + else +#endif + XmbDrawImageString(ioncore_g.dpy, brush->win, + brush->d->font->fontset, + gc, x, y, str, len); + }else if(brush->d->font->fontstruct!=NULL){ + XDrawImageString(ioncore_g.dpy, brush->win, gc, x, y, str, len); + } + } +} + + +void debrush_do_draw_string(DEBrush *brush, int x, int y, + const char *str, int len, bool needfill, + DEColourGroup *colours) +{ + CALL_DYN(debrush_do_draw_string, brush, (brush, x, y, str, len, + needfill, colours)); +} + + +void debrush_draw_string(DEBrush *brush, int x, int y, + const char *str, int len, bool needfill, + const char *attrib) +{ + DEColourGroup *cg=debrush_get_colour_group(brush, attrib); + if(cg!=NULL) + debrush_do_draw_string(brush, x, y, str, len, needfill, cg); +} + + +/*}}}*/ + diff --git a/de/font.h b/de/font.h new file mode 100644 index 0000000..d2f5e3b --- /dev/null +++ b/de/font.h @@ -0,0 +1,58 @@ +/* + * ion/de/font.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_DE_FONT_H +#define ION_DE_FONT_H + +#include +#include + +INTRSTRUCT(DEFont); + +#include "brush.h" +#include "colour.h" +#include "style.h" + +#define DE_RESET_FONT_EXTENTS(FNTE) \ + {(FNTE)->max_height=0; (FNTE)->max_width=0; (FNTE)->baseline=0;} + +DECLSTRUCT(DEFont){ + char *pattern; + int refcount; + XFontSet fontset; + XFontStruct *fontstruct; + DEFont *next, *prev; +}; + +extern bool de_load_font_for_style(DEStyle *style, const char *fontname); +extern bool de_set_font_for_style(DEStyle *style, DEFont *font); +extern DEFont *de_load_font(const char *fontname); +extern void de_free_font(DEFont *font); + +extern void debrush_draw_string(DEBrush *brush, int x, int y, + const char *str, int len, bool needfill, + const char *attrib); +extern void debrush_do_draw_string(DEBrush *brush, int x, int y, + const char *str, int len, bool needfill, + DEColourGroup *colours); +extern void debrush_do_draw_string_default(DEBrush *brush, int x, int y, + const char *str, int len, + bool needfill, + DEColourGroup *colours); + +extern void debrush_get_font_extents(DEBrush *brush, GrFontExtents *fnte); + +extern uint debrush_get_text_width(DEBrush *brush, const char *text, uint len); + +extern uint defont_get_text_width(DEFont *font, const char *text, uint len); +extern void defont_get_font_extents(DEFont *font, GrFontExtents *fnte); + +#endif /* ION_DE_FONT_H */ diff --git a/de/fontset.c b/de/fontset.c new file mode 100644 index 0000000..af164be --- /dev/null +++ b/de/fontset.c @@ -0,0 +1,186 @@ +/* + * ion/de/fontset.c + * + * This file contains routines to attempt to add fonts to a font pattern + * so that XCreateFontSet will not fail because the given font(s) do not + * contain all the characters required by the locale. The original code + * was apparently written by Tomohiro Kubota; see + * . + * + */ + +#include +#include +#include + +#include +#include + +#ifndef CF_FONT_ELEMENT_SIZE +#define CF_FONT_ELEMENT_SIZE 50 +#endif + +#define FNT_D(X) /*X*/ + + +static const char *get_font_element(const char *pattern, char *buf, + int bufsiz, ...) +{ + const char *p, *v; + char *p2; + va_list va; + + va_start(va, bufsiz); + buf[bufsiz-1]=0; + buf[bufsiz-2]='*'; + while((v=va_arg(va, char *))!=NULL){ + p=libtu_strcasestr(pattern, v); + if(p){ + strncpy(buf, p+1, bufsiz-2); + p2=strchr(buf, '-'); + if(p2) *p2=0; + va_end(va); + return p; + } + } + va_end(va); + strncpy(buf, "*", bufsiz); + return NULL; +} + + +static const char *get_font_size(const char *pattern, int *size) +{ + const char *p; + const char *p2=NULL; + int n=0; + + for(p=pattern; 1; p++){ + if(!*p){ + if(p2!=NULL && n>1 && n<72){ + *size=n; return p2+1; + }else{ + *size=16; return NULL; + } + }else if(*p=='-'){ + if(n>1 && n<72 && p2!=NULL){ + *size=n; + return p2+1; + } + p2=p; n=0; + }else if(*p>='0' && *p<='9' && p2!=NULL){ + n*=10; + n+=*p-'0'; + }else{ + p2=NULL; n=0; + } + } +} + + +XFontSet de_create_font_set(const char *fontname) +{ + XFontSet fs; + char **missing=NULL, *def="-"; + int nmissing, pixel_size=0; + char weight[CF_FONT_ELEMENT_SIZE], slant[CF_FONT_ELEMENT_SIZE]; + const char *nfontname=fontname; + char *pattern2=NULL; + int i; + + FNT_D(fprintf(stderr, "FNTRQ: %s\n", fontname)); + + fs=XCreateFontSet(ioncore_g.dpy, fontname, &missing, &nmissing, &def); + + if(fs && nmissing==0){ + if(missing!=NULL) + XFreeStringList(missing); + return fs; + } + + /* Not a warning, nothing serious */ + FNT_D(fprintf(stderr, "Failed to load fontset.\n")); + + if(!fs){ + char *lcc=NULL; + const char *lc; + if(missing!=NULL) + XFreeStringList(missing); + + lc=setlocale(LC_CTYPE, NULL); + if(lc!=NULL && strcmp(lc, "POSIX")!=0 && strcmp(lc, "C")!=0) + lcc=scopy(lc); + + setlocale(LC_CTYPE, "C"); + + fs=XCreateFontSet(ioncore_g.dpy, fontname, &missing, &nmissing, &def); + + if(lcc!=NULL){ + setlocale(LC_CTYPE, lcc); + free(lcc); + } + } + +#ifndef CF_NO_FONTSET_KLUDGE + + if(fs){ + XFontStruct **fontstructs; + char **fontnames; + XFontsOfFontSet(fs, &fontstructs, &fontnames); + nfontname=fontnames[0]; + } + + get_font_element(nfontname, weight, CF_FONT_ELEMENT_SIZE, + "-medium-", "-bold-", "-demibold-", "-regular-", NULL); + get_font_element(nfontname, slant, CF_FONT_ELEMENT_SIZE, + "-r-", "-i-", "-o-", "-ri-", "-ro-", NULL); + get_font_size(nfontname, &pixel_size); + + if(!strcmp(weight, "*")) + strncpy(weight, "medium", CF_FONT_ELEMENT_SIZE); + if(!strcmp(slant, "*")) + strncpy(slant, "r", CF_FONT_ELEMENT_SIZE); + if(pixel_size<3) + pixel_size=3; + else if(pixel_size>97) + pixel_size=97; + + if(ioncore_g.enc_utf8){ + libtu_asprintf(&pattern2, + "%s," + "-misc-fixed-%s-%s-*-*-%d-*-*-*-*-*-*-*," + "-misc-fixed-*-*-*-*-%d-*-*-*-*-*-*-*", + fontname, weight, slant, pixel_size, pixel_size); + }else{ + libtu_asprintf(&pattern2, + "%s," + "-*-*-%s-%s-*-*-%d-*-*-*-*-*-*-*," + "-*-*-*-*-*-*-%d-*-*-*-*-*-*-*", + fontname, weight, slant, pixel_size, pixel_size); + } + + if(pattern2==NULL) + return NULL; + + FNT_D(fprintf(stderr, "NRQ: %s\n", pattern2)); + + nfontname=pattern2; + + if(nmissing) + XFreeStringList(missing); + if(fs) + XFreeFontSet(ioncore_g.dpy, fs); + + FNT_D(if(fs) fprintf(stderr, "Trying '%s'.\n", nfontname)); + + fs=XCreateFontSet(ioncore_g.dpy, nfontname, &missing, &nmissing, &def); + + free(pattern2); + +#endif + + if(missing!=NULL) + XFreeStringList(missing); + + return fs; +} diff --git a/de/fontset.h b/de/fontset.h new file mode 100644 index 0000000..256d1d9 --- /dev/null +++ b/de/fontset.h @@ -0,0 +1,20 @@ +/* + * ion/de/fontset.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_DE_FONTSET_H +#define ION_DE_FONTSET_H + +#include +#include + +extern XFontSet de_create_font_set(const char *fontname); + +#endif /* ION_DE_FONTSET_H */ diff --git a/de/init.c b/de/init.c new file mode 100644 index 0000000..3e779b3 --- /dev/null +++ b/de/init.c @@ -0,0 +1,369 @@ +/* + * ion/de/init.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include "brush.h" +#include "font.h" +#include "colour.h" +#include "private.h" +#include "init.h" +#include "exports.h" + + +/*{{{ Borders */ + + +#define CF_BORDER_VAL_SANITY_CHECK 16 + +void de_get_border_val(uint *val, ExtlTab tab, const char *what) +{ + int g; + + if(extl_table_gets_i(tab, what, &g)){ + if(g>CF_BORDER_VAL_SANITY_CHECK || g<0) + warn(TR("Border attribute %s sanity check failed."), what); + else + *val=g; + } +} + + +void de_get_border_style(uint *ret, ExtlTab tab) +{ + char *style=NULL; + + if(!extl_table_gets_s(tab, "border_style", &style)) + return; + + if(strcmp(style, "inlaid")==0) + *ret=DEBORDER_INLAID; + else if(strcmp(style, "elevated")==0) + *ret=DEBORDER_ELEVATED; + else if(strcmp(style, "groove")==0) + *ret=DEBORDER_GROOVE; + else if(strcmp(style, "ridge")==0) + *ret=DEBORDER_RIDGE; + else + warn(TR("Unknown border style \"%s\"."), style); + + free(style); +} + + +void de_get_border(DEBorder *border, ExtlTab tab) +{ + de_get_border_val(&(border->sh), tab, "shadow_pixels"); + de_get_border_val(&(border->hl), tab, "highlight_pixels"); + de_get_border_val(&(border->pad), tab, "padding_pixels"); + de_get_border_style(&(border->style), tab); +} + + +/*}}}*/ + + +/*{{{ Colours */ + + +bool de_get_colour(WRootWin *rootwin, DEColour *ret, + ExtlTab tab, DEStyle *based_on, + const char *what, DEColour substitute) +{ + char *name=NULL; + bool ok=FALSE; + + if(extl_table_gets_s(tab, what, &name)){ + ok=de_alloc_colour(rootwin, ret, name); + + if(!ok) + warn(TR("Unable to allocate colour \"%s\"."), name); + + free(name); + }else if(based_on!=NULL){ + return de_get_colour(rootwin, ret, based_on->data_table, + based_on->based_on, what, substitute); + } + + if(!ok) + ok=de_duplicate_colour(rootwin, substitute, ret); + + return ok; +} + + +void de_get_colour_group(WRootWin *rootwin, DEColourGroup *cg, + ExtlTab tab, DEStyle *based_on) +{ + de_get_colour(rootwin, &(cg->hl), tab, based_on, "highlight_colour", + DE_WHITE(rootwin)); + de_get_colour(rootwin, &(cg->sh), tab, based_on, "shadow_colour", + DE_WHITE(rootwin)); + de_get_colour(rootwin, &(cg->bg), tab, based_on, "background_colour", + DE_BLACK(rootwin)); + de_get_colour(rootwin, &(cg->fg), tab, based_on, "foreground_colour", + DE_WHITE(rootwin)); + de_get_colour(rootwin, &(cg->pad), tab, based_on, "padding_colour", + cg->bg); +} + + +void de_get_extra_cgrps(WRootWin *rootwin, DEStyle *style, ExtlTab tab) +{ + + uint i=0, nfailed=0, n=extl_table_get_n(tab); + char *name; + ExtlTab sub; + + if(n==0) + return; + + style->extra_cgrps=ALLOC_N(DEColourGroup, n); + + if(style->extra_cgrps==NULL) + return; + + for(i=0; iextra_cgrps+i-nfailed);*/ + style->extra_cgrps[i-nfailed].spec=name; + de_get_colour_group(rootwin, style->extra_cgrps+i-nfailed, sub, + style); + + extl_unref_table(sub); + continue; + + err: + warn(TR("Corrupt substyle table %d."), i); + nfailed++; + } + + if(n-nfailed==0){ + free(style->extra_cgrps); + style->extra_cgrps=NULL; + } + + style->n_extra_cgrps=n-nfailed; +} + + +/*}}}*/ + + +/*{{{ Misc. */ + + +void de_get_text_align(int *alignret, ExtlTab tab) +{ + char *align=NULL; + + if(!extl_table_gets_s(tab, "text_align", &align)) + return; + + if(strcmp(align, "left")==0) + *alignret=DEALIGN_LEFT; + else if(strcmp(align, "right")==0) + *alignret=DEALIGN_RIGHT; + else if(strcmp(align, "center")==0) + *alignret=DEALIGN_CENTER; + else + warn(TR("Unknown text alignment \"%s\"."), align); + + free(align); +} + + +void de_get_transparent_background(uint *mode, ExtlTab tab) +{ + bool b; + + if(extl_table_gets_b(tab, "transparent_background", &b)) + *mode=b; +} + + +/*}}}*/ + + +/*{{{ de_defstyle */ + + +void de_get_nonfont(WRootWin *rootwin, DEStyle *style, ExtlTab tab) +{ + DEStyle *based_on=style->based_on; + + style->data_table=extl_ref_table(tab); + + if(based_on!=NULL){ + style->border=based_on->border; + style->transparency_mode=based_on->transparency_mode; + style->textalign=based_on->textalign; + style->spacing=based_on->spacing; + } + + de_get_border(&(style->border), tab); + de_get_border_val(&(style->spacing), tab, "spacing"); + + de_get_text_align(&(style->textalign), tab); + + de_get_transparent_background(&(style->transparency_mode), tab); + + style->cgrp_alloced=TRUE; + de_get_colour_group(rootwin, &(style->cgrp), tab, based_on); + de_get_extra_cgrps(rootwin, style, tab); +} + + +/*EXTL_DOC + * Define a style for the root window \var{rootwin}. + */ +EXTL_EXPORT +bool de_defstyle_rootwin(WRootWin *rootwin, const char *name, ExtlTab tab) +{ + DEStyle *style; + char *fnt; + uint n; + DEStyle *based_on=NULL; + char *based_on_name=NULL; + + if(name==NULL) + return FALSE; + + style=de_create_style(rootwin, name); + + if(style==NULL) + return FALSE; + + if(extl_table_gets_s(tab, "based_on", &based_on_name)){ + based_on=de_get_style(rootwin, based_on_name); + if(based_on==style){ + warn(TR("'based_on' for %s points back to the style itself."), + name); + }else if(based_on==NULL){ + warn(TR("Unknown base style \"%s\"."), based_on); + }else{ + style->based_on=based_on; + based_on->usecount++; + /* Copy simple parameters */ + } + free(based_on_name); + } + + de_get_nonfont(rootwin, style, tab); + + if(extl_table_gets_s(tab, "font", &fnt)){ + de_load_font_for_style(style, fnt); + free(fnt); + }else if(based_on!=NULL && based_on->font!=NULL){ + de_set_font_for_style(style, based_on->font); + } + + if(style->font==NULL) + de_load_font_for_style(style, CF_FALLBACK_FONT_NAME); + + return TRUE; +} + + +/*EXTL_DOC + * Define a style. + */ +EXTL_EXPORT +bool de_defstyle(const char *name, ExtlTab tab) +{ + bool ok=TRUE; + WRootWin *rw; + + FOR_ALL_ROOTWINS(rw){ + if(!de_defstyle_rootwin(rw, name, tab)) + ok=FALSE; + } + + return ok; +} + + +/*EXTL_DOC + * Define a substyle. + */ +EXTL_SAFE +EXTL_EXPORT +ExtlTab de_substyle(const char *pattern, ExtlTab tab) +{ + extl_table_sets_s(tab, "substyle_pattern", pattern); + return extl_ref_table(tab); +} + + +/*}}}*/ + + +/*{{{ Module initialisation */ + + +#include "../version.h" + +char de_ion_api_version[]=ION_API_VERSION; + + +bool de_init() +{ + WRootWin *rootwin; + DEStyle *style; + + if(!de_register_exports()) + return FALSE; + + if(!gr_register_engine("de", (GrGetBrushFn*)&de_get_brush)) + goto fail; + + /* Create fallback brushes */ + FOR_ALL_ROOTWINS(rootwin){ + style=de_create_style(rootwin, "*"); + if(style!=NULL){ + style->is_fallback=TRUE; + de_load_font_for_style(style, CF_FALLBACK_FONT_NAME); + } + } + + return TRUE; + +fail: + de_unregister_exports(); + return FALSE; +} + + +void de_deinit() +{ + gr_unregister_engine("de"); + de_unregister_exports(); + de_deinit_styles(); +} + + +/*}}}*/ + diff --git a/de/init.h b/de/init.h new file mode 100644 index 0000000..f3b6f10 --- /dev/null +++ b/de/init.h @@ -0,0 +1,41 @@ +/* + * ion/de/init.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_DE_INIT_H +#define ION_DE_INIT_H + +#include +#include +#include "brush.h" + +extern void de_get_border_val(uint *val, ExtlTab tab, const char *what); +extern void de_get_border_style(uint *ret, ExtlTab tab); +extern void de_get_border(DEBorder *border, ExtlTab tab); + +extern bool de_get_colour(WRootWin *rootwin, DEColour *ret, + ExtlTab tab, DEStyle *based_on, + const char *what, DEColour substitute); +extern void de_get_colour_group(WRootWin *rootwin, DEColourGroup *cg, + ExtlTab tab, DEStyle *based_on); +extern void de_get_extra_cgrps(WRootWin *rootwin, DEStyle *style, + ExtlTab tab); + +extern void de_get_text_align(int *alignret, ExtlTab tab); + +extern void de_get_transparent_background(uint *mode, ExtlTab tab); + +extern void de_get_nonfont(WRootWin *rw, DEStyle *style, ExtlTab tab); + +extern bool de_defstyle_rootwin(WRootWin *rootwin, const char *name, + ExtlTab tab); +extern bool de_defstyle(const char *name, ExtlTab tab); + +#endif /* ION_DE_INIT_H */ diff --git a/de/private.h b/de/private.h new file mode 100644 index 0000000..39141e6 --- /dev/null +++ b/de/private.h @@ -0,0 +1,21 @@ +/* + * ion/de/private.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_DE_PRIVATE_H +#define ION_DE_PRIVATE_H + +#define DE_SUB_IND " ->" +#define DE_SUB_IND_LEN 3 + +#define MATCHES(S, A) (gr_stylespec_score(S, A)>0) +#define MATCHES2(S, A1, A2) (gr_stylespec_score2(S, A1, A2)>0) + +#endif /* ION_DE_PRIVATE_H */ diff --git a/de/style.c b/de/style.c new file mode 100644 index 0000000..cf44e47 --- /dev/null +++ b/de/style.c @@ -0,0 +1,325 @@ +/* + * ion/de/style.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include + +#include + +#include +#include +#include +#include + +#include "brush.h" +#include "font.h" +#include "colour.h" +#include "private.h" +#include "style.h" + + +/*{{{ GC creation */ + + +static void create_normal_gc(DEStyle *style, WRootWin *rootwin) +{ + XGCValues gcv; + ulong gcvmask; + GC gc; + + /* Create normal gc */ + gcv.line_style=LineSolid; + gcv.line_width=1; + gcv.join_style=JoinBevel; + gcv.cap_style=CapButt; + gcv.fill_style=FillSolid; + gcvmask=(GCLineStyle|GCLineWidth|GCFillStyle| + GCJoinStyle|GCCapStyle); + + style->normal_gc=XCreateGC(ioncore_g.dpy, WROOTWIN_ROOT(rootwin), + gcvmask, &gcv); +} + + +void destyle_create_tab_gcs(DEStyle *style) +{ + Display *dpy=ioncore_g.dpy; + WRootWin *rootwin=style->rootwin; + Window root=WROOTWIN_ROOT(rootwin); + Pixmap stipple_pixmap; + XGCValues gcv; + ulong gcvmask; + GC tmp_gc; + + /* Create a temporary 1-bit GC for drawing the tag and stipple pixmaps */ + stipple_pixmap=XCreatePixmap(dpy, root, 2, 2, 1); + gcv.foreground=1; + tmp_gc=XCreateGC(dpy, stipple_pixmap, GCForeground, &gcv); + + /* Create stipple pattern and stipple GC */ + XDrawPoint(dpy, stipple_pixmap, tmp_gc, 0, 0); + XDrawPoint(dpy, stipple_pixmap, tmp_gc, 1, 1); + XSetForeground(dpy, tmp_gc, 0); + XDrawPoint(dpy, stipple_pixmap, tmp_gc, 1, 0); + XDrawPoint(dpy, stipple_pixmap, tmp_gc, 0, 1); + + gcv.fill_style=FillStippled; + /*gcv.function=GXclear;*/ + gcv.stipple=stipple_pixmap; + gcvmask=GCFillStyle|GCStipple/*|GCFunction*/; + if(style->font!=NULL && style->font->fontstruct!=NULL){ + gcv.font=style->font->fontstruct->fid; + gcvmask|=GCFont; + } + + style->stipple_gc=XCreateGC(dpy, root, gcvmask, &gcv); + XCopyGC(dpy, style->normal_gc, + GCLineStyle|GCLineWidth|GCJoinStyle|GCCapStyle, + style->stipple_gc); + + XFreePixmap(dpy, stipple_pixmap); + + /* Create tag pixmap and copying GC */ + style->tag_pixmap_w=5; + style->tag_pixmap_h=5; + style->tag_pixmap=XCreatePixmap(dpy, root, 5, 5, 1); + + XSetForeground(dpy, tmp_gc, 0); + XFillRectangle(dpy, style->tag_pixmap, tmp_gc, 0, 0, 5, 5); + XSetForeground(dpy, tmp_gc, 1); + XFillRectangle(dpy, style->tag_pixmap, tmp_gc, 0, 0, 5, 2); + XFillRectangle(dpy, style->tag_pixmap, tmp_gc, 3, 2, 2, 3); + + gcv.foreground=DE_BLACK(rootwin); + gcv.background=DE_WHITE(rootwin); + gcv.line_width=2; + gcvmask=GCLineWidth|GCForeground|GCBackground; + + style->copy_gc=XCreateGC(dpy, root, gcvmask, &gcv); + + XFreeGC(dpy, tmp_gc); + + style->tabbrush_data_ok=TRUE; +} + + +/*}}}*/ + + +/*{{{ Style lookup */ + + +static DEStyle *styles=NULL; + + +DEStyle *de_get_style(WRootWin *rootwin, const char *stylename) +{ + DEStyle *style, *maxstyle=NULL; + int score, maxscore=0; + + for(style=styles; style!=NULL; style=style->next){ + if(style->rootwin!=rootwin) + continue; + score=gr_stylespec_score(style->style, stylename); + if(score>maxscore){ + maxstyle=style; + maxscore=score; + } + } + + return maxstyle; +} + + +/*}}}*/ + + +/*{{{ Style initialisation and deinitialisation */ + + +void destyle_unref(DEStyle *style) +{ + style->usecount--; + if(style->usecount==0){ + destyle_deinit(style); + free(style); + } +} + + +void destyle_deinit(DEStyle *style) +{ + int i; + + UNLINK_ITEM(styles, style, next, prev); + + if(style->style!=NULL) + free(style->style); + + if(style->font!=NULL){ + de_free_font(style->font); + style->font=NULL; + } + + if(style->cgrp_alloced) + de_free_colour_group(style->rootwin, &(style->cgrp)); + + for(i=0; in_extra_cgrps; i++) + de_free_colour_group(style->rootwin, style->extra_cgrps+i); + + if(style->extra_cgrps!=NULL) + free(style->extra_cgrps); + + extl_unref_table(style->data_table); + + XFreeGC(ioncore_g.dpy, style->normal_gc); + + if(style->tabbrush_data_ok){ + XFreeGC(ioncore_g.dpy, style->copy_gc); + XFreeGC(ioncore_g.dpy, style->stipple_gc); + XFreePixmap(ioncore_g.dpy, style->tag_pixmap); + } + + XSync(ioncore_g.dpy, False); + + if(style->based_on!=NULL){ + destyle_unref(style->based_on); + style->based_on=NULL; + } +} + + +static void dump_style(DEStyle *style) +{ + /* Allow the style still be used but get if off the list. */ + UNLINK_ITEM(styles, style, next, prev); + destyle_unref(style); +} + + +bool destyle_init(DEStyle *style, WRootWin *rootwin, const char *name) +{ + style->style=scopy(name); + if(style->style==NULL) + return FALSE; + + style->based_on=NULL; + + style->usecount=1; + /* Fallback brushes are not released on de_reset() */ + style->is_fallback=FALSE; + + style->rootwin=rootwin; + + style->border.sh=1; + style->border.hl=1; + style->border.pad=1; + style->border.style=DEBORDER_INLAID; + + style->spacing=0; + + style->textalign=DEALIGN_CENTER; + + style->cgrp_alloced=FALSE; + style->cgrp.spec=NULL; + style->cgrp.bg=DE_BLACK(rootwin); + style->cgrp.pad=DE_BLACK(rootwin); + style->cgrp.fg=DE_WHITE(rootwin); + style->cgrp.hl=DE_WHITE(rootwin); + style->cgrp.sh=DE_WHITE(rootwin); + + style->font=NULL; + + style->transparency_mode=GR_TRANSPARENCY_NO; + + style->n_extra_cgrps=0; + style->extra_cgrps=NULL; + + style->data_table=extl_table_none(); + + create_normal_gc(style, rootwin); + + style->tabbrush_data_ok=FALSE; + + return TRUE; +} + + +static DEStyle *do_create_style(WRootWin *rootwin, const char *name) +{ + DEStyle *style=ALLOC(DEStyle); + if(style!=NULL){ + if(!destyle_init(style, rootwin, name)){ + free(style); + return NULL; + } + } + return style; +} + + +DEStyle *de_create_style(WRootWin *rootwin, const char *name) +{ + DEStyle *oldstyle, *style; + + style=do_create_style(rootwin, name); + + if(style==NULL) + return NULL; + + for(oldstyle=styles; oldstyle!=NULL; oldstyle=oldstyle->next){ + if(oldstyle->rootwin==rootwin && oldstyle->style!=NULL && + strcmp(oldstyle->style, name)==0){ + break; + } + } + + if(oldstyle!=NULL && !oldstyle->is_fallback) + dump_style(oldstyle); + + LINK_ITEM_FIRST(styles, style, next, prev); + + return style; +} + + + +/*EXTL_DOC + * Clear all styles from drawing engine memory. + */ +EXTL_EXPORT +void de_reset() +{ + DEStyle *style, *next; + for(style=styles; style!=NULL; style=next){ + next=style->next; + if(!style->is_fallback) + dump_style(style); + } +} + + +void de_deinit_styles() +{ + DEStyle *style, *next; + for(style=styles; style!=NULL; style=next){ + next=style->next; + if(style->usecount>1){ + warn(TR("Style %s still in use [%d] but the module " + "is being unloaded!"), style->style, style->usecount); + } + dump_style(style); + } +} + + +/*}}}*/ + + diff --git a/de/style.h b/de/style.h new file mode 100644 index 0000000..f25c44c --- /dev/null +++ b/de/style.h @@ -0,0 +1,97 @@ +/* + * ion/de/style.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_DE_STYLE_H +#define ION_DE_STYLE_H + +#include + +#include +#include +#include + +INTRSTRUCT(DEBorder); +INTRSTRUCT(DEStyle); + +#include "font.h" +#include "colour.h" + +enum{ + DEBORDER_INLAID=0, /* -\xxxxxx/- */ + DEBORDER_RIDGE, /* /-\xxxx/-\ */ + DEBORDER_ELEVATED, /* /-xxxxxx-\ */ + DEBORDER_GROOVE /* \_/xxxx\_/ */ +}; + + +enum{ + DEALIGN_LEFT=0, + DEALIGN_RIGHT=1, + DEALIGN_CENTER=2 +}; + + +DECLSTRUCT(DEBorder){ + uint sh, hl, pad; + uint style; +}; + + +DECLSTRUCT(DEStyle){ + char *style; + int usecount; + bool is_fallback; + + WRootWin *rootwin; + + DEStyle *based_on; + + GC normal_gc; + + DEBorder border; + bool cgrp_alloced; + DEColourGroup cgrp; + int n_extra_cgrps; + DEColourGroup *extra_cgrps; + GrTransparency transparency_mode; + DEFont *font; + int textalign; + uint spacing; + + ExtlTab data_table; + + /* Only initialised if used as a DETabBrush */ + bool tabbrush_data_ok; + GC stipple_gc; + GC copy_gc; + + Pixmap tag_pixmap; + int tag_pixmap_w; + int tag_pixmap_h; + + DEStyle *next, *prev; +}; + + +extern bool destyle_init(DEStyle *style, WRootWin *rootwin, const char *name); +extern void destyle_deinit(DEStyle *style); +extern DEStyle *de_create_style(WRootWin *rootwin, const char *name); +extern void destyle_unref(DEStyle *style); + +extern void destyle_create_tab_gcs(DEStyle *style); + +extern void de_reset(); +extern void de_deinit_styles(); + +extern DEStyle *de_get_style(WRootWin *rootwin, const char *name); + + +#endif /* ION_DE_STYLE_H */ diff --git a/doc/ChangeLog b/doc/ChangeLog new file mode 100644 index 0000000..5e3a3d1 --- /dev/null +++ b/doc/ChangeLog @@ -0,0 +1,667 @@ +2006-12-23 15:00 UTC Tuomo Valkonen + tagged ion-doc-3ds-20061223 + +2006-10-30 21:08 UTC Tuomo Valkonen + * Updates + +2006-10-28 23:16 UTC Tuomo Valkonen + tagged ion-doc-3ds-20061029 + +2006-10-17 22:02 UTC Tuomo Valkonen + * Mention the 'float' winprop + +2006-10-15 17:53 UTC Tuomo Valkonen + tagged ion-doc-3ds-20061015 + +2006-10-15 14:36 UTC Tuomo Valkonen + * Documentation updates. + +2006-08-03 09:05 UTC Tuomo Valkonen + * much->match + +2006-06-20 18:16 UTC Tuomo Valkonen + tagged ion-doc-3ds-20060620 + +2006-06-08 17:58 UTC Tuomo Valkonen + * Moving usepackage{dvipdfm} before everything else seems to fix things. + +2006-02-12 15:19 UTC Tuomo Valkonen + * Added documentation for region_activity_hook. + +2006-01-25 23:23 UTC Tuomo Valkonen + * Updated bar_inside_border stuf. + +2006-01-07 21:03 UTC Tuomo Valkonen + tagged ion-doc-3ds-20060107 + +2005-12-10 20:46 UTC Tuomo Valkonen + tagged ion-doc-3ds-20051210 + +2005-12-10 00:39 UTC Tuomo Valkonen + * Added missing min_size winprop and changes in winprop listing. + +2005-11-10 20:22 UTC Tuomo Valkonen + * Updated frame style documentation. + +2005-10-23 22:40 UTC Tuomo Valkonen + tagged ion-doc-3ds-20051023 + +2005-08-26 17:37 UTC Tuomo Valkonen + * List oneshot winprop. + +2005-08-20 11:37 UTC Tuomo Valkonen + tagged ion-doc-3ds-20050820 + +2005-07-28 17:40 UTC Tuomo Valkonen + tagged ion-doc-3ds-20050728 + +2005-06-25 15:16 UTC Tuomo Valkonen + tagged ion-doc-3ds-20050625 + +2005-06-07 13:14 UTC Tuomo Valkonen + tagged ion-doc-3ds-20050607 + +2005-06-07 13:13 UTC Tuomo Valkonen + * predist.sh updates. + +2005-06-01 17:07 UTC Tuomo Valkonen + * Added a note on NoModifier. + +2005-05-02 14:56 UTC Tuomo Valkonen + tagged ion-doc-3ds-20050502 + +2005-03-25 17:41 UTC Tuomo Valkonen + * Added documentation on ion-statusd monitors. + +2005-03-25 17:41 UTC Tuomo Valkonen + * Fixed a typo, etc. + +2005-03-22 14:34 UTC Tuomo Valkonen + tagged ion-doc-3ds-20050322 + +2005-03-21 00:16 UTC Tuomo Valkonen + * Fixed typos. + +2005-03-19 13:55 UTC Tuomo Valkonen + * Added documentation on ioncore_sigchld_hook. + +2005-03-13 12:34 UTC Tuomo Valkonen + * Fixed typos etc. + +2005-03-13 12:27 UTC Tuomo Valkonen + * fnrefx macro was still wrong. + +2005-03-09 23:12 UTC Tuomo Valkonen + * Moved new layer documentation to scripting chapter. + +2005-03-09 22:23 UTC Matthieu.Moy@imag.fr + * More doc on layer 2 and placement hooks + + Be warned that: + + * I missed some LaTeX package, and therefore couldn't compile my + changes, + + * I don't know ion's internal well enough to be sure of what I write. + + +2005-03-07 09:35 UTC Tuomo Valkonen + * Fixed copy-paste error. + +2005-03-04 08:39 UTC Tuomo Valkonen + tagged ion-doc-3ds-20050304 + +2005-03-03 18:42 UTC Tuomo Valkonen + * Added a note on hooks being called in protected mode. + +2005-03-03 18:39 UTC Tuomo Valkonen + * Updated ionws_placement_alt documentation. + +2005-02-27 13:19 UTC Tuomo Valkonen + tagged ion-doc-3ds-20050227 + +2005-02-22 22:54 UTC Tuomo Valkonen + tagged ion-doc-3-svn2darcs + +2005-01-16 11:52 UTC tuomov + tagged ion-doc-3ds-20050116 + +2005-01-16 11:29 UTC tuomov + * trunk: changeset 1931 + fixed predist.sh + +2005-01-16 11:08 UTC tuomov + * trunk: changeset 1930 + Fixed undefined references. + +2005-01-15 21:33 UTC tuomov + * trunk: changeset 1928 + Updated some scripting docs. + +2005-01-15 21:20 UTC tuomov + * trunk: changeset 1926 + tabularx header changes. + +2005-01-15 21:03 UTC tuomov + * trunk: changeset 1924 + Tuning for latex2html etc. + +2005-01-15 17:29 UTC tuomov + * trunk: changeset 1923 + Added hook reference and updated hook documentation. + +2005-01-15 17:29 UTC tuomov + * trunk: changeset 1922 + Improved function reference macros to use lists instead of tables. + +2005-01-14 14:47 UTC tuomov + * trunk: changeset 1920 + Some minor fixes. + +2005-01-14 14:25 UTC tuomov + * trunk: changeset 1918 + - Improved menu documentation. + + - Other minor improvements. + +2005-01-13 16:49 UTC tuomov + * trunk: changeset 1917 + Updated graphical styles documentation. + +2005-01-13 14:40 UTC tuomov + * trunk: changeset 1915 + Updated winprop documentation. + +2005-01-13 14:27 UTC tuomov + * trunk: changeset 1914 + Updated binding and menu definition documentation. + +2005-01-11 20:18 UTC tuomov + * trunk: changeset 1913 + Brought config file intro up-to-date + +2005-01-11 15:31 UTC tuomov + * trunk: changeset 1912 + Updated class hierarchy documentation etc. + +2004-10-11 14:16 UTC tuomov + * trunk: changeset 1821 + Just testing some settings... + +2004-10-09 06:16 UTC tuomov + * trunk: changeset 1804 + libextl related changes. + +2004-10-09 05:56 UTC tuomov + * trunk: changeset 1801 + Added predist.sh + +2004-10-09 05:56 UTC tuomov + * trunk: changeset 1800 + Fixed a typo. + +2004-09-16 22:04 UTC tuomov + * trunk: changeset 1754 + Source file path updates. + +2004-07-31 22:25 UTC tuomov + * trunk: changeset 1697 + Updated Makefile to point to the new location of mkexports.lua. + +2004-07-26 21:57 UTC tuomov + * trunk: changeset 1646 + Function reference generation updates. + +2004-05-14 12:29 UTC tuomov + * trunk: changeset 1485 + Fixed punctuation. + +2004-03-15 16:32 UTC tuomov + * trunk: changeset 1402 + Changed makefile to account for changes in Ion module directories. + +2004-03-15 16:23 UTC tuomov + * trunk: changeset 1400 + Added install target to Makefile. + +2004-03-15 08:35 UTC tuomov + * trunk: changeset 1398 + Brought document generation up-to-date with module name changes. + +2004-03-10 21:57 UTC tuomov + * trunk: changeset 1383 + Merged omission and other fixes from stable branch. + +2004-03-05 23:49 UTC tuomov + * trunk: changeset 1346 + Some more minor documentation fixes. Just to test this new svn setup. + +2004-03-05 23:00 UTC tuomov + * trunk: changeset 1345 + Minor documentation fixes. + +2004-02-16 20:33 UTC tuomov + * trunk: changeset 1323 + Build process changed for Ion3. + +2004-02-07 01:55 UTC tuomov + * trunk: changeset 1255 + Added documentation on alternative get_winprop implementations. + +2004-02-07 01:42 UTC tuomov + * trunk: changeset 1254 + Removed unobfuscated email addresses. + +2004-01-28 22:04 UTC tuomov + * trunk: changeset 1239 + Removed extra word, added function reference. + +2004-01-27 22:30 UTC tuomov + * trunk: changeset 1235 + Added 'are' in 'that they are transient for' + +2004-01-27 22:26 UTC tuomov + * trunk: changeset 1234 + Improved xprop/transient help and winprop examples. + +2004-01-27 13:54 UTC tuomov + * trunk: changeset 1231 + Minor spelling mistake fix. + +2004-01-25 18:42 UTC tuomov + * trunk: changeset 1225 + Some minor clarifications in a few sentences and put URL:s on their + own lines. + +2004-01-25 18:05 UTC tuomov + * trunk: changeset 1222 + Typo and spelling fixes. + +2004-01-23 17:32 UTC tuomov + * trunk: changeset 1215 + Fixed a few typos and added an example. + +2004-01-22 21:25 UTC tuomov + * trunk: changeset 1213 + Added dock documentation. + +2004-01-14 22:29 UTC tuomov + * trunk: changeset 1193 + Minor cleanup. + +2004-01-08 22:32 UTC tuomov + * trunk: changeset 1183 + Updated winprop documentation. + +2003-12-22 23:58 UTC tuomov + * trunk: changeset 162 + Added discussion on modules and improved the main introduction and + object hierarchy introduction. + +2003-12-21 00:17 UTC tuomov + * trunk: changeset 161 + Removed duplicate label. + +2003-12-20 19:49 UTC tuomov + * trunk: changeset 160 + Added a list of functions. + +2003-12-20 15:18 UTC tuomov + * trunk: changeset 159 + Fixed a typo. + +2003-12-20 15:15 UTC tuomov + * trunk: changeset 158 + Oops. + +2003-12-20 15:14 UTC tuomov + * trunk: changeset 157 + Added a section on object references. + +2003-12-19 22:08 UTC tuomov + * trunk: changeset 156 + Mention stylemenu. + +2003-12-18 21:37 UTC tuomov + * trunk: changeset 155 + Added a section on special menus. + +2003-12-23 20:18 UTC unknown + tagged ion-doc-20031211 + +2003-12-11 17:29 UTC tuomov + * trunk: changeset 154 + Updates for 20031211 release. + +2003-12-23 20:18 UTC unknown + tagged ion-doc-20031119 + +2003-11-19 21:57 UTC tuomov + * trunk: changeset 153 + Removed 'executive' from summary. + +2003-11-19 21:57 UTC tuomov + * trunk: changeset 152 + Removed mplexfns reference. + +2003-11-17 00:42 UTC tuomov + * trunk: changeset 151 + Added an "executive summary" to the object hierarchy chapter. + +2003-11-14 18:15 UTC tuomov + * trunk: changeset 150 + Documented menus. Changes to reflect changes in configuration files. + +2003-10-30 10:04 UTC tuomov + * trunk: changeset 149 + Get documentation from luaextl/*.c as well. + +2003-10-29 12:05 UTC tuomov + * trunk: changeset 148 + LICENSE file changed to GPL. + +2003-10-29 11:55 UTC tuomov + * trunk: changeset 147 + Removed fdl.tex. + +2003-10-29 11:55 UTC tuomov + * trunk: changeset 146 + Changed license to GPL due to problems with the GFDL. + +2003-10-29 11:54 UTC tuomov + * trunk: changeset 145 + Changed \chapter->\xchapter, \section*->\xsectionstar + +2003-10-29 11:22 UTC tuomov + * trunk: changeset 144 + Added gpl.tex; GNU General Public License. + +2003-10-29 11:20 UTC tuomov + * trunk: changeset 143 + Added menu reference and updated drawing engine documentation to + reflect the change in style names. + +2003-12-23 20:18 UTC unknown + tagged ion-doc-20030811 + +2003-08-10 23:45 UTC tuomov + * trunk: changeset 142 + Fixed a few more typos and such. + +2003-08-10 23:37 UTC tuomov + * trunk: changeset 141 + Fixed a typo. + +2003-08-10 23:37 UTC tuomov + * trunk: changeset 140 + The files draw-DISPLAY.SCREEN.lua were still being referenced to. + +2003-12-23 20:18 UTC unknown + tagged ion-doc-20030810 + +2003-08-09 12:48 UTC tuomov + * trunk: changeset 139 + Added a quote. + +2003-08-09 12:45 UTC tuomov + * trunk: changeset 138 + Added a few spaces. + +2003-08-09 12:45 UTC tuomov + * trunk: changeset 137 + Added a walk-through of ioncore.lua. + +2003-08-08 21:56 UTC tuomov + * trunk: changeset 136 + Added an introduction and some design decision rationale. + +2003-08-07 21:45 UTC tuomov + * trunk: changeset 135 + Added information on winprop name field and improved winprop lookup + order explanation. + +2003-08-06 15:08 UTC tuomov + * trunk: changeset 134 + look-cleanviolet.lua was changed and so the example. + +2003-08-06 15:06 UTC tuomov + * trunk: changeset 133 + The 'urgent' attributes was renamed 'activity'. + +2003-08-01 05:54 UTC tuomov + * trunk: changeset 132 + Added mention of ignore_cfgrq. + +2003-07-31 21:24 UTC tuomov + * trunk: changeset 131 + Started a new chapter on assorted tricks. + +2003-07-31 15:21 UTC tuomov + * trunk: changeset 130 + A few fixes. + +2003-07-31 15:17 UTC tuomov + * trunk: changeset 129 + Improved drawing engine and style documentation. + +2003-07-30 22:47 UTC tuomov + * trunk: changeset 128 + Added a \docode kludge to have latex2html interpret figure + environments instead of generating images. + +2003-07-30 22:27 UTC tuomov + * trunk: changeset 127 + Started documenting drawing engines. + +2003-07-25 20:48 UTC tuomov + * trunk: changeset 126 + Moved WRootWin under WWindow in class hierarchy. + +2003-07-24 19:35 UTC tuomov + * trunk: changeset 125 + Some bindings setup documentation improvements. + +2003-07-13 22:16 UTC tuomov + * trunk: changeset 124 + Changes to accommodate for more OO bindings. + +2003-12-23 20:18 UTC unknown + tagged ion-doc-20030710 + +2003-07-10 17:18 UTC tuomov + * trunk: changeset 123 + Class explanations improved. + +2003-12-23 20:18 UTC unknown + tagged ion-doc-20030628 + +2003-06-27 22:58 UTC tuomov + * trunk: changeset 122 + Typos etc. + +2003-06-27 20:56 UTC tuomov + * trunk: changeset 121 + Fixed QueryLib introduction. + +2003-06-17 21:02 UTC tuomov + * trunk: changeset 120 + Removed mention of region_close and make_current_or_self_fn as this + binding does not work. + +2003-06-17 10:25 UTC tuomov + * trunk: changeset 119 + Added notes on C coding style. + +2003-12-23 20:18 UTC unknown + tagged ion-doc-20030614 + +2003-06-14 19:06 UTC tuomov + * trunk: changeset 118 + Fixed "or"->"are". + +2003-06-14 18:01 UTC tuomov + * trunk: changeset 117 + \date{} setting moved to distribution building script. + +2003-06-14 17:54 UTC tuomov + * trunk: changeset 116 + Minor bug was fixed. + +2003-06-14 17:53 UTC tuomov + * trunk: changeset 115 + Some clarifications were made in a few explanations and a note on + anonymous functions was added. + +2003-06-14 17:53 UTC tuomov + * trunk: changeset 114 + Redefined the itemize environment to look better. + +2003-06-14 15:10 UTC tuomov + * trunk: changeset 113 + Updated the documentation to use the new binding setting functions. + +2003-06-08 17:58 UTC tuomov + * trunk: changeset 112 + Added FDL as a plaintext file LICENSE. + +2003-06-08 17:56 UTC tuomov + * trunk: changeset 111 + Documentation updated to mention WMPlexes. + +2003-12-23 20:18 UTC unknown + tagged ion-doc-20030531 + +2003-05-31 15:39 UTC tuomov + * trunk: changeset 110 + Dates updated. + +2003-05-30 21:32 UTC tuomov + * trunk: changeset 109 + Updated the parts that refer to the files that were moved to SHAREDIR. + +2003-05-29 13:11 UTC tuomov + * trunk: changeset 108 + Added a note on char* and const char *. + +2003-05-29 12:51 UTC tuomov + * trunk: changeset 107 + Added one more note on deferred destroy. + +2003-05-29 12:47 UTC tuomov + * trunk: changeset 106 + Added a note on deferred destroy. + +2003-05-17 13:33 UTC tuomov + * trunk: changeset 105 + Updated the documentation to switch to using WRootWins for X screens + and WScreens for physical screens. + +2003-05-13 05:22 UTC tuomov + * trunk: changeset 104 + Some minor fixes. + +2003-12-23 20:18 UTC unknown + tagged ion-doc-20030510 + +2003-05-10 20:09 UTC tuomov + * trunk: changeset 103 + Removed repeated word. + +2003-05-10 20:02 UTC tuomov + * trunk: changeset 102 + Fixed some typos. + +2003-05-10 18:55 UTC tuomov + * trunk: changeset 101 + Oops. The all-ps rule was broken. + +2003-05-10 18:51 UTC tuomov + * trunk: changeset 100 + License notes changed to use \copyright instead of "(c)" + +2003-05-10 18:28 UTC tuomov + * trunk: changeset 99 + A README was added. + +2003-05-10 18:26 UTC tuomov + * trunk: changeset 98 + File removed. + +2003-05-10 18:26 UTC tuomov + * trunk: changeset 97 + Object system implementation was split out from the chapter/section on + object and class hierarchies. + +2003-05-10 18:25 UTC tuomov + * trunk: changeset 96 + Added implementation of artikel3 for latex2html. + +2003-05-10 18:24 UTC tuomov + * trunk: changeset 95 + Implementation of rapport3 for latex2html was added. + +2003-05-10 18:24 UTC tuomov + * trunk: changeset 94 + The macros were cleaned up + +2003-05-10 18:24 UTC tuomov + * trunk: changeset 93 + Better Makefile + +2003-05-10 18:23 UTC tuomov + * trunk: changeset 92 + FDL was modified to use the \xchapter etc. macros to enable embedding + it in articles. + +2003-05-10 18:23 UTC tuomov + * trunk: changeset 91 + Clarifications (hopefully) ein the explanation of the object and class + hierarchies. + +2003-05-10 18:22 UTC tuomov + * trunk: changeset 90 + The configuration files were documented. + +2003-05-10 18:22 UTC tuomov + * trunk: changeset 89 + The original document "Ion: Objects and extending" was split into + separate documents "Ion: Configuring and extending with Lua" and "Ion: + notes for the module and patch writer". + +2003-05-10 15:50 UTC tuomov + * trunk: changeset 88 + Added the GNU Free Documentation License. + +2003-05-08 18:31 UTC tuomov + * trunk: changeset 87 + Added more documentation on binding configuration. + +2003-05-07 18:43 UTC tuomov + * trunk: changeset 86 + Added QueryLib and Ioncorelib references. Moved function reference + .tex generation code here from ion. + +2003-05-06 19:46 UTC tuomov + * trunk: changeset 85 + Did some reorganization and added links to Lua documentation. + +2003-05-06 17:48 UTC tuomov + * trunk: changeset 84 + Added a \hline in the type table. + +2003-05-06 17:35 UTC tuomov + * trunk: changeset 83 + Added Makefile rules to generate postscript and pdf + +2003-05-06 17:33 UTC tuomov + * trunk: changeset 82 + Added a Makefile + +2005-02-16 07:18 UTC tailor@f281.ttorni.ton.tut.fi + * Tailorization of trunk + Import of the upstream sources from the repository + + http://tao.uab.es/ion/svn/ion-doc/trunk + + as of revision 80 + diff --git a/doc/LICENSE b/doc/LICENSE new file mode 100644 index 0000000..d60c31a --- /dev/null +++ b/doc/LICENSE @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 0000000..bd580ca --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,136 @@ +# Settings +###################################### + +TOPDIR=../ion-3 + +include $(TOPDIR)/build/system-inc.mk + +L2H=latex2html -show_section_numbers -short_index -local_icons -noaddress \ + -up_url http://iki.fi/tuomov/ion/ -up_title "Ion homepage" -nofootnode\ +## -style greyviolet.css + + +# Function documentation to build +###################################### + +DOCS=ionconf ionnotes + +FNTEXES=ioncore-fns.tex mod_tiling-fns.tex \ + mod_query-fns.tex de-fns.tex mod_menu-fns.tex \ + mod_dock-fns.tex mod_sp-fns.tex + +# Generic rules +###################################### + +nothing: + @ echo "Please read the README first." + +%.ps: %.dvi + dvips $< + +%.pdf: %.dvi + dvipdfm -p a4 $< + +%.dvi: %.tex + latex $< + +# Install +###################################### + +install: + $(INSTALLDIR) $(DOCDIR); \ + for d in $(DOCS); do \ + for e in ps pdf dvi; do \ + test -f $$d.$$e && $(INSTALL) -m $(DATA_MODE) $$d.$$e $(DOCDIR); \ + done; \ + $(INSTALLDIR) $(DOCDIR)/$$d; \ + for i in $$d/*; do \ + $(INSTALL) -m $(DATA_MODE) $$i $(DOCDIR)/$$i; \ + done; \ + done + +# ionconf rules +###################################### +ionconf-dvi-full: + latex ionconf + latex ionconf + latex ionconf + makeindex ionconf.idx + latex ionconf + +ionconf-html: + $(L2H) -split 3 ionconf + +fntexes: $(FNTEXES) + +ionconf-all: fntexes fnlist.tex ionconf-dvi-full ionconf-html + +# ionnotes rules +###################################### + +ionnotes-dvi-full: + latex ionnotes + latex ionnotes + latex ionnotes + makeindex ionnotes.idx + latex ionnotes + +ionnotes-html: + $(L2H) -split 4 ionnotes + +ionnotes-all: ionnotes-dvi-full ionnotes-html + +# More generic rules +###################################### + +all: ionconf-all ionnotes-all + +all-ps: ionconf.ps ionnotes.ps + +all-pdf: ionconf.pdf ionnotes.pdf + + +# Clean +###################################### + +clean: + rm -f $(FNTEXES) fnlist.tex + rm -f *.aux *.toc *.log + rm -f *.idx *.ild *.ilg *.ind + +realclean: clean + rm -f *.ps *.pdf *.dvi + rm -rf $(DOCS) + + +# Function reference rules +###################################### + +ioncore-fns.tex: $(TOPDIR)/ioncore/*.c $(TOPDIR)/ioncore/*.lua + $(MKEXPORTS) -module ioncore -mkdoc -o $@ $+ + +mod_tiling-fns.tex: $(TOPDIR)/mod_tiling/*.c + $(MKEXPORTS) -module mod_tiling -mkdoc -o $@ $+ + +mod_query-fns.tex: $(TOPDIR)/mod_query/*.c $(TOPDIR)/mod_query/mod_query.lua + $(MKEXPORTS) -module mod_query -mkdoc -o $@ $+ + +mod_menu-fns.tex: $(TOPDIR)/mod_menu/*.c $(TOPDIR)/mod_menu/mod_menu.lua + $(MKEXPORTS) -module mod_menu -mkdoc -o $@ $+ + +mod_dock-fns.tex: $(TOPDIR)/mod_dock/*.c + $(MKEXPORTS) -module mod_dock -mkdoc -o $@ $+ + +mod_sp-fns.tex: $(TOPDIR)/mod_sp/*.c + $(MKEXPORTS) -module mod_sp -mkdoc -o $@ $+ + +de-fns.tex: $(TOPDIR)/de/*.c + $(MKEXPORTS) -module de -mkdoc -o $@ $+ + +# Function list +###################################### + +fnlist.tex: $(FNTEXES) + grep hyperlabel $+ | \ + sed 's/.*fn:\([^}]*\).*/\\fnlisti{\1}/;'|sort -d -f \ + > $@ diff --git a/doc/README b/doc/README new file mode 100644 index 0000000..604bf85 --- /dev/null +++ b/doc/README @@ -0,0 +1,23 @@ + +Ion-doc + +Tuomo Valkonen + + +This package contains some (advanced user and module writer) +documentation for the Ion window manager. + +You will need to install latex2html to build the HTML documents. + +How to build: + + 1. Modify TOPDIR in Makefile point to your top-level Ion source + directory, containing a working system.mk (or system-ac.mk). + + 3. After the above has been done, you may run 'make all' to build the + (hyperlinked) DVI and HTML versions of the documents. You may have + increase TeX pool size. + + 4. If you want Postcript versions of the documents, run 'make all-ps'. + For PDF you need to have dvipdfm installed. The PDF:s are obviously + built with 'make all-pdf'. diff --git a/doc/artikel3.perl b/doc/artikel3.perl new file mode 100644 index 0000000..110f32c --- /dev/null +++ b/doc/artikel3.perl @@ -0,0 +1,12 @@ +# artikel3.perl by Tuomo Valkonen, , 2003-05-10 +# +# Implementation of the documentclass for latex2html. Just load +# article. +# + +package main; + +&do_require_package("article"); + +1; + diff --git a/doc/conf-bindings.tex b/doc/conf-bindings.tex new file mode 100644 index 0000000..9afb2b8 --- /dev/null +++ b/doc/conf-bindings.tex @@ -0,0 +1,269 @@ +\section{Keys and rodents} +\label{sec:bindings} + +In the stock configuration file setup, most key and mouse bindings are set +from the file \file{cfg\_ioncore.lua} while module-specific bindings +are set from the modules' main configuration files (\file{cfg\_modname.lua}). +This, however, does not have to be so as long as the module has been +loaded prior to defining any module-specific bindings. + +Bindings are defined by calling the function +\fnrefx{ioncore}{defbindings} with the ''context'' of the +bindings and the a table of new bindings to make. The context is simply +string indicating one of the classes of regions (or modes such as +\type{WMoveresMode}) introduced in section \ref{sec:objects}, and fully +listed in appendix \ref{app:fullhierarchy}, although not all define +a binding map. For example, the following skeleton would be used to +define new bindings for all frames: + +\begin{verbatim} +defbindings("WFrame", { + -- List of bindings to make goes here. +}) +\end{verbatim} + +There has been some confusion among users about the need to define the +''context'' for each binding, so let me try to explain this design +decision here. The thing is that if there was a just a simple 'bind this +key to this action' method without knowledge of the context, some +limitations would have to be made on the available actions and writing +custom handlers would be more complicated. In addition one may want to +bind the same function to different key for different types of objects. +Indeed, the workspace and frame tab switching functions are the same both +classes being based on \type{WMPlex}, and in the stock configuration the +switch to $n$:th workspaces is bound to \key{Mod1+n} while the switch to +$n$:th tab is bound to the sequence \key{Mod1+k n}. + +Currently known ''contexts'' include: +\code{WScreen}, +\code{WMPlex}, +\code{WMPlex.toplevel}, +\code{WFrame}, +\code{WFrame.toplevel}, +\code{WFrame.floating}, +\code{WFrame.tiled}, +\code{WFrame.transient}, +\code{WMoveresMode}, +\code{WGroup}, +\code{WGroupCW}, +\code{WGroupWS}, +\code{WClientWin}, +\code{WTiling}, and +\code{WStatusBar}. +Most of these should be self-explanatory, corresponding to objects +of class with the same name. The ones with \code{.toplevel} suffix +refer to screens and ''toplevel'' frames, i.e. frames that are +not used for transient windows. Likewise \code{.transient} refers +to frames in transient mode, and \code{.tiled} and \code{.floating} +to frames in, respectively, tiled and floating modes. + + + +The following subsections describe how to construct elements of the +binding table. Note that \fnrefx{ioncore}{defbindings} adds +the the newly defined bindings to the previous bindings of the context, +overriding duplicates. To unbind an event, set the handler parameter +to \code{nil} for each of the functions to be described in the following +subsections. + +Also note that when multiple objects want to handle a binding, the +innermost (when the root window is considered the outermost) active object +in the parent--child hierarchy (see Figure \ref{fig:parentship}) of objects +gets to handle the action. + + +\subsection{Binding handlers and special variables} + +Unlike in Ion2, in Ion3 binding handlers are not normally passed as +''anonymous functions'', although this is still possible. The preferred +method now is to pass the code of the handler as a string. Two special +variables are available in this code. These are + +\begin{tabularx}{\linewidth}{lX} + \tabhead{Variable & Description} + \code{_} (underscore) & + Reference to the object on which the + binding was triggered. The object is of the same class as the the + context of the \fnrefx{ioncore}{defbindings} call + defining the binding. \\ + \code{_sub} & + Usually, the currently active \emph{managed object} of the + object referred to by \code{_}, but sometimes (e.g. mouse actions + on tabs of frames) something else relevant to the action triggering + the binding. \\ + \code{_chld} & + Object corresponding to the currently active child window of the + object referred to by \code{_}. +\end{tabularx} + +For example, supposing '\code{_}' is a \type{WFrame}, the following +handler should move the active window to the right, if possible: + +\begin{verbatim} +"_:inc_index(_sub)" +\end{verbatim} + +\subsection{Guards} + +To suppress error messages, each binding handler may also be accompanied +by a ''guard'' expression that blocks the handler from being called when +the guard condition is not met. Currently the following guard expressions +are supported (for both \code{_sub} and \code{_chld}): + +\begin{tabularx}{\linewidth}{lX} + \tabhead{Guard & Description} + \code{"_sub:non-nil"} & The \code{_sub} parameter must be set. \\ + \code{"_sub:SomeClass"} & The \code{_sub} parameter must be member + of class \type{SomeClass}. \\ +\end{tabularx} + + +\subsection{Defining the bindings} +\label{sec:binddef} + +The descriptions of the individual bindings in the binding table argument +to \fnrefx{ioncore}{defbindings} should be constructed with the following +functions. + +Key presses: +\begin{itemize} + \item \fnref{kpress}\code{(keyspec, handler [, guard])}, + \item \fnref{kpress_wait}\code{(keyspec, handler [, guard])} and + \item \fnref{submap}\code{(keyspec, \{ ... more key bindings ... \})}. +\end{itemize} +Mouse actions: +\begin{itemize} + \item \fnref{mclick}\code{(buttonspec, handler [, guard])}, + \item \fnref{mdblclick}\code{(buttonspec, handler [, guard])}, + \item \fnref{mpress}\code{(buttonspec, handler [, guard])} and + \item \fnref{mdrag}\code{(buttonspec, handler [, guard])}. +\end{itemize} + +The actions that most of these functions correspond to should be clear +and as explained in the reference, \fnref{kpress_wait} is simply +\fnref{kpress} with a flag set instructing Ioncore wait for all +modifiers to be released before processing any further actions. +This is to stop one from accidentally calling e.g. +\fnref{WRegion.rqclose} multiple times in a row. The \fnref{submap} +function is used to define submaps or ''prefix maps''. The second +argument to this function is table listing the key press actions +(\fnref{kpress}) in the submap + +The parameters \var{keyspec} and \var{buttonspec} are explained below +in detail. The parameter \var{handler} is the handler for the binding, +and the optional parameter \var{guard} its guard. These should normally +be strings as explained above. + +\subsection{Examples} + +For example, to just bind the key \key{Mod1+1} to switch to the first +workspace and \key{Mod1+Right} to the next workspace, you would make the +following call +\begin{verbatim} +defbindings("WScreen", { + kpress("Mod1+Right", "_:switch_next()"), + kpress("Mod1+1", "_:switch_nth(1)"), +}) +\end{verbatim} + +Note that \code{_:switch_nth(1)} is the same as calling +\fnref{WMPlex.switch_next}\code{(_, 1)} as \type{WScreen} inherits +\type{WMPlex} and this is where the function is actually defined. + +Similarly to the above example, to bind the key sequence \key{Mod1+k n} +switch to the next managed object within a frame, and \key{Mod1+k 1} to the +first, you would issue the following call: +\begin{verbatim} +defbindings("WFrame", { + submap("Mod1+K", { + kpress("Right", "_:switch_next()"), + kpress("1", "_:switch_nth(1)"), + }), +}) +\end{verbatim} + + +\subsection{Key specifications} + +As seen above, the functions that create key binding specifications require +a \var{keyspec} argument. This argument should be a string containing the +name of a key as listed in the X header file \file{keysymdef.h}% +\footnote{This file can usually be found in the directory +\file{/usr/X11R6/include/X11/}.} without the \code{XK_} prefix. +\index{keysymdef.h@\file{keysymdef.h}} +Most of the key names are quite intuitive while some are not. For example, +the \key{Enter} key on the main part of the keyboard has the less common +name \key{Return} while the one the numpad is called \key{KP\_Enter}. + +The \var{keyspec} string may optionally have multiple ''modifier'' names +followed by a plus sign (\code{+}) as a prefix. X defines the following +modifiers: +\begin{quotation} +\key{Shift}, \key{Control}, \key{Mod1} to \key{Mod5}, +\key{AnyModifier} and \key{Lock}. +\index{Shift@\key{Shift}} +\index{Control@\key{Control}} +\index{ModN@\key{ModN}} +\index{AnyModifier@\key{AnyModifier}} +\index{Lock@\key{Lock}} +\end{quotation} + +X allows binding all of these modifiers to almost any key and while this +list of modifiers does not explicitly list keys such as +\key{Alt}\index{Alt@\key{Alt}} that are common on modern keyboards, such +keys are bound to one of the \key{ModN}. On systems running XFree86 +\key{Alt} is usually \key{Mod1}. On Suns \key{Mod1} is the diamond key +and \key{Alt} something else. One of the ''flying window'' keys on so +called Windows-keyboards is probably mapped to \key{Mod3} if you have +such a key. Use the program \file{xmodmap}\index{xmodmap@\file{xmodmap}} +to find out what exactly is bound where. + +Ion defaults to \key{AnyModifier} in submaps. This can sometimes lead to +unwanted effects when the same key is used with and without explicitly +specified modifiers in nested regions. For this reason, Ion recognises +\key{NoModifier} as a special modifier that can be used to reset this +default. + +Ion ignores the \key{Lock} modifier and any \key{ModN} ($N=1{\ldots} 5$) +bound to \key{NumLock}\index{NumLock@\key{NumLock}} or +\key{ScrollLock}\index{ScrollLock@\key{ScrollLock}} +by default because such\footnote{Completely useless keys that should be +gotten rid of in the author's opinion.} locking keys may otherwise +cause confusion. + + +\subsection{Button specifications} + +Button specifications are similar to key definitions but now +instead of specifying modifiers and a key, you specify modifiers +and one of the button names \key{Button1} to +\key{Button5}\index{Button-n@\key{Button-n}}. Additionally the +specification may end with an optional area name following an @-sign. +Only frames currently support areas, and the supported values in this +case are +\code{"border"}, \code{"tab"}, \code{"empty_tab"}, \code{"client"} and +\code{nil} (for the whole frame). + +For example, the following code binds dragging a tab with the first +button pressed to initiate tab drag\&drop handling: + +\begin{verbatim} +defbindings("WFrame", { + mdrag("Button1@tab", "_:p_tabdrag()"), +}) +\end{verbatim} + + +\subsection{A further note on the default binding configuration} + +The default binding configuration contains references to the variables +\code{META} and \code{ALTMETA} instead of directly using the default +values of \code{"Mod1+"} and \code{""} (nothing). As explained in +section \ref{sec:walkthrough}, the definitions of these variables +appear in \file{cfg\_ion.lua}. This way you can easily change the the +modifiers used by all bindings in the default configuration without +changing the whole binding configuration. Quite a few people prefer +to use the Windows keys as modifiers because many applications already +use \key{Alt}. Nevertheless, \key{Mod1} is the default as a key bound +to it is available virtually everywhere. + diff --git a/doc/conf-menus.tex b/doc/conf-menus.tex new file mode 100644 index 0000000..3f9ee17 --- /dev/null +++ b/doc/conf-menus.tex @@ -0,0 +1,120 @@ +\section{Menus} +\label{sec:menus} + +\subsection{Defining menus} + +\index{menus} +\index{defmenu@\code{defmenu}} +\index{menuentry@\code{menuentry}} +\index{submenu@\code{submenu}} +In the stock configuration file setup, menus are defined in the file +\file{cfg\_menus.lua} as previously mentioned. The \file{mod\_menu} module +must be loaded for one to be able to define menus, and this is done with +the function \fnrefx{mod_menu}{defmenu} provided by it. + +Here's an example of the definition of a rather simple menu with a submenu: + +\begin{verbatim} +defmenu("exitmenu", { + menuentry("Restart", "ioncore.restart()"), + menuentry("Exit", "ioncore.shutdown()"), +}) + +defmenu("mainmenu", { + menuentry("Lock screen", "ioncore.exec('xlock')"), + menuentry("Help", "mod_query.query_man(_)"), + submenu("Exit", "exitmenu"), +}) +\end{verbatim} + + +The \fnrefx{mod_menu}{menuentry} function is used to create an entry in the +menu with a title and an entry handler to be called when the menu entry +is activated. The parameters to the handler are similar to those of binding +handlers, and usually the same as those of the binding that opened the menu. + +The \fnrefx{mod_menu}{submenu} function is used to insert a submenu at that +point in the menu. (One could as well just pass a table with the menu +entries, but it is not encouraged.) + +\subsection{Special menus} + +The menu module predefines the following special menus. These can be used +just like the menus defined as above. + +\begin{tabularx}{\linewidth}{lX} + \tabhead{Menu name & Description} + \code{windowlist} & + List of all client windows. Activating an entry jumps to that window. \\ + \code{workspacelist} & + List of all workspaces. Activating an entry jumps to that workspaces. \\ + \code{stylemenu} & + List of available \file{look\_*.lua} style files. Activating an entry + loads that style and ask to save the selection. \\ + \code{ctxmenu} & + Context menu for given object. \\ +\end{tabularx} + + +\subsection{Defining context menus} + +The ''ctxmenu'' is a special menu that is assembled from a defined context +menu for the object for which the menu was opened for, but also includes +the context menus for the manager objects as submenus. + +Context menus for a given region class are defined with the +\fnrefx{mod_menu}{defctxmenu} function. This is other ways similar to +\fnrefx{mod_menu}{defmenu}, but the first argument instead being the name +of the menu, the name of the region class to define context menu for. +For example, here's part of the stock \type{WFrame} context menu +definition: + +\begin{verbatim} +defctxmenu("WFrame", { + menuentry("Close", "WRegion.rqclose_propagate(_, _sub)"), + menuentry("Kill", "WClientWin.kill(_sub)", "_sub:WClientWin"), +}) +\end{verbatim} + +Some of the same ''modes'' as were available for some bindings +may also be used: \code{WFrame.tiled}, \code{WFrame.floating}, +and \code{WFrame.transient}. + + +\subsection{Displaying menus} +\label{sec:menudisp} + +The following functions may be used to display menus from binding +handlers (and elsewhere): + +\begin{tabularx}{\linewidth}{lX} + \tabhead{Function & Description} + \fnref{mod_menu.menu} & + Keyboard (or mouse) operated menus that open in the bottom-left corner + of a screen or frame. \\ + \fnref{mod_menu.bigmenu} & + Same as previous, but uses another graphical style. \\ + \fnref{mod_menu.pmenu} & + Mouse-operated drop-down menus. This function can only be called from a + mouse press or drag handler. \\ + \fnref{mod_menu.grabmenu} & + A special version of \fnref{mod_menu.menu} that grabs the keyboard + and is scrolled with a given key until all modifiers have been released, + after which the selected entry is activated. This function is meant to + be used for implementing, for example, Win***s-style \key{Alt-Tab} + handling.\footnote{See the \file{wcirculate.lua} script in the Ion + scripts repository \url{http://iki.fi/tuomov/repos/ion-scripts-3/}.} \\ +\end{tabularx} + +The \fnrefx{mod_menu}{grabmenu} function takes the extra key parameter, but +aside from that each of these functions takes three arguments, which when +called from a binding handler, should be the parameters to the handler, and +the name of the menu. For example, the following snippet of of code binds +the both ways to open a context menu for a frame: + +\begin{verbatim} +defbindings("WFrame", { + kpress(MOD1.."M", "mod_menu.menu(_, _sub, 'ctxmenu')"), + mpress("Button3", "mod_menu.pmenu(_, _sub, 'ctxmenu')"), +}) +\end{verbatim} diff --git a/doc/conf-winprops.tex b/doc/conf-winprops.tex new file mode 100644 index 0000000..10c1c10 --- /dev/null +++ b/doc/conf-winprops.tex @@ -0,0 +1,279 @@ +\section{Winprops} +\label{sec:winprops} + +The so-called ''winprops''\index{Winprops} can be used to change how +specific windows are handled and to set up some kludges to deal with +badly behaving applications. They are defined by calling the function +\code{defwinprop} with a table containing the properties to set and the +necessary information to identify a window. The currently supported +winprops are listed below, and the subsequent subsections explain the +usual method of identifying windows, and how to obtain this information. + +%\begin{table} +%\begin{htmlonly} +%\docode % latex2html kludge +%\end{htmlonly} +%\caption{Supported winprops} +%\label{tab:winprops} + +\newenvironment{winprop}[2]{ + \begin{function}% + % Sigh. (La)TeX is a mess. + %\index{% + % \ifx\\#1\\% + % #2\else#1\fi% + % @\expandafter\var{#2}} + \item[Winprop:] \var{#1} (#2) + \item[Description:] +} +{ + \end{function} +} + + +\begin{winprop}{acrobatic}{boolean} + \index{acrobatic@\var{acrobatic}} + Set this to \code{true} for Acrobat Reader. It has an annoying + habit of trying to manage its dialogs instead of setting them as + transients and letting the window manager do its job, causing + Ion and acrobat go a window-switching loop when a dialog is + opened. +\end{winprop} + + +\begin{winprop}{aspect}{table} + \index{aspect@\var{aspect}} + The table should contain the entries \var{w} and \var{h} that + override application-supplied aspect ratio hint. +\end{winprop} + + +\begin{winprop}{float}{boolean} + \index{float@\var{float}} + Set this to open the window in a floating frame, when + in a group. +\end{winprop} + + +\begin{winprop}{fullscreen}{boolean} + \index{fullscreen@\var{fullscreen}} + Should the window be initially in full screen mode? +\end{winprop} + + +\begin{winprop}{ignore_cfgrq}{boolean} + \index{ignore-cfgrq@\var{ignore_cfgrq}} + Should configure requests on the window be ignored? + Only has effect on floating windows. +\end{winprop} + + +\begin{winprop}{ignore_net_active_window}{boolean} + \index{ignore-net-active-window@\var{ignore_net_active_window}} + Ignore extended WM hints \code{_NET_ACTIVE_WINDOW} request. +\end{winprop} + + +\begin{winprop}{ignore_resizeinc}{boolean} + \index{ignore-resizeinc@\var{ignore_resizeinc}} + Should application supplied size increments be ignored? +\end{winprop} + + +\begin{winprop}{jumpto}{boolean} + \index{jumpto@\var{jumpto}} + Should a newly created client window always be made + active, even if the allocated frame isn't. +\end{winprop} + + +\begin{winprop}{max_size}{table} + \index{max-size@\var{max_size}} + The table should contain the entries \var{w} and \var{h} that + override application-supplied maximum size hint. +\end{winprop} + + +\begin{winprop}{min_size}{table} + \index{min-size@\var{min_size}} + Similar to \var{max_size} but for the minimum size hint. +\end{winprop} + + +\begin{winprop}{oneshot}{boolean} + \index{oneshot@\var{oneshot}} + Discard this winprop after first use. +\end{winprop} + + +\begin{winprop}{switchto}{boolean} + \index{switchto@\var{switchto}} + Should a newly mapped client window be switched to within + its frame. +\end{winprop} + + +\begin{winprop}{target}{string} + \index{target@\var{target}} + The name of an object (workspace, frame) that should manage + windows of this type. +\end{winprop} + + +\begin{winprop}{transient_mode}{string} + \index{transient-mode@\var{transient_mode}} + "normal": No change in behaviour. "current": The window + should be thought of as a transient for the current active + client window (if any) even if it is not marked as a + transient by the application. "off": The window should be + handled as a normal window even if it is marked as a + transient by the application. +\end{winprop} + + +\begin{winprop}{transients_at_top}{boolean} + \index{transients-at-top@\var{transients_at_top}} + When transients are managed by the client window itself (as it + is the case on tiled workspaces), should the transients be + placed at the top of the window instead of bottom? +\end{winprop} + + +\begin{winprop}{transparent}{boolean} + \index{transparent@\var{transparent}} + Should frames be made transparent when this window is selected? \\ +\end{winprop} + + + +\subsection{Classes, roles and instances} +\label{sec:classesrolesinstances} + +The identification information in the winprop specification is usually the +\var{class}\index{class@\var{class}!winprop}, +\var{role}\index{role@\var{role}!winprop}, +\var{instance}\index{instance@\var{instance}!winprop} and +\var{name} +of the window. The \var{name} field is a Lua-style regular expression +matched against the window's title and the rest are strings that must +exactly match the corresponding window information. It is not necessary +to specify all of these fields. + +Ion looks for a matching winprop in the order listed by the following +table. An 'E' indicates that the field must be set in the winprop +and it must match the window's corresponding property exactly or, in +case of \var{name}, the regular expression must match the window +title. An asterisk '*' indicates that a winprop where the field is +not specified (or is itself an asterisk in case of the first three +fields) is tried. + +\begin{center} +\begin{tabular}{llll} + \tabhead{\var{class} & \var{role} & \var{instance} & \var{name}} + E & E & E & E \\ + E & E & E & * \\ + E & E & * & E \\ + E & E & * & * \\ + E & * & E & E \\ + E & * & E & * \\ + E & * & * & E \\ + \vdots & \vdots & \vdots & etc. \\ +\end{tabular} +\end{center} + +If there are multiple winprops with other identification information +the same but different \var{name}, the longest match is chosen. + +\subsection{Finding window identification} + +The 'Window info' context menu entry (\key{Mod1+M} or \key{Button3} on a tab) +can be used to list the identification information required to set winprops +for a window and all the transient windows managed within it. + +\index{xprop} +Another way to get the identification information is to use \command{xprop}. +Simply run To get class and instance, simply run \command{xprop WM_CLASS} +and click on the particular window of interest. The class is the latter of +the strings while the instance is the former. To get the role -- few +windows have this property -- use the command \command{xprop WM_ROLE}. +This method, however, will not work on transients. + +\index{transient} +So-called ''transient windows'' are usually short-lived dialogs (although +some programs abuse this property) that have a parent window that they are +''transient for''. On tiled workspaces Ion displays these windows +simulatenously with the parent window at the bottom of the same frame. +Unfortunately \command{xprop} is stupid and can't cope with this situation, +returning the parent window's properties when the transient is clicked on. +For this reason you'll have to do a little extra work to get the properties +for that window.\footnote{There's a patch to \command{xprop} to +fix this, but nothing seems to be happening with respect to including it in +XFree86.} + +Finally, it should be mentioned that too many authors these days +''forget'' to set this vital identification to anything meaningful: +everything except name is the same for all of the programs's +windows, for example. + +\subsection{Some common examples} + +\subsubsection{Acrobat Reader} + +The following is absolutely necessary for Acrobat reader: + +\begin{verbatim} +defwinprop{ + class = "AcroRead", + instance = "documentShell", + acrobatic = true, +} +\end{verbatim} + +\subsubsection{Fixing a Mozilla Firebird transient} + +Mozilla Firebird (0.7) incorrectly does not set the \code{WM_TRANSIENT_FOR} +property for the dialog that is used to ask the action to take for a file. +It, however, sets the the property point to the main window for the save +dialog. This can be annoying and confusing, as the first dialog is not +closed before the second is displayed. + +We'd like the first dialog to be transient to the main window. The closest +we can get to that is to consider it transient to the current window (if +there's one). Unfortunately Firebird does not set any meaningful classes, +instances or roles for the windows, so we'll have to rely on an ugly title +match. + +\begin{verbatim} +defwinprop{ + class = "MozillaFirebird-bin", + name = "Opening .*", + transient_mode = "current", +} +\end{verbatim} + +\subsubsection{Forcing newly created windows in named frames} + +The following winprop should place xterm started with command-line parameter +\mbox{\code{-name sysmon}} and running a system monitoring program in a +particular frame: +\begin{verbatim} +defwinprop{ + class = "XTerm", + instance = "sysmon", + target = "sysmonframe", +} +\end{verbatim} + +For this example to work, we have to somehow create a frame named +\code{sysmonframe}. One way to do this is to make the following +call in the \key{Mod1+F3} Lua code query: + +\begin{verbatim} +mod_query.query_renameframe(_) +\end{verbatim} + +Recall that \code{_} points to the multiplexer (frame or screen) in which +the query was opened. Running this code should open a new query prefilled +with the current name of the frame. In our example we would change the +name to \code{sysmonframe}, but we could just as well have used the +default name formed from the frame's class name and an instance number. diff --git a/doc/conf.tex b/doc/conf.tex new file mode 100644 index 0000000..109093b --- /dev/null +++ b/doc/conf.tex @@ -0,0 +1,178 @@ + +\chapter{Basic configuration} +\label{chap:config} + +This chapter should help your configure Ion to your liking. As the your +probably already know, Ion uses Lua as a configuration and extension +language. If you're new to it, you might first want to read some Lua +documentation as already suggested and pointed to in the Introduction +before continuing with this chapter. + +Section \ref{sec:conffiles} is an overview of the multiple configuration +files Ion uses and as a perhaps more understandable introduction to the +general layout of the configuration files, a walk-through of the main +configuration file \file{ion.lua} is provided in section +\ref{sec:walkthrough}. +How keys and mouse action are bound to functions is described in detail +in \ref{sec:bindings} and in section \ref{sec:winprops} winprops are +explained. For a reference on exported functions, see section +\ref{sec:exports}. + +\section{The configuration files} +\label{sec:conffiles} + +Ion3, to which document applies, stores its stock configuration files in +\file{/usr/local/etc/ion3/} unless you, the OS package maintainer or +whoever installed the package on the system has modified the variables +\code{PREFIX}\index{PREFIX@\code{PREFIX}} or +\code{ETCDIR}\index{ETCDIR@\code{ETCDIR}} in +\file{system.mk}\index{system.mk@\file{system.mk}} before compiling Ion. +In the first case you probably know where to find the files and in +the other case the system administrator or the OS package maintainer +should have provided documentation to point to the correct location. +If these instructions are no help in locating the correct directory, +the command \code{locate cfg_ion.lua} might help provided \code{updatedb} +has been run recently. + +User configuration files go in \file{\~{}/.ion3/}. +Ion always searches the user configuration file directory before the stock +configuration file directory for files. Therefore, if you want to change +some setting, it is advised against that you modify the stock configuration +files in-place as subsequent installs of Ion will restore the stock +configuration files. Instead you should always make a copy of the stock +file in \file{\~{}/.ion3/} and modify this file. When searching +for a file, if no extension or path component is given, compiled \file{.lc} +files are attempted before \file{.lua} files. + +All the configuration files are named \file{cfg\_*.lua} with the ''\file{*}'' +part varying. The configuration file for each module \file{mod\_modname} is +\file{cfg\_modname.lua}, with \file{modname} varying by the module in +question. The following table summarises these and other configuration +files: + +\begin{tabularx}{\linewidth}{ + p{\widthof{cfg-bindings.lua}}% + X} + \hline + File & Description \\ + \hline + \file{cfg\_ion.lua} & + The main configuration file \\ + % + \file{cfg\_ioncore.lua} & + Configuration file for Ion's core library. + Most of the bindings and menus are configured here. Bindings that are + specific to some module are configured in the module's configuration + file. For details, see section \ref{sec:bindings}. \\ + % + \file{cfg\_kludges.lua} & + Settings to get some applications behave more nicely have been + collected here. See section \ref{sec:winprops}. \\ + % + \file{cfg\_tiling.lua} + \file{cfg\_query.lua} + \file{cfg\_menu.lua} + \file{cfg\_dock.lua} + \file{cfg\_statusbar.lua} + \dots & Configuration files for different modules. \\ +\end{tabularx} + +Additionally, there's the file \file{look.lua} that configures the +drawing engine, but it is covered in chapter \ref{chap:gr}. + +\section{A walk through \file{cfg\_ion.lua}} +\label{sec:walkthrough} + +As already mentioned \file{cfg\_ion.lua} is Ion's main configuration +file. Some basic 'feel' settings are usually configured there and +the necessary modules and other configuration files configuring some +more specific aspects of Ion are loaded there. In this section we +take a walk through the stock \file{cfg\_ion.lua}. +Notice that most of the settings are commented-out (\code{--} is a +line comment in Lua) in the actual file, as they're the defaults +nevertheless. + +The first thing one in the file is to set +\begin{verbatim} +META="Mod1+" +ALTMETA="" +\end{verbatim} +These settings cause most of Ion's key bindings to use \key{Mod1} as the +modifier key. If \code{ALTMETA} is set, it is used as modifier for the keys +that don't normally use a modifier. for details on modifiers and key +binding setup in general see section \ref{sec:bindings}. + +Next we do some basic feel configuration: + +\begin{verbatim} +ioncore.set{ + dblclick_delay=250, + kbresize_delay=1500, +} +\end{verbatim} + +These two will set the delay between button presses in a double click, and +the timeout to quit resize mode in milliseconds. + +\begin{verbatim} +ioncore.set{ + opaque_resize=true, + warp=true +} +\end{verbatim} + +The first of these two settings enables opaque resize mode: in move/resize +move frames and other objects mirror you actions immediately. If opaque +resize is disabled, a XOR rubber band is shown during the mode instead. +This will, unfortunately, cause Ion to also grab the X server and has some +side effects. + +Next we load the configuration for Ion's core, and some kludges: + +\begin{verbatim} +dopath("cfg_ioncore") +dopath("cfg_kludges") +\end{verbatim} + +Most bindings and menus are defined in \file{cfg\_ioncore.lua}. +Details on making such definitions follow in sections \ref{sec:bindings} +and \ref{sec:menus}, respectively. +some kludges or ''winprops'' to make some applications behave better +under Ion are colledted in \file{cfg\_kludges.lua}; see section +\ref{sec:winprops} for details. In addition to these, this file +lists quite a few statements of the form +\begin{verbatim} +ioncore.defshortening("[^:]+: (.*)(<[0-9]+>)", "$1$2$|$1$<...$2") +\end{verbatim} +These are used to configure how Ion attempts to shorten window titles +when they do not fit in a Tab. The first argument is a POSIX regular +expression that is used to match against the title and the next is +a rule to construct a new title of a match occurs. This particular +rule is used to shorten e.g. 'Foo: barbaz<3>' to 'barba{\ldots}<3>'; for +details see the function reference entry for \fnref{ioncore.defshortening}. + +To actually be able to do something besides display windows in full screen +mode, we must next load some modules: + +\begin{verbatim} +dopath("cfg_modules") +--dopath("mod_query") +--dopath("mod_menu") +--dopath("mod_tiling") +--dopath("mod_statusbar") +--dopath("mod_dock") +--dopath("mod_sp") +\end{verbatim} + +We actually load there another file listing the default selection of +modules. If you only want to load additional modules, just uncomment +the corresponding line. If you want to disable loading some modules, +comment out the the line loading \file{cfg\_modules}, and uncomment +the lines for the modules you want, or add more. + + +\input{conf-bindings.tex} + +\input{conf-menus.tex} + +\input{conf-winprops.tex} diff --git a/doc/confintro.tex b/doc/confintro.tex new file mode 100644 index 0000000..9331614 --- /dev/null +++ b/doc/confintro.tex @@ -0,0 +1,52 @@ + +\chapter{Introduction} + +This document is an ''advanced user'' manual for Ion, the X11 window manager, +and version 3 specifically. It is an attempt attempt at documenting what is +in Ion's configuration files, how to configure Ion by simple modifications +to these files and how to write more complex extensions in Lua, the +lightweight configuration and scripting language used by Ion. + +Readers unfamiliar with Lua is advised to first glance at the Lua manual at + +\centerurl{http://www.lua.org/docs.html} + +and perhaps some tutorial pages at the lua-users wiki: + +\centerurl{http://lua-users.org/wiki/LuaTutorial} + +Back in this document, first in chapter \ref{chap:prelim} some key +concepts and relations are explained. These include the module system +and Ion's object and class hierarchies. While it might not at first +occur that knowing such things would be necessary to \emph{configure} +a program, this material is essential because of the object-oriented +nature of most of Ion's scripting interface. + +The new user, fed up with the default key bindings and eager to just +quickly configure Ion to his liking may question the reasons for +exposing the ''heavy'' internal OO structure in the scripting and +configuration interface. I'm not the one to blame him for that. +Sure it would be faster to configure Ion to everyone's liking +if a simpler binding configuration interface was provided. Such an +interface would, however, also be far more limited and make writing +extensions more complicated and the advantages from using a real +scripting language would be partly lost. One more advantage from +a rich scripting and configuration interface is that it allows +implementing scripts to read alternate configuration file formats, +ones that could be, for example, modified by external configuration tools. + +In chapter \ref{chap:config} the very basic Ion configuration know-how +is provided. All the different configuration files and their locations +are explained and instructions are given to allow the reader to +configure bindings and so-called ''winprops''. Chapter \ref{chap:gr} +explains the notion of drawing engines and graphical styles and how to +write new looks for Ion and more advanced aspects of Ion's scripting +interface are documented in chapter \ref{chap:tricks} (a work in +progress). + +Finally, most of the functions provided by Ion's scripting interface +are listed and documented in the Function reference in chapter +\ref{sec:exports}. At the end of the document is an alphabetical +listing of all these functions. + + diff --git a/doc/cstyle.tex b/doc/cstyle.tex new file mode 100644 index 0000000..328a7c3 --- /dev/null +++ b/doc/cstyle.tex @@ -0,0 +1,118 @@ +\section{C coding style} + +If you want to submit patches to Ion, you MUST follow my coding +style, even if you think it is the root of all evil. We don't want +the code to be an incomprehensible mess of styles and I have better +things to do than fix other people's style to match mine. The style +should be obvious by studying the source, but here's a list of some +things to take note of. + +\subsection{Whitespace} + +\begin{itemize} + \item Indentations of 4 with \emph{tab size=4}. + + \item No extra spaces between operators, delimiters etc. except + \begin{itemize} + \item around logical and, or (\code{&&}, \code{||}) + \item around the conditional \code{a ? b : c} + \item after commas and semicolons + \end{itemize} + In my opinion this helps pointing out arithmetic or other + expressions within logical expressions or parameter lists. + + \item All kinds of labels are out-tended to the level of the higher + level block. For example: + +\begin{verbatim} +void foo() +{ +again: + switch(asdf){ + case 1: + ... + break; + default: + ... + break; + } +} +\end{verbatim} +\end{itemize} + +\subsection{Braces} + +\begin{itemize} +\item Opening brace is at the end of the line, except in function + bodies, where it is at the beginning of the line following + the definition. + +\item Never put the body of a control statement on the same line + with the statement (e.g. \verb!if(foo){ bar() }!). + +For example, the block +\begin{verbatim} +void foo(int a, int b) +{ + if(a==b && c+d==e){ + ... + } +} +\end{verbatim} + +has correct style while the block + +\begin{verbatim} +void foo(int a,int b) { + if (a == b && c + d == e) { + ... + } +} +\end{verbatim} + +does not. + + \item The \code{else} keyword follows immediately after the closing brace of + previous \code{if}, if any. (This might change so I don't care if you put + it on the next line.) + + \item I have used the convention that control statement bodies containing + a single statement do not need braces around the block if, in case of + the \code{if} all the blocks in \code{if ... else if ... else} + contain just one statement. If you want to, just use braces in every + case. +\end{itemize} + +\subsection{Names} + +\begin{itemize} + \item Function and variable names only have lower case letters. Type + names are in mixed case while constants and macros (\code{#define}s) + are in upper case letters. +\end{itemize} + +\subsection{Miscellaneous} + +\begin{itemize} + \item In the definition of a pointer variable, the asterisk is attached + to the variable name: \code{char *s;}. (One could claim this an + exception to the second rule.) + + \item You might optionally want to use Jed's foldings to group blocks + of related code in a file to keep it organized: + +\begin{verbatim} +/*{{{ Many related functions */ + +void code() +{ + ... +} + +... + +/*}}}*/ +\end{verbatim} +\end{itemize} + +I think that's mostly it. Study the source when in doubt. diff --git a/doc/de.tex b/doc/de.tex new file mode 100644 index 0000000..6c8c2b4 --- /dev/null +++ b/doc/de.tex @@ -0,0 +1,388 @@ + +\chapter{Graphical styles} +\label{chap:gr} + +This chapter first gives in section \ref{sec:engines} a general outline +of how drawing engines are used, of style specifications and then +in section \ref{sec:defaultde} describes how to specify styles +for the default drawing engine. + +\section{Drawing engines, style specifications and sub-styles} +\label{sec:engines} +\index{style}\index{drawing engine} + +Ion's drawing routines are abstracted into so-called drawing engine +modules that can, again depending on the system, be dynamically +loaded as needed. The drawing engine modules provide ''brushes'' +that objects can use to draw some high-level primitives such +as borders and text boxes (in addition to simple text and rectangle +drawing) on their windows and configure e.g. the shape and +background of the window. While the drawing engines therefore +do not directly implement looks for each possible object (that +would hardly be maintainable), different brush styles can be +used to give a distinctive look to different objects and engines +could interpret some styles as special cases. Style specifications +are strings of the form + +\begin{verbatim} +element1-element2-...-elementn +\end{verbatim} + +An example of such a style specification is \code{tab-frame}; +see the table in subsection \ref{sec:styles} for more styles. + +When an object asks for a brush of certain style, the selected +drawing engine will attempt to find the closest match to this +specification. The styles/brushes defined by the drawing engines +may have asterisks (\verb!*!) as some of the elements indicating +a match to anything. Exact matches are preferred to asterisk +matches and longer matches to shorter. For example, let a brush +for style \code{foo-bar-baz} be queried, then the following +brushes are in order of preference: + +\begin{verbatim} +foo-bar-baz +foo-*-baz +foo-bar +* +foo-baz -- Doesn't match, not selected! +\end{verbatim} + +Some of the drawing primitives allow extra attributes to be +specified, also in the form +\begin{verbatim} +attr1-attr2-...-attrn +\end{verbatim} +These extra attributes are called \emph{substyles}\index{substyle} +and allow, for example, the state of the object to be indicated +by different colour sets while keeping the interface at an +abstract level and the drawing engine completely ignorant +of the semantics -- only the writer of the drawing engine +configuration file has to know them. However the drawing +engine can again interpret known substyles as special cases +and the default engine indeed does so with frame tab +tag and drag states.) + + +\subsection{Known styles and substyles} +\label{sec:styles} + +\subsubsection{Frames} + +\begin{tabularx}{\linewidth}{lX} +\tabhead{Style name & Description} +\code{frame} & Style for frames. + Substyles: \code{active}, \code{inactive}. \\ +\code{frame-tiled} & A more specific style for tiled frames. + Substyles as for \code{frame}. \\ +\code{frame-tiled-alt} & An alternative style for tiled frames. + Often used to disable the tab-bar. \\ +\code{frame-floating} & A more specific style for floating + frames. \\ +\code{frame-transient} & A more specific style for frames + containing transient windows. \\ +\end{tabularx} + +\subsubsection{Tabs and menu entries} + +\begin{tabularx}{\linewidth}{lX} +\tabhead{Style name & Description} +\code{tab} & Style for frames' tabs and menu entries. + Substyles: combinations of the form \code{a-s} where + \code{a} is one of \code{active}\nobreak/\code{inactive} and + \code{s} is one of \code{selected}\nobreak/\code{unselected} \\ +\code{tab-frame} & A more specific style for frames' tabs. + Substyles: combinations of the form \code{a-s-t-d-u} where + \code{a} and \code{s} are as above and + \code{t} is one of \code{tagged}\nobreak/\code{not_tagged}, + \code{d} is one of \code{dragged}\nobreak/\code{not_dragged} and + \code{u} is one of \code{activity}\nobreak/\code{no_activity}. \\ +\code{tab-frame-tiled}, & \\ +\code{tab-frame-tiled-alt}, & \\ +\code{tab-frame-floating}, & \\ +\code{tab-frame-transient} & More specific styles for frames in the + different modes. \\ +\code{tab-menuentry} & A more specific style for entries in \type{WMenu}s. \\ +\code{tab-menuentry-bigmenu} & An alternate style for entries in \type{WMenu}s. \\ +\end{tabularx} + +\subsubsection{The rest} + +\begin{tabularx}{\linewidth}{lX} +\tabhead{Style name & Description} +\code{input} & A style for \type{WInput}s. \\ +\code{input-edln} & A more specific style for \type{WEdln}s. + Substyles: \code{selection} for selected text and + \code{cursor} for the cursor indicating current editing point. \\ +\code{input-message} & A more specific style for \type{WMessage}s. \\ +\code{input-menu} & A more specific style for \type{WMenu}s. \\ +\code{input-menu-bigmenu} & An alternate style for \type{WMenu}s. \\ +\code{moveres_display} & The box displaying position/size when + moving or resizing frames. \\ +\code{dock} & The dock. \\ +\end{tabularx} + + +\section{Defining styles for the default drawing engine} +\label{sec:defaultde} + +Drawing engine style files are usually named +\file{look\_foo.lua} where \file{foo} is the name of the +style. The file that Ion loads on startup or when +\fnref{gr.read_config} is called, however, is \file{look.lua} +and should usually be symlinked to or a copy of of some +\file{look\_foo.lua}. + +\subsection{The structure of the configuration files} + +The first thing to do in a stylefile is to choose the drawing +engine, possibly loading the module as well. This is done +with the following chunk of code. + +\begin{verbatim} +if not gr.select_engine("de") then + return +end +\end{verbatim} + +The \fnref{gr.select_engine} function sees if the engine +given as argument is registered (the default drawing engine is +simply called ''de''). If the engine could not be found, it +tries to load a module of the same name. If the engine still +is not registered, \fnref{gr.select_engine} returns \code{false} +and in this case we also exit the style setup script. +If the engine was found, \fnref{gr.select_engine} sees that +further requests for brushes are forwarded to that engine +and returns \code{true}. + +Before defining new styles it may be a good idea to clear old +styles from memory so if the old configuration defines more +specific styles than the new, the old styles don't override +those specified by the new configuration. That can be done by +calling + +\begin{verbatim} +de.reset() +\end{verbatim} + +After this the new styles can be defined with \fnref{de.defstyle} +as explained in the next subsection. Finally, after the styles have +been defined we must ask objects on the screen to look up new brushes +to reflect the changes in configuration. This is done with + +\begin{verbatim} +gr.refresh() +\end{verbatim} + +\subsection{Defining the styles} + +Styles for the default drawing engine are defined with the +function \fnref{de.defstyle}. It has two arguments the first being +a style specification as explained in previous sections and the second +a table whose fields describe the style: + +\begin{verbatim} +de.defstyle("some-style", { + attribute = value, + ... +}) +\end{verbatim} + +The supported attributes are described in tables below. The different +border elements and styles referred to there are explained in Figure +\ref{fig:borders}. + +\begin{figure} +\begin{htmlonly} +\docode % Kludge to make latex2html interpret contents instead of + % creating an image. +\end{htmlonly} +\begin{verbatim} +Elevated: Inlaid: Ridge: Groove: + hhhhhhhhhhhs ............ hhhhhhhhhhhs sssssssssssh + h..........s .sssssssssh. h..........s s..........h + h. .s .s h. h.sssssssh.s s.hhhhhhhs.h + h. .s .s h. h.s h.s s.h s.h + h. .s .s h. h.shhhhhhh.s s.hsssssss.h + h..........s .shhhhhhhhh. h..........s s..........h + hsssssssssss ............ hsssssssssss shhhhhhhhhhh + +h = highlight, s = shadow, . = padding +\end{verbatim} +\caption{Sketch of different border styles and elements} +\label{fig:borders} +\end{figure} + +\subsubsection{Colours} + +Each of these fields a string of the form that can be +passed to \code{XAllocNamedColor}. Valid strings are e.g. +hexadecimal RGB specifications of the form +\code{#RRGGBB} and colour names as specified +in \file{/usr/X11R6/lib/X11/rgb.txt} (exact path varying). + +\begin{tabularx}{\linewidth}{lX} +\tabhead{Field & Description} +\var{highlight_colour} & + Colour for the ''highlight'' part of a border. \\ +\var{shadow_colour} & + Colour for the ''highlight'' part of a border. \\ +\var{foreground_colour} & + Colour for the normal drawing operations, e.g. text. \\ +\var{background_colour} & + Window background colour (unless transparency is enabled) and + background colour boxes. \\ +\var{padding_colour} & + Colour for the ''padding'' part of a border border. Set to + \var{background_colour} if unset. \\ +\end{tabularx} + + +\subsubsection{Borders and widths} + +All other fields below except \var{border_style} are non-negative integers +indicating a number of pixels. + +\begin{tabularx}{\linewidth}{lX} +\tabhead{Field & Description} +\var{border_style} & A string indicating the style of border; one of + elevated/inlaid/ridge/groove as seen in the + above sketch. \\ +\var{highlight_pixels} & + Width of the highlight part of the border in pixels. \\ +\var{shadow_pixels} & + Width of the shadow part of the border in pixels. \\ +\var{padding_pixels} & + Width of the padding part of the border in pixels. \\ +\var{spacing} & + Space to be left between all kinds of boxes. \\ +\end{tabularx} + + +\subsubsection{Text} + +\begin{tabularx}{\linewidth}{lX} +\tabhead{Field & Description} +\var{font} & Font to be used in text-drawing operations; standard X font + name. \\ +\var{text_align} & How text is to be aligned in text boxes/tabs; one of + the strings left\nobreak/right\nobreak/center. \\ +\end{tabularx} + + +\subsubsection{Miscellaneous} + + +\begin{tabularx}{\linewidth}{lX} +\tabhead{Field & Description} +\var{transparent_background} & Should windows' that use this style + background be transparent? true/false. \\ +\var{based_on} & The name of a previously defined style that this + style should be based on. \\ +\end{tabularx} + + +\subsubsection{Substyles} + +As discussed in previous sections, styles may have substyles to e.g. +indicate different states of the object being drawn. The ''de'' engine +limits what can be configured in substyles to the set of colours in the +first table above, but also specifically interprets for the main style +\code{tab-frame} the substyles \code{*-*-tagged} and \code{*-*-*-dragged} +by, respectively, drawing a right angle shape at the top right corner +of a tab and by shading the tab with a stipple pattern. Also for +menus the substyles \code{*-*-submenu} are handled as a special case. + +Substyles are defined with the function \fnref{de.substyle} within the +table defining the main style. The parameters to this function are +similar to those of \fnref{de.defstyle}. + +\begin{verbatim} +de.defstyle("some-style", { + ... + de.substyle("some-substyle", { + ... + }), + ... +}) +\end{verbatim} + + +\subsection{An example} + +The following shortened segment from \file{look\_cleanviolet.lua} +should help to clarify the matters discussed in the previous +subsection. + +\begin{verbatim} +de.defstyle("*", { + -- Gray background + highlight_colour = "#eeeeee", + shadow_colour = "#eeeeee", + background_colour = "#aaaaaa", + foreground_colour = "#000000", + + shadow_pixels = 1, + highlight_pixels = 1, + padding_pixels = 1, + spacing = 0, + border_style = "elevated", + + font = "-*-helvetica-medium-r-normal-*-12-*-*-*-*-*-*-*", + text_align = "center", +}) + +de.defstyle("tab-frame", { + based_on = "*", + + de.substyle("active-selected", { + -- Violet tab + highlight_colour = "#aaaacc", + shadow_colour = "#aaaacc", + background_colour = "#666699", + foreground_colour = "#eeeeee", + }), + + -- More substyles would follow ... +}) +\end{verbatim} + + +\section{Miscellaneous settings} + +The following style fields are independent of the drawing engine used, +but are related to objects' styles and therefore configured in the drawing +engine configuration file. + +\subsection{Extra fields for style \code{frame}} + +\begin{tabularx}{\linewidth}{lX} +\tabhead{Field & Description} +\code{bar} & Controls the style of the tab-bar. Possible values + are the strings \code{"none"}, \code{"inside"}, \code{"outside"} + and \code{"shaped"}, with the last providing the PWM-style + tab-bars for floating frames. \\ +\code{floatframe_tab_min_w} & Minimum tab width in pixels for + the shaped style, given that this number times number of tabs + doesn't exceed frame width. \\ +\code{floatframe_bar_max_w_q} & Maximum tab-bar width quotient of + frame width for the shaped styles. A number in the + interval $(0, 1]$. +\end{tabularx} + +\subsection{Extra fields for style \code{dock}} + +\begin{tabularx}{\linewidth}{lX} +\tabhead{Field & Description} +\code{outline_style} & How borders are drawn: + \code{"none"} -- no border, + \code{"all"} -- border around whole dock, + \code{"each"} -- border around each dockapp. \\ +\code{tile_size} & A table with entries \code{width} and \code{height}, + indicating the width and height of tiles in pixels. +\end{tabularx} + + +Hopefully that's enough to get you started in writing new style +configuration files for Ion. When in doubt, study the existing +style configuration files. diff --git a/doc/designnotes.tex b/doc/designnotes.tex new file mode 100644 index 0000000..4bb6485 --- /dev/null +++ b/doc/designnotes.tex @@ -0,0 +1,54 @@ +\section{Miscellaneous design notes} + +\subsection{Destroying \type{WObj}:s} + +To keep Ion's code as simple as possible yet safe, there are restrictions +when the \type{WObj} +\code{destroy_obj}\index{destroy-obj@\code{destroy_obj}} +function that calls watches, the deinit routine and frees memory may +be called directly. In all other cases the +\code{defer_destroy}\index{defer-destroy@\code{defer_destroy}} +function should be used to defer the call of \code{destroy_obj} until +Ioncore returns to its main event loop. + +Calling the \code{destroy_obj} function directly is allowed in the +following cases: +\begin{itemize} + \item In the deinit handler for another object. Usually managed objects + are destroyed this way. + \item The object was created during the current call to the function + that wants to get rid of the object. This is the case, for example, + when the function created a frame to manage some other object but for + some reason failed to reparent the object to this frame. + \item In a deferred action handler set with + \code{defer_action}\index{defer-action@\code{defer_action}}. + Like deferred destroys, other deferred actions are called when + Ioncore has returned to the main loop. + \item You are absolute sure that C code outside your code has no + references to the object. +\end{itemize} + +If there are no serious side effects from deferring destroying the +object or you're unsure whether it is safe to destroy the object +immediately, use \code{defer_destroy}. + +\subsection{The types \code{char*} and \code{const char*} as function + parameters and return values} + +The following rules should apply to using strings as return values and +parameters to functions. + +\begin{tabularx}{\linewidth}{lXX} + \tabhead{Type & Return value & Parameter} + \code{const char*} & The string is owned by the called function + and the caller is only quaranteed short-term read access to the + string. & + The called function may only read the string during its execution. + For further reference a copy must be made. \\ + \code{char*} & The string is the caller's responsibility and it + \emph{must} free it when no longer needed. & + The called function may modify the string but the ''owner'' of + the string is case-dependant. \\ +\end{tabularx} + + diff --git a/doc/exact-version b/doc/exact-version new file mode 100644 index 0000000..0413a6b --- /dev/null +++ b/doc/exact-version @@ -0,0 +1,5 @@ + +Context: + +[TAG ion-doc-3ds-20061223 +Tuomo Valkonen **20061223150012] diff --git a/doc/fnref.tex b/doc/fnref.tex new file mode 100644 index 0000000..c270a5a --- /dev/null +++ b/doc/fnref.tex @@ -0,0 +1,38 @@ +\chapter{Function reference} +\label{sec:exports} + +\section{Functions defined in \file{ioncore}} +\label{sec:ioncoreref} +\input{ioncore-fns.tex} + +\section{Functions defined in \file{mod\_tiling}} +\label{sec:tilingref} +\input{mod_tiling-fns.tex} + +%\section{Functions defined in \file{mod\_panews}} +%\label{sec:panewsref} +%\input{mod_panews-fns.tex} + +\section{Functions defined in \file{mod\_query}} +\label{sec:queryref} +\input{mod_query-fns.tex} + +\section{Functions defined in \file{mod\_menu}} +\label{sec:menuref} +\input{mod_menu-fns.tex} + +\section{Functions defined in \file{mod\_dock}} +\label{sec:dockref} +\input{mod_dock-fns.tex} + +\section{Functions defined in \file{mod\_sp}} +\label{sec:spref} +\input{mod_sp-fns.tex} + +\section{Functions defined in \file{de}} +\label{sec:deref} +\input{de-fns.tex} + +\section{Hooks} +\label{sec:hookref} +\input{hookref} diff --git a/doc/fullhierarchy.tex b/doc/fullhierarchy.tex new file mode 100644 index 0000000..d5d5a39 --- /dev/null +++ b/doc/fullhierarchy.tex @@ -0,0 +1,43 @@ +\chapter{Full class hierarchy visible to Lua-side} +\label{app:fullhierarchy} + +%\begin{figure} +%\begin{htmlonly} +%\docode % latex2html kludge +%\end{htmlonly} +{\small +\begin{verbatim} + Obj + |-->WHook + |-->WTimer + |-->WMoveresMode + |-->WMgmtMode (mod_mgmtmode) + |-->WRegion + | |-->WClientWin + | |-->WWindow + | | |-->WRootWin + | | |-->WMPlex + | | | |-->WScreen + | | | |-->WFrame + | | |-->WInfoWin + | | | |-->WStatusBar (mod_statusbar) + | | |-->WMenu (mod_menu) + | | |-->WInput (mod_query) + | | |-->WEdln (mod_query) + | | |-->WMessage (mod_query) + | |-->WGroup + | | |-->WGroupWS + | | |-->WGroupCW + | |-->WTiling (mod_tiling) + |-->WSplit (mod_tiling) + |-->WSplitInner (mod_tiling) + | |-->WSplitSplit (mod_tiling) + | |-->WSplitFloat (mod_tiling) + |-->WSplitRegion (mod_tiling) + |-->WSplitST (mod_tiling) +\end{verbatim} +} +%\caption{Full Ioncore and most common modules' class hierarchy +% visible to Lua side.} +%\label{fig:fullclasshierarchy} +%\end{figure} diff --git a/doc/gpl.tex b/doc/gpl.tex new file mode 100644 index 0000000..d4de1de --- /dev/null +++ b/doc/gpl.tex @@ -0,0 +1,416 @@ +\xchapter{The GNU General Public License} + +\begin{center} +{\parindent 0in + +Version 2, June 1991 + +Copyright \copyright\ 1989, 1991 Free Software Foundation, Inc. + +\bigskip + +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + +\bigskip + +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. +} +\end{center} + +\begin{center} +{\bf\large Preamble} +\end{center} + + +The licenses for most software are designed to take away your freedom to +share and change it. By contrast, the GNU General Public License is +intended to guarantee your freedom to share and change free software---to +make sure the software is free for all its users. This General Public +License applies to most of the Free Software Foundation's software and to +any other program whose authors commit to using it. (Some other Free +Software Foundation software is covered by the GNU Library General Public +License instead.) You can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. +Our General Public Licenses are designed to make sure that you have the +freedom to distribute copies of free software (and charge for this service +if you wish), that you receive source code or can get it if you want it, +that you can change the software or use pieces of it in new free programs; +and that you know you can do these things. + +To protect your rights, we need to make restrictions that forbid anyone to +deny you these rights or to ask you to surrender the rights. These +restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + +For example, if you distribute copies of such a program, whether gratis or +for a fee, you must give the recipients all the rights that you have. You +must make sure that they, too, receive or can get the source code. And +you must show them these terms so they know their rights. + +We protect your rights with two steps: (1) copyright the software, and (2) +offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + +Also, for each author's protection and ours, we want to make certain that +everyone understands that there is no warranty for this free software. If +the software is modified by someone else and passed on, we want its +recipients to know that what they have is not the original, so that any +problems introduced by others will not reflect on the original authors' +reputations. + +Finally, any free program is threatened constantly by software patents. +We wish to avoid the danger that redistributors of a free program will +individually obtain patent licenses, in effect making the program +proprietary. To prevent this, we have made it clear that any patent must +be licensed for everyone's free use or not licensed at all. + +The precise terms and conditions for copying, distribution and +modification follow. + +\begin{center} +{\Large \sc Terms and Conditions For Copying, Distribution and + Modification} +\end{center} + + +%\renewcommand{\theenumi}{\alpha{enumi}} +\begin{enumerate} + +\addtocounter{enumi}{-1} + +\item + +This License applies to any program or other work which contains a notice +placed by the copyright holder saying it may be distributed under the +terms of this General Public License. The ``Program'', below, refers to +any such program or work, and a ``work based on the Program'' means either +the Program or any derivative work under copyright law: that is to say, a +work containing the Program or a portion of it, either verbatim or with +modifications and/or translated into another language. (Hereinafter, +translation is included without limitation in the term ``modification''.) +Each licensee is addressed as ``you''. + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + +\item You may copy and distribute verbatim copies of the Program's source + code as you receive it, in any medium, provided that you conspicuously + and appropriately publish on each copy an appropriate copyright notice + and disclaimer of warranty; keep intact all the notices that refer to + this License and to the absence of any warranty; and give any other + recipients of the Program a copy of this License along with the Program. + +You may charge a fee for the physical act of transferring a copy, and you +may at your option offer warranty protection in exchange for a fee. + +\item + +You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + +\begin{enumerate} + +\item + +You must cause the modified files to carry prominent notices stating that +you changed the files and the date of any change. + +\item + +You must cause any work that you distribute or publish, that in +whole or in part contains or is derived from the Program or any +part thereof, to be licensed as a whole at no charge to all third +parties under the terms of this License. + +\item +If the modified program normally reads commands interactively +when run, you must cause it, when started running for such +interactive use in the most ordinary way, to print or display an +announcement including an appropriate copyright notice and a +notice that there is no warranty (or else, saying that you provide +a warranty) and that users may redistribute the program under +these conditions, and telling the user how to view a copy of this +License. (Exception: if the Program itself is interactive but +does not normally print such an announcement, your work based on +the Program is not required to print an announcement.) + +\end{enumerate} + + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + +\item +You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + +\begin{enumerate} + +\item + +Accompany it with the complete corresponding machine-readable +source code, which must be distributed under the terms of Sections +1 and 2 above on a medium customarily used for software interchange; or, + +\item + +Accompany it with a written offer, valid for at least three +years, to give any third party, for a charge no more than your +cost of physically performing source distribution, a complete +machine-readable copy of the corresponding source code, to be +distributed under the terms of Sections 1 and 2 above on a medium +customarily used for software interchange; or, + +\item + +Accompany it with the information you received as to the offer +to distribute corresponding source code. (This alternative is +allowed only for noncommercial distribution and only if you +received the program in object code or executable form with such +an offer, in accord with Subsection b above.) + +\end{enumerate} + + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + +\item +You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + +\item +You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + +\item +Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + +\item +If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + +\item +If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + +\item +The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and ``any +later version'', you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + +\item +If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + +\begin{center} +{\Large\sc +No Warranty +} +\end{center} + +\item +{\sc Because the program is licensed free of charge, there is no warranty +for the program, to the extent permitted by applicable law. Except when +otherwise stated in writing the copyright holders and/or other parties +provide the program ``as is'' without warranty of any kind, either expressed +or implied, including, but not limited to, the implied warranties of +merchantability and fitness for a particular purpose. The entire risk as +to the quality and performance of the program is with you. Should the +program prove defective, you assume the cost of all necessary servicing, +repair or correction.} + +\item +{\sc In no event unless required by applicable law or agreed to in writing +will any copyright holder, or any other party who may modify and/or +redistribute the program as permitted above, be liable to you for damages, +including any general, special, incidental or consequential damages arising +out of the use or inability to use the program (including but not limited +to loss of data or data being rendered inaccurate or losses sustained by +you or third parties or a failure of the program to operate with any other +programs), even if such holder or other party has been advised of the +possibility of such damages.} + +\end{enumerate} + + +\begin{center} +{\Large\sc End of Terms and Conditions} +\end{center} + + +\pagebreak[2] + +\xsectionstar{Appendix: How to Apply These Terms to Your New Programs} + +If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these +terms. + + To do so, attach the following notices to the program. It is safest to + attach them to the start of each source file to most effectively convey + the exclusion of warranty; and each file should have at least the + ``copyright'' line and a pointer to where the full notice is found. + +\begin{quote} +one line to give the program's name and a brief idea of what it does. \\ +Copyright (C) yyyy name of author \\ + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +\end{quote} + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + +\begin{quote} +Gnomovision version 69, Copyright (C) yyyy name of author \\ +Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. \\ +This is free software, and you are welcome to redistribute it +under certain conditions; type `show c' for details. +\end{quote} + + +The hypothetical commands {\tt show w} and {\tt show c} should show the +appropriate parts of the General Public License. Of course, the commands +you use may be called something other than {\tt show w} and {\tt show c}; +they could even be mouse-clicks or menu items---whatever suits your +program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a ``copyright disclaimer'' for the program, if +necessary. Here is a sample; alter the names: + +\begin{quote} +Yoyodyne, Inc., hereby disclaims all copyright interest in the program \\ +`Gnomovision' (which makes passes at compilers) written by James Hacker. \\ + +signature of Ty Coon, 1 April 1989 \\ +Ty Coon, President of Vice +\end{quote} + + +This General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications +with the library. If this is what you want to do, use the GNU Library +General Public License instead of this License. diff --git a/doc/hookref.tex b/doc/hookref.tex new file mode 100644 index 0000000..f4f2a9e --- /dev/null +++ b/doc/hookref.tex @@ -0,0 +1,241 @@ + +\begin{function} + \index{clientwin-do-manage-alt@\code{clientwin_do_manage_alt}} + \hookname{clientwin_do_manage_alt} + \hookparams{(WClientWin, table)} + \begin{funcdesc} + Called when we want to manage a new client window. + The table argument contains the following fields: + + \begin{tabularx}{\linewidth}{llX} + \tabhead{Field & Type & Description} + \var{switchto} & bool & Do we want to switch to the client window. \\ + \var{jumpto} & bool & Do we want to jump to the client window. \\ + \var{userpos} & bool & Geometry set by user. \\ + \var{dockapp} & bool & Client window is a dockapp. \\ + \var{maprq} & bool & Map request (and not initialisation scan). \\ + \var{gravity} & number & Window gravity. \\ + \var{geom} & table & Requested geometry; \var{x}, \var{y}, \var{w}, \var{h}.\\ + \var{tfor} & WClientWin & Transient for window. + \end{tabularx} + + This hook is not called in protected mode and can be used for + arbitrary placement policies (deciding in which workspace a new + \type{WClientWin} should go). In this case, you can call +\begin{verbatim} +reg:attach(cwin) +\end{verbatim} + where \var{reg} is the region where the window should go, and + \var{cwin} is the first argument of the function added to the + hook. + \end{funcdesc} +\end{function} + + +\begin{function} + \index{clientwin-mapped-hook@\code{clientwin_mapped_hook}} + \hookname{clientwin_mapped_hook} + \hookparams{WClientWin} + \begin{funcdesc} + Called when we have started to manage a client window. + \end{funcdesc} +\end{function} + + +\begin{function} + \index{clientwin-unmapped-hook@\code{clientwin_unmapped_hook}} + \hookname{clientwin_unmapped_hook} + \hookparams{number} + \begin{funcdesc} + Called when we no longer manage a client window. The parameter + is the X ID of the window; see \fnref{WClientWin.xid}. + \end{funcdesc} +\end{function} + + +\begin{function} + \index{frame-managed-changed-hook@\code{frame_managed_changed_hook}} + \hookname{frame_managed_changed_hook} + \hookparams{table} + \begin{funcdesc} + Called when there are changes in the objects managed by a frame + or their order. The table parameter has the following fields: + + \begin{tabularx}{\linewidth}{llX} + \tabhead{Field & Type & Description} + \var{reg} & WFrame & The frame in question \\ + \var{mode} & string & \var{"switchonly"}, \var{"reorder"}, + \var{"add"} or \var{"remove"} \\ + \var{sw} & bool & Switch occured \\ + \var{sub} & WRegion & The managed region (primarily) affected \\ + \end{tabularx} + \end{funcdesc} +\end{function} + + +\begin{function} + \index{ioncore-sigchld-hook@\code{ioncore_sigchld_hook}} + \hookname{ioncore_sigchld_hook} + \hookparams{integer} + \begin{funcdesc} + Called when a child process has exited. The parameter + is the PID of the process. + \end{funcdesc} +\end{function} + +\begin{function} + \index{ioncore-deinit-hook@\code{ioncore_deinit_hook}} + \hookname{ioncore_deinit_hook} + \hookparams{()} + \begin{funcdesc} + Called when Ion is deinitialising and about to quit. + \end{funcdesc} +\end{function} + +%ioncore_handle_event_alt -- not available to lua side + +\begin{function} + \index{ioncore-post-layout-setup-hook@\code{ioncore_post_layout_setup_hook}} + \hookname{ioncore_post_layout_setup_hook} + \hookparams{()} + \begin{funcdesc} + Called when Ion has done all initialisation and is almost ready to + enter the mainloop, except no windows are yet being managed. + \end{funcdesc} +\end{function} + + +\begin{function} + \index{ioncore-snapshot-hook@\code{ioncore_snapshot_hook}} + \hookname{ioncore_snapshot_hook} + \hookparams{()} + \begin{funcdesc} + Called to signal scripts and modules to save their state (if any). + \end{funcdesc} +\end{function} + + +\begin{function} + \index{tiling-placement-alt@\code{tiling_placement_alt}} + \hookname{tiling_placement_alt} + \hookparams{table} + \begin{funcdesc} + Called when a client window is about to be managed by a \type{WTiling} + to allow for alternative placement policies. The table has the + following fields: + \begin{tabularx}{\linewidth}{llX} + \tabhead{Field & Type & Description} + \var{tiling} & \type{WTiling} & The tiling \\ + \var{reg} & \type{WRegion} & The region (always a WClientWin at + the moment) to be placed \\ + \var{mp} & \type{table} & This table contains the same fields as + the parameter of \fnref{clientwin_do_manage_alt} \\ + \var{res_frame} & \type{WFrame} & A succesfull handler should + return the target frame here. \\ + \end{tabularx} + This hook is just for placing within a given workspace after the + workspace has been decided by the default workspace selection + policy. It is called in protected mode. For arbitrary placement + policies, \fnref{clientwin_do_manage_alt} should be used; it + isn't called in protected mode, + \end{funcdesc} +\end{function} + + +%\begin{function} +% \index{panews-init-layout-alt@\code{panews_init_layout_alt}} +% \hookname{panews_init_layout_alt} +% \hookparams{table} +% \begin{funcdesc} +% Called to initialise panews layout. The table parameter has +% initially a single field \var{ws} pointing to the workspace, +% but the succesfull handler should set the field \var{layout} +% to a proper layout (as those saved in the layout savefiles). +% \end{funcdesc} +%\end{function} + + +\begin{function} + \index{panews-make-placement-alt@\code{panews_make_placement_alt}} + \hookname{panews_make_placement_alt} + \hookparams{table} + \begin{funcdesc} + Called to make a placement on panews. The parameter table has + the following fields: + + \begin{tabularx}{\linewidth}{llX} + \tabhead{Field & Type & Description} + \var{ws} & \type{WPaneWS} & The workspace \\ + \var{frame} & \type{WFrame} & A frame initially allocated for the + region to be placed \\ + \var{reg} & \type{WRegion} & The region to be placed \\ + \var{specifier} & \type{WRegion} & For drag\&drop on handling empty areas\\ + \end{tabularx} + + The handler should set some of these fields on success: + + \begin{tabularx}{\linewidth}{llX} + \tabhead{Field & Type & Description} + \var{res_node} & \type{WSplit} & Target split \\ + \var{res_config} & \type{WFrame} & New configuration for it, unless + \type{WSplitRegion} \\ + \var{res_w} & integer & New width for target split (optional) \\ + \var{res_h} & integer & New height for target split (optional) \\ + \end{tabularx} + \end{funcdesc} +\end{function} + + +\begin{function} + \index{region-activated-hook@\code{region_activated_hook}} + \hookname{region_activated_hook} + \hookparams{WRegion} + \begin{funcdesc} + Signalled when a region or one of its children has received the focus. + \end{funcdesc} +\end{function} + + +\begin{function} + \index{region-activity-hook@\code{region_activity_hook}} + \hookname{region_activity_hook} + \hookparams{WRegion} + \begin{funcdesc} + This hook is triggered when the activity flag of the parameter + region has been changed. + \end{funcdesc} +\end{function} + + +\begin{function} + \index{region-do-warp-alt@\code{region_do_warp_alt}} + \hookname{region_do_warp_alt} + \hookparams{WRegion} + \begin{funcdesc} + This alt-hook exist to allow for alternative pointer warping + implementations. + \end{funcdesc} +\end{function} + + +\begin{function} + \index{region-inactivated-hook@\code{region_inactivated_hook}} + \hookname{region_inactivated_hook} + \hookparams{WRegion} + \begin{funcdesc} + Signalled when the focus has moved from the parameter region or + one of its children to a non-child region of the parameter region. + \end{funcdesc} +\end{function} + + +\begin{function} + \index{screen-managed-changed-hook@\code{screen_managed_changed_hook}} + \hookname{screen_managed_changed_hook} + \hookparams{table} + \begin{funcdesc} + Called when there are changes in the objects managed by a screen + or their order. The table parameter is similar to that of + \fnref{frame_managed_changed_hook}. + \end{funcdesc} +\end{function} diff --git a/doc/ionconf.dvi.gz b/doc/ionconf.dvi.gz new file mode 100644 index 0000000000000000000000000000000000000000..905ef666b25cfeb7fbaac77cf9b89c76d8c7697b GIT binary patch literal 117932 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 z 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 Q&~H-v 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 %Eh1Rv;&|(+h*X~L6h-!!QniAq$u4iM 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

    +AjzZ|wzLRdixkb_Jm5T{02`Q~ey3>3Vx-_R* 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

    oCqPAWu&t%L9 zWXZHq_%RbPZn*>Vg2suN;|xgG-ZgfrJFp&;6L{12TY8eyw^BGv#`)xi@731?GubR| z1~P_^wQZx}-bm0kp$591?QutQ^#hd?hbwvW9Sx3gk??#3tU1)Mv~vWdYMi(44oaQG ze)g}MzlK)?2jA?3m9<#)0&+KqV!Ju6b0^L%T*ZY=?Bgt;*T&l{ev^Ze{R6hTea_12 zwA)V|vD=}iqrPI9-u(eO?X&g6c7HIwDiych)rZ||_gQE^fQ)b(2ARFa5&%2Brvc9a zhD$>qlrcM;23sH=ETV8@sN!jsnEmC=Q|J85j zmjVq}6O>=Wn%QCdaPds4U;1Y7kC&Qf{+j-G`#?DK-|#gw`DpC=Ter2a6hJ3ItH3OQ z&lfZFnLtQi&3yll<-u|U{mm{Yih1Zg1uOBJye;pyPu@$SpMNWi8Ty)+M&Xt#yU+yX+ zX}X^Z9mtmo#W`njFbu2MR<#x-B)Mm^`WHY)7fl5~*)wwMs{%DdS zXlx>LqOO6tUMXylnyGNJ%3V*>(U?gKxa?*&LK;tQ=%;=cpj!%*S^>I!|6xC*X0W1< z=FX|<5xP2fm=Herb)6W!@5A6N4f4sy|tOi%h@oOPpqvf3)%3}8>KQNUYm;)P}x2x(F{8L zEoO1;@SpOc(bCPQH%=?yyPh9P^X|s@J?E#%Q8}RHf?7L|7(W);%9ke8nAhw2!17^S zj^yI}(l$|;VWfCeum%EyDjVBA$>W&s_V@Ole`GgZc@U(=T1EcDGj1ORjF~w*o>zPd zJh#Y6ZvMODqL_nQts;LoMM{Mm=zTT~CI3YpN3Knw5L)J_9ZuD!G%IDy{9{$mh07o= zP)qTsg<%hmd(`w~a-CA%T05A@Tsn|k{sO@pq0f`hI}nlv#iRr8cr;AU zMTX3*9B~?+V^tk|8$d>j4B1db1{^!byPY{1=Cl35Egjt}qt$4n1eTiXL5!fsH69R08!?OU@eg5+h^2uEj^@3mTAkqHFqN*c41omXw007Gf})AC9P$c!rXH?@__=)?W$q!m{>=9^2A0QF;Y_KpCDq2P#k}wx(1_ z*%rXA2JvWg%d-$6!t|y=lX#&@xD6k_k^<2v?Nqcq`U@U4I)mTPsUmgIWD+aYP-~tD zy~bV9l#OCzE8o=bPND9k!dm86|ING#!?3tQoQ^=eeQFg{HANts1K(yoc&0=3Oe)c7 z3_8@3(mS|bLL3;Umla*mUT;OsGqly^YEha_{Mvb%O&c0s<8;zmZCDN*%hX#keKh|J zF*(wY7ql3Zq{Kg{SdhbQM#-{O-W*H8Y^epP`k`cS_4hSnW!uUhln5^AhRCZU`%L6( zUkRLU+J!TiL3xYo{+o^$)n#@iVs=Wtn0;77k=Xoa=M-N6F_6~k4d?pxVRSvtfESXr z6)n#I%hlaqD9sB!5JP$Dos<>eu|z&JL0_5^&FGZ`7t{jcy|bOG0pWTAC`+c(b35Sp zNE608Ko(e*#T1nC=P6gSC5{E^X{Sk>?T`ri(#|2Gpn}b7{P>}y2jPXUFqOFh|;q?*^x5G_9$hlInOeh zh-1a=t|Du-Y+pxC5XN3X@Vzx>O(vN%8$L#zWCGD4w37)$tMEk_rI$-~KQV3^Kr}p) zeXG6b-I|ep4l1kuQd`5&Vwc{~WK{|f#eOo+xDmzIrSbr2bNARb!T`Meu?8RUOfJqm zd*#Ehu|1a|q5(Qd=gSp&rlUz3(Jg`zDMGiE1HcQ6QONC-|B*h-ISz2SEM}$gK#ZTt z-|+|VD?wR}n%kD-W?9T*|8Gq9F+q=$ECysBf1a)XEglgm3DJ`nuR+d`yE`If8G)h? z(i40ZPZMxmuyYkND<$r>w7e4khR(nH_gm~ivsGzx9^jr(RAU0y6?rNLCY`1YOv0yI zGilFka^M*y_drvkFA!L?c_;%$Bi>%LWBT(eXTS*J)@>~t3%U#w_`M*J)skk!9clrj z5;rH=;-@Z15^|j#l@Hg>O&`?#--r6G}z zc#@sXcwN#+^3#t%mYEF>KggU8>cB#u1;WIxFZnA9n9PzZKOO!L31R8@m>tBypN=l) z`5Wh?>f^sql*HG_H8x(z#H@?$94p%;iN;mK-~m5uo@`G>q&I@%-967 zYVT?^1cBOmtDdzvn1oebvxr)XqdDW|w*eH0O>lW2ujyOn3E;-13V8tzA*MZgY>5oC zJ)C*oCt;WbhjU&nYOsqh(GT*5Fb}uvp3pDc;1)$58r+CBCjl+E|5|j8IBwel(@I%l zfYn*`kU>wZ4`>GvJmuV$3v6^sz{O;IO;+qUM5tB)hM1Age)41Q3a~oAFhqkM6N8qTQ4M9>Gy2sNa{}!{B z#uG7b&lnp|S{KBDK(hi7Wob-tp~2C*3l*OridPP>+a|7oMuc5gidcc$M2j4cVdh4` z8J3HI`I2XI)v-d2pT(^AXdQ>g!tF_7MCwR#VeR3{FkEJ&5`E>~!v$VY-ZIaalt)@1 zxCx7%WyUX)jp)eL=ATBI4C%Vr+_uNY6DH@nYjkE59D2snB*oD5Ki%GH60%aM^g3`G z!YiQaSRV{@&DhvK@M556M*mK@kK~RUPasRfx_t)bDn(MyVtw`&}H74`J-EXUzKOoYfskr4l|L(IW>AhieDxA6B((V zFistCKI%X2Ax|=cle1CF#WXACSbFLfVGnMzeamdUxRGZh>|s_pJCTy$XXrj>X3B5R zFQp!f@k+)-b;58>>w4rDR9E=vczw{W_!HTrBrPc0(aXhG zPCq%u7~-hB-bx-s)5k~0f-^T5_A&LG`HVswj8Iqim92K^tWrPosjw zY@?e#SMj7yh<*PC5(1(+!KHwMBs)R6OoykB?Lhapv26@77k8JR4mJJHzZz34^6c919W&Qm1@#=W>=@OWrWRy{6ak znlywWzmLT1c+}YrOGVplhdd$U zt7mKHJ;iYrJW2*X9yi0ucpSx}lC@wHb?v`wXm0S{Qkagw({_>#iViqb#4m}qQHR2qx^S~!%m)ZJb~ zNNS1Z>m(lO==O|xY4BFUtOZF0+76#e7V6>%X(nJS@C*)yL>rVndAIiSN4VQpoRy&h z6PIIAzDUP~kHBGyaN~VG2xJBAKeY6YHt)M+o`p|liBWCv%>ZtO=G*C~HKU13RN>dN z!**v@VTAi-;MK=cNgQ7XbSXR)IP&(>p_TzLrE?t4doG(lMU+LtM zH{$Uw0Qam(1&n2J_EiZ}q4a!mwt#|8+qTLm#4yJtv!qA79WaD(QCtyy@u?1XrqLV^ z&MUMb>}NlhOUk;_A!M~yi9msyYONidGSHWagf846L94%<37HBAV8Ykwllf0PpKLoMHHky8hA03Zztt*1T-DmCvLk_gwaT*J!tN79Ym$JI3 zaiy(*B?K=b%PgeJqNl*aMdBFd8nIfY)-mwDz(<9S-P|BVUmcZ5ZILquAd81SYm zodj$21if3ZPdroRw+0oMc8d&^_JryOJnrcvN+D&{dvY})Z=M?Vnq=DgT*{-lfA^kE zHIlBLsxMc)WMIVdrrD^6;mjwUZJ1t_b8UsLa(*&|;w>;(7GB{^*tiIJ4A{+dYO+jP z2vbpdYqq)&Q$ae8DQCLB3{#Pg`;>0GLrk^9D|T{+BG)DP&A>j)KEI>$WAIk<={7)3 z$$Er_`9_DCc80Zr%Gv^UJiUjqXij>7K>}!!3_^ro{N}9$uQaUNaz>JefKYhfAH@%6 z^4n$&L$X>C(poNCvevYRCvE~X+aQBie1Z@eG(F#Y9_6e{zDK>$M|p0gl+cSqawInBq2ocTm3QIs2jCzJstLTizWYohB>S%ybv@saUU{|UN z7=>&r1_5avihYa#4w};%D{**)?iiyXDY(&j*Z0XOk4MuhLZ0xPH(2`UJ+=5_-!o0Z z;f_D^bb#WETmg=RbaNi$bksifK4_WHD5TwvgA0TcPH%pOS~j=5aa=-9Ue&b2BG}Qu z@@eWNR%qyPudHr{l|suL7`!czhld)Gx6?GQ2EjO=s4c&vQA!%zFOB za!dNpBPF)X9>>+lssA|pyysb(LJmT+m!l+{>iBv=yc7`^5$STnm9oxp8l~dO5R#eH zJZsh)w+{%J;mT=bvo0PLbgdqD$vIFm&Ihf5kiHg7KeH%LdspO3Tu4Wv{kU3k#w$S1jL&^7X@2PRn1>(lV^ z^Y`5e=BZG-@`>2u%$)zB%Uw`iJ3?NI6M#gEMcv|77k8za1bach@5;yZY+kzBqR_O` zy8?;*ayh(9rrE`$g3jHO)t87$pR4UFF$6y<=Oty(o5hrflF?SroihPQYi`Opc~~xm z#rbTYx||FO#U%XiKS)<_aV^p1r(rXvEr2q^}6!9u2D5&YZR$ zg^+6c)EmZRn$Q(0M9fzsJ@ty?jzk1Ta@4xXxyYt8!Lv0GZ!8|*y8j__ITiCUx1Y(a-nCuEswMHt^68L zW#LQRL8H)&-&_ifC`+fKbjs9$EYOySJplKKg}jy!I3XO@sWvab%Ny}BC}QS7VTu~e^aQf*@<9LX)B{lhIq*Q*WMShrmC3DY zPS5HyF$_BjJd_FDgpGk8svP`v*y*I})j9D_Wmf`geJa0>aIf(J0T~-hNLRPg! z?7mW{j(mq6g0#QZHE;VxZ{e@2c%HXg(56aqi_U?2%u{N)J?=)=NB%dtDk*dt@=|atC@GV{Oev6T-1& z#q8IzIT6QEpS#`ocZ0uchHY1}UzLy0?6zmD z!eXz1$3OgC(b79NLC7K+{8ed-1@vYPQjl!1S`?&NR~~oml9iIB$u7!cG{%{AIrraL zL3Y4}?QOx|e3l-xKX^djm=0p{R^AZn0zhf8B4<~vDOR7mGRGDRC*dM|mb+ch&>fnD z5|7509JLdVQ~L9-lXwhgV7Q2B6K&xn2V_DoO@MTBe^EZV0@$H*5`}eBOBA`(@if$}oPUV=0U_CQ7? zQpG{sR={fPbz0dh{KwCi*Dha9`d2;9$^h~p9!;*me7U^g$w82U0k0PQ{E9)|wK|i4 z6qvu?4kL>P;?_k;YWnH2I6ILpGb5P9QELQC9l=`tb7U|1lD+c`1eUf~9$vF_?IbbF zAau#6hmc>KA=qZsKfd66@a6@DST8iLdBLPdFU%EQaF&!-K4W;1rgMD-pg-~&3b;2r z&Y)n{P1HU8(mW3v?R6uQS^b6#-J99ZwE$TV@%B`T>X4v1QZ@EYVz`+DrtpD=x=&rD z^63r5>$oTq8Th`_z1c2J%yM8nw1{2YN;Oy6tl!a|;DCbf?t`YlG9*p0aY4zl0bP+6 z$^G!IAbN;PPvD~4ARbSWE8@m8APd6=O1%49*2n3{z3E0RQ>Lp3EwEJI1FP)A+nn($0 zEA(9Y9E<+;SMR;K{f)KHBA$n9w}08#{M{W4~+8_-1cX7)jm#m z03es-*QAbI)cm9g(D2#S(TOQ3oIQGF&TOvufE+@LhfF(d^|iP|ST4JioIHn7NW1b$ znE;={GStHSEgX=@$aqTtQSog5d4pS<;oTnhzjw(EFP1|=m#57#w5)t#6hIC~aY0-U zR-b4gw4Y1&LMtv@tu&kJ@pDT1%Kb39OH%4~y>!ua?R%7tx*a`VloC-lZ*!CGH)2-3W(t?-aT7h}`NR21j!ti5Pf8f@rE`;vqHV@)@K?@q^u#tkZQcUgI2)gD5DL3slHlm< z;TqIJbv)TLa&*iY+KtO!ijMiqmVd=wKSaakvvqmbESa#6M!S;xZ4Wi-$%pc;+z2(W zunBMgvHycSMW5!iK*CJ2_P-bcnua?dG5! zH2X+TrI{P!?`N1;Dwc@)LZCja>d9@A%p6-+k#k6sMKdXUu{r26UFF><|7maOOqB1& z*(!Vc&^Tk?wb{=G`-jhu)EnnIWmq`2d+2*(zls_o&-{2z5#6TDXIuRp<7k}VIKN?8 zgPtHN*SX8E4zFtsINLGv-dCmQq{X925(N}-FKA)Y;c=5bxxAWbCcO&{onB7()}8d# ziLBh&^lsG6?*`MmG0~9N^lmaC8jBO1gebswvg0ndn@>olsY`3phX|vc+#w~LCoUN_ zPM0KiIJX-Gle>iG%^JyFuAX$0yZI=haqIuqlGce%r*+OIccU+ zRBw2$*$&FjylQNRVv0-lFO4**&n&~66G814|;sqcd@U@b(>%%hhYghqaV~?jf zhNJ&#k!NS(h-FP^I=0WCFwM-!@G$p1(e$y=%!I2V2Za5SCet){*~@Dn?8$0*!pz|q z4B$he?&X2&O(-E!hqnT2KYxwzZpPbfl_+V|pr&1z&mwq0B&tw*9j29#UGu*xOV>_I zR>_gr_DGgB1@8OJ;HSTS``53AADH5-63hkKN6Sx->)8h9mz9J_&)cY0pT{)7S(0Bv zUugl49Yg-OH4d269pA0M$+KypAIX|~S(yqRkNp8dzFAspx2t?PgCMiTQlOHfh`)6e zR5EEmCDArgB1(L$ABCRR*v1WOGYZFi&4N9L#rAjr*FG(SXvqXPl|UT590p!&I$|;I z9f6W1t+CR(+zzP?DUuiM`5sI@zw=1Rgao}7HFlLRrRS5HM=ui@h=-DCw@c{i)>UFb z;JDsvnM#x33BC%oOmUO}gn6W#3fQx}@?P?-gcTr3dUU;o61>Uuin8S?<|+yPq*2m? z3%h{NDNW>YxO3E_43TWAAK@*%2u}lmJq(i#yOL}IGLAm0FKNbI8y9z_JqrC1iH8Jo z=vPq3pX$F9IcTKLS@yvUIjE*RTeRGGOdm`R6zbOFD?zkCX9}HH_QL9Hc@E7coqhiJtL z=r06Fr*!FE&bBjHkwA|@8eicjonF=~VAyCa&rb*j{Ky}^6=qHQrc2ALL>NNO5?C&X z9!DDvF)Ke02&;y!61tRn%0Dh*JW%UyiCaE^XQNPhKQ?>3o!*b>&lTx?Zt2XM{flWr7xCpuzAbk}9+RuE>><{H5EiVXfP>$s;o)F(~-yQjJwtE~-^YN+4!OdQA zPx{qGsBYB~=)ziCb! zxqdet!bxCmS<|Gd7mIS!lK(9ylHQHZBd`=z|G>mo#LTMz`) zEH_`9?Vzv*M;=0y*#NMbr@iMPNJiw=5B|C#Yj`92>?)A9p6w6M%)f8VbVcv%k103& zj1?2!V9cqlT8Nfc9#sb=iLmF)@s@eIYxMm)V>_R5L7%UIx9MJk%5$3q2pWbF749ok z#PH||Aq9jkd#dVf($?_aZ|2p@)(0|5(Op7LbMGa-c(*otX8wU9ax>BE+2Q-N{PkVghF!+0A=Z z(s8ly{1rJSL@2sy3+Z;FIYr4=JTY` zHDpX!zMUawg1dUBD)IpI6jkdfl6YEt*;AMqHgjXvZ2a3l2P~*m6(X~UW}7_$L-vr< zKIOEKdHvgw?2&$fo@_s4m2fIv?m3@|s{^n#p*EbpdttdYgFw%wEHDS=pBg+E|8;8| z?8#00M1r*NpFA3%YJe|ute}Isv`@Cefq@;7ju}+GG$vOK{Bg0~J!AKgDs*>Jp}SRh zB*Tz6+%6X>j{+`+RuE}pR!Obb#6G1u`vkLKbyl1r7|EzlSa6ue%_4E19BK&KcD zC?%Zw8-91Ff7~xOfn+k->fqE>`ID=L)22ISv403o`OrhO}|VUGNKAFzX* zN4phHq{RSP@`o*D_@+5D6Vv{YzQ1v)-xKyZ5(z^|U^NaJ2-H@yJ<8d8t}~(h#PtXZ z3#Eh9JTNssiKSKz?B$F)L782M(s~rmvj$*6q%dZYBj%6qN7T}8d&FjV)9`Az?G@!4u@t|-{VbeRv6|YL9&1kF%eFLJIwFsIJ1?HWPPM`E_e@jtTT~} zMzrqp5w$m`BdW;8qmecG)2_P3)92I4ciqe$_tmyR>>I}?4A4--NiR|6Rx!TG z)^V7vRvhWHSc}bW90x0pQJ4SSqpuyv1?v`XZ#L;+2lB3XX`G)g{q%-l3Ma7#P1g4= zf`y-|_1=!GTHd(PfUkmK#pui4dU6d}#0ib1pPA9z@3b;2F>K}Bt48k>N@C{J@uClx zmI{wAl=t9_=)S4BKo|XRyNpb2FWx`jsj^xq(CL{s=(_hXHK$zWkM|AP6_eh=WYRz5$ z`8Weu8v*?Xw4TtI$fhXL9syF|085JVIwAe)-Jjmh&7xXqjg%bba!0&Fk=KU}+!Uy` z?GI*$?Suc5A|BtQqfs1v#;@pdgolD=nf(&?9yFM)AGV9q(RPUYYXvUSI1cmm{D5GN z>P^Tl%^EO;60d>Ih#eOOB+r~?5^BXHh(nLte<10BJ&jV5bwVmX$>oy$(*@6jyZPC0 z&CL|)s7RiCys)S8?9p7z{NfoSw@oDcBe=xb(~~(uAV-uW5TMpXv^Y`t0Wu9r^B1`}CI8Q^*`)S$t$LT}lxgwHMn(e%VYJ zgY?nj;1yeCTp>~hyW9hDkNS^qu;>1!&v9q-$Ehaq9}r+2p&I*WjucS|OD`9Wydl={ zPUMt!6B*Z={7+O`Di2-ban^Q3Y#}^3u^=f|MMWvPIP##g26(fKWQ(_VIA5=i^l-rK zNKqmFg&@73`~--aMr|E!8B}jfQ1A_QyKAT^OwJDQE9y1oI`;@kDOW!=cBGhIfztYi zxzE?;Qh%ZAuOquSSDlsFt#$DWq&>Xctc%J|rkohX>;L%6x0Ijz>AQdYq`iGL+6PC|H28o|o4fz<)Ath^a(PgygJ$VS_Izs}6LLbp zY#dh{$_1s3gpl2$*tk&b2;iCb)M;F?n?Kvx8rGI@@G-$_knaYWIOSAH)j-LBXkD+@ zcS6lhWx0q7FlchHO5Dp6rUxJ`t~S!gjGsr6Y`uWw-roDktRQB*<dX-hwb{&`2KG!5-T_+SaH6n`bJH7qWwNjJ^L?zfX^`q;HTZ^56QX4!;8!^s?7WS$B%1W2?vQzV zi8mp4*ms31Z>kplRJB~;Y0+b?WPyC|=o%;Dbj&6NFG_cVIiVT52_HxMyp39?>aA^K zpo2F?D`8WGJx;SEK*mv_SSFrw)%29@8CuF2HKQBbgNhQaTqw6*Ag5jkGa2{lX6!Ul zSYNRgpv53|1~?Kp9a9USU0967qUL&@CGhF`kV|nP&6`*&Se*)Ar{Y7hWxTBKEuNc`2&>XuwFlupXtN5 z%;SXi>i2`ykw8y2>l}wE#+G6^_6nO?s&;l8-|09bcyt8CH5cR&>Rc>thwoAei~(5Y z9~M=D{X89ypczFIUzl)G9<48ChF^M^*w_G|==Y3(G>^dvRffGV;(WJ91W#adRf2N8 znFVd4C!XX4L%?xk(JU*UNSIbL7SWOy(Vg%y1LFLk=pxjORIvP3a)q_|uaN6qwxb2;B6>4}5zC-Ny1 zN>bL+X@r^z6Eoq#Iyn_QYyH5LF#)ptjS)~|r;1IRo{Cgv@<(e>g7iCl-F zvQ59V4S6W0)xh4$>EhwSbc0A?3~XEnA(qr;<6TL}KvZ>|;F~W-q`H2b>>-rpPMXKq zOzf#Ay2Z%k?+@7$c!V5W-3%=M|Hs~VqVc@7Hv(MHM8i4Z8p*vExV^rL2$v(+@dq%;GcrEaA*M5 zDoF9x{e43>q(dqBR*o!~iz(C??vBuy#BBM!zT^mf|FnsKL%v6!hnj*5Z9AKS%S^P^ zQ_`;d-s#Z`n1VFx(joMf@_6tYu}&>_b^5&0QkWx z{&mOZP2)+B71DbCqw}7>I{uL)Vj4CN82VB=j(z17wt0kzHD6*@5Kyx-tpWmIuUAQL z`*S^~j*?moquxvmr|~JQg{#!U5mhw3V}GmTS-836#y$)xN{TJjyb94b8jWWP*5uHk zcteNQ8sIqO8HjL(yD)3&Dyx+gLlT22wFYHb(uZ(1 z@u-d+G)EOsj59y6BE5_z*jL;+Mr}VEp;U;LX8)2jH5x&kq@^WGUAlMB%7LQyT!A7= z!Hkk$4)SuGkQBE_4~E2cbdO1v%PaKV-aMJu*)wdNMleXvzJ-M%r|maXiRY#)Y42wCDHNz@OfPV zeD5&bCqz&YHUB8z#sYMp{c7-GBSx|uFeBgRwW^x0%l#E>@&U!D9ulLc^1%D!y9Z*@ zrNr6%os=`oUYzLb*c5OPJj_amYcHgA!Rs#rVPrL!i>pYb!>H7Ip7I!LS209KF4-N> zaAB)}^unfw*^<~qGFln}5aA2D4J988%5Y?)=EvQhf+Cv&i&TF^QeiF=h9jJlRmzLN z-7;fiwZv%>KN%2E^R(Q2yWPqA`%+679dfb4;(>~JiL2=W|J&}{g1r3OC zytK9^ZWnDn^BY>R+Z~9%@pI#8JTi}wt6d~nSk;K!u!P&|18!L5+%OY3sQb0_+%H?q z;3uTt@3qQSdtS}2E7~c8Ahh*kFbmiLxmdK*nEj8oz^|ZWtZbXILUtHFANyrQHLn7X5rL3VY{Eaeq(1Gcb z1ubk)l_qH(@i6sWmTqCG04(9S?4uI62F_x01UExVezro6zKyGkRwP$0(3Y9M3|DMF z6`;4rN@LIp?q7`1#Lcz07Ha3-IOksr31KCVPN{FJ0R*_8nQbZ+5Jl8^Oj_9G))65| zf$i*g*p{CjXG(jHXaB%9lNC+@FPjs|CQ_Cc-t9WIgNH5Df2e7ns`B^zmuMrb9>$zp zs(Fx1BJ2#9Xpx>WuS%)JxrD{?+H*D}m1hs6J+Yr1&FK|3gYkWQ9}nXZ1(>hR7(;N! z{4X?j0Cw@{-l5H!_GlO@#zB87COz&;1B3u>*pmfXTjBD0L5wP@RpLHTHj55Xk_9!o zYv*Ic;$x_mi6d@wx}B8|ZzuDVBvUH+b>si1UB%Zw(0cji&tJcKrD`Ht$M)%8=onhVC%q8Ga21tj}F&^*3=`Im41 z`dU2YAOHI2?^IG^2o2ur9(K1i!>P|&wWe-`6V_O$39fzxU#f?(Tn=6`X;|F`kZ{Q@ z1K;CbwmHRI=(ZA&q;W}%BM0lrUaJMtycsYu0Hu||aYGQH(gCb1^=Glf$3>5$C)9RW z^b^cz6DfGx@4?fQTOQYIAtZcwdY?|yP=7^Df^)^VFedXF!`Hqk6pws<`U{j5{f442Bq+XsVFPHH#3e$DTM9crZk(F(KN^jG0zIWg@2zcQrIAVGM~Krn*Jti) zhVb4l`;?~VD6x)NMC+FTeTbn;+<&+lR+>JrguDtQ70oqy>8KNRL1}go?aJSur%0 zlP+b#g=6`lJCOf8lEi02QtBxya$_bl6qgZGjXA6$99yC=Eiv`V#`{d00`X5;qf;|G z=``-ahY&g+_@Vls$pYh=jm}r;o)Y}*46Oi8Jp&ERfzH5E0m8QCSKE)?K!Yzn3S&u$ z)^iXRd5E(li?U>Ng}w`h70yA^hF468Pnn=__QOTZkW9Mn?)3J>sED#G9VuYbeCGv@ z<`;+UYPAN}GBVCTmV44TH4iek4_AXFO)>0LulQKi9C=laHC$BVCE)x>Mi)79kmqY9 z4QU^dj-blfDNg)en>QPeu8>U;rquQ_Gk*FfRtg6s=-iH-q@ngb_0NSt8QG72wCrEK z5MXTRyz(&0NViV=$k)*DXtpRBT(fD?_9Or)&J2RC>z+0kf#IEW!(9#e!pz5!FV(cc5;XH zH|*F-^D8B2!ULNbGcmoKYq2x1&S8k-xJMK+?Y0iZ@p)2#6IA$Kzq|~daTh?Zv3+8> z(y@NBFB1!ODU%@#)GGOTo*1CDgu1okI8VN)6UVKgM_4h40iZo?_6~cr{l>NcP~;(M zqSfGA$1vm}bF6l;jfx@-`Io-j0o`_`B`*HaaSybmf^4WQi(5PEy^v2KKW{Urjp-lOE?qV6&Uq>k}I!r*}JM$_$i6ogvN& z&(lp3Pr_n5ub`@v5}`g_Xypp$D}NUBM}b)wRvJJ9tWveXV=DoVF&}V}BD2oT0AtU4 z@Fpy3&X_1WYMEi#oG~s~+J2kb~j6;+q!(bE&yK+6`0$RPeNoVVxOJedsu`}OBjWDpglj90f1hz!+8+&5 zwCA-sWKImN28j;HPRT{PF%Z0C7XvN)7zn7-7&1667J{X;;n3=AIJDF#Xce_%pp=e? zFF7~U2*xhg)48-m2s19*s zeE!_)I=P)tE6S2H5U17HPopBzxDSD>5GH0+qzd+}O=w-R3%=!Zb+>*D%5^!M^-Ak|*edu$F0Fe7C z9v8PmZ-P4%2R6MAqEY_+3OiMc&CB+_bPOx)_86s8NTp4mE?l~}q9KdMnvl+OOp@{F zN;(xNZAc&S*RuOrjX^!*6QYK^#6We}U+RNLMN>Lj16>%OgB8ihP5Y_|EX_l#Dr2)W z+9|g&{rQzd6G<-pR}f7ES4uh&XZ{;h;sK2mT-2Mv%(@aRy|Ps3RMb$=V`?q8RF(px zT#(WH!5Ev_-7q#U808?#Ic#@M5sGvSdW+!T0qv86x(?X@_kd>={_OJ+$`!Gxz9htN zFvPPp6lIbg0>K^g7*9oOU8L}Awd_?9mX>87*T1J}c1jhH1c@nz8&ff&?_Ev&H1qEI!wiKonKnF_@>W=+-m%+)Grk~27o|T9z7%Fs~+!2SWsD;VDz+j z%y698%y){Sb?0c-*6i3!Y?Q|5kDn7^PQ5r(+}DhrYI4*2T*?JPkXZ%>%NKjO413kzBfrWNkB&Y-BYvXjAMy_WgRE_Fjb`oTcRK z9C)UwW#1h2#g)31NCHB#mhZHc)1C{iUM6%L%7hvlHfd)<3;Oe~oC)=$v*CtwW^{ET zExPpRURTYu=#`bW> z4LJjkkO4DK8R=}dI$80k&@Mh4ZNKGwecUr&66e54s^v5CG?d6oE{DUk&P-39+%@ge zQ&YY0B|fqoNNX-S#pssIC&%k#W(hKXi!)H2>1gnlpPvSUAc>ck*kF?Hs507P<3ypH zcWcP7d;C23(x#ay-<`)-xSI8Z4?I~8&I^ndST-JyMze1E;n9D%XmF- za_Vk6Nq&)%LTj;&X@8>3ung1~Xi>Zw{Pfpv|4M`<3mCik(djFgaV4er{q~`}lPv@V zDMDE7%6oJDyUEy`JAKoX$nrLZk{=c1eo=#5VfL>MkDunMLH zEIIHfoFM-JEvIaHrZMHRuSr7gY!@)FuGGgPEFaI9<6qKhu&iF0b8i{>8+Dl^EvCUI zd3xY@0+*7Wuhf{vN@Lt77V6HXxxn~}76d07(%2s>i(=#n_4dZ89FHf~l#Zq$M)b;w zHAzwdwZnLlMoBMHiw1xUl$1P&R{NI@we_K<=LC%sP9Z70#e^Vm3TC7c4M3?>`*p$@ z6-o&+D-U2_?5c^cX*?>VHEQww{-fM&iDdZY(u`YC6v691Z5eFV%F+$2y76{lo`x^q z_u%oeSyXkrixq01c+PumMemWb2$-WZfR^l6x^=l6t7Eo5kn*#GO*agZOG2inV3Am{ zxgc4O^wyq|bb|<5#ceJmg1=HK+4<_t7ks$zu4axts_&5`h~Nj&Oz?1{A6NAI_;%95~{%P z6ZYB4q0!B+p?qqnvbphnfE4k3d-RyB#Jh?*V_T%*)@e7kHww)1vR#prTm0Z>8O6#l zF(H$Od+D%MGvcSD2HOmCnKX&bUK(FR%hZWoxX$t=i@o@Ad|Z1vj=LREi#>G9BR)<$ z2$S%^3HYZ`Tqr`t1v3xRc5#8KSN}rH1}E(w)NnDZThH=!w`Y*pz*`TQsJOU|WW$X; zJ6)FSvYo4b;m`fNIrkqT&z7dCF#-UReEH5C_h^JmyPb5|HHhV1hAJUS}LRIG;FX5 z>656&?1}^j>UZ_JJ_!8QiHzIi03`V)#TD2v+vx^)iZO~$T;&8n-?{U%J!uFuS!tJk zSGo^(M>EdF9I0M8!VIH&z$f}YGa=cXn^)AvPh;M=?X%*9L^Dkg_Y&$07??QDexVq0 z-%4^_6hrb-bDFIDRq!#O)zA@R1TzZWx@+XX@e@2-~Sw7Nue2EnGH84PGX4YegDtFo6%^*4Wq<|i+UOa4=EhKyv%xqC}Z{kJ>eo8vBxxBhWzFn>SOO0*$xo#Kmqr{UR_Bu_RaZrDrFB0g^MmFx~3xoy?Tv$ z3~{VVBD4S?P^o+(E$*#n{Qmq1_UWdi`X^-#FIP-KuR*RXa26A zKO2{vfP8Jqn-^ksR;~n2*qm*25Lk!7@)L>xlUa*%Xd1~cd6}--iDWc3Zc1J;chEiU zT_Zk-i+4MWlbk59Cs))<2`A0T{x~ykW}l0=!FsoL$+H~<-=DnD3671OhNE}!DC>h7 zkP8R}j&^UnT(TsuKyGBcaY+L$3fxm|R*`w)ahKEg67`SrQQH7aP&>h8t{@ZTh2>fk z^6@D6L{Cq~c1I+PqMoIY#rFdgt!EnwRpNM*64UZH`phKRg>{TQ%7;)5L|_pK;j>>Z z9hON4b}8b=I-pI8jIw%)mY=Ols(1OO~Xd3n?UJp;X?H z67IL2oa<@y{Hp;MP4+xo>Bu;3pYfZL$&GVrH@ZAs>Bk3bBIPD2@v4;*WpA*pv z3q<=zw(Eto-0)+@e7Qn#`N zW{ryfmgapJsso`?nqNbm{`6!}LM9xOyST{byDowP=@sTBauY)uu|4QK+7olrofue% zG3Ca3^%Y-`=84WuTv41YBs4OQ+XJRPfEx zq_c~l2l{ipJuBtyu`8qQh*a0qet8%YNdZKO2KWvRN z{ImK0a#!xnKg?T{!H%tT(!&z+t=bvwEF)I)zFwdYcrynk9@9MIdT>7{b&xf;GW)+; zScW~(s2k@Y8nXA6n|T6a&+P-c&~;h7Fqb3LWE~CoMr+21T0djY6cL0Bo&EF3N`>t& zSHQ?Hpj!xnsJhF+QArA+<%Ytq1suZhS(NRQr&XxWrr$nmj_#6UF*&hfK*KZ#ItsUsR9&f1!xkg08P!S zv@1YU`tz?-0XjXMa5p!dxr{KGB5>I>$ z->$s(bi($Y6x&j=*ZjDJ2`~R{R*6|(#l$=)Vawh(#(*L<>JNW$d^F>hrZ*GnZ61sy z*Y%ez50kwvK4X42HIBKa_Zrvp&^Rv^BC5&U&{>azA;|*jgjWsFNmDH|f5KEZ7axrf z`bPYf7biquh_^j73uTAxBO27@2jxKcVeINOE=d^e{Hog3n-84!M>E7~$KC0xLDw`z zcBx{eF0oP>DQ=?5nJOT#`bI4}wexxT$QeLpc^+Z=4Da$AO%WsQ=9B+>2==&$V}1Ib zGXWL?0XfFg91J|y2vGSX00{*{PhICkS$g{@p*n^BV-uByu5(F&4O3gXs9zduCF^EZ z?=mv`PT6x}3Z(Zu%?mT)+YQm03`XPtE~rtC)vW9}Ln|vrH>fU!C4XyJbw#eYcWk>` zNX;>di#`pJnJ3OLRd-C1%>f52p7x1ljnNDoC~*Md_B8IUv0rOmr#0sz~JN zoxjiJL$$-VF_ZNP2AS`)Y5GYgfs^1S>P=v!`Z|<&9Ge0MblS>>gX2PAE9ZK(e*wj8 zm2!2g4|3?k(Ag)t6bfsWlZC=o@awfp34XmCw=-7j6$*9N<+5_5(beFuuA$SdL8Q8| zX-U)33T09T*%Etm#^xoe*x&G$0jV|Uz*oPj)NA{|fqFGo&gOC@_}gC{?(97z_pIPV z5ZCN~(Ff(xlwfOf>2i#DIU<$!tL~%rvzpJG<2Y#C(b(&cdY06L;AEpu8APd1Nf`^P zpN|t#HZ{M7r^bXmAbcn_;c7S1U2I~F%R9uzU0H~Jiilx~6KASv5~W6~C{6jLdFa*7 zYY&@DLnc}=n(Q!jSTApx(YqFLV?RC1LejH?)+Oe~pwx?!Sr(PFzmXilEi8`k8AA*d zX|A^iYI_v7N|5_-BGR0<(;I%XFK8K&jMyl>xUnUQvo5E8I_j!O^aJbyyYk+j7!zUEUN%l{`dlMG_?714>66zu!9C}TGk&k)wYg46-%!>T4~wD) zyV^Lg0JCm<*9+g)Fe|IAF_oM{Z!SQm5jhFN)_kp^vGPTDc~s-b#y4jCuPC+R0eAwb zhu22Uo{9|&jAG;>SDdo-Cp1c!U(a@rTK|4Sp`v89In(q&$tuoH!* ze)xeDbQYYv;H(628};Ei%{Ic?dcD{Iy+Mp`k(~_b(na#9EZIcQkpbIpqn5L|apO^fzXq z)|4we$c8qeiy<~DgLul^7D-shDL|*b5w%K6(&c%4Gmfulp=pg3@y~zBSQXdMsK17m z{KwFGLkM$dj@YU>V)5t_3UwYb#{g=jP-+BU6BB1nt|4h?jbA)Uvdb}h-J^->L46FU zW{!W{ZlRXkNJW_SwlhEf2*%sD<~6q`M=^?%$y2SkZp(sNicS|#r+YljO+4&E{3v*Ic*vIi59K>3vGMAw08yuyu6*#=#&A{RJ(n|S1}O;r zJ1}doWZ5|5(A;t&(2Un?Un*2SKz|*9ZGUMWTU$^3k2tU zgt#4i``w?v`{_FrA89TgcUw?>&PhJ803+DGEa9N-x4(Ac-f5~oU`caTWQSr*s0*Ppe=-6Hp#B8ps1*+yq-1g-G$Dje3*THhi~nV z@1(#%%-f#AZ8%=~v78@w(s-x2HW@)pvhf2k)ZFH-w&-NhE|g_si(t>Rd8HWi%Br`Z z&Nj=MA~kukzRtFJOJx8>=(`vFgoEzSl~7v@MI%{=!kf!TpP0VttV#LnT4is(S$AYi zAS7Eq>d~$h^7TlU$3>^v4EuA~qVxi1^3F`aU;f9R|2!07FSD^L zL3GYGQk5ZRQ?A9b39{XdW5g#R&P9B;R5sgRS!UATc#+$AeVQymj}&*&_h%x2ZY?s6 z&-<ZDFQVTFMg1f=QH-l|FhRt{un&WabX0D>loK#4p}Vj}sz0-5i* zt}SU+YD@@S^8&#e76_8Q%i350a}FCbWQ%Y$^~Fc%FBVT*A+~1@}M)q}?MNf4*nbrY5W^eJEq0S|{4N^lMg9$1$JT z)#h!Oqu;f>$tEB7Npl9m{B(Q`J=Li^>F%EZ$A$N~rMEDj@wPhCeui8C1Zm7Rj?0>F zljHeKtEU2iQzYo5RTFHPDA+Q|r&PepuV84T$Mu8~o-cR2n4Yu6p=I>^9TRCkzgL33 zJ}HB)|CUbAla`eAE{>y-Wz!ilcTxwPJuv&CmkmL2ycN8(6#5mCfEAWb1KV5fo3SdDQ1d{%|0R!^Fn>qaOY<;wR#_xE(GifP3GJ z=oLRJ>_xZS?8SXiUeLC%*JO83N2cVfRW3iUSfH0XqP2rA@FdTRn?4dl9@PUg-M~BE z)N2=f&SyttDjoFBR}^CLmy!C_`l78qYfGzV!oz$NDm}(GdD1RD#`Nc3r}TJ{od5*B z{A5gcpp@1$7~TmRE=ihsuODhkUn`+X{hNfDo@xrUM&rFMyjGe=z|xkVsOg1qPi52M z;apd9f@Jwp%?YRBu==Q(%vly3i!I}_r=(;J@BrhI0+t}+7_HP%Pl_~^(eAx{5Tor` zLx>ZSbu)&TDJfas#IYDQ%4p#pVh@Cyp@Wm+x2^}vSq-I_=_u|2Fz$4WM#Xu+Nk4aA zHiL(Y@J>z12CiD3kwvIZPhe2KL43oWJ6)N+`(7B?m~3nuXVE0$?GR&c#M7_VC<-L@Osr1O^?&4&We$iZ;$oPuP zd^mxF42!ZFZVvl%C>Y8noW~wh7EimSYpYPo0D@u~ndiO|eY|aIR=RYcyw-bTi~!7} z`KEiiO|5CzrKbjc2b!kd#DfntZSuE&P&DdvOl9OQ?cW8*(2SGrf3TH1)(|Cio&bqn{PY=~c9iz!~^yDQ@7>x#CqZsulZ{?oqaXcF4+@nWi4h+9QV@9i;aMBI9 zPQPX`3bzx+S3ntBc?m&?7^7kd1P5=8ozT35{N5N9!HjHNCt(&!3)ju$gz|8p^6QwX zD@SXFQc(yzl8jN%yylFw02c=jO_K98D$Nnk6K_gmnNk_KfR1&N^#S;dL-ZWPuXaUp zp((lZN;6BqNiNt0kRO6c&T9n45)UvTDrrQU2)^9ab0o*>#jui|X_-VbOX#NS*DB3= zGk7Ss^OE8Vg0lYZtE8k78l>!bw+0~Q~CPo?_o4%gAt(GLYu`@EhJ+EP_CCS)6 zNB@(@HP%mx@sn3Mb@*@{YO|=Rs)Q(2R|NO|rU~U!Sb#x_5%cW>x5G>BK&tF}-R|D3 zLroE}e%gg{p0R@d!Ip>>1T51m?T;ZtX#9yD66e8lElRIr2XW*V0vr{r=vzm;BEK+k z+{d~>DK;TW#Xw&8FxwrZVuYj%%}kt-?C^MeMJFR}YkTtSj(FG}0iJ+KRX_};mfdj< z*cRp>otKm;gd(Qd);2K3erBOryYtZWU0dXg;2Tu7Q~KL2+J;h0H{U-D9?W^Y-?O}! zd7K4)w%E;9fZ1smjMN5tvUbs#+7w(asJ@|qM#kIPEI@-nFr5$;jjJq_ho~MK76YU7c^z# zyIODGk<4({@Me>u2j(CTZjZa&lwl@Zg<>QC8gobQhq-;K0-wNP!2^fdL`WM ze9mmjk7yZQZ)XdDGN8ZqnM9P4`t3u@9~d1%;-tFS(@eRWMQ2sTjQWVZ-!uO!rKZMyQ7#5sg|Um)XHJ zh}C=PhoL?;4-j4ZuOI2EVdw&q;?@nR6ezNm;xu(@Y!=GhqRz-FQqC5!^zvl{1W2;M6bq(YvCte9TTD59W!mWgjVF6lF2}2%!OucjipuD#q zIN6Vg+oLA;x7_d994)sy9m0zD)vsC*Tv4Xx9wwB?z0`pAg`LJsmV7B9t?PxsO`Ydp*yJ%%<8obOc&4Ui1}m=Ac)$-o>m#y(22?zo-FH-^~gq9nhj zDVraOq&yxU&F{I?PVLz7?Jxr#Jn?gkX{dd0Irw_9V5iSZ23Z5;F#}>xa;Z9=j?E;E zlRn}Hf<-;V9EHL9m|4kio&LyJ^J)tDNsN>DzE6sOy860{YzezEVQz$jP)D6(& zkA9|)rE-97>6Nlhn>$rD_A(nimTY9!CzuN?{xs`@TkMR=`z>b++^9sYQ5grty_bV; zL@J7g(Q#-Av^_$?nDe2H6n4EWV&at_Xe%cJiZm_&% zr{JJj!ch^X;rqdS0XA<26kp>A1E_RLiOnu*`htOB@jYS88;_EkKG$*Ee(9euS=vj5 z1*g;pvL4jQ&5o>EPFJNUGmlcPsm1^G_grnHTiz>;P@glf_=RYMJP1{D z&OzSDr=m+$hI0ykEJdcp(SzShs)5PNVGn5e)N5A$Dt~2)Sa}sXxavHHThe&5#Q z&XZ8OGc~_%r#n;n^REaV4sH2|56V}6j_vG(EVp_Aal~E#iEJ$guYamVtK>Nt%IWgY z>4e_yBoDD7e)Hoar*x8W97jFmwgVWuTlPHmlE4rZZ*$4to+v3mt2VvDMRVEH(4+|} zdtYEDC5AikGvEyFmm1?xhIhdz`9;NlJ;~6cNTX&?laC-n8rTNub^V}*>m~v zguGPwbLFRIK=`*dgA=EhlMtB`aHa($OJ75^9oOQoHPIFufpKuCSu&Z9<0ww9VJy<; zb|}K^@}}=FSy7f=!yJKue7PaPN7A@#j<<4Wxe>1-eRMnG<8j!_@$06JHLH^EN1_cd z9udV+w=D3qM&ZvaXM~)*b7CTzkvHr)$t$*su7hzU&AhV{oDRPqCO+i8hmK8z9Eks6eY|Z1Z`t5;_svxZ00Ckqfif(LJu6qi3_`#A6?iXUz>aD5XP?_)_6LMcG?|_$|x2H4B(-40D{W&^T56`Qq zA7k>&s!X%<90_6Uw}0qxy0s6TwZ99q#x4~1w_7($CvhK38Qg{GvASR3!Fj&(?_^C6Od|@4Q@WO(gZ*@ z=`$073_u=V(Pic-X*TC!xvn3jY7?E^GL+T~K@aP}_jC!QZCZW>_q3)-e3Lza8hhG} z<|pXWjS^bL{luG-mbD44;=}rau@j)>X>&ue%hF#?`hsF;pnSLr*%-7Z7>_o)5@#wl zCTiQ5iaW?SK62X$-ULh1rnzuKmTF)ciO)2Y_%s8Mw-cWk{rSysp2>Z#hngf5Pk}uz z^&PSx>X!)*p&God*#)_3z57wx$6#HM7oAMLqH{t~O4PZj* zgHn<>8y=_TMCAAE8z1rD@p18nRiCX?^YKyDnkj<<=B0#~_hdwSzBEnB($c0i=#qsD zK!mLrZc9$QFR@iIHi`M6oig9OYA5$td0D3$Hi`oXn|`blKiOib)}0w{iB=8mzws=K z(oT%Cuo-DYmqfzk_k4JZq}}eS8Bj`wc}e{@T-4s*qOod!Sbx^6SUm0ngGsxTt6;zU zCAU4T`@@za^TwvHPV=-x)(k*~dzwI<7WSZN4w6y0b{$#S$mC3RX6c!O_i7GG-l=F8 zi;^+rmZz8FCS92Chy}?}bK5&jP3RZ8)1Gw^&SM@PbP?qYz zU4hj=8k=a5ju=ixpw|wc5y{d`<9Hmr%|+*=m1`i5_q52Kyi)k2^vx94(n0h@b_W4? z&}22l09QWFqh+axUZGEZGmwMs)m+$2tLTu*E8L*_E@USlq~Q?52rvz@ED4 zX&{iSElsEQf#h{PE^~pdRA?ka>|BDVB{qd5kE1>}=^PHq>UP@wd%;4< zbT}}^I!lBSgRwlmC@E~8M`87b8M3F71S8acv9hQL&jGR9e;DA%EqA-^PJNg(K)!p% zhv`D*()b!;3_meso}%Fe?wMu|vA!(bP16gkB#`m1G8c>iyD@1NdpoFZu=L&_H}mxN zi38hb1r!!;1O-msMqC;<&V9u9y)N;zj81PB^-Wz#Tlfh3t_f_9(dq4RZ&u0I!)Pwg zRQWbz_DDh?yPEqz8_V^UMHi5W-<1lDne7GPO_>K+8QU)><(Q)A1Jmtt@ubBy*ss^$ z(E3L(uup{Y~y3RN;?-3zAczxp%wE3;H}=_PLLU^Pn$(PI1 zv|m+P9xB!aVwa3VE$xFqGo53-mi}^Ru6cG^V&r5p9Y6Uz;owQF0A4_$zt2s;`f$?( zNuiuILkVNF(w2L&vL3v-ZdA4G(4?!6dIo&diJfnrYK%C6gL}db9nj6_owiXpL+#ZJ zPbn_HVlH!n$(yt2!aaSZbXMn0E=BgmjO?2oG!0_w2_h!xVHBYFw=3I}F8;p=v!}P)HboltNwwleT-Z)t%jy%_x(&e=+UfFSG@eG|`IKfU5PiHuEdxyPJUP7 zKaZsuU#_+E0g~q9P%Yhf)mMzTw7Br2m;mQ~Q7-2X3seWq|4Lu#(jzvD^Jy4b{;iCF zqtu#jA0D_<*dCy}UA<6^a0X6P7f!@=fNOa%5ojpq7dNSyr2~`97ZCDNZVQNe&`jn5 zLG5vVH93LbN~o{Koh*%J!gw+X^PbseLZ73j#dbr|TCIHZ!*<8b_Ga21EF}(g8veQm zD!u8(ds9xPUUo;<$1&{#at%#bfM%)>M7=#y5zAk61lIAF5(f~E`YVZv{ ziP=~aCDYP4qxAv1NU!H{t(qvf594(Ah)R-coLC>h=(0z$u3Ellg8+g1pzLlW(MoKy`~X6TZm+FPj5=%abtDsv9SJ*i~4Yqj4`F zQWHq_NI>NQ?cThr+R&#CadO#3gSU@Ip@XxU9AGZ;3CQmI}|YBP>5RJcGA;v(@I6u0WtW4ySYz1@OR<4JZco;F zxjk+cDg%e7Wdw{%9LL5nx=F7BqJWkIu`2*t=C@Fxfc&-OeW{RNanl&O)Xfm<8?Iz1 znOiN5C)Yj(jXbt7$y!{H@D0*`_zW1*60` zFpS5O^d_BLKC7O(3O!k)`D9x3Kw#$GR?}?MmArczs@>^yZ#r5MN(LLkDX62(tNY<@!L{A}=jU9K0y3DC;k zvkV`h!)JC~e)D(vyqWPc$&T<`&5nodzS@2y#{2b<0LxM#-*0wPT~GWv7M4ql$j8~v(s7-)AhZLbvy3er3j$Y(YU`9Nk!N{N3n)Th{7J`E zINAx-wLa8zb7BP;kEYZSam8`Sc4pfmTp_i_Jd`Xnzb@Lz!ZH2%*GU$(?bze?k=2gD z1nt)Jn$jDO!n%e$$8xX)gDsICZx&vSDy-7TjI`OvW4RzZH`}y{@K>5Un=M2)!|f~f znrMC0(V+Zfe6Y=oG&=X&{U?G}5Y}Mj9cX za}@1Y@us)@O(LbDUAx#GZIQ`OM~QR+bPy?1LPhouG8A-B?eb4BB z{sjlEoUiaejnR2{JOC}UsSiKT*2nU87%d4Cv{;urrkTP5-R^FMp$14U<98egD()C$ za?l-gBkn)5Z(|8p=i&h@-B88j={Sn7f@<+$yR`LfwkFn>Kz#32u~i>eAE7p<&y^yE zJao_&Rr^`%57l?M1!kW>gU0!f^KLA5iS(k(3u(3RZQkRDwQMwohc?<-^?c#CWKtYJ zz7&r1>3%pFCs903ugHnZ+88D&zd4Hx6Wy6^&{6~XT{$m#Kkosc945)++S^L_rtD|3 z2UTtu>{_#Z1CoaqpHPz-r9(DcA@S#b1>JFBl4rp@*r4Tc4gLyQ`gHype{dY}C|+>} z@dg^sBbh;TX1Jd-F*+qKbx&|>zqRgVV#~_{coF5DdgwTv;4yGQjr}U^M=?l;l?1v;Y_$igmQ_5epy`@KwgRA@cbmaWEz{( z#K#_A-!u81*T^L0&FFAB26 zR@%ZsxZN5FPDE5#7<@^xq90T^MT6swfMT^d0k;BX)$9!?qP4)@s0VwqVp!gT`Mqdq zANMRYV%rR&q=>->Le`E_A$&z{Fj@{?9@r(@+lTO?rOFHce${np?l7k+EUD#+3E%=E z4K;~<;YB8w%MCfDj%0FqH749YMIga1haNO)^^5wjtk=xt<@<@eYHra&2oLAMRa%m8 z(eP_pMky^UUP(BzSh{KHE6$kUf^N&Rj@7u5+i~!_C&AD7CLML*I~O$V9Vx$bM_%u& zUe9Nbyy)-n6K)p!H@dr&W)zkqU3@7hevwnVg_lojlE!L#q>5JUGp0(wAW_#urQ;xVA{HN;sH~avft9L7rDw+G!R0GzO;b?E0K|}@ z?;i#)-qo8I`%1aeS1h^2p1Xcs8YHOcwib|42$9Y}n>7oP4W57X{LS#z7NlLu<6{lR zcqAQZ8PIwxxhodyL)a$b zv@m5PueB8kq?y6GGdHa_l;?U-jWo0L&{d6LTXT|}-hy6(?>c+s+7fgzW( zgA{S=6qzs$FR@4~?XRaiI!EkAWU8}_-10&AxP9mgL(1}`NAV+Y z%4Sz-6Q5mVBv!cXn!v1>gfI)vOGCjZ=%xMn$QDp}mvFp_1tdY)m(&74-u&LK!=JfE zHQUu(?53sj+^nFyApY+wT5;d9S$!@@Ot(TO(^TcxNKPJG1uOpN+5{aBviKJ+aZx#Z z3|@rGlC?OTZvC$``{U3KUT{zM&PU|<>Ly^>BE{j?G%8E(#H@I1mU3B6>m{R9pT6(( z&vue*wgslj4L}`u`#HT8u_Ii0!~djpC54r@0(Xw#Ui);PW}$5d|5J~@29AAfj)qQY zs<_D~?b1|1fBtn#Q~o|Qr<2=M@aQ2L;3b1+8&}gp+CdIxF4}C^XN^-6tD5)i zUt?j<0?VEKkaz+!ennM^P8;K{dk)K*vr9FcZ*T_H1Bg<3FmyI}!6WV8K`=0L+`6ew0lttYFg~9bm@IZ2R0rx3m2Ed_PusxjA;0 zIf=%apWkxbkwOi73{CT}R64JDiagZ23DjMscF9H-Zt_w-j>L3LKZrck{&K*BcN86j zUVxl#TNGEDe$m{DF`mY?V*hHbjqsV5o}Od(jY=i1#Q9baC}C^{=JinD9r9%nrs9>kB9#R;xQ#{t5AJQo8t_VW4)6(AA_?7It!^QM?2{wBsuqUSz z)uHE~pWiBV6FK+K|8LQ?M)dw1-KG2I6hQB{8Z}oYY<%RPMT)6O+{aeuNvD{a#Fx_o zUS?d)Nrik_yR;ewQf3|%evpztQf5gJz4I)Dq|BNH3FADL7~%yr3^$Wz6~}auxyzXH z=)KyLqElHjS}b>WGv0X*gMY&VFkU{Dy6+`Pvokrfs87<&&zVKZm~iu_b7p_1XJv?- z=FGr`9Lkv`<32}FfW9{=MpsmM&#ke2bv!J#A2%#m)lThCgMz!h&9o=>s=dv1!(x{& zTe}>sfaNE&Fux>wNp}aq)xoP*e?oEgPv(<*@xm#1zO%_2ymt=aK|&dqipXP?Sjx%%zc231 z6TeV7d$es9Q~r{1tq68({^ui}X78z7lIUqqX8$%eejHyx>wnhIaURY?AjNGQv$L)u z=n5Otv1<5)Ug{7>gYVN|9&iTJ)HS>vX094GMD8WYn-R}fy)m|+25A8zLMtHo`jvU| zm6U&Z-@#d;?J5;P$aPbzbHsG6vURi&?FxY$8c2q3gw*z5$p%4E8R_kr_`%DzuO|2B z4eg4A)Vs#sv%9G%jj$^$rFzF}mc>YF!`jteYJQ-;UribQ$nTOJ(6RmbQ9>LKtkxWR z5DmqbM}DE;CCZZYmuMyS*W>4MZ}!#p9PUfeJhq^hWqBx~5tEUOeHYxzGqcO4$&Cnv zax)KUC9|l9bTJ)KE5Q{EL<+HzzCnmEg|2idqKJu_r1E3leZ(TEwuxPi$I_i8AQ&^Q z#F)BrO9!uDR1awcC!>NgAo&&0J)MuarB#M|6S~-?Q1q3s{=Ikj8CkOVw1)-kCM%+l z>g^Rvmii`W#m?SskDydwv94~091^x+3mSy~s|WZsfNw_;H86#yt_L`KW*4c-Bz0_=u%qoP2<7F&j7ItGqI^75L!aCwlnOFV z+$(Na5ntQuPp^dX?0Aqf%IXN={*-c#H04}`x*;Yv`LvyKp3tA)OobtJqMF7iDlW_H@}ty zDp;p2TS>U%hMLZjJbKqG8-m^2J1bX%*WWGH8}0H;;v%NY_s=uqfjQ5-E0<)3D-I0J ze-ffz7++H$keA+Yo_Wb_ypBF@f1rbnX-;e@iJc|RMr}EaVy-!V988$Q!8sCqwDpb; z#>Jot(25)9OHs8;%~2wQeBHz2EX26m!tl)$9V}=+raAVOgJf%Tc5H8RJk7^9lWyAn zX`9pPvf$PPvouCY6u92ST^q(CmDCgzF)*~Y-lk{=tP~K-sb9(YvyCr5nb=-H2pqgB zio?vw8{^E<`TYt0E$5}-4DxW5&Cvo?OwxzjkWNZ9CW#8mVG{%dU1D@fUpn(e8Y+#& zS(i7Wm);T{j(hUn&E_8*p%H#z+`E$g*O!Y8xAFeq1WXZJw)(#p!q3OFqr2H$T@iD| zqQ_y^r3*15)1^N8k|B^V4&b`U` z>LZ!fZ9s0k^arwZX3v=*cH#Ia!wVouf@vpnCa63urk}%n<;vBeST%+ML|F?SvZ_(m zL-nIRlp4k|BF0=sfY2$@P4g0+H-M1N){hmJU14geMul9F-ik8XFn9I3`9PcP<}UbU zZ^k$Wot{_AYp>I84m!J>^*(xPof;QlYZqvHk|HQ;2Kk7#BEh${lY=nWJdtGPaC)y@ z9}}lozvQ*YeW-Dsc3snDlPNO6E4v$wBE z^Ux%?<^2o>#W!#04W7}!(2Ko!L+_$lf!kq9`XReg*mLeQD>_1d4fiWLl+Nge#bn|* zp2{0uk@*nWZ(9fB{3E!m-@Fkf7@`)&0 zn*Vgj5yaw)1lb@Y=wEP?^=D*YKhg5=h`y}IRV0gkq4siN2ChCR?vAQFhlA)dE|m|z zuZkB>CxN|@-&Im_NHAH=ER3%N;9#4 zHsn@KDp6sT+=-?v@U_iP#@f|j9y__o;&FaOXF6Wg6C}@)KE>(0+gOyP=UH){CXZul zw-GzIIh32jUQ63z38S{n&+>7Y%alpulsIqYc->kptBTW|^ubL9BxlFxBeW~tp4*#S z?tirPvXSB1ntgLe?4+Wzu;sLJqIhhcAuGC~drz0=Wd{6IV;8X7>j1qj)p+Uvef<1X z{CFa>Xbwj(P=6M^WE!sb-J@?|od_~zHqn$2O~B4nEhMU)Z`?xH77Dhobm6@?>9irOpUJ>CzqdxF0;e;p>n!zwVDf(m8vY*g0Gyr zan@be{e~{<6RkcmblcrW+T)w2vX4c$Akoe~()BLLJa#h^{YD_bv3TtWG`J;2H#d~v zumq?pkd}HyyLg(%C+H_1*OW&id6pKL@yhxq7X5`FG+L=1s2>j?T9xNqv5U^Er<|U5 z!j+RKp7iR_Q^*tE2*|#XI+rF2A@kKyu{6 zM0n-scAfO6QpH+uP9wU;@ZtqxI(9fxMgZ9e*9Fwsp*>CFKAqhGa2>Ii}5DO$J54Ql=ten z@h@A9QFt}>T#Qj;HQpp+I#hbE#__brCJwE}qI*1FyBf^}M1ZDwxZe6*gFdTvluXCr zFzw~$(YU_Tn0T$PIm75K6m(dfV@~nnnP$kIc{ZWM!d5!V<_O;;0V0`RM^HNIa%c4t zPxVKbR9xQDieoC%=;3_M_Y@_qC5`u^U@v9`*o%dmQxcIFPTX-Mm%S)v77&hzvtIwEA zvQZUTDebqlm(ls8TcldaZ;w^Ecbl&ZUxTumA9E5D}P@VfjyaXJrzMdhVm zQ|6R!*(Aiq5M-c@YuEQU$JCge+EtWG@s^{D%E_Et=_4l@SkYPDRqwupQ_xdnuxU@O z@HIlOs~yx5v0(lp#U)n|ydbBhX7ySwFPSd_)I-_OB*_z!U%WAv=%teE zuPna-F()GA!7gE?1`$S;>d1tUwVX7P^bwN@)C>$jEBEe$Qt&PZBW^+V3UE=UbS^Fb z)^uf);>$7=*JWtKC%ivBRZ(AY6-PV9Kf0$WN^r_iP-u zQmgc{(Z76Z^>Mau$!q%jc2hC*O$9Ad=~4! zY|TS_-~}#FMG_`)7=9-3Y!U(fUT6p2dv~{;7E}$SXP67Upf-WK)_XH_CFuyTtgE3B7xaYP+s8;^=}u5~j9Tt+s+XbGt-2mfx( zQ$s(TY1cGB0&m;A1m?Uj` zL>QqV$g(T+e8ZN-g1os*`4m<~Kjz^#Lj?xa5%EfwrC++_!NvcL&VwGT%K(f+@ZVC7hf~I7`ki*$Ph8@ZKW_t`kUh1N&(`^r7fM zGkUHI!HXCgItKTV@2TJ45AUX=Bzoy$nRNL@l7O7lwObgy=Z{qHA!)d|dCoT0q64}e zpMYAZY%E<6D@szT&G(mU^yy*_Y`CQ8lAZoCLqKcn$sw5LAy$jiL%@BSi@o-6_c?{= z?G?tKYEKYn4O5?ua<3@H9;FGscH8$%)ALooC#@Mjo$)T_GqaP{b~O3tlrYEdogUYC z6dq5mMB*sm@00>FqjDlU8D`f-%&Ff1ZCjqlPOUK=Cp}=Afwh;8yDi_n)}9?6^RC9A zfK_3Qvy*?b)B$pOWup^030!~^ocx`b%yC+Dp|=;%Uf}}F@6VW*PCbrkAvemLne@o; zkd$(#;#_yeHSmhmP{5UPtz$*zYBjr&B;fR=Hv*DSc(PJ+3Oqoxvk=^+G60Mt&-0JPM%P#?+eGPQ}FZ=U*ok z(^$-dd->ltO>QPy$E$(-?(LS!g>M6k_)Ll>@;lBmmaJUafnTjbzudmMD@Jn{J6LAo=Zv z36_bQvfMAUa)UHnX{{g4-s`I5HG{;nwTn(JttJWw^1m>6QWT=$eMcE>r2njLQ#>t_ z=m{>X6J_n=JLbe_$%=Tb6^_=48U z+UBDmm*&&fS07F24~cteHw0v6;%syUG(eTFZ{D@f(UoWut7{i?Fn1<7?WBm!{LJ`SCa=q8XP%cmnaPko4@u3Dgnpa9OuAc%L>BGkKnoV2WBq>4T^qksr7yCzc z1h&vYC4pM=(0ZJJNYk9T5fk2`En>Go->5I8Ip0zD1>XVyZA2kf0I&6Ec#)&ZOsx0} z^)7yI4yw4w`X5w{p=CAfI1|CdpJ~?B0DNZCg$u*3?u9icpk$q4we+dke& z#;6euq9Y(u&#p8pY3(bZif<{My%>op#5iW?*gfur#$k`q_gtWsIv<_`)!vpfe5jZe z6s`5Yc4)u#>Cf*u?-h?^yB*J&`*xTwS-0doTCe#espWvlm{5w+WdZR5k>&z@;Bg{p`gP@}DR z=htLlWVhcA=aH{9!I2U9){b{`NiEz55HWwYXNOt~QCN!Y*`efyl?|qpvOe@sD91>$ zb#ys-NEDa?$_HN&iiFp-a2uE-X=59F@oN*nsl}GWPdW-ShXepmZZ7mx@?{OOv|IS- zHvs8*D#xv*D_}o+jO9)_!B~ zrTyOC7#u!7ma~dzol(aYz8HU1wQSt!=5Naq9Li!RH{-OO z#ZKtYZ<58PI#K>*bs|az`(>g9{dR6#hY#W_WS19;iX_ZAd4~X`O$&TCJrI+Kgo`w?&zB z2iOD1vq|@NY+R+BDEa#`k$8IJyz7cSCBxkEiRO+^^jkNLT_(|+X`Ef20xg2&ziF3p z_UY=M$0Yjg7TkZdtyxo;&qw^226z5}vNNxLEQQo{S30pQ6Hre>VM{gmnoOC<7!If7 z>5U1~ui$HeFw?POXkRh8eGfDfS+xmI>p2)xwRx7CEtX*#*p_ zL-Sj83~KUmZOW*OTw5iTY?$OH&Z(1WfOoqakMlN0?DXgrfweI13P$!iTfG-2{h|LT z#jo&op_kR01VtmO!H@W=XgqKUdRH#Z1u4Z7=VqA933bJy<=cBn&`B3@&`V@Eo_-;w zuDwv~Sdp|cOxDt)INmHNE9wAc@d-@bH+J=xLozdo@dPgKJ$L=Xjqys8$&;5k#q`S$ zo=m)|>R0@ux`f)`W)j->a6Va>C8M|sv8tAf6q@BcYi5+mVmaQMC19F$uBJ&IQ5D52#=4VTuT{2vt}0yR;~Kl6JCw@3IKNwwmT6-=3@sbakl2(wO5=aw_+i8c!@nI;R&r0O#;Vgv^~M9DD=#m?UyCQGNTBePc9z#KeYElOh~I3C``i72p6Fsk0t()= z1jgeYQh&BYiw1AvW*kn1DF&H-`v3J1W#4zi@P9-l*my=fI#)RqF=X?o6l`us8Ub@O z4Dq06q`xC_Pnu2n*xK#7g#IA?&T$@n9(>I?K-Ts2lg^YZB<=_5jj9jGZPO5=@}8Km z)7dEF!f7XoiaE{L13d6~ch#Jsz9TD_0v$vPl-a?9&^E-gGHvJkyzQ z*H#4kba-*x(0>ox)t%BllI=3@s~UEpY+=7<)3lrN?$fLlp6BRXLg0yfK$MN+aB%qz z_FQq+xWaRa;p#!5OFefWMWYWnXpX z0h}}&elfES!`NFdoOcU`PusmSFZ_15-5x&H+>H79NAr)@5Ez#qLnzK}a2XOA5j5^n zMX@AIG-YosKDTOtqOzcdM{wiO9sIo)Pm&%+ zn>3->mQKXM`xE(X+Yg-lO-AG1uEL%!Vb=Bs&ezd)(k9UE#V+u2*Cy_g&HL8u6LVk# zK*9bw$+N8wBL1QDm46!&yQ28tsxUu#OJ47l=43ygZ8-5!q@70Yrw@(QZuWD1AP8d4&%|xx9>8;oDwy*U=s8z(6b{T6 zF->yx-NmHW*8d62^t&{q9AYD~c0%}43eaXH>*V*tWSmo9@HO>%k)ymFn$;Z3HTqUT zKig|ejH4cUjtf{HxJTob#M*UInHSaA+` ziCYtv@?Cw{&5YRq;yEoKs15E^J&-`DjyyA^zDf#t-eEW}8&4zjvwMCYp z>5SvBGd7jtMOoQ}V2Xp)mi8gsj+Yxy1=Za5vM)(R1}Nnt@sguy5wJ1oJ4RU_#oVvB z#QbJ3H$y8D^1@w5X*ML7r)jKT>bdBsTD*)jIA&`vVx*O3ua%h5Zuq6XYGvN=7HGr8 zFiNJ=(M{i{;OY;(8u!AWV8iz#Z(z4Sh`tCAslIQq@{rsfPgBXM^I_#5va;x`g2?4* zn<=#;{K>9a>pYU;$u)8&zVm2I|6|E`plZAQAn-BOLkc*JSvt8QQRZAmiG|}d&}EJw zL7c=i?mCGT%)2-Z=twdjj97c1=`K2FVd+}~?%k%hyU@6v8u}<3cN6WshTaVLaigOT z@7;|*L%xFJe`VYF=J@cAGFZqf1slB^W1n}&9^78*?FPMD(M6-Z6h&2+L6cmYHg`#b z^*@aDdHLzfPjhm)>>b4x?Xv+QP^ zx07cX{rQcOXW{Vkx-8lfX@3aK(IUY5opU`!`bTsrM>bg&uq3^tF5Sm#;KyKj?^# zbCK|Q{k?i$W)Na@Rnw~?{j!vj5YWKzlj#}m;&Il4&Z(inMuiz43J_4Vlh z)x-AZ@)=c}v!q|xGuTtMHR}X{=4s5#zOuOk#9AuqX7oZi)=IohF-81?%syRH^RYdIw5Pc@M+@)piYI-Fr@=WgaTld7=pXjX zN=Ay>3>;eV6Ld`BrT)fY*n&jtjZ@2Wd|zV4!)M|LMUI8xMGO?2VV@ok9BY_h@t?<9 zr3#X+RKaOK!5`$R;US!F$~wU<&=};$?Op){>)Gcm_@>^POB^5Vz_S2wHaYDsBx$#H z^Ym=HkYgg(+e&Wb3;`p(*;`cNcy_g5A>>)u~xIiXKke{wRg9))Iv!S zC&GIxq_?*W|13Yv%f|!oF(AMYFL-YjKDjRMxn6!yFq$CE*h-29NYSHT;MBC6t91L_ zpAIwH_HQ$LG*LRSnY!pQ9i53TlgVUqO-VwPeB(F-5$(j4+zVc>HnTNj49q7f6GU@K z!Hss;OW$}k*!z(uJA8~$Lqj8h2})^q)bf3rbeo?}Ps8ZMF_gxa?_tkLuF-)>a5c?5 z9ZXzft3qiuI$aead5->5VEyvwgv^FN2MhJearT6QN7+$z+rC*+t&m?{4PO7mZ;lUh zM<}T)(bnb5rVgQK2BX|Qoy-Sg$b;{YC8T2$sj%;DOqeSj zrLZ?m-#tj1kQPO5FJPRy%V`sm#(GGI`wGsM3{wnIBX+iESMX1bJ2$zatU)0{IuSMC zfIBxv{lGeLzD%#qo=ay&13@Fq9QDt`_KqN6;2a!TCWeFA`uy};k9!7lxfn)wagm*M zO;6#3h8gi}xYWNf*mx05A#0`4FDaHv>A)e7t^*K2XuL8?xa*Rsmn z6zpNPqqZC#U+SFrEDT|re%}0cd0@UcUJZ@kGOmooh+#oK@PfrvPyuLIGhwhCW-T!-APjDLO z}Y%_Zy9mk61cxCl)B7s#^ZMCGN(V+ zq%NmZ&PRrPziuw_QgqR2TwWuxvh3z&Ooq(eIcp#MfS9gi{A><8YQ(%FtSt{U|wS;Hs$MAnoO)3eB5Gf z?&5Kt_U~a~`03u;`X54U?{D%sBO@UqK<&10>9Q|&K`TfdNgt`KAml*Iwn;xUY2Ie zW4EM%NA?|E_?_J(b)1?1*b8L++tkFn*dT8_O=bLEJ+6)Yj^SMkaDd$eM)u&hW|d4v z{ZS%vL+U!IS10@#xWIoYKMF5PyH!`*j2b#<$IV(OoA{|^>hBVg!|RQtkf-}d3cptT zQpp{OZNPi3kM#nQt#}&iNL4aC1XO3v%-PyF+;_>Bj8=8(c_Qz5OpGlJZaPBpNVPmt z*?c5qKBs+K%)`0g-j@-BQ8R7aTaZJ;YOTG*N0P)R6CY7BnqCp;Thl;!pELOXcyuz)7$w;?d>f6KD1wX_+1N&qnFm6uM4@g6UR3z#oE?e8vyiud zB?o&}K3l0Q%b~;bXwRgI?R}I?5C&bTJDn_zML*7Vk34mn zss6Hgd<)(G`_M;UlFz7=U;9iTo^1N2g&*qxcG8 zT9-x6{(tSg?T_2Yu`m3cf5qT@i3H3zdVk3R7uf7sC%)LtF4oTV31GmNBT*7P5~(FA zk0&?J|NeAUbvK)DVpnT4yOQ_7CvnCju~^+*U2nf%4MvT(Sp`O*+B?>Hdxxj`JJxxB z#|Ha5Zd88)GJag&TQ1dHyYvmEnL;u(v((vp2l^FgNcYv%ZY;t{5f;a6V$(j8sg5k z`+YZqDQ4l9?r4O;CUFP>jfsqa{`QZ5iiP=G5CsX+vHyIy--ZiV`X9nY5T&pkad@3d zFG#`-vD4u^qdzqO(9-`tM3ANK&*%N_bTSrRBpl+-cQF(>pEO;=`sVe%^I$CZ7_kljPQdu{#`{FpuRKmo&H587w{8mC!|vj~6u77Sufc&q(8am1$}-ap?8)rsg% z$PH=3@6moH#BeOX8#g@0cgZfyWIl|Bo{$QMhuFSDax4TCiA zA_#FH!&a`uMxq{6|LtwJKbTGckb|5+IQmL?HE^ptQO((4F1kgQGBJq9b z{4Ap6oiK9B3JvB%>9WIS!O;pM-`OAkj=X^txdh?<$uB<`Uy;i3W zk0-zlT94E(U#EG1FDvnt< zlx-h}RDuVPH}->&j2aZ>HyMJH;YAj$lK8`^cVBq}O3&3T02IHJE0qM4HAi+@Pp%s6 zw;#fLtQ!P1$~SB*IFOjZ6ZSm|{N*dC>w#(eml8h?Si#o*c^YB|=k+04FcwHlfP}Bmp=MnQe*gfJwJ{AATdzDQh+HMhWV59wLQi zkw4o=aM4l%M^ciE<~mTBqdySQp(0PAGO4UV>92vsP#y;!1GR6XK*Lfzy_X<3fgK@< zBJ}ErMZFK9TSFSANX|LH^R^7=hvH_Hq7!rk0P<3Bbg;D;5I$*kc|UzH^q{UeoG}X% z5*Bixx&E;9wbP2Q8=6eIg-#sR0=EbfoN7Tx6ZG}OXD|$p((pdqhFNG_;Cg$8t9(nY zy`q~Uk4YhXtC|POBbSe4%3pkN)ox4Q5fnrz`$+lt1xV+~ zEq99uvOo^{T0^Rtwr!FF)!_XDe3-d@bI6Bu0B8v{@P#UKt5lN*CpFxMYO=#d@)Ah? z2EEp+(g|M~cIg#pCAaE0c3wTO;sNN*D4rmh%5SMrk1V^Yv|>%eup$rit~8N|424=W zG%lXDE^uFd8^Jc$+fhlghN_6n?ubwC=u7Y-7D3)FC7v4kDbTj7(@fOKi2S3_F)(+s zQFQH0CUt?+^hQL`rNC(le@>~mzL(K}ERG$s7z6{ya)8?4=+b4uUWE5h3+Rj6IaH3l zj%|X94fW_`D9`^xXMi7!i6GiMe0X=rIBu9+ zuxv>qDRt<9uYRkkhw^hRc zo1%Au4YX?V8jvI9`l(#yvix9JAa!}Dv!^|xN75M{&2q9dZdZmsS>7^)JB@Ng%QLl9+Q!~F8FFw#hC^1M^*8p6N?9tU?99fo%z zRD^v4FS32YDz=;yU2=pVIjY9yd^?(`Z!Ew76|T1<3%sgS^t0eni6x zq+J)0gF258*aS3C-dFC_TD$Y;Kgt&DW%xN<9`L=z2kzwzD=nb-unN;}sW3RgjzRHT zm>>=$WQ6=_>A!D^ni78#zI%}Q`G5@ktaiMx4UlFD$PRD(meBCGirdbkiFh(8LG1hT zdLvW_LM8kHVPgJ5`iAs!K0|uPrS@ckLR#iesa9f#<^iFhi-=%Je;9i#@gA4^YKpv^ z66pbl&Ajjw?E}nk3YEV|9)+~ELY=o}l84HR*jbJC!IK7Jp&ICQ`|PVOBJ4(-f7w^?m|J!`(S|&`85E$S*ogZUi;lqVbE1Id=!hk_d z?hQv%;^p<>R_(nq>Uzk3szTaOrxVnd;q`x!bNw@Vtn=*zVNO@?$5pq)OkxHjWHy{@i;UF7DvQ=_@I=Ihx^`FcaW~FYS}PA-vvHU>4DgC zj;_-6&)Q1i!?k=uYoQwRi`wx96C7!&Duh^JR8r)6F0Y+1X_zqli*g4%nXMx%gI{oS z@Dj*XkY@zNHX=ed*;}flMit@I1dK)o^79xoCGrT%t^n}nIu_{bZ^fl?5WX>7JRdrL7e0-+^`Olba|!aR zP)bjwFc;ecXS~WTc_KQDN6gevwdP8X^x0d15KZmB@&WE)0JFSwN=0k*UZuIYd!@ zw}yJJp^jjshr_{CxcVR-zO)MLgXOKX-MVgf$Y^G}(Xlk{T?{!t9VKPJ{eJl2Jf^Ei4)ZjA(>6670KP0(BWzmDFWCJ%Htm8kS zi+P}(M44S9{znr?J#~1}wfnvk{^ntKOVPAHuMBM4< z7z)d^mCHwI+&x@z!U;wn0Jn;Y3D<=J7?l~+cc_2#3b(Yp;X@f33GFSmez-%6i}@#V z%8eNA8x&8m;inh^9>cOJW}6)W>`~cU7jFT02&One)cD0haD516>h)IB4nG(Y33j>R~>xGliurMGEilhXH)A= zE-(rsup)kE@QS)|jDpQD*W$X62+dzaDI@HtAOb6Jo>_ML*>vo5D84e?6L~P`#Ah1} zmPmaQAVP=t+^H2<8-9`D@s*x-tIs@$=+QgWm@}1tE#1K6>xsA%9SfPOj+4T}$=6dw zPV%YchJUNaH}v6tBN(K*-fp{u(~qR_Dp!G^EiJ`rQ4nM#$9&Ky9HiJqO0y{?wpC2^ z7nK3F=XVS1AY0xk9W=Fd?@8H{$Qf`FHhHcI-Wyy|*|ewS%o+IDs`nU~YH zGx&4newWdl$aF9u{m{9Gbr|1N4`O8!} zUc|?`NQ}hOD0u`;2f2BdUxPIb5Ft#KQhchomk9U zLUuu{;G`*$poI&s%(f zIXV&g2<4?2wQWLdDv=*kG;E=EV3&BQeDbtq45|;-CDkyuyV!040`z<6Kg7vWM0tta zpPfR4i|!__mGGmq(+$z58Z6*ZGaoTDMDZaqAzl<7DjtoZTSp@FF|A$>!ao&VGrCOV zkMz-@oI7Tjy*%k&$+>?Zc_9maTTIAkbLI(0YM!sf(lGk6-dFizv125SfjpcZ^}t|d zO?VE%Ks{@PrlUej{^@aIwqE*;*3%eh#rlZaQu& zD1#rFjyKNB zI^Re5_%SrI>kYiD-AA`Z@hrd9H`1j*9AW5H0>)4czu6$0ZkVXv4ty?h6Xvpf!Uuch zC%;0wFa%n2LNQ~teQNR2^32ygkeuO|4%@%jDzz+YR-my6LkZx z*RbT0tsy49pjcKA^1_eo8Z=UlrH9FcHa)UGlhLoL0M!CMeSm?ra=LO=2QtSdlZ~Ev;oc`^Mz~z~T9q7Zm0yO#nKy*v z!$%lb7rgA@h%uf7lbJwY;&+DCWaiW}@)k$h*UO8uCgtl4vR5#aYf>+{&-A8&4)pP@ zCKxjfu=SM&Uam~citK>ZY~=VTaYhygHK_>F93?e+r9kCvegXBnDx)!gzZG%}l-rzQ z7-i-f%AH+n$~A20;xi^&$qJK6j`SDVhC8>0b$XCt7|uleRLC%VyAV;$UZouSnCbKU~4x``>A|=V_Ad}7D{0`@s*Ig(^4+%HY%}DB|+Gq!* zu3#!roS$m5JFq6^8f?mI0CJ4_eR;J~`BxsU06=Xps1b-TB~yv+8=V4Uv@fR`5Lj&rJlrOfSkf)*Yj4}g4UH`bJa5S%JC~skNAOo7(sq5NZxvFNm zX@AQ-?Ws+T&m3d*R6Y|^%X$ZwDAdgDDkP~(MwU$mW+P>;x@o&ku#^hG0X6OG96NS; zd#%PI#%3|u1Wv8~u7 z^jqb^ZSw9^;N4I}RV8q-a>`9i6W%fy@zF_&fRb@{2jeJopX?62Dt}8Rt#EfQW$wT% zo#w5~=4-2WQ?y8#<2SP?IF!exeyws2nP(YjpUQI z7%KodEhjP1BZ5MH# zq!N0hN)T13rA}W_Htp~F*iw9WOYI_5WF$}$o>PoXWgbjL)2q7uRP;a)>_IUFNI-RJ z*%_m+OvIMtNgFgXMp~?NNkF>;#J8&VNt-L&efN~FB{x*%sCxarh>KJ^Ipv>xG7_-k zMrb2;WC!!K)d#6(I`JKoToPRsTjD0FQzgf|C zC`D1&Lt+Xf@f(GJU|KM-A?11T@2VA0nom>*+D^(jP$ob+7AOqWnK2`WmK)3x!Ovuy z7PcGS%Hm8^{Av-0{Cd!#_0}9;R{uy%Z&KODC=9tVD9lp*BIT}73S5Lk-XuUg&6R*( z4f!jZD%ex1aTf)N4{O?<;4`8CGJyCRwxPXYEDq}8yy{AT52o0+Nolp9 z6zT`c#(75gE9t9ci%#(6P9}$kL^dh*!S+c*)^tp{HoZ?omg>NMqbA4;%P67;U^J6? zgGZSciVX+ZlfsW`Uy1U>cq`^FCR_tgrhh8=(si+C4YMN9)t(z6>PYvXS3d?fk%9qo zpF=n|GBrHT%Q)ENq7!PWrWklNuU>aQpj+-hY_9h3%uf1Fa(pXcMGTwrCAZV&BY?IvIQ!fY5THr>`88A>=*JHMu&~R8Y>6QfDyIqbR12f>j_W za}FOBVcdck;nhiBdh(@U%K|2GB{3phv$Zah=`+CGs7ZF@6=M#rhgn`&psJ0dzm;qNvR_Yz*BwdFCIDr#+0#y?=b_!)7-tb14T`|d( z^)p)Sb;5Ohq^%1UjWWoio=lzx1xOh}(n{t$H>((cG| zA;uUB8*howg5omOtPzaPDf)nFP;QFXK+`q>76l8c)LriNFC zL^}Qj5O$dBxr`A06U9KPieu!j@;i_+%~--zMh@tmkR%UV{flB_b}={A>rS2qOG8SS z(OC!}d9$Ehp5GioS~40->F%xdwIkbkv8WQO18;Vf$~})AGK@Vxf`%5qGkvAHd2We+ zLRQ-ePL;w~S-MAL85X2IV72hmVYxO4igDL(x2m2*0jF1hi-pk&{Bqs%f(Zd?YQdO$ zWtu+See200dlz69q+A3|dKbWTSLtUXtvPVS|@>im5|;*JAons)V(*F_(qswRU4&b$Pja}hL}sEL>lC`Ii8}Y z!o`HqH?=EixeJX81v^|QPJ5#f1~T~#Hluc~Ah&W0=1l|i zhpo6&0%}e-bJVeLY}dYS%q8Yrcz7>C+y>{_;%+g>GQ7GDjQ}6PLL0j3gBrh9Xx2^;Os4KUInhsEw%~7ojc) zIYIoU;;RBP5_gH^0=Or|-|i#Xp zp0Qno$>Oj}(PzA+E1A~$-rxGh1tHT6y0;_DA?d{3g^etqWE)c88STy+<70kmsDrQAvy)K0Nj{FciN47z zo=laZg`CJ!ngu(VJkgm^$(CU9)KV%~5gqIa+q*GX`u5m+xuCg8KWgt0l^*YrDs!V* z0%XFE(zBSmM0DjEn6lzSYFz44W(lZP&5V!G4J&qcf`}FgBdd^B6*J??rW(0fiJ%}Q zbmah3VbaKFlnpc3beLvFpa2kWjbxX{5+iDHIDJiYd5kMNi-3;+E#9DjhcHtZt zt2U;hhFQxrIGhd{z{q5)EYYBGQm%<%9VHeicex9V?g1i-&3amTt6d%)<5mdjw5a1b1Gx{|DMpGxZ2wrQ~&@7Wz_+9P^?Ds#94pH*%~YP_0vtwTtPD7M3cI9bw*76ij#4zXevzoqBjf4c}Pu3>O1&5+-z?(z6GAOkNb) zk@VhV=JR{aY?aGFc1g&hhoDga2FFC4s)JY)$~P810`GHx&=pmZKn7XZ$53&v=W(B^ zd?P+V_Ipwi)E9@(_|CxmPC(!%-Id`gQ)WtDN3kuWt<|3ii34;5TjI%tJ~k?JnejB+)(U1`j9K!TC6p@@~v+77WVR{Yu` zcZ#cTk)ItSN57G>zLsS)dlKqbl7w2dJ%mMK5o*lU;G0h}3Q)_8lxwo*xUjOGxm`$i zpGpSlwrh}x$}_Q%u88zdLQiR;uy-i^N>?TQxS*8a8I(c{3&-b0m&VWyFd+*J3%%Th zz-oL>ZmOxaK9#J$ETAbnSH>P~C0n@r9?X5Y(Y`gA zpd1zjmrKsaNj8mq+-`vYR8Ckli&(Ooel`b|QzPj!yQ#1c7y36s*jQCJCddO7GBd`V zgWvhK{GH`HN1k-!l~(!&8TEPwk0PlS=k%kPbEqSb|h2 zDg20oCLZ(CCR~nS>Hp_ZJ-wHsxB%O=kDT6;UMu9fJ&U?A=8)y+GDOz zgRdxX%qj7JWWFN-eVasl8lD`egGjB#3-Q9@ch`Z$$S62zsL-Txh8g*~GQ&%6 zfvi?5cu=Ap_WZ%OX8gG(z!R9_y>?{9K6z3(9^qNvBMNNm(-R+G>9j6*24BS2V#Ri3 zOyVHRf~v<3jS(K)ta~?+Ek*DY{2_Ld!LiEs8qz`F@kO_rf(h~fWad`7ci1Yf;4^#q zP1N?dn=ZTS{NcYRS#jb z#I7RQ83(6#uQ{Lq4~;35khqOB{umbI{*dX+n5woIY1%BlrIHs}bNMaiHUQD^wTE=< zpbryX6GnZA(43Tmw8yg1nR%qszD*_m?;pm||Os^OTU^q+D9_4_&LJqipGaZz3zTWYpQePZqAyv*DiN`k zy|WQeji*B6cd#d)s9cfa(Gq|894>J84C&rcYZfN-6WI!oB*8+iq7H|tggKtrLvX;( zO{Owm-XD-(0(uicDqf8dSd`#1n3B9%(rBOold@?tDKdRZyHD6WpsMc%D#Huf7Z$>7 zp#^L4K-XM=hi_vT4V8enRS6FGg*-{UCvB3YkR+Q_p%K0#s_;n-LdME_o}-m^2b;wj z@bh{btf(TE8&x6aJ#7k*>pmd7E9?dn7m5u1ciqpkzAO{&YDAMCdMIEt*IcI zvNJONJfy0yhwn(KRzp=A>?k(g2aMsmRC<<9*EUvQS*a9yFAU$P#6Vqq)qjE%SO5>8 zrrR-bg-3)Q$5{tLa4?vQL!v%v;zHrcn6gO^`X&n_^;x(uaorTbz3M6`UrXu>W!@Fp zUdhQ{NmCwff%6>Q6C;yf*jng4$6{~!w3ng95Zo9vQek1go3<=feJ}m-6dpc`%AoLCGA0892J*D8tODGz+>g^SkUX?Q&+{! z_NqG$s+L(H&IP3Kml z81S1@N$TKrRAyH8MM4B7lks^4S);fOLUT;DP%z=Oj!}-WVuIlQER!iOrNlzIX6mtlr13P`C#*gcH20xgg3V1YU2bpnV>NAAj~9{CgOGI0O`0xVaQEvcWItjP5RuE zkwBhSTNcu4l<66iQz)})3T0K7LP7p{g+wZ(ng=APrzF+c!Zp?PKXeSbJn9k{&Z#*L z6D|sXuM7Is6sin`d*hy3BW(OAE|XWPJ1eRg@t+$>ZIIKs5`rC_e?`G9jI`M1w=g(@ zqQv)a-u+!h{e5&Ge){J-@+-dm$G^JB%HPNT^~*2c{^Q-hzMx zk+SjN$M1iUT!sT2OYq&_#W&MpeEZioKfHPOFBy%)Ieq=+-9Ns6`xeuoQRk;$#OJ?- zok0nKZ-0Ti_RV+y{6YK;KlJlIfBE^Rx8J`)A_SRJ8_wqHE`nrQaX@2zkwy;UqiiG= zK&Xh}jmDU&FnV4=JM}iJh;A(qRTWykiUO^w0^wUs`anuU*dJ_YkWhGeJ<5FobSOCM zN0>1rRl_W!(ciFY%{*6+hKx3+n5KZqkaFJs;R@Hy@-Hkmc44(@1k7Ot8Vj3yHc1eM?Xm&m+-8;b-x_mTe8# zEN#sHsbjt)s~h~K$SFDP*EPNkZl=R>(qjOBPHlYqTNKkIWXd4pPAJ&W$@~^w>=HY% z`W4_~C$BS1_mY?O)&i^us^jeg6aP%6}2{%0GUpGi4MOQ%p=}+-O`wlO@Sw zI{z2OHsN#{@t{AM&1m_UhWr+-8{|b-e%^2zA^ zuo=d$Vsp)(y{v^TCtK6L7tQs723OTm{sAi)+4v`w76)+hh`5sp|&Uq6*c1NJp zNBj&dJ-%H2kI3t%|54k!djnH)f?NcVPRy;oPXG4l;M)FJ2 z=pbsLT^o0&YJ}azLcY7j7UPx&O=`NMhUo*^mSC6u2V6u|WDG^rtA>>J2a~aZ3&d=K zjwA3FM#E9z`l2$JS|Z8vOXJOq0hnOiRWmbM%MT^cRgQGhz1#A+yC5M%bpqa7J{v5} zYpOUJDKvO;C7^|d*o=@7Ev6a}VBWBetY7Q?Q|>RPH~%jh2(nW3zCvVmtFKC#8I=!) zo_YxIgG7#BptiLX`@=De*=Blj3CcK`AjVGEt@JOHjF}a@1gSY{goy%U#4=oj5QL~& zni3sdJ!<@}&RkJf07ZgEx6MAUk$e3)mN))y(O~|GY}E%dHkKC`#sog*rLa>TS&j%X zpNbV+8+1EL+W+lSiSh3k(c!lwp*ym?p{jSzPJ58SI5jSuG|ZI|JpmGQ)87l&#gP#Hpm{iy!^ zLNT0bO8NfX*A&6c5l=XHYcTFnev&dga^lmQmGMoLVOl=svF?)?Fm?fW>RUPR)}(jO zM+@exV#HtBl^rPPy{@FkF9Um&X)=JN$$<(Bh+iMb;+b!-Hk(mXg*W$X*yW0A zG`yLP%C6B6{+xTf{CLtZUjDDrs^g6G*RoRx(iE7$6qW)L*qcyjC0TU0;y7<{FTi0Z z{xRH_KOFo!oU=0Q&pe`py?O0-e!BnE1wvkFt^RX&_rKo%J&qsfhx7c0?{HLv_^CJO z^?K^3KgOTI2L0HLJMmKdM}VIj^o`$G@3J4`r34{R0sZ(&;osT z9OoY$%YR?x|Ni68+u#d*dQ$vX_;2`)w~GYw)A$9``P6)X`jyuSc(3>+v*K^SzrGPM zj22-o|GoSWY>N1+pO{*sPKkS8Xf2Uy8}@MIw$_h+jUYNuidrgM{BQMmf*<=tG{QWz0VW%&c-fzF%(TA@kqqPzCFhlpJ@N47g z9N7JezxF1IV3SjJsQl|UqaLC{;S~X>oPSLGhxk+|QMh%V?8{FIeHK3XdK<(3IP%E> zeNuk{PFpwjD3&gL0QUEYNO%q5-c^6mV8X&u`>-zR9NkREWz#x>KMtmKMgwSLVcLZQ zl-IUahiH-4Zon0`9y&58+v-O2ZC{w;FW}>&1$(s&R%!MmEbT<>ZSeGElO1$2KwDM* z-)oD%<9~Z?zhL=gUfmBx)Oc^Flg>meq-En0&E%Z*%S*`C;;!>C5mx#UOytnRpsJ!! zwr)Jvl_)EesXSXKZ45Bp_*(dp};@vcB zZVMsQtoIctZaf&Ds<``Qyy$K!UUu#2JH?s7)Atf8obXgQadBpfDqL)qA+(Twgp$m7 zUW%D7&X3`0aO&d6lr)I+1wwc(S-Bw$5#sJCl4v`p##^{^wu?$>;Ip`aQrLq~Ld^qqVABRrb{%m3mQfOYJx>&o> zz1guNX=@)l-MPtPiDl467I9hR2(z!+Mkb3-f=rWTWs*2kZGx1=OZ$Tjm!4VITPj5 z0KwU9yxU>W8=su4-T~#*!`JJf41}#aS%=H}Eva!lwM^n!@4y#MSaY>En4P`@mx;eN zL7I7pang&Ia{;bj=H+T+Z9)cv!eG>MU{ENjF^GV6KY23yWiB+S$K8eF{>gnuyRSS_ z!vTgT>M-&@hu4yuXF7g^>FDGwXsGW>lJi_8&L$`C!F{}^rnmbw(kNVP^B`Au^XbWn zQlKM5hxt07=}Zj{d$W_%^agW>ZeI8r!nfg|ck=F2bRC2=L^d8mYW6NE(K2GfyZXU6aSm)Zt|E z4113T^jtxsC0BbeI_a6TM<^kA4!h zQLS9D23t^I)*2+Mn3r8~8}Q5Kel_*>NVD~#qUOefnuF_f=QbVO z7uH$}+RIvNVUz_4mgDijRtS8Yuho|{bT5zc`tmW)Wnb<|t?2Feo@+sO)$h3-j@qZwkrz1 zCY7SoaTT_JyX;h$_>^@u5f9B_1Gg8y>ECN>$)~EXQZqiTz7~j=mHP0%O~}LXUC6m2 zkNx}6Oj?r~?Qje=yYLKvuaX+~_?PRco-lRjhW{$*Fl{%6mqh!^RiP97OR5kD7aK(J zFdXAL#-fd100mZ~M7WrT^BN7F^zy$Y5DYYVb|6@dCO2W4#rVHI(%7Jj_>$vh#abg#n%c3XZs?7=ObzE(ke-C7# z*KX;YkX|*K+XZRruY)USyJTw64e3>*K>{19NzJ?cHh>Pyv^#Cuf+zbf&lvZPD;)~Q z>)tI8ucL9VsWkfg>hc>0xC(5+dNCC!>0`6ZUu=k#s5>YF|F2=&wIIBlv=%JGjIf!x z>AQd%mOI1C1>@DGIT&#>Zu3j5HTc3!SzHY|A-q~N2yB${ypE5#$1GKx85qLVn-jLH zMQ_`Kf8m21xLR~Vd9`S9=SP>WMdm;UCx};@7P0biu~v{h)$959VG=+O%PN6Jmdo&# zc|!u)kX=r03n}d}P9D;|zX-a6XK&X34Hedc=yFsT0ffinQiRJsqBcA1H$0+FV6Hah z9U>aOa&h?-_QbB|DFKLOCfLkRe2~*D6SSQ=9k1pb=Y!M=LFzt zQQcFJM)8H#eR%3~18~)-Fa4V9wgvc>2=TlMd~13mZh;bfYYKl}3cgjhWSjUAyUOq5 zgJMuYOxG2gix%jX{dSkS%aY*I$@JO2((@P$#BWXd*V4DetUo#7x1<)+z4%XT+KN<{ z?{)MXRU3hyAIjCNL+X^iY`>GKK_^s~)L<5FsnPt^GJpaU){rln_8_ciR!Z~5QiW>V8E(_^`YKrudkiuLAI^n$De+RITpOg5@ldel1yXp&-@Bdq}nh>tRA~$gKR&=^J&csK=BsytBd9~;d++K?S!u7p>E8m1! z;&20cX(hshn|}w_l%V43&uOfe)?aG32`?>*#@GArms==HzPk5?GOskJQn^yj zFzl{W^dT&{u4|d6LpMa3)}i8Z z+sRVq@^brm?e$i`k4@{wf6Z>D^D=&H27g`(KUTLu`y}|>oehrp@SPeL+<6htK~Ydy zq!t1%hsj@Bb>cH=mRwUsi>W@Rv0u6RupJ5ZA4cGXxf)l$^I8C7CDjc|s#D3^Z5RbM zynCIR>%~=h=OFB7Dz618*0S%E^0FBAAEa?Bc?;(a)0ze2T~6mxU<*);rNH28RA38O z%u<1v^#1yeZyQXkMSGO!Zq4)@Q=Y3LogDr1`Q~nzF|9~+$bn8?H!hj%nU~XP$kmz? zN=$2Q@26SNoiw@0tuET{H8)WmnsWxESgyr5jIuwp=!O@|wFnX06`WwM)||j$T5D#@ zCKX;TpD&%OK_^sLjRx^Dz8=6aSB*}Xuw0F1^TX=$>-Jpjxq-oy_OiqmpNi7}a#vj( z;hdzG$@0ZioFf>lNpZ0Kz7oZ`fx=!RsjW^~Azzfg46dbNY4j4a!Ez<;{4e1Sy0B^Y zZQ>;S_hs%ruHxK)VNHtr8~_jQrBYUNb>{{T%XJqouQ6!jYS9fcrnR_7uC2!~TWlmv zO6K2RIL8)Og>Im+q6&r9mWjU>n)@2>52ra{xdu~WdBYQZ1x_ZIZ7&+daCE`^j9lHhp~X;l@81G7qddRlH{+a~ z|kAYyN#JrcpcTKLf-*j}0KW2XKbL1J2m z=^}}@TO0s0o?Q6V%T=8lJglTTafMs~N}H=XH(=P0FpVxhxL4G{Kk2^&C`{?^`VbIY z{kdVml>X9S8!Xf;k?1<^Uakh+fME3+%!(oKd_^CO%R_2bXm;0vcH;Plo<|NV@ln$^_K8E{}_71q=QF7)v7wC4r{tI=NOuOYELuG@;v z?&7h3w}OFk6vvy*?FInTidzN?@W#VvDgL+F1lQbx^m_+@F%{?r0MiOwQ#JW3R-hvY ztgOK_TZZYrP>;V*%g0n-3-qf^ePQ&qpWrqaSgQtAFYqQ@E`!KIiI#8U-b?1it%60ceqnHj+b``B~Oa;2Zz-knDND>?-bn!4St_Iy;U{wv`;k_Oh7&A}i z2m`CC5eo$KngYQ|n&i4YO~0$%9;PPkuAW9nShMP@nv`9d@=7+q5tn+Nc^lCY7*C>)CZ~Xs}kNjI;PFR-q#xte`^50s4z9gwpxtXAhVfbOVJoX)w&5 zy2If`Ly4LCa|49c=x^um_Y#%(b?rbmP*{xyRRQ>`)1Vt9te`fVCUKlSQYa_>SG?*5-o@U-O!YZJ z!;0$rb+H9Tt*_4pI-*6Jble`EttfV)LgTGqC=Liog z=`c=~U#0ir2o5V~FI^--fVYOYGA#KT1?eBBEyQRgKCj#p@i|V%L|S; z?A2EQhs|riVYA-NtX~3$!Owax2M+s11cQ=$MU|;`osZ(KH#-_G3?WfnqDH0N4M)?# z%_CcFZZ+WY;CYca$9}K3Dzd>&6ci~hO=9cT9Kh!fhC>Wn8xMlWY?JX!9zj7>q|RtUj=Ll>YLw zoTBg#1==c5B?O`zfq1dga9&z>0I$j7YbqSSv;u+oDOSBQf1F;n0%cZjwT=A@etNlO z&)-;aJ9UMOrxv}_R?2v(;P!l)8`NqJJ-^|s##=dQzx=1;3H34#m8YL4y4-#m-n3PWH0KU`&N#T_EO<>;Qh z^MqZaM11ABUEs*=z=@5O>}njf~{l8{ItFcHa#8{FWvEK0ai-RD?sY z%D3HN|JZgkpVt{4jLhyrNlI!6mny`z(<8FJ`+jN<^aom9d*9l zX5y1=OICPR-+nU@-xU(D!Ly*-?&-(4n|7ArhhBdaByMmG1pTpu7RwHLXh4LJ{Kv z4Y^;&3#GCnf)(%Aqn714R0hA@jXZ;8YR9ywbNll!>pSH5?jOSK7LLPXoaozeW7}`T zTiM~@_}q<#m5sOOJPU(a#fn%gEDdFjcn5)F&s%Qo22~|WQ?@Q$ zQAkM@1|->W{Lqj}X*P04@44!Lu=#OfbUTp`Q7$xqML}++nR$?~>ewO6^x~-4LNIu?;=0k^zB;G%9?~ifi?rl5*XYpq5Ml+Wy zGYNozh)(lkD!ee(<#T?wNWrqtl?vzNa;zx_IV)@*QQb|a)~VSV4HB@O>0NMYn3Kue zWr3S~iDs1@o!(hBIfG%}dIVb@axH`Iv}arErQlV3W|d}7TYc*ct-k1YKlXfJu6;w-Eer6TC!EpoL4top0TDt6&vfFRS-2y z`HgMoD7+pw)Gt$b9ZxFD-JW?M27p?-(fqQ!0?cH3biz!-+7_4z_DsK~0?#>G_Mgr< zDJUVJhXTgFO&4&{(crDswR@)*C0|{Z2U<6 z)vT)7CVnUOP%M`Q8u;vwl+yK7lVm|L4c+qY#} zhCY-<*qdk_WZah>a$eTj=36&(a3V!c+qHGY+$87?Z#|PL<1N{;$?vC zL1**6-RIFuLBl1KbC{-gbo9Eao|=|P??+CJ8mrCB6C6)QSMJmPd~`HCu8$L>fO|jN zq+9`qjH;8xi1MQXrvB$(6>e?Xe_c~kHbcOV%SfZYnT1*AGPe!dZj_Kwcc>g>4$z}! zp!E@pqwmtJl;pSdM3{EQZDO<#4`pM9`rOUeGL^Y;Y3u8uEGx^0dfGwkY8`d|M^B7R`xrA&Rj@v3E5R7p%u3Q;g#?M;H zSV*@pON+c6+BWaCb%#dT25*>xu(rpEE9~faTCP>teG+_rQSG)&GPM#oj1MW>xnOSF zmYJaq+cv=-c$}7b1V~?f$xagZDyg`LqgA**W2Fj3oXs8HF9}vjkS7$+fGb7_L9bWY zBkgWU8f+0;-1$hwEqy`Q!n^ECHtquz*NK0Bqz&O2U@KM=_v%mCCQP$9c~Z^M1V>=* z!lN+R$|-A`ZCAewi*g#tj&n)bGC$5nN|&3K($>He#mY1Ud8^@C0?MITi)Y{CfaJFni}2a$+NE74_CGXSncfylB_!1_i6B0=CCW z{DL22v=#MGxK#YYbrdIoGU2Or@+)`y9nZ(t)V!-jwIjsyAguEmT7KvF?kZIq~FUW&MQnplM>hD!#5MiFVp*>vK zDCfBLJ3+P*8+N$sY+qL+A!l>fPC?35s-_)o$=bee0_7a_SPWt>+7m+p))aQE_GQ_B zIFo%G4TcWXO{y#Ad@Wfd9Ha1btS#nji(6B0vh0hAWQNk{J+Zz5?ZJ&CWlO=SVTq-b zZn+Vrx#1L>yo!it?mHm~o$5?Q316l!g2ZLin`#tV=s95)m${;HDo#~V8#_+UAZ`Kn z2KD_4>}_~68^y`_rSe-^z!2Z#&yEljaRx<8A~czm%3#J>2I zBaR+~_EMYH=wg+_p1TGlt@|!LfQkdaz zZ-YK|bq!I-{?IxO&4t|g8S&RE|8OVnIzY&AyOix=S14>v`K^6-?!=~CJe=A5)ZyRS zJ=opyeoN9ZeFLuIS}J=Did!cB+M-T&lHQ!qRS+`?JCi&$FmXPc-WWwZLvJr}-?>%s zBL8z(=!9ox4XY;b=#}SI$TNgABNFSgVc(RAuAx&UZy)a|Rd~O~9$_vpjdzu}VXA~> zUZYiJ)}BB(#!jp&-nH*Uqq8~(rkyQeh;p{n@0!>HWp6c!3Jg?OwPp4dRiTo{Wp%<> zNw+3C+TrkQ=Y|2b@(dAwJpiYQ>coWRaKja|w&P4$or-(VM7C~PNO+b*TcjL7 zcid%ot~1dF&Z<8X*v?lIv)HCMxk9!S^6G_r_lw2UE7u+%8sJkc;k9qQ&q0!+OUQJu zcqf*F7t6Qaqn||V3z!L6tf|_9YEP*7E#KUqFw!k`hNBmGw;5om<)Ni^f=XKKd{Q zQ07aFB*%8L_WUPi%p{oc68=W#ZrRgfoH2P}$g9b0om?vJcVUP~f}Q5Gba z0Kh%LO-ga5hOoHBO&L>T6L{?=;W_f9cl|-%VYr3D?d&=qmq8eXDx@X0KVGhi!S>g> zXB$9exwQM3GAvvFDL&xe#D@?2mlEG;CXXS08mX@iB6YdB4{>P43=@Hc^)L5L89p~`JotTU$^P$B;h7pF8QQ3 zXCKv~giAA=oX5?$a7g5*ed}=zW#wVp40o<4XD)&ZF*=E+MmPRmPHjQBbmK1re&x%a zo`54Ik5)qbGq8BH#)~-W6biPd00uhig0*|!I}zRa>wujw4|;bvP}HP5E#iXHW^|`fki|SZ=ti z7y$swSXe*}p*lj(Ufmw5mW(6qfa8c*)zS@gP?8@Jv%6&{Ji>7|(nR_0Xv{PW>WQ8H_DZfpgdEeHd zn=EV_9id+jfw*%O4#~3|?;y_*(97+X?4jrkAK4jq)O@_3uCf`F&TrN?D+G~wL6_AJssiCKhwIqVUJ2q?eh=WJOO{Cp{>}o5dc;u z?QWUHD0_BLcgPW$e(pB3XMN>jjq>G{l0h40<}Wr>+RzNEXOh$jaHJ&-mSJXgk)KPS zP6M2BDS_{jYay8uGT=uw5&~&ZalOx_Nd~iEjhx$qe`ZCxQ3`4ScfJieq$nVd`@{{7 zlnZFY66cc835%p9Oae&r)9i949}ibhCxXF}ppS9#kna6OU~&~#a<aV2Dy z1Z)5r!*CPP<-}p21%f)6IeNzO-i>=M)!JBd;OuEU&~!Fq=CIP7E1L4&XG?dxwGCF} z(|LI&tcQ`8r_oX_6gv5eWZ@RaCJe=KuyjFmRuO`}=Bjh^L0}>=`-9{3S`7($enR*B zeKY4TRLK!n6NmW^4k_?41D!Y;bGowfjT*4)^G8!#UiApguweT->ih^zq|1M(Gi!@P zsdkVyKJacdbm;+$KR0Qixi-$7Q8ynrkfjt!0MEcrNO+2#eSm26P+*tUB0fa?{!3<) z=%CMI4E9U`tw$%FCgNbi$utj45iMI-i)(B@8HVH=PF*N6Q9dLfXKq3JNhT!Tc;-M7 zX7(RiyJL(_*P2F2OLoi(RsZw1p_ATbeTTB367dLC|FblqJK+K&N$ijp4$cVqB$z|b zg}xZ%%wl%&#hxpw4#{G%{qRcfe2iDR@@u*Gs=ekd=#atj*1LwicUW4LtXx5H3S84C zpW`2gT@7}{^0CLe>S`W4nTw6o`RTqpzPaywy6q0;*3F{!kGdOFUA^Ps9q2ud0-_l3 z+Te(VO~J)x4(C0GO|i#5CUC=;)`&58n*$8A39VLP{8ytl->E6 zf6wM$V}Plh5b}G%Qr}ZIFq3xDzD-5~V8#wb;JqIOj}rdZ+4&EFn%J@R7r_qQ$IeH8 zo1KyIM2>}@iPIy*Nw9dHF@hxfYgs+Xg|OO3U;1n=7YFddz>$on*1Ohx*EF-asEP}2 z?_JU|%=!*t;ogM_lF{dPH=H@(ZuW6#6$+lDu)4wlqZ>sMt?ziA5r?qe#Iua*@GRq- z*`$nT8N;8K!n4%3=J4~3_Oo#b-NFg*WRBtIU%r3+=6|?*s&AiYL}<&%Au={5T^WH2 zlY73}t$d`QGI5~uY9a4`jvx;-IGs3TkPD4hoR%=_=$(aAEtlqtO;^m`Ui-$iR*d7( zuC;2%m37YB{`9uh@>+Tv%(=&B=wc%1h)Walk!~w%7vEmayLs;@NnFcz#7?X8?Lw!b zoJE(_bChe{(n!S)tMj!A;QpIb=QeQ~Y^ohrXXp;On~xpXHl>hWO?%ZO9|don<>1K6 zp)jo%xX@h1>2k2WVrp167*|ZWvC1}2XWxcFz?cCiMLZ}8S#GSd&dk^!aFlYu@(;_Z zG#abU(6QvkA(!t&PFI!lUaWBt8GEtVQFW%BI6jWXXxfP}QE5|Q;B4a$P{QfdX~d77 zLbZs+kzTVig$!Q4$<&&Px^7OE*J)vTZ(MA~IBtyhdOZgOqaKvSG&T%oY$uwSmH}R8 z*p8g*+LUvB&L$J5ww!#R7*h25S+MMG711$oCc=b~4aQ8$_@n`7Ii+xg^V&KgXfLrN zk902XQVp7Z^b^dvyJW0c^16hsxx8Ui*buI);v9(wqoXyTHi^$X5fAq)#EBO$5zo~= ziQnMX4<-x?<&^X(&geUbvYcv<)@8?{Sk|3s)iQ8%@xcCq#gyuh)m=T%?`zH@3~XPR zQ9__}uS5TQ?L0!}Kp#f7*NMhGiG^1uQ7XfmC(xoKYpW=;aiIsyg`N*4H@gogPW($9^#;SAg4ULDI|3jb5Esx?_(QN ze-ocm_7RUTqLpB-s6kY;kP!=$Z zmcbXUuU3`sYzzd&;4faSFs04Q1y->k&s}CQV>1`3OZE;CF=(6+|Y_ut77rGI;Q%_d(J$`0F{y2HeKE#z7~`bbGEDcsA2*^Rpd8-naVr9xq|a%4F>`_6Gy3-3F6K=@<0DglWs}o$ZDcdi zT#$?1gex|?_HkyI;%c`5W5 zszmNzgQkr!7Kzzh?=%*v+_ij$tI~NY5=|wOu~?L)XY|@E1|&5YsfTc{n#pjC?AgMH zhFM7#8!__t#Av{TyEAMQVB6*_26?&t_S=vRg=HkI8s;Eb>^%~UQdP`t$H1~%%tC6| zcS^o2Zko&3oI2(8diQIFAzAD?Eg%@bGmn#EOhhsmNV5d8d^W?7s;^kO{<&PWyrb10 z1LbcA+ZLa;0b}fyJ*B364rlC@^2rbPFdUTI6v*6!PScSP%Ha{VL^+HmRYaQf#tx|SoadzghOrH^d4u-q zCMsE2@La@f!%p`wXyXtx=0akUONbTi^F^>IV$0m7hE0efW-+(Ji#ZQ)Fc}~<=dP1J zrCG3hwR_+Wy6f`>-L(xks)vBl2p*a|G@diZ`}zC5_}LjDS?+^|7V-Y+j4ZlMlZD#t zOt|oyg7N3TU^R^uYCpT5;?QQG$4MF+_FaZ#=5xfY+eCS-)|??ixx!=?EV5=9JtbCh zn;s-YOjW_Rz?pd_A)ST?Wqz^_Ykp(`gUeZHES;3u|Ry{{f5E&y~#?5^WlGJD%uAIkIq%PQ!No;&1jbZ6kM;f%hWr(qc(OFu5DL+eRX$y+C z-kLmwkbq|%R7StE+fW0Owf!7yCpEbJc2(I>12u<@_MGyx-Nr5x#8dlb=I4-<)6AT& z?w4@M2GKB?t@|^WnbdhNRtwDxgg?U4*e9If=hK(Ga!%d^Rkn z8jlC#O$ZQXYcL~APt$vj56*0O-T#gk58buN5viILFw^7~JZ1F(ueX*m} z#;j0MJ`3Wi=I(62r9iF%`=50=-M-XBgF$O3*V?zhJvki6Qn+MyG@QGi^FH3Pd2bm| zkQzd>cH4Q6-BHIi_AJ|thuM|VQJjpdyvYN*?8V)3EevZ-buYXf`6(y+p=Wi?ukEG> zA(4<)vcK--NRk*d>c*<6B^5`c^_F-gF;J6W$ZNfB*6Qryx zjQ6wc@?^wWEeMkO%hQn0B$PPEVQ@mYh+j>!gx$N#UE5iov$g{>cLf^68CJ3b26If|j{}$k zyO3O}7pM{m=!oGzjq89DM%gbNb=f!+uMAO-pc0;C?SMs*H*}af|4p#<8H@+WRRic~ zHyv~roH(zDVL(987Fb8U`6!HCI|Bz823X&uoWlpj4F z%sfZc8AWu%caVrKu`_cMI-)zQfLYHbb{4e1S zlJGm2JCKC`KI4pBbi{gN>sGORLz+^44ibJvlPP2izN0}%qbZOx#Fudk*1&DC{WB6Xh(y8L)-*%81h-q%3y3i7O#2(;vD(&o|4H46?H`@!=y&#ir9iz zIVxh&AX-=Zc_7ijd^&_n<#qRNc5~kWGcMw`fpuNhJ|>b+bHKU0|639t_Fauf;XzW1 zC1>lY06d#h2S;hQIZ8ChwLNkH)l73U9QGZ;srxv~tZ31Td(a+fiJ~!}Ddu%&@~9y^ zh0DIg1B4DIknP1D6x@*ZH-mkEE@2!K^EuzWKS!HO6JW%m_XDMR|CSCUfH-*WUcT#b zW+i`|w*6pIY;^7qdZs$xzqKD{!JIA8w{Nb^FUmm5J>0^rMu_^tpEg z&cn^~+e=Da$33{v;e4m5@HOp^CnxD9ALI}|y#F!&93(;dBgER&ble}#JWKm?Q?x%G zFFdkuk|V)dveorh%TWW=?A9}z(A=`rJE427)wZ9lC$(0Sn6?{7m^TbtGY>I*UEH$c zxx*l}_dou;4Zg_D3aMr$8Alk^~U5%>BLzSMlZYGjO4Z< zr0k$_8iBx-&?&vGvSK^Q*mCY2>6GB!eW5!3f!`4^vS-630b%S3pN@wFJS51wz|Bup zbl3t5YDtRM`$i<<5muh(U`dm>kSYh1Ma>lDGlCxLlgz~sFqoj~fR)A5Y;6>|QnsYu z+oW_eiflNBOCml!^?u<>=|mHimv&1TgR?2qi72WlCO>7sma?S;U?HVT&VWOM2+>9x zS5!k{?icavF%6$3UMF5|4u2Ki^5I?vRors1efp7PWK<9gJXGnPI#V=S#|qVcl^z+Z z{2Z~U#9_G5K&|5}X1gD35Jt6>VZon>ZYEAD%;R`l?}OW=-$MIzj0U4TOYv;$TT?e_ zUtf}O2Lzt{7?*o+TS_O#!U@MHoGT%N@2MP4By6mbeg2cO>os*33sw>6IpjjT7+t_7 z0ApGQz?j|4=4AlJ4F0?%07H_)@hKIKtD@Sa;W9`giR0`sY#ki_TrxUgE|OmtTWI6u zVzzj-Unkgw7EL-@@!nti0DL~%KsjgNMe^&BoBp}h*9m!%{2C|AfRD+00qlh&^nppL zEA~K*7ld56Ui2Jmdn)F_ybY}RCtd_|Vait?wP-7-edW>}cp(KX`XkopIHrV7n2XBn zBQE@sQ2;g&7ZgU?4|A}mO>l=?JSHKLm!w(SI zAs_V0JJyc#Dimr?*DlmpWL^#XxUkhFm<+I`jC5*mMVLMBPIa93Z;=yLg9vzuD|E%U zFk*W`W*Vj8QdlVlE7oqrQ3m&F7{x#tczg%;6s@_<)2NVapFwdnE3rSt0|9&QT<5{9 z5<)V)i6Y_1Ys>)8`02ZZ=`--g2e?%y==Ef}8s8792fX)7g7uk4Be)d{=-qOARtFhq zdA0i{=I-ZsGfW&f4SiUKHgcU}FX{>c7LB{xkU##AXJgw&MAX|@tKl84`f3^oVbMtW zj%J|Xi$TVo=cKOuB-;dO#HXPL2RGpxY;(+~0J*qgIwk9& zX3Q@bF{ZMjXf;`Si$pkrs>>W8S@Ynq@bBv<_%2ZoCf?C>AmKQy3F!=HsKIQ5u#Ci!k<^QdXD)-{oeRQX&=c@?y9@hx_gczZ-3-sSwPS*$^shu zG&fq=2lZ~#hET?|We1!=*tQ)|z2WR_bx|m5$A!VWz^PN(AmJ?ly7!aPttwkJ0~A|MUL~SK{}-|LL{&{{!$jw7kq33;--B@ZkUe literal 0 HcmV?d00001 diff --git a/doc/ionconf.tex b/doc/ionconf.tex new file mode 100644 index 0000000..9dd71c8 --- /dev/null +++ b/doc/ionconf.tex @@ -0,0 +1,73 @@ +\documentclass[english,a4paper,11pt,oldtoc,mctitle]{rapport3} +\input{macros} + +% For including some files from articles +\newcommand{\xchapter}[1]{\chapter{#1}} +\newcommand{\xsection}[1]{\section{#1}} +\newcommand{\xsectionstar}[1]{\section*{#1}} +\newcommand{\xsubsection}[1]{\subsection{#1}} + + +\title{Configuring and extending Ion3 with Lua} +\author{Tuomo Valkonen \\ tuomov at iki.fi} +\date{2006-12-23} + +\makeindex + + +\begin{document} + +\maketitle + +Configuring and extending Ion3 with Lua\\ +Copyright \copyright\ 2003--2005 Tuomo Valkonen. + +This document is free; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This document is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +chapter entitled ''GNU General Public License'' for more details. + +\tableofcontents + +\input{confintro} + +\input{prelim} + +\input{conf} + +\input{de} + +\input{tricks} + +\input{fnref} + +\appendix + +\input{gpl} + +\input{fullhierarchy} + +\chapter*{List of functions} +\markboth{\MakeUppercase{List of functions}}% + {\MakeUppercase{List of functions}}% + +%begin{latexonly} +\makeatletter +\def\fnlisti#1{\@dottedtocline{1}{0em}{1.5em}{\lstinline!#1!}{\pageref{fn:#1}}} +{\parskip\z@\input{fnlist}} +\makeatother +%end{latexonly} + +\begin{htmlonly} +\newcommand{\fnlisti}[1]{\fnref{#1}\\} +\input{fnlist} +\end{htmlonly} + +\printindex + +\end{document} diff --git a/doc/ionconf/WARNINGS b/doc/ionconf/WARNINGS new file mode 100644 index 0000000..726c0ef --- /dev/null +++ b/doc/ionconf/WARNINGS @@ -0,0 +1,8 @@ +No implementation found for style `hyperref' +No implementation found for style `ae' +No implementation found for style `url' +No implementation found for style `tocbibind' +No implementation found for style `geometry' +No implementation found for style `calc' + +Substitution of arg to newlabelxx delayed. diff --git a/doc/ionconf/contents.png b/doc/ionconf/contents.png new file mode 100644 index 0000000000000000000000000000000000000000..0c752c66c876acaa2131d43788a7406f5979f746 GIT binary patch literal 278 zcmV+x0qOpUP)n_8B^;cn$O_g|3BG{!1Fj&^JiWpJWLeA$WK=+#U`gfB`ZJpvc?v;;IiS zWN}RlBW%jSmz3^2R|i?JM0KMv+9z;7ee +Babel and hyphenation patterns for american, french, german, ngerman, b +ahasa, basque, bulgarian, catalan, croatian, czech, danish, dutch, esperanto, e +stonian, finnish, greek, icelandic, irish, italian, latin, magyar, norsk, polis +h, portuges, romanian, russian, serbian, slovak, slovene, spanish, swedish, tur +kish, ukrainian, nohyphenation, loaded. + +(/usr/share/texmf-tetex/tex/latex/ntgclass/rapport3.cls +Document Class: rapport3 2004/06/07 v2.1a NTG LaTeX document class +(/usr/share/texmf-tetex/tex/latex/ntgclass/ntg11.clo +File: ntg11.clo 2004/06/07 v2.1a NTG LaTeX file (size option) +) +\unitindent=\dimen102 +\othermargin=\dimen103 +\c@part=\count79 +\c@chapter=\count80 +\c@section=\count81 +\c@subsection=\count82 +\c@subsubsection=\count83 +\c@paragraph=\count84 +\c@subparagraph=\count85 +\c@figure=\count86 +\c@table=\count87 +\abovecaptionskip=\skip41 +\belowcaptionskip=\skip42 +LaTeX Info: Redefining \em on input line 826. +\bibindent=\dimen104 +) (/usr/share/texmf-tetex/tex/latex/base/ifthen.sty +Package: ifthen 2001/05/26 v1.1c Standard LaTeX ifthen package (DPC) +) (/usr/share/texmf-tetex/tex/generic/babel/babel.sty +Package: babel 2004/11/20 v3.8d The Babel package +(/usr/share/texmf-tetex/tex/generic/babel/english.ldf +Language: english 2004/06/14 v3.3o English support from the babel system +(/usr/share/texmf-tetex/tex/generic/babel/babel.def +File: babel.def 2004/11/20 v3.8d Babel common definitions +\babel@savecnt=\count88 +\U@D=\dimen105 +) +\l@british = a dialect from \language\l@english +\l@UKenglish = a dialect from \language\l@english +\l@canadian = a dialect from \language\l@american +\l@australian = a dialect from \language\l@british +\l@newzealand = a dialect from \language\l@british +)) (/usr/share/texmf-tetex/tex/latex/base/inputenc.sty +Package: inputenc 2004/02/05 v1.0d Input encoding file +(/usr/share/texmf-tetex/tex/latex/base/latin1.def +File: latin1.def 2004/02/05 v1.0d Input encoding file +)) (/usr/share/texmf-tetex/tex/latex/hyperref/hyperref.sty +Package: hyperref 2003/11/30 v6.74m Hypertext links for LaTeX +(/usr/share/texmf-tetex/tex/latex/graphics/keyval.sty +Package: keyval 1999/03/16 v1.13 key=value parser (DPC) +\KV@toks@=\toks14 +) +\@linkdim=\dimen106 +\Hy@linkcounter=\count89 +\Hy@pagecounter=\count90 +(/usr/share/texmf-tetex/tex/latex/hyperref/pd1enc.def +File: pd1enc.def 2003/11/30 v6.74m Hyperref: PDFDocEncoding definition (HO) +) (/usr/share/texmf-tetex/tex/latex/hyperref/hyperref.cfg +File: hyperref.cfg 2002/06/06 v1.2 hyperref configuration of TeXLive and teTeX +) +Package hyperref Info: Hyper figures OFF on input line 1880. +Package hyperref Info: Link nesting OFF on input line 1885. +Package hyperref Info: Hyper index ON on input line 1888. +Package hyperref Info: Plain pages ON on input line 1893. +Package hyperref Info: Backreferencing OFF on input line 1900. +Implicit mode ON; LaTeX internals redefined +Package hyperref Info: Bookmarks ON on input line 2004. +(/usr/share/texmf/tex/latex/latex2html/url.sty +Package: url 1999/03/02 ver 1.4 Verb mode for urls, email addresses, and file + names +) +LaTeX Info: Redefining \url on input line 2143. +\Fld@menulength=\count91 +\Field@Width=\dimen107 +\Fld@charsize=\dimen108 +\Choice@toks=\toks15 +\Field@toks=\toks16 +Package hyperref Info: Hyper figures OFF on input line 2618. +Package hyperref Info: Link nesting OFF on input line 2623. +Package hyperref Info: Hyper index ON on input line 2626. +Package hyperref Info: backreferencing OFF on input line 2633. +Package hyperref Info: Link coloring OFF on input line 2638. +\c@Item=\count92 +\c@Hfootnote=\count93 +) +*hyperref using driver hdvipdfm* +(/usr/share/texmf-tetex/tex/latex/hyperref/hdvipdfm.def +File: hdvipdfm.def 2003/11/30 v6.74m Hyperref driver for dvipdfm +\pdfm@box=\box26 +\Fld@listcount=\count94 +\@outlinefile=\write3 +) (/usr/share/texmf-tetex/tex/latex/ae/ae.sty +Package: ae 2001/02/12 1.3 Almost European Computer Modern +(/usr/share/texmf-tetex/tex/latex/base/fontenc.sty +Package: fontenc 2004/02/22 v1.99f Standard LaTeX package +(/usr/share/texmf-tetex/tex/latex/base/t1enc.def +File: t1enc.def 2004/02/22 v1.99f Standard LaTeX file +LaTeX Font Info: Redeclaring font encoding T1 on input line 43. +) +LaTeX Font Info: Try loading font information for T1+aer on input line 100. +(/usr/share/texmf-tetex/tex/latex/ae/t1aer.fd +File: t1aer.fd 1997/11/16 Font definitions for T1/aer. +))) (/usr/share/texmf/tex/latex/html/html.sty +Package: html 1999/07/19 v1.38 hypertext commands for latex2html (nd, hws, rrm) + +\c@lpart=\count95 +\c@lchapter=\count96 +\c@lsection=\count97 +\c@lsubsection=\count98 +\c@lsubsubsection=\count99 +\c@lparagraph=\count100 +\c@lsubparagraph=\count101 +\c@lsubsubparagraph=\count102 +\ptrfile=\write4 +) (/usr/share/texmf-tetex/tex/latex/base/makeidx.sty +Package: makeidx 2000/03/29 v1.0m Standard LaTeX package +) (/usr/share/texmf-tetex/tex/latex/tools/tabularx.sty +Package: tabularx 1999/01/07 v2.07 `tabularx' package (DPC) +(/usr/share/texmf-tetex/tex/latex/tools/array.sty +Package: array 2003/12/17 v2.4a Tabular extension package (FMi) +\col@sep=\dimen109 +\extrarowheight=\dimen110 +\NC@list=\toks17 +\extratabsurround=\skip43 +\backup@length=\skip44 +) +\TX@col@width=\dimen111 +\TX@old@table=\dimen112 +\TX@old@col=\dimen113 +\TX@target=\dimen114 +\TX@delta=\dimen115 +\TX@cols=\count103 +\TX@ftn=\toks18 +) (/usr/share/texmf-tetex/tex/latex/base/textcomp.sty +Package: textcomp 2004/02/22 v1.99f Standard LaTeX package +Package textcomp Info: Sub-encoding information: +(textcomp) 5 = only ISO-Adobe without \textcurrency +(textcomp) 4 = 5 + \texteuro +(textcomp) 3 = 4 + \textohm +(textcomp) 2 = 3 + \textestimated + \textcurrency +(textcomp) 1 = TS1 - \textcircled - \t +(textcomp) 0 = TS1 (full) +(textcomp) Font families with sub-encoding setting implement +(textcomp) only a restricted character set as indicated. +(textcomp) Family '?' is the default used for unknown fonts. +(textcomp) See the documentation for details. +Package textcomp Info: Setting ? sub-encoding to TS1/1 on input line 71. +(/usr/share/texmf-tetex/tex/latex/base/ts1enc.def +File: ts1enc.def 2001/06/05 v3.0e (jk/car/fm) Standard LaTeX file +) +LaTeX Info: Redefining \oldstylenums on input line 266. +Package textcomp Info: Setting cmr sub-encoding to TS1/0 on input line 281. +Package textcomp Info: Setting cmss sub-encoding to TS1/0 on input line 282. +Package textcomp Info: Setting cmtt sub-encoding to TS1/0 on input line 283. +Package textcomp Info: Setting cmvtt sub-encoding to TS1/0 on input line 284. +Package textcomp Info: Setting cmbr sub-encoding to TS1/0 on input line 285. +Package textcomp Info: Setting cmtl sub-encoding to TS1/0 on input line 286. +Package textcomp Info: Setting ccr sub-encoding to TS1/0 on input line 287. +Package textcomp Info: Setting ptm sub-encoding to TS1/4 on input line 288. +Package textcomp Info: Setting pcr sub-encoding to TS1/4 on input line 289. +Package textcomp Info: Setting phv sub-encoding to TS1/4 on input line 290. +Package textcomp Info: Setting ppl sub-encoding to TS1/3 on input line 291. +Package textcomp Info: Setting pag sub-encoding to TS1/4 on input line 292. +Package textcomp Info: Setting pbk sub-encoding to TS1/4 on input line 293. +Package textcomp Info: Setting pnc sub-encoding to TS1/4 on input line 294. +Package textcomp Info: Setting pzc sub-encoding to TS1/4 on input line 295. +Package textcomp Info: Setting bch sub-encoding to TS1/4 on input line 296. +Package textcomp Info: Setting put sub-encoding to TS1/5 on input line 297. +Package textcomp Info: Setting uag sub-encoding to TS1/5 on input line 298. +Package textcomp Info: Setting ugq sub-encoding to TS1/5 on input line 299. +Package textcomp Info: Setting ul8 sub-encoding to TS1/4 on input line 300. +Package textcomp Info: Setting ul9 sub-encoding to TS1/4 on input line 301. +Package textcomp Info: Setting augie sub-encoding to TS1/5 on input line 302. +Package textcomp Info: Setting dayrom sub-encoding to TS1/3 on input line 303. +Package textcomp Info: Setting dayroms sub-encoding to TS1/3 on input line 304. + +Package textcomp Info: Setting pxr sub-encoding to TS1/0 on input line 305. +Package textcomp Info: Setting pxss sub-encoding to TS1/0 on input line 306. +Package textcomp Info: Setting pxtt sub-encoding to TS1/0 on input line 307. +Package textcomp Info: Setting txr sub-encoding to TS1/0 on input line 308. +Package textcomp Info: Setting txss sub-encoding to TS1/0 on input line 309. +Package textcomp Info: Setting txtt sub-encoding to TS1/0 on input line 310. +Package textcomp Info: Setting futs sub-encoding to TS1/4 on input line 311. +Package textcomp Info: Setting futx sub-encoding to TS1/4 on input line 312. +Package textcomp Info: Setting futj sub-encoding to TS1/4 on input line 313. +Package textcomp Info: Setting hlh sub-encoding to TS1/3 on input line 314. +Package textcomp Info: Setting hls sub-encoding to TS1/3 on input line 315. +Package textcomp Info: Setting hlst sub-encoding to TS1/3 on input line 316. +Package textcomp Info: Setting hlct sub-encoding to TS1/5 on input line 317. +Package textcomp Info: Setting hlx sub-encoding to TS1/5 on input line 318. +Package textcomp Info: Setting hlce sub-encoding to TS1/5 on input line 319. +Package textcomp Info: Setting hlcn sub-encoding to TS1/5 on input line 320. +Package textcomp Info: Setting hlcw sub-encoding to TS1/5 on input line 321. +Package textcomp Info: Setting hlcf sub-encoding to TS1/5 on input line 322. +Package textcomp Info: Setting pplx sub-encoding to TS1/3 on input line 323. +Package textcomp Info: Setting pplj sub-encoding to TS1/3 on input line 324. +Package textcomp Info: Setting ptmx sub-encoding to TS1/4 on input line 325. +Package textcomp Info: Setting ptmj sub-encoding to TS1/4 on input line 326. +) (/usr/share/texmf-tetex/tex/latex/tocbibind/tocbibind.sty +Package: tocbibind 2003/03/13 v1.5g extra ToC listings + +Package tocbibind Note: The document has chapter divisions. + + +Package tocbibind Note: Using chapter style headings, unless overridden. + +) (/usr/share/texmf-tetex/tex/latex/tools/enumerate.sty +Package: enumerate 1999/03/05 v3.00 enumerate extensions (DPC) +\@enLab=\toks19 +) (/usr/share/texmf-tetex/tex/latex/geometry/geometry.sty +Package: geometry 2002/07/08 v3.2 Page Geometry +\Gm@cnth=\count104 +\Gm@cntv=\count105 +\c@Gm@tempcnt=\count106 +\Gm@bindingoffset=\dimen116 +\Gm@wd@mp=\dimen117 +\Gm@odd@mp=\dimen118 +\Gm@even@mp=\dimen119 +\Gm@dimlist=\toks20 +(/usr/share/texmf-tetex/tex/latex/geometry/geometry.cfg)) (/usr/share/texmf-tet +ex/tex/latex/tools/calc.sty +Package: calc 1998/07/07 v4.1b Infix arithmetic (KKT,FJ) +\calc@Acount=\count107 +\calc@Bcount=\count108 +\calc@Adimen=\dimen120 +\calc@Bdimen=\dimen121 +\calc@Askip=\skip45 +\calc@Bskip=\skip46 +LaTeX Info: Redefining \setlength on input line 59. +LaTeX Info: Redefining \addtolength on input line 60. +\calc@denominator=\count109 +) +\@indexfile=\write5 +\openout5 = `images.idx'. + +Writing index file images.idx +(/usr/share/texmf-tetex/tex/latex/graphics/color.sty +Package: color 1999/02/16 v1.0i Standard LaTeX Color (DPC) +(/usr/share/texmf-tetex/tex/latex/graphics/color.cfg +File: color.cfg 2005/02/03 v1.3 color configuration of teTeX/TeXLive +) +Package color Info: Driver file: dvips.def on input line 125. +(/usr/share/texmf-tetex/tex/latex/graphics/dvips.def +File: dvips.def 1999/02/16 v3.0i Driver-dependant file (DPC,SPQR) +) (/usr/share/texmf-tetex/tex/latex/graphics/dvipsnam.def +File: dvipsnam.def 1999/02/16 v3.0i Driver-dependant file (DPC,SPQR) +)) +\sizebox=\box27 +\lthtmlwrite=\write6 +(./images.aux) +\openout1 = `images.aux'. + +LaTeX Font Info: Checking defaults for OML/cmm/m/it on input line 198. +LaTeX Font Info: ... okay on input line 198. +LaTeX Font Info: Checking defaults for T1/cmr/m/n on input line 198. +LaTeX Font Info: ... okay on input line 198. +LaTeX Font Info: Checking defaults for OT1/cmr/m/n on input line 198. +LaTeX Font Info: ... okay on input line 198. +LaTeX Font Info: Checking defaults for OMS/cmsy/m/n on input line 198. +LaTeX Font Info: ... okay on input line 198. +LaTeX Font Info: Checking defaults for OMX/cmex/m/n on input line 198. +LaTeX Font Info: ... okay on input line 198. +LaTeX Font Info: Checking defaults for U/cmr/m/n on input line 198. +LaTeX Font Info: ... okay on input line 198. +LaTeX Font Info: Checking defaults for PD1/pdf/m/n on input line 198. +LaTeX Font Info: ... okay on input line 198. +LaTeX Font Info: Checking defaults for TS1/cmr/m/n on input line 198. +LaTeX Font Info: Try loading font information for TS1+cmr on input line 198. + +(/usr/share/texmf-tetex/tex/latex/base/ts1cmr.fd +File: ts1cmr.fd 1999/05/25 v2.5h Standard LaTeX font definitions +) +LaTeX Font Info: ... okay on input line 198. +Package hyperref Info: Link coloring OFF on input line 198. +(/usr/share/texmf-tetex/tex/latex/hyperref/nameref.sty +Package: nameref 2003/12/03 v2.21 Cross-referencing by name of section +\c@section@level=\count110 +) +LaTeX Info: Redefining \ref on input line 198. +LaTeX Info: Redefining \pageref on input line 198. +\openout3 = `images.out'. + +-------------------- Geometry parameters +paper: a4paper +landscape: -- +twocolumn: -- +twoside: -- +asymmetric: -- +h-parts: 89.62709pt, 0.7\paperwidth , 89.6271pt (default) +v-parts: 101.40665pt, 0.7\paperheight , 152.11pt (default) +hmarginratio: 1:1 +vmarginratio: 2:3 +lines: -- +heightrounded: -- +bindingoffset: 0.0pt +truedimen: -- +includehead: -- +includefoot: -- +includemp: -- +driver: +-------------------- Page layout dimensions and switches +\paperwidth 597.50787pt +\paperheight 845.04684pt +\textwidth 349.0pt +\textheight 650.66685pt +\oddsidemargin 17.3571pt +\evensidemargin 17.3571pt +\topmargin 0.0pt +\headheight 0.0pt +\headsep 0.0pt +\footskip 0.0pt +\marginparwidth 71.0pt +\marginparsep 10.0pt +\columnsep 10.0pt +\skip\footins 10.0pt plus 4.0pt minus 2.0pt +\hoffset 0.0pt +\voffset 0.0pt +\mag 1000 + +(1in=72.27pt, 1cm=28.45pt) +----------------------- + +latex2htmlLength hsize=349.0pt + +latex2htmlLength vsize=650.66685pt + +latex2htmlLength hoffset=0.0pt + +latex2htmlLength voffset=0.0pt + +latex2htmlLength topmargin=0.0pt + +latex2htmlLength topskip=0.00003pt + +latex2htmlLength headheight=0.0pt + +latex2htmlLength headsep=0.0pt + +latex2htmlLength parskip=6.8pt plus 1.36008pt minus 1.36008pt + +latex2htmlLength oddsidemargin=17.3571pt + +latex2htmlLength evensidemargin=17.3571pt + +LaTeX Font Info: External font `cmex10' loaded for size +(Font) <10.95> on input line 235. +LaTeX Font Info: External font `cmex10' loaded for size +(Font) <8> on input line 235. +LaTeX Font Info: External font `cmex10' loaded for size +(Font) <6> on input line 235. +l2hSize :tex2html_wrap_inline1028:7.0626pt::0.0pt::7.1201pt. +[1 + + + +] +l2hSize :tex2html_wrap_inline1032:7.48248pt::0.0pt::48.86426pt. +[2 + + +] +l2hSize :tex2html_wrap_inline1981:8.7125pt::8.7125pt::23.6642pt. +[3 + + +] +l2hSize :tex2html_wrap_inline6076:7.5626pt::7.5626pt::14.53923pt. +[4 + + +] +l2hSize :tex2html_wrap_inline6080:7.5626pt::7.5626pt::14.53923pt. +[5 + + +] +l2hSize :tex2html_wrap_inline9064:8.7125pt::8.7125pt::86.9574pt. +[6 + + +] (./images.aux) ) +Here is how much of TeX's memory you used: + 4428 strings out of 94501 + 62627 string characters out of 1175793 + 121207 words of memory out of 1000000 + 7572 multiletter control sequences out of 10000+50000 + 7874 words of font info for 24 fonts, out of 500000 for 2000 + 580 hyphenation exceptions out of 8191 + 28i,5n,21p,200b,182s stack positions out of 1500i,500n,5000p,200000b,5000s + +Output written on images.dvi (6 pages, 3608 bytes). diff --git a/doc/ionconf/images.out b/doc/ionconf/images.out new file mode 100644 index 0000000..e69de29 diff --git a/doc/ionconf/images.pl b/doc/ionconf/images.pl new file mode 100644 index 0000000..6d0ccb4 --- /dev/null +++ b/doc/ionconf/images.pl @@ -0,0 +1,6 @@ +# LaTeX2HTML 2002-2-1 (1.71) +# Associate images original text with physical files. + + +1; + diff --git a/doc/ionconf/images.tex b/doc/ionconf/images.tex new file mode 100644 index 0000000..d950261 --- /dev/null +++ b/doc/ionconf/images.tex @@ -0,0 +1,371 @@ +\batchmode +\documentclass[english,a4paper,11pt,oldtoc,mctitle]{rapport3} +\RequirePackage{ifthen} + + +\usepackage{babel} +\usepackage[latin1]{inputenc} +\usepackage[dvipdfm]{hyperref} +\usepackage{ae} +\usepackage{url} +\usepackage{html} +\usepackage{makeidx} +\usepackage{tabularx} +\usepackage{textcomp} +\usepackage[nottoc]{tocbibind} +\usepackage{enumerate} +\usepackage[a4paper]{geometry} +\usepackage{calc} + + +\urlstyle{rm}% +\providecommand{\centerurl}[1]{\begin{center}\url{#1}\end{center}} + +% +\providecommand{\note}[1]{\color{red}**#1**}% +\providecommand{\file}[1]{\emph{#1}}% +\providecommand{\type}[1]{#1}% +\providecommand{\spec}[1]{#1}% +\providecommand{\indextype}[1]{\index{#1@#1}} + +% +\providecommand{\key}[1]{\textbf{#1}}% +\providecommand{\code}[1]{\texttt{#1}}% +\providecommand{\var}[1]{\texttt{#1}}% +\providecommand{\command}[1]{\texttt{#1}} + +% +\providecommand{\hyperlabel}[1]{\label{#1}}% +\providecommand{\fnref}[1]{\htmlref{\texttt{#1}}{fn:#1}}% +\providecommand{\fnrefx}[2]{\htmlref{\texttt{#2}}{fn:#1.#2}}% +\providecommand{\myhref}[2]{\htmlref{#2}{#1}} + +% +\providecommand{\tabhead}[1]{\hline #1 \\\hline} + + + +% +\newenvironment{function}{ + \begin{description} +}{ + \end{description} +} + +% +\providecommand{\synopsis}[1]{ + \item[Synopsis:] \texttt{#1} +}% +\providecommand{\funcname}[1]{ + \item[Function:] \texttt{#1} +}% +\providecommand{\hookname}[1]{ + \item[Hook name:] \label{#1}\texttt{#1} +}% +\providecommand{\hookparams}[1]{ + \item[Parameters:] \texttt{#1} +} + +% +\newenvironment{funcdesc}{ + \item[Description:] +}{} + + + +% +\providecommand{\xchapter}[1]{\chapter{#1}}% +\providecommand{\xsection}[1]{\section{#1}}% +\providecommand{\xsectionstar}[1]{\section*{#1}}% +\providecommand{\xsubsection}[1]{\subsection{#1}} + + +\title{Configuring and extending Ion3 with Lua} +\author{Tuomo Valkonen \\tuomov at iki.fi} +\date{2006-12-23} + + +\makeindex + + + + +\usepackage[dvips]{color} + + +\pagecolor[gray]{.7} + +\usepackage[latin1]{inputenc} + + + +\makeatletter + +\makeatletter +\count@=\the\catcode`\_ \catcode`\_=8 +\newenvironment{tex2html_wrap}{}{}% +\catcode`\<=12\catcode`\_=\count@ +\newcommand{\providedcommand}[1]{\expandafter\providecommand\csname #1\endcsname}% +\newcommand{\renewedcommand}[1]{\expandafter\providecommand\csname #1\endcsname{}% + \expandafter\renewcommand\csname #1\endcsname}% +\newcommand{\newedenvironment}[1]{\newenvironment{#1}{}{}\renewenvironment{#1}}% +\let\newedcommand\renewedcommand +\let\renewedenvironment\newedenvironment +\makeatother +\let\mathon=$ +\let\mathoff=$ +\ifx\AtBeginDocument\undefined \newcommand{\AtBeginDocument}[1]{}\fi +\newbox\sizebox +\setlength{\hoffset}{0pt}\setlength{\voffset}{0pt} +\addtolength{\textheight}{\footskip}\setlength{\footskip}{0pt} +\addtolength{\textheight}{\topmargin}\setlength{\topmargin}{0pt} +\addtolength{\textheight}{\headheight}\setlength{\headheight}{0pt} +\addtolength{\textheight}{\headsep}\setlength{\headsep}{0pt} +\setlength{\textwidth}{349pt} +\newwrite\lthtmlwrite +\makeatletter +\let\realnormalsize=\normalsize +\global\topskip=2sp +\def\preveqno{}\let\real@float=\@float \let\realend@float=\end@float +\def\@float{\let\@savefreelist\@freelist\real@float} +\def\liih@math{\ifmmode$\else\bad@math\fi} +\def\end@float{\realend@float\global\let\@freelist\@savefreelist} +\let\real@dbflt=\@dbflt \let\end@dblfloat=\end@float +\let\@largefloatcheck=\relax +\let\if@boxedmulticols=\iftrue +\def\@dbflt{\let\@savefreelist\@freelist\real@dbflt} +\def\adjustnormalsize{\def\normalsize{\mathsurround=0pt \realnormalsize + \parindent=0pt\abovedisplayskip=0pt\belowdisplayskip=0pt}% + \def\phantompar{\csname par\endcsname}\normalsize}% +\def\lthtmltypeout#1{{\let\protect\string \immediate\write\lthtmlwrite{#1}}}% +\newcommand\lthtmlhboxmathA{\adjustnormalsize\setbox\sizebox=\hbox\bgroup\kern.05em }% +\newcommand\lthtmlhboxmathB{\adjustnormalsize\setbox\sizebox=\hbox to\hsize\bgroup\hfill }% +\newcommand\lthtmlvboxmathA{\adjustnormalsize\setbox\sizebox=\vbox\bgroup % + \let\ifinner=\iffalse \let\)\liih@math }% +\newcommand\lthtmlboxmathZ{\@next\next\@currlist{}{\def\next{\voidb@x}}% + \expandafter\box\next\egroup}% +\newcommand\lthtmlmathtype[1]{\gdef\lthtmlmathenv{#1}}% +\newcommand\lthtmllogmath{\dimen0\ht\sizebox \advance\dimen0\dp\sizebox + \ifdim\dimen0>.95\vsize + \lthtmltypeout{% +*** image for \lthtmlmathenv\space is too tall at \the\dimen0, reducing to .95 vsize ***}% + \ht\sizebox.95\vsize \dp\sizebox\z@ \fi + \lthtmltypeout{l2hSize % +:\lthtmlmathenv:\the\ht\sizebox::\the\dp\sizebox::\the\wd\sizebox.\preveqno}}% +\newcommand\lthtmlfigureA[1]{\let\@savefreelist\@freelist + \lthtmlmathtype{#1}\lthtmlvboxmathA}% +\newcommand\lthtmlpictureA{\bgroup\catcode`\_=8 \lthtmlpictureB}% +\newcommand\lthtmlpictureB[1]{\lthtmlmathtype{#1}\egroup + \let\@savefreelist\@freelist \lthtmlhboxmathB}% +\newcommand\lthtmlpictureZ[1]{\hfill\lthtmlfigureZ}% +\newcommand\lthtmlfigureZ{\lthtmlboxmathZ\lthtmllogmath\copy\sizebox + \global\let\@freelist\@savefreelist}% +\newcommand\lthtmldisplayA{\bgroup\catcode`\_=8 \lthtmldisplayAi}% +\newcommand\lthtmldisplayAi[1]{\lthtmlmathtype{#1}\egroup\lthtmlvboxmathA}% +\newcommand\lthtmldisplayB[1]{\edef\preveqno{(\theequation)}% + \lthtmldisplayA{#1}\let\@eqnnum\relax}% +\newcommand\lthtmldisplayZ{\lthtmlboxmathZ\lthtmllogmath\lthtmlsetmath}% +\newcommand\lthtmlinlinemathA{\bgroup\catcode`\_=8 \lthtmlinlinemathB} +\newcommand\lthtmlinlinemathB[1]{\lthtmlmathtype{#1}\egroup\lthtmlhboxmathA + \vrule height1.5ex width0pt }% +\newcommand\lthtmlinlineA{\bgroup\catcode`\_=8 \lthtmlinlineB}% +\newcommand\lthtmlinlineB[1]{\lthtmlmathtype{#1}\egroup\lthtmlhboxmathA}% +\newcommand\lthtmlinlineZ{\egroup\expandafter\ifdim\dp\sizebox>0pt % + \expandafter\centerinlinemath\fi\lthtmllogmath\lthtmlsetinline} +\newcommand\lthtmlinlinemathZ{\egroup\expandafter\ifdim\dp\sizebox>0pt % + \expandafter\centerinlinemath\fi\lthtmllogmath\lthtmlsetmath} +\newcommand\lthtmlindisplaymathZ{\egroup % + \centerinlinemath\lthtmllogmath\lthtmlsetmath} +\def\lthtmlsetinline{\hbox{\vrule width.1em \vtop{\vbox{% + \kern.1em\copy\sizebox}\ifdim\dp\sizebox>0pt\kern.1em\else\kern.3pt\fi + \ifdim\hsize>\wd\sizebox \hrule depth1pt\fi}}} +\def\lthtmlsetmath{\hbox{\vrule width.1em\kern-.05em\vtop{\vbox{% + \kern.1em\kern0.8 pt\hbox{\hglue.17em\copy\sizebox\hglue0.8 pt}}\kern.3pt% + \ifdim\dp\sizebox>0pt\kern.1em\fi \kern0.8 pt% + \ifdim\hsize>\wd\sizebox \hrule depth1pt\fi}}} +\def\centerinlinemath{% + \dimen1=\ifdim\ht\sizebox<\dp\sizebox \dp\sizebox\else\ht\sizebox\fi + \advance\dimen1by.5pt \vrule width0pt height\dimen1 depth\dimen1 + \dp\sizebox=\dimen1\ht\sizebox=\dimen1\relax} + +\def\lthtmlcheckvsize{\ifdim\ht\sizebox<\vsize + \ifdim\wd\sizebox<\hsize\expandafter\hfill\fi \expandafter\vfill + \else\expandafter\vss\fi}% +\providecommand{\selectlanguage}[1]{}% +\makeatletter \tracingstats = 1 + + +\begin{document} +\pagestyle{empty}\thispagestyle{empty}\lthtmltypeout{}% +\lthtmltypeout{latex2htmlLength hsize=\the\hsize}\lthtmltypeout{}% +\lthtmltypeout{latex2htmlLength vsize=\the\vsize}\lthtmltypeout{}% +\lthtmltypeout{latex2htmlLength hoffset=\the\hoffset}\lthtmltypeout{}% +\lthtmltypeout{latex2htmlLength voffset=\the\voffset}\lthtmltypeout{}% +\lthtmltypeout{latex2htmlLength topmargin=\the\topmargin}\lthtmltypeout{}% +\lthtmltypeout{latex2htmlLength topskip=\the\topskip}\lthtmltypeout{}% +\lthtmltypeout{latex2htmlLength headheight=\the\headheight}\lthtmltypeout{}% +\lthtmltypeout{latex2htmlLength headsep=\the\headsep}\lthtmltypeout{}% +\lthtmltypeout{latex2htmlLength parskip=\the\parskip}\lthtmltypeout{}% +\lthtmltypeout{latex2htmlLength oddsidemargin=\the\oddsidemargin}\lthtmltypeout{}% +\makeatletter +\if@twoside\lthtmltypeout{latex2htmlLength evensidemargin=\the\evensidemargin}% +\else\lthtmltypeout{latex2htmlLength evensidemargin=\the\oddsidemargin}\fi% +\lthtmltypeout{}% +\makeatother +\setcounter{page}{1} +\onecolumn + +% !!! IMAGES START HERE !!! + +\stepcounter{chapter} +\stepcounter{chapter} +\stepcounter{section} +\stepcounter{section} +\stepcounter{subsection} +\stepcounter{subsection} +\stepcounter{subsubsection} +\stepcounter{subsubsection} +\stepcounter{subsection} +\stepcounter{chapter} +\stepcounter{section} +\stepcounter{section} +\stepcounter{section} +{\newpage\clearpage +\lthtmlinlinemathA{tex2html_wrap_inline1028}% +$n$% +\lthtmlinlinemathZ +\lthtmlcheckvsize\clearpage} + +\stepcounter{subsection} +\stepcounter{subsection} +\stepcounter{subsection} +\stepcounter{subsection} +\stepcounter{subsection} +{\newpage\clearpage +\lthtmlinlinemathA{tex2html_wrap_inline1032}% +$N=1{\ldots} 5$% +\lthtmlinlinemathZ +\lthtmlcheckvsize\clearpage} + +\stepcounter{subsection} +\stepcounter{subsection} +\stepcounter{section} +\stepcounter{subsection} +\stepcounter{subsection} +\stepcounter{subsection} +\stepcounter{subsection} +\stepcounter{section} + + +\newedenvironment{winprop}[2]{ + + \begin{description} +% + \item[Winprop:] \texttt{#1} (#2) + \item[Description:] +} +{ + + \end{description} +}% + +\stepcounter{subsection} +\stepcounter{subsection} +\stepcounter{subsection} +\stepcounter{subsubsection} +\stepcounter{subsubsection} +\stepcounter{subsubsection} +\stepcounter{chapter} +\stepcounter{section} +\stepcounter{subsection} +\stepcounter{subsubsection} +\stepcounter{subsubsection} +\stepcounter{subsubsection} +\stepcounter{section} +\stepcounter{subsection} +\stepcounter{subsection} +\stepcounter{subsubsection} +\stepcounter{subsubsection} +\stepcounter{subsubsection} +\stepcounter{subsubsection} +\stepcounter{subsubsection} +\stepcounter{subsection} +\stepcounter{section} +\stepcounter{subsection} +{\newpage\clearpage +\lthtmlinlinemathA{tex2html_wrap_inline1981}% +$(0, 1]$% +\lthtmlinlinemathZ +\lthtmlcheckvsize\clearpage} + +\stepcounter{subsection} +\stepcounter{chapter} +\stepcounter{section} +\stepcounter{section} +\stepcounter{subsection} +\stepcounter{subsection} +\stepcounter{section} +\stepcounter{section} +\stepcounter{chapter} +\stepcounter{section} +{\newpage\clearpage +\lthtmlinlinemathA{tex2html_wrap_inline6076}% +$-1$% +\lthtmlinlinemathZ +\lthtmlcheckvsize\clearpage} + +\stepcounter{subsection} +\stepcounter{subsection} +\stepcounter{subsection} +\stepcounter{subsection} +\stepcounter{subsection} +\stepcounter{subsection} +\stepcounter{subsection} +{\newpage\clearpage +\lthtmlinlinemathA{tex2html_wrap_inline6080}% +$-2$% +\lthtmlinlinemathZ +\lthtmlcheckvsize\clearpage} + +\stepcounter{subsection} +\stepcounter{subsection} +\stepcounter{subsection} +\stepcounter{subsection} +\stepcounter{subsection} +\stepcounter{subsection} +\stepcounter{subsection} +\stepcounter{subsection} +\stepcounter{subsection} +\stepcounter{section} +\stepcounter{subsection} +\stepcounter{subsection} +\stepcounter{subsection} +\stepcounter{subsection} +\stepcounter{subsection} +\stepcounter{section} +\stepcounter{subsection} +\stepcounter{subsection} +\stepcounter{subsection} +\stepcounter{section} +\stepcounter{subsection} +\stepcounter{section} +\stepcounter{subsection} +{\newpage\clearpage +\lthtmlinlinemathA{tex2html_wrap_inline9064}% +$\{t,m,b\}\times\{t,c,b\}$% +\lthtmlinlinemathZ +\lthtmlcheckvsize\clearpage} + +\stepcounter{section} +\stepcounter{section} +\stepcounter{section} +\appendix +\stepcounter{chapter} +\addtocounter{enumi}{-1} +\stepcounter{chapter} + +% +\providecommand{\fnlisti}[1]{\htmlref{\texttt{#1}}{fn:#1}\\}% + + +\end{document} diff --git a/doc/ionconf/img1.png b/doc/ionconf/img1.png new file mode 100644 index 0000000..e69de29 diff --git a/doc/ionconf/img2.png b/doc/ionconf/img2.png new file mode 100644 index 0000000..e69de29 diff --git a/doc/ionconf/img3.png b/doc/ionconf/img3.png new file mode 100644 index 0000000..e69de29 diff --git a/doc/ionconf/img4.png b/doc/ionconf/img4.png new file mode 100644 index 0000000..e69de29 diff --git a/doc/ionconf/img5.png b/doc/ionconf/img5.png new file mode 100644 index 0000000..e69de29 diff --git a/doc/ionconf/img6.png b/doc/ionconf/img6.png new file mode 100644 index 0000000..e69de29 diff --git a/doc/ionconf/index.html b/doc/ionconf/index.html new file mode 100644 index 0000000..7a0a20d --- /dev/null +++ b/doc/ionconf/index.html @@ -0,0 +1,341 @@ + + + + + +Configuring and extending Ion3 with Lua + + + + + + + + + + + + + + + + +

    + + +

    +

    Configuring and extending Ion3 with Lua

    +
    + +

    Tuomo Valkonen

    +

    tuomov at iki.fi

    +

    2006-12-23

    +
    + +

    +Configuring and extending Ion3 with Lua +
    +Copyright © 2003-2005 Tuomo Valkonen. + +

    +This document is free; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +

    +This document is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +chapter entitled ''GNU General Public License'' for more details. + +

    +


    + + + + + +

    + + + diff --git a/doc/ionconf/index.png b/doc/ionconf/index.png new file mode 100644 index 0000000000000000000000000000000000000000..698f09cae4df23447bf608d2a585bd4d5972c27d GIT binary patch literal 246 zcmeAS@N?(olHy`uVBq!ia0vp^+CVJ9#LU3Jn0f2YF(8L0z$e6Y|Ni~?`T5VEKaXZ$ zILr2UCs3TRB*-tA!Qt7BG$3b&r;B4q#hlW9TP`L;fmV5yrw%$cCme3Ee&-OYn%|JV z;eHbLNzG*&z8F|Gv`(HGd$F~)u_nK+LS=<8R{%>WqskW{Rg3J}7qSOsCi?q75nH?M zE^{77+Z<+1r-r1Nch|4lthS=d{J1PfXUU1#ym22LZu_2=vAt}Pd}!6<{pzf1?Y4jM u^4(GWO*wx3{4#&$%6k3J$8QJwOy|GN;;5Q**1ZVmMg~t;KbLh*2~7aKlwD!~ literal 0 HcmV?d00001 diff --git a/doc/ionconf/internals.pl b/doc/ionconf/internals.pl new file mode 100644 index 0000000..d2d476e --- /dev/null +++ b/doc/ionconf/internals.pl @@ -0,0 +1,1306 @@ +# LaTeX2HTML 2002-2-1 (1.71) +# Associate internals original text with physical files. + + +$key = q/sec:queryref/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.version/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.mx_nth/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:gr.select_engine/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.x_get_window_property/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.context/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.query_exec/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.goto_first/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.insstr/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WClientWin.quote_next/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.query_renameworkspace/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WFrame.is_shaded/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.mx_list/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.goto_activity/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WTiling.farthest/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.create_ws/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.finish/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.bskip_word/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.mark/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.switch_prev/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WTiling.unsplit_at/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.dec_index/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.current/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/sec:hookref/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/chap:config/; +$ref_files{$key} = "$dir".q|node4.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WTiling.split/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.kpress/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.is_mapped/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WClientWin.is_fullscreen/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.goto_prev_screen/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.transpose_words/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/sec:ioncoreref/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.defbindings/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.get/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WWindow.p_resize/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WComplProxy.set_completions/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.shutdown/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.getbindings/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.query/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/sec:styles/; +$ref_files{$key} = "$dir".q|node5.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.get_stdisp/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.defmenu/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.set_get_winprop_fn/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fig:borders/; +$ref_files{$key} = "$dir".q|node5.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.navi_next/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:de.reset/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.history_get/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.lookup_script/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WScreen.id/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WFrame.set_mode/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMoveresMode.resize/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMenu.select_nth/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WFrame.mode/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.warn_traced/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMenu.select_next/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/sec:modules/; +$ref_files{$key} = "$dir".q|node3.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WSplitSplit.tl/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_tiling.mkbottom/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.defctxmenu/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.set_selection/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WInput.scrollup/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.set_name_exact/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.switch_nth/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.prev_completion/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.exec_on/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.menuentry/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.forward/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_menu.menu/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WDock.set/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_sp.set_shown/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.find_screen_id/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/sec:bindings/; +$ref_files{$key} = "$dir".q|node4.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WInfoWin.set_text/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.snapshot/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/region_do_warp_alt/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.resign/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/clientwin_unmapped_hook/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.write_savefile/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/panews_make_placement_alt/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/sec:exports/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/sec:hooks/; +$ref_files{$key} = "$dir".q|node6.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WWindow.xid/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.activity_list/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WClientWin.get_ident/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fig:classhierarchy/; +$ref_files{$key} = "$dir".q|node3.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_sp.set_shown_on/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WTiling.set_floating/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.history_prev/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/sec:binddef/; +$ref_files{$key} = "$dir".q|node4.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.kill_to_eol/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.query_editfile/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:gr.read_config/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WTiling.split_at/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WFrame.p_tabdrag/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.screen_of/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.switch_next/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WGroup.bottom/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.history_search/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WTiling.node_of/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRootWin.current_scr/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.navi_first/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.kill_to_bol/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.load_module/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/sec:menuref/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/sec:menudisp/; +$ref_files{$key} = "$dir".q|node4.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WFrame.maximize_vert/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WSplitInner.current/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WDock.attach/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.set_tagged/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/app:fullhierarchy/; +$ref_files{$key} = "$dir".q|node9.html|; +$noresave{$key} = "$nosave"; + +$key = q/frame_managed_changed_hook/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/chap:tricks/; +$ref_files{$key} = "$dir".q|node6.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.get_savefile/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.history_push/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.get/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.lookup_clientwin/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.restart/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.x_set_text_property/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/sec:winprops/; +$ref_files{$key} = "$dir".q|node4.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WSplit.parent/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.getctxmenu/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/region_inactivated_hook/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WFrame.maximize_horiz/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WWindow.p_move/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/sec:objects/; +$ref_files{$key} = "$dir".q|node3.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WScreen.set_managed_offset/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.set/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.get_dir_for/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.point/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMenu.select_prev/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.goto_next/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/sec:engines/; +$ref_files{$key} = "$dir".q|node5.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_menu.pmenu/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_dock.set_floating_shown_on/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.query_ssh/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.x_get_atom_name/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.query_workspace/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMenu.typeahead_clear/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:table.copy/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WSplit.rqgeom/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.tags_first/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.set_mark/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/screen_managed_changed_hook/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.mx_count/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.attach/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fig:managership/; +$ref_files{$key} = "$dir".q|node3.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.query_menu/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMoveresMode.move/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_tiling.get/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WTiling.flip_at/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.lookup_region/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.defshortening/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.refresh_stylelist/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.show_about_ion/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.inc_index/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.x_delete_property/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.rqclose/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.set_activity/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.query_runfile/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.set_context/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.show_tree/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.contents/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.exec/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.region_list/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.mx_current/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.message/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.compile_cmd/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/sec:deref/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.bol/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.submenu/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.attach_tagged/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WSplitSplit.br/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.managed_list/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:string.shell_safe/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.query_restart/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WTiling.managed_list/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_tiling.set/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.backspace/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:de.defstyle/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WGroupWS.attach_framed/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/sec:dockref/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WTiling.set_floating_at/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.warn/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.current/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.rqclose_propagate/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.clear_mark/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/tiling_placement_alt/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.warn/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.begin_kbresize/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/sec:classesrolesinstances/; +$ref_files{$key} = "$dir".q|node4.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.is_tagged/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.next_completion/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.set_hidden/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMenu.cancel/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WClientWin.nudge/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.goto_nth_screen/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/clientwin_mapped_hook/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WClientWin.set_fullscreen/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.paste/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WClientWin.xid/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.name/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.x_get_text_property/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.query_attachclient/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:table.append/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.mclick/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.set_index/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.history_clear/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WFrame.p_switch_tab/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.x_intern_atom/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMoveresMode.finish/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.query_gotoclient/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/ioncore_deinit_hook/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WSplit.geom/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.goto_previous/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.find_manager/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.get_paths/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/sec:walkthrough/; +$ref_files{$key} = "$dir".q|node4.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WFrame.set_numbers/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMoveresMode.cancel/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.submap/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.is_active/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.bkill_word/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.attach_new/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WGroup.attach/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.mdblclick/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WSplitSplit.flip/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WTiling.split_top/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/region_activity_hook/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.progname/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.popen_completions/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.defwinprop/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.popen_bgread/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.mdrag/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WClientWin.kill/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WSplit.transpose/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/ioncore_snapshot_hook/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.clear_tags/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:table.map/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WSplitSplit.dir/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WFrame.set_shaded/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.set_stdisp/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.manager/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.x_change_property/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WInput.cancel/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.parent/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMenu.finish/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.back/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.TR/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/sec:menus/; +$ref_files{$key} = "$dir".q|node4.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.query_renameframe/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.geom/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.kpress_wait/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/clientwin_do_manage_alt/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.rootwin_of/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WTiling.transpose_at/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/ioncore_sigchld_hook/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.match_winprop_name/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WTiling.nextto/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WDock.get/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:de.substyle/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.copy/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.is_histcompl/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.getmenu/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.is_i18n/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:gr.refresh/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.rqorder/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_menu.grabmenu/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WSplitRegion.reg/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.size_hints/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.delete/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.getwinprop/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.tagged_list/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.request_selection/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.query_man/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.cut/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.goto/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.goto_next_screen/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.set_paths/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/sec:conffiles/; +$ref_files{$key} = "$dir".q|node4.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.clientwin_list/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.aboutmsg/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/chap:prelim/; +$ref_files{$key} = "$dir".q|node3.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WGroup.managed_list/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fig:parentship/; +$ref_files{$key} = "$dir".q|node3.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.read_savefile/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.restart_other/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.is_activity/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.rqgeom/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WInput.scrolldown/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/region_activated_hook/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.history_table/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/sec:defaultde/; +$ref_files{$key} = "$dir".q|node5.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.set/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.mpress/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/sec:tilingref/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_tiling.detach/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.set_name/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:table.join/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.activity_first/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/sec:spref/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.is_hidden/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_menu.get/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.query_yesno/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.history_next/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.eol/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.defcmd/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.bdoc/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.chdir_for/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.query_lua/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:export/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/chap:gr/; +$ref_files{$key} = "$dir".q|node5.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_menu.set/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.kill_word/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:de.defstyle_rootwin/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/ioncore_post_layout_setup_hook/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:table.icat/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.kill_line/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WDock.resize/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.query_shutdown/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.get_index/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.skip_word/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.complete/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WGroup.attach_new/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.transpose_chars/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WTiling.split_tree/; +$ref_files{$key} = "$dir".q|node7.html|; +$noresave{$key} = "$nosave"; + +1; + diff --git a/doc/ionconf/ionconf.css b/doc/ionconf/ionconf.css new file mode 100644 index 0000000..f0cd026 --- /dev/null +++ b/doc/ionconf/ionconf.css @@ -0,0 +1,38 @@ +/* Century Schoolbook font is very similar to Computer Modern Math: cmmi */ +.MATH { font-family: "Century Schoolbook", serif; } +.MATH I { font-family: "Century Schoolbook", serif; font-style: italic } +.BOLDMATH { font-family: "Century Schoolbook", serif; font-weight: bold } + +/* implement both fixed-size and relative sizes */ +SMALL.XTINY { font-size : xx-small } +SMALL.TINY { font-size : x-small } +SMALL.SCRIPTSIZE { font-size : smaller } +SMALL.FOOTNOTESIZE { font-size : small } +SMALL.SMALL { } +BIG.LARGE { } +BIG.XLARGE { font-size : large } +BIG.XXLARGE { font-size : x-large } +BIG.HUGE { font-size : larger } +BIG.XHUGE { font-size : xx-large } + +/* heading styles */ +H1 { } +H2 { } +H3 { } +H4 { } +H5 { } + +/* mathematics styles */ +DIV.displaymath { } /* math displays */ +TD.eqno { } /* equation-number cells */ + + +/* document-specific styles come next */ +DIV.navigation { } +DIV.center { } +SPAN.sc { } +DIV.quote { } +PRE.preform { } +SPAN.textit { font-style: italic } +SPAN.arabic { } +SPAN.textbf { font-weight: bold } diff --git a/doc/ionconf/ionconf.html b/doc/ionconf/ionconf.html new file mode 100644 index 0000000..7a0a20d --- /dev/null +++ b/doc/ionconf/ionconf.html @@ -0,0 +1,341 @@ + + + + + +Configuring and extending Ion3 with Lua + + + + + + + + + + + + + + + + + + + +

    +

    Configuring and extending Ion3 with Lua

    +
    + +

    Tuomo Valkonen

    +

    tuomov at iki.fi

    +

    2006-12-23

    +
    + +

    +Configuring and extending Ion3 with Lua +
    +Copyright © 2003-2005 Tuomo Valkonen. + +

    +This document is free; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +

    +This document is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +chapter entitled ''GNU General Public License'' for more details. + +

    +


    + + + + + +

    + + + diff --git a/doc/ionconf/labels.pl b/doc/ionconf/labels.pl new file mode 100644 index 0000000..cbfa297 --- /dev/null +++ b/doc/ionconf/labels.pl @@ -0,0 +1,2613 @@ +# LaTeX2HTML 2002-2-1 (1.71) +# Associate labels original text with physical files. + + +$key = q/sec:queryref/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.version/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.mx_nth/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:gr.select_engine/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.x_get_window_property/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.context/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.query_exec/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.goto_first/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.insstr/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WClientWin.quote_next/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.query_renameworkspace/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WFrame.is_shaded/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.mx_list/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.goto_activity/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WTiling.farthest/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.create_ws/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.finish/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.bskip_word/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.mark/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.switch_prev/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WTiling.unsplit_at/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.dec_index/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.current/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/sec:hookref/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/chap:config/; +$external_labels{$key} = "$URL/" . q|node4.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WTiling.split/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.kpress/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.is_mapped/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WClientWin.is_fullscreen/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.goto_prev_screen/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.transpose_words/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/sec:ioncoreref/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.defbindings/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.get/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WWindow.p_resize/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WComplProxy.set_completions/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.shutdown/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.getbindings/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.query/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/sec:styles/; +$external_labels{$key} = "$URL/" . q|node5.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.get_stdisp/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.defmenu/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.set_get_winprop_fn/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fig:borders/; +$external_labels{$key} = "$URL/" . q|node5.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.navi_next/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:de.reset/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.history_get/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.lookup_script/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WScreen.id/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WFrame.set_mode/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMoveresMode.resize/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMenu.select_nth/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WFrame.mode/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.warn_traced/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMenu.select_next/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/sec:modules/; +$external_labels{$key} = "$URL/" . q|node3.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WSplitSplit.tl/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_tiling.mkbottom/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.defctxmenu/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.set_selection/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WInput.scrollup/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.set_name_exact/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.switch_nth/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.prev_completion/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.exec_on/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.menuentry/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.forward/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_menu.menu/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WDock.set/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_sp.set_shown/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.find_screen_id/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/sec:bindings/; +$external_labels{$key} = "$URL/" . q|node4.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WInfoWin.set_text/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.snapshot/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/region_do_warp_alt/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.resign/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/clientwin_unmapped_hook/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.write_savefile/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/panews_make_placement_alt/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/sec:exports/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/sec:hooks/; +$external_labels{$key} = "$URL/" . q|node6.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WWindow.xid/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.activity_list/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WClientWin.get_ident/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fig:classhierarchy/; +$external_labels{$key} = "$URL/" . q|node3.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_sp.set_shown_on/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WTiling.set_floating/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.history_prev/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/sec:binddef/; +$external_labels{$key} = "$URL/" . q|node4.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.kill_to_eol/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.query_editfile/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:gr.read_config/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WTiling.split_at/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WFrame.p_tabdrag/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.screen_of/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.switch_next/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WGroup.bottom/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.history_search/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WTiling.node_of/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRootWin.current_scr/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.navi_first/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.kill_to_bol/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.load_module/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/sec:menuref/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/sec:menudisp/; +$external_labels{$key} = "$URL/" . q|node4.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WFrame.maximize_vert/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WSplitInner.current/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WDock.attach/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.set_tagged/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/app:fullhierarchy/; +$external_labels{$key} = "$URL/" . q|node9.html|; +$noresave{$key} = "$nosave"; + +$key = q/frame_managed_changed_hook/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/chap:tricks/; +$external_labels{$key} = "$URL/" . q|node6.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.get_savefile/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.history_push/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.get/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.lookup_clientwin/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.restart/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.x_set_text_property/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/sec:winprops/; +$external_labels{$key} = "$URL/" . q|node4.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WSplit.parent/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.getctxmenu/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/region_inactivated_hook/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WFrame.maximize_horiz/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WWindow.p_move/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/sec:objects/; +$external_labels{$key} = "$URL/" . q|node3.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WScreen.set_managed_offset/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.set/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.get_dir_for/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.point/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMenu.select_prev/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.goto_next/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/sec:engines/; +$external_labels{$key} = "$URL/" . q|node5.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_menu.pmenu/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_dock.set_floating_shown_on/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.query_ssh/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.x_get_atom_name/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.query_workspace/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMenu.typeahead_clear/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:table.copy/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WSplit.rqgeom/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.tags_first/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.set_mark/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/screen_managed_changed_hook/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.mx_count/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.attach/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fig:managership/; +$external_labels{$key} = "$URL/" . q|node3.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.query_menu/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMoveresMode.move/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_tiling.get/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WTiling.flip_at/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.lookup_region/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.defshortening/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.refresh_stylelist/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.show_about_ion/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.inc_index/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.x_delete_property/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.rqclose/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.set_activity/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.query_runfile/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.set_context/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.show_tree/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.contents/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.exec/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.region_list/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.mx_current/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.message/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.compile_cmd/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/sec:deref/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.bol/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.submenu/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.attach_tagged/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WSplitSplit.br/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.managed_list/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:string.shell_safe/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.query_restart/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WTiling.managed_list/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_tiling.set/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.backspace/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:de.defstyle/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WGroupWS.attach_framed/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/sec:dockref/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WTiling.set_floating_at/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.warn/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.current/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.rqclose_propagate/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.clear_mark/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/tiling_placement_alt/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.warn/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.begin_kbresize/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/sec:classesrolesinstances/; +$external_labels{$key} = "$URL/" . q|node4.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.is_tagged/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.next_completion/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.set_hidden/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMenu.cancel/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WClientWin.nudge/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.goto_nth_screen/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/clientwin_mapped_hook/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WClientWin.set_fullscreen/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.paste/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WClientWin.xid/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.name/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.x_get_text_property/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.query_attachclient/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:table.append/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.mclick/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.set_index/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.history_clear/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WFrame.p_switch_tab/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.x_intern_atom/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMoveresMode.finish/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.query_gotoclient/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/ioncore_deinit_hook/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WSplit.geom/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.goto_previous/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.find_manager/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.get_paths/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/sec:walkthrough/; +$external_labels{$key} = "$URL/" . q|node4.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WFrame.set_numbers/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMoveresMode.cancel/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.submap/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.is_active/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.bkill_word/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.attach_new/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WGroup.attach/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.mdblclick/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WSplitSplit.flip/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WTiling.split_top/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/region_activity_hook/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.progname/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.popen_completions/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.defwinprop/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.popen_bgread/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.mdrag/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WClientWin.kill/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WSplit.transpose/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/ioncore_snapshot_hook/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.clear_tags/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:table.map/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WSplitSplit.dir/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WFrame.set_shaded/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.set_stdisp/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.manager/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.x_change_property/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WInput.cancel/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.parent/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMenu.finish/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.back/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.TR/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/sec:menus/; +$external_labels{$key} = "$URL/" . q|node4.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.query_renameframe/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.geom/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.kpress_wait/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/clientwin_do_manage_alt/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.rootwin_of/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WTiling.transpose_at/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/ioncore_sigchld_hook/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.match_winprop_name/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WTiling.nextto/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WDock.get/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:de.substyle/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.copy/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.is_histcompl/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.getmenu/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.is_i18n/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:gr.refresh/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.rqorder/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_menu.grabmenu/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WSplitRegion.reg/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.size_hints/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.delete/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.getwinprop/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.tagged_list/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.request_selection/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.query_man/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.cut/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.goto/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.goto_next_screen/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.set_paths/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/sec:conffiles/; +$external_labels{$key} = "$URL/" . q|node4.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.clientwin_list/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.aboutmsg/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/chap:prelim/; +$external_labels{$key} = "$URL/" . q|node3.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WGroup.managed_list/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fig:parentship/; +$external_labels{$key} = "$URL/" . q|node3.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.read_savefile/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.restart_other/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.is_activity/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.rqgeom/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WInput.scrolldown/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/region_activated_hook/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.history_table/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/sec:defaultde/; +$external_labels{$key} = "$URL/" . q|node5.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.set/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.mpress/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/sec:tilingref/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_tiling.detach/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.set_name/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:table.join/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.activity_first/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/sec:spref/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.is_hidden/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_menu.get/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.query_yesno/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.history_next/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.eol/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.defcmd/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.bdoc/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.chdir_for/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.query_lua/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:export/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/chap:gr/; +$external_labels{$key} = "$URL/" . q|node5.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_menu.set/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.kill_word/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:de.defstyle_rootwin/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/ioncore_post_layout_setup_hook/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:table.icat/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.kill_line/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WDock.resize/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.query_shutdown/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.get_index/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.skip_word/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.complete/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WGroup.attach_new/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.transpose_chars/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WTiling.split_tree/; +$external_labels{$key} = "$URL/" . q|node7.html|; +$noresave{$key} = "$nosave"; + +1; + + +# LaTeX2HTML 2002-2-1 (1.71) +# labels from external_latex_labels array. + + +$key = q/sec:queryref/; +$external_latex_labels{$key} = q|6.3|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.version/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.mx_nth/; +$external_latex_labels{$key} = q|6.1.7|; +$noresave{$key} = "$nosave"; + +$key = q/fn:gr.select_engine/; +$external_latex_labels{$key} = q|6.1.14|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.x_get_window_property/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.context/; +$external_latex_labels{$key} = q|6.3.2|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.query_exec/; +$external_latex_labels{$key} = q|6.3|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.goto_first/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.insstr/; +$external_latex_labels{$key} = q|6.3.2|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WClientWin.quote_next/; +$external_latex_labels{$key} = q|6.1.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.query_renameworkspace/; +$external_latex_labels{$key} = q|6.3|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WFrame.is_shaded/; +$external_latex_labels{$key} = q|6.1.2|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.mx_list/; +$external_latex_labels{$key} = q|6.1.7|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.goto_activity/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WTiling.farthest/; +$external_latex_labels{$key} = q|6.2.5|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.create_ws/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.finish/; +$external_latex_labels{$key} = q|6.3.2|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.bskip_word/; +$external_latex_labels{$key} = q|6.3.2|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.mark/; +$external_latex_labels{$key} = q|6.3.2|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.switch_prev/; +$external_latex_labels{$key} = q|6.1.7|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WTiling.unsplit_at/; +$external_latex_labels{$key} = q|6.2.5|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.dec_index/; +$external_latex_labels{$key} = q|6.1.7|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.current/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/sec:hookref/; +$external_latex_labels{$key} = q|6.8|; +$noresave{$key} = "$nosave"; + +$key = q/chap:config/; +$external_latex_labels{$key} = q|3|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WTiling.split/; +$external_latex_labels{$key} = q|6.2.5|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.kpress/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.is_mapped/; +$external_latex_labels{$key} = q|6.1.9|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WClientWin.is_fullscreen/; +$external_latex_labels{$key} = q|6.1.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.goto_prev_screen/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.transpose_words/; +$external_latex_labels{$key} = q|6.3.2|; +$noresave{$key} = "$nosave"; + +$key = q/sec:ioncoreref/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.defbindings/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.get/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WWindow.p_resize/; +$external_latex_labels{$key} = q|6.1.12|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WComplProxy.set_completions/; +$external_latex_labels{$key} = q|6.3.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.shutdown/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.getbindings/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.query/; +$external_latex_labels{$key} = q|6.3|; +$noresave{$key} = "$nosave"; + +$key = q/sec:styles/; +$external_latex_labels{$key} = q|4.1.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.get_stdisp/; +$external_latex_labels{$key} = q|6.1.7|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.defmenu/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.set_get_winprop_fn/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fig:borders/; +$external_latex_labels{$key} = q|4.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.navi_next/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:de.reset/; +$external_latex_labels{$key} = q|6.7|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.history_get/; +$external_latex_labels{$key} = q|6.3|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.lookup_script/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WScreen.id/; +$external_latex_labels{$key} = q|6.1.11|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WFrame.set_mode/; +$external_latex_labels{$key} = q|6.1.2|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMoveresMode.resize/; +$external_latex_labels{$key} = q|6.1.8|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMenu.select_nth/; +$external_latex_labels{$key} = q|6.4.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WFrame.mode/; +$external_latex_labels{$key} = q|6.1.2|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.warn_traced/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMenu.select_next/; +$external_latex_labels{$key} = q|6.4.1|; +$noresave{$key} = "$nosave"; + +$key = q/sec:modules/; +$external_latex_labels{$key} = q|2.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WSplitSplit.tl/; +$external_latex_labels{$key} = q|6.2.4|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_tiling.mkbottom/; +$external_latex_labels{$key} = q|6.2|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.defctxmenu/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.set_selection/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WInput.scrollup/; +$external_latex_labels{$key} = q|6.3.3|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.set_name_exact/; +$external_latex_labels{$key} = q|6.1.9|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.switch_nth/; +$external_latex_labels{$key} = q|6.1.7|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.prev_completion/; +$external_latex_labels{$key} = q|6.3.2|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.exec_on/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.menuentry/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.forward/; +$external_latex_labels{$key} = q|6.3.2|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_menu.menu/; +$external_latex_labels{$key} = q|6.4|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WDock.set/; +$external_latex_labels{$key} = q|6.5.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_sp.set_shown/; +$external_latex_labels{$key} = q|6.6|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.find_screen_id/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/sec:bindings/; +$external_latex_labels{$key} = q|3.3|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WInfoWin.set_text/; +$external_latex_labels{$key} = q|6.1.6|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.snapshot/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/region_do_warp_alt/; +$external_latex_labels{$key} = q|6.8|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.resign/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/clientwin_unmapped_hook/; +$external_latex_labels{$key} = q|6.8|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.write_savefile/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/panews_make_placement_alt/; +$external_latex_labels{$key} = q|6.8|; +$noresave{$key} = "$nosave"; + +$key = q/sec:exports/; +$external_latex_labels{$key} = q|6|; +$noresave{$key} = "$nosave"; + +$key = q/sec:hooks/; +$external_latex_labels{$key} = q|5.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WWindow.xid/; +$external_latex_labels{$key} = q|6.1.12|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.activity_list/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WClientWin.get_ident/; +$external_latex_labels{$key} = q|6.1.1|; +$noresave{$key} = "$nosave"; + +$key = q/fig:classhierarchy/; +$external_latex_labels{$key} = q|2.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_sp.set_shown_on/; +$external_latex_labels{$key} = q|6.6|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WTiling.set_floating/; +$external_latex_labels{$key} = q|6.2.5|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.history_prev/; +$external_latex_labels{$key} = q|6.3.2|; +$noresave{$key} = "$nosave"; + +$key = q/sec:binddef/; +$external_latex_labels{$key} = q|3.3.3|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.kill_to_eol/; +$external_latex_labels{$key} = q|6.3.2|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.query_editfile/; +$external_latex_labels{$key} = q|6.3|; +$noresave{$key} = "$nosave"; + +$key = q/fn:gr.read_config/; +$external_latex_labels{$key} = q|6.1.14|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WTiling.split_at/; +$external_latex_labels{$key} = q|6.2.5|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WFrame.p_tabdrag/; +$external_latex_labels{$key} = q|6.1.2|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.screen_of/; +$external_latex_labels{$key} = q|6.1.9|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.switch_next/; +$external_latex_labels{$key} = q|6.1.7|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WGroup.bottom/; +$external_latex_labels{$key} = q|6.1.3|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.history_search/; +$external_latex_labels{$key} = q|6.3|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WTiling.node_of/; +$external_latex_labels{$key} = q|6.2.5|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRootWin.current_scr/; +$external_latex_labels{$key} = q|6.1.10|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.navi_first/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.kill_to_bol/; +$external_latex_labels{$key} = q|6.3.2|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.load_module/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/sec:menuref/; +$external_latex_labels{$key} = q|6.4|; +$noresave{$key} = "$nosave"; + +$key = q/sec:menudisp/; +$external_latex_labels{$key} = q|3.4.4|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WFrame.maximize_vert/; +$external_latex_labels{$key} = q|6.1.2|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WSplitInner.current/; +$external_latex_labels{$key} = q|6.2.2|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WDock.attach/; +$external_latex_labels{$key} = q|6.5.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.set_tagged/; +$external_latex_labels{$key} = q|6.1.9|; +$noresave{$key} = "$nosave"; + +$key = q/app:fullhierarchy/; +$external_latex_labels{$key} = q|B|; +$noresave{$key} = "$nosave"; + +$key = q/frame_managed_changed_hook/; +$external_latex_labels{$key} = q|6.8|; +$noresave{$key} = "$nosave"; + +$key = q/chap:tricks/; +$external_latex_labels{$key} = q|5|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.get_savefile/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.history_push/; +$external_latex_labels{$key} = q|6.3|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.get/; +$external_latex_labels{$key} = q|6.3|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.lookup_clientwin/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.restart/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.x_set_text_property/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/sec:winprops/; +$external_latex_labels{$key} = q|3.5|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WSplit.parent/; +$external_latex_labels{$key} = q|6.2.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.getctxmenu/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/region_inactivated_hook/; +$external_latex_labels{$key} = q|6.8|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WFrame.maximize_horiz/; +$external_latex_labels{$key} = q|6.1.2|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WWindow.p_move/; +$external_latex_labels{$key} = q|6.1.12|; +$noresave{$key} = "$nosave"; + +$key = q/sec:objects/; +$external_latex_labels{$key} = q|2.2|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WScreen.set_managed_offset/; +$external_latex_labels{$key} = q|6.1.11|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.set/; +$external_latex_labels{$key} = q|6.3|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.get_dir_for/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.point/; +$external_latex_labels{$key} = q|6.3.2|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMenu.select_prev/; +$external_latex_labels{$key} = q|6.4.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.goto_next/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/sec:engines/; +$external_latex_labels{$key} = q|4.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_menu.pmenu/; +$external_latex_labels{$key} = q|6.4|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_dock.set_floating_shown_on/; +$external_latex_labels{$key} = q|6.5|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.query_ssh/; +$external_latex_labels{$key} = q|6.3|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.x_get_atom_name/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.query_workspace/; +$external_latex_labels{$key} = q|6.3|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMenu.typeahead_clear/; +$external_latex_labels{$key} = q|6.4.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:table.copy/; +$external_latex_labels{$key} = q|6.1.16|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WSplit.rqgeom/; +$external_latex_labels{$key} = q|6.2.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.tags_first/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.set_mark/; +$external_latex_labels{$key} = q|6.3.2|; +$noresave{$key} = "$nosave"; + +$key = q/screen_managed_changed_hook/; +$external_latex_labels{$key} = q|6.8|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.mx_count/; +$external_latex_labels{$key} = q|6.1.7|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.attach/; +$external_latex_labels{$key} = q|6.1.7|; +$noresave{$key} = "$nosave"; + +$key = q/fig:managership/; +$external_latex_labels{$key} = q|2.3|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.query_menu/; +$external_latex_labels{$key} = q|6.3|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMoveresMode.move/; +$external_latex_labels{$key} = q|6.1.8|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_tiling.get/; +$external_latex_labels{$key} = q|6.2|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WTiling.flip_at/; +$external_latex_labels{$key} = q|6.2.5|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.lookup_region/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.defshortening/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.refresh_stylelist/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.show_about_ion/; +$external_latex_labels{$key} = q|6.3|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.inc_index/; +$external_latex_labels{$key} = q|6.1.7|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.x_delete_property/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.rqclose/; +$external_latex_labels{$key} = q|6.1.9|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.set_activity/; +$external_latex_labels{$key} = q|6.1.9|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.query_runfile/; +$external_latex_labels{$key} = q|6.3|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.set_context/; +$external_latex_labels{$key} = q|6.3.2|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.show_tree/; +$external_latex_labels{$key} = q|6.3|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.contents/; +$external_latex_labels{$key} = q|6.3.2|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.exec/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.region_list/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.mx_current/; +$external_latex_labels{$key} = q|6.1.7|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.message/; +$external_latex_labels{$key} = q|6.3|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.compile_cmd/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/sec:deref/; +$external_latex_labels{$key} = q|6.7|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.bol/; +$external_latex_labels{$key} = q|6.3.2|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.submenu/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.attach_tagged/; +$external_latex_labels{$key} = q|6.1.7|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WSplitSplit.br/; +$external_latex_labels{$key} = q|6.2.4|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.managed_list/; +$external_latex_labels{$key} = q|6.1.7|; +$noresave{$key} = "$nosave"; + +$key = q/fn:string.shell_safe/; +$external_latex_labels{$key} = q|6.1.15|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.query_restart/; +$external_latex_labels{$key} = q|6.3|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WTiling.managed_list/; +$external_latex_labels{$key} = q|6.2.5|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_tiling.set/; +$external_latex_labels{$key} = q|6.2|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.backspace/; +$external_latex_labels{$key} = q|6.3.2|; +$noresave{$key} = "$nosave"; + +$key = q/fn:de.defstyle/; +$external_latex_labels{$key} = q|6.7|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WGroupWS.attach_framed/; +$external_latex_labels{$key} = q|6.1.5|; +$noresave{$key} = "$nosave"; + +$key = q/sec:dockref/; +$external_latex_labels{$key} = q|6.5|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WTiling.set_floating_at/; +$external_latex_labels{$key} = q|6.2.5|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.warn/; +$external_latex_labels{$key} = q|6.3|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.current/; +$external_latex_labels{$key} = q|6.1.9|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.rqclose_propagate/; +$external_latex_labels{$key} = q|6.1.9|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.clear_mark/; +$external_latex_labels{$key} = q|6.3.2|; +$noresave{$key} = "$nosave"; + +$key = q/tiling_placement_alt/; +$external_latex_labels{$key} = q|6.8|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.warn/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.begin_kbresize/; +$external_latex_labels{$key} = q|6.1.9|; +$noresave{$key} = "$nosave"; + +$key = q/sec:classesrolesinstances/; +$external_latex_labels{$key} = q|3.5.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.is_tagged/; +$external_latex_labels{$key} = q|6.1.9|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.next_completion/; +$external_latex_labels{$key} = q|6.3.2|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.set_hidden/; +$external_latex_labels{$key} = q|6.1.7|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMenu.cancel/; +$external_latex_labels{$key} = q|6.4.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WClientWin.nudge/; +$external_latex_labels{$key} = q|6.1.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.goto_nth_screen/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/clientwin_mapped_hook/; +$external_latex_labels{$key} = q|6.8|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WClientWin.set_fullscreen/; +$external_latex_labels{$key} = q|6.1.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.paste/; +$external_latex_labels{$key} = q|6.3.2|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WClientWin.xid/; +$external_latex_labels{$key} = q|6.1.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.name/; +$external_latex_labels{$key} = q|6.1.9|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.x_get_text_property/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.query_attachclient/; +$external_latex_labels{$key} = q|6.3|; +$noresave{$key} = "$nosave"; + +$key = q/fn:table.append/; +$external_latex_labels{$key} = q|6.1.16|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.mclick/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.set_index/; +$external_latex_labels{$key} = q|6.1.7|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.history_clear/; +$external_latex_labels{$key} = q|6.3|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WFrame.p_switch_tab/; +$external_latex_labels{$key} = q|6.1.2|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.x_intern_atom/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMoveresMode.finish/; +$external_latex_labels{$key} = q|6.1.8|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.query_gotoclient/; +$external_latex_labels{$key} = q|6.3|; +$noresave{$key} = "$nosave"; + +$key = q/ioncore_deinit_hook/; +$external_latex_labels{$key} = q|6.8|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WSplit.geom/; +$external_latex_labels{$key} = q|6.2.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.goto_previous/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.find_manager/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.get_paths/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/sec:walkthrough/; +$external_latex_labels{$key} = q|3.2|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WFrame.set_numbers/; +$external_latex_labels{$key} = q|6.1.2|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMoveresMode.cancel/; +$external_latex_labels{$key} = q|6.1.8|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.submap/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.is_active/; +$external_latex_labels{$key} = q|6.1.9|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.bkill_word/; +$external_latex_labels{$key} = q|6.3.2|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.attach_new/; +$external_latex_labels{$key} = q|6.1.7|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WGroup.attach/; +$external_latex_labels{$key} = q|6.1.3|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.mdblclick/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WSplitSplit.flip/; +$external_latex_labels{$key} = q|6.2.4|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WTiling.split_top/; +$external_latex_labels{$key} = q|6.2.5|; +$noresave{$key} = "$nosave"; + +$key = q/region_activity_hook/; +$external_latex_labels{$key} = q|6.8|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.progname/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.popen_completions/; +$external_latex_labels{$key} = q|6.3|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.defwinprop/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.popen_bgread/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.mdrag/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WClientWin.kill/; +$external_latex_labels{$key} = q|6.1.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WSplit.transpose/; +$external_latex_labels{$key} = q|6.2.1|; +$noresave{$key} = "$nosave"; + +$key = q/ioncore_snapshot_hook/; +$external_latex_labels{$key} = q|6.8|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.clear_tags/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:table.map/; +$external_latex_labels{$key} = q|6.1.16|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WSplitSplit.dir/; +$external_latex_labels{$key} = q|6.2.4|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WFrame.set_shaded/; +$external_latex_labels{$key} = q|6.1.2|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.set_stdisp/; +$external_latex_labels{$key} = q|6.1.7|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.manager/; +$external_latex_labels{$key} = q|6.1.9|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.x_change_property/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WInput.cancel/; +$external_latex_labels{$key} = q|6.3.3|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.parent/; +$external_latex_labels{$key} = q|6.1.9|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMenu.finish/; +$external_latex_labels{$key} = q|6.4.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.back/; +$external_latex_labels{$key} = q|6.3.2|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.TR/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/sec:menus/; +$external_latex_labels{$key} = q|3.4|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.query_renameframe/; +$external_latex_labels{$key} = q|6.3|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.geom/; +$external_latex_labels{$key} = q|6.1.9|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.kpress_wait/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/clientwin_do_manage_alt/; +$external_latex_labels{$key} = q|6.8|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.rootwin_of/; +$external_latex_labels{$key} = q|6.1.9|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WTiling.transpose_at/; +$external_latex_labels{$key} = q|6.2.5|; +$noresave{$key} = "$nosave"; + +$key = q/ioncore_sigchld_hook/; +$external_latex_labels{$key} = q|6.8|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.match_winprop_name/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WTiling.nextto/; +$external_latex_labels{$key} = q|6.2.5|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WDock.get/; +$external_latex_labels{$key} = q|6.5.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:de.substyle/; +$external_latex_labels{$key} = q|6.7|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.copy/; +$external_latex_labels{$key} = q|6.3.2|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.is_histcompl/; +$external_latex_labels{$key} = q|6.3.2|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.getmenu/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.is_i18n/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:gr.refresh/; +$external_latex_labels{$key} = q|6.1.14|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.rqorder/; +$external_latex_labels{$key} = q|6.1.9|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_menu.grabmenu/; +$external_latex_labels{$key} = q|6.4|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WSplitRegion.reg/; +$external_latex_labels{$key} = q|6.2.3|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.size_hints/; +$external_latex_labels{$key} = q|6.1.9|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.delete/; +$external_latex_labels{$key} = q|6.3.2|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.getwinprop/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.tagged_list/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.request_selection/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.query_man/; +$external_latex_labels{$key} = q|6.3|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.cut/; +$external_latex_labels{$key} = q|6.3.2|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.goto/; +$external_latex_labels{$key} = q|6.1.9|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.goto_next_screen/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.set_paths/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/sec:conffiles/; +$external_latex_labels{$key} = q|3.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.clientwin_list/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.aboutmsg/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/chap:prelim/; +$external_latex_labels{$key} = q|2|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WGroup.managed_list/; +$external_latex_labels{$key} = q|6.1.3|; +$noresave{$key} = "$nosave"; + +$key = q/fig:parentship/; +$external_latex_labels{$key} = q|2.2|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.read_savefile/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.restart_other/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.is_activity/; +$external_latex_labels{$key} = q|6.1.9|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.rqgeom/; +$external_latex_labels{$key} = q|6.1.9|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WInput.scrolldown/; +$external_latex_labels{$key} = q|6.3.3|; +$noresave{$key} = "$nosave"; + +$key = q/region_activated_hook/; +$external_latex_labels{$key} = q|6.8|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.history_table/; +$external_latex_labels{$key} = q|6.3|; +$noresave{$key} = "$nosave"; + +$key = q/sec:defaultde/; +$external_latex_labels{$key} = q|4.2|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.set/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.mpress/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/sec:tilingref/; +$external_latex_labels{$key} = q|6.2|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_tiling.detach/; +$external_latex_labels{$key} = q|6.2|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WRegion.set_name/; +$external_latex_labels{$key} = q|6.1.9|; +$noresave{$key} = "$nosave"; + +$key = q/fn:table.join/; +$external_latex_labels{$key} = q|6.1.16|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.activity_first/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/sec:spref/; +$external_latex_labels{$key} = q|6.6|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.is_hidden/; +$external_latex_labels{$key} = q|6.1.7|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_menu.get/; +$external_latex_labels{$key} = q|6.4|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.query_yesno/; +$external_latex_labels{$key} = q|6.3|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.history_next/; +$external_latex_labels{$key} = q|6.3.2|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.eol/; +$external_latex_labels{$key} = q|6.3.2|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.defcmd/; +$external_latex_labels{$key} = q|6.3|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.bdoc/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:ioncore.chdir_for/; +$external_latex_labels{$key} = q|6.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.query_lua/; +$external_latex_labels{$key} = q|6.3|; +$noresave{$key} = "$nosave"; + +$key = q/fn:export/; +$external_latex_labels{$key} = q|6.1.13|; +$noresave{$key} = "$nosave"; + +$key = q/chap:gr/; +$external_latex_labels{$key} = q|4|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_menu.set/; +$external_latex_labels{$key} = q|6.4|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.kill_word/; +$external_latex_labels{$key} = q|6.3.2|; +$noresave{$key} = "$nosave"; + +$key = q/fn:de.defstyle_rootwin/; +$external_latex_labels{$key} = q|6.7|; +$noresave{$key} = "$nosave"; + +$key = q/ioncore_post_layout_setup_hook/; +$external_latex_labels{$key} = q|6.8|; +$noresave{$key} = "$nosave"; + +$key = q/fn:table.icat/; +$external_latex_labels{$key} = q|6.1.16|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.kill_line/; +$external_latex_labels{$key} = q|6.3.2|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WDock.resize/; +$external_latex_labels{$key} = q|6.5.1|; +$noresave{$key} = "$nosave"; + +$key = q/fn:mod_query.query_shutdown/; +$external_latex_labels{$key} = q|6.3|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WMPlex.get_index/; +$external_latex_labels{$key} = q|6.1.7|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.skip_word/; +$external_latex_labels{$key} = q|6.3.2|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.complete/; +$external_latex_labels{$key} = q|6.3.2|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WGroup.attach_new/; +$external_latex_labels{$key} = q|6.1.3|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WEdln.transpose_chars/; +$external_latex_labels{$key} = q|6.3.2|; +$noresave{$key} = "$nosave"; + +$key = q/fn:WTiling.split_tree/; +$external_latex_labels{$key} = q|6.2.5|; +$noresave{$key} = "$nosave"; + +1; + diff --git a/doc/ionconf/next.png b/doc/ionconf/next.png new file mode 100644 index 0000000000000000000000000000000000000000..1628652aac7cff4b24228abd9908c320c6026a7c GIT binary patch literal 245 zcmVQ2aP`rsfsL)H8>rF50IrEKQ7U2mrM!9=R9*Dw1X+@W9RTx^a-#5T6$bi^4|h!q3s vo9V3RdqZP$JTTh9!JZ)`voZLAB<&|;0_>J~#QNZkD16SAR=O4@7anKqmC5&ngT;#rH1+qK z20S~FlZynq-MY=T{~%>KNs884sExd44P^%bf!zNDPxW#x21^f><@$FgzM-=J5cLN5 WGDyh~oC>l40000 + + + + +Contents + + + + + + + + + + + + + + + + + + + + + +
    + +

    +Contents +

    + + + + +

    + +

    +


    + + + diff --git a/doc/ionconf/node10.html b/doc/ionconf/node10.html new file mode 100644 index 0000000..f33c3b7 --- /dev/null +++ b/doc/ionconf/node10.html @@ -0,0 +1,377 @@ + + + + + +List of functions + + + + + + + + + + + + + + + + + + + + + + +

    +List of functions +

    + +

    + +de.defstyle +
    de.defstyle_rootwin +
    de.reset +
    de.substyle +
    export +
    gr.read_config +
    gr.refresh +
    gr.select_engine +
    ioncore.aboutmsg +
    ioncore.activity_first +
    ioncore.activity_list +
    ioncore.bdoc +
    ioncore.chdir_for +
    ioncore.clear_tags +
    ioncore.clientwin_list +
    ioncore.compile_cmd +
    ioncore.create_ws +
    ioncore.current +
    ioncore.defbindings +
    ioncore.defctxmenu +
    ioncore.defmenu +
    ioncore.defshortening +
    ioncore.defwinprop +
    ioncore.exec +
    ioncore.exec_on +
    ioncore.find_manager +
    ioncore.find_screen_id +
    ioncore.get +
    ioncore.getbindings +
    ioncore.getctxmenu +
    ioncore.get_dir_for +
    ioncore.getmenu +
    ioncore.get_paths +
    ioncore.get_savefile +
    ioncore.getwinprop +
    ioncore.goto_activity +
    ioncore.goto_first +
    ioncore.goto_next +
    ioncore.goto_next_screen +
    ioncore.goto_nth_screen +
    ioncore.goto_previous +
    ioncore.goto_prev_screen +
    ioncore.is_i18n +
    ioncore.kpress +
    ioncore.kpress_wait +
    ioncore.load_module +
    ioncore.lookup_clientwin +
    ioncore.lookup_region +
    ioncore.lookup_script +
    ioncore.match_winprop_name +
    ioncore.mclick +
    ioncore.mdblclick +
    ioncore.mdrag +
    ioncore.menuentry +
    ioncore.mpress +
    ioncore.navi_first +
    ioncore.navi_next +
    ioncore.popen_bgread +
    ioncore.progname +
    ioncore.read_savefile +
    ioncore.refresh_stylelist +
    ioncore.region_list +
    ioncore.request_selection +
    ioncore.resign +
    ioncore.restart +
    ioncore.restart_other +
    ioncore.set +
    ioncore.set_get_winprop_fn +
    ioncore.set_paths +
    ioncore.set_selection +
    ioncore.shutdown +
    ioncore.snapshot +
    ioncore.submap +
    ioncore.submenu +
    ioncore.tagged_list +
    ioncore.tags_first +
    ioncore.TR +
    ioncore.version +
    ioncore.warn +
    ioncore.warn_traced +
    ioncore.write_savefile +
    ioncore.x_change_property +
    ioncore.x_delete_property +
    ioncore.x_get_atom_name +
    ioncore.x_get_text_property +
    ioncore.x_get_window_property +
    ioncore.x_intern_atom +
    ioncore.x_set_text_property +
    mod_dock.set_floating_shown_on +
    mod_menu.get +
    mod_menu.grabmenu +
    mod_menu.menu +
    mod_menu.pmenu +
    mod_menu.set +
    mod_query.defcmd +
    mod_query.get +
    mod_query.history_clear +
    mod_query.history_get +
    mod_query.history_push +
    mod_query.history_search +
    mod_query.history_table +
    mod_query.message +
    mod_query.popen_completions +
    mod_query.query +
    mod_query.query_attachclient +
    mod_query.query_editfile +
    mod_query.query_exec +
    mod_query.query_gotoclient +
    mod_query.query_lua +
    mod_query.query_man +
    mod_query.query_menu +
    mod_query.query_renameframe +
    mod_query.query_renameworkspace +
    mod_query.query_restart +
    mod_query.query_runfile +
    mod_query.query_shutdown +
    mod_query.query_ssh +
    mod_query.query_workspace +
    mod_query.query_yesno +
    mod_query.set +
    mod_query.show_about_ion +
    mod_query.show_tree +
    mod_query.warn +
    mod_sp.set_shown +
    mod_sp.set_shown_on +
    mod_tiling.detach +
    mod_tiling.get +
    mod_tiling.mkbottom +
    mod_tiling.set +
    string.shell_safe +
    table.append +
    table.copy +
    table.icat +
    table.join +
    table.map +
    WClientWin.get_ident +
    WClientWin.is_fullscreen +
    WClientWin.kill +
    WClientWin.nudge +
    WClientWin.quote_next +
    WClientWin.set_fullscreen +
    WClientWin.xid +
    WComplProxy.set_completions +
    WDock.attach +
    WDock.get +
    WDock.resize +
    WDock.set +
    WEdln.back +
    WEdln.backspace +
    WEdln.bkill_word +
    WEdln.bol +
    WEdln.bskip_word +
    WEdln.clear_mark +
    WEdln.complete +
    WEdln.contents +
    WEdln.context +
    WEdln.copy +
    WEdln.cut +
    WEdln.delete +
    WEdln.eol +
    WEdln.finish +
    WEdln.forward +
    WEdln.history_next +
    WEdln.history_prev +
    WEdln.insstr +
    WEdln.is_histcompl +
    WEdln.kill_line +
    WEdln.kill_to_bol +
    WEdln.kill_to_eol +
    WEdln.kill_word +
    WEdln.mark +
    WEdln.next_completion +
    WEdln.paste +
    WEdln.point +
    WEdln.prev_completion +
    WEdln.set_context +
    WEdln.set_mark +
    WEdln.skip_word +
    WEdln.transpose_chars +
    WEdln.transpose_words +
    WFrame.is_shaded +
    WFrame.maximize_horiz +
    WFrame.maximize_vert +
    WFrame.mode +
    WFrame.p_switch_tab +
    WFrame.p_tabdrag +
    WFrame.set_mode +
    WFrame.set_numbers +
    WFrame.set_shaded +
    WGroup.attach +
    WGroup.attach_new +
    WGroup.bottom +
    WGroup.managed_list +
    WGroupWS.attach_framed +
    WInfoWin.set_text +
    WInput.cancel +
    WInput.scrolldown +
    WInput.scrollup +
    WMenu.cancel +
    WMenu.finish +
    WMenu.select_next +
    WMenu.select_nth +
    WMenu.select_prev +
    WMenu.typeahead_clear +
    WMoveresMode.cancel +
    WMoveresMode.finish +
    WMoveresMode.move +
    WMoveresMode.resize +
    WMPlex.attach +
    WMPlex.attach_new +
    WMPlex.attach_tagged +
    WMPlex.dec_index +
    WMPlex.get_index +
    WMPlex.get_stdisp +
    WMPlex.inc_index +
    WMPlex.is_hidden +
    WMPlex.managed_list +
    WMPlex.mx_count +
    WMPlex.mx_current +
    WMPlex.mx_list +
    WMPlex.mx_nth +
    WMPlex.set_hidden +
    WMPlex.set_index +
    WMPlex.set_stdisp +
    WMPlex.switch_next +
    WMPlex.switch_nth +
    WMPlex.switch_prev +
    WRegion.begin_kbresize +
    WRegion.current +
    WRegion.geom +
    WRegion.goto +
    WRegion.is_active +
    WRegion.is_activity +
    WRegion.is_mapped +
    WRegion.is_tagged +
    WRegion.manager +
    WRegion.name +
    WRegion.parent +
    WRegion.rootwin_of +
    WRegion.rqclose +
    WRegion.rqclose_propagate +
    WRegion.rqgeom +
    WRegion.rqorder +
    WRegion.screen_of +
    WRegion.set_activity +
    WRegion.set_name +
    WRegion.set_name_exact +
    WRegion.set_tagged +
    WRegion.size_hints +
    WRootWin.current_scr +
    WScreen.id +
    WScreen.set_managed_offset +
    WSplit.geom +
    WSplitInner.current +
    WSplit.parent +
    WSplitRegion.reg +
    WSplit.rqgeom +
    WSplitSplit.br +
    WSplitSplit.dir +
    WSplitSplit.flip +
    WSplitSplit.tl +
    WSplit.transpose +
    WTiling.farthest +
    WTiling.flip_at +
    WTiling.managed_list +
    WTiling.nextto +
    WTiling.node_of +
    WTiling.set_floating +
    WTiling.set_floating_at +
    WTiling.split +
    WTiling.split_at +
    WTiling.split_top +
    WTiling.split_tree +
    WTiling.transpose_at +
    WTiling.unsplit_at +
    WWindow.p_move +
    WWindow.p_resize +
    WWindow.xid +
    +

    + + + + diff --git a/doc/ionconf/node11.html b/doc/ionconf/node11.html new file mode 100644 index 0000000..2c12ab2 --- /dev/null +++ b/doc/ionconf/node11.html @@ -0,0 +1,1091 @@ + + + + + +Index + + + + + + + + + + + + + + + + + + + + + +
    + +

    +Index +


    +
    aboutmsg + : 6.1 +
    acrobatic + : 3.5 +
    activity_first + : 6.1 +
    activity_list + : 6.1 +
    Alt + : 3.3.5 +
    AnyModifier + : 3.3.5 +
    append + : 6.1.16 +
    aspect + : 3.5 +
    attach + : 6.1.3 + | 6.1.7 + | 6.5.1 +
    attach_framed + : 6.1.5 +
    attach_new + : 6.1.3 + | 6.1.7 +
    attach_tagged + : 6.1.7 +
    back + : 6.3.2 +
    backspace + : 6.3.2 +
    bdoc + : 6.1 +
    begin_kbresize + : 6.1.9 +
    bkill_word + : 6.3.2 +
    bol + : 6.3.2 +
    bottom + : 6.1.3 +
    br + : 6.2.4 +
    bskip_word + : 6.3.2 +
    Button-n + : 3.3.6 +
    cancel + : 6.1.8 + | 6.3.3 + | 6.4.1 +
    chdir_for + : 6.1 +
    class
    +
    winprop : 3.5.1 +
    +
    clear_mark + : 6.3.2 +
    clear_tags + : 6.1 +
    clientwin_do_manage_alt + : 6.8 +
    clientwin_list + : 6.1 +
    clientwin_mapped_hook + : 6.8 +
    clientwin_unmapped_hook + : 6.8 +
    compile_cmd + : 6.1 +
    complete + : 6.3.2 +
    contents + : 6.3.2 +
    context + : 6.3.2 +
    Control + : 3.3.5 +
    copy + : 6.1.16 + | 6.3.2 +
    create_ws + : 6.1 +
    current + : 6.1 + | 6.1.9 + | 6.2.2 +
    current_scr + : 6.1.10 +
    cut + : 6.3.2 +
    de
    +
    defstyle : 6.7 +
    defstyle_rootwin : 6.7 +
    reset : 6.7 +
    substyle : 6.7 +
    +
    dec_index + : 6.1.7 +
    defbindings + : 6.1 +
    defcmd + : 6.3 +
    defctxmenu + : 6.1 +
    defmenu + : 3.4.1 + | 6.1 +
    defshortening + : 6.1 +
    defstyle + : 6.7 +
    defstyle_rootwin + : 6.7 +
    defwinprop + : 6.1 +
    delete + : 6.3.2 +
    detach + : 6.2 +
    dir + : 6.2.4 +
    drawing engine + : 4.1 +
    eol + : 6.3.2 +
    ETCDIR + : 3.1 +
    exec + : 6.1 +
    exec_on + : 6.1 +
    export + : 6.1.13 +
    farthest + : 6.2.5 +
    find_manager + : 6.1 +
    find_screen_id + : 6.1 +
    finish + : 6.1.8 + | 6.3.2 + | 6.4.1 +
    flip + : 6.2.4 +
    flip_at + : 6.2.5 +
    float + : 3.5 +
    forward + : 6.3.2 +
    frame_managed_changed_hook + : 6.8 +
    fullscreen + : 3.5 +
    geom + : 6.1.9 + | 6.2.1 +
    get + : 6.1 + | 6.2 + | 6.3 + | 6.4 + | 6.5.1 +
    get_dir_for + : 6.1 +
    get_ident + : 6.1.1 +
    get_index + : 6.1.7 +
    get_paths + : 6.1 +
    get_savefile + : 6.1 +
    get_stdisp + : 6.1.7 +
    getbindings + : 6.1 +
    getctxmenu + : 6.1 +
    getmenu + : 6.1 +
    getwinprop + : 6.1 +
    goto + : 6.1.9 +
    goto_activity + : 6.1 +
    goto_first + : 6.1 +
    goto_next + : 6.1 +
    goto_next_screen + : 6.1 +
    goto_nth_screen + : 6.1 +
    goto_prev_screen + : 6.1 +
    goto_previous + : 6.1 +
    gr
    +
    read_config : 6.1.14 +
    refresh : 6.1.14 +
    select_engine : 6.1.14 +
    +
    grabmenu + : 6.4 +
    history_clear + : 6.3 +
    history_get + : 6.3 +
    history_next + : 6.3.2 +
    history_prev + : 6.3.2 +
    history_push + : 6.3 +
    history_search + : 6.3 +
    history_table + : 6.3 +
    icat + : 6.1.16 +
    id + : 6.1.11 +
    ignore_cfgrq + : 3.5 +
    ignore_net_active_window + : 3.5 +
    ignore_resizeinc + : 3.5 +
    inc_index + : 6.1.7 +
    insstr + : 6.3.2 +
    instance
    +
    winprop : 3.5.1 +
    +
    ioncore
    +
    aboutmsg : 6.1 +
    activity_first : 6.1 +
    activity_list : 6.1 +
    bdoc : 6.1 +
    chdir_for : 6.1 +
    clear_tags : 6.1 +
    clientwin_list : 6.1 +
    compile_cmd : 6.1 +
    create_ws : 6.1 +
    current : 6.1 +
    defbindings : 6.1 +
    defctxmenu : 6.1 +
    defmenu : 6.1 +
    defshortening : 6.1 +
    defwinprop : 6.1 +
    exec : 6.1 +
    exec_on : 6.1 +
    find_manager : 6.1 +
    find_screen_id : 6.1 +
    get : 6.1 +
    get_dir_for : 6.1 +
    get_paths : 6.1 +
    get_savefile : 6.1 +
    getbindings : 6.1 +
    getctxmenu : 6.1 +
    getmenu : 6.1 +
    getwinprop : 6.1 +
    goto_activity : 6.1 +
    goto_first : 6.1 +
    goto_next : 6.1 +
    goto_next_screen : 6.1 +
    goto_nth_screen : 6.1 +
    goto_prev_screen : 6.1 +
    goto_previous : 6.1 +
    is_i18n : 6.1 +
    kpress : 6.1 +
    kpress_wait : 6.1 +
    load_module : 6.1 +
    lookup_clientwin : 6.1 +
    lookup_region : 6.1 +
    lookup_script : 6.1 +
    match_winprop_name : 6.1 +
    mclick : 6.1 +
    mdblclick : 6.1 +
    mdrag : 6.1 +
    menuentry : 6.1 +
    mpress : 6.1 +
    navi_first : 6.1 +
    navi_next : 6.1 +
    popen_bgread : 6.1 +
    progname : 6.1 +
    read_savefile : 6.1 +
    refresh_stylelist : 6.1 +
    region_list : 6.1 +
    request_selection : 6.1 +
    resign : 6.1 +
    restart : 6.1 +
    restart_other : 6.1 +
    set : 6.1 +
    set_get_winprop_fn : 6.1 +
    set_paths : 6.1 +
    set_selection : 6.1 +
    shutdown : 6.1 +
    snapshot : 6.1 +
    submap : 6.1 +
    submenu : 6.1 +
    tagged_list : 6.1 +
    tags_first : 6.1 +
    TR : 6.1 +
    version : 6.1 +
    warn : 6.1 +
    warn_traced : 6.1 +
    write_savefile : 6.1 +
    x_change_property : 6.1 +
    x_delete_property : 6.1 +
    x_get_atom_name : 6.1 +
    x_get_text_property : 6.1 +
    x_get_window_property : 6.1 +
    x_intern_atom : 6.1 +
    x_set_text_property : 6.1 +
    +
    ioncore_deinit_hook + : 6.8 +
    ioncore_post_layout_setup_hook + : 6.8 +
    ioncore_sigchld_hook + : 6.8 +
    ioncore_snapshot_hook + : 6.8 +
    is_active + : 6.1.9 +
    is_activity + : 6.1.9 +
    is_fullscreen + : 6.1.1 +
    is_hidden + : 6.1.7 +
    is_histcompl + : 6.3.2 +
    is_i18n + : 6.1 +
    is_mapped + : 6.1.9 +
    is_shaded + : 6.1.2 +
    is_tagged + : 6.1.9 +
    join + : 6.1.16 +
    jumpto + : 3.5 +
    keysymdef.h + : 3.3.5 +
    kill + : 6.1.1 +
    kill_line + : 6.3.2 +
    kill_to_bol + : 6.3.2 +
    kill_to_eol + : 6.3.2 +
    kill_word + : 6.3.2 +
    kpress + : 6.1 +
    kpress_wait + : 6.1 +
    load_module + : 6.1 +
    Lock + : 3.3.5 +
    lookup_clientwin + : 6.1 +
    lookup_region + : 6.1 +
    lookup_script + : 6.1 +
    managed_list + : 6.1.3 + | 6.1.7 + | 6.2.5 +
    manager + : 2.2.2.1 + | 6.1.9 +
    map + : 6.1.16 +
    mark + : 6.3.2 +
    match_winprop_name + : 6.1 +
    max_size + : 3.5 +
    maximize_horiz + : 6.1.2 +
    maximize_vert + : 6.1.2 +
    mclick + : 6.1 +
    mdblclick + : 6.1 +
    mdrag + : 6.1 +
    menu + : 6.4 +
    menuentry + : 3.4.1 + | 6.1 +
    menus + : 3.4.1 +
    message + : 6.3 +
    min_size + : 3.5 +
    mkbottom + : 6.2 +
    mod_dock
    +
    set_floating_shown_on : 6.5 +
    +
    mod_menu
    +
    get : 6.4 +
    grabmenu : 6.4 +
    menu : 6.4 +
    pmenu : 6.4 +
    set : 6.4 +
    +
    mod_query
    +
    defcmd : 6.3 +
    get : 6.3 +
    history_clear : 6.3 +
    history_get : 6.3 +
    history_push : 6.3 +
    history_search : 6.3 +
    history_table : 6.3 +
    message : 6.3 +
    popen_completions : 6.3 +
    query : 6.3 +
    query_attachclient : 6.3 +
    query_editfile : 6.3 +
    query_exec : 6.3 +
    query_gotoclient : 6.3 +
    query_lua : 6.3 +
    query_man : 6.3 +
    query_menu : 6.3 +
    query_renameframe : 6.3 +
    query_renameworkspace : 6.3 +
    query_restart : 6.3 +
    query_runfile : 6.3 +
    query_shutdown : 6.3 +
    query_ssh : 6.3 +
    query_workspace : 6.3 +
    query_yesno : 6.3 +
    set : 6.3 +
    show_about_ion : 6.3 +
    show_tree : 6.3 +
    warn : 6.3 +
    +
    mod_sp
    +
    set_shown : 6.6 +
    set_shown_on : 6.6 +
    +
    mod_tiling
    +
    detach : 6.2 +
    get : 6.2 +
    mkbottom : 6.2 +
    set : 6.2 +
    +
    mode + : 6.1.2 +
    ModN + : 3.3.5 +
    move + : 6.1.8 +
    mpress + : 6.1 +
    mx_count + : 6.1.7 +
    mx_current + : 6.1.7 +
    mx_list + : 6.1.7 +
    mx_nth + : 6.1.7 +
    name + : 6.1.9 +
    navi_first + : 6.1 +
    navi_next + : 6.1 +
    next_completion + : 6.3.2 +
    nextto + : 6.2.5 +
    node_of + : 6.2.5 +
    nudge + : 6.1.1 +
    NumLock + : 3.3.5 +
    Obj + : 2.2.1 +
    oneshot + : 3.5 +
    p_move + : 6.1.12 +
    p_resize + : 6.1.12 +
    p_switch_tab + : 6.1.2 +
    p_tabdrag + : 6.1.2 +
    panews_make_placement_alt + : 6.8 +
    parent + : 2.2.2.1 + | 6.1.9 + | 6.2.1 +
    paste + : 6.3.2 +
    pmenu + : 6.4 +
    point + : 6.3.2 +
    popen_bgread + : 6.1 +
    popen_completions + : 6.3 +
    PREFIX + : 3.1 +
    prev_completion + : 6.3.2 +
    progname + : 6.1 +
    query + : 6.3 +
    query_attachclient + : 6.3 +
    query_editfile + : 6.3 +
    query_exec + : 6.3 +
    query_gotoclient + : 6.3 +
    query_lua + : 6.3 +
    query_man + : 6.3 +
    query_menu + : 6.3 +
    query_renameframe + : 6.3 +
    query_renameworkspace + : 6.3 +
    query_restart + : 6.3 +
    query_runfile + : 6.3 +
    query_shutdown + : 6.3 +
    query_ssh + : 6.3 +
    query_workspace + : 6.3 +
    query_yesno + : 6.3 +
    quote_next + : 6.1.1 +
    read_config + : 6.1.14 +
    read_savefile + : 6.1 +
    refresh + : 6.1.14 +
    refresh_stylelist + : 6.1 +
    reg + : 6.2.3 +
    region_activated_hook + : 6.8 +
    region_activity_hook + : 6.8 +
    region_do_warp_alt + : 6.8 +
    region_inactivated_hook + : 6.8 +
    region_list + : 6.1 +
    request_selection + : 6.1 +
    reset + : 6.7 +
    resign + : 6.1 +
    resize + : 6.1.8 + | 6.5.1 +
    restart + : 6.1 +
    restart_other + : 6.1 +
    role
    +
    winprop : 3.5.1 +
    +
    root window + : 2.2.1 +
    rootwin_of + : 6.1.9 +
    rqclose + : 6.1.9 +
    rqclose_propagate + : 6.1.9 +
    rqgeom + : 6.1.9 + | 6.2.1 +
    rqorder + : 6.1.9 +
    screen
    +
    physical : 2.2.1 +
    X : 2.2.1 +
    +
    screen_managed_changed_hook + : 6.8 +
    screen_of + : 6.1.9 +
    scrolldown + : 6.3.3 +
    ScrollLock + : 3.3.5 +
    scrollup + : 6.3.3 +
    select_engine + : 6.1.14 +
    select_next + : 6.4.1 +
    select_nth + : 6.4.1 +
    select_prev + : 6.4.1 +
    set + : 6.1 + | 6.2 + | 6.3 + | 6.4 + | 6.5.1 +
    set_activity + : 6.1.9 +
    set_completions + : 6.3.1 +
    set_context + : 6.3.2 +
    set_floating + : 6.2.5 +
    set_floating_at + : 6.2.5 +
    set_floating_shown_on + : 6.5 +
    set_fullscreen + : 6.1.1 +
    set_get_winprop_fn + : 6.1 +
    set_hidden + : 6.1.7 +
    set_index + : 6.1.7 +
    set_managed_offset + : 6.1.11 +
    set_mark + : 6.3.2 +
    set_mode + : 6.1.2 +
    set_name + : 6.1.9 +
    set_name_exact + : 6.1.9 +
    set_numbers + : 6.1.2 +
    set_paths + : 6.1 +
    set_selection + : 6.1 +
    set_shaded + : 6.1.2 +
    set_shown + : 6.6 +
    set_shown_on + : 6.6 +
    set_stdisp + : 6.1.7 +
    set_tagged + : 6.1.9 +
    set_text + : 6.1.6 +
    shell_safe + : 6.1.15 +
    Shift + : 3.3.5 +
    show_about_ion + : 6.3 +
    show_tree + : 6.3 +
    shutdown + : 6.1 +
    size_hints + : 6.1.9 +
    skip_word + : 6.3.2 +
    snapshot + : 6.1 +
    split + : 6.2.5 +
    split_at + : 6.2.5 +
    split_top + : 6.2.5 +
    split_tree + : 6.2.5 +
    string
    +
    shell_safe : 6.1.15 +
    +
    style + : 4.1 +
    submap + : 6.1 +
    submenu + : 3.4.1 + | 6.1 +
    substyle + : 4.1 + | 6.7 +
    switch_next + : 6.1.7 +
    switch_nth + : 6.1.7 +
    switch_prev + : 6.1.7 +
    switchto + : 3.5 +
    system.mk + : 3.1 +
    table
    +
    append : 6.1.16 +
    copy : 6.1.16 +
    icat : 6.1.16 +
    join : 6.1.16 +
    map : 6.1.16 +
    +
    tagged_list + : 6.1 +
    tags_first + : 6.1 +
    target + : 3.5 +
    tiling_placement_alt + : 6.8 +
    tl + : 6.2.4 +
    TR + : 6.1 +
    transient + : 3.5.2 +
    transient_mode + : 3.5 +
    transients_at_top + : 3.5 +
    transparent + : 3.5 +
    transpose + : 6.2.1 +
    transpose_at + : 6.2.5 +
    transpose_chars + : 6.3.2 +
    transpose_words + : 6.3.2 +
    typeahead_clear + : 6.4.1 +
    unsplit_at + : 6.2.5 +
    version + : 6.1 +
    warn + : 6.1 + | 6.3 +
    warn_traced + : 6.1 +
    WClientWin + : 2.2.1 +
    +
    get_ident : 6.1.1 +
    is_fullscreen : 6.1.1 +
    kill : 6.1.1 +
    nudge : 6.1.1 +
    quote_next : 6.1.1 +
    set_fullscreen : 6.1.1 +
    xid : 6.1.1 +
    +
    WComplProxy
    +
    set_completions : 6.3.1 +
    +
    WDock
    +
    attach : 6.5.1 +
    get : 6.5.1 +
    resize : 6.5.1 +
    set : 6.5.1 +
    +
    WEdln + : 2.2.1 +
    +
    back : 6.3.2 +
    backspace : 6.3.2 +
    bkill_word : 6.3.2 +
    bol : 6.3.2 +
    bskip_word : 6.3.2 +
    clear_mark : 6.3.2 +
    complete : 6.3.2 +
    contents : 6.3.2 +
    context : 6.3.2 +
    copy : 6.3.2 +
    cut : 6.3.2 +
    delete : 6.3.2 +
    eol : 6.3.2 +
    finish : 6.3.2 +
    forward : 6.3.2 +
    history_next : 6.3.2 +
    history_prev : 6.3.2 +
    insstr : 6.3.2 +
    is_histcompl : 6.3.2 +
    kill_line : 6.3.2 +
    kill_to_bol : 6.3.2 +
    kill_to_eol : 6.3.2 +
    kill_word : 6.3.2 +
    mark : 6.3.2 +
    next_completion : 6.3.2 +
    paste : 6.3.2 +
    point : 6.3.2 +
    prev_completion : 6.3.2 +
    set_context : 6.3.2 +
    set_mark : 6.3.2 +
    skip_word : 6.3.2 +
    transpose_chars : 6.3.2 +
    transpose_words : 6.3.2 +
    +
    WFrame + : 2.2.1 +
    +
    is_shaded : 6.1.2 +
    maximize_horiz : 6.1.2 +
    maximize_vert : 6.1.2 +
    mode : 6.1.2 +
    p_switch_tab : 6.1.2 +
    p_tabdrag : 6.1.2 +
    set_mode : 6.1.2 +
    set_numbers : 6.1.2 +
    set_shaded : 6.1.2 +
    +
    WGroup + : 2.2.1 +
    +
    attach : 6.1.3 +
    attach_new : 6.1.3 +
    bottom : 6.1.3 +
    managed_list : 6.1.3 +
    +
    WGroupCW + : 2.2.1 +
    WGroupWS + : 2.2.1 +
    +
    attach_framed : 6.1.5 +
    +
    WInfoWin
    +
    set_text : 6.1.6 +
    +
    Winprops + : 3.5 +
    WInput + : 2.2.1 +
    +
    cancel : 6.3.3 +
    scrolldown : 6.3.3 +
    scrollup : 6.3.3 +
    +
    WMenu
    +
    cancel : 6.4.1 +
    finish : 6.4.1 +
    select_next : 6.4.1 +
    select_nth : 6.4.1 +
    select_prev : 6.4.1 +
    typeahead_clear : 6.4.1 +
    +
    WMessage + : 2.2.1 +
    WMoveresMode
    +
    cancel : 6.1.8 +
    finish : 6.1.8 +
    move : 6.1.8 +
    resize : 6.1.8 +
    +
    WMPlex
    +
    attach : 6.1.7 +
    attach_new : 6.1.7 +
    attach_tagged : 6.1.7 +
    dec_index : 6.1.7 +
    get_index : 6.1.7 +
    get_stdisp : 6.1.7 +
    inc_index : 6.1.7 +
    is_hidden : 6.1.7 +
    managed_list : 6.1.7 +
    mx_count : 6.1.7 +
    mx_current : 6.1.7 +
    mx_list : 6.1.7 +
    mx_nth : 6.1.7 +
    set_hidden : 6.1.7 +
    set_index : 6.1.7 +
    set_stdisp : 6.1.7 +
    switch_next : 6.1.7 +
    switch_nth : 6.1.7 +
    switch_prev : 6.1.7 +
    +
    WRegion + : 2.2.1 +
    +
    begin_kbresize : 6.1.9 +
    current : 6.1.9 +
    geom : 6.1.9 +
    goto : 6.1.9 +
    is_active : 6.1.9 +
    is_activity : 6.1.9 +
    is_mapped : 6.1.9 +
    is_tagged : 6.1.9 +
    manager : 6.1.9 +
    name : 6.1.9 +
    parent : 6.1.9 +
    rootwin_of : 6.1.9 +
    rqclose : 6.1.9 +
    rqclose_propagate : 6.1.9 +
    rqgeom : 6.1.9 +
    rqorder : 6.1.9 +
    screen_of : 6.1.9 +
    set_activity : 6.1.9 +
    set_name : 6.1.9 +
    set_name_exact : 6.1.9 +
    set_tagged : 6.1.9 +
    size_hints : 6.1.9 +
    +
    write_savefile + : 6.1 +
    WRootWin + : 2.2.1 +
    +
    current_scr : 6.1.10 +
    +
    WScreen + : 2.2.1 +
    +
    id : 6.1.11 +
    set_managed_offset : 6.1.11 +
    +
    WSplit + : 2.2.1 +
    +
    geom : 6.2.1 +
    parent : 6.2.1 +
    rqgeom : 6.2.1 +
    transpose : 6.2.1 +
    +
    WSplitInner
    +
    current : 6.2.2 +
    +
    WSplitRegion
    +
    reg : 6.2.3 +
    +
    WSplitSplit
    +
    br : 6.2.4 +
    dir : 6.2.4 +
    flip : 6.2.4 +
    tl : 6.2.4 +
    +
    WTiling + : 2.2.1 +
    +
    farthest : 6.2.5 +
    flip_at : 6.2.5 +
    managed_list : 6.2.5 +
    nextto : 6.2.5 +
    node_of : 6.2.5 +
    set_floating : 6.2.5 +
    set_floating_at : 6.2.5 +
    split : 6.2.5 +
    split_at : 6.2.5 +
    split_top : 6.2.5 +
    split_tree : 6.2.5 +
    transpose_at : 6.2.5 +
    unsplit_at : 6.2.5 +
    +
    WWindow + : 2.2.1 +
    +
    p_move : 6.1.12 +
    p_resize : 6.1.12 +
    xid : 6.1.12 +
    +
    x_change_property + : 6.1 +
    x_delete_property + : 6.1 +
    x_get_atom_name + : 6.1 +
    x_get_text_property + : 6.1 +
    x_get_window_property + : 6.1 +
    x_intern_atom + : 6.1 +
    x_set_text_property + : 6.1 +
    xid + : 6.1.1 + | 6.1.12 +
    Xinerama + : 2.2.1 +
    xmodmap + : 3.3.5 +
    xprop + : 3.5.2 + +
    + +

    +


    + + + diff --git a/doc/ionconf/node12.html b/doc/ionconf/node12.html new file mode 100644 index 0000000..90201d6 --- /dev/null +++ b/doc/ionconf/node12.html @@ -0,0 +1,77 @@ + + + + + +About this document ... + + + + + + + + + + + + + + + + + + + + +

    +About this document ... +

    + Configuring and extending Ion3 with Lua

    +This document was generated using the +LaTeX2HTML translator Version 2002-2-1 (1.71) +

    +Copyright © 1993, 1994, 1995, 1996, +Nikos Drakos, +Computer Based Learning Unit, University of Leeds. +
    +Copyright © 1997, 1998, 1999, +Ross Moore, +Mathematics Department, Macquarie University, Sydney. +

    +The command line arguments were:
    + latex2html -show_section_numbers -short_index -local_icons -noaddress -up_url http://iki.fi/tuomov/ion/ -up_title 'Ion homepage' -nofootnode -split 3 ionconf +

    +The translation was initiated by tuomov on 2006-12-23 +


    + + + diff --git a/doc/ionconf/node2.html b/doc/ionconf/node2.html new file mode 100644 index 0000000..9d85352 --- /dev/null +++ b/doc/ionconf/node2.html @@ -0,0 +1,163 @@ + + + + + +1. Introduction + + + + + + + + + + + + + + + + + + + + + + +

    +1. Introduction +

    + +

    +This document is an ''advanced user'' manual for Ion, the X11 window manager, +and version 3 specifically. It is an attempt attempt at documenting what is +in Ion's configuration files, how to configure Ion by simple modifications +to these files and how to write more complex extensions in Lua, the +lightweight configuration and scripting language used by Ion. + +

    +Readers unfamiliar with Lua is advised to first glance at the Lua manual at + +

    +

    + +

    +and perhaps some tutorial pages at the lua-users wiki: + +

    +

    + +

    +Back in this document, first in chapter 2 some key +concepts and relations are explained. These include the module system +and Ion's object and class hierarchies. While it might not at first +occur that knowing such things would be necessary to configure +a program, this material is essential because of the object-oriented +nature of most of Ion's scripting interface. + +

    +The new user, fed up with the default key bindings and eager to just +quickly configure Ion to his liking may question the reasons for +exposing the ''heavy'' internal OO structure in the scripting and +configuration interface. I'm not the one to blame him for that. +Sure it would be faster to configure Ion to everyone's liking +if a simpler binding configuration interface was provided. Such an +interface would, however, also be far more limited and make writing +extensions more complicated and the advantages from using a real +scripting language would be partly lost. One more advantage from +a rich scripting and configuration interface is that it allows +implementing scripts to read alternate configuration file formats, +ones that could be, for example, modified by external configuration tools. + +

    +In chapter 3 the very basic Ion configuration know-how +is provided. All the different configuration files and their locations +are explained and instructions are given to allow the reader to +configure bindings and so-called ''winprops''. Chapter 4 +explains the notion of drawing engines and graphical styles and how to +write new looks for Ion and more advanced aspects of Ion's scripting +interface are documented in chapter 5 (a work in +progress). + +

    +Finally, most of the functions provided by Ion's scripting interface +are listed and documented in the Function reference in chapter +6. At the end of the document is an alphabetical +listing of all these functions. + +

    + +

    + +

    + + + + diff --git a/doc/ionconf/node3.html b/doc/ionconf/node3.html new file mode 100644 index 0000000..f3dbd29 --- /dev/null +++ b/doc/ionconf/node3.html @@ -0,0 +1,543 @@ + + + + + +2. Preliminaries: Key concepts and relations + + + + + + + + + + + + + + + + + + + + + + +Subsections + + + +
    + +

    + +
    +2. Preliminaries: Key concepts and relations +

    + +

    +The purpose of this chapter to explain some of key concepts and +relations you need to understand before reading the following +chapters. These include modules explained in section 2.1 +and the Ion class and object hierarchies, section 2.2. + +

    + +

    + +
    +2.1 Modules +

    + +

    +Ion has been designed so that the 'ion' executable only implements some +basic services on top of which very different kinds of window managers +could be build by loading the appropriate 'modules'. On modern system +these modules are simply dynamically loaded .so libraries. On +more primitive systems, or if you want to squeeze total size of the +executable and libraries, the modules can optionally be statically +linked to the main binary, but must nevertheless be loaded with the +dopath function. Modules may also include Lua code. + +

    +If no modules are loaded, all client windows appear in full screen mode. +To get better window management support, one or more workspace modules +should be loaded. Currently Ion provides the following modules: + +

    +

    +
    mod_tiling
    +
    Tilings for workspaces of the original tiled + Ion kind. + +
    +
    mod_query
    +
    Queries (for starting programs and so on) + and message boxes. + +
    +
    mod_menu
    +
    Support for menus, both pull-down and + keyboard-operated in-frame menus. + +
    +
    mod_statusbar
    +
    Module that implements a statusbar that + can be adaptively embedded in each workspace's layout. + +
    +
    mod_dock
    +
    Module for docking Window Maker dockapps. + The dock can both float and be embedded as the statusbar. + +
    +
    mod_sp
    +
    This module implements a scratchpad frame that can + be toggled on/off everywhere. Think of the 'console' in some + first-person shooters. + +
    +
    mod_mgmtmode
    +
    Support module for implementing ''management + modes'' with a XOR-frame similar to move/resize mode around selected + region. + +
    +
    mod_sm
    +
    Session management support module. + Loaded automatically when needed! +
    +
    + +

    +So-called drawing engines are also implemented as a modules, +but they are not discussed here; see chapter 4. + +

    +The stock configuration for the 'ion3' executable loads all of the modules +mentioned above except mod_dock and filemod_mgmtmode. +The stock configuration for the 'pwm3' executable (which differs from the +'ion3' executable in a few configuration details, such as Xinerama usage) +loads another set of modules. + +

    + +

    + +

    + +
    +2.2 Class and object hierarchies +

    + +

    +While Ion does not not have a truly object-oriented design +2.1, +things that appear on the computer screen are, however, quite +naturally expressed as such ''objects''. Therefore Ion implements +a rather primitive OO system for these screen objects and some +other things. + +

    +It is essential for the module writer to learn this object +system, but also people who write their own binding configuration files +necessarily come into contact with the class and object hierarchies +- you need to know which binding setup routines apply where, +and what functions can be used as handlers in which bindings. +It is the purpose of this section to attempt to explain these +hierarchies. If you do not wish the read the full section, at least +read the summary at the end of it, so that you understand the very +basic relations. + +

    +For simplicity we consider only the essential-for-basic-configuration +Ioncore, mod_tiling and mod_query classes. +See Appendix B for the full class hierachy visible +to Lua side. + +

    + +

    +2.2.1 Class hierarchy +

    + +

    +One of the most important principles of object-oriented design methodology +is inheritance; roughly how classes (objects are instances of classes) +extend on others' features. Inheritance gives rise to class hierarchy. +In the case of single-inheritance this hierarchy can be expressed as a +tree where the class at the root is inherited by all others below it +and so on. Figure 2.1 lists out the Ion class +hierarchy and below we explain what features of Ion the classes +implement. + +

    + +

    + + + +
    Figure 2.1: +Partial Ioncore, mod_tiling and mod_query + class hierarchy.
    +    Obj
    +     |-->WRegion
    +     |    |-->WClientWin
    +     |    |-->WWindow
    +     |    |    |-->WRootWin
    +     |    |    |-->WMPlex
    +     |    |    |    |-->WScreen
    +     |    |    |    |-->WFrame
    +     |    |    |-->WInput (mod_query)
    +     |    |         |-->WEdln (mod_query)
    +     |    |         |-->WMessage (mod_query)
    +     |    |-->WGroup
    +     |    |    |-->WGroupWS
    +     |    |    |-->WGroupCW
    +     |    |-->WTiling (mod_tiling)
    +     |-->WSplit (mod_tiling)
    +
    +
    + +

    +The core classes: + +

    +

    +
    Obj
    +
    + Is the base of Ion's object system. + +

    +

    +
    WRegion
    +
    + is the base class for everything corresponding to something on the + screen. Each object of type WRegion has a size and position + relative to the parent WRegion. While a big part of Ion + operates on these instead of more specialised classes, WRegion + is a ''virtual'' base class in that there are no objects of ''pure'' + type WRegion; all concrete regions are objects of some class + that inherits WRegion. + +

    +

    +
    WClientWin
    +
    is a class for + client window objects, the objects that window managers are + supposed to manage. + +

    +

    +
    WWindow
    +
    is the base class for all + internal objects having an X window associated to them + (WClientWins also have X windows associated to them). + +

    +

    +
    WRootWin
    +
    is the class for + root windows of X screens. + Note that an ''X screen'' or root window is not necessarily a + single physical screen as a root window + may be split over multiple screens when multi-head extensions + such as Xinerama are used. (Actually there + can be only one WRootWin when Xinerama is used.) + +

    +

    +
    WMPlex
    +
    is a base class for all regions that''multiplex'' + other regions. This means that of the regions managed by the multiplexer, + only one can be displayed at a time. Classes that inhereit WMPlex + include screens and frames. + +

    +

    +
    WScreen
    +
    is the class for objects + corresponding to physical screens. Screens may share a root + window when Xinerama multihead extensions are used as explained + above. + +

    +

    +
    WFrame
    +
    is the class for frames. + While most Ion's objects have no graphical presentation, frames basically + add to WMPlexes the decorations around client windows + (borders, tabs). + +

    +

    +
    WGroup
    +
    is the base class for groups. + Particular types of groups are workspaces + (WGroupWS) + and groups of client windows + (WGroupCW). +
    +
    + +

    +Classes implemented by the mod_tiling module: + +

    +

    +
    WTiling
    +
    is the class for tilings + of frames. + +
    +
    WSplit
    +
    (or, more specifically, classes + that inherit it) encode the WTiling tree structure. +
    +
    + +

    +Classes implemented by the mod_query module: + +

    +

    +
    WInput
    +
    is a virtual base class for the + two classes below. + +
    +
    WEdln
    +
    is the class for the ''queries'', + the text inputs that usually appear at bottoms of frames and sometimes + screens. Queries are the functional equivalent of ''mini buffers'' in + many text editors. + +
    +
    WMessage
    +
    implements the boxes for + warning and other messages that Ion may wish to display to the user. + These also usually appear at bottoms of frames. +
    +
    + +

    +There are also some other ''proxy'' classes that do not refer +to objects on the screen. The only important one of these for +basic configuration is WMoveresMode that is used for +binding callbacks in the move and resize mode. + +

    + +

    +2.2.2 Object hierarchies: WRegion parents and managers +

    + +

    + +

    +2.2.2.1 Parent-child relations +

    +Each object of type WRegion has a parent and possibly a manager +associated to it. The parent for an object is always a +WWindow and for WRegion with an X window (WClientWin, +WWindow) the parent WWindow is given by the same relation of +the X windows. For other WRegions the relation is not as clear. +There is generally very few restrictions other than the above on the +parent--child relation but the most common is as described in +Figure 2.2. + +

    + +

    + + + +
    Figure 2.2: +Most common parent-child relations
    +    WRootWins
    +     |-->WScreens
    +          |-->WGroupWSs
    +          |-->WTilings
    +          |-->WClientWins in full screen mode
    +          |-->WFrames
    +               |-->WGroupCWs
    +               |-->WClientWins
    +               |-->WFrames for transients
    +               |-->a possible WEdln or WMessage
    +
    +
    + +

    +WRegions have very little control over their children as a parent. +The manager WRegion has much more control over its +managed WRegions. Managers, for example, handle resize requests, +focusing and displaying of the managed regions. Indeed the manager--managed +relationship gives a better picture of the logical ordering of objects on +the screen. Again, there are generally few limits, but the most common +hierarchy is given in Figure 2.3. Note that sometimes +the parent and manager are the same object and not all objects may have +a manager (e.g. the dock in the dock module at the time of writing this) +but all have a parent-a screen if not anything else. + +

    + +

    +2.2.2.2 Manager-managed relations +

    + +

    + +

    + + + +
    Figure 2.3: +Most common manager-managed relations
    +    WRootWins
    +     |-->WScreens
    +          |-->WGroupCWs for full screen WClientWins
    +          |    |-->WClientWins
    +          |    |-->WFrames for transients (dialogs)
    +          |         |--> WClientWin
    +          |-->WGroupWSs for workspaces
    +          |    |-->WTiling
    +          |    |    |-->possibly a WEdln, WMessage or WMenu
    +          |    |    |-->WFrames
    +          |    |         |-->WGroupCWs (with contents as above)
    +          |    |-->WFrames for floating content
    +          |-->WFrames for sticky stuff, such as the scratchpad
    +
    +
    + +

    +Note that a workspace can manage another workspace. This can be +achieved with the attach_new function, and allows you to nest +workspaces as deep as you want. + +

    + +

    +2.2.3 Summary +

    + +

    +In the standard setup, keeping queries, messages and menus out of +consideration: + +

    + +

      +
    • The top-level objects that matter are screens and they correspond + to physical screens. The class for screens is WScreen. +
    • +
    • Screens contain (multiplex) groups (WGroup) and other + objects, such as WFrames. Some of these are mutually exclusive + to be viewed at a time. +
    • +
    • Groups of the specific kind WGroupWS often contain a + WTiling tiling for tiling frames (WFrame), but + groups may also directly contain floating frames. +
    • +
    • Frames are the objects with decorations such as tabs and borders. + Frames contain (multiplex) among others (groups of) client windows, + to each of which corresponds a tab in the frame's decoration. Only + one client window (or other object) can be shown at a time in each + frame. The class for client windows is WClientWin. +
    • +
    + +

    +


    Footnotes

    +
    +
    ... design2.1
    +
    the author doesn't like such artificial designs + +
    +
    + + + + + diff --git a/doc/ionconf/node4.html b/doc/ionconf/node4.html new file mode 100644 index 0000000..60acef3 --- /dev/null +++ b/doc/ionconf/node4.html @@ -0,0 +1,1369 @@ + + + + + +3. Basic configuration + + + + + + + + + + + + + + + + + + + + + + +Subsections + + + +
    + +

    + +
    +3. Basic configuration +

    + +

    +This chapter should help your configure Ion to your liking. As the your +probably already know, Ion uses Lua as a configuration and extension +language. If you're new to it, you might first want to read some Lua +documentation as already suggested and pointed to in the Introduction +before continuing with this chapter. + +

    +Section 3.1 is an overview of the multiple configuration +files Ion uses and as a perhaps more understandable introduction to the +general layout of the configuration files, a walk-through of the main +configuration file ion.lua is provided in section +3.2. +How keys and mouse action are bound to functions is described in detail +in 3.3 and in section 3.5 winprops are +explained. For a reference on exported functions, see section +6. + +

    + +

    + +
    +3.1 The configuration files +

    + +

    +Ion3, to which document applies, stores its stock configuration files in +/usr/local/etc/ion3/ unless you, the OS package maintainer or +whoever installed the package on the system has modified the variables +PREFIX or +ETCDIR in +system.mk before compiling Ion. +In the first case you probably know where to find the files and in +the other case the system administrator or the OS package maintainer +should have provided documentation to point to the correct location. +If these instructions are no help in locating the correct directory, +the command locate cfg_ion.lua might help provided updatedb +has been run recently. + +

    +User configuration files go in ~/.ion3/. +Ion always searches the user configuration file directory before the stock +configuration file directory for files. Therefore, if you want to change +some setting, it is advised against that you modify the stock configuration +files in-place as subsequent installs of Ion will restore the stock +configuration files. Instead you should always make a copy of the stock +file in ~/.ion3/ and modify this file. When searching +for a file, if no extension or path component is given, compiled .lc +files are attempted before .lua files. + +

    +All the configuration files are named cfg_*.lua with the ''*'' +part varying. The configuration file for each module mod_modname is +cfg_modname.lua, with modname varying by the module in +question. The following table summarises these and other configuration +files: + +

    + + + + + + + + + + + + + + + + +
    FileDescription
    cfg_ion.luaThe main configuration file
    cfg_ioncore.luaConfiguration file for Ion's core library. + Most of the bindings and menus are configured here. Bindings that are + specific to some module are configured in the module's configuration + file. For details, see section 3.3.
    cfg_kludges.luaSettings to get some applications behave more nicely have been + collected here. See section 3.5.
    cfg_tiling.lua + cfg_query.lua + cfg_menu.lua + cfg_dock.lua + cfg_statusbar.lua + ...Configuration files for different modules.
    + +

    +Additionally, there's the file look.lua that configures the +drawing engine, but it is covered in chapter 4. + +

    + +

    + +
    +3.2 A walk through cfg_ion.lua +

    + +

    +As already mentioned cfg_ion.lua is Ion's main configuration +file. Some basic 'feel' settings are usually configured there and +the necessary modules and other configuration files configuring some +more specific aspects of Ion are loaded there. In this section we +take a walk through the stock cfg_ion.lua. +Notice that most of the settings are commented-out (- is a +line comment in Lua) in the actual file, as they're the defaults +nevertheless. + +

    +The first thing one in the file is to set +

    +META="Mod1+"
    +ALTMETA=""
    +
    +These settings cause most of Ion's key bindings to use Mod1 as the +modifier key. If ALTMETA is set, it is used as modifier for the keys +that don't normally use a modifier. for details on modifiers and key +binding setup in general see section 3.3. + +

    +Next we do some basic feel configuration: + +

    +

    +ioncore.set{
    +    dblclick_delay=250,
    +    kbresize_delay=1500,
    +}
    +
    + +

    +These two will set the delay between button presses in a double click, and +the timeout to quit resize mode in milliseconds. + +

    +

    +ioncore.set{
    +    opaque_resize=true,
    +    warp=true
    +}
    +
    + +

    +The first of these two settings enables opaque resize mode: in move/resize +move frames and other objects mirror you actions immediately. If opaque +resize is disabled, a XOR rubber band is shown during the mode instead. +This will, unfortunately, cause Ion to also grab the X server and has some +side effects. + +

    +Next we load the configuration for Ion's core, and some kludges: + +

    +

    +dopath("cfg_ioncore")
    +dopath("cfg_kludges")
    +
    + +

    +Most bindings and menus are defined in cfg_ioncore.lua. +Details on making such definitions follow in sections 3.3 +and 3.4, respectively. +some kludges or ''winprops'' to make some applications behave better +under Ion are colledted in cfg_kludges.lua; see section +3.5 for details. In addition to these, this file +lists quite a few statements of the form +

    +ioncore.defshortening("[^:]+: (.*)(<[0-9]+>)", "$1$2$|$1$<...$2")
    +
    +These are used to configure how Ion attempts to shorten window titles +when they do not fit in a Tab. The first argument is a POSIX regular +expression that is used to match against the title and the next is +a rule to construct a new title of a match occurs. This particular +rule is used to shorten e.g. 'Foo: barbaz<3>' to 'barba...<3>'; for +details see the function reference entry for ioncore.defshortening. + +

    +To actually be able to do something besides display windows in full screen +mode, we must next load some modules: + +

    +

    +dopath("cfg_modules")
    +--dopath("mod_query")
    +--dopath("mod_menu")
    +--dopath("mod_tiling")
    +--dopath("mod_statusbar")
    +--dopath("mod_dock")
    +--dopath("mod_sp")
    +
    + +

    +We actually load there another file listing the default selection of +modules. If you only want to load additional modules, just uncomment +the corresponding line. If you want to disable loading some modules, +comment out the the line loading cfg_modules, and uncomment +the lines for the modules you want, or add more. + +

    + +

    + +
    +3.3 Keys and rodents +

    + +

    +In the stock configuration file setup, most key and mouse bindings are set +from the file cfg_ioncore.lua while module-specific bindings +are set from the modules' main configuration files (cfg_modname.lua). +This, however, does not have to be so as long as the module has been +loaded prior to defining any module-specific bindings. + +

    +Bindings are defined by calling the function +defbindings with the ''context'' of the +bindings and the a table of new bindings to make. The context is simply +string indicating one of the classes of regions (or modes such as +WMoveresMode) introduced in section 2.2, and fully +listed in appendix B, although not all define +a binding map. For example, the following skeleton would be used to +define new bindings for all frames: + +

    +

    +defbindings("WFrame", {
    +    -- List of bindings to make goes here.
    +})
    +
    + +

    +There has been some confusion among users about the need to define the +''context'' for each binding, so let me try to explain this design +decision here. The thing is that if there was a just a simple 'bind this +key to this action' method without knowledge of the context, some +limitations would have to be made on the available actions and writing +custom handlers would be more complicated. In addition one may want to +bind the same function to different key for different types of objects. +Indeed, the workspace and frame tab switching functions are the same both +classes being based on WMPlex, and in the stock configuration the +switch to :th workspaces is bound to Mod1+n while the switch to +:th tab is bound to the sequence Mod1+k n. + +

    +Currently known ''contexts'' include: +WScreen, +WMPlex, +WMPlex.toplevel, +WFrame, +WFrame.toplevel, +WFrame.floating, +WFrame.tiled, +WFrame.transient, +WMoveresMode, +WGroup, +WGroupCW, +WGroupWS, +WClientWin, +WTiling, and +WStatusBar. +Most of these should be self-explanatory, corresponding to objects +of class with the same name. The ones with .toplevel suffix +refer to screens and ''toplevel'' frames, i.e. frames that are +not used for transient windows. Likewise .transient refers +to frames in transient mode, and .tiled and .floating +to frames in, respectively, tiled and floating modes. + +

    +The following subsections describe how to construct elements of the +binding table. Note that defbindings adds +the the newly defined bindings to the previous bindings of the context, +overriding duplicates. To unbind an event, set the handler parameter +to nil for each of the functions to be described in the following +subsections. + +

    +Also note that when multiple objects want to handle a binding, the +innermost (when the root window is considered the outermost) active object +in the parent-child hierarchy (see Figure 2.2) of objects +gets to handle the action. + +

    + +

    +3.3.1 Binding handlers and special variables +

    + +

    +Unlike in Ion2, in Ion3 binding handlers are not normally passed as +''anonymous functions'', although this is still possible. The preferred +method now is to pass the code of the handler as a string. Two special +variables are available in this code. These are + +

    + + + + + + + + + + + + + +
    VariableDescription
    _ (underscore)Reference to the object on which the + binding was triggered. The object is of the same class as the the + context of the defbindings call + defining the binding.
    _subUsually, the currently active managed object of the + object referred to by _, but sometimes (e.g. mouse actions + on tabs of frames) something else relevant to the action triggering + the binding.
    _chldObject corresponding to the currently active child window of the + object referred to by _.
    + +

    +For example, supposing '_' is a WFrame, the following +handler should move the active window to the right, if possible: + +

    +

    +"_:inc_index(_sub)"
    +
    + +

    + +

    +3.3.2 Guards +

    + +

    +To suppress error messages, each binding handler may also be accompanied +by a ''guard'' expression that blocks the handler from being called when +the guard condition is not met. Currently the following guard expressions +are supported (for both _sub and _chld): + +

    + + + + + + + + + + +
    GuardDescription
    "_sub:non-nil"The _sub parameter must be set.
    "_sub:SomeClass"The _sub parameter must be member + of class SomeClass.
    + +

    + +

    + +
    +3.3.3 Defining the bindings +

    + +

    +The descriptions of the individual bindings in the binding table argument +to defbindings should be constructed with the following +functions. + +

    +Key presses: + +

      +
    • kpress(keyspec, handler [, guard]), +
    • +
    • kpress_wait(keyspec, handler [, guard]) and +
    • +
    • submap(keyspec, { ... more key bindings ... }). +
    • +
    +Mouse actions: + +
      +
    • mclick(buttonspec, handler [, guard]), +
    • +
    • mdblclick(buttonspec, handler [, guard]), +
    • +
    • mpress(buttonspec, handler [, guard]) and +
    • +
    • mdrag(buttonspec, handler [, guard]). +
    • +
    + +

    +The actions that most of these functions correspond to should be clear +and as explained in the reference, kpress_wait is simply +kpress with a flag set instructing Ioncore wait for all +modifiers to be released before processing any further actions. +This is to stop one from accidentally calling e.g. +WRegion.rqclose multiple times in a row. The submap +function is used to define submaps or ''prefix maps''. The second +argument to this function is table listing the key press actions +(kpress) in the submap + +

    +The parameters keyspec and buttonspec are explained below +in detail. The parameter handler is the handler for the binding, +and the optional parameter guard its guard. These should normally +be strings as explained above. + +

    + +

    +3.3.4 Examples +

    + +

    +For example, to just bind the key Mod1+1 to switch to the first +workspace and Mod1+Right to the next workspace, you would make the +following call +

    +defbindings("WScreen", {
    +    kpress("Mod1+Right", "_:switch_next()"),
    +    kpress("Mod1+1", "_:switch_nth(1)"),
    +})
    +
    + +

    +Note that _:switch_nth(1) is the same as calling +WMPlex.switch_next(_, 1) as WScreen inherits +WMPlex and this is where the function is actually defined. + +

    +Similarly to the above example, to bind the key sequence Mod1+k n +switch to the next managed object within a frame, and Mod1+k 1 to the +first, you would issue the following call: +

    +defbindings("WFrame", {
    +    submap("Mod1+K", {
    +        kpress("Right", "_:switch_next()"),
    +        kpress("1", "_:switch_nth(1)"),
    +   }),
    +})
    +
    + +

    + +

    +3.3.5 Key specifications +

    + +

    +As seen above, the functions that create key binding specifications require +a keyspec argument. This argument should be a string containing the +name of a key as listed in the X header file keysymdef.h3.1 without the XK_ prefix. + +Most of the key names are quite intuitive while some are not. For example, +the Enter key on the main part of the keyboard has the less common +name Return while the one the numpad is called KP_Enter. + +

    +The keyspec string may optionally have multiple ''modifier'' names +followed by a plus sign (+) as a prefix. X defines the following +modifiers: +

    +Shift, Control, Mod1 to Mod5, +AnyModifier and Lock. + + + + + + +
    + +

    +X allows binding all of these modifiers to almost any key and while this +list of modifiers does not explicitly list keys such as +Alt that are common on modern keyboards, such +keys are bound to one of the ModN. On systems running XFree86 +Alt is usually Mod1. On Suns Mod1 is the diamond key +and Alt something else. One of the ''flying window'' keys on so +called Windows-keyboards is probably mapped to Mod3 if you have +such a key. Use the program xmodmap +to find out what exactly is bound where. + +

    +Ion defaults to AnyModifier in submaps. This can sometimes lead to +unwanted effects when the same key is used with and without explicitly +specified modifiers in nested regions. For this reason, Ion recognises +NoModifier as a special modifier that can be used to reset this +default. + +

    +Ion ignores the Lock modifier and any ModN () +bound to NumLock or +ScrollLock +by default because such3.2 locking keys may otherwise +cause confusion. + +

    + +

    +3.3.6 Button specifications +

    + +

    +Button specifications are similar to key definitions but now +instead of specifying modifiers and a key, you specify modifiers +and one of the button names Button1 to +Button5. Additionally the +specification may end with an optional area name following an @-sign. +Only frames currently support areas, and the supported values in this +case are +"border", "tab", "empty_tab", "client" and +nil (for the whole frame). + +

    +For example, the following code binds dragging a tab with the first +button pressed to initiate tab drag&drop handling: + +

    +

    +defbindings("WFrame", {
    +    mdrag("Button1@tab", "_:p_tabdrag()"),
    +})
    +
    + +

    + +

    +3.3.7 A further note on the default binding configuration +

    + +

    +The default binding configuration contains references to the variables +META and ALTMETA instead of directly using the default +values of "Mod1+" and "" (nothing). As explained in +section 3.2, the definitions of these variables +appear in cfg_ion.lua. This way you can easily change the the +modifiers used by all bindings in the default configuration without +changing the whole binding configuration. Quite a few people prefer +to use the Windows keys as modifiers because many applications already +use Alt. Nevertheless, Mod1 is the default as a key bound +to it is available virtually everywhere. + +

    + +

    + +
    +3.4 Menus +

    + +

    + +

    +3.4.1 Defining menus +

    + +

    + + + + +In the stock configuration file setup, menus are defined in the file +cfg_menus.lua as previously mentioned. The mod_menu module +must be loaded for one to be able to define menus, and this is done with +the function defmenu provided by it. + +

    +Here's an example of the definition of a rather simple menu with a submenu: + +

    +

    +defmenu("exitmenu", {
    +    menuentry("Restart", "ioncore.restart()"),
    +    menuentry("Exit", "ioncore.shutdown()"),
    +})
    +
    +defmenu("mainmenu", {
    +    menuentry("Lock screen", "ioncore.exec('xlock')"),
    +    menuentry("Help", "mod_query.query_man(_)"),
    +    submenu("Exit", "exitmenu"),
    +})
    +
    + +

    +The menuentry function is used to create an entry in the +menu with a title and an entry handler to be called when the menu entry +is activated. The parameters to the handler are similar to those of binding +handlers, and usually the same as those of the binding that opened the menu. + +

    +The submenu function is used to insert a submenu at that +point in the menu. (One could as well just pass a table with the menu +entries, but it is not encouraged.) + +

    + +

    +3.4.2 Special menus +

    + +

    +The menu module predefines the following special menus. These can be used +just like the menus defined as above. + +

    + + + + + + + + + + + + + + + + +
    Menu nameDescription
    windowlistList of all client windows. Activating an entry jumps to that window.
    workspacelistList of all workspaces. Activating an entry jumps to that workspaces.
    stylemenuList of available look_*.lua style files. Activating an entry + loads that style and ask to save the selection.
    ctxmenuContext menu for given object.
    + +

    + +

    +3.4.3 Defining context menus +

    + +

    +The ''ctxmenu'' is a special menu that is assembled from a defined context +menu for the object for which the menu was opened for, but also includes +the context menus for the manager objects as submenus. + +

    +Context menus for a given region class are defined with the +defctxmenu function. This is other ways similar to +defmenu, but the first argument instead being the name +of the menu, the name of the region class to define context menu for. +For example, here's part of the stock WFrame context menu +definition: + +

    +

    +defctxmenu("WFrame", {
    +    menuentry("Close", "WRegion.rqclose_propagate(_, _sub)"),
    +    menuentry("Kill",  "WClientWin.kill(_sub)", "_sub:WClientWin"),
    +})
    +
    + +

    +Some of the same ''modes'' as were available for some bindings +may also be used: WFrame.tiled, WFrame.floating, +and WFrame.transient. + +

    + +

    + +
    +3.4.4 Displaying menus +

    + +

    +The following functions may be used to display menus from binding +handlers (and elsewhere): + +

    + + + + + + + + + + + + + + + + +
    FunctionDescription
    mod_menu.menuKeyboard (or mouse) operated menus that open in the bottom-left corner + of a screen or frame.
    mod_menu.bigmenuSame as previous, but uses another graphical style.
    mod_menu.pmenuMouse-operated drop-down menus. This function can only be called from a + mouse press or drag handler.
    mod_menu.grabmenuA special version of mod_menu.menu that grabs the keyboard + and is scrolled with a given key until all modifiers have been released, + after which the selected entry is activated. This function is meant to + be used for implementing, for example, Win***s-style Alt-Tab + handling.3.3
    + +

    +The grabmenu function takes the extra key parameter, but +aside from that each of these functions takes three arguments, which when +called from a binding handler, should be the parameters to the handler, and +the name of the menu. For example, the following snippet of of code binds +the both ways to open a context menu for a frame: + +

    +

    +defbindings("WFrame", {
    +    kpress(MOD1.."M", "mod_menu.menu(_, _sub, 'ctxmenu')"),
    +    mpress("Button3", "mod_menu.pmenu(_, _sub, 'ctxmenu')"),
    +})
    +
    + +

    + +

    + +
    +3.5 Winprops +

    + +

    +The so-called ''winprops'' can be used to change how +specific windows are handled and to set up some kludges to deal with +badly behaving applications. They are defined by calling the function +defwinprop with a table containing the properties to set and the +necessary information to identify a window. The currently supported +winprops are listed below, and the subsequent subsections explain the +usual method of identifying windows, and how to obtain this information. + +

    + +

    + +

    +
    Winprop:
    +
    acrobatic (boolean) + +
    +
    Description:
    +
    + Set this to true for Acrobat Reader. It has an annoying + habit of trying to manage its dialogs instead of setting them as + transients and letting the window manager do its job, causing + Ion and acrobat go a window-switching loop when a dialog is + opened. + +
    +
    + +

    + +

    +
    Winprop:
    +
    aspect (table) + +
    +
    Description:
    +
    + The table should contain the entries w and h that + override application-supplied aspect ratio hint. + +
    +
    + +

    + +

    +
    Winprop:
    +
    float (boolean) + +
    +
    Description:
    +
    + Set this to open the window in a floating frame, when + in a group. + +
    +
    + +

    + +

    +
    Winprop:
    +
    fullscreen (boolean) + +
    +
    Description:
    +
    + Should the window be initially in full screen mode? + +
    +
    + +

    + +

    +
    Winprop:
    +
    ignore_cfgrq (boolean) + +
    +
    Description:
    +
    + Should configure requests on the window be ignored? + Only has effect on floating windows. + +
    +
    + +

    + +

    +
    Winprop:
    +
    ignore_net_active_window (boolean) + +
    +
    Description:
    +
    + Ignore extended WM hints _NET_ACTIVE_WINDOW request. + +
    +
    + +

    + +

    +
    Winprop:
    +
    ignore_resizeinc (boolean) + +
    +
    Description:
    +
    + Should application supplied size increments be ignored? + +
    +
    + +

    + +

    +
    Winprop:
    +
    jumpto (boolean) + +
    +
    Description:
    +
    + Should a newly created client window always be made + active, even if the allocated frame isn't. + +
    +
    + +

    + +

    +
    Winprop:
    +
    max_size (table) + +
    +
    Description:
    +
    + The table should contain the entries w and h that + override application-supplied maximum size hint. + +
    +
    + +

    + +

    +
    Winprop:
    +
    min_size (table) + +
    +
    Description:
    +
    + Similar to max_size but for the minimum size hint. + +
    +
    + +

    + +

    +
    Winprop:
    +
    oneshot (boolean) + +
    +
    Description:
    +
    + Discard this winprop after first use. + +
    +
    + +

    + +

    +
    Winprop:
    +
    switchto (boolean) + +
    +
    Description:
    +
    + Should a newly mapped client window be switched to within + its frame. + +
    +
    + +

    + +

    +
    Winprop:
    +
    target (string) + +
    +
    Description:
    +
    + The name of an object (workspace, frame) that should manage + windows of this type. + +
    +
    + +

    + +

    +
    Winprop:
    +
    transient_mode (string) + +
    +
    Description:
    +
    + "normal": No change in behaviour. "current": The window + should be thought of as a transient for the current active + client window (if any) even if it is not marked as a + transient by the application. "off": The window should be + handled as a normal window even if it is marked as a + transient by the application. + +
    +
    + +

    + +

    +
    Winprop:
    +
    transients_at_top (boolean) + +
    +
    Description:
    +
    + When transients are managed by the client window itself (as it + is the case on tiled workspaces), should the transients be + placed at the top of the window instead of bottom? + +
    +
    + +

    + +

    +
    Winprop:
    +
    transparent (boolean) + +
    +
    Description:
    +
    + Should frames be made transparent when this window is selected? +
    + +
    +
    + +

    + +

    + +
    +3.5.1 Classes, roles and instances +

    + +

    +The identification information in the winprop specification is usually the +class, +role, +instance and +name +of the window. The name field is a Lua-style regular expression +matched against the window's title and the rest are strings that must +exactly match the corresponding window information. It is not necessary +to specify all of these fields. + +

    +Ion looks for a matching winprop in the order listed by the following +table. An 'E' indicates that the field must be set in the winprop +and it must match the window's corresponding property exactly or, in +case of name, the regular expression must match the window +title. An asterisk '*' indicates that a winprop where the field is +not specified (or is itself an asterisk in case of the first three +fields) is tried. + +

    +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    classroleinstancename
    EEEE
    EEE*
    EE*E
    EE**
    E*EE
    E*E*
    E**E
       etc.
    +
    + +

    +If there are multiple winprops with other identification information +the same but different name, the longest match is chosen. + +

    + +

    +3.5.2 Finding window identification +

    + +

    +The 'Window info' context menu entry (Mod1+M or Button3 on a tab) +can be used to list the identification information required to set winprops +for a window and all the transient windows managed within it. + +

    + +Another way to get the identification information is to use xprop. +Simply run To get class and instance, simply run xprop WM_CLASS +and click on the particular window of interest. The class is the latter of +the strings while the instance is the former. To get the role - few +windows have this property - use the command xprop WM_ROLE. +This method, however, will not work on transients. + +

    + +So-called ''transient windows'' are usually short-lived dialogs (although +some programs abuse this property) that have a parent window that they are +''transient for''. On tiled workspaces Ion displays these windows +simulatenously with the parent window at the bottom of the same frame. +Unfortunately xprop is stupid and can't cope with this situation, +returning the parent window's properties when the transient is clicked on. +For this reason you'll have to do a little extra work to get the properties +for that window.3.4 +

    +Finally, it should be mentioned that too many authors these days +''forget'' to set this vital identification to anything meaningful: +everything except name is the same for all of the programs's +windows, for example. + +

    + +

    +3.5.3 Some common examples +

    + +

    + +

    +3.5.3.1 Acrobat Reader +

    + +

    +The following is absolutely necessary for Acrobat reader: + +

    +

    +defwinprop{
    +    class = "AcroRead",
    +    instance = "documentShell",
    +    acrobatic = true,
    +}
    +
    + +

    + +

    +3.5.3.2 Fixing a Mozilla Firebird transient +

    + +

    +Mozilla Firebird (0.7) incorrectly does not set the WM_TRANSIENT_FOR +property for the dialog that is used to ask the action to take for a file. +It, however, sets the the property point to the main window for the save +dialog. This can be annoying and confusing, as the first dialog is not +closed before the second is displayed. + +

    +We'd like the first dialog to be transient to the main window. The closest +we can get to that is to consider it transient to the current window (if +there's one). Unfortunately Firebird does not set any meaningful classes, +instances or roles for the windows, so we'll have to rely on an ugly title +match. + +

    +

    +defwinprop{
    +    class = "MozillaFirebird-bin",
    +    name = "Opening .*",
    +    transient_mode = "current",
    +}
    +
    + +

    + +

    +3.5.3.3 Forcing newly created windows in named frames +

    + +

    +The following winprop should place xterm started with command-line parameter +-name sysmon and running a system monitoring program in a +particular frame: +

    +defwinprop{
    +    class = "XTerm",
    +    instance = "sysmon",
    +    target = "sysmonframe",
    +}
    +
    + +

    +For this example to work, we have to somehow create a frame named +sysmonframe. One way to do this is to make the following +call in the Mod1+F3 Lua code query: + +

    +

    +mod_query.query_renameframe(_)
    +
    + +

    +Recall that _ points to the multiplexer (frame or screen) in which +the query was opened. Running this code should open a new query prefilled +with the current name of the frame. In our example we would change the +name to sysmonframe, but we could just as well have used the +default name formed from the frame's class name and an instance number. + +

    + +

    +


    Footnotes

    +
    +
    ...keysymdef.h3.1
    +
    This file can usually be found in the directory +/usr/X11R6/include/X11/. + +
    +
    ... such3.2
    +
    Completely useless keys that should be +gotten rid of in the author's opinion. + +
    +
    ... handling.3.3
    +
    See the wcirculate.lua script in the Ion + scripts repository http://iki.fi/tuomov/repos/ion-scripts-3/. + +
    +
    ... window.3.4
    +
    There's a patch to xprop to +fix this, but nothing seems to be happening with respect to including it in +XFree86. + +
    +
    + + + + + diff --git a/doc/ionconf/node5.html b/doc/ionconf/node5.html new file mode 100644 index 0000000..b6951d4 --- /dev/null +++ b/doc/ionconf/node5.html @@ -0,0 +1,720 @@ + + + + + +4. Graphical styles + + + + + + + + + + + + + + + + + + + + + + +Subsections + + + +
    + +

    + +
    +4. Graphical styles +

    + +

    +This chapter first gives in section 4.1 a general outline +of how drawing engines are used, of style specifications and then +in section 4.2 describes how to specify styles +for the default drawing engine. + +

    + +

    + +
    +4.1 Drawing engines, style specifications and sub-styles +

    + +

    +Ion's drawing routines are abstracted into so-called drawing engine +modules that can, again depending on the system, be dynamically +loaded as needed. The drawing engine modules provide ''brushes'' +that objects can use to draw some high-level primitives such +as borders and text boxes (in addition to simple text and rectangle +drawing) on their windows and configure e.g. the shape and +background of the window. While the drawing engines therefore +do not directly implement looks for each possible object (that +would hardly be maintainable), different brush styles can be +used to give a distinctive look to different objects and engines +could interpret some styles as special cases. Style specifications +are strings of the form + +

    +

    +element1-element2-...-elementn
    +
    + +

    +An example of such a style specification is tab-frame; +see the table in subsection 4.1.1 for more styles. + +

    +When an object asks for a brush of certain style, the selected +drawing engine will attempt to find the closest match to this +specification. The styles/brushes defined by the drawing engines +may have asterisks (*) as some of the elements indicating +a match to anything. Exact matches are preferred to asterisk +matches and longer matches to shorter. For example, let a brush +for style foo-bar-baz be queried, then the following +brushes are in order of preference: + +

    +

    +foo-bar-baz
    +foo-*-baz
    +foo-bar
    +*
    +foo-baz   -- Doesn't match, not selected!
    +
    + +

    +Some of the drawing primitives allow extra attributes to be +specified, also in the form +

    +attr1-attr2-...-attrn
    +
    +These extra attributes are called substyles +and allow, for example, the state of the object to be indicated +by different colour sets while keeping the interface at an +abstract level and the drawing engine completely ignorant +of the semantics - only the writer of the drawing engine +configuration file has to know them. However the drawing +engine can again interpret known substyles as special cases +and the default engine indeed does so with frame tab +tag and drag states.) + +

    + +

    + +
    +4.1.1 Known styles and substyles +

    + +

    + +

    +4.1.1.1 Frames +

    + +

    + + + + + + + + + + + + + + + + + + + +
    Style nameDescription
    frameStyle for frames. + Substyles: active, inactive.
    frame-tiledA more specific style for tiled frames. + Substyles as for frame.
    frame-tiled-altAn alternative style for tiled frames. + Often used to disable the tab-bar.
    frame-floatingA more specific style for floating + frames.
    frame-transientA more specific style for frames + containing transient windows.
    + +

    + +

    +4.1.1.2 Tabs and menu entries +

    + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Style nameDescription
    tabStyle for frames' tabs and menu entries. + Substyles: combinations of the form a-s where + a is one of active/inactive and + s is one of selected/unselected
    tab-frameA more specific style for frames' tabs. + Substyles: combinations of the form a-s-t-d-u where + a and s are as above and + t is one of tagged/not_tagged, + d is one of dragged/not_dragged and + u is one of activity/no_activity.
    tab-frame-tiled, 
    tab-frame-tiled-alt, 
    tab-frame-floating, 
    tab-frame-transientMore specific styles for frames in the + different modes.
    tab-menuentryA more specific style for entries in WMenus.
    tab-menuentry-bigmenuAn alternate style for entries in WMenus.
    + +

    + +

    +4.1.1.3 The rest +

    + +

    + + + + + + + + + + + + + + + + + + + + + + + + + +
    Style nameDescription
    inputA style for WInputs.
    input-edlnA more specific style for WEdlns. + Substyles: selection for selected text and + cursor for the cursor indicating current editing point.
    input-messageA more specific style for WMessages.
    input-menuA more specific style for WMenus.
    input-menu-bigmenuAn alternate style for WMenus.
    moveres_displayThe box displaying position/size when + moving or resizing frames.
    dockThe dock.
    + +

    + +

    + +
    +4.2 Defining styles for the default drawing engine +

    + +

    +Drawing engine style files are usually named +look_foo.lua where foo is the name of the +style. The file that Ion loads on startup or when +gr.read_config is called, however, is look.lua +and should usually be symlinked to or a copy of of some +look_foo.lua. + +

    + +

    +4.2.1 The structure of the configuration files +

    + +

    +The first thing to do in a stylefile is to choose the drawing +engine, possibly loading the module as well. This is done +with the following chunk of code. + +

    +

    +if not gr.select_engine("de") then 
    +    return 
    +end
    +
    + +

    +The gr.select_engine function sees if the engine +given as argument is registered (the default drawing engine is +simply called ''de''). If the engine could not be found, it +tries to load a module of the same name. If the engine still +is not registered, gr.select_engine returns false +and in this case we also exit the style setup script. +If the engine was found, gr.select_engine sees that +further requests for brushes are forwarded to that engine +and returns true. + +

    +Before defining new styles it may be a good idea to clear old +styles from memory so if the old configuration defines more +specific styles than the new, the old styles don't override +those specified by the new configuration. That can be done by +calling + +

    +

    +de.reset()
    +
    + +

    +After this the new styles can be defined with de.defstyle +as explained in the next subsection. Finally, after the styles have +been defined we must ask objects on the screen to look up new brushes +to reflect the changes in configuration. This is done with + +

    +

    +gr.refresh()
    +
    + +

    + +

    +4.2.2 Defining the styles +

    + +

    +Styles for the default drawing engine are defined with the +function de.defstyle. It has two arguments the first being +a style specification as explained in previous sections and the second +a table whose fields describe the style: + +

    +

    +de.defstyle("some-style", {
    +    attribute = value,
    +    ...
    +})
    +
    + +

    +The supported attributes are described in tables below. The different +border elements and styles referred to there are explained in Figure +4.1. + +

    + +

    + + + +
    Figure 4.1: +Sketch of different border styles and elements
    +Elevated:        Inlaid:         Ridge:            Groove:        
    +  hhhhhhhhhhhs     ............	   hhhhhhhhhhhs      sssssssssssh
    +  h..........s     .sssssssssh.	   h..........s      s..........h
    +  h.        .s     .s        h.	   h.sssssssh.s      s.hhhhhhhs.h
    +  h.        .s     .s        h.	   h.s      h.s      s.h      s.h
    +  h.        .s     .s        h.	   h.shhhhhhh.s      s.hsssssss.h
    +  h..........s     .shhhhhhhhh.	   h..........s      s..........h
    +  hsssssssssss     ............	   hsssssssssss      shhhhhhhhhhh
    +
    +h = highlight, s = shadow, . = padding
    +
    +
    + +

    + +

    +4.2.2.1 Colours +

    + +

    +Each of these fields a string of the form that can be +passed to XAllocNamedColor. Valid strings are e.g. +hexadecimal RGB specifications of the form +#RRGGBB and colour names as specified +in /usr/X11R6/lib/X11/rgb.txt (exact path varying). + +

    + + + + + + + + + + + + + + + + + + + +
    FieldDescription
    highlight_colourColour for the ''highlight'' part of a border.
    shadow_colourColour for the ''highlight'' part of a border.
    foreground_colourColour for the normal drawing operations, e.g. text.
    background_colourWindow background colour (unless transparency is enabled) and + background colour boxes.
    padding_colourColour for the ''padding'' part of a border border. Set to + background_colour if unset.
    + +

    + +

    +4.2.2.2 Borders and widths +

    + +

    +All other fields below except border_style are non-negative integers +indicating a number of pixels. + +

    + + + + + + + + + + + + + + + + + + + +
    FieldDescription
    border_styleA string indicating the style of border; one of + elevated/inlaid/ridge/groove as seen in the + above sketch.
    highlight_pixelsWidth of the highlight part of the border in pixels.
    shadow_pixelsWidth of the shadow part of the border in pixels.
    padding_pixelsWidth of the padding part of the border in pixels.
    spacingSpace to be left between all kinds of boxes.
    + +

    + +

    +4.2.2.3 Text +

    + +

    + + + + + + + + + + +
    FieldDescription
    fontFont to be used in text-drawing operations; standard X font + name.
    text_alignHow text is to be aligned in text boxes/tabs; one of + the strings left/right/center.
    + +

    + +

    +4.2.2.4 Miscellaneous +

    + +

    + + + + + + + + + + +
    FieldDescription
    transparent_backgroundShould windows' that use this style + background be transparent? true/false.
    based_onThe name of a previously defined style that this + style should be based on.
    + +

    + +

    +4.2.2.5 Substyles +

    + +

    +As discussed in previous sections, styles may have substyles to e.g. +indicate different states of the object being drawn. The ''de'' engine +limits what can be configured in substyles to the set of colours in the +first table above, but also specifically interprets for the main style +tab-frame the substyles *-*-tagged and *-*-*-dragged +by, respectively, drawing a right angle shape at the top right corner +of a tab and by shading the tab with a stipple pattern. Also for +menus the substyles *-*-submenu are handled as a special case. + +

    +Substyles are defined with the function de.substyle within the +table defining the main style. The parameters to this function are +similar to those of de.defstyle. + +

    +

    +de.defstyle("some-style", {
    +   ...
    +   de.substyle("some-substyle", {
    +      ...
    +   }),
    +   ...
    +})
    +
    + +

    + +

    +4.2.3 An example +

    + +

    +The following shortened segment from look_cleanviolet.lua +should help to clarify the matters discussed in the previous +subsection. + +

    +

    +de.defstyle("*", {
    +    -- Gray background
    +    highlight_colour = "#eeeeee",
    +    shadow_colour = "#eeeeee",
    +    background_colour = "#aaaaaa",
    +    foreground_colour = "#000000",
    +    
    +    shadow_pixels = 1,
    +    highlight_pixels = 1,
    +    padding_pixels = 1,
    +    spacing = 0,
    +    border_style = "elevated",
    +    
    +    font = "-*-helvetica-medium-r-normal-*-12-*-*-*-*-*-*-*",
    +    text_align = "center",
    +})
    +
    +de.defstyle("tab-frame", {
    +    based_on = "*",
    +    
    +    de.substyle("active-selected", {
    +        -- Violet tab
    +        highlight_colour = "#aaaacc",
    +        shadow_colour = "#aaaacc",
    +        background_colour = "#666699",
    +        foreground_colour = "#eeeeee",
    +    }),
    +
    +    --  More substyles would follow ...
    +})
    +
    + +

    + +

    +4.3 Miscellaneous settings +

    + +

    +The following style fields are independent of the drawing engine used, +but are related to objects' styles and therefore configured in the drawing +engine configuration file. + +

    + +

    +4.3.1 Extra fields for style frame +

    + +

    + + + + + + + + + + + + + +
    FieldDescription
    barControls the style of the tab-bar. Possible values + are the strings "none", "inside", "outside" + and "shaped", with the last providing the PWM-style + tab-bars for floating frames.
    floatframe_tab_min_wMinimum tab width in pixels for + the shaped style, given that this number times number of tabs + doesn't exceed frame width.
    floatframe_bar_max_w_qMaximum tab-bar width quotient of + frame width for the shaped styles. A number in the + interval .
    + +

    + +

    +4.3.2 Extra fields for style dock +

    + +

    + + + + + + + + + + +
    FieldDescription
    outline_styleHow borders are drawn: + "none" - no border, + "all" - border around whole dock, + "each" - border around each dockapp.
    tile_sizeA table with entries width and height, + indicating the width and height of tiles in pixels.
    + +

    +Hopefully that's enough to get you started in writing new style +configuration files for Ion. When in doubt, study the existing +style configuration files. + +

    + +

    + + + + diff --git a/doc/ionconf/node6.html b/doc/ionconf/node6.html new file mode 100644 index 0000000..b1bbe8b --- /dev/null +++ b/doc/ionconf/node6.html @@ -0,0 +1,351 @@ + + + + + +5. Scripting + + + + + + + + + + + + + + + + + + + + + + +Subsections + + + +
    + +

    + +
    +5. Scripting +

    + +

    +This chapter documents some additional features of the Ion configuration +and scripting interface that can be used for more advanced scripting than +the basic configuration exlained in chapter 3. + +

    + +

    + +
    +5.1 Hooks +

    + +

    +Hooks are lists of functions to be called when a certain event occurs. +There are two types of them; normal and ''alternative'' hooks. Normal +hooks do not return anything, but alt-hooks should return a boolean +indicating whether it handled its assigned task succesfully. In the case +that true is returned, remaining handlers are not called. + +

    +Hook handlers are registered by first finding the hook +with ioncore.get_hook and then calling WHook.add +on the (succesfull) result with the handler as parameter. Similarly +handlers are unregistered with WHook.remove. For example: + +

    +

    +ioncore.get_hook("ioncore_snapshot_hook"):add(
    +    function() print("Snapshot hook called.") end
    +)
    +
    + +

    +In this example the hook handler has no parameters, but many hook +handlers do. The types of parameters for each hook are listed in +the hook reference, section 6.8. + +

    +Note that many of the hooks are called in ''protected mode'' and can not +use any functions that modify Ion's internal state. TODO: More detailed +documentation when this is final. + +

    + +

    +5.2 Referring to regions +

    + +

    + +

    +5.2.1 Direct object references +

    + +

    +All Ion objects are passed to Lua scriptss as 'userdatas', and you may +safely store such object references for future use. The C-side object +may be destroyed while Lua still refers to the object. All exported +functions gracefully fail in such a case, but if you need to explicitly +test that the C-side object still exists, use obj_exists. + +

    +As an example, the following short piece of code implements +bookmarking: + +

    +

    +local bookmarks={}
    +
    +-- Set bookmark bm point to the region reg
    +function set_bookmark(bm, reg)
    +    bookmarks[bm]=reg
    +end
    +
    +-- Go to bookmark bm
    +function goto_bookmark(bm)
    +    if bookmarks[bm] then
    +        -- We could check that bookmarks[bm] still exists, if we
    +        -- wanted to avoid an error message.
    +        bookmarks[bm]:goto()
    +    end
    +end
    +
    + +

    + +

    +5.2.2 Name-based lookups +

    + +

    +If you want to a single non-WClientWin region with an exact known +name, use ioncore.lookup_region. If you want a list of all regions, +use ioncore.region_list. Both functions accept an optional argument +that can be used to specify that the returned region(s) must be of a more +specific type. Client windows live in a different namespace and for them +you should use the equivalent functions ioncore.lookup_clientwin +and ioncore.clientwin_list. + +

    +To get the name of an object, use WRegion.name. Please be +aware, that the names of client windows reflect their titles and +are subject to changes. To change the name of a non-client window +region, use WRegion.set_name. + +

    + +

    +5.3 Alternative winprop selection criteria +

    + +

    +It is possible to write more complex winprop selection routines than +those described in section 3.5. To match a particular +winprop using whatever way you want to, just set the match +field of the winprop to a function that receives the client window +as its sole parameter, and that returns true if the winprop +matches, and false otherwise. + +

    +The class, instance and role properties can be obtained with +WClientWin.get_ident, and the title with WRegion.name. +If you want to match against (almost) arbitrary window properties, +have a look at the documentation for the following functions, and +their standard Xlib counterparts: ioncore.x_intern_atom +(XInternAtom), ioncore.x_get_window_property (XGetWindowProperty), +and ioncore.x_get_text_property (XGetTextProperty). + +

    + +

    + +

    +5.4 Writing ion-statusd monitors +

    + +

    +All statusbar meters that do not monitor the internal state of Ion should +go in the separate ion-statusd program. + +

    +Whenever the user requests a meter %foo or %foo_bar to be +inserted in a statusbar, mod_statusbar asks ion-statusd to +load statusd_foo.lua on its search path (same as that for Ion-side +scripts). This script should then supply all meters with the initial part +'foo'. + +

    +To provide this value, the script should simply call statusd.inform +with the name of the meter and the value as a string. +Additionally the script should provide a 'template' for the meter to +facilitate expected width calculation by mod_statusbar, and +may provide a 'hint' for colour-coding the value. The interpretation +of hints depends on the graphical style in use, and currently the +stock styles support the normal, important and +critical hints. + +

    +In our example of the 'foo monitor', at script init we might broadcast +the template as follows: + +

    +

    +statusd.inform("foo_template", "000")
    +
    + +

    +To inform mod_statusbar of the actual value of the meter and +indicate that the value is critical if above 100, we might write the +following function: + +

    +

    +local function inform_foo(foo)
    +    statusd.inform("foo", tostring(foo))
    +    if foo>100 then
    +        statusd.inform("foo_hint", "critical")
    +    else
    +        statusd.inform("foo_hint", "normal")
    +    end
    +end
    +
    + +

    +To periodically update the value of the meter, we must use timers. +First we must create one: + +

    +

    +local foo_timer=statusd.create_timer()
    +
    + +

    +Then we write a function to be called whenever the timer expires. +This function must also restart the timer. + +

    +

    +local function update_foo()
    +    local foo= ... measure foo somehow ...
    +    inform_foo(foo)
    +    foo_timer:set(settings.update_interval, update_foo)
    +end
    +
    + +

    +Finally, at the end of our script we want to do the initial +measurement, and set up timer for further measurements: + +

    +

    +update_foo()
    +
    + +

    +If our scripts supports configurable parameters, the following code +(at the beginning of the script) will allow them to be configured in +cfg_statusbar.lua and passed to the status daemon and our script: + +

    +

    +local defaults={
    +    update_interval=10*1000, -- 10 seconds
    +}
    +                
    +local settings=table.join(statusd.get_config("foo"), defaults)
    +
    + +

    + +

    + + + + diff --git a/doc/ionconf/node7.html b/doc/ionconf/node7.html new file mode 100644 index 0000000..5a3e390 --- /dev/null +++ b/doc/ionconf/node7.html @@ -0,0 +1,6232 @@ + + + + + +6. Function reference + + + + + + + + + + + + + + + + + + + + + + +Subsections + + + +
    + +

    + +
    +6. Function reference +

    + +

    + +

    + +
    +6.1 Functions defined in ioncore +

    + +
    +
    + + +
    +
    Synopsis:
    +
    ioncore.TR(s, ...) + +
    +
    Description:
    +
    gettext+string.format + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    ioncore.bdoc(text) + +
    +
    Description:
    +
    Used to enter documentation among bindings so that other programs + can read it. Does nothing. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    ioncore.chdir_for(reg, dir) + +
    +
    Description:
    +
    Change default working directory for new programs started in reg. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    ioncore.compile_cmd(cmd, guard) + +
    +
    Description:
    +
    Compile string cmd into a bindable function. Within cmd, the + variable ''_'' (underscore) can be used to refer to the object + that was selecting for the bound action and chosen to handle it. + The variable ''_sub'' refers to a ''currently active'' sub-object + of _, or a sub-object where the action loading to the binding + being called actually occured. + +

    +The string guard maybe set to pose limits on _sub. Currently + supported guards are _sub:non-nil and _sub:WFoobar, where + WFoobar is a class. + +

    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    ioncore.create_ws(scr, tmpl, no_default) + +
    +
    Description:
    +
    Create new workspace on screen scr. The table tmpl + may be used to override parts of default_ws_params, + and no_default may be set to true to complete ignore it. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    ioncore.defbindings(context, bindings) + +
    +
    Description:
    +
    Define bindings for context context. Here binding is + a table composed of entries created with ioncore.kpress, + etc.; see section 3.3 for details. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    ioncore.defctxmenu(ctx, ...) + +
    +
    Description:
    +
    Define context menu for context ctx, tab being a table + of menu entries. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    ioncore.defmenu(name, tab) + +
    +
    Description:
    +
    Define a new menu with name being the menu's name and tab + being a table of menu entries. If tab.append is set, the entries + are appended to previously-defined ones, if possible. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    ioncore.defwinprop(list) + +
    +
    Description:
    +
    Define a winprop. For more information, see section 3.5. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    ioncore.exec_on(reg, cmd, merr_internal) + +
    +
    Description:
    +
    Run cmd with the environment variable DISPLAY set to point to the + root window of the X screen reg is on. If cmd is prefixed + by a colon (:), the following command is executed in an xterm + (or other terminal emulator) with the help of the ion-runinxterm + script. If the command is prefixed by two colons, ion-runinxterm + will ask you to press enter after the command is finished, even if it + returns succesfully. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    table ioncore.read_savefile(string basename) + +
    +
    Description:
    +
    Read a savefile. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    string ioncore.get_savefile(string basename) + +
    +
    Description:
    +
    Get a file name to save (session) data in. The string basename + should contain no path or extension components. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    string ioncore.lookup_script(string file, string sp) + +
    +
    Description:
    +
    Lookup script file. If try_in_dir is set, it is tried + before the standard search path. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    bool ioncore.write_savefile(string basename, table tab) + +
    +
    Description:
    +
    Write tab in file with basename basename in the + session directory. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    ioncore.find_manager(obj, t) + +
    +
    Description:
    +
    Find an object with type name t managing obj or one of + its managers. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    ioncore.get_dir_for(reg) + +
    +
    Description:
    +
    Get default working directory for new programs started in reg. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    ioncore.getbindings(maybe_context) + +
    +
    Description:
    +
    Get a table of all bindings. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    ioncore.getctxmenu(name) + +
    +
    Description:
    +
    Returns a context menu defined with ioncore.defctxmenu. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    ioncore.getmenu(name) + +
    +
    Description:
    +
    Returns a menu defined with ioncore.defmenu. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    ioncore.getwinprop(cwin) + +
    +
    Description:
    +
    Find winprop table for cwin. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    string ioncore.aboutmsg() + +
    +
    Description:
    +
    Returns an about message (version, author, copyright notice). + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    WRegion ioncore.activity_first() + +
    +
    Description:
    +
    Return first regio non activity list. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    table ioncore.activity_list() + +
    +
    Description:
    +
    Return list of regions with activity/urgency bit set. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void ioncore.clear_tags() + +
    +
    Description:
    +
    Untag all regions. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    table ioncore.clientwin_list() + +
    +
    Description:
    +
    Return a list of all client windows. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    WRegion ioncore.current() + +
    +
    Description:
    +
    Returns the currently focused region, if any. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    bool ioncore.defshortening(string rx, string rule, bool always) + +
    +
    Description:
    +
    Add a rule describing how too long titles should be shortened to fit in tabs. + The regular expression rx (POSIX, not Lua!) is used to match titles + and when rx matches, rule is attempted to use as a replacement + for title. If always is set, the rule is used even if no shortening + is necessary. + +

    +Similarly to sed's 's' command, rule may contain characters that are + inserted in the resulting string and specials as follows: + +

    + + + + + + + + + + + + + + + + + + + +
    SpecialDescription
    $0Place the original string here.
    $1 to $9Insert n:th capture here (as usual,captures are surrounded + by parentheses in the regex).
    $|Alternative shortening separator. The shortening described + before the first this kind of separator is tried first and + if it fails to make the string short enough, the next is + tried, and so on.
    $<Remove characters on the left of this marker to shorten the + string.
    $>Remove characters on the right of this marker to shorten the + string. Only the first $< or $> within an alternative + shortening is used.
    + +

    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    integer ioncore.exec(string cmd) + +
    +
    Description:
    +
    Run cmd with the environment variable DISPLAY set to point to the + X display the WM is running on. No specific screen is set unlike with + WRootWin.exec_on. The PID of the (shell executing the) new + process is returned. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    WScreen ioncore.find_screen_id(integer id) + +
    +
    Description:
    +
    Find the screen with numerical id id. If Xinerama is + not present, id corresponds to X screen numbers. Otherwise + the ids are some arbitrary ordering of Xinerama rootwins. + If id is , the screen with the highest id is returned. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    table ioncore.get() + +
    +
    Description:
    +
    Get ioncore basic settings. For details see ioncore.set. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    table ioncore.get_paths(table tab) + +
    +
    Description:
    +
    Get important directories (userdir, sessiondir, searchpath). + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    bool ioncore.goto_activity() + +
    +
    Description:
    +
    Go to first region on activity list. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    WRegion ioncore.goto_first(WRegion reg, string dirstr, table param) + +
    +
    Description:
    +
    Go to first region within reg in direction dirstr + (up/down/left/right/beg/end/any). For information on param, + see ioncore.navi_next. Additionally this function supports + the boolean nofront field, for not bringing the object to + front. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    WRegion ioncore.goto_next(WRegion reg, string dirstr, table param) + +
    +
    Description:
    +
    Go to region next from reg in direction dirstr + (up/down/left/right/next/prev/any). For information on param, + see ioncore.navi_next. Additionally this function supports + the boolean nofront field, for not bringing the object to + front. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    WScreen ioncore.goto_next_screen() + +
    +
    Description:
    +
    Switch focus to the next screen and return it. + +

    +Note that this function is asynchronous; the screen will not + actually have received the focus when this function returns. + +

    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    WScreen ioncore.goto_nth_screen(integer id) + +
    +
    Description:
    +
    Switch focus to the screen with id id and return it. + +

    +Note that this function is asynchronous; the screen will not + actually have received the focus when this function returns. + +

    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    WScreen ioncore.goto_prev_screen() + +
    +
    Description:
    +
    Switch focus to the previous screen and return it. + +

    +Note that this function is asynchronous; the screen will not + actually have received the focus when this function returns. + +

    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    WRegion ioncore.goto_previous() + +
    +
    Description:
    +
    Go to and return to a previously active region (if any). + +

    +Note that this function is asynchronous; the region will not + actually have received the focus when this function returns. + +

    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    bool ioncore.is_i18n() + +
    +
    Description:
    +
    Is Ion supporting locale-specifically multibyte-encoded strings? + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    bool ioncore.load_module(string modname) + +
    +
    Description:
    +
    Attempt to load a C-side module. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    WClientWin ioncore.lookup_clientwin(string name) + +
    +
    Description:
    +
    Attempt to find a client window with name name. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    WRegion ioncore.lookup_region(string name, string typenam) + +
    +
    Description:
    +
    Attempt to find a non-client window region with name name and type + inheriting typenam. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    WRegion ioncore.navi_first(WRegion reg, string dirstr, table param) + +
    +
    Description:
    +
    Find first region within reg in direction dirstr + (up/down/left/right/beg/end/any). For information on param, + see ioncore.navi_next. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    WRegion ioncore.navi_next(WRegion reg, string dirstr, table param) + +
    +
    Description:
    +
    Find region next from reg in direction dirstr + (up/down/left/right/next/prev/any). The table param may + contain the boolean field nowrap, instructing not to wrap + around, and the WRegions no_ascend and no_descend, + and functions ascend_filter and descend_filter from + WRegions (to, from), used to decide when to descend + or ascend into another region. (TODO: more detailed explanation.) + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    integer ioncore.popen_bgread(string cmd, function h, function errh) + +
    +
    Description:
    +
    Run cmd with a read pipe connected to its stdout. + When data is received through the pipe, handler is called + with that data. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    string ioncore.progname() + +
    +
    Description:
    +
    Returns the name of program using Ioncore. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    table ioncore.region_list(string typenam) + +
    +
    Description:
    +
    Find all non-client window regions inheriting typenam. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void ioncore.request_selection(function fn) + +
    +
    Description:
    +
    Request (string) selection. The function fn will be called + with the selection when and if it is received. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void ioncore.resign() + +
    +
    Description:
    +
    Causes the window manager to simply exit without saving + state/session. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void ioncore.restart() + +
    +
    Description:
    +
    Restart, saving session first. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void ioncore.restart_other(string cmd) + +
    +
    Description:
    +
    Attempt to restart another window manager cmd. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void ioncore.set(table tab) + +
    +
    Description:
    +
    Set ioncore basic settings. The table tab may contain the + following fields. + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    FieldDescription
    opaque_resize(boolean) Controls whether interactive move and + resize operations simply draw a rubberband during + the operation (false) or immediately affect the + object in question at every step (true).
    warp(boolean) Should focusing operations move the + pointer to the object to be focused?
    switchto(boolean) Should a managing WMPlex switch + to a newly mapped client window?
    screen_notify(boolean) Should notification tooltips be displayed + for hidden workspaces with activity?
    frame_default_index(string) Specifies where to add new regions + on the mutually exclusive list of a frame. One of + ''last'', ''next'' (for after current), ''next-act'' + (for after current and anything with activity right + after it).
    dblclick_delay(integer) Delay between clicks of a double click.
    kbresize_delay(integer) Delay in milliseconds for ending keyboard + resize mode after inactivity.
    kbresize_t_max(integer) Controls keyboard resize acceleration. + See description below for details.
    kbresize_t_min(integer) See below.
    kbresize_step(floating point) See below.
    kbresize_maxacc(floating point) See below.
    framed_transients(boolean) Put transients in nested frames.
    float_placement_method(string) How to place floating frames. + One of ''udlr'' (up-down, then left-right), + ''lrud'' (left-right, then up-down) or ''random''.
    default_ws_params(table) Default workspace layout; the + attach/creation parameters for a WGroup.
    + +

    +When a keyboard resize function is called, and at most kbresize_t_max + milliseconds has passed from a previous call, acceleration factor is reset + to 1.0. Otherwise, if at least kbresize_t_min milliseconds have + passed from the from previous acceleration update or reset the squere root + of the acceleration factor is incremented by kbresize_step. The + maximum acceleration factor (pixels/call modulo size hints) is given by + kbresize_maxacc. The default values are (200, 50, 30, 100). + +

    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void ioncore.set_get_winprop_fn(function fn) + +
    +
    Description:
    +
    Set function used to look up winprops. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    bool ioncore.set_paths(table tab) + +
    +
    Description:
    +
    Set important directories (sessiondir, searchpath). + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void ioncore.set_selection(string p) + +
    +
    Description:
    +
    Set primary selection and cutbuffer0 to p. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void ioncore.shutdown() + +
    +
    Description:
    +
    End session saving it first. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void ioncore.snapshot() + +
    +
    Description:
    +
    Save session. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    table ioncore.tagged_list() + +
    +
    Description:
    +
    Returns a list of tagged regions. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    WRegion ioncore.tags_first() + +
    +
    Description:
    +
    Returns first tagged object. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    string ioncore.version() + +
    +
    Description:
    +
    Returns Ioncore version string. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void ioncore.warn(string str) + +
    +
    Description:
    +
    Issue a warning. How the message is displayed depends on the current + warning handler. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void ioncore.warn_traced(string str) + +
    +
    Description:
    +
    Similar to ioncore.warn, but also print Lua stack trace. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void ioncore.x_change_property(integer win, integer atom, integer atom_type, integer format, string mode, table tab) + +
    +
    Description:
    +
    Modify a window property. The mode is one of + "replace", "prepend" or "append", and format + is either 8, 16 or 32. Also see ioncore.x_get_window_property + and the XChangeProperty(3) manual page. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void ioncore.x_delete_property(integer win, integer atom) + +
    +
    Description:
    +
    Delete a window property. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    string ioncore.x_get_atom_name(integer atom) + +
    +
    Description:
    +
    Get the name of an atom. See XGetAtomName(3) manual page for + details. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    table ioncore.x_get_text_property(integer win, integer atom) + +
    +
    Description:
    +
    Get a text property for a window (STRING, COMPOUND_TEXT, + or UTF8_STRING property converted). The fields in the returned + table (starting from 1) are the null-separated parts of the property. + See the XGetTextProperty(3) manual page for more information. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    table ioncore.x_get_window_property(integer win, integer atom, integer atom_type, integer n32expected, bool more) + +
    +
    Description:
    +
    Get a property atom of type atom_type for window win. + The n32expected parameter indicates the expected number of 32bit + words, and more indicates whether all or just this amount of data + should be fetched. Each 8, 16 or 32bit element of the property, as + deciphered from atom_type is a field in the returned table. + See XGetWindowProperty(3) manual page for more information. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    integer ioncore.x_intern_atom(string name, bool only_if_exists) + +
    +
    Description:
    +
    Create a new atom. See XInternAtom(3) manual page for details. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void ioncore.x_set_text_property(integer win, integer atom, table tab) + +
    +
    Description:
    +
    Set a text property for a window. The fields of tab starting from + 1 should be the different null-separated parts of the property. + See the XSetTextProperty(3) manual page for more information. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    ioncore.kpress(keyspec, cmd, guard) + +
    +
    Description:
    +
    Creates a binding description table for the action of pressing a key given + by keyspec (with possible modifiers) to the function func. + For more information on bindings, see section 3.3. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    ioncore.kpress_wait(keyspec, cmd, guard) + +
    +
    Description:
    +
    This is similar to kpress but after calling cmd, + Ioncore waits for all modifiers to be released before processing + any further actions. + For more information on bindings, see section 3.3. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    ioncore.match_winprop_name(prop, cwin) + +
    +
    Description:
    +
    The basic name-based winprop matching criteria. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    ioncore.mclick(buttonspec, cmd, guard) + +
    +
    Description:
    +
    Creates a binding description table for the action of clicking a mouse + button while possible modifier keys are pressed, + both given by buttonspec, to the function func. + For more information, see section 3.3. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    ioncore.mdblclick(buttonspec, cmd, guard) + +
    +
    Description:
    +
    Similar to mclick but for double-click. + Also see section 3.3. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    ioncore.mdrag(buttonspec, cmd, guard) + +
    +
    Description:
    +
    Creates a binding description table for the action of moving the mouse + (or other pointing device) while the button given by buttonspec + is held pressed and the modifiers given by buttonspec were pressed + when the button was initially pressed. + Also see section 3.3. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    ioncore.menuentry(name, cmd, guard) + +
    +
    Description:
    +
    Use this function to define normal menu entries. The string name + is the string shown in the visual representation of menu, and the + parameter cmd and guard are similar to those of + ioncore.defbindings. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    ioncore.mpress(buttonspec, cmd, guard) + +
    +
    Description:
    +
    Similar to mclick but for just pressing the mouse button. + Also see section 3.3. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    ioncore.refresh_stylelist() + +
    +
    Description:
    +
    Refresh list of known style files. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    ioncore.submap(kcb_, list) + +
    +
    Description:
    +
    Returns a function that creates a submap binding description table. + When the key press action keyspec occurs, Ioncore will wait for + a further key presse and act according to the submap. + For details, see section 3.3. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    ioncore.submenu(name, sub_or_name, options) + +
    +
    Description:
    +
    Use this function to define menu entries for submenus. The parameter + sub_or_name is either a table of menu entries or the name + of an already defined menu. The initial menu entry to highlight can be + specified by options.initial as either an integer starting from 1, + or a function that returns such a number. Another option supported is + options.noautoexpand that will cause mod_query.query_menu + to not automatically expand this submenu. + +
    +
    + +

    + +

    +6.1.1 WClientWin functions +

    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    table WClientWin.get_ident(WClientWin cwin) + +
    +
    Description:
    +
    Returns a table containing the properties WM_CLASS (table entries + instance and class) and WM_WINDOW_ROLE (role) + properties for cwin. If a property is not set, the corresponding + field(s) are unset in the table. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    bool WClientWin.is_fullscreen(WClientWin cwin) + +
    +
    Description:
    +
    Is cwin in full screen mode? + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WClientWin.kill(WClientWin cwin) + +
    +
    Description:
    +
    Attempt to kill (with XKillWindow) the client that owns the X + window correspoding to cwin. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WClientWin.nudge(WClientWin cwin) + +
    +
    Description:
    +
    Attempts to fix window size problems with non-ICCCM compliant + programs. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WClientWin.quote_next(WClientWin cwin) + +
    +
    Description:
    +
    Send next key press directly to cwin. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    bool WClientWin.set_fullscreen(WClientWin cwin, string how) + +
    +
    Description:
    +
    Set client window cwin full screen state according to the + parameter how (set/unset/toggle). Resulting state is returned, + which may not be what was requested. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    double WClientWin.xid(WClientWin cwin) + +
    +
    Description:
    +
    Return the X window id for the client window. + +
    +
    + +

    + +

    +6.1.2 WFrame functions +

    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    bool WFrame.is_shaded(WFrame frame) + +
    +
    Description:
    +
    Is frame shaded? + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WFrame.maximize_horiz(WFrame frame) + +
    +
    Description:
    +
    Attempt to toggle horizontal maximisation of frame. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WFrame.maximize_vert(WFrame frame) + +
    +
    Description:
    +
    Attempt to toggle vertical maximisation of frame. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    string WFrame.mode(WFrame frame) + +
    +
    Description:
    +
    Get frame mode. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WFrame.p_switch_tab(WFrame frame) + +
    +
    Description:
    +
    Display the region corresponding to the tab that the user pressed on. + This function should only be used by binding it to a mouse action. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WFrame.p_tabdrag(WFrame frame) + +
    +
    Description:
    +
    Start dragging the tab that the user pressed on with the pointing device. + This function should only be used by binding it to mpress or + mdrag action with area ''tab''. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    bool WFrame.set_mode(WFrame frame, string modestr) + +
    +
    Description:
    +
    Set frame mode. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    bool WFrame.set_numbers(WFrame frame, string how) + +
    +
    Description:
    +
    Control whether tabs show numbers (set/unset/toggle). + Resulting state is returned, which may not be what was + requested. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    bool WFrame.set_shaded(WFrame frame, string how) + +
    +
    Description:
    +
    Set shading state according to the parameter how + (set/unset/toggle). Resulting state is returned, which may not be + what was requested. + +
    +
    + +

    + +

    +6.1.3 WGroup functions +

    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    WRegion WGroup.attach(WGroup ws, WRegion reg, table param) + +
    +
    Description:
    +
    Attach and reparent existing region reg to ws. + The table param may contain the fields index and + switchto that are interpreted as for WMPlex.attach_new. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    WRegion WGroup.attach_new(WGroup ws, table param) + +
    +
    Description:
    +
    Create a new region to be managed by ws. At least the following + fields in param are understood: + +

    + + + + + + + + + + + + + + + + + + + + + + +
    FieldDescription
    typeClass name (a string) of the object to be created. Mandatory.
    nameName of the object to be created (a string). Optional.
    switchtoShould the region be switched to (boolean)? Optional.
    levelStacking level; default is 1.
    modalMake object modal; ignored if level is set.
    sizepolicySize policy.
    + +

    +In addition parameters to the region to be created are passed in this + same table. + +

    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    WRegion WGroup.bottom(WGroup ws) + +
    +
    Description:
    +
    Returns the 'bottom' of ws. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    table WGroup.managed_list(WGroup ws) + +
    +
    Description:
    +
    Returns a list of regions managed by the workspace (frames, mostly). + +
    +
    + +

    + +

    +6.1.4 WGroupCW functions +

    + +

    + +

    +6.1.5 WGroupWS functions +

    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    bool WGroupWS.attach_framed(WGroupWS ws, WRegion reg, table t) + +
    +
    Description:
    +
    Attach region reg on ws. + At least the following fields in t are supported: + +

    + + + + + + + + + + +
    FieldDescription
    switchtoShould the region be switched to (boolean)? Optional.
    geomGeometry; x and y, if set, indicates top-left of + the frame to be created while width and height, if set, indicate + the size of the client window within that frame. Optional.
    + +

    +
    + +

    + +

    +6.1.6 WInfoWin functions +

    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WInfoWin.set_text(WInfoWin p, string str) + +
    +
    Description:
    +
    Set contents of the info window. + +
    +
    + +

    + +

    +6.1.7 WMPlex functions +

    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    WRegion WMPlex.attach(WMPlex mplex, WRegion reg, table param) + +
    +
    Description:
    +
    Attach and reparent existing region reg to mplex. + The table param may contain the fields index and + switchto that are interpreted as for WMPlex.attach_new. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    WRegion WMPlex.attach_new(WMPlex mplex, table param) + +
    +
    Description:
    +
    Create a new region to be managed by mplex. At least the following + fields in param are understood (all but type are optional). + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    FieldDescription
    type(string) Class name (a string) of the object to be created.
    name(string) Name of the object to be created (a string).
    switchto(boolean) Should the region be switched to (boolean)?
    unnumbered(boolean) Do not put on the numbered mutually + exclusive list.
    index(integer) Index on this list, same as for + WMPlex.set_index.
    level(integer) Stacking level.
    modal(boolean) Shortcut for modal stacking level.
    hidden(boolean) Attach hidden, if not prevented + by e.g. the mutually exclusive list being empty. + This option overrides switchto.
    sizepolicy(integer) Size policy. + (TODO: document them somewhere.)
    geom(table) Geometry specification.
    + +

    +In addition parameters to the region to be created are passed in this + same table. + +

    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WMPlex.attach_tagged(WMPlex mplex) + +
    +
    Description:
    +
    Attach all tagged regions to mplex. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WMPlex.dec_index(WMPlex mplex, WRegion r) + +
    +
    Description:
    +
    Move r ''right'' within objects managed by mplex on list 1. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    integer WMPlex.get_index(WMPlex mplex, WRegion reg) + +
    +
    Description:
    +
    Get index of reg within the multiplexer on list 1. The first region + managed by mplex has index zero. If reg is not managed by + mplex, -1 is returned. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    table WMPlex.get_stdisp(WMPlex mplex) + +
    +
    Description:
    +
    Get status display information. See WMPlex.get_stdisp for + information on the fields. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WMPlex.inc_index(WMPlex mplex, WRegion r) + +
    +
    Description:
    +
    Move r ''right'' within objects managed by mplex on list 1. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    bool WMPlex.is_hidden(WMPlex mplex, WRegion reg) + +
    +
    Description:
    +
    Is reg on within mplex and hidden? + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    table WMPlex.managed_list(WMPlex mplex) + +
    +
    Description:
    +
    Returns a list of all regions managed by mplex. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    integer WMPlex.mx_count(WMPlex mplex) + +
    +
    Description:
    +
    Returns the number of objects on the mutually exclusive list of mplex. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    WRegion WMPlex.mx_current(WMPlex mplex) + +
    +
    Description:
    +
    Returns the managed object currently active within the mutually exclusive + list of mplex. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    table WMPlex.mx_list(WMPlex mplex) + +
    +
    Description:
    +
    Returns a list of regions on the numbered/mutually exclusive list of + mplex. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    WRegion WMPlex.mx_nth(WMPlex mplex, integer n) + +
    +
    Description:
    +
    Returns the n:th object managed by mplex on the + l:th layer. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    bool WMPlex.set_hidden(WMPlex mplex, WRegion reg, string how) + +
    +
    Description:
    +
    Set the visibility of the region reg on mplex + as specified with the parameter how (set/unset/toggle). + The resulting state is returned. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WMPlex.set_index(WMPlex mplex, WRegion reg, integer index) + +
    +
    Description:
    +
    Set index of reg within the multiplexer to index within + the mutually exclusive list. Special values for index are: + + + + + + + +
    After WMPlex.mx_current.
    Last.
    + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    WRegion WMPlex.set_stdisp(WMPlex mplex, table t) + +
    +
    Description:
    +
    Set/create status display for mplex. Table is a standard + description of the object to be created (as passed to e.g. + WMPlex.attach_new). In addition, the following fields are + recognised: + +

    + + + + + + + + + + +
    FieldDescription
    posThe corner of the screen to place the status display + in. One of tl, tr, bl or br.
    actionIf this field is set to keep, corner + and orientation are changed for the existing + status display. If this field is set to remove, + the existing status display is removed. If this + field is not set or is set to replace, a + new status display is created and the old, if any, + removed.
    + +

    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WMPlex.switch_next(WMPlex mplex) + +
    +
    Description:
    +
    Have mplex display next (wrt. currently selected) object managed + by it. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WMPlex.switch_nth(WMPlex mplex, integer n) + +
    +
    Description:
    +
    Have mplex display the n:th object managed by it. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WMPlex.switch_prev(WMPlex mplex) + +
    +
    Description:
    +
    Have mplex display previous (wrt. currently selected) object + managed by it. + +
    +
    + +

    + +

    +6.1.8 WMoveresMode functions +

    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WMoveresMode.cancel(WMoveresMode mode) + +
    +
    Description:
    +
    Return from move/resize cancelling changes if opaque + move/resize has not been enabled. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WMoveresMode.finish(WMoveresMode mode) + +
    +
    Description:
    +
    Return from move/resize mode and apply changes unless opaque + move/resize is enabled. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WMoveresMode.move(WMoveresMode mode, integer horizmul, integer vertmul) + +
    +
    Description:
    +
    Move resize mode target one step: + +

    + + + + + + + + + + + + + +
    horizmul/vertmuleffect
    -1Move left/up
    0No effect
    1Move right/down
    + +

    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WMoveresMode.resize(WMoveresMode mode, integer left, integer right, integer top, integer bottom) + +
    +
    Description:
    +
    Shrink or grow resize mode target one step in each direction. + Acceptable values for the parameters left, right, top + and bottom are as follows: -1: shrink along, + 0: do not change, 1: grow along corresponding border. + +
    +
    + +

    + +

    +6.1.9 WRegion functions +

    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    WMoveresMode WRegion.begin_kbresize(WRegion reg) + +
    +
    Description:
    +
    Enter move/resize mode for reg. The bindings set with + ioncore.set_bindings for WMoveresMode are used in + this mode. Of the functions exported by the Ion C core, only + WMoveresMode.resize, WMoveresMode.move, + WMoveresMode.cancel and WMoveresMode.end are + allowed to be called while in this mode. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    WRegion WRegion.current(WRegion mgr) + +
    +
    Description:
    +
    Return the object, if any, that is considered ''currently active'' + within the objects managed by mplex. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    table WRegion.geom(WRegion reg) + +
    +
    Description:
    +
    Returns the geometry of reg within its parent; a table with fields + x, y, w and h. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    bool WRegion.goto(WRegion reg) + +
    +
    Description:
    +
    Attempt to display reg, save region activity status and then + warp to (or simply set focus to if warping is disabled) reg. + +

    +Note that this function is asynchronous; the region will not + actually have received the focus when this function returns. + +

    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    bool WRegion.is_active(WRegion reg) + +
    +
    Description:
    +
    Is reg active/does it or one of it's children of focus? + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    bool WRegion.is_activity(WRegion reg) + +
    +
    Description:
    +
    Is activity notification set on reg. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    bool WRegion.is_mapped(WRegion reg) + +
    +
    Description:
    +
    Is reg visible/is it and all it's ancestors mapped? + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    bool WRegion.is_tagged(WRegion reg) + +
    +
    Description:
    +
    Is reg tagged? + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    WRegion WRegion.manager(WRegion reg) + +
    +
    Description:
    +
    Returns the region that manages reg. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    string WRegion.name(WRegion reg) + +
    +
    Description:
    +
    Returns the name for reg. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    WWindow WRegion.parent(WRegion reg) + +
    +
    Description:
    +
    Returns the parent region of reg. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    WRootWin WRegion.rootwin_of(WRegion reg) + +
    +
    Description:
    +
    Returns the root window reg is on. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    bool WRegion.rqclose(WRegion reg, bool relocate) + +
    +
    Description:
    +
    Attempt to close/destroy reg. Whether this operation works + depends on whether the particular type of region in question has + implemented the feature and, in case of client windows, whether + the client supports the WM_DELETE protocol (see also + WClientWin.kill). If the operation is likely to succeed, + true is returned, otherwise false. In most cases the + region will not have been actually destroyed when this function returns. + If relocate is not set, and reg manages other regions, it + will not be closed. Otherwise the managed regions will be attempted + to be relocated. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    WRegion WRegion.rqclose_propagate(WRegion reg, WRegion maybe_sub) + +
    +
    Description:
    +
    Recursively attempt to close a region or one of the regions managed by + it. If sub is set, it will be used as the managed region, otherwise + WRegion.current(reg). The object to be closed is + returned or NULL if nothing can be closed. Also see notes for + WRegion.rqclose. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    table WRegion.rqgeom(WRegion reg, table g) + +
    +
    Description:
    +
    Attempt to resize and/or move reg. The table g is a usual + geometry specification (fields x, y, w and h), + but may contain missing fields, in which case, reg's manager may + attempt to leave that attribute unchanged. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    bool WRegion.rqorder(WRegion reg, string ord) + +
    +
    Description:
    +
    Request ordering. Currently supported values for ord + are 'front' and 'back'. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    WScreen WRegion.screen_of(WRegion reg) + +
    +
    Description:
    +
    Returns the screen reg is on. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    bool WRegion.set_activity(WRegion reg, string how) + +
    +
    Description:
    +
    Set activity flag of reg. The how parameter most be + one of (set/unset/toggle). + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    bool WRegion.set_name(WRegion reg, string p) + +
    +
    Description:
    +
    Set the name of reg to p. If the name is already in use, + an instance number suffix <n> will be attempted. If p has + such a suffix, it will be modified, otherwise such a suffix will be + added. Setting p to nil will cause current name to be removed. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    bool WRegion.set_name_exact(WRegion reg, string p) + +
    +
    Description:
    +
    Similar to WRegion.set_name except if the name is already in use, + other instance numbers will not be attempted. The string p should + not contain a <n> suffix or this function will fail. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    bool WRegion.set_tagged(WRegion reg, string how) + +
    +
    Description:
    +
    Change tagging state of reg as defined by how + (set/unset/toggle). Resulting state is returned. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    table WRegion.size_hints(WRegion reg) + +
    +
    Description:
    +
    Returns size hints for reg. The returned table always contains the + fields min_?, base_? and sometimes the fields max_?, + base_? and inc_?, where ?=w, h. + +
    +
    + +

    + +

    +6.1.10 WRootWin functions +

    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    WScreen WRootWin.current_scr(WRootWin rootwin) + +
    +
    Description:
    +
    Returns previously active screen on root window rootwin. + +
    +
    + +

    + +

    +6.1.11 WScreen functions +

    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    integer WScreen.id(WScreen scr) + +
    +
    Description:
    +
    Return the numerical id for screen scr. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    bool WScreen.set_managed_offset(WScreen scr, table offset) + +
    +
    Description:
    +
    Set offset of objects managed by the screen from actual screen geometry. + The table offset should contain the entries x, y, + w and h indicating offsets of that component of screen + geometry. + +
    +
    + +

    + +

    +6.1.12 WWindow functions +

    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WWindow.p_move(WWindow wwin) + +
    +
    Description:
    +
    Start moving wwin with the mouse or other pointing device. + This function should only be used by binding it to mpress or + mdrag action. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WWindow.p_resize(WWindow wwin) + +
    +
    Description:
    +
    Start resizing wwin with the mouse or other pointing device. + This function should only be used by binding it to mpress or + mdrag action. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    double WWindow.xid(WWindow wwin) + +
    +
    Description:
    +
    Return the X window id for wwin. + +
    +
    + +

    + +

    +6.1.13 global functions +

    + +

    + +

    +
    + +
    +
    Synopsis:
    +
    export(lib, ...) + +
    +
    Description:
    +
    Export a list of functions from lib into global namespace. + +
    +
    + +

    + +

    +6.1.14 gr functions +

    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void gr.read_config() + +
    +
    Description:
    +
    Read drawing engine configuration file draw.lua. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void gr.refresh() + +
    +
    Description:
    +
    Refresh objects' brushes to update them to use newly loaded style. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    bool gr.select_engine(string engine) + +
    +
    Description:
    +
    Future requests for ''brushes'' are to be forwarded to the drawing engine + engine. If no engine of such name is known, a module with that name + is attempted to be loaded. This function is only intended to be called from + colour scheme etc. configuration files and can not be used to change the + look of existing objects; for that use gr.read_config. + +
    +
    + +

    + +

    +6.1.15 string functions +

    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    string.shell_safe(str) + +
    +
    Description:
    +
    Make str shell-safe. + +
    +
    + +

    + +

    +6.1.16 table functions +

    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    table.append(t1, t2) + +
    +
    Description:
    +
    Add entries that do not exist in t1 from t2 to t1. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    table.copy(t, deep) + +
    +
    Description:
    +
    Make copy of table. If deep is unset, shallow one-level + copy is made, otherwise a deep copy is made. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    table.icat(t1, t2) + +
    +
    Description:
    +
    Insert all positive integer entries from t2 into t1. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    table.join(t1, t2) + +
    +
    Description:
    +
    Create a table containing all entries from t1 and those from + t2 that are missing from t1. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    table.map(f, t) + +
    +
    Description:
    +
    Map all entries of t by f. + +
    +
    + +

    + +

    + +
    +6.2 Functions defined in mod_tiling +

    + +
    +
    + + +
    +
    Synopsis:
    +
    bool mod_tiling.detach(WRegion reg) + +
    +
    Description:
    +
    Detach reg, i.e. make it managed by its nearest ancestor + WGroup, framed if reg is not itself WFrame. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    table mod_tiling.get() + +
    +
    Description:
    +
    Get parameters. For details see mod_tiling.set. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    bool mod_tiling.mkbottom(WRegion reg) + +
    +
    Description:
    +
    Create a new WTiling 'bottom' for the group of reg, + consisting of reg. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void mod_tiling.set(table tab) + +
    +
    Description:
    +
    Set parameters. Currently only raise_delay (in milliseconds) + is supported. + +
    +
    + +

    + +

    +6.2.1 WSplit functions +

    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    table WSplit.geom(WSplit split) + +
    +
    Description:
    +
    Returns the area of workspace used by the regions under split. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    WSplitInner WSplit.parent(WSplit split) + +
    +
    Description:
    +
    Return parent split for split. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    table WSplit.rqgeom(WSplit node, table g) + +
    +
    Description:
    +
    Attempt to resize and/or move the split tree starting at node. + Behaviour and the g parameter are as for WRegion.rqgeom + operating on node (if it were a WRegion). + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WSplit.transpose(WSplit node) + +
    +
    Description:
    +
    Transpose contents of node. + +
    +
    + +

    + +

    +6.2.2 WSplitInner functions +

    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    WSplit WSplitInner.current(WSplitInner node) + +
    +
    Description:
    +
    Returns the most previously active child node of split. + +
    +
    + +

    + +

    +6.2.3 WSplitRegion functions +

    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    WRegion WSplitRegion.reg(WSplitRegion node) + +
    +
    Description:
    +
    Returns the region contained in node. + +
    +
    + +

    + +

    +6.2.4 WSplitSplit functions +

    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    WSplit WSplitSplit.br(WSplitSplit split) + +
    +
    Description:
    +
    Returns the bottom or right child node of split depending + on the direction of the split. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    string WSplitSplit.dir(WSplitSplit split) + +
    +
    Description:
    +
    Returns the direction of split; either ''vertical'' or + ''horizontal''. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WSplitSplit.flip(WSplitSplit split) + +
    +
    Description:
    +
    Flip contents of node. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    WSplit WSplitSplit.tl(WSplitSplit split) + +
    +
    Description:
    +
    Returns the top or left child node of split depending + on the direction of the split. + +
    +
    + +

    + +

    +6.2.5 WTiling functions +

    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    bool WTiling.flip_at(WTiling ws, WRegion reg) + +
    +
    Description:
    +
    Flip ws at reg or root if nil. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    bool WTiling.transpose_at(WTiling ws, WRegion reg) + +
    +
    Description:
    +
    Transpose ws at reg or root if nil. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    WRegion WTiling.farthest(WTiling ws, string dirstr, bool any) + +
    +
    Description:
    +
    Return the most previously active region on ws with no + other regions next to it in direction dirstr + (left/right/up/down). If any is not set, the status + display is not considered. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    table WTiling.managed_list(WTiling ws) + +
    +
    Description:
    +
    Returns a list of regions managed by the workspace (frames, mostly). + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    WRegion WTiling.nextto(WTiling ws, WRegion reg, string dirstr, bool any) + +
    +
    Description:
    +
    Return the most previously active region next to reg in + direction dirstr (left/right/up/down). The region reg + must be managed by ws. If any is not set, the status display + is not considered. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    WSplitRegion WTiling.node_of(WTiling ws, WRegion reg) + +
    +
    Description:
    +
    For region reg managed by ws return the WSplit + a leaf of which reg is. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    bool WTiling.set_floating_at(WTiling ws, WRegion reg, string how, string dirstr) + +
    +
    Description:
    +
    Toggle floating of the sides of a split containin reg as indicated + by the parameters how (set/unset/toggle) and dirstr + (left/right/up/down/any). The new status is returned (and false + also on error). + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    WSplitSplit WTiling.set_floating(WTiling ws, WSplitSplit split, string how) + +
    +
    Description:
    +
    Toggle floating of a split's sides at split as indicated by the + parameter how (set/unset/toggle). A split of the appropriate is + returned, if there was a change. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    WFrame WTiling.split(WTiling ws, WSplit node, string dirstr) + +
    +
    Description:
    +
    Create a new frame on ws above/below/left of/right of + node as indicated by dirstr. If dirstr is + prefixed with ''floating:'' a floating split is created. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    WFrame WTiling.split_at(WTiling ws, WFrame frame, string dirstr, bool attach_current) + +
    +
    Description:
    +
    Split frame creating a new frame to direction dirstr + (one of ''left'', ''right'', ''top'' or ''bottom'') of frame. + If attach_current is set, the region currently displayed in + frame, if any, is moved to thenew frame. + If dirstr is prefixed with ''floating:'' a floating split is + created. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    WFrame WTiling.split_top(WTiling ws, string dirstr) + +
    +
    Description:
    +
    Same as WTiling.split at the root of the split tree. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    WSplit WTiling.split_tree(WTiling ws) + +
    +
    Description:
    +
    Returns the root of the split tree. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    bool WTiling.unsplit_at(WTiling ws, WFrame frame) + +
    +
    Description:
    +
    Try to relocate regions managed by frame to another frame + and, if possible, destroy the frame. + +
    +
    + +

    + +

    + +
    +6.3 Functions defined in mod_query +

    + +
    +
    + + +
    +
    Synopsis:
    +
    mod_query.defcmd(cmd, fn) + +
    +
    Description:
    +
    Define a command override for the query_exec query. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    table mod_query.get() + +
    +
    Description:
    +
    Get module configuration. For more information see + mod_query.set. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void mod_query.history_clear() + +
    +
    Description:
    +
    Clear line editor history. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    string mod_query.history_get(integer n) + +
    +
    Description:
    +
    Get entry at index n in line editor history, 0 being the latest. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    bool mod_query.history_push(string str) + +
    +
    Description:
    +
    Push an entry into line editor history. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    integer mod_query.history_search(string s, integer from, bool bwd) + +
    +
    Description:
    +
    Try to find matching history entry. Returns -1 if none was + found. The parameter from specifies where to start + searching from, and bwd causes backward search from + that point. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    table mod_query.history_table() + +
    +
    Description:
    +
    Return table of history entries. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    WMessage mod_query.message(WMPlex mplex, string p) + +
    +
    Description:
    +
    Display a message in the mplex. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void mod_query.set(table tab) + +
    +
    Description:
    +
    Set module configuration. The following are supported: + +

    + + + + + + + + + + +
    FieldDescription
    autoshowcompl(boolean) Is auto-show-completions enabled? + (default: true).
    autoshowcompl_delay(integer) auto-show-completions delay + in milliseconds (default: 250).
    + +

    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    WMessage mod_query.warn(WMPlex mplex, string p) + +
    +
    Description:
    +
    Display an error message box in the multiplexer mplex. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    mod_query.popen_completions(cp, cmd, fn, reshnd) + +
    +
    Description:
    +
    This function can be used to read completions from an external source. + The parameter cp is the completion proxy to be used, + and the string cmd the shell command to be executed. To its stdout, + the command should on the first line write the common_beg + parameter of WComplProxy.set_completions (which fn maybe used + to override) and a single actual completion on each of the successive lines. + The function reshnd may be used to override a result table + building routine. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    mod_query.query(mplex, prompt, initvalue, handler, completor, + context) + +
    +
    Description:
    +
    Low-level query routine. mplex is the WMPlex to display + the query in, prompt the prompt string, and initvalue + the initial contents of the query box. handler is a function + that receives (mplex, result string) as parameter when the + query has been succesfully completed, completor the completor + routine which receives a (cp, str, point) as parameters. + The parameter str is the string to be completed and point + cursor's location within it. Completions should be eventually, + possibly asynchronously, set with WComplProxy.set_completions + on cp. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    mod_query.query_attachclient(mplex) + +
    +
    Description:
    +
    This query asks for the name of a client window and switches + focus to the one entered. It uses the completion function + ioncore.complete_clientwin. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    mod_query.query_editfile(mplex, script, prompt) + +
    +
    Description:
    +
    Asks for a file to be edited. This script uses + run-mailcap -mode=edit by default, but you may provide an + alternative script to use. The default prompt is "Edit file:" (translated). + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    mod_query.query_exec(mplex) + +
    +
    Description:
    +
    This function asks for a command to execute with /bin/sh. + If the command is prefixed with a colon (':'), the command will + be run in an XTerm (or other terminal emulator) using the script + ion-runinxterm. Two colons ('::') will ask you to press + enter after the command has finished. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    mod_query.query_gotoclient(mplex) + +
    +
    Description:
    +
    This query asks for the name of a client window and attaches + it to the frame the query was opened in. It uses the completion + function ioncore.complete_clientwin. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    mod_query.query_lua(mplex) + +
    +
    Description:
    +
    This query asks for Lua code to execute. It sets the variable '_' + in the local environment of the string to point to the mplex where the + query was created. It also sets the table arg in the local + environment to {_, _:current()}. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    mod_query.query_man(mplex, prog) + +
    +
    Description:
    +
    This query asks for a manual page to display. By default it runs the + man command in an xterm using ion-runinxterm, + but it is possible to pass another program as the prog argument. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    mod_query.query_menu(mplex, themenu, prompt) + +
    +
    Description:
    +
    This query can be used to create a query of a defined menu. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    mod_query.query_renameframe(frame) + +
    +
    Description:
    +
    This function asks for a name new for the frame where the query + was created. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    mod_query.query_renameworkspace(mplex) + +
    +
    Description:
    +
    This function asks for a name new for the workspace on which the + query resides. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    mod_query.query_restart(mplex) + +
    +
    Description:
    +
    This query asks whether the user wants restart Ioncore. + If the answer is 'y', 'Y' or 'yes', so will happen. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    mod_query.query_runfile(mplex, script, prompt) + +
    +
    Description:
    +
    Asks for a file to be viewed. This script uses + run-mailcap -action=view by default, but you may provide an + alternative script to use. The default prompt is "View file:" (translated). + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    mod_query.query_shutdown(mplex) + +
    +
    Description:
    +
    This query asks whether the user wants to exit Ion (no session manager) + or close the session (running under a session manager that supports such + requests). If the answer is 'y', 'Y' or 'yes', so will happen. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    mod_query.query_ssh(mplex, ssh) + +
    +
    Description:
    +
    This query asks for a host to connect to with SSH. + Hosts to tab-complete are read from ~/.ssh/known_hosts. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    mod_query.query_workspace(mplex) + +
    +
    Description:
    +
    This query asks for the name of a workspace. If a workspace + (an object inheriting WGroupWS) with such a name exists, + it will be switched to. Otherwise a new workspace with the + entered name will be created and the user will be queried for + the type of the workspace. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    mod_query.query_yesno(mplex, prompt, handler) + +
    +
    Description:
    +
    This function query will display a query with prompt prompt in + mplex and if the user answers affirmately, call handler + with mplex as parameter. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    mod_query.show_about_ion(mplex) + +
    +
    Description:
    +
    Display an "About Ion" message in mplex. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    mod_query.show_tree(mplex, reg, max_depth) + +
    +
    Description:
    +
    Show information about a region tree + +
    +
    + +

    + +

    +6.3.1 WComplProxy functions +

    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    bool WComplProxy.set_completions(WComplProxy proxy, table compls) + +
    +
    Description:
    +
    Set completion list of the WEdln that proxy refers to to + compls, if it is still waiting for this completion run. The + numerical indexes of compls list the found completions. If the + entry common_beg (common_end) exists, it gives an extra + common prefix (suffix) of all found completions. + +
    +
    + +

    + +

    +6.3.2 WEdln functions +

    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WEdln.back(WEdln wedln) + +
    +
    Description:
    +
    Move backward one character. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WEdln.backspace(WEdln wedln) + +
    +
    Description:
    +
    Delete previous character. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WEdln.bkill_word(WEdln wedln) + +
    +
    Description:
    +
    Starting from the previous characters, delete possible whitespace and + preceding alphanumeric characters until previous non-alphanumeric character. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WEdln.bol(WEdln wedln) + +
    +
    Description:
    +
    Go to the beginning of line. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WEdln.bskip_word(WEdln wedln) + +
    +
    Description:
    +
    Go to to beginning of current sequence of alphanumeric characters + followed by whitespace. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WEdln.clear_mark(WEdln wedln) + +
    +
    Description:
    +
    Clear mark. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WEdln.complete(WEdln wedln, string cycle, string mode) + +
    +
    Description:
    +
    Call completion handler with the text between the beginning of line and + current cursor position, or select next/previous completion from list if in + auto-show-completions mode and cycle is set to ``next'' or ``prev'', + respectively. The mode may be ``history'' or ``normal''. If it is + not set, the previous mode is used. Normally next entry is not cycled to + despite the setting of cycle if mode switch occurs. To override + this, use ``next-always'' and ``prev-always'' for cycle. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    string WEdln.contents(WEdln wedln) + +
    +
    Description:
    +
    Get line editor contents. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    string WEdln.context(WEdln wedln) + +
    +
    Description:
    +
    Get history context for wedln. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WEdln.copy(WEdln wedln) + +
    +
    Description:
    +
    Copy text between mark and current cursor position to clipboard. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WEdln.cut(WEdln wedln) + +
    +
    Description:
    +
    Copy text between mark and current cursor position to clipboard + and then delete that sequence. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WEdln.delete(WEdln wedln) + +
    +
    Description:
    +
    Delete current character. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WEdln.eol(WEdln wedln) + +
    +
    Description:
    +
    Go to the end of line. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WEdln.finish(WEdln wedln) + +
    +
    Description:
    +
    Close wedln and call any handlers. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WEdln.forward(WEdln wedln) + +
    +
    Description:
    +
    Move forward one character. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WEdln.history_next(WEdln wedln, bool match) + +
    +
    Description:
    +
    Replace line editor contents with next entry in history if one exists. + If match is true, the initial part of the history entry + must match the current line from beginning to point. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WEdln.history_prev(WEdln wedln, bool match) + +
    +
    Description:
    +
    Replace line editor contents with previous in history if one exists. + If match is true, the initial part of the history entry + must match the current line from beginning to point. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WEdln.insstr(WEdln wedln, string str) + +
    +
    Description:
    +
    Input str in wedln at current editing point. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    bool WEdln.is_histcompl(WEdln wedln) + +
    +
    Description:
    +
    Get history completion mode. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WEdln.kill_line(WEdln wedln) + +
    +
    Description:
    +
    Delete the whole line. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WEdln.kill_to_bol(WEdln wedln) + +
    +
    Description:
    +
    Delete all characters from previous to beginning of line. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WEdln.kill_to_eol(WEdln wedln) + +
    +
    Description:
    +
    Delete all characters from current to end of line. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WEdln.kill_word(WEdln wedln) + +
    +
    Description:
    +
    Starting from the current point, delete possible whitespace and + following alphanumeric characters until next non-alphanumeric character. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    integer WEdln.mark(WEdln wedln) + +
    +
    Description:
    +
    Get current mark (start of selection) for wedln. + Return value of -1 indicates that there is no mark, and + 0 is the beginning of the line. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    bool WEdln.next_completion(WEdln wedln) + +
    +
    Description:
    +
    Select next completion. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WEdln.paste(WEdln wedln) + +
    +
    Description:
    +
    Request selection from application holding such. + +

    +Note that this function is asynchronous; the selection will not + actually be inserted before Ion receives it. This will be no + earlier than Ion return to its main loop. + +

    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    integer WEdln.point(WEdln wedln) + +
    +
    Description:
    +
    Get current editing point. + Beginning of the edited line is point 0. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    bool WEdln.prev_completion(WEdln wedln) + +
    +
    Description:
    +
    Select previous completion. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WEdln.set_context(WEdln wedln, string context) + +
    +
    Description:
    +
    Set history context for wedln. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WEdln.set_mark(WEdln wedln) + +
    +
    Description:
    +
    Set mark to current cursor position. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WEdln.skip_word(WEdln wedln) + +
    +
    Description:
    +
    Go to to end of current sequence of whitespace followed by alphanumeric + characters.. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WEdln.transpose_chars(WEdln wedln) + +
    +
    Description:
    +
    Transpose characters. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WEdln.transpose_words(WEdln wedln) + +
    +
    Description:
    +
    Transpose words. + +
    +
    + +

    + +

    +6.3.3 WInput functions +

    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WInput.cancel(WInput input) + +
    +
    Description:
    +
    Close input not calling any possible finish handlers. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WInput.scrolldown(WInput input) + +
    +
    Description:
    +
    Scroll input input text contents down. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WInput.scrollup(WInput input) + +
    +
    Description:
    +
    Scroll input input text contents up. + +
    +
    + +

    + +

    + +
    +6.4 Functions defined in mod_menu +

    + +
    +
    + + +
    +
    Synopsis:
    +
    mod_menu.grabmenu(mplex, sub, menu_or_name, param) + +
    +
    Description:
    +
    This function is similar to mod_menu.menu, but input + is grabbed and the key used to active the menu can be used to + cycle through menu entries. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    mod_menu.menu(mplex, sub, menu_or_name, param) + +
    +
    Description:
    +
    Display a menu in the lower-left corner of mplex. + The variable menu_or_name is either the name of a menu + defined with mod_menu.defmenu or directly a table similar + to ones passesd to this function. When this function is + called from a binding handler, sub should be set to + the second argument of to the binding handler (_sub) + so that the menu handler will get the same parameters as the + binding handler. Extra options can be passed in the table + param. The initial entry can be specified as the field + initial as an integer starting from 1. Menus can be made + to use a bigger style by setting the field big to true. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    table mod_menu.get() + +
    +
    Description:
    +
    Get module basic settings. For details, see mod_menu.set. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void mod_menu.set(table tab) + +
    +
    Description:
    +
    Set module basic settings. The parameter table may contain the + following fields: + +

    + + + + + + + + + + +
    FieldDescription
    scroll_amountNumber of pixels to scroll at a time + pointer-controlled menus when one extends + beyond a border of the screen and the pointer + touches that border.
    scroll_delayTime between such scrolling events in + milliseconds.
    + +

    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    mod_menu.pmenu(win, sub, menu_or_name) + +
    +
    Description:
    +
    This function displays a drop-down menu and should only + be called from a mouse press handler. The parameters are + similar to those of mod_menu.menu. + +
    +
    + +

    + +

    +6.4.1 WMenu functions +

    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WMenu.cancel(WMenu menu) + +
    +
    Description:
    +
    Close menu not calling any possible finish handlers. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WMenu.finish(WMenu menu) + +
    +
    Description:
    +
    If selected entry is a submenu, display that. + Otherwise destroy the menu and call handler for selected entry. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WMenu.select_next(WMenu menu) + +
    +
    Description:
    +
    Select next entry in menu. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WMenu.select_nth(WMenu menu, integer n) + +
    +
    Description:
    +
    Select n:th entry in menu. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WMenu.select_prev(WMenu menu) + +
    +
    Description:
    +
    Select previous entry in menu. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WMenu.typeahead_clear(WMenu menu) + +
    +
    Description:
    +
    Clear typeahead buffer. + +
    +
    + +

    + +

    + +
    +6.5 Functions defined in mod_dock +

    + +
    +
    + + +
    +
    Synopsis:
    +
    void mod_dock.set_floating_shown_on(WMPlex mplex, string how) + +
    +
    Description:
    +
    Toggle floating docks on mplex. + +
    +
    + +

    + +

    +6.5.1 WDock functions +

    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    bool WDock.attach(WDock dock, WRegion reg) + +
    +
    Description:
    +
    Attach reg to dock. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    table WDock.get(WDock dock) + +
    +
    Description:
    +
    Get dock's configuration table. See WDock.set for a + description of the table. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WDock.resize(WDock dock) + +
    +
    Description:
    +
    Resizes and refreshes dock. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void WDock.set(WDock dock, table conftab) + +
    +
    Description:
    +
    Configure dock. conftab is a table of key/value pairs: + +

    + + + + + + + + + + + + + + + + + + + + + +
    KeyValuesDescription
    namestringName of dock
    posstring in +Dock position. + Can only be used in floating mode.
    growup/down/left/rightGrowth direction where new dockapps are added. Also + sets orientation for dock when working as WMPlex status + display (see WMPlex.set_stdisp).
    is_autoboolShould dock automatically manage new dockapps?
    + +

    +Any parameters not explicitly set in conftab will be left unchanged. + +

    +
    + +

    + +

    + +
    +6.6 Functions defined in mod_sp +

    + +
    +
    + + +
    +
    Synopsis:
    +
    bool mod_sp.set_shown(WFrame sp, string how) + +
    +
    Description:
    +
    Toggle displayed status of sp. + The parameter how is one of (set/unset/toggle). + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    bool mod_sp.set_shown_on(WMPlex mplex, string how) + +
    +
    Description:
    +
    Change displayed status of some scratchpad on mplex if one is + found. The parameter how is one of (set/unset/toggle). + +
    +
    + +

    + +

    + +
    +6.7 Functions defined in de +

    + +
    +
    + + +
    +
    Synopsis:
    +
    bool de.defstyle(string name, table tab) + +
    +
    Description:
    +
    Define a style. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    bool de.defstyle_rootwin(WRootWin rootwin, string name, table tab) + +
    +
    Description:
    +
    Define a style for the root window rootwin. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    void de.reset() + +
    +
    Description:
    +
    Clear all styles from drawing engine memory. + +
    +
    + +

    + +

    +
    + + +
    +
    Synopsis:
    +
    table de.substyle(string pattern, table tab) + +
    +
    Description:
    +
    Define a substyle. + +
    +
    + +

    + +

    + +
    +6.8 Hooks +

    + +

    + +

    +
    + +
    +
    Hook name:
    +
    clientwin_do_manage_alt + +
    +
    Parameters:
    +
    (WClientWin, table) + +
    +
    Description:
    +
    Called when we want to manage a new client window. + The table argument contains the following fields: + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    FieldTypeDescription
    switchtoboolDo we want to switch to the client window.
    jumptoboolDo we want to jump to the client window.
    userposboolGeometry set by user.
    dockappboolClient window is a dockapp.
    maprqboolMap request (and not initialisation scan).
    gravitynumberWindow gravity.
    geomtableRequested geometry; x, y, w, h.
    tforWClientWinTransient for window.
    + +

    +This hook is not called in protected mode and can be used for + arbitrary placement policies (deciding in which workspace a new + WClientWin should go). In this case, you can call +

    +reg:attach(cwin)
    +
    + where reg is the region where the window should go, and + cwin is the first argument of the function added to the + hook. + +
    +
    + +

    + +

    +
    + +
    +
    Hook name:
    +
    clientwin_mapped_hook + +
    +
    Parameters:
    +
    WClientWin + +
    +
    Description:
    +
    Called when we have started to manage a client window. + +
    +
    + +

    + +

    +
    + +
    +
    Hook name:
    +
    clientwin_unmapped_hook + +
    +
    Parameters:
    +
    number + +
    +
    Description:
    +
    Called when we no longer manage a client window. The parameter + is the X ID of the window; see WClientWin.xid. + +
    +
    + +

    + +

    +
    + +
    +
    Hook name:
    +
    frame_managed_changed_hook + +
    +
    Parameters:
    +
    table + +
    +
    Description:
    +
    Called when there are changes in the objects managed by a frame + or their order. The table parameter has the following fields: + +

    + + + + + + + + + + + + + + + + + + + + + +
    FieldTypeDescription
    regWFrameThe frame in question
    modestring"switchonly", "reorder", + "add" or "remove"
    swboolSwitch occured
    subWRegionThe managed region (primarily) affected
    + +

    +
    + +

    + +

    +
    + +
    +
    Hook name:
    +
    ioncore_sigchld_hook + +
    +
    Parameters:
    +
    integer + +
    +
    Description:
    +
    Called when a child process has exited. The parameter + is the PID of the process. + +
    +
    + +

    + +

    +
    + +
    +
    Hook name:
    +
    ioncore_deinit_hook + +
    +
    Parameters:
    +
    () + +
    +
    Description:
    +
    Called when Ion is deinitialising and about to quit. + +
    +
    + +

    + +

    +
    + +
    +
    Hook name:
    +
    ioncore_post_layout_setup_hook + +
    +
    Parameters:
    +
    () + +
    +
    Description:
    +
    Called when Ion has done all initialisation and is almost ready to + enter the mainloop, except no windows are yet being managed. + +
    +
    + +

    + +

    +
    + +
    +
    Hook name:
    +
    ioncore_snapshot_hook + +
    +
    Parameters:
    +
    () + +
    +
    Description:
    +
    Called to signal scripts and modules to save their state (if any). + +
    +
    + +

    + +

    +
    + +
    +
    Hook name:
    +
    tiling_placement_alt + +
    +
    Parameters:
    +
    table + +
    +
    Description:
    +
    Called when a client window is about to be managed by a WTiling + to allow for alternative placement policies. The table has the + following fields: + + + + + + + + + + + + + + + + + + + + + +
    FieldTypeDescription
    tilingWTilingThe tiling
    regWRegionThe region (always a WClientWin at + the moment) to be placed
    mptableThis table contains the same fields as + the parameter of clientwin_do_manage_alt
    res_frameWFrameA succesfull handler should + return the target frame here.
    + This hook is just for placing within a given workspace after the + workspace has been decided by the default workspace selection + policy. It is called in protected mode. For arbitrary placement + policies, clientwin_do_manage_alt should be used; it + isn't called in protected mode, + +
    +
    + +

    + +

    +
    + +
    +
    Hook name:
    +
    panews_make_placement_alt + +
    +
    Parameters:
    +
    table + +
    +
    Description:
    +
    Called to make a placement on panews. The parameter table has + the following fields: + +

    + + + + + + + + + + + + + + + + + + + + + +
    FieldTypeDescription
    wsWPaneWSThe workspace
    frameWFrameA frame initially allocated for the + region to be placed
    regWRegionThe region to be placed
    specifierWRegionFor drag&drop on handling empty areas
    + +

    +The handler should set some of these fields on success: + +

    + + + + + + + + + + + + + + + + + + + + + +
    FieldTypeDescription
    res_nodeWSplitTarget split
    res_configWFrameNew configuration for it, unless + WSplitRegion
    res_wintegerNew width for target split (optional)
    res_hintegerNew height for target split (optional)
    + +

    +
    + +

    + +

    +
    + +
    +
    Hook name:
    +
    region_activated_hook + +
    +
    Parameters:
    +
    WRegion + +
    +
    Description:
    +
    Signalled when a region or one of its children has received the focus. + +
    +
    + +

    + +

    +
    + +
    +
    Hook name:
    +
    region_activity_hook + +
    +
    Parameters:
    +
    WRegion + +
    +
    Description:
    +
    This hook is triggered when the activity flag of the parameter + region has been changed. + +
    +
    + +

    + +

    +
    + +
    +
    Hook name:
    +
    region_do_warp_alt + +
    +
    Parameters:
    +
    WRegion + +
    +
    Description:
    +
    This alt-hook exist to allow for alternative pointer warping + implementations. + +
    +
    + +

    + +

    +
    + +
    +
    Hook name:
    +
    region_inactivated_hook + +
    +
    Parameters:
    +
    WRegion + +
    +
    Description:
    +
    Signalled when the focus has moved from the parameter region or + one of its children to a non-child region of the parameter region. + +
    +
    + +

    + +

    +
    + +
    +
    Hook name:
    +
    screen_managed_changed_hook + +
    +
    Parameters:
    +
    table + +
    +
    Description:
    +
    Called when there are changes in the objects managed by a screen + or their order. The table parameter is similar to that of + frame_managed_changed_hook. + +
    +
    + +

    + +

    + +

    + + + + diff --git a/doc/ionconf/node8.html b/doc/ionconf/node8.html new file mode 100644 index 0000000..7b79bc0 --- /dev/null +++ b/doc/ionconf/node8.html @@ -0,0 +1,597 @@ + + + + + +A. The GNU General Public License + + + + + + + + + + + + + + + + + + + + + + +Subsections + + + +
    + +

    +A. The GNU General Public License +

    + +

    +

    +
    +

    +

    Version 2, June 1991 +
    +

    +

    Copyright © 1989, 1991 Free Software Foundation, Inc. +
    +

    +

    +

    +
    +

    +

    +

    59 Temple Place - Suite 330, Boston, MA 02111-1307, USA +
    +

    +

    +

    +
    +

    +

    +

    Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. + + +
    + +

    +

    +Preamble + +
    + +

    +The licenses for most software are designed to take away your freedom to +share and change it. By contrast, the GNU General Public License is +intended to guarantee your freedom to share and change free software--to +make sure the software is free for all its users. This General Public +License applies to most of the Free Software Foundation's software and to +any other program whose authors commit to using it. (Some other Free +Software Foundation software is covered by the GNU Library General Public +License instead.) You can apply it to your programs, too. + +

    +When we speak of free software, we are referring to freedom, not price. +Our General Public Licenses are designed to make sure that you have the +freedom to distribute copies of free software (and charge for this service +if you wish), that you receive source code or can get it if you want it, +that you can change the software or use pieces of it in new free programs; +and that you know you can do these things. + +

    +To protect your rights, we need to make restrictions that forbid anyone to +deny you these rights or to ask you to surrender the rights. These +restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + +

    +For example, if you distribute copies of such a program, whether gratis or +for a fee, you must give the recipients all the rights that you have. You +must make sure that they, too, receive or can get the source code. And +you must show them these terms so they know their rights. + +

    +We protect your rights with two steps: (1) copyright the software, and (2) +offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + +

    +Also, for each author's protection and ours, we want to make certain that +everyone understands that there is no warranty for this free software. If +the software is modified by someone else and passed on, we want its +recipients to know that what they have is not the original, so that any +problems introduced by others will not reflect on the original authors' +reputations. + +

    +Finally, any free program is threatened constantly by software patents. +We wish to avoid the danger that redistributors of a free program will +individually obtain patent licenses, in effect making the program +proprietary. To prevent this, we have made it clear that any patent must +be licensed for everyone's free use or not licensed at all. + +

    +The precise terms and conditions for copying, distribution and +modification follow. + +

    +

    +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND + MODIFICATION + +
    + +

    + +

      +
    1. +This License applies to any program or other work which contains a notice +placed by the copyright holder saying it may be distributed under the +terms of this General Public License. The ``Program'', below, refers to +any such program or work, and a ``work based on the Program'' means either +the Program or any derivative work under copyright law: that is to say, a +work containing the Program or a portion of it, either verbatim or with +modifications and/or translated into another language. (Hereinafter, +translation is included without limitation in the term ``modification''.) +Each licensee is addressed as ``you''. + +

      +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + +

      +

    2. +
    3. You may copy and distribute verbatim copies of the Program's source + code as you receive it, in any medium, provided that you conspicuously + and appropriately publish on each copy an appropriate copyright notice + and disclaimer of warranty; keep intact all the notices that refer to + this License and to the absence of any warranty; and give any other + recipients of the Program a copy of this License along with the Program. + +

      +You may charge a fee for the physical act of transferring a copy, and you +may at your option offer warranty protection in exchange for a fee. + +

      +

    4. +
    5. +You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + +

      + +

        +
      1. +You must cause the modified files to carry prominent notices stating that +you changed the files and the date of any change. + +

        +

      2. +
      3. +You must cause any work that you distribute or publish, that in +whole or in part contains or is derived from the Program or any +part thereof, to be licensed as a whole at no charge to all third +parties under the terms of this License. + +

        +

      4. +
      5. If the modified program normally reads commands interactively +when run, you must cause it, when started running for such +interactive use in the most ordinary way, to print or display an +announcement including an appropriate copyright notice and a +notice that there is no warranty (or else, saying that you provide +a warranty) and that users may redistribute the program under +these conditions, and telling the user how to view a copy of this +License. (Exception: if the Program itself is interactive but +does not normally print such an announcement, your work based on +the Program is not required to print an announcement.) + +

        +

      6. +
      + +

      +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +

      +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +

      +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + +

      +

    6. +
    7. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + +

      + +

        +
      1. +Accompany it with the complete corresponding machine-readable +source code, which must be distributed under the terms of Sections +1 and 2 above on a medium customarily used for software interchange; or, + +

        +

      2. +
      3. +Accompany it with a written offer, valid for at least three +years, to give any third party, for a charge no more than your +cost of physically performing source distribution, a complete +machine-readable copy of the corresponding source code, to be +distributed under the terms of Sections 1 and 2 above on a medium +customarily used for software interchange; or, + +

        +

      4. +
      5. +Accompany it with the information you received as to the offer +to distribute corresponding source code. (This alternative is +allowed only for noncommercial distribution and only if you +received the program in object code or executable form with such +an offer, in accord with Subsection b above.) + +

        +

      6. +
      + +

      +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +

      +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + +

      +

    8. +
    9. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + +

      +

    10. +
    11. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + +

      +

    12. +
    13. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + +

      +

    14. +
    15. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +

      +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +

      +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +

      +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + +

      +

    16. +
    17. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + +

      +

    18. +
    19. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +

      +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and ``any +later version'', you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + +

      +

    20. +
    21. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + +

      +

      + +NO WARRANTY + + +
      + +

      +

    22. +
    23. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM ``AS IS'' WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + +

      +

    24. +
    25. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + +

      +

    26. +
    + +

    +

    +END OF TERMS AND CONDITIONS + +
    + +

    + +

    + +

    +Appendix: How to Apply These Terms to Your New Programs +

    + +

    +If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these +terms. + +

    +To do so, attach the following notices to the program. It is safest to + attach them to the start of each source file to most effectively convey + the exclusion of warranty; and each file should have at least the + ``copyright'' line and a pointer to where the full notice is found. + +

    +

    +one line to give the program's name and a brief idea of what it does. +
    +Copyright (C) yyyy name of author +
    +

    +

    This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. +
    +

    +

    This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +
    +

    +

    You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +
    + +

    +Also add information on how to contact you by electronic and paper mail. + +

    +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + +

    +

    +Gnomovision version 69, Copyright (C) yyyy name of author +
    +Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. +
    +This is free software, and you are welcome to redistribute it +under certain conditions; type `show c' for details. + +
    + +

    +The hypothetical commands show w and show c should show the +appropriate parts of the General Public License. Of course, the commands +you use may be called something other than show w and show c; +they could even be mouse-clicks or menu items--whatever suits your +program. + +

    +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a ``copyright disclaimer'' for the program, if +necessary. Here is a sample; alter the names: + +

    +

    +Yoyodyne, Inc., hereby disclaims all copyright interest in the program +
    `Gnomovision' (which makes passes at compilers) written by James Hacker. +
    +

    +

    signature of Ty Coon, 1 April 1989 +
    +Ty Coon, President of Vice + +
    + +

    +This General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications +with the library. If this is what you want to do, use the GNU Library +General Public License instead of this License. + +

    + +

    + + + + diff --git a/doc/ionconf/node9.html b/doc/ionconf/node9.html new file mode 100644 index 0000000..69a5aff --- /dev/null +++ b/doc/ionconf/node9.html @@ -0,0 +1,103 @@ + + + + + +B. Full class hierarchy visible to Lua-side + + + + + + + + + + + + + + + + + + + + + + +

    + +
    +B. Full class hierarchy visible to Lua-side +

    + +

    +

    +    Obj
    +     |-->WHook
    +     |-->WTimer
    +     |-->WMoveresMode
    +     |-->WMgmtMode (mod_mgmtmode)
    +     |-->WRegion
    +     |    |-->WClientWin
    +     |    |-->WWindow
    +     |    |    |-->WRootWin
    +     |    |    |-->WMPlex
    +     |    |    |    |-->WScreen
    +     |    |    |    |-->WFrame
    +     |    |    |-->WInfoWin
    +     |    |    |    |-->WStatusBar (mod_statusbar)
    +     |    |    |-->WMenu (mod_menu)
    +     |    |    |-->WInput (mod_query)
    +     |    |         |-->WEdln (mod_query)
    +     |    |         |-->WMessage (mod_query)
    +     |    |-->WGroup
    +     |    |    |-->WGroupWS
    +     |    |    |-->WGroupCW
    +     |    |-->WTiling (mod_tiling)
    +     |-->WSplit (mod_tiling)
    +          |-->WSplitInner (mod_tiling)
    +          |    |-->WSplitSplit (mod_tiling)
    +          |         |-->WSplitFloat (mod_tiling)
    +          |-->WSplitRegion (mod_tiling)
    +               |-->WSplitST (mod_tiling)
    +
    + +

    +


    + + + diff --git a/doc/ionconf/prev.png b/doc/ionconf/prev.png new file mode 100644 index 0000000000000000000000000000000000000000..e60b8b4073572dcd83b07c60b082ea6e2ba394c9 GIT binary patch literal 279 zcmV+y0qFjTP)fWJ4@hJr9=)Yx5|PDs2VC9?nk002ovPDHLkV1i^Ya=-up literal 0 HcmV?d00001 diff --git a/doc/ionconf/prev_g.png b/doc/ionconf/prev_g.png new file mode 100644 index 0000000000000000000000000000000000000000..476d9568c900e2ada6c2019b67eeee166a5f1288 GIT binary patch literal 327 zcmV-N0l5B&P)18~ zIpil5yY|hg&aw;rvXQ~olHp&x|G5Aw{ug* Z|8M28X+2RX!WaMm002ovPDHLkV1gF^iYx#C literal 0 HcmV?d00001 diff --git a/doc/ionconf/up.png b/doc/ionconf/up.png new file mode 100644 index 0000000000000000000000000000000000000000..3937e168f44bc997766dbe3b9383bd1db44f094d GIT binary patch literal 211 zcmeAS@N?(olHy`uVBq!ia0vp^Qa~)h#LU3J`0lmo0U(Dbz$e6Y|Ni~?`T5VEKaXZ$ zILr2UCs3TRB*-tA!Qt7BG$5zc)5S5QVoq$YAz!lrkL&!DV=`*aVJGv=2TrrsUKR?`=XF~0x$5q(BOL&qeQ?X^G$FnGH9 KxvXK-j+@iwFotMU6!O18Hw=Zf|sDb1r0dX#ngzZEzgLm22-BI}kz?pMivr3?^Wk zNLsB{GX4N!VarBfTaG@EY-6Bzws*Jf*_rixNM2XCP)Vgy`PfD4KDGugbuvjvFqI=I zimJO4l({N6AaNiccjS@^6%sCitK1J!s4f(D81B99-klLhvT_$2Dp*z4?(}rO_xgRm z?kB&#aKRT3{nN$kBk&XbkBhVcI;>fqTku@1y{&C^YofiiJ-IG%bzAalFk?Yv9zSox z&*Z^EICDVn+BMf`@d4%c16n+7xkLwOt`^UEW|r7q-XI`=xRtd`x=)MS0;0=|G}V%8 zwfKI|a;XD@z|pm%%7Z}~KFa`0)& z&x~OK3iz~%?lL4V>oG$hFlfdNJ=!drgXcUmJ%9sp0=ak&G1H1DK$Id54W8us`jc8N z9z$=!!vc+j;o2lk^TY-YWEfJZ#kV?!pt!di^hmLCXPN<{#m082A>gP%9S0T}YJmmt z9QF2~{7kF{spy`KLMp=3YLt<+kD4iB!y_;=0IXYuqM{NMEkl6;%mkKS-3}iqzC9kd2&=RwC) z2D~B)1W{8+7z9y7kN^~sY$9m-4K`5)geo9341@-YnVQGcJQkkEf_W6#WGdvAT4bq3 zR(O$BpA;-kvk@kS6q7u)-c#$n@Om#;S5)s^$)sZa%^vW<+T>8!U+yBCQXwz7*+O3j zaljRiIG6x2lRP|#sqyQ<0mxaqNFlQcSePQNBLMFY1HOS8Bl83-riK9HGj+joLtc9Z z-;OA65hy)Z8dW%)X&ZrOj)o4UWjkIP{gW&4fp-)2)+F5#b6x$@lEiIDtKOIZ3k*%}yuiwi`v3w(i_3VXKDW#okOLOza~KhU#0TIa8yc{Mf`f_A!jCYL zM+XyD!s$@vc@MaT?MIsXVaS^YtU$H<=Be5zhZ!i|t#iAjE!zjRE!2b{WN16Ql)-duD}%u6 z%q^X+2K)`G#vEaE6sE^}3)JiqlXh>w$__&RX69;p;yd7Bn}s>fphJ5lL6}TTCW69n zlcl%xyO0piT^o*HyLeA&s%hCzCSHs*ZFwm2VJLXdA~ba%(f(MEWv;89b?~>_(Kva= zvbme1Ja1JZU%52x8I*%=^8#`8%G)Eq&GAtiG{`QGf2#Ck34iWdFtt>`MVKMD#|Elz zz0~Vj04R26H{Z3mw2A68 z53f^Wx2>Tz&S_6Jb1d)y{fb*lQ|DcszJF`v?5SkrY(Tt-P&?5!A@&o!-+kwP#%_9G z+IGes8EWk2Tj$<$xU|tK-2KRqD44j55T-7fxV1AY;lsZru5RZOZEHK9x$`KW05X!x z;8zD9sJ(r`Fz$rk?BgyU8Nyw*mdBS)R7wLNlj@@3jx^$je3Y7p9~p8hi-tQH5C`SV zdS3J`kL$#&!64K~;2><4f*h4I7m0=9$d}Z!OcBRCGfl;&d>}pVa4S>3uLO_P=+wtR zJ1f!GPACQCQbB9csV1ePoH%{~2b~;E^Aylp6p%JoU=KhI@3%58aAA}~+9O9Tz$e1` z`U)VR45r(Fa0K=No)5;6Rqa59;IqQh%z^JKL8Q1jn_C5u7r=J>B8S`t=pXUrYQd*N zl25T4zmT>LD?R~jpb-ua7?S496i9Mb!Kd&IRmSC`P@V7;40sOBct$gaxs}+BUmWTg zxS@A&0KN`&ZQltb4^`fJ<`-RkeO=oJcCN3+Uhc(%8?bav#bzXIOl26L8uf{71HCR# z413$w-REz-0ahYQx~}ip+B2}T68rs|u+7aq11$i3dv||-b>V-1XEVU+?c-g1M-`{` z@qwO=gIl}$D)4T{VBe13{_a+o_p^RVVd!5Fr*aaU0y=axX?)RyvV=9$WeJEBi<^+| zsNQ+YraVnu!VCwvdHz}7C{2CxJJ&867FKa~!z9~&*p&iS65TD2e@uJ5G}qu2fH232S8}i%X7&iB`1BheE*;xwnRg#~+C|Yg<-cA2 zqsjKb^(m%Zb+U7@@eB7ZC{1Q@VTzb$sP5`A#py?*@Y>4EeNbyzHrSW8yG?7;5_`1x zMs0TjzP9a|S6x(^(j@DZQ&Oxyo+wv7YWzr4M%7s2UUWiMsuKBr1=DD*ikh*bmtb8p zxde%&Ky2wyH6%^X>oetyEdI_x#f<^+tYHg6~4;y`Y|lSD<=U1IdGL6O}ht zl{Xiax9>V4tkz5KkY+6S$UnayG(&K-LvVD}y;sT8m{Y^iS?d3icq&&eKYQH{FdyU^ zcbDlLPZiLz8YO>-cW1?Z`F`Ntduj#mfy}0v0vZFzOdZiIQztaDNo>Kt|K=}SVnQaJ zr`0nhL*hsZ&!kSW!j^LGxBs&F{(kya*MV_2W*+8|TYmX+ON|jI{q`$zFY0B^z=$Dm1EF1I*ab?KA`{(sNy$BGGW&w(D0PJR!da zg6aqfszV5B+VM2npg27VL^EdJ^1+kLpHaSR%H{I-rI84P*oT%sktmnHj-6l&=hF!W zKUbZ+8>@YO-B?FXJPIHLgj1Y-1Uh!SL!=+mR16alc?-wip=ppHz5a(2YwAhJWzSrF z8cd=ioP>0!Bt+ip;TgP&OG8{8LaYl%riISX8PoXxnZ|vmRL(PBbCx<(_JL{f!l#bN z6cZv6b&$TN#>ZdFWF|WUGYf+`Y44*1F?IQuZhZQW;lo$gO|(6Scei57$0shjs$8xt zzxMD11jLi0J*kmP5YLG#V)1lE@|O*4*Q^*sY?~hZ*9qb@VsQ-?1UJVRn01lSd@SHjITHiKIQaEKd5Lrl~0jv2qD2o_kQ|};NM%dIPcXv z5^bkdFOng>koA!0C<*5dZ@hIzEk0;!(GgOM8LF?a*ZA6_XVl}Aiv8QCC5h<@l9;aW zQS6rOgM4~FMZIBVT0L}p$s-9U8IjWQ3%;>D+;A1T@Sn5k;IGbaEtN{g5XwdWb4=a) z-x;aj`yVSzyZ1lxG~TL~$Cv($mnI;QF=#b9_VXS{7NcX&i`F6PVBIGEWwckC7L#$43KBPc(DjpqX`KD-S7%YJk|jC0cR7Md(((;6a*Pjjr13=r*M$_9Zpf zqN`ZW5^YKp7-2W#`~4m^n3x1oX<`6N=z)Sw9S67o$+zQ)elNc%V3gC;%0Vq`n!)}V z;5Fc@-y>j;mQ!8$!Y)!-_`*(`V9ygT*euUnHsVA1^1{yEUU;=a-9G?`8q9`i4&{{i1b83Wja zOoC+r9xkbA_ER2k({L;<7-|&^(ToH869B5Q#c6=cY;M6mM@^n$rbz9fin%xo+!H-L z7aP>6#1@0GKw{+(n*mF8+yXOwAOPn2F43|3%FT(!DIIkjaKK)xvBB?#$eP|ez?W5G zgMTNQ-b@P80bu(~%O3?2neON6)Zios>Y@U-EziZ)M^q3XViaZ8tPn*Xdu|kUCphTT z32f*vY#ZCbgzR%L(STB`5`FB)%oJ!u@1H{CwDKO&--Meecy__Z<|Wb2#&AE%;;W06 zJOQK<7dTvSYm1gV2~VJ724O;5VNKnd`m}rNR&d6$!_TfopZ#k`biU~f+y9D{=(Bf#ars!-vNRgtvv*jgQIyz2irNB- z+9F`Lgw@9fd(y$Kn)%d~S8TlCaJVFQj6~Z{rBdJKGCZyZ@+O2d?|0$FVSKUQ1F|bI z{tpvEuaKSnl=7}Z!H3PW10{a^GoL9}md2M))LT4}mr@I*BvhLcLLFfaOMw-D$naPk zx71o5Kljz&Os)>O_IGqa&FSBp?s^0K`*G}$3OsxTLQK6y>gXkaTY2mbWlO_Bw)pF?9yU>iD32GkbJjo#ExFn-d(g3@cmoL3^1AcEPX_5#Z@` zeKq>>da#^q&M4NRFRz2y=q?pA`O1)Y^1-FTa!0Jgc2Zx>S_S&;3RMGdhK|cw!7~&c zEQkXT^$3(=Sv3|-WSMBA1TI9|ZRQAWhs0rnar;Hox7HfW=#lM~IuXX^%fAb4VF5_# z-KKS%VMHKiHCiJnbZoU~Efx0iKH;F<)E6E?n2kiQjK)TWS#H2Aax3Tt-^@VEDWA-onxL8y;Dd;o2m+>$2U8#+Pi_coOfuM?#wDZ{ z^9;2U43pqpDGn=tWdiYR4O(lA`f-c{eCz3UI ztarHOgkm}DplBWU1uln)7%5$;AP8e3YMkhY6Xp!-TNFx!(^`$v{}ibmC$%X3LyBdw zdCpb5@hyQEyeFDqJ))phVh8{O5naAwr&I^O%TVQ&5Sq*5=WTYs5{YCo5x_Qn?&Uu% zm8Py(b^9&1M?M5T_d_ZLTlmdVY1a6!R?z<*+?=F;c4%Q73#SGtsUtWuo)Dlex0vB!rk*nFr|S-Jg{=Xz6n)%8c~IZu7ArRBQ1KHeusD(6pqP7H4}z!keg?A>o* zENzX}hpRZuB_!Z%XhG=BwjBmN@Q!i$reA36jITE1$?*)pdQ8ZaD~ld`ab@08;d9kA zA}h&n{+pQDoi@yQP27fwUzW}_7pHB3FjJU;A~8PHKiiv)L+^q#AhLl5KA9Xe5DLaX zisHU&!uSOsx>DuZXMZ>$oijS0#JW<6i^Ki4KuZGWI=7zi9b?#}DN^x9DpSu*BwCT2 zpr3_d|6K{o^uQ&eUKzCilc4>TmZ4QjzpBx`z1UgLn?{)Jdc&z9#8(k1NdrJ zgftA)rp3dt8@xvoB(1cG2lpw`^+ub6iT)rz!5ORr3wHPm5RhK z|HeW_OQ+uSD}6Q^;PD~IbA(NBq2uMsEw{Za+;{SiM1er3t>Y9dsvrdhGx0|&vE=Jk zs#eF^EaHxp@+A!dtC%U0mU>$;Dgx|>NclQmLHc4@L&!;C(u75h9lc}}$olNg%aN?; z!|1wd(T9Kij;MkO0235a+XR`1Ig%s(h`5k^_}5Td48!NI>bt-p#}R+p!-y*gQA7fo zd=02vIRqLIa0o&P)Mb8-a4g`5eF2D%!f+T21QQHg`BmZV^x+kZrPzG)q0NHL&mU|A zY{6)i;{?nT0v)c!o^O^>5b|jJ!zfj8o4je*moJ*QWh>ehkZNrCmjoQF60txM2N0oh z5ThbyZM*=9orC|I1g{tAsMFJE@llL^Q6?n{kAucsi>`jck_Q@gCA#{_JeU9f0abK4 zn9iBOo0c2|L|-JP_~%0ozJX}NbHGA>nBmz@p5P$<0TY&htqR-9E*&k*D5}f^lbA-W zh_wy;LesJSm$z$=k*mD!X763VN)%#h+!h?dLt=OEcpR^J*}P)5HeNq~V#3k{V(!k| znVsy++-2?^uh$~sQ4y%nS_~IYK0GE4HQ`kRCOjloD3GcRtpX-U5owiFC8bS6MN0`# z5lBhT@0{qYk9Y3ld!Fa-obO!4iwr-Lc3U@y>2qTlYez(W?`m$?_+Lz1 z`|l&ozJI^PzO*F@TxXaKtG=j8`=aN#W09Dh<^doQIFW@GqAdbpa-z?Lh*c7rzhWY(6 z*)u#{`kk*d3dX3C#G)c}QfL#aR$#O?C%wvR^YAsycn^OyrB$29X4-Kur^ZJi(QLYc z6R3%z>apP-j?y+8xzKW&=X4Mq6K>}|$`XNw;*^G12pYIR+sOEV6${jM)ge~aH$Qss zDOSR}n(HslF&qB*>z!<<7bF&Y58mD^`!`H*JiJa)y;S$cM|raFX#Co8BS}1g2Am%s zlxEw`k6&Vsn_(`m0CAyr$7d9SoJ3krPbYrWaez78_ADiwE>e*J$M<;0;eh=I5OL_lb8DtZW%T~9| zo{vs0N92Q(^+7wzW7orCH#ZKWdLs`Hj#>9(%{?_%lBrvk`6_LgyYn;5FZ}!NhWQ!u zHOx0{_$e9YW3T1JFgMn7IKw>GVgM80`0UBHx!G||maF4ZV!?+kG9yXOGi;>w78#Yk z{V*Hb{CcT!y=}U7TuYm@ugbgVgvq5&A{~#}&v>{?^A~W64Rf5EHHNpbYr_M7WZAU? zbJ(?A^Ht(4kVW~RS~X|4NCqA99FnnC4L!4eLG*ml#9qx0WW$KjI#UvW(&P|vz<7Zs zZVOq-$#$ByF>$cUo!??B(vA`z1~ z0x=Uywe<~d{UA4v&Bq;%yX}11#jj~SF(XKv>aMae) zU5qqNAZg0ybBVsH4DU_ycw@g%j)HoJsx+HTM(P7J|BeWRyYSyx(y|`fp-^Rq z5O_xFavY`QoOcE-SKB#n6*rRxJjg|az$PH=Hm_lpwN=x{_jz#EKDi#gae zj)2`yAu0eT)BM)sz9-+}GDyV4M5;MHbj* z#@(jNqwpLo5RHp9u$KoJIjKA|pLnLj6qh3{RXEmrgPSzcc5moqAFpP^k7c-G6PQ9g z8RB^_)*d5QKOmC{@0cY;luNPq=YHeG#JM5@nc?4M$;3TU+KDGJP3PgG5>POX@YCE= z>;@gV(F~@?8MfWaZWK46l2AL` zHd@v^Vum{82@Jd>ad6^uji|;&A2X^W3%4xMw3c(*J<=1Y@)eelG3-uXCGjURfY6ahy0HdDD68#K!p(Vg*dA#Lh?R@e&e6*|-K7r~P7-7{jkWa~) z$XQhovJbO|BEOkIlYtR1g$%Z)5*k#$XBtd$)>c_G$QTVz4oYd~DT$5Jc6Xl2acU#n zlqKVpF;*Z$to#F(W-J8|=CamQoMqKJum8%@_L9zimE2L2}vE(FF?9SMRK!T1EW=P|>Hz$>$Z4zq6^&dG@T z3uTX3D$FFWXjC8Id1n}7rUPYKn@BwLS>0Y?!uEVQMfSBXGl?cVacHuMJEL8jT4VNm zmZO#QlY}AcW$Wj36OxJ%&l1+cNU+_{bXk~UCGaAh;(|A4`*`NbFe>NACh5OMv6w{| zYZRSmL)44%qvKw|_+^C`zG?YotLE^_y2hnC{IY6iv4Pn3$=IY^ykNoHmRuLRa^I)e zv6J!!Y|G76-(c5$u;d_-TJEwOL{i&bw%m&+ka40(1OTJf6U4V-GT8Suu$jV;lATdT z`c^?>!kB<4b1Qyd|N?KxHU}z{rxV(I7eiGNM3Ixa0VrpOLCcWU0*M zTXY%tzQng!B_yD$Dj*kUsAljk&Lr~cb-8+)cv zPIkh_Gq7W%oKio=xyQ_|=s@yJYj6y=>QsUIXAM)!^LEeP2F(g)E*T)rp1o0j60z8k zQG!^i{^6LVRt671`%2!QHK(i!#n7Ysp&FL@YvNVQc(;Tz-`A!Br%PUvc|9xagz%Qx$cdG%H+xj2%b}Es~F22veSA} z{k#KayIYR)qDK)w|o!j5wMXY=dpu+L*19x za(=y&MyO4$rt|AbUW&fZnocgh)P$~PVdQatP{&&$j?*>Cq~RdL9HWv7$|jki9rP;$ z`vogo?c#$dF;rt{8Q3BYa>f)KdZ z>W#iRUe`%GmC1H*bPL@>z5o};OI}q4e`$?@C4ZMXHjF1DEVk|ecC9;?D@vDKgeok*<4lYnI4>L_5Gt><;J9`$CW z-(_+wci&UAUF3RCy5asY4$?&9~bc~$UIvGpi9G2Ab zBF@xa#F6v!BCc|~dl6@S+*9F_Ez)u?*jpnWBa5KzUT_aFeT7}9!X#ZS=hO6NC2OOX znsY+2*hFQ9`a;25%o`vj?U_(-i!f9yO;BHy0n#xf=P`zmiqaaePZ|^gA{rLYjLzbd zAhh8PRH&?#vQ4M8s^ry2VDFhikPw3gk17%gO&aE7OOmkx^c4jy_b0#c8c{$Ry_|2m zKlvc^iar-ZJ%r!S$QzPRBtIGUxtg6tsD^Z=T$&-LyR!(U{(lWA<#0BGVm256r0fy)M1;3C}!w6=3_Td6S(a>=rIX+LlkQjVp1ua|Kc_U{y z$yy495Y8Jb#yMP`=LA}^Nc>1U?0zSOg-UzMf)WRiS+Pqo!uYKJaPi3~kFY1bpKHU3m!=M7i?Pu7?&^>QmEF zYxZXk=(gML&mLS_W1Gr?Aw@9TDX@ab>O!lGspzEY4+{h2St%|IC7C8onhY-u zOL|NPFq{|=EoaI1I?lb+0ab;Xm9QPU1`R8uQP?QB$nuVQlEpI)qL3xYg`mpuEP91H zk{moNcCOWP@Zq6k1>;Y)m=gVIpDR81SB!=tEngKhsqf%*V-vQc4v&zHerM8R0iG)= z{5Cwc#OQ{VjZ!OE^zzBfHU%eh7CadzQ^j72F(`G1;?AN&Whx4Oy%pSWPzmH#3I%iNWFxheq)mXR^RE~Rt#P%+T1H48Om2pY?35p}u7B+40n zcW<;RGmI!-qS)m3p*hT&?83X(!*bMV+nOk8+T`7CI5MCy?tA{T4(DPYan6GW?pt`v zQZf~eTyVrtL4qUYrO0#pE`+0pTFicZ*Hp9e&Zh^{vTJOZPu;oTiA&#ObEeGIv&nLC zcfOvqd@nV7&40AY^0r79{nC(#ni4e))lY7I^Xk9c6UvipCVKVL^|M=}I_J5dxiA0o z#;y7x)U)7Gc0N5A@G%iIkGo;#%LRG45HyILldB_gbp++*;-DheuYO`YlI!bsL=`To z?TzS8E;m}1wMmdl2zm9Iy?R*NA4rq>Dn8lim3U>Fu7b_XdA68zqpz%_icIVk zIj|zs46_i6&_6f6)ipC`YU*I6;W4JQ=&XAL+*-%5h1ZFlXKsy7y6 zakpg@8ht6K#Sjgpsy*Hzdwvypsd)zksW0jyb5`}S zC*Ox~@q`r3H((1*BB=XXUGeEVL8DZqT=aDeM5Q#yk+HmA-p;86!%v0{F@(IZ=?p*R z;b3bC)z%f61s>UZ6K%Sjn%*6{QEXDdO%3RLnC$!H%BkOg9+2UMjR%s`! zmT-VjJ3npbA1tr3AP#EyXu>1w>`syR+j4%msTQ=IA8xD!HN-THBXxz9(-oSfJ?qd* zyYdSkZ*8A>K@-;Y`?2TEW&d4B=;v@<%%i~7!E~dR9{9>O6K<%{-1+a-*FN2!LCW=a zzCW!iN@2AQm99ps1IMi+rh1k}Lynps8dBKU9S!Nqkh9Pb3&|n+Z(Nu<5N)S%!3-aQ zp@B|c9=U7GZWnLkVa?Xb<|odmT)s5 zAXI?+pPhX6H%V8-C}`i1H4yQ`@!+y$atN4Y4vQK-s(}Q>8oraN=Q)DdF4Ew!*cNiK zTz7^eiKOjY?y1!dT#2j)zv-S@W1t5%R((1aYEhmx^#JX540f!1vH4>mW z#hZHR5?&R(sZ5?EdC&nvW@NRroXVuFK@TT}=27r2J{XLAjRnr){Yyj&9l!0pawz5R z{_%-^{$p9|PI@fMe{Wd-pQped1;Tj>uH3ocrU&+usJe5(t%aMXbM%N)*)iw1<`IQO z8`jW{GedjVbgjqorVQ{B=SSw@ZEHp$H%If&Yq=TPhv{niyd{_he2SzMYfm(6^_j=} z59`}@i+3#g`M-&8t0NvL&&;kZpHEo1H&tp6E&lvBrlynf&9txX&1WXJ+wRS0^vkz* zkk}){Rjzna`9MlHm@_YmGVd_4<*)6pNfXomhIZ!9A2{P1sH(a=qp%QxNq?@coV#Q_)Y zk87jEQo}L@>1Q)hY||?fJ#Y#8`vPp4Z-@rrJHfIAz3>oJ+BLW#k;$m2(c- zBkSM*r3)Ex8o55%42CiagZfHnz2!XF5L6lIylv;n?G-GG4~(?)tyssYxz)j@^W=4D zd=c~9ys~2yNXHv#L@oE~m6F3k|1J7n)P{_7(|VgC;wWdH2nUs{Vdt z+kJC8$uFOj6sZnnvWKM>jeuMxg&&CF9*zITd1b)gut`nm907|+adil8n!kN_TJEIn zs3SCm(V$fi3c&qOiHy||dp&D=CgcRb1_?97bJ4QqM_pSeJ)RF_q$jU=yog}LX z=hn%RoJ@|=D6&rDiCRj|O4pZCn+KG=sETv^B$`JY{Dhpv_~Jip9%#E?{JBjcs^3ak zP={8<#vb9sZBgr*BB9c?!p|rE3=df4q@pm{%|P5iWtqX|b!aW}%`bk4IC&!y|@5bOi%k5_(W67Jo0Og6%!` z<=uJ|szFk%;!6VgVAFkh1AG@+8*26CmLUj1d9~xMi_pPwizGotTT&5OB~vwg|`QFiT7Zj{&E*2qu@ z0owt3i0$x-y}L$U*tHFQ#5U$(vIJ~{ZQc}XrO|p=t!5cX#(T`3asp|gjW>uX>KG-* zxCI*%5=bC{if7;yXgM*Drom7+;gth{(?Sl%G!z28zkBaDGumBSf`3>`qxoL<`@Z}5 z-QVTVJY>Myc!UY(8Bs81MI(6;iks?cdio9RH4fGZnM3FktVKS-J4)>#S%KjV57>ls zBvu=q*`ZWM8)}SX00eT_%31p~U7Cg6r!0F%OcxEjoBAVK_GnD~kqh3;+9pDmyu7bL z?Yex!e$|^6T&#FUMT`hJ{OI{wwgTB4N32lM-H|l)VHG0kz$iNIuTN&Y1o-E-Jju|$tb6QOqZ0exJXE( z-0p@~vLB+H}BeCM0AkA*UlxrOZ>+x^^aN?_T@OeQ6gQB*X)eZqYGHwW`s#v7UzEEx8yq zF<2cS(R0=o_G2tJu&M}mjk(yc>FpeGpe{Ysw@U5;u!K*G}xDS zd(&jSUwWfp;%G){)}=SJvm%-nP8LoOAN1TldK;%uKzcYMGisoNMM{!xq6h(d$w&iz z{nMda-~z2AU;k`?%YO%?L)^kr$Ibj3hU;j*(FUUsGdMH|Bg1v^Q z0hzNd#)*lv4tRy;T#k6p5hM(2Ck-LLBJM`FVjb|Zq<6*+en?m2LWoP4v_JENX_$rd z$4AT0Yu1my5jh|ctTX}|MABKZp}ROym_|tOM&E`&vu9YrFiF~;7B%V|*@b*c_I4X@ zDINWPc1Yp4{BtMfImM0J%OE7Sdbl;Vv(PH1k4ZY8&DjWn^5%tI6(6W^zorY-N$0;U zp8QGYzZLg|`r-%;_IozA;0O((gK_$bD>NQsEiKwo31sfV|B}xZd|c;1f`bwvmE3DT zuk)sgO(H8xPE;Z!;LR>0L7Zj|%w4B|8@xIx`3BX)dr1S+Gx zo6iQOJMpY~%r|hhmms*u6y=?M*c{RpFI&~N;n+M05v`V88zV4QoY;N&w@rXI35Kl ziWhhLKJ}m`p3WVj1kKZAs)pKLqzZAxB}s^{pC02Ps9M_xrpJ+Qw@UCRon=_S_5vyE z%uXGWESnBD(<55&%=)|0khmWI;SU(3-mlx6 zGWKA}B&RAI!$3?)=N3+BcQAy2k?p9#S0OeEm`wCSI-opvklBQU*N%hC3aZeEi2?&z zc}y3g)R45->y$hYB1a)*rd;Ox6Q9v9+h7&)t>_I^lNSaiouQJvaBDnKf%iuQ{;1K{ z@D3xf0dJB{kXqhs&c7VgV%m2O*L`u|M^RvR$%88ueo&qh26ivmu!{q`S1$ab?!YOa z87$T0N?Re@wdBeyf`#@b-3N^mW9YKvW@iE8;Asq2u&+_I;O&f$YWW)~NuNI+8`VA2 zbX5KSN48{<#`0G`d8HoPecQLR_pmTI61la#oMZ##?LIP?kf6EJK6JN4{Cr}gQ!gEuhxDMu?-qgm_-Im-^xi)7YQ&xBJ^S#!1<4to^7M?-P)27$~g%?{lr%{teD{pvTY$O+GF zk?wVUKQAk?V#9E~ES6J9^cs3=m{bN?inuv|UXa#l!B>CRji?F;>;@12@<)NrF*0~xYABuwufg&d>@THRl?cWY(W0ZnHzG6A&mhG!hp0a`D(L4%$!YS|jxR}0jAbx>>WtG}Dt z+d95Do!9YgS+jOtuT0ZL+jB@6;DQ-}J&kuayV_+nVE9K1W;SF@I$cj^ zrws(F1x#e*!=fcadwgQCe~WYM_%<-F9Q{6LV8G6T@yifr(go5?SX(lMV#2!aIlaQB z{xrJ6R(|X0x*~lhANbBb^`NFmpFe$F^q}rop4zIfOY+K17rbdVQ*r0EWMqUDNT;1v zg_<~XM3OL9hhB@* z2LIv3`&)r|t$5_=9kE4dA6OhN!m{-bNEVCfvN)x0+bPpjvu?W+077UA4ECZcn7$10 z*IC_~^utg|J>b>1)U8xG;MKPkrj3^r@{Dw(XRj*LxCI16r}Kf|ynvRfikpNL zH0H>Us#m&dm3QRIUP+j@O^O8onuDBA!r@5s8_D6NUJs-JOu^cooa<|KMO&#L%qwaZ zar#HY<1OM*bIc+hmC_u4S&g=$A5upYojK7O_Bwfo(_^aEO&>7KqvXH9Bdb`?{(Bu} z1U}-6v*79qiKB*Hw*r(OwK$Vb-7bq$n{?`J;Wn4W2_z5J$qkKKoaFI;)Xpc5|4^CO z{3#NgwY@2dkTCOYLL2N0fbKOXYx71dWYb!TM*iEj>hy8xC7K+O+a@1gg4H`;BbKmq z>FT~>fhli%+tWjN4pR2yYRRF^CNwZ~4QL_#5*&*34?6b;oDv-{CbxX(8oK6t5YCcc z1m~_>vzDGblMgiiYW!%xA5!2r+r9Lbl0O8VKJXwF@PU&HNXpA>3ps`Us`d5+aOA)B zg({0g2Cu z6avb@Whg@cNxMqirxeJ%*$km>DD-=viGVeL7R&>}-8@|;*@ zurt8`S+vFjZCb2auRYoj_i%fIjjV$7217OLwMUAjayHa71db_`GSFPwiV<&w)Z2^t zR}wg+U>$fryE!@+_HmWZZ|2gt{ct`5o}F)88d3S_adq!(48mGeUY~j5*L?vTFW_ff zGOr#ZMvnh}B~$kb-d#DN*jF6OPo*~^Ar9F$q&a7836;>jXO}Xae(1EtmEo>^_6U0M zyso`BEc@06*@{(ni zcyGmk5#)Rn<7^BJDMK*KBiTVbEdwoEwwxA2W1<&QPY7xZ^l=!GU$thV(c?diLY_K1`FxCb;ag+_#JZr4M z2s`8KJ79&R%lXnGy~HnFdaQPu{?blw=O>3pgwlIhOp>oY@SA;}W6eyGuce-nb4*PV zsiaOS`4IzSBw-a}#S0AZIX>NsC)xcZH@Xz1l$>Qc+EbO}@$2Q>@tqhUO>f`^StgFt zbQ{4o7@v%~09n2aB0cujMz5Cmtfm(nqvee`I7Q;TYI1$|plTkHiJl#*B-hUc8~pn2 zv_zIXHxI(QlFh(jWt?TqD<0+6!$4g0oc5c^QgN;9c5>|`u^pR1DcPI$+B^K=dWWe# z7iyq5&efH~or(hAaq+LJ_&WvK@!$(7`MnKWfh3*UOD|c{i$`b8@-ru7mn^Ym8$?o1 zj}7b3%>U~fgnF5|$H1hcS1=f&@^-va!G5|VJ@TWzFiU6JU$VsS_0b*w1V4*^+4|7_ z`=sHl5IZogcM!0o!ruL!>MddP3ww8iS(aXW^b5IqKY+GFSA&4KmMna%S{o^Lg}pJi zLkwY&$-8WX@;-nPOJ^(VD$TB(mcmp{4wv1#_tk0DbLP-vLMHZ`G7ylxlmM*2`w0v# zyBPf*Uo^oC7xv5vXC$QG35B|VVg!)ru%JB)bPK;=JXC5bhdbwETQ%SnTcM`;K zCD7WqgLJ1kz%9-^&O~n+>1|3!@zbVGqGFwBOK!=!wdDHmOT{T*89gDwlwvlcHVln1 zh3KxSCEDB@Kd+^@952D)i}utN7!$s=UxN*XFCRoVyG~Li z_baU_xp%E=LCI~pBn?U~sjRzCoV|H7>qh6M3qxjTF{JqH6IuNg1=pAW88>UUo9bYj zRSQ-ld(V$1Cz>dq=4|<)2+9iS1gruuBS6RL=g_gHLw{5oz@px{Al1xptRd?^?KE zVTzyhSJXghYuo-BHzz&=Euy(1z=bLm=KP*QrgP->WAM_`?tOm#LZ(XQ_8DifIuCt+ zj=!etlx%=uNX*7TM<+;oKle)XQy5@AvJ?WH`zi!QPEX>47GtSiXC8R4fzU7oe6D7~-MbHg-9lg{sKzauqsUOFCGcS|mXQg_DQ05qq;|;4{M&cRNT4V!} zMkLPQunr0-bpa@n5x~EB!Y@`7A6-p`)xb@+J>8p+G80~2tgQ%go2y0?<5NdZXq4OO zGeO2Q^9=ZDdOsKfaL0O#*2p4z*p*s^nww&JN0YMmL5SaJN__0m(V41Qd;bNJNFq|P z_WtwuQi8lGP3SeIcOSdrgAy$?w~l~^v|qJ;_i4Nfe%Jx-ArB7y_Ut3sMb{rl*i$!z z`4ZE}&l^0u!}ZL-x`C=Rm7hQ(ttBfzZR~Pjx{UCR!`zWFrs*3@DyWY*JjFYtVDRS-FZD)vi-trSnzmpkuWE#hP`{v7A8FaJ9r5BqP_VD&xd3^AF0WK><`^7iPu|~O0Cgs7 z@n|aRYDR^)yK%NDd@lLox9CMBF1q#NV)?b_du%cmW%*%W;mdH}$ANPE1sbM#?RBzT zlO=#bfgs3>i?ek2%1I~SE$Gj~cYCV-6yZn4#w_-SwGE1l*2$^~tsODd=Nce-T+AfT zS%0pfJmLJJFxbTrE=Q~s97sAj#^N0#Nyd*9-vIP8^L!Y-;!Q(PO%V?C9b{33NFQ&k7d?_H#D9QbZH!ged-0L_y@GTo$ZioxBXE z2T7H!iA_#E>)c^Q-P@m8$ztJ91oQY3{OBB=(2 zu}c-kLt0Z9f3c_qg|TTsbPO~lO2e*Nzq*LYMhi9TR~Pqbx@hCB?ev!yvSuGjUTKku zX6exZvrWJrP@z`{8Pt&MrwEx+;yPZafnw%3mc=5$wyIUW)9fs(ymQpfQhtr{PRJPv zeaRjyF7n&EI`A3EN-!i3juyuneT}`|-reE5(`>YW?Fwqo~|PiUlxj zlq~l3tCkh$O2@>O+B%W-8zcFR{oz^^)aLSh(M?q+&3>Q6wl-<@drBpx*DC4JIuj-_ zpJoDw3*SKMWuRY$2aIlJ{tTik0n1TIu{@G zZ~)V|`P9dQs-Fb$JE9X`^@yg?aCZPOx915R{PZv>gsC^HiFaQ{1QBN)Tr5(FkTcXO ziFfxv>BZrxCLPcQW}7O?P>yJH9nW8_>E=U9=cW4SNW_PgoW9g0p>W7lOx2Rpm$7K3 zf#nxY9?vBOb-<{6o={F_w6?F5pTN(U(l0MX|sR>pfjBdptX$C#wwpV`%I0W)T0GH777UtWR*&f7mbcWG#OigIS z8uu}0c(LY0wq9&PEbpZ(E>Mpw<=?WLkYZ&-mpZTXn(+?t;8Cq}qmvbQs@uyYP_(Yr{o@Z$YGz&T@az+a%I?yh10ZIl2i22l33|O?woFk5UZqbvFlYHJR z-&zTw3_>E!X{QO;8Wz;)Y|PdnG;Rx4i| zQfH_r8{Sr~FAj3-0jpdK!%2gjLk=gJNB(mb~oa$*W}`Q&RED%P9do>44er7L6quc7=Y- zET1V|7WFjUd+B5?>BR+~>6oW$_})vW7o$@y(S*Jj@IC&R*wh43xo7JvVrM^bPEJV zccIo)L>E^R!*`Vo^}WKVYl-1qT;?Mqd25o(m(Flnp;wkbaBNMDN5|LI*W;-PuXuGJYanEoOg7m(AP>a~At9k4!?lmlJ_1<^IpFzn+BYC^{YFz0{FeDGoEe{q2tyG9DPcqXs-%U zF)JD!9bH9_Xw*A8x{2nSSb<^@M&)v88`b%aJej)o#zbfsY47UVjyzo}Y|ntL{5#hF z!E%9}iXj`y`Ne>2wU`F$8qKT_4ty#|izYa%C?a1U$!DtNYEa;QfuN^@oT9kW#s~_} z0QSp+@bPQTgR`_c2?^|w9WVM6{%#9$k^17cq~HM}Cm)=Jl~#yw2@Z*9beXh8;pN;& z!+q%r+{+%8vwG=DHRkwbV#<+(X8swFb;ftbmF+4-fE*BF6*Zue(#b*_38i>hqvL%y zQAYIpZX|n>Cyw?wUbD=~rmup(3UfRBf`8c^(Org6QHqu+Bto9n<*Mt%d1arh6X%sL zshh@&!1hH22QOmNBU?i**N04B*eYH}38{Go(jgJ~==6EzOZ2IW9KyT_fk@(?pvb^r)7dGG18I+9~s!c1qpddm&f4tGRnu#!S zHde^;n9I9&rQoL4nL}ByU=eS<41lbg3m5J(`z_O&uTugBkN1i`5uXw zZ}PuKl4IO_@<}sbZ@IgEpqfz|Ri#g6xVPr+dLbqsN#pECv|GiYg*Gb{04(Nm2ez+lSt;YiVY&9|j#}rV zI}UVt-8&g6XrhN_JVYf1u^KGVbm5WWobhmMr84^{n)I-wX*DB|wqztyfVX43pU)AQ zV{vXkOUCnW(2}QVjy=Jt(BptT@ty8eVnxq-PM-DP8dJ8-!9bWtbe73?0e)S$ZK_8e{17@@CcI zcec#-IFKebciw;Htz8+N>NP`k_j5N;@>{%mRL|#19Fk2DC~pobp``dAe-IuTg;9J5 z#X>1I$d;vwrxEks$m{WbdXxkcm1mbG z%6=BKWw5!OubpEhj6w@vn+<9gUzPN9ioKRipg!(LEq9X)QVmOf1?Phwi2hI{qNMk%jgC5SNJvX1a}gy{>w zkIK=TC5RuB>KAb0 z_hWOYjB{iqwB8<7j4jpA(TrPX%JW55-~)-pX;!J(ST*O?vsiS+0)enrNX->h@~08l zMGyn#7#*rRqq8|dSqMuArpWq5&a`iSq^cC zGtOc_ghohA;yMaZDk<~JgHrpU=KS&}1Tgh;>`A5_zpM_IeWWSy(^B+8O>}k%(PP_l zJ~}<=G+}{`mzk_NM0{!zW}fhCV8K<}IYsVV8Uz=I!ET0XT*oyv6}Pj_tc4im%Q-W zs9dNPS-&HyB_t&%n-qI%GU};AbJ@lYt*Wq%)S-EUhIt{`f1}q#>cTNDOLK~}+EwFV zp>4ZrL9++K`Rz8fEPuP*cp&QVNAjpNCqfrRU8Td3nQXM8sP~arEixbTyX)@8C-U&Y zHa=-3p_}}FUouRI9|{exX5B7|4WWQp%A@(AUA;*C5I&M$kyT{8JF#JuEn9rw>WGJL zu(Zu|j)QBCRq{u7#J0zBBFAI*Y&~_;K)w>^IS_4Ljw0MviE^UNMPUP$#~f53`EW}F zVpUf}najudRj-q_Sa+}b4U0?5-OV~oWKUfbPaP>m^Am-iYVZ~eH%cqKRtfk8O_YnC zpNI2D=^+zwzlKnxfTRe?yjcc&I^AY6$3bqlw{LVZcTwi;H*d%6&2QFumquFL27MA~ zW$4Ru7rjbUfOEV;F*RnD$ay}&dyrV1v1KJ*H>~=vdjCr@%fuO1webTzS;M2BnNMjQVK4sQMk=@8gTUw?5;>^03??Pyp=aIG(=EYk!XMV z5}Df^gBl0zZPrM_FiUJW=l;CC;jkdOUu3U3ey*Bx|J907A_u-`p5)fTyjwFz#+spt z-mMc|cOLkd*_l{dq2@erGpCMt1sGw;9CgAZMEFR&iLmuJH6`R#DHf%s61I^?Qr2gW zw--gjdG-Vf{^L^!xk}=z*t;MW3*HO!+bUEe7OHa+Zk`3==6UzT+&s@7Hvu=#xCy5T z7|O~WTMmez7qAf@%b}^_D0>>+5y&;_Gr?ZiXAt_~pv~{!E&iA}1Kkv@U3`90c;d7- zoW&nBrPbUB&mRG8!1!+b~SlHK!D!7MANjmbe!Ku zeky)MT0(Uw^@}H&beGA18>wGB!OZZxBp->1Oyxlg()Lx3-p;m@{-U}=#EPy7CqWoq`Vec=rRC8o9R-1{d7I`;6dbabR+fR zw<$>(m5%O6sj>KMV4orvb&Dw0BqbcMNo)1^)l#5S$Ftp0G^Gx+sPPI%L2d|XiQnBB3Olq!Tdsuu{2e^kyodiw}o2+M%n89}Y1w;v}Nf1zz^qd4&# z#U++l?)Zf5FTf~AMLmoOhX#hA8}1_!`Asolba%^6vQddQ@|H9tr+>w{=q6Z)nG zw+b~4K9wz#1D9u{4Z@?R?(Fya(Nm7})#;F{nVT&_IsMiU$T`1%&>ln=sU*>;?WR+w zDXfFcHRRvO6x6?wHt26qdNb(LBwMEew{R~#4wxrCUqrk$umIVk87H*R!X_M>v*q6? z!(_{|N$_0M9vy?^9DZV;2?s>kxJ$GZc3xw&x

    M*2O$dK+?e6&efHyC?NN(9x6jl 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 literal 0 HcmV?d00001 diff --git a/doc/ionnotes.out b/doc/ionnotes.out new file mode 100644 index 0000000..f29e5e3 --- /dev/null +++ b/doc/ionnotes.out @@ -0,0 +1,22 @@ +\BOOKMARK [1][-]{section.1}{ Class and object hierarchies}{} +\BOOKMARK [2][-]{subsection.1.1}{ Class hierarchy}{section.1} +\BOOKMARK [2][-]{subsection.1.2}{ Object hierarchies: WRegion parents and managers}{section.1} +\BOOKMARK [3][-]{subsubsection.1.2.1}{ Parent--child relations}{subsection.1.2} +\BOOKMARK [3][-]{subsubsection.1.2.2}{ Manager--managed relations}{subsection.1.2} +\BOOKMARK [2][-]{subsection.1.3}{ Summary}{section.1} +\BOOKMARK [1][-]{section.2}{ Object system implementation}{} +\BOOKMARK [1][-]{section.3}{ The Lua interface}{} +\BOOKMARK [2][-]{subsection.3.1}{ Supported types}{section.3} +\BOOKMARK [2][-]{subsection.3.2}{ Exporting functions}{section.3} +\BOOKMARK [2][-]{subsection.3.3}{ Calling Lua functions and code}{section.3} +\BOOKMARK [2][-]{subsection.3.4}{ Miscellaneous notes}{section.3} +\BOOKMARK [1][-]{section.4}{ Miscellaneous design notes}{} +\BOOKMARK [2][-]{subsection.4.1}{ Destroying WObj:s}{section.4} +\BOOKMARK [2][-]{lstlisting.-36}{ The types !char*! and !const char*! as function parameters and return values}{section.4} +\BOOKMARK [1][-]{section.5}{ C coding style}{} +\BOOKMARK [2][-]{subsection.5.1}{ Whitespace}{section.5} +\BOOKMARK [2][-]{subsection.5.2}{ Braces}{section.5} +\BOOKMARK [2][-]{subsection.5.3}{ Names}{section.5} +\BOOKMARK [2][-]{subsection.5.4}{ Miscellaneous}{section.5} +\BOOKMARK [1][-]{section.A}{ The GNU General Public License}{} +\BOOKMARK [1][-]{section*.4}{Index}{} diff --git a/doc/ionnotes.ps.gz b/doc/ionnotes.ps.gz new file mode 100644 index 0000000000000000000000000000000000000000..d432ffb83576a7cfdbc9e77265d89c84b6d008ad GIT binary patch literal 109113 zcmWhxXFMBT7q&-jYL6&2S}UlcMihVA($Zzrh^?)WShXUtcWJ9u72OLpYfBJ&rL8?9 zg47;C5{Y=dpYHkHdw%DBIOjR%c@7|hgX7MB&;Mg!egX*$goJvBs6#^-Hc$L275c-z zaKcI8Ac;}d8-PBjZb*w>9HZCjsu7AjiuDsr!^?YK{=hS|l9 z3K~zt@_o`r6#134HRW{rO3AuF_gbMi$WZ4oSFMouV+?9gyMUcqE4#Ws%B;aJ(icVi^z_`X7(NIy^HsN&WSzOA#-|0aVg%1y)AMJps z7z}(xn!b#ygi~j+bn-ab!X~kxn8Sk& zUr0&Yk_Q+@rJcT+Je&s`M^om@FqD62jgpgn+)6kEc@oI`l)fhdms+x^ud!fXnXW8F z5J{D;aQbwuE+{gBxJd;dsOz6U*3wc=pG0+Ixawh;8akPS_g6RX<#*}F% zI4PQbdJ{n{BVb`TC-`ZtQfXVv(Fp;(ht|Nt>uH;$lbg}h?r`k>js~25G+g!-+X~-1 z7}loKXone-d|>04z2j*#ri8Yt^YN%EfKR~?b~4WeCyvu@p9C%058^9bfy!7UdHb*$ zSzKB)NteuArRYLV>KjfQjbZyGTR6+v9W4EE^c4J*Ht6f>>k`q5IHs-WmV#o?JFQcg z(l$6{sYN$aBby?LAo3x0%OE8u$GBfP2f)f|Sa6aJc0ehHginY-ZJ?!68cJIUmOBeq zN&}3?j_DKdy%5O191{F^J{x>YADu;yY9I^p$=#UKAjU4jl?5l>WAu?#s2OHA^I9)re4F~r; zIC+j45`$4UxA1jsk%!W?J#Xne$;b1fqGyRa>YiOWEcyyTMpyhYq>wjyNgnY?Dls2x zj5s>p$~hgx^YNjU^GP7;q4p_-Cm$ZMx4$N8$V3`wZ$*?KynwC< z%69+4F}^1|JA6adh~^(j>T2D~&;X;l#bA>f04y<^Si8Y|`nV=!S;iQ?7)9M0gtU1$ zIb3ZN+1WxH9VrnY*pj(pbcQdMc06p)_vFz?W$Ex4C=x(hJEwKzh8=7mBZ}OQ#Zyt# zN(g11&sfHo7Rlv}^u`&1VW$bY0i~{V3chp!m~SKF+Yg8wXvmU+ypiua)yTHO`${x4 zoVb@PAmPW7TW;viYBEM<25%TGietncZc-^kUw|(R`4&9Vji9tIwv{T$97WTOPzXH) z2@2QQ852bni-Hghz;9>$h*stcHlz*{F{Kptg%vIG<5ke{-m34aC4#hzr!<}(9Kq;R z0$51;?6c$2e0z+mkW%TMEPg8o10u-Km-%TqaC{NO*q9G;Xc2adg%MLQ__K;B_|ok= z3*e2Q0wqd~?kwhy;&)v#@*^ST^i%5lZ5#TLT0RV>A3-V0$0AR*4{Pm5`F58q!EG=K zhVPU%Jcxx8pGBU&fkf|5V~vN-Y6<<3uhf%xIEXcjChqrx3~858JtqM~nJ7r8F8~fjZf=lAQlMFcA_}QJ$fgtrr*Hz6g zwEYwmjJp{{JHg~BVUdT9kt3z2FA?SV8fGn@aZ__tg(tB z+Gyig_r*#&v5X*FlA;D;+@>9Z+D_b1JE+z9K~W5yY?HmZFZQphkbvF2jbijLrfLvt zwC@;E=}mo$B>HOcS}Ru$3X!5OgJ@bLoaN`R4hyEi)P3pMrC06$nsXUyWz#vJQs@TW z8Kn*Ct`KK0*WU_8?v#Un*c%7foNk8817T$n9*^mvO5Y7Vc%{H82ta}y$An#-M@so5hLUx2IoR>(*r$-fm zJb--Jpthy2y&%-;*IvFh+}B>nafubI9@g5o)0ng?7LSaHxE7<}_OBMRK)GI;0^U7F z1JjlAEamoif1F-grf+T7R8#rx9+zog1-DQIe`KWSf3OC9fXNF9I!3)YR0z8^m0w1> zKj-vdn0UHBU)zgiWKvp2P#03@ZgDhx3uN(X4fBephZDvak&+m9eg{rv_NTHxcG9KG zAYly94b!Tq{ZU`NvnB_E7yhK-=RvKO?hwFfU&x5RsGETbQicPWcCbX;9V}~QWGQAr;8VknWz+(0e_8V8TT+T- z6d^{Y^mkFz-acCS6i@A=v4f8Li4i?71#ll%I*$V-8*2A%MeCW-Ear9Tfn~z%Z8_dpAH>3AG;ROx*^yn`_h_?k; zjUAtrgaF!TN>B`ylHuF6nh!qfw3r>jE=37UTAvFnu0NKcV5qA*G0;&1u~o%HjUbN|z1Kt@rA2Q>=gC$(Q8t}<`?#*c_g<^$Ri zsvdQbkUO|=BC6*UL~gfZysOc1Akj(13~zM*H+%Tia^PyiAC6}F1_X_!qAC#wq~77G z$hDQdzhaA}&9qi*b_1}Ix~o*#Vi=!(Msa8(I|%YHh}^}u;|E_SLGno-kG2Qs2xWu^ z1AVMLhBg#To7SX`lIVmUe~Px}(Joq(x*V7dGI}rw0B?gOet=HMa183o_l>t&?EBJI zg_x5vtTbhdZz}}6@KG#g3|~S>sVK@uoy#uCD5C7W{2CH1)n|z>ICYV!B%QHm1XZRM z?zYQ^g`Y6`U;*m0nx%)X&2l~DswJ*?1N z2pDnm+vz4dt;m*G+Z&L0Mla}a`bGnC13~8w-tG3y+S?CAEt@fr|E+@D#}HD%#*rUZ zq+zrpg{*{*B7302Fg232PPK6rUonn3+=dLoC~Kt)Cpg?F*ANJ{&DKuCQNuy4B|9gj zD6nzU9Bn;qKsu$jVYm{}+DDCkOCsU|__DF}tx|D|M8~ zpaxQ@+2D1hHURc;Bo}oeio^+lN!vMt zN~N@aU@UIchOM`OIuXu%5HX30hG@4suYQ&RAFtZD(*N+EOefXQAQi7kcK;MF+mYZ} zfUmS-&fW5ypQrTkT1ctUiM$ng3EvVrzq4=vr>^0IieRX0V;4hFuOvFB%8>@xm$?x~RzxCiX#QDu7N~YbZ6O zZRkS4sK~?p(k3irt(Onx@CIy**n779BW8bc1>YHS;XLD9NM#PO>Z#Aah%@C-nfet4 z+dsjBe)d`V)0J$vn{N8YAj9F?e_AJ+#%cWx@WzQz`%(=6Y9E(_?gXJcSJc~Wj7#1i zZgB6CKr`^x81gLoNb`QIvVlVYC~_*xGx{<1M52{cm0P+=QKDAUQf&UR(bs8y*atE* zlbADcPg~RF19=y;p$l)ENqS5vD4N&;09~;-Ai$S?McZHsx-{Ue{%0WC&fgN`bLR_tx&g9kfpsoy=Hje7QeS*u!V%sA2YQKW1 zhuP=@OGw1=AyE_+P0Gs$qZJmpKrxi=T6-)F=X838wHE4Htwsrgbhf5AgkTM0_GrYb zN>MSiapEM2xKkRlN5WuX^zq(di3SV$uA*2|B$v&C%_{A1`BHXtBmk=mh5*2**$}EF z<_CaJ%9wUwgMb$D8R!6bZ{&r4mE!{(O|B}AP}M#}^6xX~IK3Tg1Q}8^u-BiW$m1Ek zY5S>zsLc)L2f0amOVjzKv}e(zSx|&FnAp@W0YK!)l)w&KAZLq{;a;$}@!&FwwuG-7 zK<7+qlpyKrXowbM9RS`V5%FcmyIwF@n4w{laVrxX)C5AN^PMRMyb5yV>zRidh`Re@ ze*|NUT+)Z{?FpAsF#uO-9NzZC%^J9_1OU($HxP$=!nzbniv6~X5oIYSKXbL##x*!e z0~Fa`adMoKKR0V%+UgZsir9ZuL?)a)iE0=}_6NXD_7r-^%SC4wlK=M&fKmIc0`X@x z^=dZa@c-tal13p?x3qQnCz86X5PtZ7S9{b6Sqo~9Jqy-yJP)Jvat-!8V!M^b)%e&;OHW+SC4#!d{W z5AS2M<&U5)G0s+-PGZrVZEwS8LS}v%>}ICjeN1^=?2eh7ik8HUM2~2?Y&<(?g{&A_ zty!hxem)rsFc`d6ZzL69@F?A{sj;%`YmQA~tqiFUQ?*d&^t1P2Pxbty>%`}fy$u<) zE`#SS%kYZt^VCS`pp4H+Ta6Y08w*)?Es(v^gSpX7n4k*012uT8H6`M+e5KlR{B{f| z`}O?+dudX2$w}a3Q$DPrkI>Xq8>moh*)l4!lW~16OFL83bQ`~Hl^rn#pQAe9Z?sB~ znq-2!mHkF0pJ~+uwMF&QF89<(ZA2A^W93BgvyUoM47cTXCtqTI+0$=lo`^RH+-qUN%8c6OdnSfR;cgZ7JEBD< zqe9s}W#tuoIsbQ>QnN6#z%@yzRnv<3ZdZP>egzqAbZ_%}Z`;06++A>X%9eN zhZ|)<=f3w44Mi+~o5f8Ysl1tDq*sl&uJf7gz2fvk2ck>8w$%66xfgJn=eU%#y*Fwq z?TrnvL62Pm%B>w+#I~ve!%>rd*eFli`ex}cKX3fr_SR*;rtGa-p86`I*=+%lV>QXu znwFQc!)C=FSyNjlN|?ZZ__nt_q&%eGlMGy&whDsRHr{LpbSHX#chb|$^#eN0g}D;- z#NUOWdW9QTrOlLe-o6xQ2^SeEEJ)I6czwsBxciy(*37lCee0&KS4&O33su4*_ar3I zO~tLhettX}0rWPFZT-;QsBr%w`UQhty0;C@{1#L7ZC~uW$lLCTi9hut_FZsSHDH9d z>YCie#aCGsb&u+!H^^D?4b;Y`>Iz@NR3e5&HlCG+cbO#$Xjty0s*Z%|TY_!M+X4@l zMGY^DE{RiC9P10v=_z8GQ0DJT^4lf%JM^IU7zh0Z(?cBh{`2k)2Cm_+9RaHZ^MDu6_Y1z20Lcm9W#d8b|8! z5f;d!+F$U=7^8ZH#2%rY6R*!>9m9!|ke69K@$)1&&N)1}4bOFd>L}P{Z|MDkrIVQh zvDu$_*2CJP>vVgsCz4$%U_-iggnmWqOC;FurfNkU>%uTk$;yle8n9dp+1jw1gcE|@ zemd7{c5JvXp+Ek@g&fc+ZqEa}vG=8#+*zabN4#gXA8&b8pD$PY6|#}s-il?>KzGW+bgE0|gC=KN`tgSpR{G?5V&9 zNv*fPX0AoyvPK>N@#)o`k(#|!q=Q)F>b1+pFB=46*M%Xg3J33UJU7iAJ(IeDOHes= zDlE;BbP8XZm8mv%4%Kg5D6z2qECk}>%6t5uEj}k2C2IQD)iJA=a~%}OcI4{RE-j;U z8XlG$>ITbgdJc2(_|P+7fBm;Uw9!Y!M})De8Wdfgf&@aO?h;Db%`D_ z^wL|udF4i8=L<};Oj>()Ge_b~F0{W{Oev5jMa8)Nps?E*=v0ze=vHE|S`(0%wB?Xi zllT<b>xSHrb|R9tH$;+rqvBfcheB<)Ez zn$&i6m-T&!zMy--9{Y2b$*lWX)bY!@Fa5mR=D3H7A+72oT5l=&& zJ4apg5YJ8wBh{5QCmb3|mJqw}2P5FUCw@r=@#Q%waYlY!NPo1mN)y%DiJ+dmbA5-qL^aLq% z2Y%b!PRe^O5(5`d%l5lucpCXx%+lAn*;NOsq6YbhUDJzZlo4^0V^)6b=5i%Z!YXSg zLqUB=$RXv%+iP}RADG1w{k)B?N?T9Aops`CTVmoelR4J^!q=}`2I&ZFlH*9xXmf-Y zz%uo2%n8Q5Op0FY_xP4_x3n*_%tNsAMw^zByJ~`LGsGZ7b17zh)-gB%*vM#LqVJS3 zqm-|<(x{Dh<~XHvsTMSSRC9=Z`N~Gp~dbo|ov>XP*k*30dkw^aDKsO7vVYUQ?RK2L1DzQ~Y!UPM(CIuzm+ z7`g~k*90R+Hkj%cWuhExz6|%(3uW$Zn>G0m}nN@%Uf5vUkPtOZg|Q9axrw8vYMy zhNR~)dlBU>bILt@mtqU0UKV8E^*~$#v`18pxmAN#q?m6MEFl@Z!&|q#`3I{#a~S$I z;)!k#bbpFFMC3xn8{&o~7oe%7HU%o0IqghO-P<*-S5Z12rs1Wr>pWSVL&O}+TwGE6 zWktTzj*6X|9O*5oT%q?;5G9_V5Z$Y>8Mj#W%_`c(yYx-2h{~QHlMwqdB@=fQuvnHq zbRH-sTsIiy+K};1Aa=wrpwO>J@Q$45D(|;IJ%2bd^@gPTmDr&pR^OCpmc4S{bDz6; zF77-4J9MlZ6WqIQ&5hks_zEgY|)o$&lqUNAanf0ImA z0Y~MU(%5+ynKh8hVlh%&Y}GZ3@;QYTXf8H4*XBsJ33e4Wb0d!+-U82e5?|`V^CiU? z^+JGLgfya0m+o&mXKIHV6aVt!&E29#P(c zYb5(kQ{iSKO^pYVJ;Mjg2kn*ob8Oj=-<)wLY>cr?^Utq)1blhkkuh0q@*{2|B2W%8 z)js>qp&&SrJ7nwvFqHpRo*p^yABSLBqUEJnX4s}Fv^Um-krtDjp_+bimyf1+_tGax zh7|Wi*(5nPD9;z~Q3D@){8a~8!rZ+Vn&l?9^4wTfcAr#%cm>{ch*lOq^tj<(jq7Fj zQxN9R80wX@?zOjAu>L6SjX6J1wueLGyUL6AeZzM|5?Y^6ag??f#Vh2uD>HZe3{x_7 zX6J>^s{b}J7n>^Oeao&Icj0A6`I?(_@9W)XK6KJByKH7h1!g z^BWhu=*4t(f-@Oigqt+E!!D|p;S-$Lc0TW#8z1Hzud!38%hH40Dh7xwv!Q(~fMjAW zUnhLSquWJ|&3g6drwU)|phQP;9B!^^YZJ|M_r6=T$sLs!E&hdi*ZKc^KMZ+t()p)Y z@-EdwU&+me<=z~nYmkxOPJQ}1C33^^>6C;O^VN8iyQYh7f*#uosiH)sJr4FRa^R3Y z`TCWf>@_BnT$MmcmJeZeM=|LJ)zWrbT2%#&+BQ{oG-YsSdq{q!B#Wew@J2;VVUD}p z!5UX!#3kj-ssA1``z-nJi7Lng_;ib#-@S`XcqcmKWyr$OsAVT8G9jkhIyB9<)D=%m zV0Hm2u$7j&S;Xn2Y*qY58ws;7MH)rz6O~_H`Q@GJJ*MG6E zbQi@Prl-BduTMBk^Lv(SvV7P(M`FltQEKF|Wm5bl*cjCQMqP!cu9{Coh<0h{<3(S`M1EC*s9}bu*O)-8QWSF~>FA;Q}d3K_E>)jjZvbnFEU^wH*R7VXE)XLbX z%8cEc%ms2&1?~zjbiJ)3jB^gY4yM@VDhWrZDv&MlrZ|1osl)vZ2Ct(c6|4U!HO$w3$Wlnmmy zYMVy8`KD_~kaAp)pO)Oc(!AAgdoiykc+sW(&v2MbqLKjFbUO# zEeX$QwAy@=FK0Qr)-JKFtM!td@u_$09jX5?CdUPp;*@K@Z#7Pm~V8|al zncgs88C$yXwUur&#;J?Gp(f3-Q`2x$yM8Ix; zktNtkpj`l2O~`R#uwV9;6awKdJ8q?%pOwykIKsRg5ZwXfT^LM%Iage*EoVZx?Hnyw ztU26fT3&$ItX=}SUn0wwGfMTN*xlo81YJ4gx34Y=g}*rFwM&*&GdIcGva6CvPR23S z6%%kHw>$Ob{e*2-ww)jJ*|V>wT+U-#nrL?0ZvUWe4Snc77QxQoDQDf1FKyo#0#3{C z5*_-)&>Nimp|lFF_gAhi8)}0#m6uT~duwFmRJA_PfDPN1r0ZPR4?*mA-%Sg_?bkN&K<|>cj_RJUp$>1(GuK6>l7)E;Z}ep+&oQ zr0A^{n3i-Ud`NbFH%Yk6h_l@!FcmAouA9id;mH~nYRk@nJXarQIId%0GF&b^H7ruO z(xbyP&E^`PyS*SdGJdjI7UkNxE)7e!?HS{r*rOJ3yl764ms1H`<2JsL<@A)fhuy5Z z>0#PHWObiG&!(}wb&3X445^d5Hvr@qIJD7LwcIE=RF;03G2{8;8r>3^Yl!t*cm|ta ze~;45$|@EZkc<%lG_Ey|b(6^%{Jg|#2s6dr(1y>=N|hUA6H$Ke{zQ#|-R^p5^|wcf zHYHrEt_OTYBgGDE%}^VyzMCed&lhbC%VWLWf&wcQ=XDTDHNd5UeSsni%u~0X4sBEG z@FhD_riIisXo?S-0-(o^x;NH4&t3kel-cd_>)$H|WcBK!J2(%)tkA09jsi<-VE~6` z22Qq`|&?RybhA)}tgG~>rNbNlKBK1u9?nY9zp4F+zRC}|7(c2P~) zDzA=ErP{?OecOfXV>-Iw%8U6CkKTG_4=p6wSjXy=Myh#SXRB`d%=H)-eIVO4+vg>S z;3NxHWiXbu45^s6YHVM}L3J(kU8}VFM44~BEx)dQNm<-nrA?EW<$Egh($BC}<+?k@ zrt-23;bI4dc^uI z{hX8Xw0l0OSNuMuyLOwB2oQoX+-!Z>Xv_2<6fo?vd_9#Z%}qf!Kf%MqZNAn!J+`5T z_?AKRH)!q)d#hemk{i5{P=^=M77E1$+q}Q*keF%%n{j)g%Ia*4Bc#s!z1@{6BOY(- z@HNCGTk_%~6Yj>3GqO*um&7y3$+pFfgu9hz^!|gC7&(4rktx52$@@~ELm!WairZb& z7E7J^$Gz9QtE-l5ZstN4&W@3`|!Iuj5lannPN-<1S}< zHs&V_?*bbwKbZaOF)5f3u}sL4jGvE_t*Vb+EPkKIP%~TACCBuj#6j+cW$mDeU~*yX zR8LlplhPMy6?s(W{^h#~xw-YD+HXgNbV*H)T;t3-vQ~e5cYm1=#5a6PV937T)s$HD z(Dd>5=xb0x!&vl%5U&w8RowDy)1^$d_00=7P63vu55Fr!SP@u$dX2wjMqy-?Oa%9q z8e^%$#muehGK$`Bfmit0^X9P|Vn9z3=66(}f?G_3Oys+BdvjHizmJ>t+rA41F^?(W z#!ci~dOtxW8Fl8^PHO=)PBPl#N&MEch0kjfoT?@QB+m=8AI*Xb05r=-low(Wrg0al z{)wA9oD&;}xg+=8z)|>bcfMfLUgnhINb#CaA>S~^+c--J_}2J*;Wfi^?np^>Ml*nP zejT|kDK(z2Q@U0W63413ozRvhrx)U^=1@OaMKJ4(H5oCecPi{TIg;YckMua__oP4< zF>SG?da+#kBEzLQOJ7@!p&_&?ZdCb^X<$YKez%ULM4Z_@HSXC*<@UXXrzWf-9dBa; zI}w^3u2ba?bID zgp&u$3*g4lvkyx3Yl@1RJnM{$84Q0qKggiBlJXd%^{CF)Iu0}AssE9`6t_c_#1|D> z6=aIy z7@LyA?aMsbZYvi0;wf)8cWQyd+B=S9UJaMHuBr1rC8@1Aw!oz|1_$%afRt>r+;o}8 zL00}cwHNIKG6;!_df9Q;eYO6eE4VVf_`jys`!dG(10}h||J~1rMs8w$*Dh%cOZYI1 z&$Z7thKulU1#tN~+F)AA10kq6g}+uS%?1;dlz)RC=PudLsLVq9@jPne z@?DQVS@%Y)P|3OpeO1oThq!f7P3Iqx;O`m{AJ$#v5=_cHU3eC?+E-3q z%Av-I8~O~hjJy5Nq!!zs$iAq6l;3_IW)t&w#J=VhXHVN!Je%%<=#n!lH(KcW6~f)7 ztK6JUd} zVylHjOJ^iQ(hCUV+>Fqv-b?O>n=b^=xu3JFeh7x}3k)HPc)Duh1R1;Pwuz~3 zhNc=0Y@Vz?y5$?ZjQhiWQir7i$@kIevX^UR(NNyV30>i}b>9>n-Rdc#^&4 z@2MO7=1pXMWo2=o?Y`9&o_+lhn~6{n=iRwt!giOG+otA_O8Yk)w}}f(HSw1j&!T&x zB;P&gocVd=ejrrVIkrTay0mJqD}?!$J;Q~I?5Mxz-4%P-l>V!rzRlOh={>aDxjOkz z?H!v5?+%OIwf5hM753i_wB{&MRADAF7Q=m*tSh-_)h=|-(ESabkI)#~S46N>{1>|_ z{;`I^r{H^*j;@IcKZhP)1y6}vJn@|&e<0KB6W#%}sLns=r?N`L(6y&h2LJp@>Y|KG z#hYQ3 zAEPVI>q*-@S?t^Wd4Hm3XWtFSz|S`oFz{3Sx8BeB;RI;1*P9ENPKmL>-DUkPb*pFL#~Oh3Cvo5Z>4<3;)sQkASl@?d8vKc1T(LyJojcz-arOm%d2w3$&KG( zZeGp4k$Hjm%`>GWF{*c!@xRu@0PP&oRryL8VbPrMkY(<+#Glrg!70~zg>MBnuKhOT z>9Zi{EEZQ)NbeMhg=x!?+G}ROqkByyob$XpBLYw!@d-)pdY^m zrY_4^3+MQiz=S%O(1R2BLU;WJ=2G$=4V)~fp@s$?yn@sHvVI4 za(_kG1z#8c($FD;?^w~c6+K5V(z>(A!5N< z0vY)i2s`-1R({tjPA0ke_1<6ACw8v!w`*d3Cg3dQe8ArxonmqE*GF*%RdI2WjX^eZ z*SN1TYRz9sNvv@a2#yvV5Whla7Kx)QpnaO|Bf7@`$k^RM|@%h@jx)(F}Wwp1- zesjv%h@!4!GhF^%m6`guNGbCR^!&SShx~54sptoNOWyGp8=sP(oZ&uZa_ao8^#ad6 z8e*CgILr?GPwud5zqX4AaXUI!7Gc1IyueGWbw4e+9p<+1giV%1G+VJ^<}Ygj)=hTm zZ-%vu@OnC`r-~pW5{CNMAFL$m#rRJ?s_b6D@kjUtCOTHMyESXR5>uR+HM06!yZX zwwvwacW`w4(foE_-BU%$f|X#Cy>9qs$qcH#CdpODmnzAbJI()(x{!$<;y*yOjcD zpXTSi%K}urA-jt;{f%O%dL3yMzp$0HAfBrGKYe9rZ$xjZX+%nXYj?im@8c_?GAOs~ zti2&|8qF9})&f=%==$_{m3LG*^Lj3?bl{o{;b}2fM%qo>hcD6zu<{VSgh(yHEB?oF zS$T^iosA=`jVTDm?YAR;=2VyOC3=>`SMhDn-_99Y4Ko?(82w=FpibAU2y-+v zvR&Kc7f(m%tM|kQ`TPpolYxvjT3-_-yQ3$j-rf8Ceo!jG{}wmnzp>7BnGk1=3x6BV zwGwzc%KVBJFKfGdGmo2%kRR&29IVx8(7GC)el_6+fJ=Gv;9W`4ZBx&J9k@$-j8W&5 zxf=A6kMh8Sp%9JZ3EoWfOY6f9FuloTqnspQ-!0NN)U}7h_;XdhUmhs5x z{o;$FR~>04w(pkR;}XA-nNka8P~F5pMH$8gSxvneDZ7Q&kzz1y#tLmtCFS7@3lDO)+vY9j?*_CEVqV`qC zVfavc_xW+dTZy9QeS2*E&pQ_kjnv%G6U@2G&;k?JWVTvGsJITk;Wx&J6|cED^OZ|U z%&_-y42)KN60RlO3L3>F~tIxb%`>-65%e>O2Ya+o*6QtxwhBFck17|iu z-)rA}@9lq~u&;Bx_mTOrCz`iIQ%h$Psq?2Vv)HQlQc>Kcb6GKbt3uNH7$Yw2u6LJj zr&Qy%(^LZ=hFd*UedguwrrN06L7BVpB+yW3$U$1v=AWbORuTK0VgSFp#oE^(*`f+y>k8-Sa)8BP3*5zC4&AD7CKW5x=tT04rewiHM3?UhOF47u2w7VR}ylx*S3o`7kL# zn)6f3ahHr`8tuautm@hvpDz&{y{E5xkQn<_d(QE5bBOC|kTN>7yIgvi9P6HUC1V#7Z(mu$N$?L8C zjglVu)caM={MVf(PJ2!%Bn>jCR}?0NbIU1_NuQA&{l#MAtX8jFuwIyqJxNL+vKNNu7oUo%~1ld8$;zRfS@ zEjig0$zwWh*p<-PY1q)hU)tcx@x;wL1hQx~>C#Zc$jMpXZ%TL-_rwBf+!lsp)2rfZ zzz;}de%up0cT{+hN2TsK29+-$+h+G{D~%Lw936d;`zLVaDR%Vdw{`|f*m&X-(#+pocf@BMsx$ln;KhAr5@YEH8(#2R* zbl}(Ihkl=f-6+o$cu_K3OW>)0Yx1kZd<`mWWmwd^>HN4jI+OIIWW9|!t>aP^GX^8w z^=IM@6MB=8G4|6BM&LQ;*_jz{e;JSXD;o!1@4b*tgQ^+_e`sW z@#|ooM(Qg^>*#+gajMsvLSH7p?;A4-X+7cj@j~vkr=!tctI;CEOkl}^lY&42rj__j zvA5#ZW3L-I{XM@I&p8Iyy4U&y>F2!mV~wx41^I+-mGlp5v))hQu*gx+_){GcQjlrO zu&Yh+AikFR$OTXPJ}8-wUt=G7zSlhRaxV4<+P|I0Aj7+D_=6qmYI%HfRXZKW^2H69 z&3~>S=ex;Jz2%IoEyHSLCG+Lcka&qBzlc>{AnT1CPXPq8u8l~x2Nz>!<4)k@_0EB( zf^(5$TYV)3DXRi)-D2M#DJ29Qu@+T)N!6LKS1pM%dE(~mxoBnmay|NcOKV@NgkFJx zo#Lg&D^6J1b=9h>Uk{T6CdN8yH2rYtl70o3z8mTG$NhWj*DpVRoK(MDocpVwv3`8` z3d>NtbNPeq-2diA293t&jXwUU;aJ^R!`%2X>{6AcmNy`}$y0Lebq>_30N1CTHnFcp z>Q{4Kii!}Z6@Q9ye`ok7Kp(eIA5xg|H9w4BGJ}ElW)(zHyu>HWya8*^Vej?E)HuZW zALldn+ z;#%++c`q5@t^;YzTHKp@Se37Rz|7Rk6RORgLx}ILo3CGdq8>Ob)DbvP>$?(A@OQ`Y zLljWOrduBW;!){EACX)C^-R{H6=Y4=lYiEmeGb#hU-ilu6_cFd;E~9rnXHeG8!UYpvHQ@z3jD{?* z;>B+r`BcyAt$mO$3w&1d)PwX}`osJX3FPqB%uzw+cx0N{HnrkdAlaBzW8HRmZT}(@ zJsDT>^vWlX8C+C-G+buBz2Y?4{wZ7^XpE0tQ*kse!y&_HOC)+&x}%W zc%{12nav+D-_WgAR~C5kq9AY;&r&k#`LxH=&$~S2?|wkVGETHErw^D>z?Vn!h-sbW z*5=X-qBd$SVV~oFt@f}W=89H&H)8fj+e3qViUgt?MQ#~qpWI9MG{JMv`RZDXW`q7x zd-J6j)^nxh)14YwgE^y*tki=~Jl($?6{Unco*(nrQ!(tm96z9as+v~88VL8936^Yy z6g->EW&Hi3o#p0*@~vj;YhHS)ztuC(w$@39T?JaE;=!eH)&UWH65AQwQRPo?Y;!t~ zJZFV(7amRdCNfF$!;90ue|y=XU~$khS(IXM$ENw5Od2RQ(L{b?g+T|(J0O?&%(k_+Y(!*IsiOJ^R+g=<&$jCsD`QTpHgiCe>iyOq6UEuG%@x zYcz0$`=GTi;h$X5t=9~AKU_=s6y9WCF89leL2I7PKV&J4GcYJIc`V-Uz zeJhoD=RdC>6Y4S_cYJn~@4kjUHt&rAdJ^mhVWarWWXEo!`TF|yvJ(uk{Oh9ta>(sNA+!6VPiN6)d!=Q6*(l{%0 z{>$sS(ch7Qnp;8O%F$rQ%_oeGn|YVHW92SvZw}}!;&p}e)ew^+`vE}ALPhWc1UI@}wNB)n`lY5c~Vqrp1%xt)2c?x)^oPr#R8f65KtR?#xirs01q z?6tVq8x#?q6a@qa!{jZNzW$#oyIY{hv zL6++c-%5>j3fuSzr7Kc>6@Ab9*na*x`~x=F*;vME^Y1&Pgw?I2ws+P)+4+s{&jy`# zx86^7wri{1MXxK-J!ZHB%x7{-fE`R%NKtsUGp(P=_b+(&li2<6N17v&uB?LB&f7d@ zsbuPVYG-i!KCA8{aDC zA90&^Sm!P^|3jb!G~tty`RUOr02UQjzH8VwOS3I!Hfsj+8J^Vx0(d{JdF5$MrYt7L47gd?J$z}W znf=XSgSke949@KKZ-m8|Vv%wBpjdMzh>#gmXYRr^t9SEZPA!yn$kuCI;3b!y-qQ?x zFC0XX5IC;sOj`^$HjBGXP^ug2#dRs|6ZorjsQFr)6Ki-+^{rR{IJB#FOhw!RL12J) z)7vYIsvKtYUlA0)v)U>?q%FYD%UW!{6R$LOmI(el=i&MxbaSV;3zPZ@!h1{jPF4w{ z4iAQbtqUI~0^U>8%lZbq^T{X_V5BUD5E`t5W#!<<)s?JWPJTJI5V>P%!fLz)9D>P# z$BD?c>+mSp0!Zn_5bQ*YUh6uPW&_#+y$DSKl*YJ>W*QHW7mC0l!N7{qeT`t%8z6y) z^t1>R9`H`rj_0fH>s#@*;Oson&mDuF5{`dRu66SM~Ynn$?p z9E+7!0CiT>-c7URZa{apwAGr;(}Z1o8W1&Sma4fn;Aka1RT?Sy1E4Gzj0FcTss4x! z6Y2>EmG#gq3*x^eSYK#~eX&>#t2r$Jutwg}0Ju2u99v6J&_h6~>ey-i*_Ks+h(z6H z&i%S>xJumA3bs?Bb2&D%4}^TJ09r2Ppd!2{43i!~V&aXi4w5@RM*Exauv+zUmToe}dCL6(u1`QrKE|qOp04#=u3Hr#2G zI+-O!n*nV#NnK*46r-oCcx5=!#c!j>{JTm}EYl-r07)_V#Z%k6f#KbcvKrn!3ClR3 zMoHb#O}(g^h%6Cg(N*VAqq1^8mlyAhoczZMB<3w!bcnO^%cE($%gu-c(=R;u;~P~< z6b78`M=^sztqS!}mCm*1XJdSX(kEfWB74_rO8YFCBsvj07_lP&EAa^-oC(sy;PFk^ zU`P$}uUbr)@{+BT#bUwjR?9S~!MW#EFn199)!<|f6|4lcK@CjqO|n%}hy#m~#h#Xe zPd2-ZtHT*E-qrdTqZW4684l^QA`@NxVN)#=;7T1Mb%vmQW`0=2%#0DpFqcb61)qbJ zZtC?Nmx?&sqlCvz1T&18u+l;S7cDWE3+K+SumErfA%TOOha0EcIL=IPFi`I-Zr0*) zyWKk)(ZQ-upe~ONP6W)lcPGgWkTnbJM!)cPifCS|g%Xye7>C6bS>S!CeThRMdO-eJ zkl1!v4{nQKr5M?M-}|lF<$kz7$Feu~1eBtbO~b)s*I^lEHsiQ0jvBgtBH0M_s+W}y z;dCFH*|->6T=vXtw3Kkgh8oXPaqDpC>^XVk(X3}sB&7r;TnOeV>3glTEeY>lI*9x+!dQMZ=iuoZ~ z!eLQQbog|HT;$v=a|t_6PJm+JVePCBIJqg?09Lad&GN#p0`qPKg}`y=%qs!+ni;go z#TjJK2+V;|%JZ0K5D77jJwf&Eo4Yb!!%ZjH3r?w7dO~co0_Os`1pwUDqhdMSyS0@n z5{y}nc>|X25UwRub=ZIhy;w0(T36HN#E~~RH6SA4BHZxI)qoGQsqRN75I7zyVMzQ3 zymFm=W_F*^bo3jtwwltqE80S!<@qBl5_kuhNW@Rfo4~{ZT_VtTBNDGSzSsi-Q~$71 z*F??k8CgbV2_qyB8~{W;6HX0~55#JHaUcMNSnw2SQcH-L1Fu>)Sd690bGviAyP<;r zyP%v^^@vvSFpCFp`)Xx#RdHmWGGREuLBJv~61k^l(dbh-74Q_wXv%{re8iO*L|3pB z4ZNG(p{XZsDYYq0z2(aSUbtTk*P$Xd&}7?^q6MeP)8_)*pOpsB?G5T#_ zaChV^G+68F-HqBKo)QmZ~}<7`T4*I zV392AEWRx}#Mdl2*g;uT@4hW32L%roGLvtib60FOt|t$FC=enN9##epGn@cOfP^shid(-3VQk7$6T3g#?F8F?&*s^m+$l6&}Zj=SN0Eg|G)mF zH`e*Ht#ckFXk2H@Gh@t3-RAu>HPmwD;);`@~<$$a`4nJ^c{$6w~7SJ<3@({ zVsYPZC83HUBAH*neM?lq1q7X%67YLiAUsVSdPQS4Yng7KU@UzAf@x#ZhLmj+ScQ= zDl;6G6s(qDY?k7|C8On@VY0*9h8F;5cYC1o-0xvc!rqpC3a)3{94P;~PGuBUd~{3f zw1Wjy=(@KEY*gSx3Bj%+z@gq%sY~)m>ampJcCVhXfPS*{viSmlcoQoj7aCXOEZR1{}BCwjekyrm`3Kv)Lc#zCz*E#j<`s z*@mNnx>?UT+K+=P57V0Lx5G7V3(pHYhNa39@!WNH_mZcW3&Kho82(a^7G`zyqiBER z*2`vvkojKbP7F^R_c0R)hiCdqT4z}dORcsgtwk|KSa6>k>a5m?IjFY9*Sd+zsJ2<} zv)=1WO0;JWEDyqn)i3AHk{L>G$h#WvXyzdFdn@vY4Vq}J4eo_yJs1a!yO}CDR20A6 zRLfk-q{vMf+au?$G+L-u<*&OHUS*1N;_)Knx!f_qPnn?5&#S~GTq<9xrL^PInBm(B zKzl88%P<=pB~MW(^|l5#GnxlALn-}uUO-INl=pkibM0O{TC--U?)&VPpmgrAH|A)LajxgPQxL)%p%k ztg5?@*Esna$l-`>FIE&4yDjnPH}3JiLie196>?^?J-aIqmy(y;Bt7#aV!M^vi|!v^ z+>WwBdxmi;`gG4eRQk#=%3=2j7;E2CN{g)zT87dpPiXP7ZFH~;>=m-T6;(yC_LvTf zl6FO-x!-slm@t88U-ZyNNtl}g0r<-9w!}Hlw0Gv(0Kq--Rh`h1X0e{>m&1J;=5pDB z$}yLHE)KW9>$)lYzi+s-qO(biR#NApnY5(t71qRW)iNO#FWs|xToWH)lyQ2od5YL@ zKA4LMtI6FoR+|DlS%D3P#8g!LWxvY8q6BO}_j?0L$R zGnk^5ZXa;VOURaawb-oUYWqA0Kb8D5hq?W~f^z8Xs1 zD{EjuU{7lv#Gx1zuUHE{AWfxG#duhzIVTKD2cxto-(tKtoAQ|#e>oQJH=nHH6m?cg zxLcANYZ0ETfDBwm@zmL425aB@&Eoa!ji6KeSP02$o1O?#Jpm7qN*Tci$!Q{HB;G zNxKjcE*`wv>8dM93B19=t{W_7M&fD@u%L3aHQ zyzp&q)`M8uO_=0qma0Zdow9>7$+cjvLzxBhkJOqi7DsYnY-+G3U2XybwM{%zTQ++c zDFLi#CZ;+!@NZyQ_1L#}yKsPI6@nt1ZfZ!mx#y5GD{EX#r~%h0AhRZ+f}NI*0B$_O zrBFK#nIJI*I6R2lO>-w$al^sRI|>Gb)*b0P2h~sVfxoZTDn}E})VwSySckvwRq>(( zl)!Znv5$1D*a3j-7~`A=EOx-LsGjNb*hhs%YrusCu@w^(+yzwI%Z?!8_CUDVt@pBq={YQMY*X-QSli#6fqG;$qL0&) zSe(;-IF-8xx$2&pC%Bog3RE?Nv`RB9>9rk$4LdSmQK`G*wx>a>(vsoOxXbM}xq&+^ z=B1_s53GQM(tH-jmf5kzMiIO!HU_X{;38%OlXhp@B~~|%*M5a~HsYum5*}4u3h!ns ztP4xfjPVYW=uG_1bh6s{a z#e0^pCwq^yDSrTCiBpg3P+k`USnWYqq5-_lasQ7W6qlgAj)FnGB=RHV%|wC zt8wG%rO!@YcvD53SxNR+IcMg(9vg6SE5Y7u%QxZ1x-TGZ6L5!7%u>)*mH#^&=B(^f z=J(1TpW>yCe&bIb3xv(W9*RgT02r(9lhRS}i_z4TVOip-;^c}a_%< zJK4z5&DCqB5ygU$+nfj6zouYYv)#!xWye`bSo#?)#Ds$?h9je^AlP;ZTq-X){cV?qMncPNsk?BS!?{Z1%>%`$l^b*?3I> z6*cXWl4U(a6X-76Ah_)DRD6@2gU*J>#!fwW#BPciD={8+u+VDuWSX{H?K|EJwl;gg zg*BR@RdBG;6Ym|{T@IUh3M_yF zi|%T1swTsmx>D$;r3ZR$j1$;m*~nt&z&qO%@PeqTdw(*kjJwf@$Rk~DN@u9@QE*Rq zPnfD*UIP)m+30ST{c?W^6F8V7tB?*T8f)KG4<76cZuQKdS9zS(&SCDIY{zngZA_2> zp@1i@eP%h|tKB<`daR}hGGA6v5HG-q7EO3aeW@YdgU7!t&Dqz0S`HAhxAqel3>Dgl877BAV$kq2e-baAC%IX@{xkcE4Z@ z!5a4HBSem2A(f4Xfaz16TAHGiZ7~ZsI^Avwo6@(eGJ1+6sE(4dU={(16cR1UNRWo*okjm~^b61Wj zpLXjLVWM3Tx&X<-Ks*MjAP2r7fbZ3NKCx3b7P<{Kc3TTXlNLOa9dSuktW-cQ{sPjJ z?$8SSU{>}&CXK~ro&0TMzMyncw!{mn-xM!qHHKsZHgiZ1NT;AZ1{huONRM2f3Z2C~A*ei7zbm#=_1rC) zEEHzX$jt_jE^1p=oyw%h6X0Q%nE@P@O_f}?uC^&k+LfKZ%;ez7LQ()QPr<8V3m7$U z+(Ph7+cVriaK(`ja9Kitvvw&NZu7;eu6bMRi;c7K21;4Kxs9j7CH?&|L+!OdfvV-XtGvM4|v$cX%hme)S zoagNzk>svmRiEh?fb@jNz>H&lQ63uvRh|VECLV< zCnXl{tuATBE#OChwamZcrMf>pF99Un5VFd%ugzABz%8ECg-DcFD3U<5=wp+o-Gl{` zQMqw@{;;foWl=p9tVPA)6Z?dI?z4>#q$1d2FP+P+rim&?EOod7=FNoIR{X>iO$QeZ zF;O_fIF$ErYv?k~i08)3xrYKL96*w6X$78>@6DSsQUH`2cNSx|c<;jDMK@zQ-jusJ z1avH>%J2nv*Q)3eH*qNUYBr0)cVeBSIHS2((FTLc!&RGm4;u!ay{@=R$9k&eRmYESd4x)-L>kgiJ=$LPdJc^kpJM@_y{AE< zWr6k_w|bf2p>U~tRNyst-xY#8xgVR@s1oyQ~RdF5v z)n=CWu!Urq+Gd+Fto^bJF=)p^Q2S!qzk69{x``FP_NS_W$lxAVgMYz3rJ8Q!ZFcxu zkM7oNm^(l=S>__pzAPt}gn?L;3o5gpw6$%sHvrS1^iRJr3?K+&a@iWnz5HFpvn63< zV^@P&B1!O8#)ivn);xo}-2%8(47JhNV_SfM>r3`O&ewV1(xhP<5O+4vIacV28{&s5 z?;CKW+B=a|v0m!^b-To@n`PJbg(bKu3~N5rAY4RKJnl=#Jlk2@*Gi2yBa+=dZ(~?j z@7_$s4DBix0Anj|eOU#aJdn6lxHhZB!4_CL$bIHU7&vPz2 z1=)4uA`+R8vfI0RFLpM&%Va)3E=Q$bMmjg%zZoi`f{HxmA5`B4C(>w5m{BFt{mvJrrhkofN?1 zu`Y61U|8o1aZ3^Rf2-GgOrLIK#@DUWciIGjaIi7+DM z_P}b*vYW6cJEeg07V|2Tt+XM7cQ0f?>p z+Gamnz(rV1j@-1SY%AzyL)JJu?cUM0I}7;nwAKUkUhGRZ%iy_lC5<%5Vd;%*cZ(ig z?yCOT+i|TX;#(={#sxHGwUdS|!XZ(|Z0M@2m9e;)$3@_cO|?Ax-DM9qOUrh`Ud&Ny zQj>npQxw~kY8xj+3>dv$;7?P=%-9ZiH*?jtuGn)^DK0I?X|n~QMYhim_)#6N*+AzW z`GzYD%sNzpL|2rR*|T6hw_<-x^Nz6=ydIw1R=4)eg^k$;*iO?xnlXDr;%r%5MQG!G zp$hm>1m8zh?cWaC3e=C#>@KoO`7!C@fssmicKyZK`Z4RpyFGdoVuJ*)$KnPbal9}m zS-k*rq^)|jvseyo67_aH9_6^BU}2iQ$}gy;yDW=G%^rlSWk@K+V({^}t1D1pVL@Ox#IA@(+vgXDFv|uj@ z)qF#e@47F1#0{)+;uU*SkL-5)h}$M4d7yJCLhdPr`OCK@WO$^tFK$m(mb$TBc~(5N zz6XL(Ac5Uod&nEXJE)OwA1}VTc+6yv*1i*5i*@@`Ki|fn#((?cr5fFXtbk41R}X#o z1~`jwEKtO*^q)9rfol=1@D!E|Trwd8MOB^x=+G=sF@GL>6bbS;BG18abp)2V-LOpb zdtkN-tY^zvSVLMw+}G{wYP4Tg7n)lDBffE@j5Z4cS5LrA3y8?>rxwzh*q7Z4Fc?Ss z%cp$?@p!kMXJX-La|PRd5-)fgkbBv{V=R8v$3P&>PRO<@t}Yc=4*YCb=~${Qm6Kw3 zFq$faWSM~t7TA99=ovSax4eR3@F&1R(_cl%a}~$g!_GVfyWCo@t041g-RKh4b>CN; zjW>9)omppt(AzRGHOM2Q4lP{|%L)0T?YCKtY7$=8JSI}M1q-#*LiK0Q@Qvn8YC6>J zb4MfERL2gIrml!%|*!y{bvbZ^#!Y1X@ite|f9bKLaW zs+CW*Etb(GwUY@Ab{)gq@d8f%ZhGpl-Zm;Qda7`k)f&O6<%Rx-|E#-{nP=_>I4286~b8*{C;`ETdZuUuR zlC1kQUm8qF^^rzFV#3VhW9+f zu-md-YI5@gja%$yfe;7ArHmyMp+nP4?}B`r>ah^A zZM%X$&sT*=ifCH#sLBK#HGaJL#?&31l z!;!_ z{a~V_vNyUtYX$7;Y0iv#xbnd060sv4c6K_Kpt5V}=WhS-&3lY6JJ*^OKYYl;#@ll# z#YC?u@_ZCqCgAoxv9pJo4G%HW zHWO%o1ca6bno^JF+Git>Yql~G#9~Kj_WDwq#R6}rm%BH1RLjm?06dFh2=09f zRvg%uu391b6-vG31w5g*C-PzXxDEp5b|h2Qs$IT-UG@ZOCcqHy@euHt-lhnSeIB>P z&M&LSQKwaosdd(}?To@aod@FF1Zx0qA>v=F;;=n>v~btk!LJK4zhHw9jmw z-9ojgnGnB&d~FHR zR)#%)37+7JYV9`h>D&ldtO;!MzEZ<$gR|^q!(wo+-oyH9CcONjKiYjW zU0@V-sMI??FB}iy*lumb=9tL_5Zf7-sVHDoktaHbXPp>{)vdNP2YVPI>tV||KqweD zDbL07-z}iXtu6{TgEt7gJ){94BoOd{V(GC(|GRzoz_>Q7<}NNXt=2AgyEnB1mX7x) zKCtlIJZu)r%mSm^K5K5=F?yF}0-h{P(eu*;%V3isEM{LIY1gXQ_g<8~9%EJ+<9k!K zHLXgm+dM8;85feQ$n;H&+)dosRNUgqt9PL=KHM!b`)*^Im6UA?x?z;SP>j4lBCT^% zuEB^y@C^*2TsU=n+F)jfoud_LwYhy#@W|vaXsYqetU?cS@WCCYa#rh!7oe6IeW!aP z#V+8`Oq+JVZBj91iObxI+~bp)IuhK`Fv(+X6D1qr0w3UTn*^ZDj^ubgBa^G_Ru7(V z3;~6jZz}g-Wri*#t1P@+SiLqQ0BO%S&u*OpaT8GTvJkTD(2l)uYEf73)(drc_Abz%?h zL+o~{#`sJyu$K)+dY=RAvQ$Mn3y8=~0kDhNur{6EJ6wIhNt&{%2OMHc*f*0|oAMQ# z3@Wboavg*;geBRS_nH}sIjkcs&AbEk5{LWknyyWt zSOT`&?kn?E?CHK?CGl{%BzG@vt{v|JA%XPWH_Iz4F4vi34~^Ga7!?V6S?{KLz!d8| zR@*K$r714+72^%@S*p3JMSZRF3`YjkUK{k;%z@Ru5@$E785XFN5hxPI0+?ku3f^u% zO#Qb*-Fg5lcjY0#@FUpb5st#QYcP$mOunxZ|BZ0cotRJ(558+XXCd$!i4 z*moSZp#-5d&${YmwO;7nJmAkzVUYK^->Ks<&9RN9P4BBBv;hVWeX}7GD;FZFDpA0p zRCv5e%nEey!1G+g%BW&@;?YcXf&eBf;kQ~7PZGL96p%ex6Jr3s>%phBV=ZI1}Qhh>zK?Z=}kX0`l*v9vR~`hDD*5>jT1nrLQ0KjTl>xNf%;h)RbC zs{O_S&0Otg9E&@^_u(#MF|RK&XC^*oeEI=xyA)aRXsa*<2V|-0m>5gQ_N{32qG`w8 zF1NSKJmNcx%1yQ~~0+6}$U%L#SeIgZLA^msHUb{3u{*qg5A761x=f_uE?E3Hu+WO@F z_=-#Z-#)(LtN-@nE57sp@$nT;{&yc=afP3Le8mI)?Bgr0_a8sJs$B2yKfdCDe*WOUva%(epEx?o!?<{uU>!t^3|VhnDXBsprBa8zmcB8 zzt~*mFGOBp<8;|3*64Ho_=yeA-#Lw$Cdi$c3k#{ z9|e2^@EcqD_x+Z?u>F>MT@^w!bJ4^;^Ylhl*8&O!CbqDBJAlRZ!Um`p)1oTCcx!4O z_GnHBlYtq%T$aSMQek6vjJMhE@b0yGifxI>wlB;}hKH{$oV!I9cMP>{8l%y6?uaaN}k3Z{^#`kJd9>uVS=;CtB7)?9#NY8_rGDrOuzDAHVEB7vEu>I`ty9{qit1rei(4*Mx2i%C6jJqce^cg;Pu)=GVTM?~A({75 zd33p&eQAg37R@r5oZzU{V$wKU{3OZd)w=aL3b?j2aN%kqVj8<=!N+!6KrlzTttRRW zt4(V=xzk{}D{CUg!wpAMHV$k$?rpS1Rr3vg4o}1s`BjQni)nV=#0t~R)9)&}&-!ii zZ0dEN&|H%Amfx&X~b+T!YTMlW#e7W3IsM> zTE6Sq@4SY@1D=5;w5x1g4}~<~;R=2*a5G&5A{*4Sf&U-Nr9z-8jQ5soT5zw&W%f>C zG2sr93F>EMmDp3#g1*ac7gpcwZY+l7a-Fkyw`STDciJ07Ih?a?wv7_v0mU{Q5pSf5 zVNdDEGljRucopITW6a=5*ki=kGo$9{;pSF%si502+Mi*MO67#BTsQ*jk5w0T_;ReucIZRZVyIbefOFGmcP!!N^ zq?fAU>GZv6IpQ`N!Gurq?02^|kk#gi?dn2xci9}BRZ(1<)!1!-5LfiNe0Who(6K7} zI$K~|2hUTl)7Bf-@hFd@E`PLBhiPKh-YS+?EPLFn&7P8_gu7W+(a&(!I*Z9#!|v=5 zW8aTv7nG__&S8aNSnYJPk9)gj=29E0ExTDt%F2X1yZWf56QU`qb_>Ra58U&SbclzUa06T0S0-a&iEJhtDzVAy zd&&_J&{wN|tqW*b;S?S@nOuain$vQb|Ix7c%UQS-w;c!3!vdkr?yz`Mcdl%=K9~1- z$E=UwcBj5YR|9i3QFq~HJ3+x(-NcP4z8iWr%f@!FHlsafiaR(*eUofwVrvEL)Z-@i zGF4@j^SFk+r$%5MR6=-VIJ5Gg-KOK$6o|KJnpIh|DGX8kB(^Q5EkR9pfjF~c2=BVN? zc!`(W!)|kDcl{N^Ul;w~y-inF%uG{6`xvutwDreUPle75vlfDB=fh^qctsa1NTDuM z=!NlDCoAoqDu91f$iN5>^WK6M(+SfjO9+eHRENeznMrY6`(gu(dGK`StUSMRN6BLU zuJF4VMOVd9Y&$E(3Xi1Z$97w{P4LhAa`SB~0SEtqZ(>fi%`N7=*}qvd8nQ{qMd^&) z8azgyX)m{ZH9$-Yz)o1efa8Uy?uv5fd}L?&jSci?dGwe<2I_PVRv9q^H+VA^^&b|5~YrInm4loo{7#llnWGr&sl+dxRr9M zYHzX3dH_DJVtw0n-*zjo6AKWx0PEVWT=i0mGjE@fY*7Lxfpz2~WnwK>Zj-*_!W%2V zZ&6CTJ-2HDZk)!^6vkfdqz7YQf|#+(G-sw`x5$_k4Z=|#EeHFKTaT*#G-w#i)9pup z+BU;N-!%hnl^P1JD*tb_^24rmO;4VtU`sSdw9FhB)-waP1js~XeXaMt4>hA^#SyTA zd%(IFp!G7@GzImEJRVepDfELCeI4DFPPdBVx0=2Tn(%%AbCmh93mkyaLV7iV0W#To zB0Ds#!IKgKb_Hl!J$KukEN5dY&damJkyl%_SZvN1bSr5=H$p4h4@SO1k8{~h9%RYy zPW;cfr#=u)5Yyw;dEPugci1X8vk4veb7bPOX|;r!eOYaom~1%R536*2jvLqK&99{& z*&ai&sW4&jnBCNqjYG`SJTR(-mF}qpgy=}Yu-$+iD$)#3IBgeWxe-p5)n6|>HbL-Q zO(TSs@%$_%fi`O_#Yfc`#~zDOC*vX-M{rW_BP(fEl&6bX;fRgk8SthjNL7t6Eqp+D zGIP!(D%0$tvh|v)xv(dIF4{7nfsYjgB5GTflCA{+GnZqvEqGxScH7ynpi+C3;HdD< ziM!qmJJE6mfKcrUt`zI?mX*i`07zleafH)2sso7@GbDHQcZ(pL|nMTmc908YBoRaq7|ri+q-j;AYc~=iDpq z3|nGWEq$`GFX9S$7RVrT)U#>$&7x^M*Kz?U`8r#NF&2_sm5O-tFkQ|@npX<|C`%6r z*xt#&od|3~oAqa`4u#_L(`TGxO8++ckSlGRA1FnK8tP!)bPfLJmkvRj(?}sXuvQc zFSO=f_p~h*dV3ZQHjvww=xzdh{qh{O7Y761gK?<|5G)@tlObJ~)nowBC3??l#=%XM zW(d$ZWe9Qf7`4iY_Ek1mPKnFuR7RwNE2DiV-n5xYsR|w=j8;hCFn2Y-g0RVyP!f^s|h*T%j zcH>)@#E-q2(Mxo#FpdtO8{O8PCT$P*W5D77ubl`{Ro}+c)TqahQ`#3R?_#z?rX2vx zd+t`Dn-p*SJ`C)x^N6|N^sbh(5+XQnPduZ=-5rmhVh!NQs?cPcqK=QTy{QG*$%@-y zClKxr(!N7XOs=$9eYJ!xyEB6^*oW~n2?Q^C%md-H&JII-9pvLGb|-bq>ICow;EJ84 zqO@;!*phuTF7@e8r|3`+Fsr+_%?5Y32iyF}ioh*)YmDq%6N*r`S|yK9(spOLC8HUS z&giF^+^)c6#m!i!sTFR?oZsNy*VdO^Nqz+*P9Y5tUiCl1!EKM$DOEMB0-sa~PoR4I z*i+0 zqN&WHL)tyCw@NpuGsR2&RvKTLlPnAIZYwg9%EV9lE3 z0U)kwT3RnsW*f4QXN~r{nOz-JkxS5{YI_M)aCkrL9n;HpRx^`W!LxHHh0!*f@LD}! zC&|v@wP~l&RgL`2C{|l&+{(`sz`cC}v)v)M>7<^;+3X<1{}KaqpmjGHDmFvV*as9e zpRVKu1aI=YovyIm(}o}2ydNBNRh2b6_ubYqt3|x!Hanv2g2AiU^6@(5H@nrK=Ci}9 zS@Yn$W)e`jCvS#GMle4vijc6oSA*Xb<9N_-X=$8|NNr@ojI>LIFm>Oo!I>q2*d?Z8 zT{=iY7(xZO;aI-*H=U!w$U@Rq^n2@WkB;h^z7sM!Q)E=x!l2BLg58 zSc%;~hrCM3J>YZ(T|v{cRO5q#Oa?G-&*;w_w6|xP>xd!q#M4$US|ZVu&fXp+aqE*&E1rE6niefyt|5>`>`vx9wpz zEMQRp69P*NICra8xW_|HE8+^_gaNjTV?9sHq`SR3+HMkJwz)w}rU-#FTUj93u~>11 zLu^VRG=X(g^U{ud5Z~Pr(8aBfJKC^U`Oq5eu%;Dui?g$$fS|Twewa0mIQ=V~vzwmM zM%XGS&H8tdL$`oYr4#I3Wn(*Ns>ZAyhB;jp1K*L+!l&B1o6!eYFEDVMc;6s>7WOf- z#+V*pB z(3ftNL{U`>U^(Ubay7#*nEM8p-IB>(fX(EFKW>iLKL3;Mndr1c0-Jc^lB5unBpEGb zaoXBp$aq1319TcEjAb&8Vgtech9}SWvNynot%3-ecEW}ln&WyYT)lhT;?{+HzS!>C zN=5}8F^@YIm5{AB$?lxkA$E%c1`q2QK%(x>Pz|%mEetMs>f;{f?9t4PF=J<{d4cI= zXpuk+n-yBv>~S^zvRsBMJX=HqF~@w9i$)>7D_~Bd&n5PoMd>mkiowSAnZ&y*QVQAv z?_?mAy}nMzs#gQgJq4n^&Z+L_U35V}om z1OXoQESUA|eN<0dk0lnsPLN`h&dK_FXDoJt&01-e~ zO$#Q{b}Qfn*|tCw2s>|fz^O{aHXD)R7FD1+XV+ks?j^ieLv>m0(}?%R6cy1>L;N~- z+areqo?ERSWCcPSz}L#Y+S>23<$FCbn6#*vy5AB?{Jgzy%aAPI?tXwlv73~PA>hCw znrAshYo=DNZ;JGCE0S8RG~K{@TeU~rD5?tAaS+{^u0t^jyHQyNWka!HtM-0-1ad67 zuUduazV?vknWz5B$V5ilb(+^q9D!tdR-3cQm-8mK-S)X*!9DRnr?u(!6y0pA2Itsp zj^_?&wMzb+%A`umHVgEQ5mZgZ%o8YWy$FY8*l8!odJm6tht>TS0MBN-tSwsyZnjKy z*HEITTZ0S4rOjrdK)ZRwgF~8W)GG4Kb5PL>SjznnW%`G*pDJtuwR+#V-6e0zQ`qy{ zineySanInmnGD#l*&i+(m!<%xTIHZ5d$wnf?pW$m7|lM6knKx(2*P=_&tFp3ds9Jl z+_uB6s(mT(=kW@>o!c|TcO@`!Q+I*SSmz`MfOJkZ{EGy8W|)KWshpW2&%N9sCl4>@f}wNJKv+W^x} zsMRt_T>cdVx4m!50K~Y|s8Cr1ix=RSJLESD-6Cx}4Y(@}RV@;ITZO{cLR34l*vb~~ z7?*4F5XpVVDW)dL^+^53te6oX;cf3y7&o_mfHPnpYB*I!x>#YpK*(mdPMeQy1up^! zsw^Ax9WGVq;z5)F?GUBxORo|iXkfR^H4CRNQaO85sD`oT<8p5hgl_k;CdGm-ZZmC>sVwosonOdB@mVU=59biS=d%@_1ba& zs|_etRj*23(rgl{kP%@_Z#6-_`UFs5dz>Y;jjQd}SUtgW)?vW+82ZQ~Ej%DJaxdy2 zHxmg4-WaNG!A!YroLCrE&_!bz0ep8)(5n63jpQC*$!f&7@~!&PUWj|Em^B|a_}Ol%!~F?R+=%|thww3^E@ z>;ZV$H(BkG;Btkt?V|U^TkFsn0>gHj6@&#{7qJMp2oJx_#K5ybsZ=BZb#$QEH4kKg z7FNb3 z2|(B|iEK}haZG{sX4B9^yrJsSwaq$wbzv5WLsOu6C8D0gT^9(Gf|i>S|Ac|rL6n6X zm^{y>jA~Oxn5dicYBJBNQqykhV7R)<6lx;!|4@d_ObjMA12LhAIA#4%zwy(vEw`;6 z__DZ5ZW`>8f6mpGF}AUbwmSrC%e)G|Ran<{w&2hf^OT6fAAqVXPzdUy+OH8CKLnOa zJ^GVj0Q`&4~5GDz!C(b zx@WPhL`=5WQ9y% z)CL@mafHrE0qh0?HoLaD5CZ>}d3Jn)o9DJ=>8wV4^D!&Hh~Fo$bZ+2};{M`lQ+hYR zrsfV-Qgn9qY&`HoN;Bns5er&RXI~k2ezKW?s=C=W$qvG&+lR*?+EqT%BNfv+l0}yl zh{GL$w0DyTRbe>5iiE#Y5CCc&Q$vFQfg@0gCc78s8BS0uv?-8>`B>v71~AB zgp%1FYo^CUtg@wbDzj$1Q+La8)efwk!yf=sK&-!1eiIbG16M4zYf^}ekvi2g+E10M zfb9j`nFWfGWe`1mOeQL=v8WZUt{P9am8Dv1>H%m{X(vGMZ#B41yO6UQLHdyFV0UH! z&gEWI+g5n`d07wH$6`NVKHGP7;Xw*8aaD%auFk!xpUgGZ3LW2$Zo3J)OS#qL0zOAw z;3&L%wHNns+vdoOm&^(qsv{}L+T3>n3&j0a+>glanb_F6xp)9gD{Ohv+p4ZrFip%! zXYRn%-G)8U^FrXYMR(QiE`jNj31J;cl|gRGw7ti^CmxRsgKY|{{l^ItrS4OhKjgGu zL2Z?`+37@ff)fPp2cOgw-F5QBXP8oq5~xCy_8p7Rh7U_I3DQCKj{12|>P z`Y`|mKrU5ZFg9*NnMGAJM{2Nl-dpk3i3#QwAcN+4+OGts+wI!5MTk99X%(w|3GxO& zbFWG@=Cmy)y$uyrcJrgH`&GKx-RPjp0}0t(9v;)pzTVBD#u#lGlHBElu{kT3>;UJ+ z%rYe?A$|aI+iDPv)P=Nxvn7#Y&(*}EtDBhG-E~)MSL_@#J769l65Q1<7!u&RR(CZ4 zYd9!nP%ASPth;2f@LE{DNn^u7z6yp0h|T&AyT=s7RI6^fC9!hRQ&9yMP3- zcu)EbLOf8j5>=OB74dZ5LcgG|0I|9wFX@JgsaDJ|RUW*lBZ(z3{R9&h)_N+S+b+C7 z*_*hl*f(0;O>?g7m!}AoNdz!Wf>g8(;wGRXNCueKzH+jMa=F0T zU<<{+VZ1gJ??+aAouz5XIv|BHAgtnvqb(Krrdg^UB;IFcy``DLbOhF0b&nQL3mNYO zoMQu#a2^6ym-TZ{GFaVpqf35=Mfk#!zHt<_dO^ z8167*2fP!k$M4KGVHoV9V#}y><6q}C5ODi(A5g_>!mr!1X;lX6+}xp2NowF>hYQiI zx74*<7Dnfx7oumqY!QNy=$h@2?jeme8A^YJSz;c{KR%rXGh$9Va^#q$`inAS_8*B* zDD~qq3k6`5j;q?njCGYJ>SNj0sHCS^pM>!+n{-wN(T1A4>iHC1QC^h7-_PL;J%zP0gOx; zP>B16ZJ1gfV-+UaZ&Oy1CYHMrLwFE{O>Mj;y^A~0odvS1E8CS4EwkhMr96oOnV53C z?W2QZR`e4WKw!L0Xunid6JMD1eb^WXKw1ck7~83$u32q&zz&G#X;lCq20J6JAe9b2 zP>q$x?o%nilN|N<6*MsBGq@HU`-8`jzk-;t@@E+1+>J_lz{G087NWK+2;M#(?zzrffJNeCGja zway~Vln23iZR#}%vW0wKC_uq zw9jN18^+cO(gFH=P-x?ep?mkBAFNQELTqqbO4(UpnTY6iTEfQ%B}Hzt%A=jSHq8qb zPlOg7OfX_%JM(IF%VHAeb{m1Z-8Rj{2}o-ZsW)R zrGa7yfEQuGJi%;`ixMS5xPpytiFmf){$V`aG_uajO&qhUw`JI2WlEvLe%A-hjy9I2j@p8Am+^mS(g=fQMX3R{;@ve49x@dRs29Nr{7fW~QQF5Yn1 z8Rx>^n#;Gg%NDA(3johp8OHPXYAM;_E-TIrxE0;4WDtiv%{(i_lT}=~tpT`K`@Go{ zDbJ>j?zrqqaf|Fhz<6ii>kSCCM}SXoZXKjG26&pWOi)eM8V0x^)~QXQOO=$@@$-SBmzS z%9KM16zPPe1BTS9bSYki_@CA1)MyzXH`Bnt&^`#TS#J%3s@}V@uUK}oJM>|ClHKTT zxHb;QynD$U{`~}OscE^`P3^uqR5_}0U!px@>`O!dZ@35bKHMz?FM$L=XW3)30`N&J zarlBKu*@+`7!)369|+)m11A*IR3lL;mzg|sZs%-;t?DGhdJkCsYMTQPSL1Y-cn|tAiSmBop$$^>j^ZE_N}sg8xI}r({S%l8v+%p0f0Xb%&viJ zSU_kewjE}&$m~nzc8vL7_II|;E8Ys2g%ymbmSwY;qD31XM2`pdv_X$uY|y4^tFb61 zG(qCtYrl=>mh+j3b$G1{Gr^)g!vtRSWoGuKTTBX1H*HzI+$>T9_PGPXFoT9SyB^q- zG$>_sj|oE*)V3q9B#prZ=T?-|F5rV1aK8fUMQS_O;_oZ9{m?97tA8Ch%i6 z{F0{`n$v2v1zv#wLwxiM1tPk1Q|+)2ykb9K6j-?3B5fQCa5nQgsEy05UG@|YW53KU z9m%G>gn%Vm-eofw;(*p(?NFzKBs^8cT5$=CZpg}7+avn2-_<@3+qYSQ;c1KgECCI< zmSK!?`ftnPf|@_UEi+s56;q>e#*^2<7HqQ%$eP`MclFI;AMPf{)nmie&T$>YdIXkZ z#$Sb1nP8T%`(jba?i=sFKw3>Q;tPQ)_KCE&kCqkh?qNYQrEAO#U>MFsuaFxn&_Z== zmtszUNfpEs$3|VGRdesAot(A{8+fs3=0{b$C@8w^{^Jm)uRt6NsDtg7!EW3oc9Gi=M~&OEStd=4xo#qPGeyy)IS`bR)`2zCcZ^}hW7%v7 zBe9vqAaZ8Yws?fuL93WYX5;16)()1K%vvBD&480^Xz$LC+iT;(iAVr|!O@Vs^eM-e z-MLZZ7ay3mxywuB#aOwmszg4#EtyMZnpNRbzKWv9wyWKXqJNrnSwdtu$a8j}^ zt@cnU_Rldhu&&8ET%4^<8Qgib8IlPz(JV7tJ>k1ku^6aZE{$tR9v=@7n(=PVH-Ol7+p**{+e)+ew^;>1 zTyZn{JPM=;+Leot82=xFS>wIgr`HlT2HFONcC#GPe5Y`doE;9OZL$iFpSi;N+YO1b@44Fko`r%GR2FotZ*Hq69d=N!F2b4YPmJN4`gGc@5d1#O z%(-ttuZI%W^A*#gf}K(}<c)yM@tmwUE@`SLV1{@N(?d`kM6Dxza}5#D@-BloMJ`=m36<$hRCf z7^QBevau-LRcIy@3MR@rr_L?(Y>PM(Rwv7BFf-8Ntjt=9O?f@n_TV|#hs+$k3qW$> ztda#Y&9b3;zfzD+B~uZ%Dgu@v+jDd`B)~*?m+pHf;&glVy3@Z|d8eShB-n;4=V}w6 zZCGmo9th^{TQ?5y#k}nev28OU%V?2UF_!RhyH<}L07M3E=b7>jwrbUCBHI8ivKZdz^mJ)(;nF+fKxtTQ14C{8oINI2Lujc;SN5~F0MD21I z?$2Q*jUDiqk#71QRFvhH>?LSgkZ^Om>>7RG*@L3xA+YudXlpn@x4Uf)7(MWpT_USn zBUy39w#~MKI4@AvNcN>V9bR?l2#DCYr{4i!Va2t|6?@30V=D4!GLw6Amz9aEV!~WB z70Dh3SN0eKP_WK(+ys@lmf1UwTfN1Ex$gZDrr?UYyJ*-sIJB71fuEZN1;n!I834o% z@VMAkFcg?d0#;WBSo%HpUb{P+En;P>1wUgd9K#|3i zT}aQCFHc{r{6Eg4nsjJ>k9EmwGO=ze=3m&**Ou$tstM>U7~Hy@9c_HZV=zjqx+XDr z)M)QdjJafHu+L^EmRefmkj><&n9+7DQVsJ8Xa8p6^QvJo?uRY5HVZ$QPcT8jyyY$i zSZoR6&kYBaIEw1I7ItBV2r8&j4qSveOhMaTAAc1*RKvMg0f?#PNN_FtY{3w1 z294#K(C(>B%$V=&%9d*C+ThW+mX$-SejvJ43b*A*gz_{Yo~0=k6xCaxe}OLSCjiI- z9GdIG%-Yrr7HPkGE>zE~c!05nNa}`1J}nv=xQ@K;pd$kd6ydP9(5z{@?G1gdZbW2} z6vWefXY6lE)t)I>Az@A$Q36BbLhhGTEUKF#i1kA2cF)QOQ)vg!#gxTB|D#E{FGuqHjKGed0KAeuzG>so9ffp011x&hD3vIf?Q zvh9%T)@!qv+qNEnC}j-h>1p?9tKl<<&K?NZ6h6(wBoHhc-4s6(h|(uHEcbEdR9N+PuCyTUL8A81M?263~Xpjhf1aRbTf#;4pTxx|Q{H z_syMy)s{xXgK|%NY#nl&G3PG3oz*QOzs=JKAVroVR*64=hkzqY%wvti2)hXk?XpYa z^l6aQGP|L`8|~()dMN&S^PID;tVUu}25s8Wr+N9dUIin=thwg0^$?F?B>4ahQR!GTF0t=(sJ0&4{fUnpq*X&n04k22D0N zyW6Z$8_Y1sh`Z(@sTrYD-xc5Bcy#U}1M(m;sQT zO{oU|HQNH>w-a(Lcf_c~KRf|IyxL<4U%r?`c1pUcocv^+Sb=5o5tD8|7aqJ?S`GFD zXAsf}BX%oFyde57stJkvZ1=6KZ8kZ<+M%@oF<9Cl=-bXI{o-%#v2B*2@7Wz9vxPks z&YPtZtNjO^PwOs@a6^2niUL!1N)?5wyT9#88p7;~#z1U~0_IlLSbR_5Gi?qtho+U(WZG&P)57Qm=ip&KZd88rPT8y(in= z1GR}4;&SQt4OuhKHqGW{Cnlt~8_|88*#)Ms-kUd<|Re5<{Wz^Yhj zWZe`Kxfai_@jf=c0#NOzV%)M^V$U_p0aZJj+n@pL7j@RdLn%UZw1@3N=q*MARqB(e zj5~`A&VHzgl5dMoPg^u)V8*p2DWEyLaTv$5Fz;S7V;{C)!MLF6+NMeu4xT`WEqfln zDNEsCW#Ek37$<~);DJZ7S2r9o>UU?>qABL~f$j_oOutwJWeRhNvrrhDtnGHj6hBGF zaw~1Zq*%BUEO*|NBf3?vv9)L!wq0|pC!YG+#2~34QD#S5d=+2dmP-P?n(26?>@9khg-nzy}lB5|q)cIu+B!xZJ{>?X}2X0YnyG;MCkrX91Xf z#qzLb$1d~DUQ(;)Jj}yX(*TMyjsIR#N5Il)0!G~K=E216v4i3uOLc$26psYL11MmKw$r-GR2lFj z!8&ZSyi{`oGgb>?3+@BRa^(xs#i}qiS_^`?*bLcfD{Kxb%{=6IE6bIPnGsl&Cg9}v zfDSihaduG$vC6CMJ(e;)^YEG7;W9@h<#Bn^q4by4t)W&ZE_1n^_=DZ#ykbLcLA5xR zcsLGKA^_ve-wLcZ>;5oRE}5cvWpWN+%tWK1*otAh%Pk*u*L(AsQ%Y%-^N;GV{{z)~W<0_0c_-8O6KVt%&D2n4u<4T{w!6d&3| zgRsJ4jVUbzDvP<`W$u^aP8{B9Db?)VseC`t%naY$ac5BHU?OdpnHgnGizhrb6?3l} zLU0D%AJHmeO=gxuN8wP}EEBsHSKBK@w_4kw&g3v-+NAYMMbg`2C1&!v@iNTUUEE7M ztV&8Y7*tPF%?X=t)O`+8Sc*w##*_lW*7f5%Y;4_B_qBQ-BJ*XYpVmD_2zA=$HiQO2!x!SVJes&ut#tJFc6R5JwtH=fe+Ah?e3) zLN6xPb_s69Fe&B3 z!Xvl|Sk`XSunE3@0lM)2{sn&tU+}L}=kLG#v+Df*>0eUkzn}ezANaqYr2Qvr{vTMK z-;w$^Q0MP2U;NQ!{;hhT&B^>r6zBg_68C>7Xum!@dGp(kV&rM#^;fT+eew=9(_en~ zQSm!Pe)0S_pS)v#c=G!BM}=;}Gu!#uWC}fYgv>R&RPdGHYXn73}sKgag1p z6t9XZDl<_wNxp3HXU3nWFR)va%uPHwtvA?XxA1U>fRzH$H!1T7Fqbg**sl)tCtmf| za+M2u#AKRUF!uxW%pEF#(@eRnx0Rn=Isj4k0CZC?Vz-)hHE(S>W^SI!>BTmT1v+@C zI$I%Y%o8JR?p;lFwedHc9&@fX`?F)fR`PP*)>Z{~>y=w!SWbHkmSOEP1dWk0A2`G7 zEGoEE;hsn2to~BsSaK~E-mW#-rOV7g6)a`x-0jir)O;yQ6h{kjtC%><)O4OJ@InnV zt9rXKfw1vaiG*>R__!(xeoa|Is|Dhz%tz0*20ys+8f-jy)oU?dM5SP>^I&}T6EyAi zVrfZeEf>ylKVVWPe7IX}zVB9TnIf)2sg0sp_?C!bHmr#&aQkLE$D^KM+!8X_?5PHS zo5zO7%2KULscdWc~x6^#fmgy|( zwL|XJ-DS|+SFN29v!aq~vjk1UDqA@$cq=Tl&f>y_Lzz zeKQ-@1UtM-;68?Dc6XcdsCL`7b}8QX;(3 zs_VTcR-rlgka-V)&X^kxL$$(FF&rzNw##_Yc6`30-g`dt<_ zo_#OJtYV2Nl%oxvqA~=#6Frh;msXD~*Xm)lVik3kHq5eK53l$>pU8vd+d`e0VN*(W z=PN}#*B&nT_hMUm#&fNqUlwt5p8@BytG-|r#B73j-Kz1FctQVFGzz!p$?g~TWmsfx zPXOCgoX475a$97kKd;LtP*b%mcXb>$rtA(<;P$7NGdHC`t8-In01FK1#4HgRP0 zKADDwlilqnd8j$mx`H2g#AB@P(3yDA!X3wEj}A+CVe%}^5?J#rZ544gh8qWjpLSD? z(PF+VoQSP08&;67V2;^Y^--!pVL2Xh?wMtb$4%*);tXomo5-Eam}^{<$z8bEFi%eL z@vS6x$OvWTw#0~6s#w<^ja)2SJBzAlg+=6ob+)P2yW7a3Uy2f4EYeNcuy~yKO-1i& zI%oDJ0b=k?$8}BxbT@>PseN_1)kN7$2Fupa?j}?YCgMn}3y(ogV@}waezQ7D#VDfL>HPn8o_Ho)gxe+s3`gLqtJ*Ma%$miGk)K@- zY~GRX$W({OH(zQTT`EFfQ%c$G)}SDo`n8JsGAGvWp7A^sXIGXgyN|o8s{tBN#yh&$ zZ|_q5ICCj2S;eBw{fgXy9cG9_`;nsRA6L+&#AM=v5@p;OXwZ^n#*c-5ncfh)f_N&T z+h-;yNE(l1{ybhC!@Jx#+tn9PiG;AF>W6~GWyj(U4`5Z!V_f+51sks{7|(f93!4(O zcLF4A%_ARWR#kCk_*Em{nP=jPDQHfeHLOcB=T%*}vQKqM-x8@4TpwyGKu=e8JtDC*ag989$uT8~l#>gV&I5kTT9PyEs zVa7Fv)aW2qU{CGtc;h|! zU;KW=ay|E9d2Q#TD!p(97inD*Gp|nMQamN!3<=#t^xXzH8+WG?X;b?X1iRN;VFY3- z_P3Tk_qjb-%~lX!Ea+<6UjjrHM(nF<8%1d-rN07D*d5Vs!k1NM*W3jpnw}V|E zr$O8zz74z~D<*+))VQ&Obo+3d8j}h_Z60c^~a1UyahgHRWzz8my z!97PC<64gc#H+f!-X1VIuDaN)dJM$~SA|zryHTuz@`UOUExe&)U+$JmArZ5Y`{x7)K{a!E&rdB)GzV*2zeE1wt&`5KDWa#nNW7 z9{|miU|eTQr+T^`&4AazD6)lQ;qP$*R^S0=T--K46sg;6ma8CjNLHlJQe7Klt+ui* zHYZRVYFLP=Sb*6snA@?~@%tjuuw>VvAj2@*gjLOh_QR9ZO%+y8600?5Q$W*ztzGtcm;&d)14`SHdH}0YObJhJH_~6I|YZe9)t;l>p9MLU|7Q-&vcpW z0gMBqDH8}(E*1lak?q0-5d~#b8?meUurI1NscxoTQ&(ebzf9Rnsx1vw+syU9EbKa` zYz~iI;6=1fM#PcD+Np=iFOm~gK@?25ntBB6Jw?{DNzHdzCE z-7Ls>OoiKm3z+B3DLjT^&ns6`0<7lqoDmZSNiiUd=52pw{6Mxw0OLA=Ph3!$%=@%= z7$88&YzzLG#(nndI`o69HJv!pVpj`I$C6#OjfVY~F!mdUiHGEq+?!cg&^}Fg_c{{fktM;oe4T-CZrzJq%63zsv$NYNgnsXUoIX9 zpKLES3z07?06mOS3m~g^ro_;g4oNNSg{8c*3?ifnS2U}Xfdv;&7HpQoLaide^9(qb zKsfOnl-L$*8xPG}+Hlh%sykyP3-4ErjF7KNhb1CjzbU6$ZjmIeG1@}M<1U7t*qB!J zzFbaU#Y%If4;XpdD5!P-(b}=3 zBbj(~+;s#qe%F%0R0#tz0l|)ZLFI{ski{1(rD)Q%!aW{*7o5LB>N_wSSgC^6+)%;e zP;_R{Nb?H-j^;+43aS8`l?H=P*`Y?^DAWGUr--H( zL0SL=uoFvGHybU2jkr0lXTqJiLL$Wo0|r^$I27LIc8j{BC+3OgicHN;Fm3Lo<=uCh zK%?}dc|$Cr>fyEhhLSq5tmF7T$O zN7%Y))AQXk$V&~7asu$wiop{CCI;-nZ5PWFI*`CZ?rtsLa4WS>%};AE0z{2Fs2F{_ zZl$u1jQ`tQq`?wh(d8=PP?x}-Z!m^)N}J9?jt;j_g-zYw&8gfI06ceg0HYV%M~H(r-J({yD;a``6_tkJMJgO0!2-ecc+B{SU_G7# zNEAw2R>fgv)yS9$Y^M4!-q9NQtUPN;nXoZ6dt#YdU?$p^4yR+?97}YY$8u}A0={&& zqDCdobt94kjBk}8?8g+1uXrQkSMN5#1kvs1;tqLVw`v4Z=2BH((C-Fmx3F&z^3uxi zCfiTm?z)@DHTGu80Q6unnH1+W;Jt@6Y?iX)&_G^hrC5rG7Y=IruI`;l47+kh=Xw@7 z@q&#ta4Nq9DOflJe`N$ytI%LrDJUsM%iV5Z^V-d(QJhdl3KK46Q3`#V*_&yqJ~hYHi@a#&fG z6wuH!!X+RE*_7hd&UmY`ARSl5s)q`>6}$)d&U)D;gIQnn?#rL=0e%?+2FMuxQ6ZHNIniDm?T)~88A!QK%n%ejYq z@9NmB87hq-SuIhCyN*=Eko$&jHxek1tS|)lH6F z&QQ+)AU>*9sJBW`$!2SfHD(sEvUsY;9H!Qto4_VRzD-Lytrbuoy_m(WO))6elE$zX z^$S3z;LGa%;;NoRgc?J5dAaNWj2*U>EZR8KZ{Uw=IqCJ&VUp`^zLKT3$^`%$YqGI{ z*@HO_z_wj)L{F?aMgxG!BeS+}awm+G39t}b!R%87)o@()nAt&@EebRhQ8R142)7B7 zde33$7=b1cq-r<_5L1e=sKAB8Oj@Jc;tG~7rUdUcRx|xvO&!)ST!dM%%Wsqlu_$by z8*?u&=AgGo&J?w);IX6@el zl2sW}joAY%OF&jjGgMNV6G=VPlG@By#PzbY!{1yIU&@7gaEtbyHnMz`jOeBM26xV9oFmcTo)n)~%&o8;tC3vwqc5_GB~9dt29{ zI+m4U@QeixlK`OGVe<8DADF|gPnK(nMHt`h=9zg0g~T>X>#IGf;J9O$HW@7k!D*|} zzO(S%7a2-XG3w{NNs^k)Y0Wn@AJ|h}OMAl9ly_TLh(*ku`Ndne1urnq^*aFWdsOH{ ztL5S=t;o-ta;g{J@*TCzTDYyj4=XbhUXoXRp>9`{Fa`^O#OH~?q6?ZGdMIb%K;Z3(JC)N2d;q#?<2 zLGWF8zRl)vD$Cv`o@>;5Om1s*7g}+%*X}nV>WiW83zH3a#A5A4ZKG$xuNCp|Pho+I zC_C^&RXIR}aGLlMK+X1kVgW2^_W0xZ;Q`jS%@c|Hwu&Lyn0L%ZY`c}NMcog#C6CLJ zCGq2_9^hqdi>lqTwHB`6O668dtBbOMe_JfwzJ6HvKJ0pHyELY(VO!UNJq5!*_KF|h z++8SjI8DH`1E`$MwB_2C=~UB+xl^L0e1aI$7fWb^`*dJGa{}t22Ip!w;mg{H!^Xk( zw6zhyMj*$ch6e%|p3iIH=V{$AGZ9NW5c#jPBD_Cf@0*|=pklGR{9Q|5{+tGiJ(vw&bx`w2*F^#3}~(P(K&`n zIM+}*EdtYQ*obEdONWA*YYgxhVV)GPfDgYEw6Yu6%5E9NX-8I!qMLM|;V7OV1J)ML z$W$#zA>WExkJX5?1semwbZo?Amm`*sYNb%*!!tJ3YVMr{q%LINp$pI#`vf+d!{T+W z*;Hz}dWeN>Uxu}}7|LeJUbAHfwgG@$qUr9ZO?eyZ-7IeGhKPOy+ZU;0<>BBMMCRKR zZV8e-60G&!8QR5Sxa^kaZIAiFg?VyipiDY3xt*_8Eb!3<18qH=WVTOW9<#XN zp2F;@reMMahjX8d0BME|&!Da*E8F}aH_Yq>vcWf4ILG2odBVX&S{o) zR$qf<;yxd1s6iwHE0QbOvQPWC7sIBF@jLW@i*bL{ zPN4RlY3n}3%1IFeYcCYDJLH&Zn-@nqL`C_b8FISU?2idn5Lx2Gz8(Y~iR@)mR=4-)8wLjf)jX1Dj3KE3nO$b!FQ<*0wop7*b>| zl)`ck3^EmP!L87Avklg+j*Ug+n>)qXZ8*1StyH#A>!Iqs$w3WPXVv5mOa@{!8@RfD zPW%~xcRv&610f&ECtmiUeV8DbdErK@^cyF1w)C|uyRb=yf3r{4AP zttigblDQEoz-6n)-O(J<3;B!_(QO5s?6)c}IqWKV;*8x`AZ~bWscaNx2L7-!U4S=Q zHmdBVn##6GBMPQ*sdi(_J+5jKc)~v6+436J_Abuy;NHG{ZLx5gRZCb>Pr)|yyUATv z!JO)e;#6UxD|qQRrdYMTVZe!?{o8uNWmOU{m?E$XJLZ*SW3!!KU#PRiz zZxoC3PMpuoidwDPH(+agXYsI)HJ2?8GLcEyq1pb)dj4VEje-g5!0$z6;L>HJ*{&0> zmS#iKq^6ObSGOrpt>o1F4!$Xe4N5RR(xIx{2!nI zTdcyJn}TFhir@a^*ES2wJ$mj$2s;y3t7c3Pyj91YXskaR0K4Sa(C&PM9#aAwtcv0cj^<{r|ao}Qg+PZk(n)RC)Ge5gO- z@Yy?|5vvc|JJ^dasI_jVZWhXIi9VP9GmRzK<%qZ1`O?0cd9%IL z0a9c&DTOZcKqA`T624 zX44Y8wunoco!d9yv*IuJxS8lw-G=oP{#MzYk&N!L6A+Hw&a=DOy7*Q+@xmIgTfimK zbR`|YOm&5sef#rp$A_IiDxid+sk;(7R{s(xRy%}<)(KeZWAFA90Wjk8OjBLM3CWjfVH}KOg9~Gk{5~)5j)Z*^UA!EkZLw%sPdFiu(YAn^Tt}f#0 z&Yl{c8K6_z>ORaZ^vTwuisptQA-0)Sqr+Sr>@?XNa%@d^=me`FyqYGf2*BkYf6s(c z=F~m_W(?TKqFR3I>CD}Gw%!@M3hE$|avj1sJhAQeB0X7@xAOG_AsqY8dQuuAOKcY< zbUT&}xf=p?>rt7eP4On%o`BG7LxG(`afz6$Q5B5iEXRxZ#MO?~Hl9hA^%ndkpRun= z1b^HYj?!a3Q0`-QQC48uX4iNj!A)$m=jXPmGJ6eO4)$qhANx?lx~69oG0&Z5w|=Ki z;9=hMX;R>B`|*`=u($P4@_&FgSDPhA8xfoac>@0zA zc4CP)pmny!==S0SR@#LgxG}drK_J#9gC1u?*pz46*n6{9e_wa&$3Y9BFYd{9i|;`g zJ7aJt=_?;#M}f^XswBT+|!FZ-cj1J*$a>-1#Nq5bTT4%#LWmGl!T6e_Qz(UcN@Q6 zHqo@>tacwWq7kF#=A$xTmWA4r)s&rTowOU3U`9g!39(Vs&qJ!qB;0C3ld>Q7 zf&mvAzKIWt1*>vgk9qJ|3RFihH&u);8w~@z3qgzEE9?run!++p3x}{z`!i(*f~R`!?4%9y!=po69pX@ice$wD`4W@P+AXm|wSS zf40Meh`M9n2>G@$Q!OZ)JiFWe6?bF5%X`aZNgmm(o9a<%yOf7g{=D>SYw!I0$)lXg z=DYO`PcdC%EhZqDq!)XAbf+KUDCqr4` zsKbW2Yu=7+v~h>cJw#7o88;n~-5$=qO4hrO1}2a$+hk`Ya6B$F8_^UI0P@_48X>tK z=w{Nf&4X1v3np!E(_N^VK;lg_iZSK|Jc5N4g5%~ER-MNcpxmt6w^^vA^$-u!6u}qc z=&JJCOtIzYV#3g`VlUMVnr!OQX(D4$xLJXCJGtpcln{Y;A2_loy3mI*lrb$5t{`?k z&fAC&1W`=m0;$M9)nVHeB_h0MzL>>IQ4lu@e|S#`+xVMRz(REjlr z*j2?=uj=hTDzY_uDwM=>?N;NjheWgTVc9P%e-D1UEp$C^8`Ux`IIsD~?J^B?sAUD%vJ!yQNTgt)yZHYZ*H+XPU(XueZ%& zuO5&jk4spefe-;Z1h!nOI%t6(5{(sne^XAB|E4%mq^C|H9#d`$gV(HEU z<+l5dZTe}lNVVi3BRaBxOdyLG%1A(DS>50Lt=~T94hf*BDoJ)L!ri7zRX`5+o_o&T zYfo#@!@#f#9GY@86+>>Q7!Y*oY{osr6SAhz@KKGRS*_4h(6wr9WF=%31t*Fe2BKVn zB4rWT&>FJG5rUHukr$mr9<9RC5=k@%HE~JjHw{k=D$pd1Zjx3qWh5|D^e()RntWLz zv)&1xK%0@l&Mt#uy$uS?qPRwy2rW+?enB!_hpa~5*OB}x?J$g~3*BYap!Em)N|6u%h>6K2;}zy?*vqjT2f=6Cn7Jnq|C<1hsfNA*csu_MqgaCsr;8hQTWDnxlx}(bIfQ=#^y$v14W;%E(KVlXzaC> zZQPIK6dhM`gONaJp(SLRQ}#_}NawwVHci;B&B-5QZDqxx97@9!xeyRcDwq!`T@EW( zfp(?hk5StrrUKf*-)FN`G8sf|axwr0N5KhNRy{x=4sAqgK4mes2>c*F-3=t3EM%8o zC~1+GAEHCI?PUL4hYU?VD%xQpM@CSgH)}=EE-Y8+)R3P?qmu$GX@%M(aB3o$SdOJ5 zE=JL}!>!ZIl*=d(CBV&M3Uw_!j9Fz|rhf$RT_C~lSlOG3dtojN<+>iTo_x2Y^JEcL9<4Wxm36zUm{CT zXGY47!PXP4YNHDslq;^0feWn8%Yx~I!p(qJ0THUtvj?wl~(LF=b zoW`04MIF6POX^q^q=4ija@~n`MNS2|MB0a9Tu*01PFEgktwkv}tP4wRU|M#obVJTK zStMA96dMz1rHpBd851mRq;f0y>%|bBZLQl4?-Kpe>~9DKiTSwX_^GR*+z2QZUom z)H(=XqC=^8$P9GqP@)h3Y8jT(tw#y;Jk~qaqJgGUR_Ntaa7m#m^hxQZp={jgId_WM zga*l{l>!h*fza`$6H(Cz(%H8}^1Yds3M#p}ED^pfX#%7|PUE}v)(Br`=r7jPbaOO~ zx?aGjfK6Gpw3VSRYP}9UK)s@60D_*Xd~~T?3=I9n>=354#24F8zEYk?p#t3OFtaBDYeI4Q zjUtu?u_l?SjK;kZ0YpwEju!KU98``bq6G$R- ziSRmf^NNsQ)|Z{ucWUps7V1($h8pjap_AT2tFKRMTgYp%$wL*%^=AypWfAEy1r4(4 zwL&Kc|Fs;F4pW}dkfoEuJlB9W1Ma0kk{9%eN_5r>amX0aHqtXN#|IdxzaHq=M`Kna zdr@HgT9~>O$Ww@j5gUL`H$mytH*VIlh|rKZVj_i>vK$Q!9=+_OHm%}t#qSl6&p7f$nDWaP)K=h!| z&|!Ue30kL4;gwK2cPlGVk^_StyUDY<)ETDmNq0$K7wB5S-$hH$m?DNoAAgQKNW$!N zQKA?~1(%^!#gs{Diu66El`enQT^mPtR5eJ&(Ss)&>f#3ZSy`Y;u}eWl1?8j^1>TIh zawl(<)>@T82$Bt)LA^Rc{cTMl&^pp#1&w_%Fqpk1=rOlSuIkk>K>S7kk}O;!>)DBaot| znkICQWWClW(uvj`Bl(ATZwtR-iV#gAzl8i}p|wp~CrsLA^5+Rebpl&4oMIW6WzU zc|O!EhfpN6;2kY}P2JUUP`VGw`RLt5G$WBKtzB(OK*EV3yDeuYl zX|uo#QOMUZ0Ft9*#dvm8X0F#MFSTH?HjGx4rm)VW<7h}FHIu|8^ByKyl3L5UOuL*1 z1B;y^SLzLr36dyi1fPl~u~cJabjkEQ4^)h`gt9EchaK$18APE(bktm9h&mKV7nEH3 z(xd?+o62aZn+f-p1HJJOE7Uas2)&~u@A6D9Q^W$LC}qnYfzgzzM%mW#B?u2h6|K7& z>hQ9)qujU+^Ok8UKtglE=!`BvyR)O?myXvGJw)o0ePcHUaVg0XnU+|Q7~Pds16N1f zGeS)Cu5D;QUt5QSmm=+$)Z$2HA(lJV*+f#ZO5_(4+P5;{)+AysQxS^*U@V|el5EHm zvY!y2=rskS17(WbtzHUXATiI_8g+|ECT^4qDP5&=PlQ8go+s8vBIA=%V}bakp_*Zv zFqb_NT%mGK_oHwOj%suRBLa}nC>EvMDQwEjPBWy8pHlkKjkTy_=?gn%rjev2K+(H& z(jZi+Xz`(>B2mZx0&^IqDVOCgnnTLdr z1vW~!{$rpdlPzgQAvI>!RG!Y372aRjz-V-G`w0@a3Mz%S==<&kk_cLBQyNmFg~UC* zYuZl8;3-_v3h5PcWQyA4caddkEHI@*08L3n^AN35<5wW0Fij_m?mQR};CLT7iPrnt zx`?*Qn2}h}vLYeATk&Z}_ixI$Y0ubhTA&mg5ylC)BBHFbsr4LOuWO>fP6J$CSJn*`Gw3agl?v`htJXC}Yr&Nd!7m@b*o`l+l=* zq64DgC}n3%?DmS0Tc_>cpiroXBuEaKg>b0OQ;jqqI!I(GD{OOu1_$gX6$Rynunv_M zdG<~VOXg6KOINie?I8?oChU$TqUTT%D9TnvN{&I%j)0sl#PoSwM{pyDPRgeQ9G8MP z$llQB)ly7OOuLY7=31a4h3r`9xF+J)Z8?T_8UI7dm4+Ld&=udcdT!dVEG$Gaj0@7W zm7+J8kzLfIkQTVWxZBOq^zN7R1C zlBAU^D5+>+RK>>(x5MX=p&whbbr3ch(otJT-&i1xCx{g*^c@)dD^ej@p|HUOdMgS~ z6=KJg5iS%8(oq|LT)$3*u7p5wGU-U^yie0oRv3**Jqn$*R>46GXRJKWKq%HC_@S7q zEM%_8YcVCB!>S*{e_$CxB!&eSTlBRDm<&jV>1LpN(8EyZL-WWmCNkfG`*GAW3MSy$ z=R&I5a*)yrMWIS5<7sRg4J^+|S_`BI8`h}P(6x*@V2=oifRc%}g^lp`8A%<5GmSuV)`DSX=t)!Hn~Zo#-Uy)a37`CY@}swwL~?n z#gM61^e^b6JG4=U8ksYSI2nQ^Ahs;Fu#e()%)|f%$C`d~n%`uc6pd!W6;YBBNTXeY zYIcl5f;mTgd8iVGz0a+?fHOvssuftoBMG_sA zQ^n0W=Uq#IAh`T`0U?wgd(7}Hc~@<*AsP8pqcUU0-@&5a(c>g{Ucrl2$jF!SHBgsD zDN+l46n!%&KrGZe+6|4@%!A_wEH&Q61p|qp+qc|*s`kp8zr!_iexT7u5 zGO?;bxgOiaf+9DUsPfeChC{DoyV|ZuuBUw!)g9w=732dDX>2HJv;jvlys$+h z!l>9;zEEg7w#SyDqJj1~Gc0iX(L>81dMr!D`-OPn++iF*&tHMV=oksg^q@J%Nzd~i zv@H-R?ofhA24aLBeu1>1c@$rjF)=N$DwiaTga`&wMqN}FmC&P}j^aC{2uq`Ss*oqi zgmebV1xW+DpKb?@v_|(q14Dr~GgK0{WPB*+>+TCdG^h=sGVEdLfE%LANOyhDu-&*Dx?g%sia|9QM5u~k;PZWzGB%8u-9+7S@Dlqk^)7U^tlL?zNP|e2( zyyUdzq5v)bAVf)_IAZ8sIEmQ;;l4(YK<15R9xfP$M}hMWik^y*emRV&ALv;xjNDzp zV3YzF1#UX+B%w{+0cl3gLPw&oX4;1i)ts@Oi0*H+ZA&dB9jQr&Fb_iHjCQBUZ3_L6 zWu@_bfZPx1oLoPrU1vnKq-M~hs-Vx-qBLpRp<7YtftKhlM;+ch)mhLPMH;2F8H1WB z)^seP{6f>QX{)pqt{)J0D6xqnA|Z_&6GU4I3!E@WL9V{Skjrh!)uApe%uI{skWe)x z%{!H>;X-3@5VaOPzRJml2(O^#Eej)yuazJT#M!%HFnO{* zPvJ{z+kx7=7FzJIm(^@YFN{nt$BI?z5&xuXQ1qf8sFbY|mIYFd3Zb0bhK>c>QifXB zJt88UB8?mk4D@G$Bpqb~kc`#<&ona- z_{Ujv^3!*l`|a)ZcDMRz9De%b#k!o$fsZ(RxBO67@$UL@A+F9cIL~ix7UJ$KKl$m^ z`*&B{g&F%L%J%Z)r-$o_l-hn$o&5BFZtoWY?f)&T?yzug|FF8&Ka1x*ulbmp{S)^0 ze_Nt=-Rgh+Ytz2#+yC2NP6DrM`KJ$2!s}Xo{~?0-WZCaWJGd^wFOGlux5y{2Yx-{W z&%YwQJW>OH18noUs{i>x@_GL=UE22MPZH86`9w$`XOAsIa1Dt(AbT z)q2WYW^@~IsU;g@M*bE54BEOVS0T|2;~Mn(3jZjRF;S{mJyQf6C8ffAN0pK`;sQ4L zw1=E1X{cDEb7~S(mA;5-TUo&cOo0u?9{Nz4xE@>tQ&Itg2i32tD0KvwENPfcsURB6 zbk%Vgkz_@`vW2-wtDC_SnnF#`HCl|ucuir->+tiIECOyCMz>dB>bO z9|M|>KJpy?fXccVx>ON!l|`+|vnGjx@wA|$K1)g?b*FhsuaE)AiY|^@$IKlZ$4LeO z9$g2SiH%jg&O%HQ9bK)Vr?aG?aKUI+Mm1G7UYo}CB}`9rWg&Xjpf<-?uF+)-Ie9VC zWLZtYNM{kL&4fzc*z2xy?E_f-+FZ}9q^GHai==3n@TB@(#g%rwK3E5i$?=E^a}5DW z%_F4e-$9^OAvMd)Nh1>z!8TgtC3IPc3D-2dF^f%Sse=1T&m(1<45V68SN5^hJF83< zPq(3m)(r@@L)oTKpEk#?FVd=_rku6|Jm$s+~#J^Be{S|YWVw@qFl6<809XTs*J(^@pW9!3UP8k`Pwn^ouq_GmYbx>zPQ zvMy@|W>hJT^sZsNtL}AWdV7-Qb-_46k14-Ug@All(jlH}M@LcV zD1au|>PFTgg9oRS&7hrxJ~=4*(;OM%GA=OCG8($RypW-X?gb+9VN9W4qD#MOp$IY} zCRp~;4*uW*{;ha`O{kGSkah2wzl}#7hGEEELbcU+uF-59P|(EelvPn6b5YQ2rpJ>Z zIU1EH%XTsa47x*AIxy%{7P5({c1Q;uITGh73^)ZuMcuFy})DvvS zmdZX;83=$xDA&cfh-{=+prQKQIqDl1Hbd81|p8iBdgKNWf)Vb;v4NqzFQIyAsJ0eJyFx+NmM+d2Auo~^)mOw>qe?$WQzq^zYJspw zclk&emKm_5fVWvIO@&H+OTJOtVUS&itDEV1ni}CCt|FUi$znt&wUiN36+PUR*j{wf zNd>1ahAu2hW4t1aJ5D@ zw9U$d65WWY8%qYGI-2uc1PcnZ32}k?Ey-^>R(qX>p^`(=3Q!W`LBZtH1MIEb5u6Rkd$w?PM7>et$JD;_ggG=> zSB6;P1P(%dU&Og8Mkg?rQeLFd6E>GYZgGt25q9r779bjhkYEZ%5F5s5YbQ@&uGDTG z6Nlsjq;p0p%?utg0H}?(NaGYE{Bhu$jL{a%JUy87@uU!(XeUDZ-l8lkq{z^5j37du zwbafbMIH6SI31b&6Jpc~!>vV?x7FKoT>%}h_l3yMQLgs4H5Ic zkU+*Ths&E;_i0yWl_0AuMJ1HatSp>H-~$wB^1K&($_8P;r0%f56@(u}6BmLjeXN_1 z#YGuju&Jwi`8Jb17!oJH0qwecAF2*0S{Kj_afab2qN0^^P8pW~bqWy4Dd{v@L+}v0 zxEZJnFs7$l=TXc2(YcnoQvoSv zOi=+Yl0>ju=Kvv*&cV(vPnp5?Qqvq;K>Jvf!(cDAbCLsfbo?`cUuG+c0k)J3n!?2J-#b!>cyw6i8DqtSvPS|NcI3Up9m>wbm`MJE@_ zTl5X>UG!&n%-?6aMWRHiNo@wZgD^s>E0jU+X@XBt(vlmF5Hs%3DQO2;i6XOxH^F&@ z4plCrF*5tunQ(WOu$WK;%8%m~i-Cz5=-eP}7jjob(-1?JMzIUkJ96646d0XFS9Bx2 zvaQZ&o53a`MBVEEyNYI2eUE%s(NdL7sizNBkqDd+UM9@>m&llP!dAkbirTe8I({-I zN$UzC+e+w}{)9v(Sq`z*Hr9{zU$4C>#6-AIC5!=&Wx*_!%yq%6W74^X_HY^sWS3%0 zM6d5_8KtSp8qyDZA51EoIvR6hh2@NXwH;~#t<&+TjCih7tW?u>EKmrazL9mKKooJ~ zjExG<>jhR=jkP7|1j=%!P((*h$Ad|MvO$7dPrenLiD96UM?srOXA;R1geU5%px|9I z-6JdIkaj2_n;`bfRRTdt}r(*gGU9ARQTNpxFunq(JsTHtYy9n48GX*3z9 zbe9hhjdwaXb>6jFY5~&-DV&OBuB`DZq5D8_15-z>OLGPC%s$4cj%AM+g-$Ac0P@7f z8J-K{F#4;QSKAP&7Hvp+qEVnu+?WvF2m~h3agI>$*B$fTRJ~K9Y?b9uSeeHuYzYaJ zFn(H)-em2AhB~fna1`|nAJn!JQZ8wTx|F_vMkk!vT-w@cR@U>YmFsHNEk`@ohYw|*ssnba!xJHJTX+- z@{W4w3ZFWLP5MSx51B@zL*3~?=!Splu?ET?GE3ie~n_(zVbxjs=skLM~w} znDQXM2RlcZ7cNT~D5~4Ygb<lp}4S z1y|^-KjD+q1A#1Aa7p$`LBdE!iLL-v)3P2BM}wn6-T`*ZIu?#$ju>X=K7n{tsG0$L znE?e`G?}rbb5}t(HzJPMw|z%bypBm?4Sz9{{5s4E;)aBt`^W^$le9vB0~&p~EhqP- zB>)YOJkTeU9S)A*KpQAcWsWj7omOn@RONy5mJQk@yNaOsO1Po^Wf4Bh#AA~2pO_YZ3<|gn#9H=GAOi0hP@QK4=K_!d1^9Z^5bv-%EHTM%YydI9qFWHFkIC1=je$y z2na<`50nrhCye0h$T=N3%fe`dGa=}Xm1<>3bxV*vCu3gMg>@Gz5*}%o>1-re_$NYS z#pxNvH17>`I%&0SI3MuM+!$%_)RlHg0_kxMwDQ*7g&>P zOceFB3BeKCkQI6p74(3}$;siwnYQ47=2UG=%Pr(Qm8I3N?-dyoCLc07`{gStI@a!q{!XBi(G!P(UC>DAN!o9Jo@e}O%loLGw1^c6K%M4mPh$G z@+jL39!oTAgb~@~nRb~_Sw3j{Y8zxISSY$5wMR;r33GU8G9w0+2h<4@GVgSfVN~w3 z0@B4!=psfdq(Qi?VZ>vU`>-k6LFad{0j;#Dh4RSf_3&2F9NW>9PZ$wpHVQW;da~NP zG~QK=`qr*w#3h#LWpz`vSQ98Z z00txY?_CW$oMLNv-dqRKxJ05PrnNbOe@an$E_B6sD@8cPumWy!mkqh3JdVw=K*KH6 z6O&}san!NlnIUz?0C`SQvu9err**Z@(2^BGQ_zv+wNspy#ub$<8%`W>Y%<|O!tEgQ zFGt-n@L7{|gYg0X@IE@L1<8iW;f`W3(-icGSj8nH5FX8n{0|=|FR4NB%d0I=|L(BNjwOQvq0^2(mL}Nj(m< zWNPE(l8Nn_Qd?Q}m27e`>YUZ|i^_w@$m9@yrR-Xn*rb5@uQZK04m)bwZ7b)}kH+E+ z=T(bbh$1@LOtti=-^R60F=YD@#!bP!-V&2=*4;%3>nJZKzaZNSm!zp>=~!oj;v>1B z0p8V?K?LEyirMHC#oicrq}V^HX||1Fb7%TAhgMLS+@v`mSrbxaoX*rV(~gm1BEOoc zbDNkl9!kBlCY#q9`9)vK0kv}E(B{iu#pQ@;) zO)#xR;*%sJ6wio*$j)dN%aKE2IF>Hf7HG+a%ksy@-%om*=|1Lzky&YFPGF9%6?ye? zLyEp(3a-eA#I4}ItQLLK1!2{Y%O(og8e8Y6g{|P=k3qvSbu>D_;M(ziXnxbAbx}(- zL0%W`QzoC1UG9m)wX38L7&!!srSVvQs8rB#(j2a;7*yh#86= zB+HOXM{ikQExVNP`2%$FmBPV67te%9#58v)<0~JLXj^xP1?ip@kC3LI#y@K7E2=^v zIWaM`9@@w_A?VUo_X>7~T()uOS^*|wzRz<3iPC1nh#-);7498slgbksm#^t%BIKDm z@PsjAK>t3(Ch6ChZV$r}2vHPF{R=)86!ZxEVZ!L-3bZF(jHcKypr=i74eC6BRTv=C zj1fdsV}vbgrim0s$(?G~!4xR#2;V;13Q=0XsKcNbMameOc)F=0=+gA08p4jDl}{*A zMfuvnC~*eLSaMwuynMc*9gg@h@s_0z^JKgMpFA-pg)FrUQUN`7Z_#a_28Qv%Mlu#NXL03 zxrREUz7Q5JL$ied3Y|TaKPx8bdgNnC@WzM^!A>rxqs=FqDA!go-Y5`Qp=6c~R@8)c zyVC_Ticv#TG-$&Q1M??3!15sp+?w0)Tb&e{s;R$5GY2ixR zcm@I^U0P#Y{|Zv%wqTsKpWWVC6&hQehI@ zIT2(pjg1X6c4!HCZ93U_ibN@(0f8?U*EK?sLsvr*9mCJa>>P}G@<_r{ilUe_M0+(S zDXafwEjc(UN z$2^V&J5hltIwu2@tR)kvT2FI~z?uj``IN+GQ%0!Y3|)*4Su4|B5z(N2#eHY-t!i9RefiH4hG}$2=DYRO`s@Bz%MW(cRktZG1CW-4c8fyxfh1(+En3K zBh*R5j}Rn69r7r=xZUMmf28bARpM|6!CP`8@V*P+S(2WF0xSQ=AZ#%i8t(6lBcWllwZI-)K021xM9HfEW?s$MAP#_%eQdvfL}8BN_|#}n>) zxZWE5ydqzw3QWf zF$C!X92w-LDSp{9M6kqnS&}D?9yA)k+ZwH#*ceXG_+s8v5kSnV;9yTJV~^}Ja=Muj zw+r*f4s@u4JW#Q;9TwB8il0QH2%%`*A1d3~FrYA`Sfaqy$X*JeMy1OtC}vB`6zkjM z6=^|ev@$J9P$}KS@+Y)t(KL<79*rW}c@$5($Yr#mHL1z+DQ*(>R;Vv?h-kewc?n-x z-atZM!h1Nt$P^=%mDHd&#VSjttX`U))2pOZn;t1H$b7}0A_YMj1DIw0U&$dXE+Ag(c|83%@9 z9T`^bKAPogMU)ItN07h>VOBBb;s3MoCGp0`($cvMPmeR_9kwqG0c$D?=Jj z-n5k`gf3Rdq7>*SIZ)8)o5`>9HH`lXBUP@JF^u2_>)}yt;D|>T9x_41AjrQ{xba#e zIZ?ep0TINebi2V_tIgMIzcv+OzViH36J%!f88E{LE|3P$evBOQ+CA+KBVxEZ%(^;j zj-_!6(Gy`i9i_3Q@KwbL6ehY*1fx?TPngUjg$R+ri{LCZsveS&7~+*0DkIrsSoLTR zN@3oSLrvj(AGrBgA3#Z+&_)4#4_}uyZ%)uqGV}qu#veAt@M_*jL!6Mbh0vp zvsxWMXo`jZm{tIz3kAz1X3?kvE3BNBpk7C}3gWb!+k&tystcIH=&GqlN=x+Gwb1Iy z!_p>Mbx1#VXuS$7JLr*N6>8~#)`1i{g8i)_MkwQoE*PG>Y?*8qS>6t&8fIJuGOa>s zXwe|QrKnuNwy?FV&QP)ubvP8b36sNnA|N=TKPI18p*chnm|Js(lw|pJ2#19Fb!9Rh zh;>%5#%6k^!gjLyH7r@M7{ZU6j1&mlxN;Trrc5A^X_7C2A~1(z03@ydKcbk0nougZ zpBI$Tb+Lpm4W=-5lo&GvP6Pt2=Fmc_B~zVJbs1=%qks*CW|hOV6w^S`M#huX38|vt zMd7E&k(bpQGvsmxG7v#IWGf3INp+4URgE0`)Zd)Os2NWM7H)ihzBsPg0oKB3_mYzN9d% zS5#nxg&+;EI-cBnoD*AhasV(N%izGdM8hHs`F)D43z_76&{J*&0u?f&yjdC@wool4 zQ`8GM#&T$xOuDv!_CO3xQ(PvQpm!I?!W~)%9c+bVS}D{zGH51Z*jif(B*JHUoJ;^k zZK5c+W=GQDO3#UIv273%h-0mOMLPlfb*%ucgO~*RmW*7w#Uu*nq0rMF?qz>QnBt>> zx{h2t46JjPf)-&ti?Noq4qp;8~G(ve-k*)zJmA!+!FM%^cmI|F2 zGF2_9GvRz)E5e;Ma)BsVcWp`dnj~6xbdR7WhkzQ1*#i7glYBr+(FDV3UzHn9V+M{g zNc1v6VYpTmel)#&tRlR|DGWw`Jz^7)lC1<~$`MDVeN~&34g`bHoo6*IAOcwkp5w^t zH0oTD<+6Zu-63lR>Ew#yJ6T3K?-xCh`V~1&s6(S`oUeyAi81AIW z^$UfBvK_PzDBc;+GfGUJJQB`J_4wf2#JM3C7&}txIr)}?E@*vGC$E+ye1JkJp?rcX zM2@sAP^1%B=%86aTD;M-aB}ArFbZYJDEKhR<3SAe~ z(3_Ehgg6mIE?709SjfqRMKT$kwR*}7Rwz`ph+Y#cBqV{Bo)MO~G%@N%-x*U`QozEj3^O(b(OChjING!=Xr255O@y+( z1H3)R6QV$5^rZ5#uxX;imceRHE2FkUg38eVJEoKO>IZZPN~WP|)WDitu{}NWNe+8X z38flT^@1xH=$fe^x!Q`6ozy{*Mo)syhAvFLN=He+Kzpwwr5Mprp+`= zhp`mq8k`_JjAbRGEOLlNp}k+^#wAD>KCF67g+?;-ImM2Wfl^e*8j|o);uuZU#pIA0 zb-EJ7gtRZTuUa_6v#g=7Mu^Dt;#)yY^ph$fEzra`I($?GQ>|=GHVzbfDr(bB4eZFN zMQMx3y2X$g3wSWx!;mSnp0GEM%x}==DX`BFbJkg|MethyR}qU0q`Wu*03(|veIh!c zy(|>-pE1e?v@;BO0#|yA)ur`X(S!~p$5rtTq);Q`7CG9<+HZ`E!UxSxb6RFxvgrmA z0MM&I@-`LlhpY$=Eabs+tuq-uXw*p5$YNV@%n@X@=mRoEHnceaLs4pmu&cst-EPBv zYH2nZ_44YEF@XVOy7P}ULp>Cv1H-Ti1j zQj>ByHb_gY!%WjoPQ4fer{gWm$nmdn1E?$xO$`Sau<3co$1!c`abf8{ucJoxo!YN4hW8yA$kaUCGg^PqSJ2cZ88I6Je5t!}yzcgXeO|A$n5Gok56@{t) zHiMH-*A@{J@qDL{7ZZ~>_DH{#izDVA>oQ~awuC*u@zLMRA&pd0iPy)$02wmP= zK#~ooTT@h|Euy%nH=6j`Ap?VXY?v@=gl-f|^(6qO;m`mK&x{W%UxI7hR=?6IpX*kKt|UsHJEsbke~$Smrc^ z*y;-o=!wW`#-y(c&9-TC9jA^;G;IJm7^AZG ze}Q7BC^R1}NHdMA%!Z|7R1skVubAHu9?%wNX1Srwkfz?++UvE^a~BD&cTBZ$hzD(K z#FBfG+BJRDh;Pa&G1<{Ff!$}IR#`U1K$Rl;+UaXi)NBx#>Nv_-lnP5ES`$dE`xe~{ z?P>iJ_Er)BWM+o0Vr({%W9gIJ_`w-mCu`Cpnu9`tY(2z`I%&ee9S|v_u~H~WfRap9 zq)l2oZTnQvOG_h1r30=9*P@OAKSR^iqbxdYJUW)bh%g#ZZ<6IxG()qEpl(h@Pcfs` zVWf%&0*NAKO%VG@7^ne8lU+=ZR4Ab>)I(%aFYTZc=qX0DeF>n;#l?=k^f(=;bdw@SZGuK=!7$kC6-$tuY`|{hrW8*>H<*zZ z(yDfJ3o9HO>BiT8;w_|V~j&eEVmddk!LX6VoT}lT;B#hN^v~`~f$|8uy2N@OZ zvcb}qNU@K2nu;z{j8Y3V3?@aFGd6lTy@8exkpCa)Y9L+J<9`&>v$>o;mev~-!Tr-7 zgic9d+oXkjx;9h6=|^6m9b^wv2BC|oKTxMp$d$gvi2S!H?Z^};Kq@Pm>rk8$$W$mN z^R53GOh1TO3B}VoZ=IteEnM2?6uX7nWlLoKyG$oY0I_F=9_=YgI2EI(CEYv%ZD9XQ z4%D@#c}XD_VOjJ?^%U?G+JQ15bjinMG=f%y*oO?sQy`ut{q(7BAPsCvsPy8Xkttt` zM{N<5R}7|;PXQ2wl#&pxVKiy!g45!11!)G)LZxRQFE}tM10_Y%BS)ZTS0Yi@N61kM zEwm(JyNZ6^6pHv1S%4J2*dpr&YZ4uVb97Hk*Opp#AxK3s+AAo7!1baZK=)o!5JtAk zH7s*0&uoG;vWhZZ5d*dzEz6abV?b#{p{{~_%6OGPGV(^kVzhc7OpwH5)0coH=zpkg z2sof}p@Y+F9Z`%VaBfi1MoCX`iLQz^dZyNyeJ-30nF}@x22(C%)50-<#t^h60t?zkQDf7T zIv)|D@U*5=veX*(PLQ)l5Q!jw#`-2Fi5@{X-{aHU64HOJgwEx%7RZqI(Aky&?i8sy z+OF{X7}gA?=(ZAmkl%woyA*sJzyrd*1BxaTVP$6Zz!9|W1khZP4Df|(`!_?4Ft!lk zC}7ES8aFUf_Eyk}#F(CS49T{2XUNEq-*9;FeyHRb<;`Ub2R$e1MT4F!>>rf6Q<+CJ zNsGR=E=}n2X%}PuPhi^y*(-K1W$IKB%P?Z4S}J<9>gI(mP-lvI8FctVVOnhli(G{KuH${qonYQlBQr2CYQiqIT>9}OB%_g82r((u@w92=9 zIAS=dY+DT3t-|0~k+)8t5KvBr56Y`TV5saX_z!x-ALBelM8|4)CBpIrtnQcy?R5*K zpb19VvJ_s`3enG?V94oU3LB)*7R3<&xuD)(l8qTrOoD%Keg3 zQj7`v< zpi6%PlFD;a|7ReMrDz$XN-ABstb&4@Trn#&@ev$Cf=?7K(eO)L3HCrf73K+04!VI6 z@U)aeFKS~oQZy`2Mw>e1X+uMTcx>>~y@M$kh+}OiI+!t+H97ql;Vs`B#M&4H8aNgtQs7K)UXlM19%O)jiTu%vmUf z3@QbVOy0mrRcq@ByAPo_ChXS@xB|X!PvDRM_@|0ryWc8BulRw(8CwRqdPQ` zU#^*7WROMf4ypwi+sf52Y&TRpw8g3%)l_Y~nnJlyF0&q0L^qALMN&s-3L#L71g7jl z&n%C^8(L!Jcv@p!hy6!SUp7*EQAWbVF!byQx0y?;;6gxwT0mA#+A*ZSe1k%SQG1Lz zqG1GE6@(N5tHK3(T@BNmwo~7vjHl$pm^B(vu2bmN=#H7rr1+26l1@eew@ab0NFctA z3Bv1?DxWrRE~sD9#8k-RR4z+Od-z(WLQ$AW*2$FVQ|pK}Kb|*YyRl#?wo&$4&yW60 z1(Y&sj%U-O^i!0&WQ8ORvjK7?X-qqpOs`_88lBD*wHq3d@*X0A1ksjJYhZ=4Qtn|) z2c5D835^k8h94e$8A^`#-V;+qlr=u4Rs_s^knfiA-#9zutGE|_Y#Y#XL^d#s4OJ$9G=SI0D<_KKiTQZkrNJ_Rn37Wt

    >*a)QS~+crC1H9RHpv0iGLtk8nc{@(PN~>7fv& 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 literal 0 HcmV?d00001 diff --git a/doc/ionnotes/index.html b/doc/ionnotes/index.html new file mode 100644 index 0000000..27c31f2 --- /dev/null +++ b/doc/ionnotes/index.html @@ -0,0 +1,172 @@ + + + + + +Ion: Notes for the module and patch writer + + + + + + + + + + + + + + + + +

    + + +

    +

    Ion: Notes for the module and patch writer

    +
    + +

    Tuomo Valkonen

    +

    tuomov at iki.fi

    +
    + +

    +Ion: Notes for the module and patch writer +
    +Copyright © 2003-2004 Tuomo Valkonen. + +

    +This document is free; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +

    +This document is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +section entitled ''GNU General Public License'' for more details. + +

    + +

    +
    + +

    + +

    Abstract:

    +
    + This document is an unorganized collection of notes for + those who want to write modules or patches to Ion. +
    +

    + +

    +


    + + + + + +

    + + + diff --git a/doc/ionnotes/index.png b/doc/ionnotes/index.png new file mode 100644 index 0000000000000000000000000000000000000000..698f09cae4df23447bf608d2a585bd4d5972c27d GIT binary patch literal 246 zcmeAS@N?(olHy`uVBq!ia0vp^+CVJ9#LU3Jn0f2YF(8L0z$e6Y|Ni~?`T5VEKaXZ$ zILr2UCs3TRB*-tA!Qt7BG$3b&r;B4q#hlW9TP`L;fmV5yrw%$cCme3Ee&-OYn%|JV z;eHbLNzG*&z8F|Gv`(HGd$F~)u_nK+LS=<8R{%>WqskW{Rg3J}7qSOsCi?q75nH?M zE^{77+Z<+1r-r1Nch|4lthS=d{J1PfXUU1#ym22LZu_2=vAt}Pd}!6<{pzf1?Y4jM u^4(GWO*wx3{4#&$%6k3J$8QJwOy|GN;;5Q**1ZVmMg~t;KbLh*2~7aKlwD!~ literal 0 HcmV?d00001 diff --git a/doc/ionnotes/internals.pl b/doc/ionnotes/internals.pl new file mode 100644 index 0000000..89e33bb --- /dev/null +++ b/doc/ionnotes/internals.pl @@ -0,0 +1,34 @@ +# LaTeX2HTML 2002-2-1 (1.71) +# Associate internals original text with physical files. + + +$key = q/sec:objects/; +$ref_files{$key} = "$dir".q|node2.html|; +$noresave{$key} = "$nosave"; + +$key = q/fig:parentship/; +$ref_files{$key} = "$dir".q|node2.html|; +$noresave{$key} = "$nosave"; + +$key = q/sec:supptypes/; +$ref_files{$key} = "$dir".q|node4.html|; +$noresave{$key} = "$nosave"; + +$key = q/sec:calling/; +$ref_files{$key} = "$dir".q|node4.html|; +$noresave{$key} = "$nosave"; + +$key = q/fig:managership/; +$ref_files{$key} = "$dir".q|node2.html|; +$noresave{$key} = "$nosave"; + +$key = q/sec:exporting/; +$ref_files{$key} = "$dir".q|node4.html|; +$noresave{$key} = "$nosave"; + +$key = q/fig:classhierarchy/; +$ref_files{$key} = "$dir".q|node2.html|; +$noresave{$key} = "$nosave"; + +1; + diff --git a/doc/ionnotes/ionnotes.css b/doc/ionnotes/ionnotes.css new file mode 100644 index 0000000..d22be91 --- /dev/null +++ b/doc/ionnotes/ionnotes.css @@ -0,0 +1,37 @@ +/* Century Schoolbook font is very similar to Computer Modern Math: cmmi */ +.MATH { font-family: "Century Schoolbook", serif; } +.MATH I { font-family: "Century Schoolbook", serif; font-style: italic } +.BOLDMATH { font-family: "Century Schoolbook", serif; font-weight: bold } + +/* implement both fixed-size and relative sizes */ +SMALL.XTINY { font-size : xx-small } +SMALL.TINY { font-size : x-small } +SMALL.SCRIPTSIZE { font-size : smaller } +SMALL.FOOTNOTESIZE { font-size : small } +SMALL.SMALL { } +BIG.LARGE { } +BIG.XLARGE { font-size : large } +BIG.XXLARGE { font-size : x-large } +BIG.HUGE { font-size : larger } +BIG.XHUGE { font-size : xx-large } + +/* heading styles */ +H1 { } +H2 { } +H3 { } +H4 { } +H5 { } + +/* mathematics styles */ +DIV.displaymath { } /* math displays */ +TD.eqno { } /* equation-number cells */ + + +/* document-specific styles come next */ +DIV.navigation { } +DIV.center { } +SPAN.sc { } +DIV.quote { } +PRE.preform { } +SPAN.textit { font-style: italic } +SPAN.arabic { } diff --git a/doc/ionnotes/ionnotes.html b/doc/ionnotes/ionnotes.html new file mode 100644 index 0000000..27c31f2 --- /dev/null +++ b/doc/ionnotes/ionnotes.html @@ -0,0 +1,172 @@ + + + + + +Ion: Notes for the module and patch writer + + + + + + + + + + + + + + + + + + + +

    +

    Ion: Notes for the module and patch writer

    +
    + +

    Tuomo Valkonen

    +

    tuomov at iki.fi

    +
    + +

    +Ion: Notes for the module and patch writer +
    +Copyright © 2003-2004 Tuomo Valkonen. + +

    +This document is free; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +

    +This document is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +section entitled ''GNU General Public License'' for more details. + +

    + +

    +
    + +

    + +

    Abstract:

    +
    + This document is an unorganized collection of notes for + those who want to write modules or patches to Ion. +
    +

    + +

    +


    + + + + + +

    + + + diff --git a/doc/ionnotes/labels.pl b/doc/ionnotes/labels.pl new file mode 100644 index 0000000..2c1867a --- /dev/null +++ b/doc/ionnotes/labels.pl @@ -0,0 +1,69 @@ +# LaTeX2HTML 2002-2-1 (1.71) +# Associate labels original text with physical files. + + +$key = q/sec:objects/; +$external_labels{$key} = "$URL/" . q|node2.html|; +$noresave{$key} = "$nosave"; + +$key = q/fig:parentship/; +$external_labels{$key} = "$URL/" . q|node2.html|; +$noresave{$key} = "$nosave"; + +$key = q/sec:supptypes/; +$external_labels{$key} = "$URL/" . q|node4.html|; +$noresave{$key} = "$nosave"; + +$key = q/sec:calling/; +$external_labels{$key} = "$URL/" . q|node4.html|; +$noresave{$key} = "$nosave"; + +$key = q/fig:managership/; +$external_labels{$key} = "$URL/" . q|node2.html|; +$noresave{$key} = "$nosave"; + +$key = q/sec:exporting/; +$external_labels{$key} = "$URL/" . q|node4.html|; +$noresave{$key} = "$nosave"; + +$key = q/fig:classhierarchy/; +$external_labels{$key} = "$URL/" . q|node2.html|; +$noresave{$key} = "$nosave"; + +1; + + +# LaTeX2HTML 2002-2-1 (1.71) +# labels from external_latex_labels array. + + +$key = q/sec:objects/; +$external_latex_labels{$key} = q|1|; +$noresave{$key} = "$nosave"; + +$key = q/fig:parentship/; +$external_latex_labels{$key} = q|2|; +$noresave{$key} = "$nosave"; + +$key = q/sec:supptypes/; +$external_latex_labels{$key} = q|3.1|; +$noresave{$key} = "$nosave"; + +$key = q/sec:calling/; +$external_latex_labels{$key} = q|3.3|; +$noresave{$key} = "$nosave"; + +$key = q/fig:managership/; +$external_latex_labels{$key} = q|3|; +$noresave{$key} = "$nosave"; + +$key = q/sec:exporting/; +$external_latex_labels{$key} = q|3.2|; +$noresave{$key} = "$nosave"; + +$key = q/fig:classhierarchy/; +$external_latex_labels{$key} = q|1|; +$noresave{$key} = "$nosave"; + +1; + diff --git a/doc/ionnotes/next.png b/doc/ionnotes/next.png new file mode 100644 index 0000000000000000000000000000000000000000..1628652aac7cff4b24228abd9908c320c6026a7c GIT binary patch literal 245 zcmVQ2aP`rsfsL)H8>rF50IrEKQ7U2mrM!9=R9*Dw1X+@W9RTx^a-#5T6$bi^4|h!q3s vo9V3RdqZP$JTTh9!JZ)`voZLAB<&|;0_>J~#QNZkD16SAR=O4@7anKqmC5&ngT;#rH1+qK z20S~FlZynq-MY=T{~%>KNs884sExd44P^%bf!zNDPxW#x21^f><@$FgzM-=J5cLN5 WGDyh~oC>l40000 + + + + +Contents + + + + + + + + + + + + + + + + + + + + + +
    + +

    +Contents +

    + + + + +

    + +

    +


    + + + diff --git a/doc/ionnotes/node2.html b/doc/ionnotes/node2.html new file mode 100644 index 0000000..e25fd2d --- /dev/null +++ b/doc/ionnotes/node2.html @@ -0,0 +1,444 @@ + + + + + +1 Class and object hierarchies + + + + + + + + + + + + + + + + + + + + + + +Subsections + + + +
    + +

    + +
    +1 Class and object hierarchies +

    + +

    +While Ion does not not have a truly object-oriented design +1, +things that appear on the computer screen are, however, quite +naturally expressed as such ''objects''. Therefore Ion implements +a rather primitive OO system for these screen objects and some +other things. + +

    +It is essential for the module writer to learn this object +system, but also people who write their own binding configuration files +necessarily come into contact with the class and object hierarchies +- you need to know which binding setup routines apply where, +and what functions can be used as handlers in which bindings. +It is the purpose of this section to attempt to explain these +hierarchies. If you do not wish the read the full section, at least +read the summary at the end of it, so that you understand the very +basic relations. + +

    +For simplicity we consider only the essential-for-basic-configuration +Ioncore, mod_tiling and mod_query classes. +See Appendix [*] for the full class hierachy visible +to Lua side. + +

    + +

    +1.1 Class hierarchy +

    + +

    +One of the most important principles of object-oriented design methodology +is inheritance; roughly how classes (objects are instances of classes) +extend on others' features. Inheritance gives rise to class hierarchy. +In the case of single-inheritance this hierarchy can be expressed as a +tree where the class at the root is inherited by all others below it +and so on. Figure 1 lists out the Ion class +hierarchy and below we explain what features of Ion the classes +implement. + +

    + +

    + + + +
    Figure 1: +Partial Ioncore, mod_tiling and mod_query + class hierarchy.
    +    Obj
    +     |-->WRegion
    +     |    |-->WClientWin
    +     |    |-->WWindow
    +     |    |    |-->WRootWin
    +     |    |    |-->WMPlex
    +     |    |    |    |-->WScreen
    +     |    |    |    |-->WFrame
    +     |    |    |-->WInput (mod_query)
    +     |    |         |-->WEdln (mod_query)
    +     |    |         |-->WMessage (mod_query)
    +     |    |-->WGroup
    +     |    |    |-->WGroupWS
    +     |    |    |-->WGroupCW
    +     |    |-->WTiling (mod_tiling)
    +     |-->WSplit (mod_tiling)
    +
    +
    + +

    +The core classes: + +

    +

    +
    Obj
    +
    + Is the base of Ion's object system. + +

    +

    +
    WRegion
    +
    + is the base class for everything corresponding to something on the + screen. Each object of type WRegion has a size and position + relative to the parent WRegion. While a big part of Ion + operates on these instead of more specialised classes, WRegion + is a ''virtual'' base class in that there are no objects of ''pure'' + type WRegion; all concrete regions are objects of some class + that inherits WRegion. + +

    +

    +
    WClientWin
    +
    is a class for + client window objects, the objects that window managers are + supposed to manage. + +

    +

    +
    WWindow
    +
    is the base class for all + internal objects having an X window associated to them + (WClientWins also have X windows associated to them). + +

    +

    +
    WRootWin
    +
    is the class for + root windows of X screens. + Note that an ''X screen'' or root window is not necessarily a + single physical screen as a root window + may be split over multiple screens when multi-head extensions + such as Xinerama are used. (Actually there + can be only one WRootWin when Xinerama is used.) + +

    +

    +
    WMPlex
    +
    is a base class for all regions that''multiplex'' + other regions. This means that of the regions managed by the multiplexer, + only one can be displayed at a time. Classes that inhereit WMPlex + include screens and frames. + +

    +

    +
    WScreen
    +
    is the class for objects + corresponding to physical screens. Screens may share a root + window when Xinerama multihead extensions are used as explained + above. + +

    +

    +
    WFrame
    +
    is the class for frames. + While most Ion's objects have no graphical presentation, frames basically + add to WMPlexes the decorations around client windows + (borders, tabs). + +

    +

    +
    WGroup
    +
    is the base class for groups. + Particular types of groups are workspaces + (WGroupWS) + and groups of client windows + (WGroupCW). +
    +
    + +

    +Classes implemented by the mod_tiling module: + +

    +

    +
    WTiling
    +
    is the class for tilings + of frames. + +
    +
    WSplit
    +
    (or, more specifically, classes + that inherit it) encode the WTiling tree structure. +
    +
    + +

    +Classes implemented by the mod_query module: + +

    +

    +
    WInput
    +
    is a virtual base class for the + two classes below. + +
    +
    WEdln
    +
    is the class for the ''queries'', + the text inputs that usually appear at bottoms of frames and sometimes + screens. Queries are the functional equivalent of ''mini buffers'' in + many text editors. + +
    +
    WMessage
    +
    implements the boxes for + warning and other messages that Ion may wish to display to the user. + These also usually appear at bottoms of frames. +
    +
    + +

    +There are also some other ''proxy'' classes that do not refer +to objects on the screen. The only important one of these for +basic configuration is WMoveresMode that is used for +binding callbacks in the move and resize mode. + +

    + +

    +1.2 Object hierarchies: WRegion parents and managers +

    + +

    + +

    +1.2.1 Parent-child relations +

    +Each object of type WRegion has a parent and possibly a manager +associated to it. The parent for an object is always a +WWindow and for WRegion with an X window (WClientWin, +WWindow) the parent WWindow is given by the same relation of +the X windows. For other WRegions the relation is not as clear. +There is generally very few restrictions other than the above on the +parent--child relation but the most common is as described in +Figure 2. + +

    + +

    + + + +
    Figure 2: +Most common parent-child relations
    +    WRootWins
    +     |-->WScreens
    +          |-->WGroupWSs
    +          |-->WTilings
    +          |-->WClientWins in full screen mode
    +          |-->WFrames
    +               |-->WGroupCWs
    +               |-->WClientWins
    +               |-->WFrames for transients
    +               |-->a possible WEdln or WMessage
    +
    +
    + +

    +WRegions have very little control over their children as a parent. +The manager WRegion has much more control over its +managed WRegions. Managers, for example, handle resize requests, +focusing and displaying of the managed regions. Indeed the manager--managed +relationship gives a better picture of the logical ordering of objects on +the screen. Again, there are generally few limits, but the most common +hierarchy is given in Figure 3. Note that sometimes +the parent and manager are the same object and not all objects may have +a manager (e.g. the dock in the dock module at the time of writing this) +but all have a parent-a screen if not anything else. + +

    + +

    +1.2.2 Manager-managed relations +

    + +

    + +

    + + + +
    Figure 3: +Most common manager-managed relations
    +    WRootWins
    +     |-->WScreens
    +          |-->WGroupCWs for full screen WClientWins
    +          |    |-->WClientWins
    +          |    |-->WFrames for transients (dialogs)
    +          |         |--> WClientWin
    +          |-->WGroupWSs for workspaces
    +          |    |-->WTiling
    +          |    |    |-->possibly a WEdln, WMessage or WMenu
    +          |    |    |-->WFrames
    +          |    |         |-->WGroupCWs (with contents as above)
    +          |    |-->WFrames for floating content
    +          |-->WFrames for sticky stuff, such as the scratchpad
    +
    +
    + +

    +Note that a workspace can manage another workspace. This can be +achieved with the attach_new function, and allows you to nest +workspaces as deep as you want. + +

    + +

    +1.3 Summary +

    + +

    +In the standard setup, keeping queries, messages and menus out of +consideration: + +

    + +

      +
    • The top-level objects that matter are screens and they correspond + to physical screens. The class for screens is WScreen. +
    • +
    • Screens contain (multiplex) groups (WGroup) and other + objects, such as WFrames. Some of these are mutually exclusive + to be viewed at a time. +
    • +
    • Groups of the specific kind WGroupWS often contain a + WTiling tiling for tiling frames (WFrame), but + groups may also directly contain floating frames. +
    • +
    • Frames are the objects with decorations such as tabs and borders. + Frames contain (multiplex) among others (groups of) client windows, + to each of which corresponds a tab in the frame's decoration. Only + one client window (or other object) can be shown at a time in each + frame. The class for client windows is WClientWin. +
    • +
    + +

    +


    Footnotes

    +
    +
    ... design1
    +
    the author doesn't like such artificial designs + +
    +
    + + + + + diff --git a/doc/ionnotes/node3.html b/doc/ionnotes/node3.html new file mode 100644 index 0000000..4f17c4e --- /dev/null +++ b/doc/ionnotes/node3.html @@ -0,0 +1,121 @@ + + + + + +2 Object system implementation + + + + + + + + + + + + + + + + + + + + + + +

    +2 Object system implementation +

    + +

    +First, to get things clear, what are considered objects here are C +structures containing a properly initialized +structure defined in ioncore/obj.h as the first element (or the +first element of the structure which is the first element and so on which +gives rise to inheritance). The WObj structure contains a pointer +to a WObjDescr class type info structure and +a list of so called ''watches''. The WObjDescr structure simply +lists the class name, a table of dynamic functions and a pointer to +deinitialisation function (or ''destructor''). + +

    +Ion does not do any reference counting, garbage collecting or other +fancy things related to automatic safe freeing of objects with its +simplistic object system. Instead special watches (the WWatch + structure) may be used to create safe references to +objects that might be destroyed during the time the specific pointer is +needed. When an object is destroyed, its list of watches is processed, +setting the pointers in the watches to NULL and the watch handlers for +each watch are called. + +

    + +

    + +

    + + + + diff --git a/doc/ionnotes/node4.html b/doc/ionnotes/node4.html new file mode 100644 index 0000000..037e8bc --- /dev/null +++ b/doc/ionnotes/node4.html @@ -0,0 +1,310 @@ + + + + + +3 The Lua interface + + + + + + + + + + + + + + + + + + + + + + +Subsections + + + +
    + +

    +3 The Lua interface +

    + +

    +This section finally describes the implementation details and how modules +should us the Lua interface. First, in section 3.1 +we look at types supported by the interface, how objects are passed to Lua +code and how Lua tables should be accessed from Ion and modules. In section +3.2 the methods for exporting functions and how they +are called from Lua are explained and in section 3.3 the +method for calling Lua functions is explained. + +

    + +

    + +
    +3.1 Supported types +

    + +

    +The following types are supported in passing parameters between the +C side of Ion and Lua: + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Identifier + characterC typeDescription
    iintInteger
    schar*String
    Sconst char*Constant string
    ddouble 
    bbool 
    tExtlTabReference to Lua table
    fExltFnReference to Lua function.
    oAny WObj* 
    + +

    +The difference between identifiers 's' and 'S' is that constant +strings as return values are not free'd by the level 1 call handler +(see below) after passing to Lua (lua_pushstring always makes a +copy) unlike normal strings. String parameters are always assumed to be +the property of the caller and thus implicitly const. + +

    +Likewise, if a reference to 't' or 'f' is wished to be stored +beyond the lifetime of a function receiving such as an argument, a new +reference should be created with extl_ref_table/fn. +References can be free'd with +extl_unref_table/fn. References gotten as return values with +the extl_table_get (how these work should be self-explanatory!) +functions are property of the caller and should be unreferenced with the +above-mentioned functions when no longer needed. +The functions extl_fn/table_none() +return the equivalent of NULL. + +

    +WObjs are passed to Lua code with WWatch userdatas pointing to +them so the objects can be safely deleted although Lua code might still be +referencing them. (This is why SWIG or tolua would not have helped in +creating the interface: extra wrappers for each function would still +have been needed to nicely integrate into Ion's object system. Even in +the case that Ion was written in C++ this would be so unless extra bloat +adding pointer-like objects were used everywhere instead of pointers.) +It may be sometimes necessary check in Lua code that a value known to +be an Ion WObj is of certain type. This can be accomplished with +obj_is(obj, "typename"). obj_typename(obj) returns type +name for a WObj. + +

    + +

    + +
    +3.2 Exporting functions +

    + +

    +Exported functions (those available to the extension language) are +defined by placing EXTL_EXPORT before the function implementation +in the C source. The script mkexports.pl is then used to automatically +generate exports.c from the source files if +MAKE_EXPORTS=modulename +is specified in the Makefile. All pointers with type beginning with a 'W' +are assumed to be pointers to something inheriting WObj. In +addition to a table of exported functions and second level call handlers +for these, exports.c will contain two functions +module_register_exports() and +module_unregister_exports() that should then be called in module +initialisation and deinitialisation code. + +

    +You've seen the terms level 1 and 2 call handler mentioned above. + +The Lua support code uses two so called call handlers to convert and check +the types of parameters passed from Lua to C and back to Lua. The first +one of these call handlers is the same for all exported functions and +indeed lua sees all exported as the same C function (the L1 call handler) +but with different upvalues passing a structure describing the actual +function and the second level call handler. The L1 call handler checks +that the parameters received from Lua match a template given as a string +of the identifier characters defined above. If everything checks out ok, +the parameters are then put in an array of C unions that can contain +anyof these known types and the L2 call handler is called. + +

    +The L2 call handler (which is automatically generated by the mkexports.pl +script) for each exported function checks that the passed WObjs +are of the more refined type required by the function and then calls the +actual function. While the WObj checking could be done in the L1 handler +too, the L2 call handlers are needed because we may not know how the target +platform passes each parameter type to the called function. Thefore we +must let the C compiler generate the code to convert from a simple and +known enough parameter passing method (the unions) to the actual +parameter passing method. When the called function returns everything +is done in reverse order for return values (only one return value is +supported by the generated L2 call handlers). + +

    + +

    + +
    +3.3 Calling Lua functions and code +

    + +

    +The functions +extl_call, +extl_call_named, +extl_dofile and +extl_dostring +call a referenced function (ExtlFn), named function, execute a +string and a file, respectively. The rest of the parameters for all these +functions are similar. The 'spec' argument is a string of identifier +characters (see above) describing the parameters to be passed. These +parameters follow after 'rspec'. For dofile and dostring these parameters +are passed in the global table arg (same as used for program command +lien parameters) and for functions as you might expect. The parameter +'rspec' is a similar description of return values. Pointers to variables +that should be set to the return values follow after the input values. +The return value of all these functions tells if the call and parameter +passing succeeded or not. + +

    +Sometimes it is necessary to block calls to all but a limited set of +Ion functions. This can be accomplished with +extl_set_safelist. +The parameter to this function is a NULL-terminated array of strings +and the return value is a similar old safelist. +The call extl_set_safelist(NULL) removes any safelist and allows +calls to all exported functions. + +

    + +

    +3.4 Miscellaneous notes +

    + +

    +Configuration files should be read as before with the function +read_config_for +except that the list of known options is no longer present. + +

    +Winprops are now stored in Lua tables and can contain arbitrary +properties. The 'proptab' entry in each WClientWin is a reference +to a winprop table or extl_table_none() if such does not exist +and properties may be read with the extl_table_gets functions. +(It is perfectly legal to pass extl_table_none() references to +extl_table_get*.) + +

    + +

    + + + + diff --git a/doc/ionnotes/node5.html b/doc/ionnotes/node5.html new file mode 100644 index 0000000..51c46f8 --- /dev/null +++ b/doc/ionnotes/node5.html @@ -0,0 +1,188 @@ + + + + + +4 Miscellaneous design notes + + + + + + + + + + + + + + + + + + + + + + +Subsections + + + +
    + +

    +4 Miscellaneous design notes +

    + +

    + +

    +4.1 Destroying WObj:s +

    + +

    +To keep Ion's code as simple as possible yet safe, there are restrictions +when the WObj +destroy_obj +function that calls watches, the deinit routine and frees memory may +be called directly. In all other cases the +defer_destroy +function should be used to defer the call of destroy_obj until +Ioncore returns to its main event loop. + +

    +Calling the destroy_obj function directly is allowed in the +following cases: + +

      +
    • In the deinit handler for another object. Usually managed objects + are destroyed this way. +
    • +
    • The object was created during the current call to the function + that wants to get rid of the object. This is the case, for example, + when the function created a frame to manage some other object but for + some reason failed to reparent the object to this frame. +
    • +
    • In a deferred action handler set with + defer_action. + Like deferred destroys, other deferred actions are called when + Ioncore has returned to the main loop. +
    • +
    • You are absolute sure that C code outside your code has no + references to the object. +
    • +
    + +

    +If there are no serious side effects from deferring destroying the +object or you're unsure whether it is safe to destroy the object +immediately, use defer_destroy. + +

    + +

    +4.2 The types char* and const char* as function + parameters and return values +

    + +

    +The following rules should apply to using strings as return values and +parameters to functions. + +

    + + + + + + + + + + + + + +
    TypeReturn valueParameter
    const char*The string is owned by the called function + and the caller is only quaranteed short-term read access to the + string.The called function may only read the string during its execution. + For further reference a copy must be made.
    char*The string is the caller's responsibility and it + must free it when no longer needed.The called function may modify the string but the ''owner'' of + the string is case-dependant.
    + +

    + +

    + +

    + + + + diff --git a/doc/ionnotes/node6.html b/doc/ionnotes/node6.html new file mode 100644 index 0000000..8c20249 --- /dev/null +++ b/doc/ionnotes/node6.html @@ -0,0 +1,285 @@ + + + + + +5 C coding style + + + + + + + + + + + + + + + + + + + + + + +Subsections + + + +
    + +

    +5 C coding style +

    + +

    +If you want to submit patches to Ion, you MUST follow my coding +style, even if you think it is the root of all evil. We don't want +the code to be an incomprehensible mess of styles and I have better +things to do than fix other people's style to match mine. The style +should be obvious by studying the source, but here's a list of some +things to take note of. + +

    + +

    +5.1 Whitespace +

    + +

    + +

      +
    • Indentations of 4 with tab size=4. + +

      +

    • +
    • No extra spaces between operators, delimiters etc. except + +
        +
      • around logical and, or (&&, ||) +
      • +
      • around the conditional a ? b : c +
      • +
      • after commas and semicolons + +
      • +
      + In my opinion this helps pointing out arithmetic or other + expressions within logical expressions or parameter lists. + +

      +

    • +
    • All kinds of labels are out-tended to the level of the higher + level block. For example: + +

      +

          
      +void foo()
      +{
      +again:
      +    switch(asdf){
      +    case 1:
      +        ...
      +        break;
      +    default:
      +        ...
      +        break;
      +    }
      +}
      +
      +
    • +
    + +

    + +

    +5.2 Braces +

    + +

    + +

      +
    • Opening brace is at the end of the line, except in function + bodies, where it is at the beginning of the line following + the definition. + +

      +

    • +
    • Never put the body of a control statement on the same line + with the statement (e.g. if(foo){ bar() }). + +

      +For example, the block +

          
      +void foo(int a, int b)
      +{
      +    if(a==b && c+d==e){
      +        ...
      +    }
      +}
      +
      + +

      +has correct style while the block + +

      +

         
      +void foo(int a,int b) {
      +    if (a == b && c + d == e) {
      +        ...
      +    }
      +}
      +
      + +

      +does not. + +

      +

    • +
    • The else keyword follows immediately after the closing brace of + previous if, if any. (This might change so I don't care if you put + it on the next line.) + +

      +

    • +
    • I have used the convention that control statement bodies containing + a single statement do not need braces around the block if, in case of + the if all the blocks in if ... else if ... else + contain just one statement. If you want to, just use braces in every + case. +
    • +
    + +

    + +

    +5.3 Names +

    + +

    + +

      +
    • Function and variable names only have lower case letters. Type + names are in mixed case while constants and macros (#defines) + are in upper case letters. +
    • +
    + +

    + +

    +5.4 Miscellaneous +

    + +

    + +

      +
    • In the definition of a pointer variable, the asterisk is attached + to the variable name: char *s;. (One could claim this an + exception to the second rule.) + +

      +

    • +
    • You might optionally want to use Jed's foldings to group blocks + of related code in a file to keep it organized: + +

      +

          
      +/*{{{ Many related functions */
      +	
      +void code()
      +{
      +    ...	
      +}
      +
      +...
      +
      +/*}}}*/
      +
      +
    • +
    + +

    +I think that's mostly it. Study the source when in doubt. + +

    + +

    + +

    + + + + diff --git a/doc/ionnotes/node7.html b/doc/ionnotes/node7.html new file mode 100644 index 0000000..7e36403 --- /dev/null +++ b/doc/ionnotes/node7.html @@ -0,0 +1,597 @@ + + + + + +A. The GNU General Public License + + + + + + + + + + + + + + + + + + + + + + +Subsections + + + +
    + +

    +A. The GNU General Public License +

    + +

    +

    +
    +

    +

    Version 2, June 1991 +
    +

    +

    Copyright © 1989, 1991 Free Software Foundation, Inc. +
    +

    +

    +

    +
    +

    +

    +

    59 Temple Place - Suite 330, Boston, MA 02111-1307, USA +
    +

    +

    +

    +
    +

    +

    +

    Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. + + +
    + +

    +

    +Preamble + +
    + +

    +The licenses for most software are designed to take away your freedom to +share and change it. By contrast, the GNU General Public License is +intended to guarantee your freedom to share and change free software--to +make sure the software is free for all its users. This General Public +License applies to most of the Free Software Foundation's software and to +any other program whose authors commit to using it. (Some other Free +Software Foundation software is covered by the GNU Library General Public +License instead.) You can apply it to your programs, too. + +

    +When we speak of free software, we are referring to freedom, not price. +Our General Public Licenses are designed to make sure that you have the +freedom to distribute copies of free software (and charge for this service +if you wish), that you receive source code or can get it if you want it, +that you can change the software or use pieces of it in new free programs; +and that you know you can do these things. + +

    +To protect your rights, we need to make restrictions that forbid anyone to +deny you these rights or to ask you to surrender the rights. These +restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + +

    +For example, if you distribute copies of such a program, whether gratis or +for a fee, you must give the recipients all the rights that you have. You +must make sure that they, too, receive or can get the source code. And +you must show them these terms so they know their rights. + +

    +We protect your rights with two steps: (1) copyright the software, and (2) +offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + +

    +Also, for each author's protection and ours, we want to make certain that +everyone understands that there is no warranty for this free software. If +the software is modified by someone else and passed on, we want its +recipients to know that what they have is not the original, so that any +problems introduced by others will not reflect on the original authors' +reputations. + +

    +Finally, any free program is threatened constantly by software patents. +We wish to avoid the danger that redistributors of a free program will +individually obtain patent licenses, in effect making the program +proprietary. To prevent this, we have made it clear that any patent must +be licensed for everyone's free use or not licensed at all. + +

    +The precise terms and conditions for copying, distribution and +modification follow. + +

    +

    +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND + MODIFICATION + +
    + +

    + +

      +
    1. +This License applies to any program or other work which contains a notice +placed by the copyright holder saying it may be distributed under the +terms of this General Public License. The ``Program'', below, refers to +any such program or work, and a ``work based on the Program'' means either +the Program or any derivative work under copyright law: that is to say, a +work containing the Program or a portion of it, either verbatim or with +modifications and/or translated into another language. (Hereinafter, +translation is included without limitation in the term ``modification''.) +Each licensee is addressed as ``you''. + +

      +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + +

      +

    2. +
    3. You may copy and distribute verbatim copies of the Program's source + code as you receive it, in any medium, provided that you conspicuously + and appropriately publish on each copy an appropriate copyright notice + and disclaimer of warranty; keep intact all the notices that refer to + this License and to the absence of any warranty; and give any other + recipients of the Program a copy of this License along with the Program. + +

      +You may charge a fee for the physical act of transferring a copy, and you +may at your option offer warranty protection in exchange for a fee. + +

      +

    4. +
    5. +You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + +

      + +

        +
      1. +You must cause the modified files to carry prominent notices stating that +you changed the files and the date of any change. + +

        +

      2. +
      3. +You must cause any work that you distribute or publish, that in +whole or in part contains or is derived from the Program or any +part thereof, to be licensed as a whole at no charge to all third +parties under the terms of this License. + +

        +

      4. +
      5. If the modified program normally reads commands interactively +when run, you must cause it, when started running for such +interactive use in the most ordinary way, to print or display an +announcement including an appropriate copyright notice and a +notice that there is no warranty (or else, saying that you provide +a warranty) and that users may redistribute the program under +these conditions, and telling the user how to view a copy of this +License. (Exception: if the Program itself is interactive but +does not normally print such an announcement, your work based on +the Program is not required to print an announcement.) + +

        +

      6. +
      + +

      +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +

      +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +

      +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + +

      +

    6. +
    7. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + +

      + +

        +
      1. +Accompany it with the complete corresponding machine-readable +source code, which must be distributed under the terms of Sections +1 and 2 above on a medium customarily used for software interchange; or, + +

        +

      2. +
      3. +Accompany it with a written offer, valid for at least three +years, to give any third party, for a charge no more than your +cost of physically performing source distribution, a complete +machine-readable copy of the corresponding source code, to be +distributed under the terms of Sections 1 and 2 above on a medium +customarily used for software interchange; or, + +

        +

      4. +
      5. +Accompany it with the information you received as to the offer +to distribute corresponding source code. (This alternative is +allowed only for noncommercial distribution and only if you +received the program in object code or executable form with such +an offer, in accord with Subsection b above.) + +

        +

      6. +
      + +

      +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +

      +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + +

      +

    8. +
    9. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + +

      +

    10. +
    11. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + +

      +

    12. +
    13. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + +

      +

    14. +
    15. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +

      +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +

      +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +

      +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + +

      +

    16. +
    17. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + +

      +

    18. +
    19. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +

      +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and ``any +later version'', you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + +

      +

    20. +
    21. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + +

      +

      + +NO WARRANTY + + +
      + +

      +

    22. +
    23. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM ``AS IS'' WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + +

      +

    24. +
    25. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + +

      +

    26. +
    + +

    +

    +END OF TERMS AND CONDITIONS + +
    + +

    + +

    + +

    +Appendix: How to Apply These Terms to Your New Programs +

    + +

    +If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these +terms. + +

    +To do so, attach the following notices to the program. It is safest to + attach them to the start of each source file to most effectively convey + the exclusion of warranty; and each file should have at least the + ``copyright'' line and a pointer to where the full notice is found. + +

    +

    +one line to give the program's name and a brief idea of what it does. +
    +Copyright (C) yyyy name of author +
    +

    +

    This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. +
    +

    +

    This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +
    +

    +

    You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +
    + +

    +Also add information on how to contact you by electronic and paper mail. + +

    +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + +

    +

    +Gnomovision version 69, Copyright (C) yyyy name of author +
    +Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. +
    +This is free software, and you are welcome to redistribute it +under certain conditions; type `show c' for details. + +
    + +

    +The hypothetical commands show w and show c should show the +appropriate parts of the General Public License. Of course, the commands +you use may be called something other than show w and show c; +they could even be mouse-clicks or menu items--whatever suits your +program. + +

    +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a ``copyright disclaimer'' for the program, if +necessary. Here is a sample; alter the names: + +

    +

    +Yoyodyne, Inc., hereby disclaims all copyright interest in the program +
    `Gnomovision' (which makes passes at compilers) written by James Hacker. +
    +

    +

    signature of Ty Coon, 1 April 1989 +
    +Ty Coon, President of Vice + +
    + +

    +This General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications +with the library. If this is what you want to do, use the GNU Library +General Public License instead of this License. + +

    + +

    + + + + diff --git a/doc/ionnotes/node8.html b/doc/ionnotes/node8.html new file mode 100644 index 0000000..0ec574a --- /dev/null +++ b/doc/ionnotes/node8.html @@ -0,0 +1,139 @@ + + + + + +Index + + + + + + + + + + + + + + + + + + + + + +
    + +

    +Index +


    +
    call handler + : 3.2 +
    defer_action + : 4.1 +
    defer_destroy + : 4.1 +
    destroy_obj + : 4.1 +
    extl_call + : 3.3 +
    extl_call_named + : 3.3 +
    extl_dofile + : 3.3 +
    extl_dostring + : 3.3 +
    extl_set_safelist + : 3.3 +
    ExtlFn + : 3.1 +
    ExtlTab + : 3.1 +
    manager + : 1.2.1 +
    Obj + : 1.1 +
    parent + : 1.2.1 +
    read_config_for + : 3.4 +
    root window + : 1.1 +
    screen
    +
    physical : 1.1 +
    X : 1.1 +
    +
    WClientWin + : 1.1 +
    WEdln + : 1.1 +
    WFrame + : 1.1 +
    WGroup + : 1.1 +
    WGroupCW + : 1.1 +
    WGroupWS + : 1.1 +
    WInput + : 1.1 +
    WMessage + : 1.1 +
    WObj + : 2 +
    WObjDescr + : 2 +
    WRegion + : 1.1 +
    WRootWin + : 1.1 +
    WScreen + : 1.1 +
    WSplit + : 1.1 +
    WTiling + : 1.1 +
    WWatch + : 2 +
    WWindow + : 1.1 +
    Xinerama + : 1.1 + +
    + +

    +


    + + + diff --git a/doc/ionnotes/node9.html b/doc/ionnotes/node9.html new file mode 100644 index 0000000..a4ce07f --- /dev/null +++ b/doc/ionnotes/node9.html @@ -0,0 +1,77 @@ + + + + + +About this document ... + + + + + + + + + + + + + + + + + + + + +

    +About this document ... +

    + Ion: Notes for the module and patch writer

    +This document was generated using the +LaTeX2HTML translator Version 2002-2-1 (1.71) +

    +Copyright © 1993, 1994, 1995, 1996, +Nikos Drakos, +Computer Based Learning Unit, University of Leeds. +
    +Copyright © 1997, 1998, 1999, +Ross Moore, +Mathematics Department, Macquarie University, Sydney. +

    +The command line arguments were:
    + latex2html -show_section_numbers -short_index -local_icons -noaddress -up_url http://iki.fi/tuomov/ion/ -up_title 'Ion homepage' -nofootnode -split 4 ionnotes +

    +The translation was initiated by tuomov on 2006-12-23 +


    + + + diff --git a/doc/ionnotes/prev.png b/doc/ionnotes/prev.png new file mode 100644 index 0000000000000000000000000000000000000000..e60b8b4073572dcd83b07c60b082ea6e2ba394c9 GIT binary patch literal 279 zcmV+y0qFjTP)fWJ4@hJr9=)Yx5|PDs2VC9?nk002ovPDHLkV1i^Ya=-up literal 0 HcmV?d00001 diff --git a/doc/ionnotes/prev_g.png b/doc/ionnotes/prev_g.png new file mode 100644 index 0000000000000000000000000000000000000000..476d9568c900e2ada6c2019b67eeee166a5f1288 GIT binary patch literal 327 zcmV-N0l5B&P)18~ zIpil5yY|hg&aw;rvXQ~olHp&x|G5Aw{ug* Z|8M28X+2RX!WaMm002ovPDHLkV1gF^iYx#C literal 0 HcmV?d00001 diff --git a/doc/ionnotes/up.png b/doc/ionnotes/up.png new file mode 100644 index 0000000000000000000000000000000000000000..3937e168f44bc997766dbe3b9383bd1db44f094d GIT binary patch literal 211 zcmeAS@N?(olHy`uVBq!ia0vp^Qa~)h#LU3J`0lmo0U(Dbz$e6Y|Ni~?`T5VEKaXZ$ zILr2UCs3TRB*-tA!Qt7BG$5zc)5S5QVoq$YAz!lrkL&!DV=`*aVJGv=2TrrsUKR?`=XF~0x$5q(BOL&qeQ?X^G$FnGH9 KxvX3 + \@toodeep + \else + \advance\@itemdepth \@ne + \edef\@itemitem{labelitem\romannumeral\the\@itemdepth}% + \list{\csname\@itemitem\endcsname}% + {% + \if@revlabel + \def\makelabel##1{\hskip .5\unitindent{\hfil ##1}}\else + %^^^^^^^^^ Changed + \def\makelabel##1{\hfil ##1} + %^^^^^^^^^ Changed + \fi + }% + \fi} + {\global\@ignoretrue \endlist} +\makeatletter +%end{latexonly} diff --git a/doc/objects.tex b/doc/objects.tex new file mode 100644 index 0000000..e751eab --- /dev/null +++ b/doc/objects.tex @@ -0,0 +1,246 @@ + +\section{Class and object hierarchies} +\label{sec:objects} + +While Ion does not not have a truly object-oriented design +\footnote{the author doesn't like such artificial designs}, +things that appear on the computer screen are, however, quite +naturally expressed as such ''objects''. Therefore Ion implements +a rather primitive OO system for these screen objects and some +other things. + +It is essential for the module writer to learn this object +system, but also people who write their own binding configuration files +necessarily come into contact with the class and object hierarchies +-- you need to know which binding setup routines apply where, +and what functions can be used as handlers in which bindings. +It is the purpose of this section to attempt to explain these +hierarchies. If you do not wish the read the full section, at least +read the summary at the end of it, so that you understand the very +basic relations. + +For simplicity we consider only the essential-for-basic-configuration +Ioncore, \file{mod\_tiling} and \file{mod\_query} classes. +See Appendix \ref{app:fullhierarchy} for the full class hierachy visible +to Lua side. + +\subsection{Class hierarchy} + +One of the most important principles of object-oriented design methodology +is inheritance; roughly how classes (objects are instances of classes) +extend on others' features. Inheritance gives rise to class hierarchy. +In the case of single-inheritance this hierarchy can be expressed as a +tree where the class at the root is inherited by all others below it +and so on. Figure \ref{fig:classhierarchy} lists out the Ion class +hierarchy and below we explain what features of Ion the classes +implement. + +\begin{figure} +\begin{htmlonly} +\docode % latex2html kludge +\end{htmlonly} +\begin{verbatim} + Obj + |-->WRegion + | |-->WClientWin + | |-->WWindow + | | |-->WRootWin + | | |-->WMPlex + | | | |-->WScreen + | | | |-->WFrame + | | |-->WInput (mod_query) + | | |-->WEdln (mod_query) + | | |-->WMessage (mod_query) + | |-->WGroup + | | |-->WGroupWS + | | |-->WGroupCW + | |-->WTiling (mod_tiling) + |-->WSplit (mod_tiling) +\end{verbatim} +\caption{Partial Ioncore, \file{mod\_tiling} and \file{mod\_query} + class hierarchy.} +\label{fig:classhierarchy} +\end{figure} + +The core classes: + +\begin{description} + \item[\type{Obj}]\indextype{Obj} + Is the base of Ion's object system. + + \item[\type{WRegion}]\indextype{WRegion} + is the base class for everything corresponding to something on the + screen. Each object of type \type{WRegion} has a size and position + relative to the parent \type{WRegion}. While a big part of Ion + operates on these instead of more specialised classes, \type{WRegion} + is a ''virtual'' base class in that there are no objects of ''pure'' + type \type{WRegion}; all concrete regions are objects of some class + that inherits \type{WRegion}. + + \item[\type{WClientWin}]\indextype{WClientWin} is a class for + client window objects, the objects that window managers are + supposed to manage. + + \item[\type{WWindow}]\indextype{WWindow} is the base class for all + internal objects having an X window associated to them + (\type{WClientWins} also have X windows associated to them). + + \item[\type{WRootWin}]\indextype{WRootWin} is the class for + root windows\index{root window} of X screens\index{screen!X}. + Note that an ''X screen'' or root window is not necessarily a + single physical screen\index{screen!physical} as a root window + may be split over multiple screens when multi-head extensions + such as Xinerama\index{Xinerama} are used. (Actually there + can be only one \type{WRootWin} when Xinerama is used.) + + \item[\type{WMPlex}] is a base class for all regions that''multiplex'' + other regions. This means that of the regions managed by the multiplexer, + only one can be displayed at a time. Classes that inhereit \type{WMPlex} + include screens and frames. + + \item[\type{WScreen}]\indextype{WScreen} is the class for objects + corresponding to physical screens. Screens may share a root + window when Xinerama multihead extensions are used as explained + above. + + \item[\type{WFrame}]\indextype{WFrame} is the class for frames. + While most Ion's objects have no graphical presentation, frames basically + add to \type{WMPlex}es the decorations around client windows + (borders, tabs). + + \item[\type{WGroup}]\indextype{WGroup} is the base class for groups. + Particular types of groups are workspaces + (\type{WGroupWS}\indextype{WGroupWS}) + and groups of client windows + (\type{WGroupCW}\indextype{WGroupCW}). +\end{description} + + +Classes implemented by the \file{mod\_tiling} module: + +\begin{description} + \item[\type{WTiling}]\indextype{WTiling} is the class for tilings + of frames. + \item[\type{WSplit}]\indextype{WSplit} (or, more specifically, classes + that inherit it) encode the \type{WTiling} tree structure. +\end{description} + + +Classes implemented by the \file{mod\_query} module: + +\begin{description} + \item[\type{WInput}]\indextype{WInput} is a virtual base class for the + two classes below. + \item[\type{WEdln}]\indextype{WEdln} is the class for the ''queries'', + the text inputs that usually appear at bottoms of frames and sometimes + screens. Queries are the functional equivalent of ''mini buffers'' in + many text editors. + \item[\type{WMessage}]\indextype{WMessage} implements the boxes for + warning and other messages that Ion may wish to display to the user. + These also usually appear at bottoms of frames. +\end{description} + +There are also some other ''proxy'' classes that do not refer +to objects on the screen. The only important one of these for +basic configuration is \type{WMoveresMode} that is used for +binding callbacks in the move and resize mode. + + +\subsection{Object hierarchies: \type{WRegion} parents and managers} + +\subsubsection{Parent--child relations} +Each object of type \type{WRegion} has a parent and possibly a manager +associated to it. The parent\index{parent} for an object is always a +\type{WWindow} and for \type{WRegion} with an X window (\type{WClientWin}, +\type{WWindow}) the parent \type{WWindow} is given by the same relation of +the X windows. For other \type{WRegion}s the relation is not as clear. +There is generally very few restrictions other than the above on the +parent---child relation but the most common is as described in +Figure \ref{fig:parentship}. + +\begin{figure} +\begin{htmlonly} +\docode % latex2html kludge +\end{htmlonly} +\begin{verbatim} + WRootWins + |-->WScreens + |-->WGroupWSs + |-->WTilings + |-->WClientWins in full screen mode + |-->WFrames + |-->WGroupCWs + |-->WClientWins + |-->WFrames for transients + |-->a possible WEdln or WMessage +\end{verbatim} +\caption{Most common parent--child relations} +\label{fig:parentship} +\end{figure} + +\type{WRegion}s have very little control over their children as a parent. +The manager\index{manager} \type{WRegion} has much more control over its +managed \type{WRegion}s. Managers, for example, handle resize requests, +focusing and displaying of the managed regions. Indeed the manager---managed +relationship gives a better picture of the logical ordering of objects on +the screen. Again, there are generally few limits, but the most common +hierarchy is given in Figure \ref{fig:managership}. Note that sometimes +the parent and manager are the same object and not all objects may have +a manager (e.g. the dock in the dock module at the time of writing this) +but all have a parent--a screen if not anything else. + +\subsubsection{Manager--managed relations} + +\begin{figure} +\begin{htmlonly} +\docode % latex2html kludge +\end{htmlonly} +\begin{verbatim} + WRootWins + |-->WScreens + |-->WGroupCWs for full screen WClientWins + | |-->WClientWins + | |-->WFrames for transients (dialogs) + | |--> WClientWin + |-->WGroupWSs for workspaces + | |-->WTiling + | | |-->possibly a WEdln, WMessage or WMenu + | | |-->WFrames + | | |-->WGroupCWs (with contents as above) + | |-->WFrames for floating content + |-->WFrames for sticky stuff, such as the scratchpad +\end{verbatim} +\caption{Most common manager--managed relations} +\label{fig:managership} +\end{figure} + +Note that a workspace can manage another workspace. This can be +achieved with the \fnref{attach_new} function, and allows you to nest +workspaces as deep as you want. + +%Note how the \type{WClientWin}s managed by \type{WFloatFrame}s don't have +%transients managed by them. This is because WFloatWSs choose to handle +%transients differently (transients are put in separate frames like normal +%windows). + +\subsection{Summary} + +In the standard setup, keeping queries, messages and menus out of +consideration: + +\begin{itemize} + \item The top-level objects that matter are screens and they correspond + to physical screens. The class for screens is \type{WScreen}. + \item Screens contain (multiplex) groups (\type{WGroup}) and other + objects, such as \type{WFrames}. Some of these are mutually exclusive + to be viewed at a time. + \item Groups of the specific kind \type{WGroupWS} often contain a + \type{WTiling} tiling for tiling frames (\type{WFrame}), but + groups may also directly contain floating frames. + \item Frames are the objects with decorations such as tabs and borders. + Frames contain (multiplex) among others (groups of) client windows, + to each of which corresponds a tab in the frame's decoration. Only + one client window (or other object) can be shown at a time in each + frame. The class for client windows is \type{WClientWin}. +\end{itemize} + diff --git a/doc/objectsimpl.tex b/doc/objectsimpl.tex new file mode 100644 index 0000000..ccc440d --- /dev/null +++ b/doc/objectsimpl.tex @@ -0,0 +1,20 @@ +\section{Object system implementation} + +First, to get things clear, what are considered objects here are C +structures containing a properly initialized \type\indextype{WObj} +structure defined in \file{ioncore/obj.h} as the first element (or the +first element of the structure which is the first element and so on which +gives rise to inheritance). The \type{WObj} structure contains a pointer +to a \type{WObjDescr}\indextype{WObjDescr} class type info structure and +a list of so called ''watches''. The \type{WObjDescr} structure simply +lists the class name, a table of dynamic functions and a pointer to +deinitialisation function (or ''destructor''). + +Ion does not do any reference counting, garbage collecting or other +fancy things related to automatic safe freeing of objects with its +simplistic object system. Instead special watches (the \type{WWatch} +\indextype{WWatch} structure) may be used to create safe references to +objects that might be destroyed during the time the specific pointer is +needed. When an object is destroyed, its list of watches is processed, +setting the pointers in the watches to NULL and the watch handlers for +each watch are called. diff --git a/doc/predist.sh b/doc/predist.sh new file mode 100644 index 0000000..31e7405 --- /dev/null +++ b/doc/predist.sh @@ -0,0 +1,39 @@ +#!/bin/sh + +## +## Versioning +## + +pwd=`pwd` +dir=`basename "$pwd"` + +release=`echo "$dir"|sed 's/^.\+-\([^-]\+-[0-9]\+\)$/\1/p; d'` + +if test "$release" == ""; then + echo "Invalid package name $dir." + exit 1 +fi + +## +## Ion path +## + +if test "$ION_PATH" = ""; then + ION_PATH="../ion-${release}" +fi + +## +## Build +## + +set -e + +d=`echo $release|sed 's/[^-]\+-\(....\)\(..\)\(..\)/\1-\2-\3/'` + +perl -p -i -e "s/%%DATE/\\\\date{$d}/" ionconf.tex +sed "s:^TOPDIR=.*:TOPDIR=${ION_PATH}:" Makefile > Makefile.tmp +make -f Makefile.tmp all +make -f Makefile.tmp all-ps +make -f Makefile.tmp clean +rm Makefile.tmp +gzip *.dvi *.ps diff --git a/doc/prelim.tex b/doc/prelim.tex new file mode 100644 index 0000000..c5db5f8 --- /dev/null +++ b/doc/prelim.tex @@ -0,0 +1,58 @@ + +\chapter{Preliminaries: Key concepts and relations} +\label{chap:prelim} + +The purpose of this chapter to explain some of key concepts and +relations you need to understand before reading the following +chapters. These include modules explained in section \ref{sec:modules} +and the Ion class and object hierarchies, section \ref{sec:objects}. + + +\section{Modules} +\label{sec:modules} + +Ion has been designed so that the 'ion' executable only implements some +basic services on top of which very different kinds of window managers +could be build by loading the appropriate 'modules'. On modern system +these modules are simply dynamically loaded \file{.so} libraries. On +more primitive systems, or if you want to squeeze total size of the +executable and libraries, the modules can optionally be statically +linked to the main binary, but must nevertheless be loaded with the +\fnref{dopath} function. Modules may also include Lua code. + +If no modules are loaded, all client windows appear in full screen mode. +To get better window management support, one or more workspace modules +should be loaded. Currently Ion provides the following modules: + +\begin{description} + \item[\file{mod\_tiling}] Tilings for workspaces of the original tiled + Ion kind. + \item[\file{mod\_query}] Queries (for starting programs and so on) + and message boxes. + \item[\file{mod\_menu}] Support for menus, both pull-down and + keyboard-operated in-frame menus. + \item[\file{mod\_statusbar}] Module that implements a statusbar that + can be adaptively embedded in each workspace's layout. + \item[\file{mod\_dock}] Module for docking Window Maker dockapps. + The dock can both float and be embedded as the statusbar. + \item[\file{mod\_sp}] This module implements a scratchpad frame that can + be toggled on/off everywhere. Think of the 'console' in some + first-person shooters. + \item[\file{mod\_mgmtmode}] Support module for implementing ''management + modes'' with a XOR-frame similar to move/resize mode around selected + region. + \item[\file{mod\_sm}] Session management support module. + \emph{Loaded automatically when needed!} +\end{description} + +So-called drawing engines are also implemented as a modules, +but they are not discussed here; see chapter \ref{chap:gr}. + +The stock configuration for the 'ion3' executable loads all of the modules +mentioned above except \file{mod\_dock} and file{mod\_mgmtmode}. +The stock configuration for the 'pwm3' executable (which differs from the +'ion3' executable in a few configuration details, such as Xinerama usage) +loads another set of modules. + + +\input{objects} diff --git a/doc/rapport3.perl b/doc/rapport3.perl new file mode 100644 index 0000000..77b2cbd --- /dev/null +++ b/doc/rapport3.perl @@ -0,0 +1,38 @@ +# rapport3.perl by Tuomo Valkonen, , 2003-05-10 +# +# Implementation of the documentclass for latex2html. Just make some +# sectioning commands saner and load report. +# + +package main; + +# +# Sections should start at H2 instead of the insane default H1. +# + +%standard_section_headings = + ('part' , 'H1' , 'chapter' , 'H1', 'section', 'H2', 'subsection', 'H3' + , 'subsubsection', 'H4', 'paragraph', 'H4', 'subparagraph', 'H5'); + +&generate_sectioning_subs; + +%section_headings = + ('partstar' , 'H1' , 'chapterstar' , 'H1', 'sectionstar', 'H2' + , 'subsectionstar', 'H3', 'subsubsectionstar', 'H4', 'paragraphstar' + , 'H4', 'subparagraphstar', 'H5', %section_headings); + +# +# These should be chapters in a report +# + +%section_headings = + ('tableofcontents', 'H1', 'listoffigures', 'H1', 'listoftables', 'H1' + , 'bibliography', 'H1', 'textohtmlindex', 'H1' + , %standard_section_headings + , %section_headings); + + +&do_require_package("report"); + +1; + diff --git a/doc/statusd.tex b/doc/statusd.tex new file mode 100644 index 0000000..e1fe6b2 --- /dev/null +++ b/doc/statusd.tex @@ -0,0 +1,81 @@ + +\section{Writing \command{ion-statusd} monitors} + +All statusbar meters that do not monitor the internal state of Ion should +go in the separate \command{ion-statusd} program. + +Whenever the user requests a meter \code{\%foo} or \code{\%foo_bar} to be +inserted in a statusbar, \file{mod\_statusbar} asks \command{ion-statusd} to +load \fnref{statusd_foo.lua} on its search path (same as that for Ion-side +scripts). This script should then supply all meters with the initial part +'\code{foo}'. + +To provide this value, the script should simply call \code{statusd.inform} +with the name of the meter and the value as a string. +Additionally the script should provide a 'template' for the meter to +facilitate expected width calculation by \file{mod\_statusbar}, and +may provide a 'hint' for colour-coding the value. The interpretation +of hints depends on the graphical style in use, and currently the +stock styles support the \code{normal}, \code{important} and +\code{critical} hints. + + +In our example of the 'foo monitor', at script init we might broadcast +the template as follows: + +\begin{verbatim} +statusd.inform("foo_template", "000") +\end{verbatim} + +To inform \file{mod\_statusbar} of the actual value of the meter and +indicate that the value is critical if above 100, we might write the +following function: + +\begin{verbatim} +local function inform_foo(foo) + statusd.inform("foo", tostring(foo)) + if foo>100 then + statusd.inform("foo_hint", "critical") + else + statusd.inform("foo_hint", "normal") + end +end +\end{verbatim} + +To periodically update the value of the meter, we must use timers. +First we must create one: + +\begin{verbatim} +local foo_timer=statusd.create_timer() +\end{verbatim} + +Then we write a function to be called whenever the timer expires. +This function must also restart the timer. + +\begin{verbatim} +local function update_foo() + local foo= ... measure foo somehow ... + inform_foo(foo) + foo_timer:set(settings.update_interval, update_foo) +end +\end{verbatim} + +Finally, at the end of our script we want to do the initial +measurement, and set up timer for further measurements: + +\begin{verbatim} +update_foo() +\end{verbatim} + + +If our scripts supports configurable parameters, the following code +(at the beginning of the script) will allow them to be configured in +\file{cfg\_statusbar.lua} and passed to the status daemon and our script: + +\begin{verbatim} +local defaults={ + update_interval=10*1000, -- 10 seconds +} + +local settings=table.join(statusd.get_config("foo"), defaults) +\end{verbatim} diff --git a/doc/tricks.tex b/doc/tricks.tex new file mode 100644 index 0000000..69fe945 --- /dev/null +++ b/doc/tricks.tex @@ -0,0 +1,104 @@ + +\chapter{Scripting} +\label{chap:tricks} + +This chapter documents some additional features of the Ion configuration +and scripting interface that can be used for more advanced scripting than +the basic configuration exlained in chapter \ref{chap:config}. + +\section{Hooks} +\label{sec:hooks} + +Hooks are lists of functions to be called when a certain event occurs. +There are two types of them; normal and ''alternative'' hooks. Normal +hooks do not return anything, but alt-hooks should return a boolean +indicating whether it handled its assigned task succesfully. In the case +that \var{true} is returned, remaining handlers are not called. + +Hook handlers are registered by first finding the hook +with \fnref{ioncore.get_hook} and then calling \fnref{WHook.add} +on the (succesfull) result with the handler as parameter. Similarly +handlers are unregistered with \fnref{WHook.remove}. For example: + +\begin{verbatim} +ioncore.get_hook("ioncore_snapshot_hook"):add( + function() print("Snapshot hook called.") end +) +\end{verbatim} + +In this example the hook handler has no parameters, but many hook +handlers do. The types of parameters for each hook are listed in +the hook reference, section \ref{sec:hookref}. + + +Note that many of the hooks are called in ''protected mode'' and can not +use any functions that modify Ion's internal state. TODO: More detailed +documentation when this is final. + +\section{Referring to regions} + +\subsection{Direct object references} + +All Ion objects are passed to Lua scriptss as 'userdatas', and you may +safely store such object references for future use. The C-side object +may be destroyed while Lua still refers to the object. All exported +functions gracefully fail in such a case, but if you need to explicitly +test that the C-side object still exists, use \fnref{obj_exists}. + +As an example, the following short piece of code implements +bookmarking: + +\begin{verbatim} +local bookmarks={} + +-- Set bookmark bm point to the region reg +function set_bookmark(bm, reg) + bookmarks[bm]=reg +end + +-- Go to bookmark bm +function goto_bookmark(bm) + if bookmarks[bm] then + -- We could check that bookmarks[bm] still exists, if we + -- wanted to avoid an error message. + bookmarks[bm]:goto() + end +end +\end{verbatim} + +\subsection{Name-based lookups} + +If you want to a single non-\type{WClientWin} region with an exact known +name, use \fnref{ioncore.lookup_region}. If you want a list of all regions, +use \fnref{ioncore.region_list}. Both functions accept an optional argument +that can be used to specify that the returned region(s) must be of a more +specific type. Client windows live in a different namespace and for them +you should use the equivalent functions \fnref{ioncore.lookup_clientwin} +and \fnref{ioncore.clientwin_list}. + +To get the name of an object, use \fnref{WRegion.name}. Please be +aware, that the names of client windows reflect their titles and +are subject to changes. To change the name of a non-client window +region, use \fnref{WRegion.set_name}. + + +\section{Alternative winprop selection criteria} + +It is possible to write more complex winprop selection routines than +those described in section \ref{sec:winprops}. To match a particular +winprop using whatever way you want to, just set the \var{match} +field of the winprop to a function that receives the client window +as its sole parameter, and that returns \code{true} if the winprop +matches, and \code{false} otherwise. + +The class, instance and role properties can be obtained with +\fnref{WClientWin.get_ident}, and the title with \fnref{WRegion.name}. +If you want to match against (almost) arbitrary window properties, +have a look at the documentation for the following functions, and +their standard Xlib counterparts: \fnref{ioncore.x_intern_atom} +(XInternAtom), \fnref{ioncore.x_get_window_property} (XGetWindowProperty), +and \fnref{ioncore.x_get_text_property} (XGetTextProperty). + + +\input{statusd} + diff --git a/etc/Makefile b/etc/Makefile new file mode 100644 index 0000000..81ba5c1 --- /dev/null +++ b/etc/Makefile @@ -0,0 +1,27 @@ +## +## Ion etc Makefile +## + +# System-specific configuration is in system.mk +TOPDIR=.. +include $(TOPDIR)/build/system-inc.mk + +###################################### + +ETC = cfg_ion.lua cfg_ioncore.lua cfg_kludges.lua cfg_modules.lua \ + cfg_tiling.lua cfg_query.lua cfg_menu.lua \ + cfg_statusbar.lua cfg_dock.lua \ + look_brownsteel.lua look_clean.lua look_dusky.lua \ + look_greyviolet.lua look_ios.lua look_cleanviolet.lua \ + look_simpleblue.lua look_cleanios.lua \ + lookcommon_clean.lua lookcommon_emboss.lua + +###################################### + +include $(TOPDIR)/build/rules.mk + +###################################### + +_install: etc_install + rm -f $(ETCDIR)/look.lua + ln -s look_cleanviolet.lua $(ETCDIR)/look.lua diff --git a/etc/cfg_dock.lua b/etc/cfg_dock.lua new file mode 100644 index 0000000..70dcf3c --- /dev/null +++ b/etc/cfg_dock.lua @@ -0,0 +1,51 @@ +-- +-- Ion dock module configuration +-- + +-- Create a dock +mod_dock.create{ + -- Dock mode: embedded|floating + mode="floating", + -- The screen to create the dock on + screen=0, + -- Corner or side of the screen to place the dock on. + -- For embedded dock the valid values are: tl|tr|bl|br + -- For floating dock the following are also valid: tc|bc|ml|mc|mr + pos="bl", + -- Growth direction: left|right|up|down + grow="right", + -- Whether new dockapps should be added automatically to this dock + is_auto=true, + -- Show floating dock initially? + floating_hidden=false, + -- Name of the dock + name="*dock*", +} + + +-- For floating docks, you may want the following toggle binding. +defbindings("WScreen", { + bdoc("Toggle floating dock."), + kpress(META.."D", "mod_dock.set_floating_shown_on(_, 'toggle')") +}) + + +-- Dock settings menu. For this to work, mod_menu must have been loaded +-- previously. +if mod_menu then + defmenu("dock-settings", { + menuentry("Pos-TL", "_:set{pos='tl'}"), + menuentry("Pos-TR", "_:set{pos='tr'}"), + menuentry("Pos-BL", "_:set{pos='bl'}"), + menuentry("Pos-BR", "_:set{pos='br'}"), + menuentry("Grow-L", "_:set{grow='left'}"), + menuentry("Grow-R", "_:set{grow='right'}"), + menuentry("Grow-U", "_:set{grow='up'}"), + menuentry("Grow-D", "_:set{grow='down'}"), + }) + + defbindings("WDock", { + mpress("Button3", "mod_menu.pmenu(_, _sub, 'dock-settings')"), + }) +end + diff --git a/etc/cfg_ion.lua b/etc/cfg_ion.lua new file mode 100644 index 0000000..8e8d02e --- /dev/null +++ b/etc/cfg_ion.lua @@ -0,0 +1,56 @@ +-- +-- Ion main configuration file +-- +-- This file only includes some settings that are rather frequently altered. +-- The rest of the settings are in cfg_ioncore.lua and individual modules' +-- configuration files (cfg_modulename.lua). +-- + +-- Set default modifiers. Alt should usually be mapped to Mod1 on +-- XFree86-based systems. The flying window keys are probably Mod3 +-- or Mod4; see the output of 'xmodmap'. +--META="Mod1+" +--ALTMETA="" + +-- Terminal emulator +--XTERM="xterm" + +-- Some basic settings +ioncore.set{ + -- Maximum delay between clicks in milliseconds to be considered a + -- double click. + --dblclick_delay=250, + + -- For keyboard resize, time (in milliseconds) to wait after latest + -- key press before automatically leaving resize mode (and doing + -- the resize in case of non-opaque move). + --kbresize_delay=1500, + + -- Opaque resize? + --opaque_resize=false, + + -- Movement commands warp the pointer to frames instead of just + -- changing focus. Enabled by default. + --warp=true, +} + +-- cfg_ioncore contains configuration of the Ion 'core' +dopath("cfg_ioncore") + +-- Load some kludges to make apps behave better. +dopath("cfg_kludges") + +-- Load some modules. Disable the loading of cfg_modules by commenting out +-- the corresponding line with -- if you don't want the whole default set +-- (everything except mod_dock). Then uncomment the lines for the modules +-- you want. +dopath("cfg_modules") +--dopath("mod_query") +--dopath("mod_menu") +--dopath("mod_tiling") +--dopath("mod_statusbar") +--dopath("mod_dock") +--dopath("mod_sp") + +-- Deprecated. +dopath("cfg_user", true) diff --git a/etc/cfg_ioncore.lua b/etc/cfg_ioncore.lua new file mode 100644 index 0000000..acf7e94 --- /dev/null +++ b/etc/cfg_ioncore.lua @@ -0,0 +1,377 @@ +-- +-- Ion core configuration file +-- + + +-- +-- Bindings. This includes global bindings and bindings common to +-- screens and all types of frames only. See modules' configuration +-- files for other bindings. +-- + + +-- WScreen context bindings +-- +-- The bindings in this context are available all the time. +-- +-- The variable META should contain a string of the form 'Mod1+' +-- where Mod1 maybe replaced with the modifier you want to use for most +-- of the bindings. Similarly ALTMETA may be redefined to add a +-- modifier to some of the F-key bindings. + +defbindings("WScreen", { + bdoc("Switch to n:th object (workspace, full screen client window) ".. + "within current screen."), + kpress(META.."1", "WScreen.switch_nth(_, 0)"), + kpress(META.."2", "WScreen.switch_nth(_, 1)"), + kpress(META.."3", "WScreen.switch_nth(_, 2)"), + kpress(META.."4", "WScreen.switch_nth(_, 3)"), + kpress(META.."5", "WScreen.switch_nth(_, 4)"), + kpress(META.."6", "WScreen.switch_nth(_, 5)"), + kpress(META.."7", "WScreen.switch_nth(_, 6)"), + kpress(META.."8", "WScreen.switch_nth(_, 7)"), + kpress(META.."9", "WScreen.switch_nth(_, 8)"), + kpress(META.."0", "WScreen.switch_nth(_, 9)"), + + bdoc("Switch to next/previous object within current screen."), + kpress(META.."comma", "WScreen.switch_prev(_)"), + kpress(META.."period", "WScreen.switch_next(_)"), + + submap(META.."K", { + bdoc("Go to first region demanding attention or previously active one."), + kpress("K", "ioncore.goto_activity() or ioncore.goto_previous()"), + + --bdoc("Go to previous active object."), + --kpress("K", "ioncore.goto_previous()"), + + --bdoc("Go to first object on activity/urgency list."), + --kpress("I", "ioncore.goto_activity()"), + + bdoc("Clear all tags."), + kpress("T", "ioncore.clear_tags()"), + }), + + bdoc("Go to n:th screen on multihead setup."), + kpress(META.."Shift+1", "ioncore.goto_nth_screen(0)"), + kpress(META.."Shift+2", "ioncore.goto_nth_screen(1)"), + + bdoc("Go to next/previous screen on multihead setup."), + kpress(META.."Shift+comma", "ioncore.goto_prev_screen()"), + kpress(META.."Shift+period", "ioncore.goto_next_screen()"), + + bdoc("Create a new workspace of chosen default type."), + kpress(META.."F9", "ioncore.create_ws(_)"), + + bdoc("Display the main menu."), + kpress(ALTMETA.."F12", "mod_query.query_menu(_, 'mainmenu', 'Main menu:')"), + --kpress(ALTMETA.."F12", "mod_menu.menu(_, _sub, 'mainmenu', {big=true})"), + mpress("Button3", "mod_menu.pmenu(_, _sub, 'mainmenu')"), + + bdoc("Display the window list menu."), + mpress("Button2", "mod_menu.pmenu(_, _sub, 'windowlist')"), + + bdoc("Forward-circulate focus."), + -- '_chld' used here stands to for an actual child window that may not + -- be managed by the screen itself, unlike '_sub', that is likely to be + -- the managing group of that window. The right/left directions are + -- used instead of next/prev, because they work better in conjunction + -- with tilings. + kpress(META.."Tab", "ioncore.goto_next(_chld, 'right')", + "_chld:non-nil"), + submap(META.."K", { + bdoc("Backward-circulate focus."), + kpress("AnyModifier+Tab", "ioncore.goto_next(_chld, 'left')", + "_chld:non-nil"), + + bdoc("Raise focused object, if possible."), + kpress("AnyModifier+R", "WRegion.rqorder(_chld, 'front')", + "_chld:non-nil"), + }), + +}) + + +-- Client window bindings +-- +-- These bindings affect client windows directly. + +defbindings("WClientWin", { + bdoc("Nudge the client window. This might help with some ".. + "programs' resizing problems."), + kpress_wait(META.."L", "WClientWin.nudge(_)"), + + submap(META.."K", { + bdoc("Kill client owning the client window."), + kpress("C", "WClientWin.kill(_)"), + + bdoc("Send next key press to the client window. ".. + "Some programs may not allow this by default."), + kpress("Q", "WClientWin.quote_next(_)"), + }), +}) + + +-- Client window group bindings + +defbindings("WGroupCW", { + bdoc("Toggle client window group full-screen mode"), + kpress_wait(META.."Return", + "WClientWin.set_fullscreen(_:bottom(), 'toggle')"), +}) + + +-- WMPlex context bindings +-- +-- These bindings work in frames and on screens. The innermost of such +-- contexts/objects always gets to handle the key press. Most of these +-- bindings define actions on client windows. (Remember that client windows +-- can be put in fullscreen mode and therefore may not have a frame.) + +defbindings("WMPlex", { + bdoc("Close current object."), + kpress_wait(META.."C", "WRegion.rqclose_propagate(_, _sub)"), +}) + +-- Frames for transient windows ignore this bindmap + +defbindings("WMPlex.toplevel", { + bdoc("Toggle tag of current object."), + kpress(META.."T", "WRegion.set_tagged(_sub, 'toggle')", "_sub:non-nil"), + + bdoc("Query for manual page to be displayed."), + kpress(ALTMETA.."F1", "mod_query.query_man(_, ':man')"), + + bdoc("Show the Ion manual page."), + kpress(META.."F1", "ioncore.exec_on(_, ':man ion3')"), + + bdoc("Run a terminal emulator."), + kpress(ALTMETA.."F2", "ioncore.exec_on(_, XTERM or 'xterm')"), + + bdoc("Query for command line to execute."), + kpress(ALTMETA.."F3", "mod_query.query_exec(_)"), + + bdoc("Query for Lua code to execute."), + kpress(META.."F3", "mod_query.query_lua(_)"), + + bdoc("Query for host to connect to with SSH."), + kpress(ALTMETA.."F4", "mod_query.query_ssh(_, ':ssh')"), + + bdoc("Query for file to edit."), + kpress(ALTMETA.."F5", + "mod_query.query_editfile(_, 'run-mailcap --action=edit')"), + + bdoc("Query for file to view."), + kpress(ALTMETA.."F6", + "mod_query.query_runfile(_, 'run-mailcap --action=view')"), + + bdoc("Query for workspace to go to or create a new one."), + kpress(ALTMETA.."F9", "mod_query.query_workspace(_)"), + + bdoc("Query for a client window to go to."), + kpress(META.."G", "mod_query.query_gotoclient(_)"), + + bdoc("Query for a client window to attach."), + kpress(META.."A", "mod_query.query_attachclient(_)"), + + bdoc("Display context menu."), + --kpress(META.."M", "mod_menu.menu(_, _sub, 'ctxmenu')"), + kpress(META.."M", "mod_query.query_menu(_, 'ctxmenu', 'Context menu:')"), + +}) + + +-- WFrame context bindings +-- +-- These bindings are common to all types of frames. The rest of frame +-- bindings that differ between frame types are defined in the modules' +-- configuration files. + +defbindings("WFrame", { + submap(META.."K", { + bdoc("Maximize the frame horizontally/vertically."), + kpress("H", "WFrame.maximize_horiz(_)"), + kpress("V", "WFrame.maximize_vert(_)"), + }), + + bdoc("Display context menu."), + mpress("Button3", "mod_menu.pmenu(_, _sub, 'ctxmenu')"), + + bdoc("Begin move/resize mode."), + kpress(META.."R", "WFrame.begin_kbresize(_)"), + + bdoc("Switch the frame to display the object indicated by the tab."), + mclick("Button1@tab", "WFrame.p_switch_tab(_)"), + mclick("Button2@tab", "WFrame.p_switch_tab(_)"), + + bdoc("Resize the frame."), + mdrag("Button1@border", "WFrame.p_resize(_)"), + mdrag(META.."Button3", "WFrame.p_resize(_)"), + + bdoc("Move the frame."), + mdrag(META.."Button1", "WFrame.p_move(_)"), + + bdoc("Move objects between frames by dragging and dropping the tab."), + mdrag("Button1@tab", "WFrame.p_tabdrag(_)"), + mdrag("Button2@tab", "WFrame.p_tabdrag(_)"), + +}) + +-- Frames for transient windows ignore this bindmap + +defbindings("WFrame.toplevel", { + bdoc("Tag current object within the frame."), + kpress(META.."T", "WRegion.set_tagged(_sub, 'toggle')", "_sub:non-nil"), + + submap(META.."K", { + bdoc("Switch to n:th object within the frame."), + kpress("1", "WFrame.switch_nth(_, 0)"), + kpress("2", "WFrame.switch_nth(_, 1)"), + kpress("3", "WFrame.switch_nth(_, 2)"), + kpress("4", "WFrame.switch_nth(_, 3)"), + kpress("5", "WFrame.switch_nth(_, 4)"), + kpress("6", "WFrame.switch_nth(_, 5)"), + kpress("7", "WFrame.switch_nth(_, 6)"), + kpress("8", "WFrame.switch_nth(_, 7)"), + kpress("9", "WFrame.switch_nth(_, 8)"), + kpress("0", "WFrame.switch_nth(_, 9)"), + + bdoc("Switch to next/previous object within the frame."), + kpress("N", "WFrame.switch_next(_)"), + kpress("P", "WFrame.switch_prev(_)"), + + bdoc("Move current object within the frame left/right."), + kpress("comma", "WFrame.dec_index(_, _sub)", "_sub:non-nil"), + kpress("period", "WFrame.inc_index(_, _sub)", "_sub:non-nil"), + + bdoc("Maximize the frame horizontally/vertically."), + kpress("H", "WFrame.maximize_horiz(_)"), + kpress("V", "WFrame.maximize_vert(_)"), + + bdoc("Attach tagged objects to this frame."), + kpress("A", "WFrame.attach_tagged(_)"), + }), +}) + +-- Bindings for floating frames. + +defbindings("WFrame.floating", { + bdoc("Toggle shade mode"), + mdblclick("Button1@tab", "WFrame.set_shaded(_, 'toggle')"), + + bdoc("Raise the frame."), + mpress("Button1@tab", "WRegion.rqorder(_, 'front')"), + mpress("Button1@border", "WRegion.rqorder(_, 'front')"), + mclick(META.."Button1", "WRegion.rqorder(_, 'front')"), + + bdoc("Lower the frame."), + mclick(META.."Button3", "WRegion.rqorder(_, 'back')"), + + bdoc("Move the frame."), + mdrag("Button1@tab", "WFrame.p_move(_)"), +}) + + +-- WMoveresMode context bindings +-- +-- These bindings are available keyboard move/resize mode. The mode +-- is activated on frames with the command begin_kbresize (bound to +-- META.."R" above by default). + +defbindings("WMoveresMode", { + bdoc("Cancel the resize mode."), + kpress("AnyModifier+Escape","WMoveresMode.cancel(_)"), + + bdoc("End the resize mode."), + kpress("AnyModifier+Return","WMoveresMode.finish(_)"), + + bdoc("Grow in specified direction."), + kpress("Left", "WMoveresMode.resize(_, 1, 0, 0, 0)"), + kpress("Right", "WMoveresMode.resize(_, 0, 1, 0, 0)"), + kpress("Up", "WMoveresMode.resize(_, 0, 0, 1, 0)"), + kpress("Down", "WMoveresMode.resize(_, 0, 0, 0, 1)"), + kpress("F", "WMoveresMode.resize(_, 1, 0, 0, 0)"), + kpress("B", "WMoveresMode.resize(_, 0, 1, 0, 0)"), + kpress("P", "WMoveresMode.resize(_, 0, 0, 1, 0)"), + kpress("N", "WMoveresMode.resize(_, 0, 0, 0, 1)"), + + bdoc("Shrink in specified direction."), + kpress("Shift+Left", "WMoveresMode.resize(_,-1, 0, 0, 0)"), + kpress("Shift+Right", "WMoveresMode.resize(_, 0,-1, 0, 0)"), + kpress("Shift+Up", "WMoveresMode.resize(_, 0, 0,-1, 0)"), + kpress("Shift+Down", "WMoveresMode.resize(_, 0, 0, 0,-1)"), + kpress("Shift+F", "WMoveresMode.resize(_,-1, 0, 0, 0)"), + kpress("Shift+B", "WMoveresMode.resize(_, 0,-1, 0, 0)"), + kpress("Shift+P", "WMoveresMode.resize(_, 0, 0,-1, 0)"), + kpress("Shift+N", "WMoveresMode.resize(_, 0, 0, 0,-1)"), + + bdoc("Move in specified direction."), + kpress(META.."Left", "WMoveresMode.move(_,-1, 0)"), + kpress(META.."Right", "WMoveresMode.move(_, 1, 0)"), + kpress(META.."Up", "WMoveresMode.move(_, 0,-1)"), + kpress(META.."Down", "WMoveresMode.move(_, 0, 1)"), + kpress(META.."F", "WMoveresMode.move(_,-1, 0)"), + kpress(META.."B", "WMoveresMode.move(_, 1, 0)"), + kpress(META.."P", "WMoveresMode.move(_, 0,-1)"), + kpress(META.."N", "WMoveresMode.move(_, 0, 1)"), +}) + + +-- +-- Menu definitions +-- + + +-- Main menu +defmenu("mainmenu", { + submenu("Programs", "appmenu"), + menuentry("Lock screen", "ioncore.exec_on(_, 'xlock')"), + menuentry("Help", "mod_query.query_man(_)"), + menuentry("About Ion", "mod_query.show_about_ion(_)"), + submenu("Styles", "stylemenu"), + submenu("Session", "sessionmenu"), +}) + + +-- Application menu +defmenu("appmenu", { + menuentry("XTerm", "ioncore.exec_on(_, 'xterm')"), + menuentry("W3M", "ioncore.exec_on(_, ':w3m -v')"), + menuentry("Rxvt", "ioncore.exec_on(_, 'rxvt')"), + menuentry("Opera", "ioncore.exec_on(_, 'opera')"), + menuentry("Links", "ioncore.exec_on(_, ':links')"), + menuentry("Konqueror", "ioncore.exec_on(_, 'konqueror')"), + menuentry("Dillo", "ioncore.exec_on(_, 'dillo')"), + menuentry("Run...", "mod_query.query_exec(_)"), +}) + + +-- Session control menu +defmenu("sessionmenu", { + menuentry("Save", "ioncore.snapshot()"), + menuentry("Restart", "ioncore.restart()"), + menuentry("Restart TWM", "ioncore.restart_other('twm')"), + menuentry("Exit", "ioncore.shutdown()"), +}) + + +-- Context menu (frame/client window actions) +defctxmenu("WFrame", "Frame", { + menuentry("Close", "WRegion.rqclose_propagate(_, _sub)"), + menuentry("Kill", "WClientWin.kill(_sub)", + "_sub:WClientWin"), + menuentry("Toggle tag", "WRegion.set_tagged(_sub, 'toggle')", + "_sub:non-nil"), + menuentry("Attach tagged", "WFrame.attach_tagged(_)"), + menuentry("Clear tags", "ioncore.clear_tags()"), + menuentry("Window info", "mod_query.show_tree(_, _sub)"), +}) + + +-- Context menu for screens +defctxmenu("WScreen", "Screen", { + menuentry("New workspace", "ioncore.create_ws(_)"), + menuentry("New empty workspace", + "ioncore.create_ws(_, nil, true)"), + menuentry("Close workspace","WRegion.rqclose(_sub)"), +}) + diff --git a/etc/cfg_kludges.lua b/etc/cfg_kludges.lua new file mode 100644 index 0000000..cd6fa00 --- /dev/null +++ b/etc/cfg_kludges.lua @@ -0,0 +1,46 @@ +-- +-- Options to get some programs work more nicely (or at all) +-- + + +defwinprop{ + class = "AcroRead", + instance = "documentShell", + acrobatic = true +} + +-- Galeon's find dialog does not always have its transient_for hint +-- set when the window is being mapped. +defwinprop{ + class = "galeon_browser", + instance = "dialog_find", + transient_mode = "current", +} + +-- You might want to enable these if you really must use XMMS. +--[[ +defwinprop{ + class = "xmms", + instance = "XMMS_Playlist", + transient_mode = "off" +} + +defwinprop{ + class = "xmms", + instance = "XMMS_Player", + transient_mode = "off" +} +--]] + + + +-- Define some additional title shortening rules to use when the full +-- title doesn't fit in the available space. The first-defined matching +-- rule that succeeds in making the title short enough is used. +ioncore.defshortening("(.*) - Mozilla(<[0-9]+>)", "$1$2$|$1$<...$2") +ioncore.defshortening("(.*) - Mozilla", "$1$|$1$<...") +ioncore.defshortening("XMMS - (.*)", "$1$|...$>$1") +ioncore.defshortening("[^:]+: (.*)(<[0-9]+>)", "$1$2$|$1$<...$2") +ioncore.defshortening("[^:]+: (.*)", "$1$|$1$<...") +ioncore.defshortening("(.*)(<[0-9]+>)", "$1$2$|$1$<...$2") +ioncore.defshortening("(.*)", "$1$|$1$<...") diff --git a/etc/cfg_menu.lua b/etc/cfg_menu.lua new file mode 100644 index 0000000..fff7957 --- /dev/null +++ b/etc/cfg_menu.lua @@ -0,0 +1,32 @@ +-- +-- Menu module configuration. +-- +-- Only bindings that are effect in menus are configured here. +-- See ion-menus.lua for menu definitions and ion-bindings.lua +-- for bindings to display menus. +-- + + +defbindings("WMenu", { + bdoc("Close the menu."), + kpress("Escape", "WMenu.cancel(_)"), + kpress("Control+G", "WMenu.cancel(_)"), + kpress("Control+C", "WMenu.cancel(_)"), + kpress("Left", "WMenu.cancel(_)"), + + bdoc("Activate current menu entry."), + kpress("Return", "WMenu.finish(_)"), + kpress("KP_Enter", "WMenu.finish(_)"), + kpress("Control+M", "WMenu.finish(_)"), + kpress("Right", "WMenu.finish(_)"), + + bdoc("Select next/previous menu entry."), + kpress("Control+N", "WMenu.select_next(_)"), + kpress("Control+P", "WMenu.select_prev(_)"), + kpress("Up", "WMenu.select_prev(_)"), + kpress("Down", "WMenu.select_next(_)"), + + bdoc("Clear the menu's typeahead find buffer."), + kpress("BackSpace", "WMenu.typeahead_clear(_)"), +}) + diff --git a/etc/cfg_modules.lua b/etc/cfg_modules.lua new file mode 100644 index 0000000..ccbfb70 --- /dev/null +++ b/etc/cfg_modules.lua @@ -0,0 +1,11 @@ +-- +-- Ion default module set. +-- + +dopath("mod_query") +dopath("mod_menu") +dopath("mod_tiling") +--dopath("mod_panews") +dopath("mod_statusbar") +--dopath("mod_dock") +dopath("mod_sp") diff --git a/etc/cfg_panews.lua b/etc/cfg_panews.lua new file mode 100644 index 0000000..65761a0 --- /dev/null +++ b/etc/cfg_panews.lua @@ -0,0 +1,57 @@ +-- +-- Ion panews module configuration file +-- + +-- Bindings for unused area. + +defbindings("WUnusedWin", { + bdoc("Begin move/resize mode."), + kpress(META.."R", "WUnusedWin.begin_kbresize(_)"), + + bdoc("Resize the area."), + mdrag(META.."Button3", "WUnusedWin.p_resize(_)"), + mdrag(META.."Button1", "WUnusedWin.p_move(_)"), +}) + + +mod_panews.set{ + -- Layout template may be one of default|alternative1|alternative2 + -- or a template table. (The one for 'default' is reproduced below + -- as an example.) + --template="default", + -- The scale factor parameter controls the size-based classification + -- heuristic. The default of 1.0 is designed for 1280x1024 at 75dpi. + --scalef=1.0, +} + + +-- The layout template for the 'default' layout looks as follows. +--[[ +{ + type="WSplitFloat", + dir="horizontal", + tls=settings.b_ratio, + brs=settings.s_ratio, + tl={ + type="WSplitPane", + contents={ + type="WSplitFloat", + dir="vertical", + tls=settings.b_ratio2, + brs=settings.s_ratio2, + tl={ + type="WSplitPane", + marker="V:single", + }, + br={ + type="WSplitPane", + marker="M:right", + }, + }, + }, + br={ + type="WSplitPane", + marker="T:up", + }, +} +--]] diff --git a/etc/cfg_query.lua b/etc/cfg_query.lua new file mode 100644 index 0000000..84f7da6 --- /dev/null +++ b/etc/cfg_query.lua @@ -0,0 +1,117 @@ +-- +-- Query module configuration. +-- +-- Only bindings that are in effect in queries and message displays are +-- configured here. Actions to display queries are configured in +-- ion-bindings.lua +-- + + +defbindings("WEdln", { + bdoc("Move one character forward/backward."), + kpress("Control+F", "WEdln.forward(_)"), + kpress("Control+B", "WEdln.back(_)"), + kpress("Right", "WEdln.forward(_)"), + kpress("Left", "WEdln.back(_)"), + + bdoc("Go to end/beginning."), + kpress("Control+E", "WEdln.eol(_)"), + kpress("Control+A", "WEdln.bol(_)"), + kpress("End", "WEdln.eol(_)"), + kpress("Home", "WEdln.bol(_)"), + + bdoc("Skip one word forward/backward."), + kpress("Control+X", "WEdln.skip_word(_)"), + kpress("Control+Z", "WEdln.bskip_word(_)"), + + bdoc("Delete next character."), + kpress("Control+D", "WEdln.delete(_)"), + kpress("Delete", "WEdln.delete(_)"), + + bdoc("Delete previous character."), + kpress("BackSpace", "WEdln.backspace(_)"), + kpress("Control+H", "WEdln.backspace(_)"), + + bdoc("Delete one word forward/backward."), + kpress("Control+W", "WEdln.kill_word(_)"), + kpress("Control+O", "WEdln.bkill_word(_)"), + + bdoc("Delete to end of line."), + kpress("Control+J", "WEdln.kill_to_eol(_)"), + + bdoc("Delete the whole line."), + kpress("Control+Y", "WEdln.kill_line(_)"), + + bdoc("Transpose characters."), + kpress("Control+T", "WEdln.transpose_chars(_)"), + + bdoc("Select next/previous (matching) history entry."), + kpress("Control+P", "WEdln.history_prev(_)"), + kpress("Control+N", "WEdln.history_next(_)"), + kpress("Up", "WEdln.history_prev(_)"), + kpress("Down", "WEdln.history_next(_)"), + kpress("Control+Up", "WEdln.history_prev(_, true)"), + kpress("Control+Down", "WEdln.history_next(_, true)"), + + bdoc("Paste from the clipboard."), + mclick("Button2", "WEdln.paste(_)"), + submap("Control+K", { + kpress("C", "WEdln.paste(_)"), + + bdoc("Set mark/begin selection."), + kpress("B", "WEdln.set_mark(_)"), + + bdoc("Cut selection."), + kpress("Y", "WEdln.cut(_)"), + + bdoc("Copy selection."), + kpress("K", "WEdln.copy(_)"), + + bdoc("Clear mark/cancel selection."), + kpress("G", "WEdln.clear_mark(_)"), + + --bdoc("Transpose words."), + --kpress("T", "WEdln.transpose_words(_)"), + }), + + bdoc("Try to complete the entered text or cycle through completions."), + kpress("Tab", "WEdln.complete(_, 'next', 'normal')"), + kpress("Shift+Tab", "WEdln.complete(_, 'prev', 'normal')"), + -- Do not cycle; only force evaluation of new completions + kpress("Control+Tab", "WEdln.complete(_, nil, 'normal')"), + + bdoc("Complete from history"), + kpress("Control+R", "WEdln.complete(_, 'next', 'history')"), + kpress("Control+S", "WEdln.complete(_, 'prev', 'history')"), + + bdoc("Close the query and execute bound action."), + kpress("Control+M", "WEdln.finish(_)"), + kpress("Return", "WEdln.finish(_)"), + kpress("KP_Enter", "WEdln.finish(_)"), +}) + + +defbindings("WInput", { + bdoc("Close the query/message box, not executing bound actions."), + kpress("Escape", "WInput.cancel(_)"), + kpress("Control+G", "WInput.cancel(_)"), + kpress("Control+C", "WInput.cancel(_)"), + + bdoc("Scroll the message or completions up/down."), + kpress("Control+U", "WInput.scrollup(_)"), + kpress("Control+V", "WInput.scrolldown(_)"), + kpress("Page_Up", "WInput.scrollup(_)"), + kpress("Page_Down", "WInput.scrolldown(_)"), +}) + + +-- Some settings +--[[ +mod_query.set{ + -- Auto-show completions? + autoshowcompl=true, + -- Delay for completion after latest keypress/modification in + -- milliseconds + autoshowcompl_delay=250, +} +--]] diff --git a/etc/cfg_statusbar.lua b/etc/cfg_statusbar.lua new file mode 100644 index 0000000..82fa506 --- /dev/null +++ b/etc/cfg_statusbar.lua @@ -0,0 +1,87 @@ +-- +-- Ion statusbar module configuration file +-- + + +-- Create a statusbar +mod_statusbar.create{ + -- First screen, bottom left corner + screen=0, + pos='bl', + -- Set this to true if you want a full-width statusbar + fullsize=false, + -- Swallow systray windows + systray=true, + + -- Template. Tokens %string are replaced with the value of the + -- corresponding meter. Currently supported meters are: + -- date date + -- load load average (1min, 5min, 15min) + -- load_Nmin N minute load average (N=1, 5, 15) + -- mail_new mail count (mbox format file $MAIL) + -- mail_unread mail count + -- mail_total mail count + -- mail_*_new mail count (from an alternate mail folder, see below) + -- mail_*_unread mail count + -- mail_*_total mail count + -- + -- Space preceded by % adds stretchable space for alignment of variable + -- meter value widths. > before meter name aligns right using this + -- stretchable space , < left, and | centers. + -- Meter values may be zero-padded to a width preceding the meter name. + -- These alignment and padding specifiers and the meter name may be + -- enclosed in braces {}. + -- + -- %filler causes things on the marker's sides to be aligned left and + -- right, respectively, and %systray is a placeholder for system tray + -- windows and icons. + -- + --template="[ %date || load:% %>load || mail:% %>mail_new/%>mail_total ] %filler%systray", + --template="[ %date || load: %05load_1min || mail: %02mail_new/%02mail_total ] %filler%systray", +} + + +-- Launch ion-statusd. This must be done after creating any statusbars +-- for necessary statusd modules to be parsed from the templates. +mod_statusbar.launch_statusd{ + -- Date meter + date={ + -- ISO-8601 date format with additional abbreviated day name + date_format='%a %Y-%m-%d %H:%M', + -- Finnish etc. date format + --date_format='%a %d.%m.%Y %H:%M', + -- Locale date format (usually shows seconds, which would require + -- updating rather often and can be distracting) + --date_format='%c', + + -- Additional date formats. + --[[ + formats={ + time = '%H:%M', -- %date_time + } + --]] + }, + + -- Load meter + load={ + --update_interval=10*1000, + --important_threshold=1.5, + --critical_threshold=4.0, + }, + + -- Mail meter + -- + -- To monitor more mbox files, add them to the files table. For + -- example, add mail_work_new and mail_junk_new to the template + -- above, and define them in the files table: + -- + -- files = { work = "/path/to/work_email", junk = "/path/to/junk" } + -- + -- Don't use the keyword 'spool' as it's reserved for mbox. + mail={ + --update_interval=60*1000, + --mbox=os.getenv("MAIL"), + --files={}, + }, +} + diff --git a/etc/cfg_tiling.lua b/etc/cfg_tiling.lua new file mode 100644 index 0000000..0225d3a --- /dev/null +++ b/etc/cfg_tiling.lua @@ -0,0 +1,146 @@ +-- +-- Ion tiling module configuration file +-- + +-- Bindings for the tilings. + +defbindings("WTiling", { + bdoc("Split current frame vertically."), + kpress(META.."S", "WTiling.split_at(_, _sub, 'bottom', true)"), + + bdoc("Go to frame above/below/right/left of current frame."), + kpress(META.."P", "ioncore.goto_next(_sub, 'up', {no_ascend=_})"), + kpress(META.."N", "ioncore.goto_next(_sub, 'down', {no_ascend=_})"), + kpress(META.."Tab", "ioncore.goto_next(_sub, 'right')"), + submap(META.."K", { + kpress("Tab", "ioncore.goto_next(_sub, 'left')"), + + bdoc("Split current frame horizontally."), + kpress("S", "WTiling.split_at(_, _sub, 'right', true)"), + + bdoc("Destroy current frame."), + kpress("X", "WTiling.unsplit_at(_, _sub)"), + }), +}) + + +-- Frame bindings + +defbindings("WFrame.tiled", { + submap(META.."K", { + bdoc("Detach window from tiled frame"), + kpress("D", "mod_tiling.detach(_sub)", "_sub:non-nil"), + }), +}) + + +defbindings("WFrame.transient", { + submap(META.."K", { + bdoc("Detach transient frame"), + kpress("D", "mod_tiling.detach(_)", "_sub:non-nil"), + }), +}) + + +defbindings("WFrame.floating", { + submap(META.."K", { + bdoc("Tile frame, if no tiling exists on the workspace"), + kpress("B", "mod_tiling.mkbottom(_)"), + }), +}) + + +-- Context menu for tiled workspaces. + +defctxmenu("WTiling", "Tiling", { + menuentry("Destroy frame", + "WTiling.unsplit_at(_, _sub)"), + + menuentry("Split vertically", + "WTiling.split_at(_, _sub, 'bottom', true)"), + menuentry("Split horizontally", + "WTiling.split_at(_, _sub, 'right', true)"), + + menuentry("Flip", "WTiling.flip_at(_, _sub)"), + menuentry("Transpose", "WTiling.transpose_at(_, _sub)"), + + submenu("Float split", { + menuentry("At left", + "WTiling.set_floating_at(_, _sub, 'toggle', 'left')"), + menuentry("At right", + "WTiling.set_floating_at(_, _sub, 'toggle', 'right')"), + menuentry("Above", + "WTiling.set_floating_at(_, _sub, 'toggle', 'up')"), + menuentry("Below", + "WTiling.set_floating_at(_, _sub, 'toggle', 'down')"), + }), + + submenu("At root", { + menuentry("Split vertically", + "WTiling.split_top(_, 'bottom')"), + menuentry("Split horizontally", + "WTiling.split_top(_, 'right')"), + menuentry("Flip", "WTiling.flip_at(_)"), + menuentry("Transpose", "WTiling.transpose_at(_)"), + }), +}) + + +-- Context menu entries for tiled frames. + +defctxmenu("WFrame.tiled", "Tiled frame", { + menuentry("Detach window", "mod_tiling.detach(_sub)", "_sub:non-nil"), +}) + + +-- Context menu entries for transient frames. + +defctxmenu("WFrame.transient", "Transient frame", { + append=true, + menuentry("Detach", "mod_tiling.detach(_)", "_sub:non-nil"), +}) + + +-- Extra context menu extra entries for floatframes. + +defctxmenu("WFrame.floating", "Floating frame", { + append=true, + menuentry("New tiling", "mod_tiling.mkbottom(_)"), +}) + + +-- Adjust default workspace layout + +local a_frame = { + type="WSplitRegion", + regparams = { + type = "WFrame", + frame_style = "frame-tiled" + } +} + +ioncore.set{ + default_ws_params = { + -- Destroy workspace if the 'bottom' tiling is destroyed last + bottom_last_close = true, + -- Layout + managed = { + { + type = "WTiling", + bottom = true, + -- The default is a single 1:1 horizontal split + split_tree = { + type = "WSplitSplit", + dir = "horizontal", + tls = 1, + brs = 1, + tl = a_frame, + br = a_frame + } + -- For a single frame + --split_tree = nil + } + } + } +} + diff --git a/etc/look_brownsteel.lua b/etc/look_brownsteel.lua new file mode 100644 index 0000000..221c5f5 --- /dev/null +++ b/etc/look_brownsteel.lua @@ -0,0 +1,108 @@ +-- look_brownsteel.lua drawing engine configuration file for Ion. + +if not gr.select_engine("de") then return end + +de.reset() + +de.defstyle("*", { + shadow_colour = "#404040", + highlight_colour = "#707070", + background_colour = "#505050", + foreground_colour = "#a0a0a0", + padding_pixels = 1, + highlight_pixels = 1, + shadow_pixels = 1, + border_style = "elevated", + font = "-*-helvetica-medium-r-normal-*-14-*-*-*-*-*-*-*", + text_align = "center", +}) + +de.defstyle("frame", { + based_on = "*", + shadow_colour = "#404040", + highlight_colour = "#707070", + padding_colour = "#505050", + background_colour = "#000000", + foreground_colour = "#ffffff", + padding_pixels = 1, + highlight_pixels = 1, + shadow_pixels = 1, +}) + +de.defstyle("tab", { + based_on = "*", + font = "-*-helvetica-medium-r-normal-*-12-*-*-*-*-*-*-*", + de.substyle("active-selected", { + shadow_colour = "#304050", + highlight_colour = "#708090", + background_colour = "#506070", + foreground_colour = "#ffffff", + }), + de.substyle("active-unselected", { + shadow_colour = "#203040", + highlight_colour = "#607080", + background_colour = "#405060", + foreground_colour = "#a0a0a0", + }), + de.substyle("inactive-selected", { + shadow_colour = "#404040", + highlight_colour = "#909090", + background_colour = "#606060", + foreground_colour = "#a0a0a0", + }), + de.substyle("inactive-unselected", { + shadow_colour = "#404040", + highlight_colour = "#707070", + background_colour = "#505050", + foreground_colour = "#a0a0a0", + }), + text_align = "center", +}) + +de.defstyle("tab-menuentry", { + based_on = "tab", + text_align = "left", + highlight_pixels = 0, + shadow_pixels = 0, +}) + +de.defstyle("tab-menuentry-big", { + based_on = "tab-menuentry", + font = "-*-helvetica-medium-r-normal-*-17-*-*-*-*-*-*-*", + padding_pixels = 7, +}) + +de.defstyle("input", { + based_on = "*", + shadow_colour = "#404040", + highlight_colour = "#707070", + background_colour = "#000000", + foreground_colour = "#ffffff", + padding_pixels = 1, + highlight_pixels = 1, + shadow_pixels = 1, + border_style = "elevated", + de.substyle("*-cursor", { + background_colour = "#ffffff", + foreground_colour = "#000000", + }), + de.substyle("*-selection", { + background_colour = "#505050", + foreground_colour = "#ffffff", + }), +}) + +de.defstyle("input-menu", { + based_on = "*", + de.substyle("active", { + shadow_colour = "#304050", + highlight_colour = "#708090", + background_colour = "#506070", + foreground_colour = "#ffffff", + }), +}) + +dopath("lookcommon_emboss") + +gr.refresh() + diff --git a/etc/look_clean.lua b/etc/look_clean.lua new file mode 100644 index 0000000..7da34a9 --- /dev/null +++ b/etc/look_clean.lua @@ -0,0 +1,102 @@ +-- look_clean.lua drawing engine configuration file for Ion. + +if not gr.select_engine("de") then return end + +de.reset() + +de.defstyle("*", { + shadow_colour = "grey", + highlight_colour = "grey", + background_colour = "#545d75", + foreground_colour = "grey", + padding_pixels = 1, + highlight_pixels = 1, + shadow_pixels = 1, + border_style = "elevated", + font = "-misc-fixed-medium-r-*-*-13-*-*-*-*-60-*-*", + text_align = "center", +}) + +de.defstyle("frame", { + based_on = "*", + padding_colour = "#545d75", + background_colour = "black", + de.substyle("active", { + shadow_colour = "grey", + highlight_colour = "grey", + padding_colour = "#545d75", + background_colour = "black", + }), +}) + +de.defstyle("tab", { + based_on = "*", + font = "-misc-fixed-medium-r-*-*-13-*-*-*-*-60-*-*", + de.substyle("active-selected", { + shadow_colour = "white", + highlight_colour = "white", + background_colour = "#8a999e", + foreground_colour = "white", + }), + de.substyle("active-unselected", { + shadow_colour = "grey", + highlight_colour = "grey", + background_colour = "#545d75", + foreground_colour = "grey", + }), + de.substyle("inactive-selected", { + shadow_colour = "grey", + highlight_colour = "grey", + background_colour = "#545d75", + foreground_colour = "grey", + }), + de.substyle("inactive-unselected", { + shadow_colour = "grey", + highlight_colour = "grey", + background_colour = "#545d75", + foreground_colour = "grey", + }), + text_align = "center", +}) + +de.defstyle("tab-menuentry", { + based_on = "tab", + text_align = "left", + de.substyle("inactive-selected", { + shadow_colour = "grey", + highlight_colour = "grey", + background_colour = "#8a999e", + foreground_colour = "grey", + }), +}) + +de.defstyle("tab-menuentry-big", { + based_on = "tab-menuentry", + padding_pixels = 7, +}) + +de.defstyle("input", { + based_on = "*", + shadow_colour = "grey", + highlight_colour = "grey", + background_colour = "#545d75", + foreground_colour = "white", + padding_pixels = 1, + highlight_pixels = 1, + shadow_pixels = 1, + border_style = "elevated", + de.substyle("*-cursor", { + background_colour = "white", + foreground_colour = "#545d75", + }), + de.substyle("*-selection", { + background_colour = "#aaaaaa", + foreground_colour = "black", + }), + font = "-misc-fixed-medium-r-*-*-13-*-*-*-*-60-*-*", +}) + +dopath("lookcommon_clean") + +gr.refresh() + diff --git a/etc/look_cleanios.lua b/etc/look_cleanios.lua new file mode 100644 index 0000000..6a70546 --- /dev/null +++ b/etc/look_cleanios.lua @@ -0,0 +1,85 @@ +-- look_cleanios.lua drawing engine configuration file for Ion. + +if not gr.select_engine("de") then return end + +de.reset() + +de.defstyle("*", { + shadow_colour = "#ffffff", + highlight_colour = "#ffffff", + background_colour = "#d8d8d8", + foreground_colour = "#000000", + padding_pixels = 1, + highlight_pixels = 1, + shadow_pixels = 1, + spacing = 0, + border_style = "elevated", + font = "-*-helvetica-medium-r-normal-*-14-*-*-*-*-*-*-*", + text_align = "center", +}) + +de.defstyle("frame", { + based_on = "*", + padding_colour = "#d8d8d8", + background_colour = "#000000", + transparent_background = false, +}) + +de.defstyle("tab", { + based_on = "*", + font = "-*-helvetica-bold-r-normal-*-10-*-*-*-*-*-*-*", + de.substyle("active-selected", { + shadow_colour = "#f0f066", + highlight_colour = "#f0f066", + background_colour = "#f0c000", + foreground_colour = "#000000", + }), + de.substyle("active-unselected", { + shadow_colour = "#ffffff", + highlight_colour = "#ffffff", + background_colour = "#d8d8d8", + foreground_colour = "#000000", + }), + de.substyle("inactive-selected", { + shadow_colour = "#ffffff", + highlight_colour = "#ffffff", + background_colour = "#a8a8a8", + foreground_colour = "#000000", + }), + de.substyle("inactive-unselected", { + shadow_colour = "#ffffff", + highlight_colour = "#ffffff", + background_colour = "#d8d8d8", + foreground_colour = "#000000", + }), + text_align = "center", +}) + +de.defstyle("tab-menuentry", { + based_on = "tab", + text_align = "left", + spacing = 1, +}) + +de.defstyle("tab-menuentry-big", { + based_on = "tab-menuentry", + font = "-*-helvetica-medium-r-normal-*-17-*-*-*-*-*-*-*", + padding_pixels = 7, +}) + +de.defstyle("input-edln", { + based_on = "*", + de.substyle("*-cursor", { + background_colour = "#000000", + foreground_colour = "#d8d8d8", + }), + de.substyle("*-selection", { + background_colour = "#f0c000", + foreground_colour = "#000000", + }), +}) + +dopath("lookcommon_clean") + +gr.refresh() + diff --git a/etc/look_cleanviolet.lua b/etc/look_cleanviolet.lua new file mode 100644 index 0000000..32dce50 --- /dev/null +++ b/etc/look_cleanviolet.lua @@ -0,0 +1,100 @@ +-- +-- Look_cleanviolet for Ion's default drawing engine. +-- Based on look-clean and look-violetgrey. +-- + +if not gr.select_engine("de") then + return +end + +-- Clear existing styles from memory. +de.reset() + +-- Base style +de.defstyle("*", { + -- Gray background + highlight_colour = "#eeeeee", + shadow_colour = "#eeeeee", + background_colour = "#aaaaaa", + foreground_colour = "#000000", + + shadow_pixels = 1, + highlight_pixels = 1, + padding_pixels = 1, + spacing = 0, + border_style = "elevated", + + font = "-*-helvetica-medium-r-normal-*-14-*-*-*-*-*-*-*", + text_align = "center", +}) + + +de.defstyle("frame", { + based_on = "*", + padding_colour = "#aaaaaa", + background_colour = "#000000", + transparent_background = false, +}) + + +de.defstyle("tab", { + based_on = "*", + font = "-*-helvetica-medium-r-normal-*-12-*-*-*-*-*-*-*", + + de.substyle("active-selected", { + -- Violet tab + highlight_colour = "#aaaacc", + shadow_colour = "#aaaacc", + background_colour = "#666699", + foreground_colour = "#eeeeee", + }), + + de.substyle("inactive-selected", { + -- Greyish violet tab + highlight_colour = "#eeeeff", + shadow_colour = "#eeeeff", + background_colour = "#9999aa", + foreground_colour = "#000000", + }), +}) + + +de.defstyle("tab-menuentry", { + based_on = "tab", + text_align = "left", + spacing = 1, +}) + + +de.defstyle("tab-menuentry-big", { + based_on = "tab-menuentry", + font = "-*-helvetica-medium-r-normal-*-17-*-*-*-*-*-*-*", + padding_pixels = 7, +}) + + +de.defstyle("input", { + based_on = "*", + text_align = "left", + spacing = 1, + -- Greyish violet background + highlight_colour = "#eeeeff", + shadow_colour = "#eeeeff", + background_colour = "#9999aa", + foreground_colour = "#000000", + + de.substyle("*-selection", { + background_colour = "#777799", + foreground_colour = "#000000", + }), + + de.substyle("*-cursor", { + background_colour = "#000000", + foreground_colour = "#9999aa", + }), +}) + +dopath("lookcommon_clean") + +-- Refresh objects' brushes. +gr.refresh() diff --git a/etc/look_dusky.lua b/etc/look_dusky.lua new file mode 100644 index 0000000..5effeee --- /dev/null +++ b/etc/look_dusky.lua @@ -0,0 +1,105 @@ +-- look_dusky.lua drawing engine configuration file for Ion. + +if not gr.select_engine("de") then return end + +de.reset() + +de.defstyle("*", { + shadow_colour = "#404040", + highlight_colour = "#707070", + background_colour = "#505050", + foreground_colour = "#a0a0a0", + padding_pixels = 1, + highlight_pixels = 1, + shadow_pixels = 1, + border_style = "elevated", + font = "-*-helvetica-medium-r-normal-*-14-*-*-*-*-*-*-*", + text_align = "center", +}) + +de.defstyle("frame", { + based_on = "*", + shadow_colour = "#404040", + highlight_colour = "#707070", + padding_colour = "#505050", + background_colour = "#000000", + foreground_colour = "#ffffff", + padding_pixels = 2, + highlight_pixels = 1, + shadow_pixels = 1, +}) + +de.defstyle("tab", { + based_on = "*", + font = "-*-helvetica-medium-r-normal-*-12-*-*-*-*-*-*-*", + de.substyle("active-selected", { + shadow_colour = "#452727", + highlight_colour = "#866868", + background_colour = "#664848", + foreground_colour = "#ffffff", + }), + de.substyle("active-unselected", { + shadow_colour = "#351818", + highlight_colour = "#765858", + background_colour = "#563838", + foreground_colour = "#a0a0a0", + }), + de.substyle("inactive-selected", { + shadow_colour = "#404040", + highlight_colour = "#909090", + background_colour = "#606060", + foreground_colour = "#a0a0a0", + }), + de.substyle("inactive-unselected", { + shadow_colour = "#404040", + highlight_colour = "#707070", + background_colour = "#505050", + foreground_colour = "#a0a0a0", + }), + text_align = "center", +}) + +de.defstyle("tab-menuentry", { + based_on = "tab", + text_align = "left", + highlight_pixels = 0, + shadow_pixels = 0, +}) + +de.defstyle("tab-menuentry-big", { + based_on = "tab-menuentry", + font = "-*-helvetica-medium-r-normal-*-17-*-*-*-*-*-*-*", + padding_pixels = 7, +}) + +de.defstyle("input", { + based_on = "*", + shadow_colour = "#404040", + highlight_colour = "#707070", + background_colour = "#000000", + foreground_colour = "#ffffff", + border_style = "elevated", + de.substyle("*-cursor", { + background_colour = "#ffffff", + foreground_colour = "#000000", + }), + de.substyle("*-selection", { + background_colour = "#505050", + foreground_colour = "#ffffff", + }), +}) + +de.defstyle("input-menu", { + based_on = "*", + de.substyle("active", { + shadow_colour = "#452727", + highlight_colour = "#866868", + background_colour = "#664848", + foreground_colour = "#ffffff", + }), +}) + +dopath("lookcommon_emboss") + +gr.refresh() + diff --git a/etc/look_greyviolet.lua b/etc/look_greyviolet.lua new file mode 100644 index 0000000..ec15a78 --- /dev/null +++ b/etc/look_greyviolet.lua @@ -0,0 +1,98 @@ +-- look_greyviolet.lua drawing engine configuration file for Ion. + +if not gr.select_engine("de") then return end + +de.reset() + +de.defstyle("*", { + shadow_colour = "#777777", + highlight_colour = "#eeeeee", + background_colour = "#aaaaaa", + foreground_colour = "#000000", + padding_pixels = 1, + highlight_pixels = 1, + shadow_pixels = 1, + border_style = "elevated", + font = "-*-helvetica-medium-r-normal-*-14-*-*-*-*-*-*-*", + text_align = "center", +}) + +de.defstyle("frame", { + based_on = "*", + shadow_colour = "#777777", + highlight_colour = "#eeeeee", + padding_colour = "#aaaaaa", + background_colour = "#000000", + foreground_colour = "#ffffff", + padding_pixels = 2, + highlight_pixels = 1, + shadow_pixels = 1, +}) + +de.defstyle("tab", { + based_on = "*", + font = "-*-helvetica-medium-r-normal-*-12-*-*-*-*-*-*-*", + de.substyle("active-selected", { + shadow_colour = "#333366", + highlight_colour = "#aaaacc", + background_colour = "#666699", + foreground_colour = "#eeeeee", + }), + de.substyle("active-unselected", { + shadow_colour = "#777777", + highlight_colour = "#eeeeee", + background_colour = "#aaaaaa", + foreground_colour = "#000000", + }), + de.substyle("inactive-selected", { + shadow_colour = "#777788", + highlight_colour = "#eeeeff", + background_colour = "#9999aa", + foreground_colour = "#000000", + }), + de.substyle("inactive-unselected", { + shadow_colour = "#777777", + highlight_colour = "#eeeeee", + background_colour = "#aaaaaa", + foreground_colour = "#000000", + }), + text_align = "center", +}) + +de.defstyle("tab-menuentry", { + based_on = "tab", + text_align = "left", + highlight_pixels = 0, + shadow_pixels = 0, +}) + +de.defstyle("tab-menuentry-big", { + based_on = "tab-menuentry", + font = "-*-helvetica-medium-r-normal-*-17-*-*-*-*-*-*-*", + padding_pixels = 7, +}) + +de.defstyle("input", { + based_on = "*", + shadow_colour = "#777777", + highlight_colour = "#eeeeee", + background_colour = "#aaaaaa", + foreground_colour = "#000000", + padding_pixels = 1, + highlight_pixels = 1, + shadow_pixels = 1, + border_style = "elevated", + de.substyle("*-cursor", { + background_colour = "#000000", + foreground_colour = "#aaaaaa", + }), + de.substyle("*-selection", { + background_colour = "#aaaaaa", + foreground_colour = "black", + }), +}) + +dopath("lookcommon_emboss") + +gr.refresh() + diff --git a/etc/look_ios.lua b/etc/look_ios.lua new file mode 100644 index 0000000..4b8f4da --- /dev/null +++ b/etc/look_ios.lua @@ -0,0 +1,104 @@ +-- look_ios.lua drawing engine configuration file for Ion. + +if not gr.select_engine("de") then return end + +de.reset() + +de.defstyle("*", { + shadow_colour = "#606060", + highlight_colour = "#ffffff", + background_colour = "#d8d8d8", + foreground_colour = "#000000", + padding_pixels = 1, + highlight_pixels = 1, + shadow_pixels = 1, + border_style = "elevated", + font = "-*-helvetica-medium-r-normal-*-14-*-*-*-*-*-*-*", + text_align = "center", +}) + +de.defstyle("frame", { + based_on = "*", + shadow_colour = "#606060", + highlight_colour = "#ffffff", + padding_colour = "#d8d8d8", + background_colour = "#000000", + foreground_colour = "#000000", + padding_pixels = 2, + highlight_pixels = 1, + shadow_pixels = 1, + de.substyle("active", { + shadow_colour = "#606060", + highlight_colour = "#ffffff", + background_colour = "#d8d8d8", + foreground_colour = "#000000", + }), +}) + +de.defstyle("tab", { + based_on = "*", + font = "-*-helvetica-bold-r-normal-*-10-*-*-*-*-*-*-*", + de.substyle("active-selected", { + shadow_colour = "#f09000", + highlight_colour = "#f0f066", + background_colour = "#f0c000", + foreground_colour = "#000000", + }), + de.substyle("active-unselected", { + shadow_colour = "#606060", + highlight_colour = "#ffffff", + background_colour = "#d8d8d8", + foreground_colour = "#000000", + }), + de.substyle("inactive-selected", { + shadow_colour = "#606060", + highlight_colour = "#efefef", + background_colour = "#a8a8a8", + foreground_colour = "#000000", + }), + de.substyle("inactive-unselected", { + shadow_colour = "#606060", + highlight_colour = "#ffffff", + background_colour = "#d8d8d8", + foreground_colour = "#000000", + }), + text_align = "center", +}) + +de.defstyle("tab-menuentry", { + based_on = "tab", + text_align = "left", + highlight_pixels = 0, + shadow_pixels = 0, +}) + +de.defstyle("tab-menuentry-big", { + based_on = "tab-menuentry", + font = "-*-helvetica-medium-r-normal-*-17-*-*-*-*-*-*-*", + padding_pixels = 7, +}) + +de.defstyle("input", { + based_on = "*", + shadow_colour = "#606060", + highlight_colour = "#ffffff", + background_colour = "#d8d8d8", + foreground_colour = "#000000", + padding_pixels = 1, + highlight_pixels = 1, + shadow_pixels = 1, + border_style = "elevated", + de.substyle("*-cursor", { + background_colour = "#000000", + foreground_colour = "#d8d8d8", + }), + de.substyle("*-selection", { + background_colour = "#f0c000", + foreground_colour = "#000000", + }), +}) + +dopath("lookcommon_emboss") + +gr.refresh() + diff --git a/etc/look_simpleblue.lua b/etc/look_simpleblue.lua new file mode 100644 index 0000000..0773f7a --- /dev/null +++ b/etc/look_simpleblue.lua @@ -0,0 +1,112 @@ +-- look_simpleblue.lua drawing engine configuration file for Ion. + +if not gr.select_engine("de") then return end + +de.reset() + +de.defstyle("*", { + shadow_colour = "black", + highlight_colour = "black", + background_colour = "#0f1f4f", + foreground_colour = "#9f9f9f", + padding_pixels = 1, + highlight_pixels = 0, + shadow_pixels = 0, + border_style = "elevated", + font = "-*-helvetica-medium-r-normal-*-14-*-*-*-*-*-*-*", + text_align = "center", +}) + +de.defstyle("frame", { + based_on = "*", + shadow_colour = "black", + highlight_colour = "black", + padding_colour = "black", + background_colour = "black", + foreground_colour = "#ffffff", + padding_pixels = 0, + highlight_pixels = 0, + shadow_pixels = 0, + de.substyle("active", { + shadow_colour = "black", + highlight_colour = "black", + background_colour = "black", + foreground_colour = "#ffffff", + }), +}) + +de.defstyle("tab", { + based_on = "*", + font = "-*-helvetica-medium-r-normal-*-12-*-*-*-*-*-*-*", + de.substyle("active-selected", { + shadow_colour = "black", + highlight_colour = "black", + background_colour = "#3f3f3f", + foreground_colour = "#9f9f9f", + }), + de.substyle("active-unselected", { + shadow_colour = "black", + highlight_colour = "black", + background_colour = "#0f1f4f", + foreground_colour = "#9f9f9f", + }), + de.substyle("inactive-selected", { + shadow_colour = "black", + highlight_colour = "black", + background_colour = "#0f1f4f", + foreground_colour = "#9f9f9f", + }), + de.substyle("inactive-unselected", { + shadow_colour = "black", + highlight_colour = "black", + background_colour = "#0f1f4f", + foreground_colour = "#9f9f9f", + }), + text_align = "center", +}) + +de.defstyle("tab-frame", { + based_on = "tab", + de.substyle("*-*-*-*-activity", { + shadow_colour = "#907070", + highlight_colour = "#907070", + background_colour = "#990000", + foreground_colour = "#eeeeee", + }), +}) + +de.defstyle("tab-menuentry", { + based_on = "tab", + text_align = "left", +}) + +de.defstyle("tab-menuentry-big", { + based_on = "tab-menuentry", + font = "-*-helvetica-medium-r-normal-*-17-*-*-*-*-*-*-*", + padding_pixels = 7, +}) + +de.defstyle("input", { + based_on = "*", + shadow_colour = "black", + highlight_colour = "black", + background_colour = "#3f3f3f", + foreground_colour = "white", + padding_pixels = 1, + highlight_pixels = 0, + shadow_pixels = 0, + border_style = "elevated", + de.substyle("*-cursor", { + background_colour = "white", + foreground_colour = "#3f3f3f", + }), + de.substyle("*-selection", { + background_colour = "black", + foreground_colour = "white", + }), +}) + +dopath("lookcommon_clean") + +gr.refresh() + diff --git a/etc/lookcommon_clean.lua b/etc/lookcommon_clean.lua new file mode 100644 index 0000000..cfb059a --- /dev/null +++ b/etc/lookcommon_clean.lua @@ -0,0 +1,60 @@ +-- Settings common to some styles. + +de.defstyle("stdisp", { + based_on = "*", + shadow_pixels = 0, + highlight_pixels = 0, + text_align = "left", + background_colour = "#000000", + foreground_colour = "grey", + font="-misc-fixed-medium-r-*-*-13-*-*-*-*-60-*-*", + + de.substyle("important", { + foreground_colour = "green", + }), + + de.substyle("critical", { + foreground_colour = "red", + }), +}) + +de.defstyle("actnotify", { + based_on = "*", + shadow_colour = "#e0c0c0", + highlight_colour = "#e0c0c0", + background_colour = "#990000", + foreground_colour = "#eeeeee", +}) + +de.defstyle("tab-frame", { + based_on = "tab", + de.substyle("*-*-*-*-activity", { + shadow_colour = "#e0c0c0", + highlight_colour = "#e0c0c0", + background_colour = "#990000", + foreground_colour = "#eeeeee", + }), +}) + +de.defstyle("tab-frame-tiled", { + based_on = "tab-frame", + spacing = 1, +}) + +de.defstyle("frame-tiled", { + based_on = "frame", + shadow_pixels = 0, + highlight_pixels = 0, + padding_pixels = 0, + spacing = 1, +}) + +de.defstyle("frame-tiled-alt", { + based_on = "frame-tiled", + bar = "none", +}) + +de.defstyle("frame-floating", { + based_on = "frame", + bar = "shaped" +}) diff --git a/etc/lookcommon_emboss.lua b/etc/lookcommon_emboss.lua new file mode 100644 index 0000000..4c558e1 --- /dev/null +++ b/etc/lookcommon_emboss.lua @@ -0,0 +1,57 @@ +-- Settings common to some styles. + +de.defstyle("stdisp", { + based_on = "*", + shadow_pixels = 0, + highlight_pixels = 0, + text_align = "left", + + de.substyle("important", { + foreground_colour = "green", + }), + + de.substyle("critical", { + foreground_colour = "red", + }), +}) + +de.defstyle("actnotify", { + based_on = "*", + shadow_colour = "#401010", + highlight_colour = "#eec0c0", + background_colour = "#990000", + foreground_colour = "#eeeeee", +}) + +de.defstyle("tab-frame", { + based_on = "tab", + de.substyle("*-*-*-*-activity", { + shadow_colour = "#401010", + highlight_colour = "#eec0c0", + background_colour = "#990000", + foreground_colour = "#eeeeee", + }), +}) + +de.defstyle("tab-frame-tiled", { + based_on = "tab-frame", + spacing = 1, +}) + +de.defstyle("frame-tiled", { + based_on = "frame", + border_style = "inlaid", + padding_pixels = 1, + spacing = 1, +}) + +de.defstyle("frame-floating", { + based_on = "frame", + border_style = "ridge", + bar = "shaped" +}) + +de.defstyle("frame-tiled-alt", { + based_on = "frame-tiled", + bar = "none", +}) diff --git a/exact-version b/exact-version new file mode 100644 index 0000000..d04e47f --- /dev/null +++ b/exact-version @@ -0,0 +1,5 @@ + +Context: + +[TAG ion-3ds-20061223 +Tuomo Valkonen **20061223145904] diff --git a/install-sh b/install-sh new file mode 100755 index 0000000..e9de238 --- /dev/null +++ b/install-sh @@ -0,0 +1,251 @@ +#!/bin/sh +# +# install - install a program, script, or datafile +# This comes from X11R5 (mit/util/scripts/install.sh). +# +# Copyright 1991 by the Massachusetts Institute of Technology +# +# Permission to use, copy, modify, distribute, and sell this software and its +# documentation for any purpose is hereby granted without fee, provided that +# the above copyright notice appear in all copies and that both that +# copyright notice and this permission notice appear in supporting +# documentation, and that the name of M.I.T. not be used in advertising or +# publicity pertaining to distribution of the software without specific, +# written prior permission. M.I.T. makes no representations about the +# suitability of this software for any purpose. It is provided "as is" +# without express or implied warranty. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. It can only install one file at a time, a restriction +# shared with many OS's install programs. + + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" +mkdirprog="${MKDIRPROG-mkdir}" + +transformbasename="" +transform_arg="" +instcmd="$mvprog" +chmodcmd="$chmodprog 0755" +chowncmd="" +chgrpcmd="" +stripcmd="" +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src="" +dst="" +dir_arg="" + +while [ x"$1" != x ]; do + case $1 in + -c) instcmd="$cpprog" + shift + continue;; + + -d) dir_arg=true + shift + continue;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + -s) stripcmd="$stripprog" + shift + continue;; + + -t=*) transformarg=`echo $1 | sed 's/-t=//'` + shift + continue;; + + -b=*) transformbasename=`echo $1 | sed 's/-b=//'` + shift + continue;; + + *) if [ x"$src" = x ] + then + src=$1 + else + # this colon is to work around a 386BSD /bin/sh bug + : + dst=$1 + fi + shift + continue;; + esac +done + +if [ x"$src" = x ] +then + echo "install: no input file specified" + exit 1 +else + true +fi + +if [ x"$dir_arg" != x ]; then + dst=$src + src="" + + if [ -d $dst ]; then + instcmd=: + chmodcmd="" + else + instcmd=mkdir + fi +else + +# Waiting for this to be detected by the "$instcmd $src $dsttmp" command +# might cause directories to be created, which would be especially bad +# if $src (and thus $dsttmp) contains '*'. + + if [ -f $src -o -d $src ] + then + true + else + echo "install: $src does not exist" + exit 1 + fi + + if [ x"$dst" = x ] + then + echo "install: no destination specified" + exit 1 + else + true + fi + +# If destination is a directory, append the input filename; if your system +# does not like double slashes in filenames, you may need to add some logic + + if [ -d $dst ] + then + dst="$dst"/`basename $src` + else + true + fi +fi + +## this sed command emulates the dirname command +dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` + +# Make sure that the destination directory exists. +# this part is taken from Noah Friedman's mkinstalldirs script + +# Skip lots of stat calls in the usual case. +if [ ! -d "$dstdir" ]; then +defaultIFS=' +' +IFS="${IFS-${defaultIFS}}" + +oIFS="${IFS}" +# Some sh's can't handle IFS=/ for some reason. +IFS='%' +set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` +IFS="${oIFS}" + +pathcomp='' + +while [ $# -ne 0 ] ; do + pathcomp="${pathcomp}${1}" + shift + + if [ ! -d "${pathcomp}" ] ; + then + $mkdirprog "${pathcomp}" + else + true + fi + + pathcomp="${pathcomp}/" +done +fi + +if [ x"$dir_arg" != x ] +then + $doit $instcmd $dst && + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi +else + +# If we're going to rename the final executable, determine the name now. + + if [ x"$transformarg" = x ] + then + dstfile=`basename $dst` + else + dstfile=`basename $dst $transformbasename | + sed $transformarg`$transformbasename + fi + +# don't allow the sed command to completely eliminate the filename + + if [ x"$dstfile" = x ] + then + dstfile=`basename $dst` + else + true + fi + +# Make a temp file name in the proper directory. + + dsttmp=$dstdir/#inst.$$# + +# Move or copy the file name to the temp name + + $doit $instcmd $src $dsttmp && + + trap "rm -f ${dsttmp}" 0 && + +# and set any options; do chmod last to preserve setuid bits + +# If any of these fail, we abort the whole thing. If we want to +# ignore errors from any of these, just make sure not to ignore +# errors from the above "$doit $instcmd $src $dsttmp" command. + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && + +# Now rename the file to the real destination. + + $doit $rmcmd -f $dstdir/$dstfile && + $doit $mvcmd $dsttmp $dstdir/$dstfile + +fi && + + +exit 0 diff --git a/ion/Makefile b/ion/Makefile new file mode 100644 index 0000000..a79fa9f --- /dev/null +++ b/ion/Makefile @@ -0,0 +1,61 @@ +## +## Ion Makefile +## + +# System-specific configuration is in system.mk +TOPDIR=.. +include $(TOPDIR)/build/system-inc.mk + +# List of modules to possibly preload +include $(TOPDIR)/modulelist.mk + +###################################### + +SOURCES=ion.c + +TARGETS=ion3 + +INCLUDES += $(X11_INCLUDES) +INCLUDES += $(LIBMAINLOOP_INCLUDES) $(LIBTU_INCLUDES) $(LIBEXTL_INCLUDES) +INCLUDES += -I.. + +LIBS += $(X11_LIBS) $(XINERAMA_LIBS) +LIBS += $(WHOLEA) $(LIBMAINLOOP_LIBS) $(LIBEXTL_LIBS) $(LIBTU_LIBS) $(NO_WHOLEA) +LIBS += $(LUA_LIBS) $(DL_LIBS) +LIBS += -lm + +ifeq ($(PRELOAD_MODULES),1) +EXT_OBJS += $(foreach mod, $(MODULE_LIST), ../$(mod)/$(mod).a) +DEPEND_DEPENDS += preload.c +SOURCES += preload.c +TO_CLEAN += preload.c +LIBS += -lSM -lICE +else +LINKOPTS = $(EXPORT_DYNAMIC) +WHOLEA = -Wl,-whole-archive +NO_WHOLEA = -Wl,-no-whole-archive +endif + +EXT_OBJS += ../ioncore/ioncore.a + +DEFINES += -DETCDIR=\"$(ETCDIR)\" -DSHAREDIR=\"$(SHAREDIR)\" \ + -DEXTRABINDIR=\"$(EXTRABINDIR)\" -DMODULEDIR=\"$(MODULEDIR)\" \ + -DLCDIR=\"$(LCDIR)\" -DLOCALEDIR=\"$(LOCALEDIR)\" + +CFLAGS += $(XOPEN_SOURCE) $(C99_SOURCE) + +###################################### + +include $(TOPDIR)/build/rules.mk + +###################################### + +ion3: $(OBJS) $(EXT_OBJS) + $(CC) $(LINKOPTS) $(OBJS) $(WHOLEA) $(EXT_OBJS) $(NO_WHOLEA) $(LDFLAGS) -o $@ + +preload.c: + $(LUA) ../build/mkpreload.lua $(MODULE_LIST) > preload.c + +_install: + $(INSTALLDIR) $(BINDIR) + $(INSTALL) -m $(BIN_MODE) ion3 $(BINDIR) diff --git a/ion/ion.c b/ion/ion.c new file mode 100644 index 0000000..93a5977 --- /dev/null +++ b/ion/ion.c @@ -0,0 +1,293 @@ +/* + * ion/ion/ion.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include "../version.h" + + +/* Options. Getopt is not used because getopt_long is quite gnu-specific + * and they don't know of '-display foo' -style args anyway. + * Instead, I've reinvented the wheel in libtu :(. + */ +static OptParserOpt ion_opts[]={ + {OPT_ID('d'), "display", OPT_ARG, "host:dpy.scr", + DUMMY_TR("X display to use")}, + + {'c', "conffile", OPT_ARG, "config_file", + DUMMY_TR("Configuration file")}, + + {'s', "searchdir", OPT_ARG, "dir", + DUMMY_TR("Add directory to search path")}, + + {OPT_ID('o'), "oneroot", 0, NULL, + DUMMY_TR("Manage default root window/non-Xinerama screen only")}, + +#if defined(CF_XINERAMA) || defined(CF_SUN_XINERAMA) + {OPT_ID('x'), "xinerama", OPT_ARG, "1|0", + DUMMY_TR("Use Xinerama screen information (default: 1/yes)")}, +#else + {OPT_ID('x'), "xinerama", OPT_ARG, "?", + DUMMY_TR("Ignored: not compiled with Xinerama support")}, +#endif + + {OPT_ID('s'), "session", OPT_ARG, "session_name", + DUMMY_TR("Name of session (affects savefiles)")}, + + {OPT_ID('S'), "smclientid", OPT_ARG, "client_id", + DUMMY_TR("Session manager client ID")}, + + {OPT_ID('N'), "noerrorlog", 0, NULL, + DUMMY_TR("Do not create startup error log and display it " + "with xmessage.")}, + + {'h', "help", 0, NULL, + DUMMY_TR("Show this help")}, + + {'V', "version", 0, NULL, + DUMMY_TR("Show program version")}, + + {OPT_ID('a'), "about", 0, NULL, + DUMMY_TR("Show about text")}, + + END_OPTPARSEROPTS +}; + + +void check_new_user_help() +{ + const char *userdir=extl_userdir(); + char *oldbeard=NULL; + char *tmp=NULL, *cmd=NULL; + pid_t pid; + bool ret; + + if(userdir==NULL){ + warn(TR("Could not get user configuration file directory.")); + return; + } + + libtu_asprintf(&oldbeard, "%s/.welcome_msg_displayed", userdir); + + if(oldbeard==NULL) + return; + + if(access(oldbeard, F_OK)==0){ + free(oldbeard); + return; + } + + libtu_asprintf(&tmp, TR("%s/welcome.txt"), SHAREDIR); + + if(tmp!=NULL){ + if(access(tmp, F_OK)==0) + libtu_asprintf(&cmd, "%s %s", CF_XMESSAGE, tmp); + else + libtu_asprintf(&cmd, "%s %s/welcome.txt", CF_XMESSAGE, SHAREDIR); + + free(tmp); + + if(cmd!=NULL){ + ret=ioncore_exec(cmd); + + free(cmd); + + if(ret){ + /* This should actually be done when less or xmessage returns, + * but that would mean yet another script... + */ + mkdir(userdir, 0700); + if(open(oldbeard, O_CREAT|O_RDWR, 0600)<0) + warn_err_obj(oldbeard); + } + } + } + + free(oldbeard); +} + + +static void help() +{ + int i; + printf(TR("Usage: %s [options]\n\n"), prog_execname()); + for(i=0; ion_opts[i].descr!=NULL; i++) + ion_opts[i].descr=TR(ion_opts[i].descr); + optparser_printhelp(OPTP_MIDLONG, ion_opts); + printf("\n"); +} + + +int main(int argc, char*argv[]) +{ + const char *cfgfile="cfg_ion"; + const char *display=NULL; + char *cmd=NULL; + int stflags=0; + int opt; + ErrorLog el; + FILE *ef=NULL; + char *efnam=NULL; + bool may_continue=FALSE; + bool noerrorlog=FALSE; + + libtu_init(argv[0]); + + if(!ioncore_init("ion3", argc, argv, LOCALEDIR)) + return EXIT_FAILURE; + + extl_add_searchdir(EXTRABINDIR); /* ion-completefile */ + extl_add_searchdir(MODULEDIR); + extl_add_searchdir(ETCDIR); + extl_add_searchdir(SHAREDIR); + extl_add_searchdir(LCDIR); + extl_set_userdirs("ion3"); + + optparser_init(argc, argv, OPTP_MIDLONG, ion_opts); + + while((opt=optparser_get_opt())){ + switch(opt){ + case OPT_ID('d'): + display=optparser_get_arg(); + break; + case 'c': + cfgfile=optparser_get_arg(); + break; + case 's': + extl_add_searchdir(optparser_get_arg()); + break; + case OPT_ID('S'): + ioncore_g.sm_client_id=optparser_get_arg(); + break; + case OPT_ID('o'): + stflags|=IONCORE_STARTUP_ONEROOT; + break; + case OPT_ID('x'): + { + const char *p=optparser_get_arg(); + if(strcmp(p, "1")==0) + stflags&=~IONCORE_STARTUP_NOXINERAMA; + else if(strcmp(p, "0")==0) + stflags|=IONCORE_STARTUP_NOXINERAMA; + else + warn(TR("Invalid parameter to -xinerama.")); + } + break; + case OPT_ID('s'): + extl_set_sessiondir(optparser_get_arg()); + break; + case OPT_ID('N'): + noerrorlog=TRUE; + break; + case 'h': + help(); + return EXIT_SUCCESS; + case 'V': + printf("%s\n", ION_VERSION); + return EXIT_SUCCESS; + case OPT_ID('a'): + printf("%s\n", ioncore_aboutmsg()); + return EXIT_SUCCESS; + default: + warn(TR("Invalid command line.")); + help(); + return EXIT_FAILURE; + } + } + + if(!noerrorlog){ + /* We may have to pass the file to xmessage so just using tmpfile() + * isn't sufficient. + */ + libtu_asprintf(&efnam, "%s/ion-%d-startup-errorlog", P_tmpdir, + getpid()); + if(efnam==NULL){ + warn_err(); + }else{ + ef=fopen(efnam, "wt"); + if(ef==NULL){ + warn_err_obj(efnam); + free(efnam); + efnam=NULL; + }else{ + cloexec_braindamage_fix(fileno(ef)); + fprintf(ef, TR("Ion startup error log:\n")); + errorlog_begin_file(&el, ef); + } + } + } + + if(ioncore_startup(display, cfgfile, stflags)) + may_continue=TRUE; + +fail: + if(!may_continue) + warn(TR("Refusing to start due to encountered errors.")); + else + check_new_user_help(); + + if(ef!=NULL){ + pid_t pid=-1; + if(errorlog_end(&el) && ioncore_g.dpy!=NULL){ + fclose(ef); + pid=fork(); + if(pid==0){ + ioncore_setup_environ(DefaultScreen(ioncore_g.dpy)); + if(!may_continue) + XCloseDisplay(ioncore_g.dpy); + else + close(ioncore_g.conn); + libtu_asprintf(&cmd, CF_XMESSAGE " %s", efnam); + if(cmd==NULL){ + warn_err(); + }else if(system(cmd)==-1){ + warn_err_obj(cmd); + } + unlink(efnam); + exit(EXIT_SUCCESS); + } + if(!may_continue && pid>0) + waitpid(pid, NULL, 0); + }else{ + fclose(ef); + } + if(pid<0) + unlink(efnam); + free(efnam); + } + + if(!may_continue) + return EXIT_FAILURE; + + ioncore_mainloop(); + + /* The code should never return here */ + return EXIT_SUCCESS; +} diff --git a/ioncore/Makefile b/ioncore/Makefile new file mode 100644 index 0000000..c0bd8d9 --- /dev/null +++ b/ioncore/Makefile @@ -0,0 +1,53 @@ +## +## Ioncore Makefile +## + +# System-specific configuration is in system.mk +TOPDIR=.. +include $(TOPDIR)/build/system-inc.mk + +###################################### + +INCLUDES += $(X11_INCLUDES) $(LIBTU_INCLUDES) $(LIBEXTL_INCLUDES) -I.. + +CFLAGS += $(XOPEN_SOURCE) $(C99_SOURCE) + +SOURCES=binding.c conf-bindings.c cursor.c event.c exec.c focus.c \ + strings.c key.c modules.c mwmhints.c pointer.c property.c \ + screen.c sizehint.c window.c ioncore.c \ + xic.c selection.c clientwin.c colormap.c region.c eventh.c \ + attach.c resize.c grab.c manage.c regbind.c \ + rootwin.c tags.c names.c saveload.c frame.c \ + frame-pointer.c conf.c reginfo.c extlconv.c fullscreen.c mplex.c \ + bindmaps.c gr.c infowin.c activity.c netwm.c frame-draw.c \ + kbresize.c rectangle.c xwindow.c presize.c extlrx.c \ + pholder.c mplexpholder.c llist.c basicpholder.c sizepolicy.c \ + stacking.c group.c grouppholder.c group-cw.c navi.c \ + group-ws.c float-placement.c groupedpholder.c framedpholder.c + +LUA_SOURCES=\ + ioncore_ext.lua ioncore_luaext.lua ioncore_bindings.lua \ + ioncore_winprops.lua ioncore_misc.lua ioncore_efbb.lua \ + ioncore_wd.lua ioncore_menudb.lua + +ifeq ($(PRELOAD_MODULES),1) +CFLAGS += -DCF_PRELOAD_MODULES +endif + +MAKE_EXPORTS=ioncore + +TARGETS=ioncore.a + +include $(TOPDIR)/libmainloop/rx.mk + +###################################### + +include $(TOPDIR)/build/rules.mk + +###################################### + +ioncore.a: $(OBJS) + $(AR) $(ARFLAGS) $@ $+ + $(RANLIB) $@ + +_install: lc_install diff --git a/ioncore/activity.c b/ioncore/activity.c new file mode 100644 index 0000000..0f7b7a6 --- /dev/null +++ b/ioncore/activity.c @@ -0,0 +1,172 @@ +/* + * ion/ioncore/activity.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include "common.h" +#include "global.h" +#include "region.h" +#include "activity.h" + + +static ObjList *actlist=NULL; + + +void region_mark_mgd_activity(WRegion *mgr) +{ + bool mgr_marked; + + if(mgr==NULL) + return; + + mgr_marked=region_is_activity_r(mgr); + mgr->mgd_activity++; + + if(!mgr_marked){ + region_notify_change(mgr, "sub-activity"); + region_mark_mgd_activity(REGION_MANAGER(mgr)); + } +} + + +void region_clear_mgd_activity(WRegion *mgr) +{ + if(mgr==NULL) + return; + + mgr->mgd_activity=maxof(0, mgr->mgd_activity-1); + + if(!region_is_activity_r(mgr)){ + region_notify_change(mgr, "sub-activity"); + region_clear_mgd_activity(REGION_MANAGER(mgr)); + } +} + + +static void propagate_activity(WRegion *reg) +{ + region_mark_mgd_activity(REGION_MANAGER(reg)); +} + + +static void propagate_clear(WRegion *reg) +{ + region_clear_mgd_activity(REGION_MANAGER(reg)); +} + + +bool region_set_activity(WRegion *reg, int sp) +{ + bool set=(reg->flags®ION_ACTIVITY); + bool nset=libtu_do_setparam(sp, set); + + if(!XOR(set, nset)) + return nset; + + if(nset){ + if(REGION_IS_ACTIVE(reg)) + return FALSE; + + reg->flags|=REGION_ACTIVITY; + objlist_insert_last(&actlist, (Obj*)reg); + + if(reg->mgd_activity==0) + propagate_activity(reg); + }else{ + reg->flags&=~REGION_ACTIVITY; + objlist_remove(&actlist, (Obj*)reg); + + if(reg->mgd_activity==0) + propagate_clear(reg); + } + + region_notify_change(reg, "activity"); + + return nset; +} + + +/*EXTL_DOC + * Set activity flag of \var{reg}. The \var{how} parameter most be + * one of (set/unset/toggle). + */ +EXTL_EXPORT_AS(WRegion, set_activity) +bool region_set_activity_extl(WRegion *reg, const char *how) +{ + return region_set_activity(reg, libtu_string_to_setparam(how)); +} + + +/*EXTL_DOC + * Is activity notification set on \var{reg}. + */ +EXTL_SAFE +EXTL_EXPORT_MEMBER +bool region_is_activity(WRegion *reg) +{ + return (reg->flags®ION_ACTIVITY); +} + + +bool region_is_activity_r(WRegion *reg) +{ + return (reg->flags®ION_ACTIVITY || reg->mgd_activity!=0); +} + + +/*EXTL_DOC + * Return list of regions with activity/urgency bit set. + */ +EXTL_SAFE +EXTL_EXPORT +ExtlTab ioncore_activity_list() +{ + ExtlTab t=extl_create_table(); + ObjListIterTmp tmp; + Obj *obj; + int i=1; + + FOR_ALL_ON_OBJLIST(Obj*, obj, actlist, tmp){ + extl_table_seti_o(t, i, obj); + } + + return t; +} + + +/*EXTL_DOC + * Return first regio non activity list. + */ +EXTL_SAFE +EXTL_EXPORT +WRegion *ioncore_activity_first() +{ + if(actlist==NULL) + return NULL; + return (WRegion*)actlist->watch.obj; +} + + +/*EXTL_DOC + * Go to first region on activity list. + */ +EXTL_EXPORT +bool ioncore_goto_activity() +{ + WRegion *reg=ioncore_activity_first(); + + if(reg!=NULL) + return region_goto(reg); + else + return FALSE; +} + diff --git a/ioncore/activity.h b/ioncore/activity.h new file mode 100644 index 0000000..912e883 --- /dev/null +++ b/ioncore/activity.h @@ -0,0 +1,31 @@ +/* + * ion/ioncore/activity.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_ACTIVITY_H +#define ION_IONCORE_ACTIVITY_H + +#include +#include +#include +#include "region.h" + +extern bool region_set_activity(WRegion *reg, int sp); +extern bool region_is_activity(WRegion* re); +extern bool region_is_activity_r(WRegion *reg); + +extern void region_mark_mgd_activity(WRegion *mgr); +extern void region_clear_mgd_activity(WRegion *mgr); + +extern ExtlTab ioncore_activity_list(); +extern WRegion *ioncore_activity_first(); +extern bool ioncore_goto_activity(); + +#endif /* ION_IONCORE_ACTIVITY_H */ diff --git a/ioncore/attach.c b/ioncore/attach.c new file mode 100644 index 0000000..23004cb --- /dev/null +++ b/ioncore/attach.c @@ -0,0 +1,165 @@ +/* + * ion/ioncore/attach.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include + +#include "common.h" +#include "global.h" +#include "region.h" +#include "attach.h" +#include "clientwin.h" +#include "saveload.h" +#include "manage.h" +#include "extlconv.h" +#include "names.h" + + +/*{{{ Helper */ + + +static WRegion *doit_new(WRegion *mgr, + WWindow *par, const WFitParams *fp, + WRegionDoAttachFn *cont, void *cont_param, + WRegionCreateFn *fn, void *fn_param) +{ + WRegion *reg=fn(par, fp, fn_param); + + if(reg==NULL) + return NULL; + + if(!cont(mgr, reg, cont_param)){ + destroy_obj((Obj*)reg); + return NULL; + } + + return reg; +} + + +static WRegion *doit_reparent(WRegion *mgr, + WWindow *par, const WFitParams *fp, + WRegionDoAttachFn *cont, void *cont_param, + WRegion *reg) +{ + WFitParams fp2; + + if(!region_attach_reparent_check(mgr, reg)) + return NULL; + + if(fp->mode®ION_FIT_WHATEVER){ + /* fp->g is not final; substitute size with current to avoid + * useless resizing. + */ + fp2.mode=fp->mode; + fp2.g.x=fp->g.x; + fp2.g.y=fp->g.y; + fp2.g.w=REGION_GEOM(reg).w; + fp2.g.h=REGION_GEOM(reg).h; + fp=&fp2; + } + + if(!region_fitrep(reg, par, fp)){ + warn(TR("Unable to reparent.")); + return NULL; + } + + region_detach_manager(reg); + + if(!cont(mgr, reg, cont_param)){ + #warning "TODO: What?" + return NULL; + } + + return reg; +} + + +static WRegion *wrap_load(WWindow *par, const WFitParams *fp, + ExtlTab *tab) +{ + return create_region_load(par, fp, *tab); +} + + +static WRegion *doit_load(WRegion *mgr, + WWindow *par, const WFitParams *fp, + WRegionDoAttachFn *cont, void *cont_param, + ExtlTab tab) +{ + WRegion *reg; + + if(extl_table_gets_o(tab, "reg", (Obj**)®)){ + if(!OBJ_IS(reg, WRegion)) + return FALSE; + return doit_reparent(mgr, par, fp, cont, cont_param, reg); + } + + return doit_new(mgr, par, fp, cont, cont_param, + (WRegionCreateFn*)wrap_load, &tab); +} + +WRegion *region_attach_helper(WRegion *mgr, + WWindow *par, const WFitParams *fp, + WRegionDoAttachFn *fn, void *fn_param, + const WRegionAttachData *data) +{ + if(data->type==REGION_ATTACH_NEW){ + return doit_new(mgr, par, fp, fn, fn_param, + data->u.n.fn, data->u.n.param); + }else if(data->type==REGION_ATTACH_LOAD){ + return doit_load(mgr, par, fp, fn, fn_param, data->u.tab); + }else if(data->type==REGION_ATTACH_REPARENT){ + return doit_reparent(mgr, par, fp, fn, fn_param, data->u.reg); + }else{ + return NULL; + } +} + + +/*}}}*/ + + +/*{{{ Reparent check */ + + +bool region_attach_reparent_check(WRegion *mgr, WRegion *reg) +{ + WRegion *reg2; + + /*if(REGION_MANAGER(reg)==mgr){ + warn(TR("Same manager.")); + return FALSE; + }*/ + + /* Check that reg is not a parent or manager of mgr */ + for(reg2=mgr; reg2!=NULL; reg2=REGION_MANAGER(reg2)){ + if(reg2==reg) + goto err; + } + + for(reg2=REGION_PARENT_REG(mgr); reg2!=NULL; reg2=REGION_PARENT_REG(reg2)){ + if(reg2==reg) + goto err; + } + + return TRUE; + +err: + warn(TR("Attempt to make region %s manage its ancestor %s."), + region_name(mgr), region_name(reg)); + return FALSE; +} + + +/*}}}*/ + + diff --git a/ioncore/attach.h b/ioncore/attach.h new file mode 100644 index 0000000..9183ee2 --- /dev/null +++ b/ioncore/attach.h @@ -0,0 +1,59 @@ +/* + * ion/ioncore/attach.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_ATTACH_H +#define ION_IONCORE_ATTACH_H + +#include "region.h" +#include "reginfo.h" +#include "window.h" + + +typedef WRegion *WRegionCreateFn(WWindow *parent, + const WFitParams *fp, + void *param); + +typedef WRegion *WRegionAttachFn(WRegion *reg, + void *param, + WRegionAttachData *data); + + +typedef enum{ + REGION_ATTACH_REPARENT, + REGION_ATTACH_NEW, + REGION_ATTACH_LOAD +} WRegionAttachType; + + +DECLSTRUCT(WRegionAttachData){ + WRegionAttachType type; + union{ + WRegion *reg; + struct{ + WRegionCreateFn *fn; + void *param; + } n; + ExtlTab tab; + } u; +}; + + +typedef bool WRegionDoAttachFn(WRegion *reg, WRegion *sub, void *param); +typedef bool WRegionDoAttachFnSimple(WRegion *reg, WRegion *sub); + +extern WRegion *region_attach_helper(WRegion *mgr, + WWindow *par, const WFitParams *fp, + WRegionDoAttachFn *fn, void *fn_param, + const WRegionAttachData *data); + +extern bool region_attach_reparent_check(WRegion *mgr, WRegion *reg); + +#endif /* ION_IONCORE_ATTACH_H */ diff --git a/ioncore/basicpholder.c b/ioncore/basicpholder.c new file mode 100644 index 0000000..b8b2e96 --- /dev/null +++ b/ioncore/basicpholder.c @@ -0,0 +1,122 @@ +/* + * ion/ioncore/basicpholder.c + * + * Copyright (c) Tuomo Valkonen 2005-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include + +#include "basicpholder.h" + + +/*{{{ Init/deinit */ + + +static void basicpholder_watch_handler(Watch *watch, Obj *reg) +{ + WBasicPHolder *ph=FIELD_TO_STRUCT(WBasicPHolder, reg_watch, watch); + pholder_redirect(&(ph->ph), (WRegion*)reg); +} + + +bool basicpholder_init(WBasicPHolder *ph, WRegion *reg, + WBasicPHolderHandler *hnd) +{ + assert(reg!=NULL && hnd!=NULL); + + pholder_init(&(ph->ph)); + + watch_init(&(ph->reg_watch)); + + if(!watch_setup(&(ph->reg_watch), (Obj*)reg, basicpholder_watch_handler)){ + pholder_deinit(&(ph->ph)); + return FALSE; + } + + ph->hnd=hnd; + + return TRUE; +} + + +WBasicPHolder *create_basicpholder(WRegion *reg, + WBasicPHolderHandler *hnd) +{ + CREATEOBJ_IMPL(WBasicPHolder, basicpholder, (p, reg, hnd)); +} + + +void basicpholder_deinit(WBasicPHolder *ph) +{ + watch_reset(&(ph->reg_watch)); + pholder_deinit(&(ph->ph)); +} + + +/*}}}*/ + + +/*{{{ Dynfuns */ + + +WRegion *basicpholder_do_attach(WBasicPHolder *ph, int flags, + WRegionAttachData *data) +{ + WRegion *reg=(WRegion*)ph->reg_watch.obj; + + if(reg==NULL || ph->hnd==NULL) + return FALSE; + + return ph->hnd(reg, flags, data); +} + + +bool basicpholder_do_goto(WBasicPHolder *ph) +{ + WRegion *reg=(WRegion*)ph->reg_watch.obj; + + if(reg!=NULL) + return region_goto((WRegion*)reg); + + return FALSE; +} + + +WRegion *basicpholder_do_target(WBasicPHolder *ph) +{ + return (WRegion*)ph->reg_watch.obj; +} + + +/*}}}*/ + + +/*{{{ Class information */ + + +static DynFunTab basicpholder_dynfuntab[]={ + {(DynFun*)pholder_do_attach, + (DynFun*)basicpholder_do_attach}, + + {(DynFun*)pholder_do_goto, + (DynFun*)basicpholder_do_goto}, + + {(DynFun*)pholder_do_target, + (DynFun*)basicpholder_do_target}, + + END_DYNFUNTAB +}; + +IMPLCLASS(WBasicPHolder, WPHolder, basicpholder_deinit, + basicpholder_dynfuntab); + + +/*}}}*/ + diff --git a/ioncore/basicpholder.h b/ioncore/basicpholder.h new file mode 100644 index 0000000..5008537 --- /dev/null +++ b/ioncore/basicpholder.h @@ -0,0 +1,46 @@ +/* + * ion/ioncore/basicpholder.h + * + * Copyright (c) Tuomo Valkonen 2005-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_BASICPHOLDER_H +#define ION_IONCORE_BASICPHOLDER_H + +#include "common.h" +#include "pholder.h" +#include "attach.h" + + +typedef WRegion *WBasicPHolderHandler(WRegion *reg, int flags, + WRegionAttachData *data); + +INTRCLASS(WBasicPHolder); + +DECLCLASS(WBasicPHolder){ + WPHolder ph; + Watch reg_watch; + WBasicPHolderHandler* hnd; +}; + +extern WBasicPHolder *create_basicpholder(WRegion *reg, + WBasicPHolderHandler *hnd); + +extern bool basicpholder_init(WBasicPHolder *ph, WRegion *reg, + WBasicPHolderHandler *hnd); + +extern void basicpholder_deinit(WBasicPHolder *ph); + +extern bool basicpholder_do_goto(WBasicPHolder *ph); + +extern WRegion *basicpholder_do_target(WBasicPHolder *ph); + +extern WRegion *basicpholder_do_attach(WBasicPHolder *ph, int flags, + WRegionAttachData *data); + +#endif /* ION_IONCORE_BASICPHOLDER_H */ diff --git a/ioncore/binding.c b/ioncore/binding.c new file mode 100644 index 0000000..4d03abd --- /dev/null +++ b/ioncore/binding.c @@ -0,0 +1,745 @@ +/* + * ion/ioncore/binding.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include "common.h" +#include "event.h" +#include "binding.h" +#include "global.h" +#include +#include "regbind.h" +#include + + +#ifndef CF_NO_LOCK_HACK +#define CF_HACK_IGNORE_EVIL_LOCKS +#endif + +#ifdef CF_HACK_IGNORE_EVIL_LOCKS +#define XK_MISCELLANY +#include +#endif + + +/* */ + + +#define N_MODS 8 + +static const uint modmasks[N_MODS]={ + ShiftMask, LockMask, ControlMask, Mod1Mask, Mod2Mask, Mod3Mask, + Mod4Mask, Mod5Mask +}; + +static XModifierKeymap *modmap=NULL; + +#define KNOWN_MODIFIERS_MASK (ShiftMask|LockMask|ControlMask|Mod1Mask|\ + Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask) + +#ifdef CF_HACK_IGNORE_EVIL_LOCKS + +#define N_EVILLOCKS 3 +#define N_LOOKUPEVIL 2 + +static uint evillockmasks[N_EVILLOCKS]={ + 0, 0, LockMask +}; + +static const KeySym evillocks[N_LOOKUPEVIL]={ + XK_Num_Lock, XK_Scroll_Lock +}; + +static uint evilignoremask=LockMask; + +static void lookup_evil_locks(); + +static void evil_grab_key(Display *display, uint keycode, uint modifiers, + Window grab_window, bool owner_events, + int pointer_mode, int keyboard_mode); + +static void evil_grab_button(Display *display, uint button, uint modifiers, + Window grab_window, bool owner_events, + uint event_mask, int pointer_mode, + int keyboard_mode, Window confine_to, + Cursor cursor); + +static void evil_ungrab_key(Display *display, uint keycode, uint modifiers, + Window grab_window); + +static void evil_ungrab_button(Display *display, uint button, uint modifiers, + Window grab_window); + +#endif + + +#define CVAL(A, B, V) ( A->V < B->V ? -1 : (A->V > B->V ? 1 : 0)) + +static int compare_bindings(const WBinding *a, const WBinding *b) +{ + int r=CVAL(a, b, act); + if(r==0){ + r=CVAL(a, b, kcb); + if(r==0){ + r=CVAL(a, b, state); + if(r==0){ + r=CVAL(a, b, area); + } + } + } + return r; +} + +/* This is only used for searching AnyKey etc. */ +static int compare_bindings_ksb(const WBinding *a, const WBinding *b) +{ + int r=CVAL(a, b, act); + if(r==0){ + r=CVAL(a, b, ksb); + if(r==0){ + r=CVAL(a, b, state); + if(r==0){ + r=CVAL(a, b, area); + } + } + } + return r; +} + +#undef CVAL + + +bool init_bindmap(WBindmap *bindmap) +{ + bindmap->rbind_list=NULL; + bindmap->areamap=NULL; + bindmap->nbindings=0; + bindmap->bindings=make_rb(); + if(bindmap->bindings==NULL){ + warn_err(); + return FALSE; + } + return TRUE; +} + + +WBindmap *create_bindmap() +{ + WBindmap *bindmap=ALLOC(WBindmap); + + if(bindmap==NULL){ + warn_err(); + return NULL; + } + + if(!init_bindmap(bindmap)){ + free(bindmap); + return NULL; + } + + return bindmap; +} + + +void binding_deinit(WBinding *binding) +{ + if(binding->submap!=NULL){ + bindmap_destroy(binding->submap); + binding->submap=NULL; + } + + binding->func=extl_unref_fn(binding->func); +} + + +static void do_destroy_binding(WBinding *binding) +{ + assert(binding!=NULL); + binding_deinit(binding); + free(binding); +} + + +static void bindmap_deinit(WBindmap *bindmap) +{ + WBinding *b=NULL; + Rb_node node=NULL; + + while(bindmap->rbind_list!=NULL){ + region_remove_bindmap(bindmap->rbind_list->reg, + bindmap); + } + + if(bindmap->bindings==NULL) + return; + + FOR_ALL_BINDINGS(b, node, bindmap->bindings){ + do_destroy_binding((WBinding*)rb_val(node)); + bindmap->nbindings--; + } + + assert(bindmap->nbindings==0); + + rb_free_tree(bindmap->bindings); + bindmap->bindings=NULL; +} + + +void bindmap_destroy(WBindmap *bindmap) +{ + bindmap_deinit(bindmap); + free(bindmap); +} + + +static void free_map(Rb_node map) +{ + Rb_node node; + WBinding *b; + + FOR_ALL_BINDINGS(b, node, map) + free(b); + + rb_free_tree(map); +} + + +void bindmap_refresh(WBindmap *bindmap) +{ + WRegBindingInfo *rbind; + Rb_node newtree, node; + WBinding *b, *b2; + + if(bindmap->bindings==NULL) + return; + + newtree=make_rb(); + + if(newtree==NULL){ + warn_err(); + return; + } + + FOR_ALL_BINDINGS(b, node, bindmap->bindings){ + b2=ALLOC(WBinding); + if(b2==NULL){ + warn_err(); + free_map(newtree); + return; + } + + *b2=*b; + + if(b->act==BINDING_KEYPRESS){ + for(rbind=bindmap->rbind_list; rbind!=NULL; rbind=rbind->bm_next) + rbind_binding_removed(rbind, b, bindmap); + b2->kcb=XKeysymToKeycode(ioncore_g.dpy, b->ksb); + } + + if(!rb_insertg(newtree, b2, b2, (Rb_compfn*)compare_bindings)){ + warn_err(); + free(b2); + free_map(newtree); + return; + } + } + + free_map(bindmap->bindings); + bindmap->bindings=newtree; + + FOR_ALL_BINDINGS(b, node, bindmap->bindings){ + if(b->act!=BINDING_KEYPRESS) + continue; + for(rbind=bindmap->rbind_list; rbind!=NULL; rbind=rbind->bm_next) + rbind_binding_added(rbind, b, bindmap); + if(b->submap!=NULL) + bindmap_refresh(b->submap); + } +} + + +bool bindmap_add_binding(WBindmap *bindmap, const WBinding *b) +{ + WRegBindingInfo *rbind=NULL; + WBinding *binding=NULL; + Rb_node node=NULL; + int found=0; + + /* Handle adding the binding */ + binding=ALLOC(WBinding); + + if(binding==NULL){ + warn_err(); + return FALSE; + } + + memcpy(binding, b, sizeof(*b)); + + node=rb_find_gkey_n(bindmap->bindings, binding, + (Rb_compfn*)compare_bindings, &found); + + if(found){ + if(!rb_insert_a(node, binding, binding)){ + free(binding); + return FALSE; + } + do_destroy_binding((WBinding*)rb_val(node)); + rb_delete_node(node); + bindmap->nbindings--; + }else{ + if(!rb_insertg(bindmap->bindings, binding, binding, + (Rb_compfn*)compare_bindings)){ + free(binding); + return FALSE; + } + } + + bindmap->nbindings++; + + for(rbind=bindmap->rbind_list; rbind!=NULL; rbind=rbind->bm_next) + rbind_binding_added(rbind, binding, bindmap); + + return TRUE; +} + + +bool bindmap_remove_binding(WBindmap *bindmap, const WBinding *b) +{ + WRegBindingInfo *rbind=NULL; + WBinding *binding=NULL; + Rb_node node=NULL; + int found=0; + + if(bindmap->bindings==NULL) + return FALSE; + + node=rb_find_gkey_n(bindmap->bindings, b, (Rb_compfn*)compare_bindings, + &found); + + if(!found) + return FALSE; + + binding=(WBinding*)rb_val(node); + + for(rbind=bindmap->rbind_list; rbind!=NULL; rbind=rbind->bm_next) + rbind_binding_removed(rbind, binding, bindmap); + + do_destroy_binding(binding); + rb_delete_node(node); + + bindmap->nbindings--; + + return TRUE; +} + + +void ioncore_init_bindings() +{ + modmap=XGetModifierMapping(ioncore_g.dpy); + + assert(modmap!=NULL); + +#ifdef CF_HACK_IGNORE_EVIL_LOCKS + lookup_evil_locks(); +#endif +} + + +void ioncore_update_modmap() +{ + XModifierKeymap *nm=XGetModifierMapping(ioncore_g.dpy); + + if(nm!=NULL){ + XFreeModifiermap(modmap); + modmap=nm; + } +} + + +/* */ + + +void binding_grab_on(const WBinding *binding, Window win) +{ + if(binding->act==BINDING_KEYPRESS && binding->kcb!=0){ +#ifndef CF_HACK_IGNORE_EVIL_LOCKS + XGrabKey(ioncore_g.dpy, binding->kcb, binding->state, win, + True, GrabModeAsync, GrabModeAsync); +#else + evil_grab_key(ioncore_g.dpy, binding->kcb, binding->state, win, + True, GrabModeAsync, GrabModeAsync); +#endif + } + + if(binding->act!=BINDING_BUTTONPRESS && + binding->act!=BINDING_BUTTONCLICK && + binding->act!=BINDING_BUTTONDBLCLICK && + binding->act!=BINDING_BUTTONMOTION) + return; + + if(binding->state==0) + return; + +#ifndef CF_HACK_IGNORE_EVIL_LOCKS + XGrabButton(ioncore_g.dpy, binding->kcb, binding->state, win, + True, IONCORE_EVENTMASK_PTRGRAB, GrabModeAsync, GrabModeAsync, + None, None); +#else + evil_grab_button(ioncore_g.dpy, binding->kcb, binding->state, win, + True, IONCORE_EVENTMASK_PTRGRAB, GrabModeAsync, GrabModeAsync, + None, None); +#endif +} + + +void binding_ungrab_on(const WBinding *binding, Window win) +{ + if(binding->act==BINDING_KEYPRESS){ +#ifndef CF_HACK_IGNORE_EVIL_LOCKS + XUngrabKey(ioncore_g.dpy, binding->kcb, binding->state, win); +#else + evil_ungrab_key(ioncore_g.dpy, binding->kcb, binding->state, win); +#endif + } + + if(binding->act!=BINDING_BUTTONPRESS && + binding->act!=BINDING_BUTTONCLICK && + binding->act!=BINDING_BUTTONDBLCLICK && + binding->act!=BINDING_BUTTONMOTION) + return; + + if(binding->state==0) + return; + +#ifndef CF_HACK_IGNORE_EVIL_LOCKS + XUngrabButton(ioncore_g.dpy, binding->kcb, binding->state, win); +#else + evil_ungrab_button(ioncore_g.dpy, binding->kcb, binding->state, win); +#endif +} + + +/* */ + + +static WBinding *search_binding(WBindmap *bindmap, WBinding *binding) +{ + Rb_node node; + int found=0; + + if(bindmap->bindings==NULL) + return NULL; + + node=rb_find_gkey_n(bindmap->bindings, binding, + (Rb_compfn*)compare_bindings, &found); + + if(found==0) + return NULL; + + return (WBinding*)rb_val(node); +} + + +static WBinding *search_binding_ksb(WBindmap *bindmap, WBinding *binding) +{ + Rb_node node; + int found=0; + + if(bindmap->bindings==NULL) + return NULL; + + node=rb_find_gkey_n(bindmap->bindings, binding, + (Rb_compfn*)compare_bindings_ksb, &found); + + if(found==0) + return NULL; + + return (WBinding*)rb_val(node); +} + + +static WBinding *do_bindmap_lookup_binding(WBindmap *bindmap, + int act, uint state, + uint kcb, int area) +{ + WBinding *binding, tmp; + + if(bindmap->nbindings==0) + return NULL; + +#ifdef CF_HACK_IGNORE_EVIL_LOCKS + state&=~evilignoremask; +#endif + state&=KNOWN_MODIFIERS_MASK; + + tmp.act=act; + tmp.kcb=kcb; + tmp.state=state; + tmp.area=area; + + binding=search_binding(bindmap, &tmp); + + if(binding==NULL){ + tmp.state=AnyModifier; + binding=search_binding(bindmap, &tmp); + + if(binding==NULL){ + tmp.state=state; + tmp.ksb=(act==BINDING_KEYPRESS ? AnyKey : AnyButton); + + binding=search_binding_ksb(bindmap, &tmp); + + if(binding==NULL){ + tmp.state=AnyModifier; + binding=search_binding_ksb(bindmap, &tmp); + } + } + } + + return binding; +} + + +WBinding *bindmap_lookup_binding(WBindmap *bindmap, + int act, uint state, uint kcb) +{ + return do_bindmap_lookup_binding(bindmap, act, state, kcb, 0); +} + + +WBinding *bindmap_lookup_binding_area(WBindmap *bindmap, + int act, uint state, uint kcb, int area) +{ + WBinding *binding; + + binding=do_bindmap_lookup_binding(bindmap, act, state, kcb, area); + + if(binding==NULL) + binding=do_bindmap_lookup_binding(bindmap, act, state, kcb, 0); + + return binding; +} + + +/* + * A dirty hack to deal with (==ignore) evil locking modifier keys. + */ + + +int ioncore_unmod(int state, int keycode) +{ + int j; + +#ifdef CF_HACK_IGNORE_EVIL_LOCKS + state&=~evilignoremask; +#endif + + for(j=0; jmax_keypermod; j++){ + if(modmap->modifiermap[j]==keycode) + return state&~modmasks[j/modmap->max_keypermod]; + } + + return state; +} + + +int ioncore_modstate() +{ + char keys[32]; + int state=0; + int j; + + XQueryKeymap(ioncore_g.dpy, keys); + + for(j=0; jmax_keypermod; j++){ + int a=(modmap->modifiermap[j]&7); + int b=(modmap->modifiermap[j]>>3); + if(b<32){ + if(keys[b]&(1<max_keypermod]; + } + } + +#ifdef CF_HACK_IGNORE_EVIL_LOCKS + state&=~evilignoremask; +#endif + return state; +} + + +bool ioncore_ismod(int keycode) +{ + int j; + + for(j=0; jmax_keypermod; j++){ + if(modmap->modifiermap[j]==keycode) + return TRUE; + } + + return FALSE; +} + + +#ifdef CF_HACK_IGNORE_EVIL_LOCKS + +static void lookup_evil_locks() +{ + uint keycodes[N_LOOKUPEVIL]; + int i, j; + + for(i=0; imax_keypermod; j++){ + for(i=0; imodifiermap[j]==keycodes[i]){ + evillockmasks[i]=modmasks[j/modmap->max_keypermod]; + evilignoremask|=evillockmasks[i]; + } + } + } +} + + +static void evil_grab_key(Display *display, uint keycode, uint modifiers, + Window grab_window, bool owner_events, + int pointer_mode, int keyboard_mode) +{ + uint mods; + int i, j; + + XGrabKey(display, keycode, modifiers, grab_window, owner_events, + pointer_mode, keyboard_mode); + + if(modifiers==AnyModifier) + return; + + for(i=0; i +#include +#include +#include "common.h" +#include "region.h" +#include + + +#define BINDING_KEYPRESS 0 +#define BINDING_BUTTONPRESS 1 +#define BINDING_BUTTONMOTION 2 +#define BINDING_BUTTONCLICK 3 +#define BINDING_BUTTONDBLCLICK 4 + +#define BINDMAP_INIT {0, NULL, NULL, NULL, NULL} + +#define FOR_ALL_BINDINGS(B, NODE, MAP) \ + rb_traverse(NODE, MAP) if(((B)=(WBinding*)rb_val(NODE))!=NULL) + +INTRSTRUCT(WBinding); +INTRSTRUCT(WBindmap); +INTRSTRUCT(WRegBindingInfo); + + +DECLSTRUCT(WBinding){ + uint kcb; /* keycode or button */ + uint ksb; /* keysym or button */ + uint state; + uint act; + int area; + bool wait; + WBindmap *submap; + ExtlFn func; +}; + + + +DECLSTRUCT(WRegBindingInfo){ + WBindmap *bindmap; + WRegBindingInfo *next, *prev; + WRegBindingInfo *bm_next, *bm_prev; + WRegion *reg; + WRegion *owner; + int tmp; +}; + + + +DECLSTRUCT(WBindmap){ + int nbindings; + Rb_node bindings; + WRegBindingInfo *rbind_list; + const StringIntMap *areamap; +}; + + +extern void ioncore_init_bindings(); +extern void ioncore_update_modmap(); +extern int ioncore_unmod(int state, int keycode); +extern bool ioncore_ismod(int keycode); +extern int ioncore_modstate(); + +extern WBindmap *create_bindmap(); + +extern void bindmap_destroy(WBindmap *bindmap); +extern void bindmap_refresh(WBindmap *bindmap); +extern bool bindmap_add_binding(WBindmap *bindmap, const WBinding *binding); +extern bool bindmap_remove_binding(WBindmap *bindmap, const WBinding *binding); +extern WBinding *bindmap_lookup_binding(WBindmap *bindmap, int act, + uint state, uint kcb); +extern WBinding *bindmap_lookup_binding_area(WBindmap *bindmap, int act, + uint state, uint kcb, int area); + +extern void binding_deinit(WBinding *binding); +extern void binding_grab_on(const WBinding *binding, Window win); +extern void binding_ungrab_on(const WBinding *binding, Window win); + +#endif /* ION_IONCORE_BINDING_H */ diff --git a/ioncore/bindmaps.c b/ioncore/bindmaps.c new file mode 100644 index 0000000..dd4a1d0 --- /dev/null +++ b/ioncore/bindmaps.c @@ -0,0 +1,200 @@ +/* + * ion/ioncore/bindmaps.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include "common.h" +#include "conf-bindings.h" +#include "binding.h" +#include +#include "framep.h" +#include "bindmaps.h" + + +/* + * This file contains higher-level bindmap management code + */ + + +WBindmap *ioncore_rootwin_bindmap=NULL; +WBindmap *ioncore_mplex_bindmap=NULL; +WBindmap *ioncore_mplex_toplevel_bindmap=NULL; +WBindmap *ioncore_frame_bindmap=NULL; +WBindmap *ioncore_frame_toplevel_bindmap=NULL; +WBindmap *ioncore_frame_floating_bindmap=NULL; +WBindmap *ioncore_frame_tiled_bindmap=NULL; +WBindmap *ioncore_frame_transient_bindmap=NULL; +WBindmap *ioncore_moveres_bindmap=NULL; +WBindmap *ioncore_group_bindmap=NULL; +WBindmap *ioncore_groupcw_bindmap=NULL; +WBindmap *ioncore_groupws_bindmap=NULL; +WBindmap *ioncore_clientwin_bindmap=NULL; + +static Rb_node known_bindmaps=NULL; + +static StringIntMap frame_areas[]={ + {"border", FRAME_AREA_BORDER}, + {"tab", FRAME_AREA_TAB}, + {"empty_tab", FRAME_AREA_TAB}, + {"client", FRAME_AREA_CLIENT}, + END_STRINGINTMAP +}; + + +#define DO_FREE(X, Y) \ + if(ioncore_ ## X ## _bindmap!=NULL){ \ + ioncore_free_bindmap(Y, ioncore_ ## X ## _bindmap); \ + ioncore_ ## X ## _bindmap=NULL; \ + } + +void ioncore_deinit_bindmaps() +{ + DO_FREE(rootwin, "WScreen"); + DO_FREE(mplex, "WMPlex"); + DO_FREE(mplex_toplevel, "WMPlex.toplevel"); + DO_FREE(frame, "WFrame"); + DO_FREE(frame_toplevel, "WFrame.toplevel"); + DO_FREE(frame_floating, "WFrame.floating"); + DO_FREE(frame_tiled, "WFrame.tiled"); + DO_FREE(frame_transient, "WFrame.transient"); + DO_FREE(moveres, "WMoveresMode"); + DO_FREE(group, "WGroup"); + DO_FREE(groupcw, "WGroupCW"); + DO_FREE(groupws, "WGroupWS"); + DO_FREE(clientwin, "WClientWin"); + rb_free_tree(known_bindmaps); + known_bindmaps=NULL; +} + + +#define DO_ALLOC(X, Y, Z) \ + ioncore_ ## X ## _bindmap=ioncore_alloc_bindmap(Y, Z); \ + if(ioncore_ ## X ## _bindmap==NULL) \ + return FALSE; + +bool ioncore_init_bindmaps() +{ + known_bindmaps=make_rb(); + + if(known_bindmaps==NULL) + return FALSE; + + DO_ALLOC(rootwin, "WScreen", NULL); + DO_ALLOC(mplex, "WMPlex", NULL); + DO_ALLOC(mplex_toplevel, "WMPlex.toplevel", NULL); + DO_ALLOC(frame, "WFrame", frame_areas); + DO_ALLOC(frame_toplevel, "WFrame.toplevel", frame_areas); + DO_ALLOC(frame_floating, "WFrame.floating", frame_areas); + DO_ALLOC(frame_tiled, "WFrame.tiled", frame_areas); + DO_ALLOC(frame_transient, "WFrame.transient", frame_areas); + DO_ALLOC(moveres, "WMoveresMode", NULL); + DO_ALLOC(group, "WGroup", NULL); + DO_ALLOC(groupcw, "WGroupCW", NULL); + DO_ALLOC(groupws, "WGroupWS", NULL); + DO_ALLOC(clientwin, "WClientWin", NULL); + + return TRUE; +} + + + +void ioncore_refresh_bindmaps() +{ + Rb_node node; + + ioncore_update_modmap(); + + rb_traverse(node,known_bindmaps){ + bindmap_refresh((WBindmap*)rb_val(node)); + } +} + + +WBindmap *ioncore_alloc_bindmap(const char *name, const StringIntMap *areas) +{ + WBindmap *bm=create_bindmap(); + + if(bm==NULL) + return NULL; + + bm->areamap=areas; + + if(!rb_insert(known_bindmaps, name, bm)){ + bindmap_destroy(bm); + return NULL; + } + + return bm; +} + + +WBindmap *ioncore_alloc_bindmap_frame(const char *name) +{ + return ioncore_alloc_bindmap(name, frame_areas); +} + + +void ioncore_free_bindmap(const char *name, WBindmap *bm) +{ + int found=0; + Rb_node node; + + node=rb_find_key_n(known_bindmaps, name, &found); + assert(found!=0 && rb_val(node)==(void*)bm); + + rb_delete_node(node); + bindmap_destroy(bm); +} + + +WBindmap *ioncore_lookup_bindmap(const char *name) +{ + int found=0; + Rb_node node; + + node=rb_find_key_n(known_bindmaps, name, &found); + + if(found==0) + return NULL; + + return (WBindmap*)rb_val(node); +} + + +EXTL_EXPORT +bool ioncore_do_defbindings(const char *name, ExtlTab tab) +{ + WBindmap *bm=ioncore_lookup_bindmap(name); + if(bm==NULL){ + warn("Unknown bindmap %s.", name); + return FALSE; + } + return bindmap_defbindings(bm, tab, FALSE); +} + + +EXTL_SAFE +EXTL_EXPORT +ExtlTab ioncore_do_getbindings() +{ + Rb_node node; + ExtlTab tab; + + tab=extl_create_table(); + + rb_traverse(node, known_bindmaps){ + ExtlTab bmtab=bindmap_getbindings((WBindmap*)rb_val(node)); + extl_table_sets_t(tab, (const char*)node->k.key, bmtab); + extl_unref_table(bmtab); + } + + return tab; +} + diff --git a/ioncore/bindmaps.h b/ioncore/bindmaps.h new file mode 100644 index 0000000..bef0ae0 --- /dev/null +++ b/ioncore/bindmaps.h @@ -0,0 +1,46 @@ +/* + * ion/ioncore/bindmaps.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include "binding.h" + +#ifndef ION_IONCORE_BINDMAP_H +#define ION_IONCORE_BINDMAP_H + +extern WBindmap *ioncore_rootwin_bindmap; +extern WBindmap *ioncore_mplex_bindmap; +extern WBindmap *ioncore_mplex_toplevel_bindmap; +extern WBindmap *ioncore_frame_bindmap; +extern WBindmap *ioncore_frame_toplevel_bindmap; +extern WBindmap *ioncore_frame_floating_bindmap; +extern WBindmap *ioncore_frame_tiled_bindmap; +extern WBindmap *ioncore_frame_transient_bindmap; +extern WBindmap *ioncore_moveres_bindmap; +extern WBindmap *ioncore_group_bindmap; +extern WBindmap *ioncore_groupcw_bindmap; +extern WBindmap *ioncore_groupws_bindmap; +extern WBindmap *ioncore_clientwin_bindmap; + +extern void ioncore_deinit_bindmaps(); +extern bool ioncore_init_bindmaps(); +extern void ioncore_refresh_bindmaps(); + +extern WBindmap *ioncore_alloc_bindmap(const char *name, + const StringIntMap *areas); +extern WBindmap *ioncore_alloc_bindmap_frame(const char *name); +extern void ioncore_free_bindmap(const char *name, WBindmap *bm); +extern WBindmap *ioncore_lookup_bindmap(const char *name); + +extern bool ioncore_do_defbindings(const char *name, ExtlTab tab); +extern ExtlTab ioncore_do_getbindings(); + +#endif /* ION_IONCORE_BINDMAP_H */ + diff --git a/ioncore/classes.h b/ioncore/classes.h new file mode 100644 index 0000000..c990b14 --- /dev/null +++ b/ioncore/classes.h @@ -0,0 +1,47 @@ +/* + * ion/ioncore/classes.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_CLASSES_H +#define ION_IONCORE_CLASSES_H + +/* Forward declarations of some classes to avoid problems + * with the header system. + */ + +#include + +INTRCLASS(WClientWin); +INTRCLASS(WFrame); +INTRCLASS(WInfoWin); +INTRCLASS(WMPlex); +INTRCLASS(WRegion); +INTRCLASS(WMoveresMode); +INTRCLASS(WRootWin); +INTRCLASS(WScreen); +INTRCLASS(WWindow); +INTRCLASS(WGroup); +INTRCLASS(WGroupCW); +INTRCLASS(WGroupWS); + +INTRCLASS(WPHolder); +INTRCLASS(WMPlexPHolder); + +INTRSTRUCT(WStacking); +INTRSTRUCT(WLListNode); +INTRSTRUCT(WStackingIterTmp); + +INTRSTRUCT(WSubmapState); + +INTRSTRUCT(WRegionAttachData); + +INTRSTRUCT(WRQGeomParams); + +#endif /* ION_IONCORE_CLASSES_H */ diff --git a/ioncore/clientwin.c b/ioncore/clientwin.c new file mode 100644 index 0000000..4b4dbe4 --- /dev/null +++ b/ioncore/clientwin.c @@ -0,0 +1,1403 @@ +/* + * ion/ioncore/clientwin.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include "common.h" +#include "global.h" +#include "property.h" +#include "focus.h" +#include "sizehint.h" +#include "event.h" +#include "clientwin.h" +#include "colormap.h" +#include "resize.h" +#include "attach.h" +#include "regbind.h" +#include "bindmaps.h" +#include "names.h" +#include "saveload.h" +#include "manage.h" +#include "extlconv.h" +#include "fullscreen.h" +#include "event.h" +#include "rootwin.h" +#include "activity.h" +#include "netwm.h" +#include "xwindow.h" +#include "bindmaps.h" + + +static void set_clientwin_state(WClientWin *cwin, int state); +static bool send_clientmsg(Window win, Atom a, Time stmp); + + +WHook *clientwin_do_manage_alt=NULL; +WHook *clientwin_mapped_hook=NULL; +WHook *clientwin_unmapped_hook=NULL; +WHook *clientwin_property_change_hook=NULL; + + +/*{{{ Get properties */ + + +void clientwin_get_protocols(WClientWin *cwin) +{ + Atom *protocols=NULL, *p; + int n; + + cwin->flags&=~(CLIENTWIN_P_WM_DELETE|CLIENTWIN_P_WM_TAKE_FOCUS); + + if(!XGetWMProtocols(ioncore_g.dpy, cwin->win, &protocols, &n)) + return; + + for(p=protocols; n; n--, p++){ + if(*p==ioncore_g.atom_wm_delete) + cwin->flags|=CLIENTWIN_P_WM_DELETE; + else if(*p==ioncore_g.atom_wm_take_focus) + cwin->flags|=CLIENTWIN_P_WM_TAKE_FOCUS; + } + + if(protocols!=NULL) + XFree((char*)protocols); +} + + +static bool get_winprop_fn_set=FALSE; +static ExtlFn get_winprop_fn; + +/*EXTL_DOC + * Set function used to look up winprops. + */ +EXTL_EXPORT +void ioncore_set_get_winprop_fn(ExtlFn fn) +{ + if(get_winprop_fn_set) + extl_unref_fn(get_winprop_fn); + get_winprop_fn=extl_ref_fn(fn); + get_winprop_fn_set=TRUE; +} + + +static WSizePolicy get_sizepolicy_winprop(WClientWin *cwin, + const char *propname, + WSizePolicy value) +{ + char *szplcy; + + if(extl_table_gets_s(cwin->proptab, propname, &szplcy)){ + string2sizepolicy(szplcy, &value); + free(szplcy); + } + return value; +} + + +#define SIZEHINT_PROPS (CLIENTWIN_PROP_MAXSIZE| \ + CLIENTWIN_PROP_MINSIZE| \ + CLIENTWIN_PROP_ASPECT| \ + CLIENTWIN_PROP_IGNORE_RSZINC) + + +static void clientwin_get_winprops(WClientWin *cwin) +{ + ExtlTab tab, tab2; + int i1, i2; + bool ret; + + if(!get_winprop_fn_set) + return; + + extl_protect(NULL); + ret=extl_call(get_winprop_fn, "o", "t", cwin, &tab); + extl_unprotect(NULL); + + if(!ret) + return; + + cwin->proptab=tab; + + if(tab==extl_table_none()) + return; + + if(extl_table_is_bool_set(tab, "transparent")) + cwin->flags|=CLIENTWIN_PROP_TRANSPARENT; + + if(extl_table_is_bool_set(tab, "acrobatic")) + cwin->flags|=CLIENTWIN_PROP_ACROBATIC; + + if(extl_table_gets_t(tab, "max_size", &tab2)){ + if(extl_table_gets_i(tab2, "w", &i1) && + extl_table_gets_i(tab2, "h", &i2)){ + cwin->size_hints.max_width=i1; + cwin->size_hints.max_height=i2; + cwin->size_hints.flags|=PMaxSize; + cwin->flags|=CLIENTWIN_PROP_MAXSIZE; + } + extl_unref_table(tab2); + } + + if(extl_table_gets_t(tab, "min_size", &tab2)){ + if(extl_table_gets_i(tab2, "w", &i1) && + extl_table_gets_i(tab2, "h", &i2)){ + cwin->size_hints.min_width=i1; + cwin->size_hints.min_height=i2; + cwin->size_hints.flags|=PMinSize; + cwin->flags|=CLIENTWIN_PROP_MINSIZE; + } + extl_unref_table(tab2); + } + + if(extl_table_gets_t(tab, "aspect", &tab2)){ + if(extl_table_gets_i(tab2, "w", &i1) && + extl_table_gets_i(tab2, "h", &i2)){ + cwin->size_hints.min_aspect.x=i1; + cwin->size_hints.max_aspect.x=i1; + cwin->size_hints.min_aspect.y=i2; + cwin->size_hints.max_aspect.y=i2; + cwin->size_hints.flags|=PAspect; + cwin->flags|=CLIENTWIN_PROP_ASPECT; + } + extl_unref_table(tab2); + } + + if(extl_table_is_bool_set(tab, "ignore_resizeinc")) + cwin->flags|=CLIENTWIN_PROP_IGNORE_RSZINC; + + if(extl_table_is_bool_set(tab, "ignore_cfgrq")) + cwin->flags|=CLIENTWIN_PROP_IGNORE_CFGRQ; + +#if 0 + cwin->szplcy=get_sizepolicy_winprop(cwin, "sizepolicy", + SIZEPOLICY_DEFAULT); + cwin->transient_szplcy=get_sizepolicy_winprop(cwin, + "transient_sizepolicy", + DFLT_SZPLCY); +#endif +} + + +void clientwin_get_size_hints(WClientWin *cwin) +{ + XSizeHints tmp=cwin->size_hints; + + xwindow_get_sizehints(cwin->win, &(cwin->size_hints)); + + if(cwin->flags&CLIENTWIN_PROP_MAXSIZE){ + cwin->size_hints.max_width=tmp.max_width; + cwin->size_hints.max_height=tmp.max_height; + cwin->size_hints.flags|=PMaxSize; + } + + if(cwin->flags&CLIENTWIN_PROP_MINSIZE){ + cwin->size_hints.min_width=tmp.min_width; + cwin->size_hints.min_height=tmp.min_height; + cwin->size_hints.flags|=PMinSize; + } + + if(cwin->flags&CLIENTWIN_PROP_ASPECT){ + cwin->size_hints.min_aspect=tmp.min_aspect; + cwin->size_hints.max_aspect=tmp.max_aspect; + cwin->size_hints.flags|=PAspect; + } + + if(cwin->flags&CLIENTWIN_PROP_IGNORE_RSZINC) + cwin->size_hints.flags&=~PResizeInc; +} + + +void clientwin_get_set_name(WClientWin *cwin) +{ + char **list=NULL; + int n=0; + + if(ioncore_g.use_mb) + list=netwm_get_name(cwin); + + if(list==NULL){ + list=xwindow_get_text_property(cwin->win, XA_WM_NAME, &n); + }else{ + cwin->flags|=CLIENTWIN_USE_NET_WM_NAME; + } + + if(list==NULL){ + /* Special condition kludge: property exists, but couldn't + * be converted to a string list. + */ + clientwin_set_name(cwin, (n==-1 ? "???" : NULL)); + }else{ + clientwin_set_name(cwin, *list); + XFreeStringList(list); + } +} + + +/* Some standard winprops */ + + +bool clientwin_get_switchto(const WClientWin *cwin) +{ + bool b; + + if(ioncore_g.opmode==IONCORE_OPMODE_INIT) + return FALSE; + + if(extl_table_gets_b(cwin->proptab, "switchto", &b)) + return b; + + return ioncore_g.switchto_new; +} + + +int clientwin_get_transient_mode(const WClientWin *cwin) +{ + char *s; + int mode=TRANSIENT_MODE_NORMAL; + + if(extl_table_gets_s(cwin->proptab, "transient_mode", &s)){ + if(strcmp(s, "current")==0) + mode=TRANSIENT_MODE_CURRENT; + else if(strcmp(s, "off")==0) + mode=TRANSIENT_MODE_OFF; + free(s); + } + return mode; +} + + +/*}}}*/ + + +/*{{{ Manage/create */ + + +static void configure_cwin_bw(Window win, int bw) +{ + XWindowChanges wc; + ulong wcmask=CWBorderWidth; + + wc.border_width=bw; + XConfigureWindow(ioncore_g.dpy, win, wcmask, &wc); +} + + +static void set_sane_gravity(Window win) +{ + XSetWindowAttributes attr; + + attr.win_gravity=NorthWestGravity; + + XChangeWindowAttributes(ioncore_g.dpy, win, + CWWinGravity, &attr); +} + + +static bool clientwin_init(WClientWin *cwin, WWindow *par, Window win, + XWindowAttributes *attr) +{ + WFitParams fp; + + cwin->flags=0; + cwin->win=win; + cwin->state=WithdrawnState; + + fp.g.x=attr->x; + fp.g.y=attr->y; + fp.g.w=attr->width; + fp.g.h=attr->height; + fp.mode=REGION_FIT_EXACT; + + /* The idiot who invented special server-supported window borders that + * are not accounted for in the window size should be "taken behind a + * sauna". + */ + cwin->orig_bw=attr->border_width; + configure_cwin_bw(cwin->win, 0); + if(cwin->orig_bw!=0 && cwin->size_hints.flags&PWinGravity){ + fp.g.x+=xgravity_deltax(cwin->size_hints.win_gravity, + -cwin->orig_bw, -cwin->orig_bw); + fp.g.y+=xgravity_deltay(cwin->size_hints.win_gravity, + -cwin->orig_bw, -cwin->orig_bw); + } + + set_sane_gravity(cwin->win); + + cwin->transient_for=None; + + cwin->n_cmapwins=0; + cwin->cmap=attr->colormap; + cwin->cmaps=NULL; + cwin->cmapwins=NULL; + cwin->n_cmapwins=0; + cwin->event_mask=IONCORE_EVENTMASK_CLIENTWIN; + + cwin->fs_pholder=NULL; + + region_init(&(cwin->region), par, &fp); + + cwin->region.flags|=REGION_GRAB_ON_PARENT; + region_add_bindmap(&cwin->region, ioncore_clientwin_bindmap); + + XSelectInput(ioncore_g.dpy, win, cwin->event_mask); + + clientwin_register(cwin); + clientwin_get_set_name(cwin); + clientwin_get_colormaps(cwin); + clientwin_get_protocols(cwin); + clientwin_get_winprops(cwin); + clientwin_get_size_hints(cwin); + + XSaveContext(ioncore_g.dpy, win, ioncore_g.win_context, (XPointer)cwin); + XAddToSaveSet(ioncore_g.dpy, win); + + return TRUE; +} + + +static WClientWin *create_clientwin(WWindow *par, Window win, + XWindowAttributes *attr) +{ + CREATEOBJ_IMPL(WClientWin, clientwin, (p, par, win, attr)); +} + + +static bool handle_target_prop(WClientWin *cwin, const WManageParams *param) +{ + WRegion *r=NULL; + char *target_name=NULL; + + if(extl_table_gets_s(cwin->proptab, "target", &target_name)){ + r=ioncore_lookup_region(target_name, NULL); + + free(target_name); + + if(r!=NULL){ + if(region_manage_clientwin(r, cwin, param, + MANAGE_REDIR_PREFER_NO)) + return TRUE; + } + } + + return FALSE; +} + + +WClientWin *clientwin_get_transient_for(const WClientWin *cwin) +{ + Window tforwin; + WClientWin *tfor=NULL; + + if(clientwin_get_transient_mode(cwin)!=TRANSIENT_MODE_NORMAL) + return NULL; + + if(!XGetTransientForHint(ioncore_g.dpy, cwin->win, &tforwin)) + return NULL; + + if(tforwin==None) + return NULL; + + tfor=XWINDOW_REGION_OF_T(tforwin, WClientWin); + + if(tfor==cwin){ + warn(TR("The transient_for hint for \"%s\" points to itself."), + region_name((WRegion*)cwin)); + }else if(tfor==NULL){ + if(xwindow_region_of(tforwin)!=NULL){ + warn(TR("Client window \"%s\" has broken transient_for hint. " + "(\"Extended WM hints\" multi-parent brain damage?)"), + region_name((WRegion*)cwin)); + } + }else if(!region_same_rootwin((WRegion*)cwin, (WRegion*)tfor)){ + warn(TR("The transient_for window for \"%s\" is not on the same " + "screen."), region_name((WRegion*)cwin)); + }else{ + return tfor; + } + + return NULL; +} + + +static bool postmanage_check(WClientWin *cwin, XWindowAttributes *attr) +{ + /* Check that the window exists. The previous check and selectinput + * do not seem to catch all cases of window destroyal. + */ + XSync(ioncore_g.dpy, False); + + if(XGetWindowAttributes(ioncore_g.dpy, cwin->win, attr)) + return TRUE; + + warn(TR("Window %#x disappeared."), cwin->win); + + return FALSE; +} + + +static bool do_manage_mrsh(bool (*fn)(WClientWin *cwin, WManageParams *pm), + void **p) +{ + return fn((WClientWin*)p[0], (WManageParams*)p[1]); +} + + + +static bool do_manage_mrsh_extl(ExtlFn fn, void **p) +{ + WClientWin *cwin=(WClientWin*)p[0]; + WManageParams *mp=(WManageParams*)p[1]; + ExtlTab t=manageparams_to_table(mp); + bool ret=FALSE; + + extl_call(fn, "ot", "b", cwin, t, &ret); + + extl_unref_table(t); + + return (ret && REGION_MANAGER(cwin)!=NULL); +} + + +/* This is called when a window is mapped on the root window. + * We want to check if we should manage the window and how and + * act appropriately. + */ +WClientWin* ioncore_manage_clientwin(Window win, bool maprq) +{ + WRootWin *rootwin; + WClientWin *cwin=NULL; + XWindowAttributes attr; + XWMHints *hints; + int init_state=NormalState; + WManageParams param=MANAGEPARAMS_INIT; + + param.dockapp=FALSE; + +again: + /* Is the window already being managed? */ + cwin=XWINDOW_REGION_OF_T(win, WClientWin); + if(cwin!=NULL) + return cwin; + + /* Select for UnmapNotify and DestroyNotify as the + * window might get destroyed or unmapped in the meanwhile. + */ + xwindow_unmanaged_selectinput(win, StructureNotifyMask); + + + /* Is it a dockapp? + */ + hints=XGetWMHints(ioncore_g.dpy, win); + + if(hints!=NULL && hints->flags&StateHint) + init_state=hints->initial_state; + + if(!param.dockapp && init_state==WithdrawnState && + hints->flags&IconWindowHint && hints->icon_window!=None){ + /* The dockapp might be displaying its "main" window if no + * wm that understands dockapps has been managing it. + */ + if(!maprq) + XUnmapWindow(ioncore_g.dpy, win); + + xwindow_unmanaged_selectinput(win, 0); + + win=hints->icon_window; + + /* It is a dockapp, do everything again from the beginning, now + * with the icon window. + */ + param.dockapp=TRUE; + goto again; + } + + if(hints!=NULL) + XFree((void*)hints); + + if(!XGetWindowAttributes(ioncore_g.dpy, win, &attr)){ + if(maprq) + warn(TR("Window %#x disappeared."), win); + goto fail2; + } + + attr.width=maxof(attr.width, 1); + attr.height=maxof(attr.height, 1); + + /* Do we really want to manage it? */ + if(!param.dockapp && (attr.override_redirect || + (!maprq && attr.map_state!=IsViewable))){ + goto fail2; + } + + /* Find root window */ + FOR_ALL_ROOTWINS(rootwin){ + if(WROOTWIN_ROOT(rootwin)==attr.root) + break; + } + + if(rootwin==NULL){ + warn(TR("Unable to find a matching root window!")); + goto fail2; + } + + /* Allocate and initialize */ + cwin=create_clientwin((WWindow*)rootwin, win, &attr); + + if(cwin==NULL){ + warn_err(); + goto fail2; + } + + param.geom=REGION_GEOM(cwin); + param.maprq=maprq; + param.userpos=(cwin->size_hints.flags&USPosition); + param.switchto=(init_state!=IconicState && clientwin_get_switchto(cwin)); + param.jumpto=extl_table_is_bool_set(cwin->proptab, "jumpto"); + param.gravity=(cwin->size_hints.flags&PWinGravity + ? cwin->size_hints.win_gravity + : ForgetGravity); + param.tfor=clientwin_get_transient_for(cwin); + + if(cwin->flags&SIZEHINT_PROPS){ + /* If size hints have been messed with, readjust requested geometry + * here. If programs themselves give incompatible geometries and + * things don't look good then, it's their fault. + */ + region_size_hints_correct((WRegion*)cwin, ¶m.geom.w, ¶m.geom.h, + FALSE); + } + + if(!handle_target_prop(cwin, ¶m)){ + bool managed; + void *mrshpm[2]; + + mrshpm[0]=cwin; + mrshpm[1]=¶m; + + managed=hook_call_alt(clientwin_do_manage_alt, &mrshpm, + (WHookMarshall*)do_manage_mrsh, + (WHookMarshallExtl*)do_manage_mrsh_extl); + + if(!managed){ + warn(TR("Unable to manage client window %#x."), win); + goto failure; + } + } + + if(ioncore_g.opmode==IONCORE_OPMODE_NORMAL && + !region_is_fully_mapped((WRegion*)cwin) && + !region_skip_focus((WRegion*)cwin)){ + region_set_activity((WRegion*)cwin, SETPARAM_SET); + } + + + if(postmanage_check(cwin, &attr)){ + if(param.jumpto && ioncore_g.focus_next==NULL) + region_goto((WRegion*)cwin); + hook_call_o(clientwin_mapped_hook, (Obj*)cwin); + return cwin; + } + +failure: + clientwin_destroyed(cwin); + return NULL; + +fail2: + xwindow_unmanaged_selectinput(win, 0); + return NULL; +} + + +void clientwin_tfor_changed(WClientWin *cwin) +{ +#if 0 + WManageParams param=MANAGEPARAMS_INIT; + bool succeeded=FALSE; + param.tfor=clientwin_get_transient_for(cwin); + if(param.tfor==NULL) + return; + + region_rootpos((WRegion*)cwin, &(param.geom.x), &(param.geom.y)); + param.geom.w=REGION_GEOM(cwin).w; + param.geom.h=REGION_GEOM(cwin).h; + param.maprq=FALSE; + param.userpos=FALSE; + param.switchto=region_may_control_focus((WRegion*)cwin); + param.jumpto=extl_table_is_bool_set(cwin->proptab, "jumpto"); + param.gravity=ForgetGravity; + + CALL_ALT_B(succeeded, clientwin_do_manage_alt, (cwin, ¶m)); + warn("WM_TRANSIENT_FOR changed for \"%s\".", + region_name((WRegion*)cwin)); +#else + warn(TR("Changes is WM_TRANSIENT_FOR property are unsupported.")); +#endif +} + + +/*}}}*/ + + +/*{{{ Unmanage/destroy */ + + +static bool reparent_root(WClientWin *cwin) +{ + XWindowAttributes attr; + WWindow *par; + Window dummy; + int x=0, y=0; + + if(!XGetWindowAttributes(ioncore_g.dpy, cwin->win, &attr)) + return FALSE; + + par=REGION_PARENT(cwin); + + if(par==NULL){ + x=REGION_GEOM(cwin).x; + y=REGION_GEOM(cwin).y; + }else{ + int dr=REGION_GEOM(par).w-REGION_GEOM(cwin).w-REGION_GEOM(cwin).x; + int db=REGION_GEOM(par).h-REGION_GEOM(cwin).h-REGION_GEOM(cwin).y; + dr=maxof(dr, 0); + db=maxof(db, 0); + + XTranslateCoordinates(ioncore_g.dpy, par->win, attr.root, 0, 0, + &x, &y, &dummy); + + x-=xgravity_deltax(cwin->size_hints.win_gravity, + maxof(0, REGION_GEOM(cwin).x), dr); + y-=xgravity_deltay(cwin->size_hints.win_gravity, + maxof(0, REGION_GEOM(cwin).y), db); + } + + XReparentWindow(ioncore_g.dpy, cwin->win, attr.root, x, y); + + return TRUE; +} + + +void clientwin_deinit(WClientWin *cwin) +{ + WRegion *reg; + + if(cwin->win!=None){ + xwindow_unmanaged_selectinput(cwin->win, 0); + XUnmapWindow(ioncore_g.dpy, cwin->win); + + if(cwin->orig_bw!=0) + configure_cwin_bw(cwin->win, cwin->orig_bw); + + if(reparent_root(cwin)){ + if(ioncore_g.opmode==IONCORE_OPMODE_DEINIT){ + XMapWindow(ioncore_g.dpy, cwin->win); + /* Make sure the topmost window has focus; it doesn't really + * matter which one has as long as some has. + */ + xwindow_do_set_focus(cwin->win); + }else{ + set_clientwin_state(cwin, WithdrawnState); + netwm_delete_state(cwin); + } + } + + XRemoveFromSaveSet(ioncore_g.dpy, cwin->win); + XDeleteContext(ioncore_g.dpy, cwin->win, ioncore_g.win_context); + } + + clientwin_clear_colormaps(cwin); + + if(cwin->fs_pholder!=NULL){ + WPHolder *ph=cwin->fs_pholder; + cwin->fs_pholder=NULL; + destroy_obj((Obj*)ph); + } + + region_deinit((WRegion*)cwin); +} + + + +static bool mrsh_u_c(WHookDummy *fn, void *param) +{ + fn(*(Window*)param); + return TRUE; +} + +static bool mrsh_u_extl(ExtlFn fn, void *param) +{ + double d=*(Window*)param; + extl_call(fn, "d", NULL, d); + return TRUE; +} + +static void clientwin_do_unmapped(WClientWin *cwin, Window win) +{ + bool mcf=region_may_control_focus((WRegion*)cwin); + + if(mcf && cwin->fs_pholder!=NULL) + pholder_goto(cwin->fs_pholder); + + destroy_obj((Obj*)cwin); + + hook_call(clientwin_unmapped_hook, &win, mrsh_u_c, mrsh_u_extl); +} + +/* Used when the window was unmapped */ +void clientwin_unmapped(WClientWin *cwin) +{ + clientwin_do_unmapped(cwin, cwin->win); +} + + +/* Used when the window was deastroyed */ +void clientwin_destroyed(WClientWin *cwin) +{ + Window win=cwin->win; + XRemoveFromSaveSet(ioncore_g.dpy, cwin->win); + XDeleteContext(ioncore_g.dpy, cwin->win, ioncore_g.win_context); + xwindow_unmanaged_selectinput(cwin->win, 0); + cwin->win=None; + clientwin_do_unmapped(cwin, win); +} + + +/*}}}*/ + + +/*{{{ Kill/close */ + + +static bool send_clientmsg(Window win, Atom a, Time stmp) +{ + XClientMessageEvent ev; + + ev.type=ClientMessage; + ev.window=win; + ev.message_type=ioncore_g.atom_wm_protocols; + ev.format=32; + ev.data.l[0]=a; + ev.data.l[1]=stmp; + + return (XSendEvent(ioncore_g.dpy, win, False, 0L, (XEvent*)&ev)!=0); +} + + +/*EXTL_DOC + * Attempt to kill (with XKillWindow) the client that owns the X + * window correspoding to \var{cwin}. + */ +EXTL_EXPORT_MEMBER +void clientwin_kill(WClientWin *cwin) +{ + XKillClient(ioncore_g.dpy, cwin->win); +} + + +bool clientwin_rqclose(WClientWin *cwin, bool relocate_ignored) +{ + /* Ignore relocate parameter -- client windows can always be + * destroyed by the application in any case, so way may just as + * well assume relocate is always set. + */ + + if(cwin->flags&CLIENTWIN_P_WM_DELETE){ + send_clientmsg(cwin->win, ioncore_g.atom_wm_delete, + ioncore_get_timestamp()); + return TRUE; + }else{ + warn(TR("Client does not support the WM_DELETE protocol.")); + return FALSE; + } +} + + +/*}}}*/ + + +/*{{{ State (hide/show) */ + + +static void set_clientwin_state(WClientWin *cwin, int state) +{ + if(cwin->state!=state){ + cwin->state=state; + xwindow_set_state_property(cwin->win, state); + } +} + + +static void hide_clientwin(WClientWin *cwin) +{ + if(cwin->flags&CLIENTWIN_PROP_ACROBATIC){ + XMoveWindow(ioncore_g.dpy, cwin->win, + -2*REGION_GEOM(cwin).w, -2*REGION_GEOM(cwin).h); + return; + } + + set_clientwin_state(cwin, IconicState); + XSelectInput(ioncore_g.dpy, cwin->win, + cwin->event_mask&~(StructureNotifyMask|EnterWindowMask)); + XUnmapWindow(ioncore_g.dpy, cwin->win); + XSelectInput(ioncore_g.dpy, cwin->win, cwin->event_mask); +} + + +static void show_clientwin(WClientWin *cwin) +{ + if(cwin->flags&CLIENTWIN_PROP_ACROBATIC){ + XMoveWindow(ioncore_g.dpy, cwin->win, + REGION_GEOM(cwin).x, REGION_GEOM(cwin).y); + if(cwin->state==NormalState) + return; + } + + XSelectInput(ioncore_g.dpy, cwin->win, + cwin->event_mask&~(StructureNotifyMask|EnterWindowMask)); + XMapWindow(ioncore_g.dpy, cwin->win); + XSelectInput(ioncore_g.dpy, cwin->win, cwin->event_mask); + set_clientwin_state(cwin, NormalState); +} + + +/*}}}*/ + + +/*{{{ Resize/reparent/reconf helpers */ + + +void clientwin_notify_rootpos(WClientWin *cwin, int rootx, int rooty) +{ + XEvent ce; + Window win; + + if(cwin==NULL) + return; + + win=cwin->win; + + ce.xconfigure.type=ConfigureNotify; + ce.xconfigure.event=win; + ce.xconfigure.window=win; + ce.xconfigure.x=rootx-cwin->orig_bw; + ce.xconfigure.y=rooty-cwin->orig_bw; + ce.xconfigure.width=REGION_GEOM(cwin).w; + ce.xconfigure.height=REGION_GEOM(cwin).h; + ce.xconfigure.border_width=cwin->orig_bw; + ce.xconfigure.above=None; + ce.xconfigure.override_redirect=False; + + XSelectInput(ioncore_g.dpy, win, cwin->event_mask&~StructureNotifyMask); + XSendEvent(ioncore_g.dpy, win, False, StructureNotifyMask, &ce); + XSelectInput(ioncore_g.dpy, win, cwin->event_mask); +} + + +static void sendconfig_clientwin(WClientWin *cwin) +{ + int rootx, rooty; + + region_rootpos(&cwin->region, &rootx, &rooty); + clientwin_notify_rootpos(cwin, rootx, rooty); +} + + +static void do_reparent_clientwin(WClientWin *cwin, Window win, int x, int y) +{ + XSelectInput(ioncore_g.dpy, cwin->win, + cwin->event_mask&~StructureNotifyMask); + XReparentWindow(ioncore_g.dpy, cwin->win, win, x, y); + XSelectInput(ioncore_g.dpy, cwin->win, cwin->event_mask); +} + + +static void convert_geom(const WFitParams *fp, + WClientWin *cwin, WRectangle *geom) +{ + WFitParams fptmp=*fp; + WSizePolicy szplcy=SIZEPOLICY_FULL_EXACT; + + /*if(cwin->szplcy!=SIZEPOLICY_DEFAULT) + szplcy=cwin->szplcy;*/ + + sizepolicy(&szplcy, (WRegion*)cwin, NULL, REGION_RQGEOM_WEAK_ALL, &fptmp); + + *geom=fptmp.g; +} + + +/*}}}*/ + + +/*{{{ Region dynfuns */ + + +static bool clientwin_fitrep(WClientWin *cwin, WWindow *np, + const WFitParams *fp) +{ + WRectangle geom; + bool changes; + int w, h; + + if(np!=NULL && !region_same_rootwin((WRegion*)cwin, (WRegion*)np)) + return FALSE; + + if(fp->mode®ION_FIT_WHATEVER){ + geom.x=fp->g.x; + geom.y=fp->g.y; + geom.w=REGION_GEOM(cwin).w; + geom.h=REGION_GEOM(cwin).h; + }else{ + geom=fp->g; + } + + changes=(REGION_GEOM(cwin).x!=geom.x || + REGION_GEOM(cwin).y!=geom.y || + REGION_GEOM(cwin).w!=geom.w || + REGION_GEOM(cwin).h!=geom.h); + + REGION_GEOM(cwin)=geom; + + if(np==NULL && !changes) + return TRUE; + + if(np!=NULL){ + region_unset_parent((WRegion*)cwin); + do_reparent_clientwin(cwin, np->win, geom.x, geom.y); + region_set_parent((WRegion*)cwin, np); + sendconfig_clientwin(cwin); + + if(!REGION_IS_FULLSCREEN(cwin) && cwin->fs_pholder!=NULL){ + WPHolder *ph=cwin->fs_pholder; + cwin->fs_pholder=NULL; + cwin->flags&=~CLIENTWIN_FS_RQ; + /* Can't destroy it yet - messes up mplex placeholder + * reorganisation. + */ + mainloop_defer_destroy((Obj*)ph); + } + + netwm_update_state(cwin); + } + + w=maxof(1, geom.w); + h=maxof(1, geom.h); + + if(cwin->flags&CLIENTWIN_PROP_ACROBATIC && !REGION_IS_MAPPED(cwin)){ + XMoveResizeWindow(ioncore_g.dpy, cwin->win, + -2*REGION_GEOM(cwin).w, -2*REGION_GEOM(cwin).h, + w, h); + }else{ + XMoveResizeWindow(ioncore_g.dpy, cwin->win, geom.x, geom.y, w, h); + } + + cwin->flags&=~CLIENTWIN_NEED_CFGNTFY; + + return TRUE; +} + + +static void clientwin_map(WClientWin *cwin) +{ + show_clientwin(cwin); + REGION_MARK_MAPPED(cwin); +} + + +static void clientwin_unmap(WClientWin *cwin) +{ + hide_clientwin(cwin); + REGION_MARK_UNMAPPED(cwin); +} + + +static void clientwin_do_set_focus(WClientWin *cwin, bool warp) +{ + if(cwin->flags&CLIENTWIN_P_WM_TAKE_FOCUS){ + Time stmp=ioncore_get_timestamp(); + send_clientmsg(cwin->win, ioncore_g.atom_wm_take_focus, stmp); + } + + region_finalise_focusing((WRegion*)cwin, cwin->win, warp); + + XSync(ioncore_g.dpy, 0); +} + + +void clientwin_restack(WClientWin *cwin, Window other, int mode) +{ + xwindow_restack(cwin->win, other, mode); +} + + +void clientwin_stacking(WClientWin *cwin, Window *bottomret, Window *topret) +{ + *bottomret=cwin->win; + *topret=cwin->win; +} + + +static Window clientwin_x_window(WClientWin *cwin) +{ + return cwin->win; +} + + +static void clientwin_activated(WClientWin *cwin) +{ + clientwin_install_colormap(cwin); +} + + +static void clientwin_size_hints(WClientWin *cwin, WSizeHints *hints_ret) +{ + if(cwin->flags&CLIENTWIN_FS_RQ){ + /* Do not use size hints, when full screen mode has been + * requested by the client window itself. + */ + sizehints_clear(hints_ret); + }else{ + xsizehints_to_sizehints(&cwin->size_hints, hints_ret); + } +} + + +/*}}}*/ + + +/*{{{ Identity & lookup */ + + +/*EXTL_DOC + * Returns a table containing the properties \code{WM_CLASS} (table entries + * \var{instance} and \var{class}) and \code{WM_WINDOW_ROLE} (\var{role}) + * properties for \var{cwin}. If a property is not set, the corresponding + * field(s) are unset in the table. + */ +EXTL_SAFE +EXTL_EXPORT_MEMBER +ExtlTab clientwin_get_ident(WClientWin *cwin) +{ + char **p=NULL, *wrole=NULL; + int n=0, n2=0, n3=0, tmp=0; + ExtlTab tab; + + p=xwindow_get_text_property(cwin->win, XA_WM_CLASS, &n); + wrole=xwindow_get_string_property(cwin->win, ioncore_g.atom_wm_window_role, &n2); + + tab=extl_create_table(); + if(n>=2 && p[1]!=NULL) + extl_table_sets_s(tab, "class", p[1]); + if(n>=1 && p[0]!=NULL) + extl_table_sets_s(tab, "instance", p[0]); + if(wrole!=NULL) + extl_table_sets_s(tab, "role", wrole); + + if(p!=NULL) + XFreeStringList(p); + if(wrole!=NULL) + free(wrole); + + return tab; +} + + +/*}}}*/ + + +/*{{{ ConfigureRequest */ + + +void clientwin_handle_configure_request(WClientWin *cwin, + XConfigureRequestEvent *ev) +{ + if(ev->value_mask&CWBorderWidth) + cwin->orig_bw=ev->border_width; + + if(cwin->flags&CLIENTWIN_PROP_IGNORE_CFGRQ){ + sendconfig_clientwin(cwin); + return; + } + + /* check full screen request */ + if((ev->value_mask&(CWWidth|CWHeight))==(CWWidth|CWHeight)){ + bool sw=clientwin_fullscreen_may_switchto(cwin); + if(clientwin_check_fullscreen_request(cwin, ev->width, ev->height, sw)) + return; + } + + cwin->flags|=CLIENTWIN_NEED_CFGNTFY; + + if(ev->value_mask&(CWX|CWY|CWWidth|CWHeight)){ + WRQGeomParams rq=RQGEOMPARAMS_INIT; + int gdx=0, gdy=0; + + rq.flags=REGION_RQGEOM_WEAK_ALL|REGION_RQGEOM_ABSOLUTE; + + if(cwin->size_hints.flags&PWinGravity){ + rq.flags|=REGION_RQGEOM_GRAVITY; + rq.gravity=cwin->size_hints.win_gravity; + } + + /* Do I need to insert another disparaging comment on the person who + * invented special server-supported window borders that are not + * accounted for in the window size? Keep it simple, stupid! + */ + if(cwin->size_hints.flags&PWinGravity){ + gdx=xgravity_deltax(cwin->size_hints.win_gravity, + -cwin->orig_bw, -cwin->orig_bw); + gdy=xgravity_deltay(cwin->size_hints.win_gravity, + -cwin->orig_bw, -cwin->orig_bw); + } + + region_rootpos((WRegion*)cwin, &(rq.geom.x), &(rq.geom.y)); + rq.geom.w=REGION_GEOM(cwin).w; + rq.geom.h=REGION_GEOM(cwin).h; + + if(ev->value_mask&CWWidth){ + /* If x was not changed, keep reference point where it was */ + if(cwin->size_hints.flags&PWinGravity){ + rq.geom.x+=xgravity_deltax(cwin->size_hints.win_gravity, 0, + ev->width-rq.geom.w); + } + rq.geom.w=maxof(ev->width, 1); + rq.flags&=~REGION_RQGEOM_WEAK_W; + } + if(ev->value_mask&CWHeight){ + /* If y was not changed, keep reference point where it was */ + if(cwin->size_hints.flags&PWinGravity){ + rq.geom.y+=xgravity_deltay(cwin->size_hints.win_gravity, 0, + ev->height-rq.geom.h); + } + rq.geom.h=maxof(ev->height, 1); + rq.flags&=~REGION_RQGEOM_WEAK_H; + } + if(ev->value_mask&CWX){ + rq.geom.x=ev->x+gdx; + rq.flags&=~REGION_RQGEOM_WEAK_X; + } + if(ev->value_mask&CWY){ + rq.geom.y=ev->y+gdy; + rq.flags&=~REGION_RQGEOM_WEAK_Y; + } + + region_rqgeom((WRegion*)cwin, &rq, NULL); + } + + if(cwin->flags&CLIENTWIN_NEED_CFGNTFY){ + sendconfig_clientwin(cwin); + cwin->flags&=~CLIENTWIN_NEED_CFGNTFY; + } +} + + +/*}}}*/ + + +/*{{{ Kludges */ + + +/*EXTL_DOC + * Attempts to fix window size problems with non-ICCCM compliant + * programs. + */ +EXTL_EXPORT_MEMBER +void clientwin_nudge(WClientWin *cwin) +{ + XResizeWindow(ioncore_g.dpy, cwin->win, + 2*REGION_GEOM(cwin).w, 2*REGION_GEOM(cwin).h); + XFlush(ioncore_g.dpy); + XResizeWindow(ioncore_g.dpy, cwin->win, + REGION_GEOM(cwin).w, REGION_GEOM(cwin).h); +} + + +/*}}}*/ + + +/*{{{ Misc. */ + + +/*EXTL_DOC + * Return the X window id for the client window. + */ +EXTL_SAFE +EXTL_EXPORT_MEMBER +double clientwin_xid(WClientWin *cwin) +{ + return cwin->win; +} + + +/*}}}*/ + + +/*{{{ Save/load */ + + +static int last_checkcode=1; + + +static ExtlTab clientwin_get_configuration(WClientWin *cwin) +{ + int chkc=0; + ExtlTab tab; + SMCfgCallback *cfg_cb; + SMAddCallback *add_cb; + + tab=region_get_base_configuration((WRegion*)cwin); + + extl_table_sets_d(tab, "windowid", (double)(cwin->win)); + + if(last_checkcode!=0){ + chkc=last_checkcode++; + xwindow_set_integer_property(cwin->win, ioncore_g.atom_checkcode, + chkc); + extl_table_sets_i(tab, "checkcode", chkc); + } + + ioncore_get_sm_callbacks(&add_cb, &cfg_cb); + + if(cfg_cb!=NULL) + cfg_cb(cwin, tab); + + return tab; +} + + +WRegion *clientwin_load(WWindow *par, const WFitParams *fp, ExtlTab tab) +{ + double wind=0; + Window win=None; + int chkc=0, real_chkc=0; + WClientWin *cwin=NULL; + XWindowAttributes attr; + WRectangle rg; + bool got_chkc; + + if(!extl_table_gets_d(tab, "windowid", &wind) || + !extl_table_gets_i(tab, "checkcode", &chkc)){ + return NULL; + } + + win=(Window)wind; + + if(XWINDOW_REGION_OF(win)!=NULL){ + warn("Client window %x already managed.", win); + return NULL; + } + + got_chkc=xwindow_get_integer_property(win, ioncore_g.atom_checkcode, + &real_chkc); + + if(!got_chkc || real_chkc!=chkc){ + ioncore_clientwin_load_missing(); + return NULL; + } + + /* Found it! */ + + if(!XGetWindowAttributes(ioncore_g.dpy, win, &attr)){ + warn(TR("Window %#x disappeared."), win); + return NULL; + } + + if(attr.override_redirect || + (ioncore_g.opmode==IONCORE_OPMODE_INIT && attr.map_state!=IsViewable)){ + warn(TR("Saved client window does not want to be managed.")); + return NULL; + } + + /* + attr.x=fp->g.x; + attr.y=fp->g.y; + attr.width=fp->g.w; + attr.height=fp->g.h; + */ + + cwin=create_clientwin(par, win, &attr); + + if(cwin==NULL) + return FALSE; + + /* Reparent and resize taking limits set by size hints into account */ + convert_geom(fp, cwin, &rg); + REGION_GEOM(cwin)=rg; + do_reparent_clientwin(cwin, par->win, rg.x, rg.y); + XResizeWindow(ioncore_g.dpy, win, maxof(1, rg.w), maxof(1, rg.h)); + + if(!postmanage_check(cwin, &attr)){ + clientwin_destroyed(cwin); + return NULL; + } + + return (WRegion*)cwin; +} + + +/*}}}*/ + + +/*{{{ Dynfuntab and class info */ + + +static DynFunTab clientwin_dynfuntab[]={ + {(DynFun*)region_fitrep, + (DynFun*)clientwin_fitrep}, + + {region_map, + clientwin_map}, + + {region_unmap, + clientwin_unmap}, + + {region_do_set_focus, + clientwin_do_set_focus}, + + {region_notify_rootpos, + clientwin_notify_rootpos}, + + {region_restack, + clientwin_restack}, + + {region_stacking, + clientwin_stacking}, + + {(DynFun*)region_xwindow, + (DynFun*)clientwin_x_window}, + + {region_activated, + clientwin_activated}, + + {region_size_hints, + clientwin_size_hints}, + + {(DynFun*)region_rqclose, + (DynFun*)clientwin_rqclose}, + + {(DynFun*)region_get_configuration, + (DynFun*)clientwin_get_configuration}, + + END_DYNFUNTAB +}; + + +EXTL_EXPORT +IMPLCLASS(WClientWin, WRegion, clientwin_deinit, clientwin_dynfuntab); + + +/*}}}*/ diff --git a/ioncore/clientwin.h b/ioncore/clientwin.h new file mode 100644 index 0000000..2080d3d --- /dev/null +++ b/ioncore/clientwin.h @@ -0,0 +1,115 @@ +/* + * ion/ioncore/clientwin.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_CLIENTWIN_H +#define ION_IONCORE_CLIENTWIN_H + +#include +#include +#include +#include "common.h" +#include "region.h" +#include "window.h" +#include "rectangle.h" +#include "attach.h" +#include "manage.h" +#include "pholder.h" +#include "sizepolicy.h" + + +#define CLIENTWIN_P_WM_DELETE 0x00001 +#define CLIENTWIN_P_WM_TAKE_FOCUS 0x00002 +#define CLIENTWIN_PROP_ACROBATIC 0x00010 +#define CLIENTWIN_PROP_MAXSIZE 0x00020 +#define CLIENTWIN_PROP_ASPECT 0x00040 +#define CLIENTWIN_PROP_TRANSPARENT 0x00080 +#define CLIENTWIN_PROP_IGNORE_RSZINC 0x00100 +#define CLIENTWIN_PROP_MINSIZE 0x00200 +#define CLIENTWIN_PROP_IGNORE_CFGRQ 0x00400 +#define CLIENTWIN_NEED_CFGNTFY 0x01000 +#define CLIENTWIN_USE_NET_WM_NAME 0x10000 +#define CLIENTWIN_FS_RQ 0x20000 + + +DECLCLASS(WClientWin){ + WRegion region; + + int flags; + int state; + int event_mask; + Window win; + + int orig_bw; + + Window transient_for; + + Colormap cmap; + Colormap *cmaps; + Window *cmapwins; + int n_cmapwins; + + XSizeHints size_hints; + + WPHolder *fs_pholder; + + ExtlTab proptab; +}; + + +extern void clientwin_get_protocols(WClientWin *cwin); +extern void clientwin_get_size_hints(WClientWin *cwin); +extern void clientwin_unmapped(WClientWin *cwin); +extern void clientwin_destroyed(WClientWin *cwin); +extern void clientwin_kill(WClientWin *cwin); +extern bool clientwin_rqclose(WClientWin *cwin, bool relocate_ignored); + +extern void clientwin_tfor_changed(WClientWin *cwin); + +extern void clientwin_get_set_name(WClientWin *cwin); + +extern void clientwin_handle_configure_request(WClientWin *cwin, + XConfigureRequestEvent *ev); + +extern void clientwin_broken_app_resize_kludge(WClientWin *cwin); + +extern WRegion *clientwin_load(WWindow *par, const WFitParams *fp, + ExtlTab tab); + +/* Some standard winprops */ + +enum{ + TRANSIENT_MODE_NORMAL, + TRANSIENT_MODE_CURRENT, + TRANSIENT_MODE_OFF +}; + +extern bool clientwin_get_switchto(const WClientWin *cwin); +extern int clientwin_get_transient_mode(const WClientWin *cwin); +extern WClientWin *clientwin_get_transient_for(const WClientWin *cwin); + +/* Hooks */ + +/* This hook has parameters (WClientWin*, WManageParams*). */ +extern WHook *clientwin_do_manage_alt; +/* This hook has just WClientWin* as parameter. */ +extern WHook *clientwin_mapped_hook; +/* This hook has an X Window id as parameter. */ +extern WHook *clientwin_unmapped_hook; +/* This hook has (WClientWin*, const XPropertyEvent *) as parameters on + * C side, and (WClientWin*, int atom) on Lua side. + */ +extern WHook *clientwin_property_change_hook; + +/* Manage */ + +extern WClientWin *ioncore_manage_clientwin(Window win, bool maprq); + +#endif /* ION_IONCORE_CLIENTWIN_H */ diff --git a/ioncore/colormap.c b/ioncore/colormap.c new file mode 100644 index 0000000..83c3b75 --- /dev/null +++ b/ioncore/colormap.c @@ -0,0 +1,249 @@ +/* + * ion/ioncore/colormap.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include "common.h" +#include "global.h" +#include "property.h" +#include "clientwin.h" +#include "colormap.h" +#include "region.h" +#include "names.h" +#include "xwindow.h" + + +/*{{{ Installing colormaps */ + + +void rootwin_install_colormap(WRootWin *rootwin, Colormap cmap) +{ + if(cmap==None) + cmap=rootwin->default_cmap; + XInstallColormap(ioncore_g.dpy, cmap); +} + + +void clientwin_install_colormap(WClientWin *cwin) +{ + WRootWin *rw=region_rootwin_of((WRegion*)cwin); + bool found=FALSE; + int i; + + for(i=cwin->n_cmapwins-1; i>=0; i--){ + rootwin_install_colormap(rw, cwin->cmaps[i]); + if(cwin->cmapwins[i]==cwin->win) + found=TRUE; + } + + if(found) + return; + + rootwin_install_colormap(rw, cwin->cmap); +} + + +/*}}}*/ + + +/*{{{ Management */ + + +static XContext ctx=None; + + +void xwindow_unmanaged_selectinput(Window win, long mask) +{ + int *p=NULL; + + /* We may be monitoring for colourmap changes */ + if(ctx!=None){ + if(XFindContext(ioncore_g.dpy, win, ctx, (XPointer*)&p)==0){ + if(*p>0) + mask|=ColormapChangeMask; + } + } + + XSelectInput(ioncore_g.dpy, win, mask); +} + + + +static void xwindow_selcmap(Window win) +{ + int *p=NULL; + XWindowAttributes attr; + + if(ctx==None) + ctx=XUniqueContext(); + + if(XFindContext(ioncore_g.dpy, win, ctx, (XPointer*)&p)==0){ + (*p)++; + }else{ + p=ALLOC(int); + if(p==NULL) + return; + + *p=1; + if(XSaveContext(ioncore_g.dpy, win, ctx, (XPointer)p)!=0){ + warn(TR("Unable to store colourmap watch info.")); + return; + } + + if(XWINDOW_REGION_OF(win)==NULL){ + XGetWindowAttributes(ioncore_g.dpy, win, &attr); + XSelectInput(ioncore_g.dpy, win, + attr.your_event_mask|ColormapChangeMask); + } + } +} + + +static void xwindow_unselcmap(Window win) +{ + int *p=NULL; + XWindowAttributes attr; + + if(ctx==None) + return; + + if(XFindContext(ioncore_g.dpy, win, ctx, (XPointer*)&p)==0){ + (*p)--; + if(*p==0){ + XDeleteContext(ioncore_g.dpy, win, ctx); + free(p); + if(XWINDOW_REGION_OF(win)==NULL){ + XGetWindowAttributes(ioncore_g.dpy, win, &attr); + XSelectInput(ioncore_g.dpy, win, + attr.your_event_mask&~ColormapChangeMask); + } + } + } +} + + +void clientwin_get_colormaps(WClientWin *cwin) +{ + Window *wins; + XWindowAttributes attr; + int i, n; + + clientwin_clear_colormaps(cwin); + + n=xwindow_get_property(cwin->win, ioncore_g.atom_wm_colormaps, + XA_WINDOW, 100L, TRUE, (uchar**)&wins); + + if(n<=0) + return; + + cwin->cmaps=ALLOC_N(Colormap, n); + + if(cwin->cmaps==NULL) + return; + + cwin->cmapwins=wins; + cwin->n_cmapwins=n; + + for(i=0; iwin){ + cwin->cmaps[i]=cwin->cmap; + }else{ + xwindow_selcmap(wins[i]); + XGetWindowAttributes(ioncore_g.dpy, wins[i], &attr); + cwin->cmaps[i]=attr.colormap; + } + } +} + + +void clientwin_clear_colormaps(WClientWin *cwin) +{ + int i; + XWindowAttributes attr; + + if(cwin->n_cmapwins==0) + return; + + for(i=0; in_cmapwins; i++){ + if(cwin->cmapwins[i]!=cwin->win) + xwindow_unselcmap(cwin->cmapwins[i]); + } + + free(cwin->cmapwins); + free(cwin->cmaps); + cwin->n_cmapwins=0; + cwin->cmapwins=NULL; + cwin->cmaps=NULL; +} + + +/*}}}*/ + + +/*{{{ Event handling */ + + +static void handle_cwin_cmap(WClientWin *cwin, const XColormapEvent *ev) +{ + int i; + + if(ev->window==cwin->win){ + cwin->cmap=ev->colormap; + if(REGION_IS_ACTIVE(cwin)) + clientwin_install_colormap(cwin); + }else{ + for(i=0; in_cmapwins; i++){ + if(cwin->cmapwins[i]!=ev->window) + continue; + cwin->cmaps[i]=ev->colormap; + if(REGION_IS_ACTIVE(cwin)) + clientwin_install_colormap(cwin); + break; + } + } +} + + +static void handle_all_cmaps(const XColormapEvent *ev) +{ + Rb_node node; + + if(!ioncore_clientwin_ns.initialised) + return; + + rb_traverse(node, ioncore_clientwin_ns.rb){ + WClientWin *cwin=(WClientWin*)rb_val(node); + if(cwin!=NULL) + handle_cwin_cmap(cwin, ev); + } +} + + + +void ioncore_handle_colormap_notify(const XColormapEvent *ev) +{ + WClientWin *cwin; + + if(!ev->new) + return; + + cwin=XWINDOW_REGION_OF_T(ev->window, WClientWin); + + if(cwin!=NULL){ + handle_cwin_cmap(cwin, ev); + /*set_cmap(cwin, ev->colormap);*/ + }else{ + handle_all_cmaps(ev); + } +} + + +/*}}}*/ + diff --git a/ioncore/colormap.h b/ioncore/colormap.h new file mode 100644 index 0000000..b44f7fd --- /dev/null +++ b/ioncore/colormap.h @@ -0,0 +1,28 @@ +/* + * ion/ioncore/colormap.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_COLORMAP_H +#define ION_IONCORE_COLORMAP_H + +#include "common.h" +#include "clientwin.h" + +extern void ioncore_handle_colormap_notify(const XColormapEvent *ev); + +extern void rootwin_install_colormap(WRootWin *scr, Colormap cmap); + +extern void clientwin_install_colormap(WClientWin *cwin); +extern void clientwin_get_colormaps(WClientWin *cwin); +extern void clientwin_clear_colormaps(WClientWin *cwin); + +extern void xwindow_unmanaged_selectinput(Window win, long mask); + +#endif /* ION_IONCORE_COLORMAP_H */ diff --git a/ioncore/common.h b/ioncore/common.h new file mode 100644 index 0000000..4959a5b --- /dev/null +++ b/ioncore/common.h @@ -0,0 +1,31 @@ +/* + * ion/ioncore/common.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_COMMON_H +#define ION_IONCORE_COMMON_H + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "../config.h" +#include "classes.h" + +#endif /* ION_IONCORE_COMMON_H */ diff --git a/ioncore/conf-bindings.c b/ioncore/conf-bindings.c new file mode 100644 index 0000000..e39b811 --- /dev/null +++ b/ioncore/conf-bindings.c @@ -0,0 +1,505 @@ +/* + * ion/ioncore/conf-bindings.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include + +#define XK_MISCELLANY +#include + +#include + +#include "common.h" +#include "binding.h" +#include +#include "global.h" +#include +#include "conf-bindings.h" +#include "bindmaps.h" + + +/*{{{ parse_keybut */ + + +#define MOD5_NDX 7 + +static StringIntMap state_map[]={ + {"Shift", ShiftMask}, + {"Lock", LockMask}, + {"Control", ControlMask}, + {"Mod1", Mod1Mask}, + {"Mod2", Mod2Mask}, + {"Mod3", Mod3Mask}, + {"Mod4", Mod4Mask}, + {"Mod5", Mod5Mask}, + {"AnyModifier", AnyModifier}, + {"NoModifier", 0}, + {NULL, 0}, +}; + +static StringIntMap button_map[]={ + {"Button1", Button1}, + {"Button2", Button2}, + {"Button3", Button3}, + {"Button4", Button4}, + {"Button5", Button5}, + {"Button6", 6}, + {"Button7", 7}, + {"AnyButton", AnyButton}, + {NULL, 0}, +}; + + +bool ioncore_parse_keybut(const char *str, uint *mod_ret, uint *ksb_ret, + bool button, bool init_any) +{ + char *str2, *p, *p2; + int keysym=NoSymbol, i; + bool ret=FALSE; + + *ksb_ret=NoSymbol; + *mod_ret=(init_any && !button ? AnyModifier : 0); + + str2=scopy(str); + + if(str2==NULL) + return FALSE; + + p=str2; + + while(*p!='\0'){ + p2=strchr(p, '+'); + + if(p2!=NULL) + *p2='\0'; + + if(!button){ + keysym=XStringToKeysym(p); +#ifdef CF_SUN_F1X_REMAP + if(keysym==XK_F11) + keysym=XK_SunF36; + else if(keysym==XK_F12) + keysym=XK_SunF37; +#endif + } + + if(!button && keysym!=NoSymbol){ + int tmp; + if(*ksb_ret!=NoSymbol){ + warn_obj(str, TR("Insane key combination.")); + break; + } + if(XKeysymToKeycode(ioncore_g.dpy, keysym)==0){ + warn_obj(str, TR("Could not convert keysym to keycode.")); + break; + } + *ksb_ret=keysym; + }else{ + i=stringintmap_ndx(state_map, p); + + if(i<0){ + i=stringintmap_ndx(button_map, p); + + if(i<0){ + warn(TR("Unknown button \"%s\"."), p); + break; + } + + if(!button || *ksb_ret!=NoSymbol){ + warn_obj(str, TR("Insane button combination.")); + break; + } + *ksb_ret=button_map[i].value; + }else{ + if(*mod_ret==AnyModifier){ + if(!init_any){ + warn_obj(str, TR("Insane modifier combination.")); + break; + }else{ + *mod_ret=state_map[i].value; + } + }else{ + if(*mod_ret!=0 && state_map[i].value==AnyModifier){ + warn_obj(str, TR("Insane modifier combination.")); + break; + }else{ + *mod_ret|=state_map[i].value; + } + } + } + } + + if(p2==NULL){ + ret=TRUE; + break; + } + + p=p2+1; + } + + free(str2); + + return ret; +} + +#undef BUTTON1_NDX + + +/*}}}*/ + + +/*{{{ bindmap_defbindings */ + + +static bool do_action(WBindmap *bindmap, const char *str, + ExtlFn func, uint act, uint mod, uint ksb, + int area, bool wr) +{ + WBinding binding; + + if(wr && mod==0){ + warn(TR("Can not wait on modifiers when no modifiers set in \"%s\"."), + str); + wr=FALSE; + } + + binding.wait=wr; + binding.act=act; + binding.state=mod; + binding.ksb=ksb; + binding.kcb=(act==BINDING_KEYPRESS ? XKeysymToKeycode(ioncore_g.dpy, ksb) : ksb); + binding.area=area; + binding.submap=NULL; + + if(func!=extl_fn_none()){ + binding.func=extl_ref_fn(func); + if(bindmap_add_binding(bindmap, &binding)) + return TRUE; + extl_unref_fn(binding.func); + warn(TR("Unable to add binding %s."), str); + }else{ + binding.func=func; + if(bindmap_remove_binding(bindmap, &binding)) + return TRUE; + warn(TR("Unable to remove binding %s."), str); + } + + return FALSE; +} + + +static bool do_submap(WBindmap *bindmap, const char *str, + ExtlTab subtab, uint action, uint mod, uint ksb) +{ + WBinding binding, *bnd; + uint kcb; + + if(action!=BINDING_KEYPRESS) + return FALSE; + + kcb=XKeysymToKeycode(ioncore_g.dpy, ksb); + bnd=bindmap_lookup_binding(bindmap, action, mod, kcb); + + if(bnd!=NULL && bnd->submap!=NULL && bnd->state==mod) + return bindmap_defbindings(bnd->submap, subtab, TRUE); + + binding.wait=FALSE; + binding.act=BINDING_KEYPRESS; + binding.state=mod; + binding.ksb=ksb; + binding.kcb=kcb; + binding.area=0; + binding.func=extl_fn_none(); + binding.submap=create_bindmap(); + + if(binding.submap==NULL) + return FALSE; + + if(bindmap_add_binding(bindmap, &binding)) + return bindmap_defbindings(binding.submap, subtab, TRUE); + + binding_deinit(&binding); + + warn(TR("Unable to add submap for binding %s."), str); + + return FALSE; +} + + +static StringIntMap action_map[]={ + {"kpress", BINDING_KEYPRESS}, + {"mpress", BINDING_BUTTONPRESS}, + {"mclick", BINDING_BUTTONCLICK}, + {"mdblclick", BINDING_BUTTONDBLCLICK}, + {"mdrag", BINDING_BUTTONMOTION}, + {NULL, 0} +}; + + +static bool do_entry(WBindmap *bindmap, ExtlTab tab, + const StringIntMap *areamap, bool init_any) +{ + bool ret=FALSE; + char *action_str=NULL, *ksb_str=NULL, *area_str=NULL; + int action=0; + uint ksb=0, mod=0; + WBinding *bnd=NULL; + ExtlTab subtab; + ExtlFn func; + bool wr=FALSE; + int area=0; + + if(!extl_table_gets_s(tab, "action", &action_str)){ + warn(TR("Binding type not set.")); + goto fail; + } + + if(strcmp(action_str, "kpress_wait")==0){ + action=BINDING_KEYPRESS; + wr=TRUE; + }else{ + action=stringintmap_value(action_map, action_str, -1); + if(action<0){ + warn(TR("Unknown binding type \"%s\"."), action_str); + goto fail; + } + } + + if(!extl_table_gets_s(tab, "kcb", &ksb_str)) + goto fail; + + if(!ioncore_parse_keybut(ksb_str, &mod, &ksb, + (action!=BINDING_KEYPRESS && action!=-1), + init_any)){ + goto fail; + } + + if(extl_table_gets_t(tab, "submap", &subtab)){ + ret=do_submap(bindmap, ksb_str, subtab, action, mod, ksb); + extl_unref_table(subtab); + }else{ + if(areamap!=NULL){ + if(extl_table_gets_s(tab, "area", &area_str)){ + area=stringintmap_value(areamap, area_str, -1); + if(area<0){ + warn(TR("Unknown area \"%s\" for binding %s."), + area_str, ksb_str); + area=0; + } + } + } + + if(!extl_table_gets_f(tab, "func", &func)){ + /*warn("Function for binding %s not set/nil/undefined.", ksb_str); + goto fail;*/ + func=extl_fn_none(); + } + ret=do_action(bindmap, ksb_str, func, action, mod, ksb, area, wr); + if(!ret) + extl_unref_fn(func); + } + +fail: + if(action_str!=NULL) + free(action_str); + if(ksb_str!=NULL) + free(ksb_str); + if(area_str!=NULL) + free(area_str); + return ret; +} + + +bool bindmap_defbindings(WBindmap *bindmap, ExtlTab tab, bool submap) +{ + int i, n, nok=0; + ExtlTab ent; + + n=extl_table_get_n(tab); + + for(i=1; i<=n; i++){ + if(extl_table_geti_t(tab, i, &ent)){ + nok+=do_entry(bindmap, ent, bindmap->areamap, submap); + extl_unref_table(ent); + continue; + } + warn(TR("Unable to get bindmap entry %d."), i); + } + return (nok!=0); +} + + +/*}}}*/ + + +/*{{{ bindmap_getbindings */ + + +static char *get_mods(uint state) +{ + char *ret=NULL; + int i; + + if(state==AnyModifier){ + ret=scopy("AnyModifier+"); + }else{ + ret=scopy(""); + for(i=0; i<=MOD5_NDX; i++){ + if(ret==NULL) + break; + if((int)(state&state_map[i].value)==state_map[i].value){ + char *ret2=ret; + ret=scat3(ret, state_map[i].string, "+"); + free(ret2); + } + } + } + + return ret; +} + + +static char *get_key(char *mods, uint ksb) +{ + const char *s=XKeysymToString(ksb); + char *ret=NULL; + + if(s==NULL){ + warn(TR("Unable to convert keysym to string.")); + return NULL; + } + + return scat(mods, s); +} + + +static char *get_button(char *mods, uint ksb) +{ + const char *s=stringintmap_key(button_map, ksb, NULL); + char *ret=NULL; + + if(s==NULL){ + warn(TR("Unable to convert button to string.")); + return NULL; + } + + return scat(mods, s); +} + + +static bool get_kpress(WBindmap *bindmap, WBinding *b, ExtlTab t) +{ + char *mods; + char *key; + + if(b->wait) + extl_table_sets_s(t, "action", "kpress_wait"); + else + extl_table_sets_s(t, "action", "kpress"); + + mods=get_mods(b->state); + + if(mods==NULL) + return FALSE; + + key=get_key(mods, b->ksb); + + free(mods); + + if(key==NULL) + return FALSE; + + extl_table_sets_s(t, "kcb", key); + + free(key); + + if(b->submap!=NULL){ + ExtlTab stab=bindmap_getbindings(b->submap); + extl_table_sets_t(t, "submap", stab); + }else{ + extl_table_sets_f(t, "func", b->func); + } + + return TRUE; +} + + +static bool get_mact(WBindmap *bindmap, WBinding *b, ExtlTab t) +{ + char *mods; + char *button; + + extl_table_sets_s(t, "action", stringintmap_key(action_map, b->act, NULL)); + + mods=get_mods(b->state); + + if(mods==NULL) + return FALSE; + + button=get_button(mods, b->ksb); + + free(mods); + + if(button==NULL) + return FALSE; + + extl_table_sets_s(t, "kcb", button); + + free(button); + + if(b->area!=0 && bindmap->areamap!=NULL) + extl_table_sets_s(t, "area", + stringintmap_key(bindmap->areamap, b->area, NULL)); + + extl_table_sets_f(t, "func", b->func); + + return TRUE; +} + + +static ExtlTab getbinding(WBindmap *bindmap, WBinding *b) +{ + ExtlTab t=extl_create_table(); + + if(b->act==BINDING_KEYPRESS){ + if(get_kpress(bindmap, b, t)) + return t; + }else{ + if(get_mact(bindmap, b, t)) + return t; + } + + return extl_unref_table(t); +} + + +ExtlTab bindmap_getbindings(WBindmap *bindmap) +{ + Rb_node node; + WBinding *b; + ExtlTab tab; + ExtlTab btab; + int n=0; + + tab=extl_create_table(); + + FOR_ALL_BINDINGS(b, node, bindmap->bindings){ + btab=getbinding(bindmap, b); + extl_table_seti_t(tab, n+1, btab); + extl_unref_table(btab); + n++; + } + + return tab; +} + + +/*}}}*/ + diff --git a/ioncore/conf-bindings.h b/ioncore/conf-bindings.h new file mode 100644 index 0000000..c888a6f --- /dev/null +++ b/ioncore/conf-bindings.h @@ -0,0 +1,27 @@ +/* + * ion/ioncore/conf-bindings.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_CONF_BINDINGS_H +#define ION_IONCORE_CONF_BINDINGS_H + +#include + +#include "binding.h" +#include + +extern bool bindmap_defbindings(WBindmap *bindmap, ExtlTab tab, bool submap); +extern ExtlTab bindmap_getbindings(WBindmap *bindmap); + +extern bool ioncore_parse_keybut(const char *str, + uint *mod_ret, uint *ksb_ret, + bool button, bool init_any); + +#endif /* ION_IONCORE_CONF_BINDINGS_H */ diff --git a/ioncore/conf.c b/ioncore/conf.c new file mode 100644 index 0000000..207c1c1 --- /dev/null +++ b/ioncore/conf.c @@ -0,0 +1,245 @@ +/* + * ion/ioncore/conf.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include "common.h" +#include "global.h" +#include "modules.h" +#include "rootwin.h" +#include "bindmaps.h" +#include "kbresize.h" +#include "reginfo.h" +#include "group-ws.h" +#include "llist.h" + + +StringIntMap frame_idxs[]={ + {"last", LLIST_INDEX_LAST}, + {"next", LLIST_INDEX_AFTER_CURRENT}, + {"next-act", LLIST_INDEX_AFTER_CURRENT_ACT}, + END_STRINGINTMAP +}; + + +/*EXTL_DOC + * Set ioncore basic settings. The table \var{tab} may contain the + * following fields. + * + * \begin{tabularx}{\linewidth}{lX} + * \tabhead{Field & Description} + * \var{opaque_resize} & (boolean) Controls whether interactive move and + * resize operations simply draw a rubberband during + * the operation (false) or immediately affect the + * object in question at every step (true). \\ + * \var{warp} & (boolean) Should focusing operations move the + * pointer to the object to be focused? \\ + * \var{switchto} & (boolean) Should a managing \type{WMPlex} switch + * to a newly mapped client window? \\ + * \var{screen_notify} & (boolean) Should notification tooltips be displayed + * for hidden workspaces with activity? \\ + * \var{frame_default_index} & (string) Specifies where to add new regions + * on the mutually exclusive list of a frame. One of + * ''last'', ''next'' (for after current), ''next-act'' + * (for after current and anything with activity right + * after it). \\ + * \var{dblclick_delay} & (integer) Delay between clicks of a double click.\\ + * \var{kbresize_delay} & (integer) Delay in milliseconds for ending keyboard + * resize mode after inactivity. \\ + * \var{kbresize_t_max} & (integer) Controls keyboard resize acceleration. + * See description below for details. \\ + * \var{kbresize_t_min} & (integer) See below. \\ + * \var{kbresize_step} & (floating point) See below. \\ + * \var{kbresize_maxacc} & (floating point) See below. \\ + * \var{framed_transients} & (boolean) Put transients in nested frames. \\ + * \var{float_placement_method} & (string) How to place floating frames. + * One of ''udlr'' (up-down, then left-right), + * ''lrud'' (left-right, then up-down) or ''random''. \\ + * \var{default_ws_params} & (table) Default workspace layout; the + * attach/creation parameters for a \type{WGroup}. \\ + * \end{tabularx} + * + * When a keyboard resize function is called, and at most \var{kbresize_t_max} + * milliseconds has passed from a previous call, acceleration factor is reset + * to 1.0. Otherwise, if at least \var{kbresize_t_min} milliseconds have + * passed from the from previous acceleration update or reset the squere root + * of the acceleration factor is incremented by \var{kbresize_step}. The + * maximum acceleration factor (pixels/call modulo size hints) is given by + * \var{kbresize_maxacc}. The default values are (200, 50, 30, 100). + */ +EXTL_EXPORT +void ioncore_set(ExtlTab tab) +{ + int dd, rd; + char *wst, *tmp; + ExtlTab t; + + extl_table_gets_b(tab, "opaque_resize", &(ioncore_g.opaque_resize)); + extl_table_gets_b(tab, "warp", &(ioncore_g.warp_enabled)); + extl_table_gets_b(tab, "switchto", &(ioncore_g.switchto_new)); + extl_table_gets_b(tab, "screen_notify", &(ioncore_g.screen_notify)); + extl_table_gets_b(tab, "framed_transients", &(ioncore_g.framed_transients)); + + if(extl_table_gets_s(tab, "frame_default_index", &tmp)){ + ioncore_g.frame_default_index=stringintmap_value(frame_idxs, + tmp, + ioncore_g.frame_default_index); + free(tmp); + } + + if(extl_table_gets_i(tab, "dblclick_delay", &dd)) + ioncore_g.dblclick_delay=maxof(0, dd); + + ioncore_set_moveres_accel(tab); + + ioncore_groupws_set(tab); +} + + +/*EXTL_DOC + * Get ioncore basic settings. For details see \fnref{ioncore.set}. + */ +EXTL_SAFE +EXTL_EXPORT +ExtlTab ioncore_get() +{ + ExtlTab tab=extl_create_table(); + + extl_table_sets_b(tab, "opaque_resize", ioncore_g.opaque_resize); + extl_table_sets_b(tab, "warp", ioncore_g.warp_enabled); + extl_table_sets_b(tab, "switchto", ioncore_g.switchto_new); + extl_table_sets_i(tab, "dblclick_delay", ioncore_g.dblclick_delay); + extl_table_sets_b(tab, "screen_notify", ioncore_g.screen_notify); + + extl_table_sets_s(tab, "frame_default_index", + stringintmap_key(frame_idxs, + ioncore_g.frame_default_index, + NULL)); + + ioncore_get_moveres_accel(tab); + + ioncore_groupws_get(tab); + + return tab; +} + + +/*EXTL_DOC + * Get important directories (userdir, sessiondir, searchpath). + */ +EXTL_SAFE +EXTL_EXPORT +ExtlTab ioncore_get_paths(ExtlTab tab) +{ + tab=extl_create_table(); + extl_table_sets_s(tab, "userdir", extl_userdir()); + extl_table_sets_s(tab, "sessiondir", extl_sessiondir()); + extl_table_sets_s(tab, "searchpath", extl_searchpath()); + return tab; +} + + +/*EXTL_DOC + * Set important directories (sessiondir, searchpath). + */ +EXTL_EXPORT +bool ioncore_set_paths(ExtlTab tab) +{ + char *s; + + if(extl_table_gets_s(tab, "userdir", &s)){ + warn(TR("User directory can not be set.")); + free(s); + return FALSE; + } + + if(extl_table_gets_s(tab, "sessiondir", &s)){ + extl_set_sessiondir(s); + free(s); + return FALSE; + } + + if(extl_table_gets_s(tab, "searchpath", &s)){ + extl_set_searchpath(s); + free(s); + return FALSE; + } + + return TRUE; +} + + +/* Exports these in ioncore. */ + +/*EXTL_DOC + * Lookup script \var{file}. If \var{try_in_dir} is set, it is tried + * before the standard search path. + */ +EXTL_SAFE +EXTL_EXPORT_AS(ioncore, lookup_script) +char *extl_lookup_script(const char *file, const char *sp); + + +/*EXTL_DOC + * Get a file name to save (session) data in. The string \var{basename} + * should contain no path or extension components. + */ +EXTL_SAFE +EXTL_EXPORT_AS(ioncore, get_savefile) +char *extl_get_savefile(const char *basename); + + +/*EXTL_DOC + * Write \var{tab} in file with basename \var{basename} in the + * session directory. + */ +EXTL_SAFE +EXTL_EXPORT_AS(ioncore, write_savefile) +bool extl_write_savefile(const char *basename, ExtlTab tab); + + +/*EXTL_DOC + * Read a savefile. + */ +EXTL_SAFE +EXTL_EXPORT_AS(ioncore, read_savefile) +ExtlTab extl_extl_read_savefile(const char *basename); + + + +bool ioncore_read_main_config(const char *cfgfile) +{ + bool ret; + int unset=0; + + if(cfgfile==NULL) + cfgfile="cfg_ion"; + + ret=extl_read_config(cfgfile, ".", TRUE); + + unset+=(ioncore_rootwin_bindmap->nbindings==0); + unset+=(ioncore_mplex_bindmap->nbindings==0); + unset+=(ioncore_frame_bindmap->nbindings==0); + + if(unset>0){ + warn(TR("Some bindmaps were empty, loading ioncore_efbb.")); + extl_read_config("ioncore_efbb", NULL, TRUE); + } + + return (ret && unset==0); +} diff --git a/ioncore/conf.h b/ioncore/conf.h new file mode 100644 index 0000000..da3679b --- /dev/null +++ b/ioncore/conf.h @@ -0,0 +1,17 @@ +/* + * ion/ioncore/conf.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_CONF_H +#define ION_IONCORE_CONF_H + +extern bool ioncore_read_main_config(const char *cfgfile); + +#endif /* ION_IONCORE_CONF_H */ diff --git a/ioncore/cursor.c b/ioncore/cursor.c new file mode 100644 index 0000000..31ea25b --- /dev/null +++ b/ioncore/cursor.c @@ -0,0 +1,38 @@ +/* + * ion/ioncore/cursor.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include "common.h" +#include "event.h" +#include "cursor.h" +#include "global.h" + + +static Cursor cursors[IONCORE_N_CURSORS]; + +#define LCURS(TYPE) \ + cursors[IONCORE_CURSOR_##TYPE]=XCreateFontCursor(ioncore_g.dpy, CF_CURSOR_##TYPE) + +void ioncore_init_cursors() +{ + LCURS(DEFAULT); + LCURS(RESIZE); + LCURS(MOVE); + LCURS(DRAG); + LCURS(WAITKEY); +} + + +Cursor ioncore_xcursor(int cursor) +{ + return cursors[cursor]; +} + + diff --git a/ioncore/cursor.h b/ioncore/cursor.h new file mode 100644 index 0000000..0137e8c --- /dev/null +++ b/ioncore/cursor.h @@ -0,0 +1,28 @@ +/* + * ion/ioncore/cursor.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_CURSOR_H +#define ION_IONCORE_CURSOR_H + +#include +#include + +#define IONCORE_CURSOR_DEFAULT 0 +#define IONCORE_CURSOR_RESIZE 1 +#define IONCORE_CURSOR_MOVE 2 +#define IONCORE_CURSOR_DRAG 3 +#define IONCORE_CURSOR_WAITKEY 4 +#define IONCORE_N_CURSORS 5 + +extern void ioncore_init_cursors(); +extern Cursor ioncore_xcursor(int cursor); + +#endif /* ION_IONCORE_CURSOR_H */ diff --git a/ioncore/dummywc.h b/ioncore/dummywc.h new file mode 100644 index 0000000..f3343be --- /dev/null +++ b/ioncore/dummywc.h @@ -0,0 +1,55 @@ +/* + * ion/ioncore/dummywc.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +/* This file contains dummy implementations of multibyte/widechar routines + * used by Ion for retarded platforms. + */ + +#ifndef ION_IONCORE_DUMMYWC_H +#define ION_IONCORE_DUMMYWC_H + +#include +#include + +#define wchar_t int +#define mbstate_t int + +#define iswalnum isalnum +#define iswprint isprint +#define iswspace isspace + +#define mbrlen dummywc_mbrlen +#define mbtowc dummywc_mbtowc +#define mbrtowc dummywc_mbrtowc + +static size_t dummywc_mbrlen(const char *s, size_t n, mbstate_t *ps) +{ + if(*s=='\0') + return 0; + return 1; +} + +static int dummywc_mbtowc(wchar_t *pwc, const char *s, size_t n) +{ + if(n>0 && *s!='\0'){ + *pwc=*s; + return 1; + } + return 0; +} + +static size_t dummywc_mbrtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *ps) +{ + return mbtowc(pwc, s, n); +} + +#endif /* ION_IONCORE_DUMMYWC_H */ + diff --git a/ioncore/event.c b/ioncore/event.c new file mode 100644 index 0000000..84943d5 --- /dev/null +++ b/ioncore/event.c @@ -0,0 +1,239 @@ +/* + * ion/ioncore/event.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "common.h" +#include "global.h" +#include "event.h" +#include "eventh.h" +#include "focus.h" +#include "exec.h" +#include "ioncore.h" + + + +/*{{{ Hooks */ + + +WHook *ioncore_handle_event_alt=NULL; + + +/*}}}*/ + + +/*{{{ Signal check */ + + +static void check_signals() +{ + int kill_sig=mainloop_check_signals(); + + if(kill_sig!=0){ + if(kill_sig==SIGUSR1){ + ioncore_restart(); + assert(0); + } + if(kill_sig==SIGTERM){ + /* Save state if not running under a session manager. */ + ioncore_emergency_snapshot(); + ioncore_resign(); + /* We may still return here if running under a session manager. */ + }else{ + ioncore_emergency_snapshot(); + ioncore_deinit(); + kill(getpid(), kill_sig); + } + } +} + + +/*}}}*/ + + +/*{{{ Timestamp stuff */ + +#define CHKEV(E, T) case E: tm=((T*)ev)->time; break; +#define CLOCK_SKEW_MS 30000 + +static Time last_timestamp=CurrentTime; + +void ioncore_update_timestamp(XEvent *ev) +{ + Time tm; + + switch(ev->type){ + CHKEV(ButtonPress, XButtonPressedEvent); + CHKEV(ButtonRelease, XButtonReleasedEvent); + CHKEV(EnterNotify, XEnterWindowEvent); + CHKEV(KeyPress, XKeyPressedEvent); + CHKEV(KeyRelease, XKeyReleasedEvent); + CHKEV(LeaveNotify, XLeaveWindowEvent); + CHKEV(MotionNotify, XPointerMovedEvent); + CHKEV(PropertyNotify, XPropertyEvent); + CHKEV(SelectionClear, XSelectionClearEvent); + CHKEV(SelectionNotify, XSelectionEvent); + CHKEV(SelectionRequest, XSelectionRequestEvent); + default: + return; + } + + if(tm>last_timestamp || last_timestamp - tm > CLOCK_SKEW_MS) + last_timestamp=tm; +} + + +Time ioncore_get_timestamp() +{ + if(last_timestamp==CurrentTime){ + /* Idea blatantly copied from wmx */ + XEvent ev; + Atom dummy; + + D(fprintf(stderr, "Attempting to get time from X server.")); + + dummy=XInternAtom(ioncore_g.dpy, "_ION_TIMEREQUEST", False); + if(dummy==None){ + warn(TR("Time request from X server failed.")); + return 0; + } + /* TODO: use some other window that should also function as a + * NET_WM support check window. + */ + XChangeProperty(ioncore_g.dpy, ioncore_g.rootwins->dummy_win, + dummy, dummy, 8, PropModeAppend, + (unsigned char*)"", 0); + ioncore_get_event(&ev, PropertyChangeMask); + XPutBackEvent(ioncore_g.dpy, &ev); + } + + return last_timestamp; +} + + +/*}}}*/ + + +/*{{{ Event reading */ + + +void ioncore_get_event(XEvent *ev, long mask) +{ + fd_set rfds; + + while(1){ + check_signals(); + + if(XCheckMaskEvent(ioncore_g.dpy, mask, ev)){ + ioncore_update_timestamp(ev); + return; + } + + FD_ZERO(&rfds); + FD_SET(ioncore_g.conn, &rfds); + + /* Other FD:s are _not_ to be handled! */ + select(ioncore_g.conn+1, &rfds, NULL, NULL, NULL); + } +} + + +/*}}}*/ + + +/*{{{ Flush */ + + +static void skip_enterwindow() +{ + XEvent ev; + + XSync(ioncore_g.dpy, False); + + while(XCheckMaskEvent(ioncore_g.dpy, EnterWindowMask, &ev)){ + ioncore_update_timestamp(&ev); + } +} + + +void ioncore_flush() +{ + if(ioncore_g.focus_next!=NULL && + ioncore_g.input_mode==IONCORE_INPUTMODE_NORMAL){ + bool warp=ioncore_g.warp_next; + WRegion *next=ioncore_g.focus_next; + + ioncore_g.focus_next=NULL; + + region_do_set_focus(next, warp); + + /* Just greedily eating it all away that X has to offer + * seems to be the best we can do with Xlib. + */ + skip_enterwindow(); + } + + XFlush(ioncore_g.dpy); +} + + +/*}}}*/ + + +/*{{{ X connection FD handler */ + + +void ioncore_x_connection_handler(int conn, void *unused) +{ + XEvent ev; + + XNextEvent(ioncore_g.dpy, &ev); + ioncore_update_timestamp(&ev); + + hook_call_alt_p(ioncore_handle_event_alt, &ev, NULL); +} + + +/*}}}*/ + + +/*{{{ Mainloop */ + + +void ioncore_mainloop() +{ + mainloop_trap_signals(NULL); + + ioncore_g.opmode=IONCORE_OPMODE_NORMAL; + + while(1){ + check_signals(); + mainloop_execute_deferred(); + ioncore_flush(); + + if(QLength(ioncore_g.dpy)>0) + ioncore_x_connection_handler(ioncore_g.conn, NULL); + else + mainloop_select(); + } +} + + +/*}}}*/ + diff --git a/ioncore/event.h b/ioncore/event.h new file mode 100644 index 0000000..1bf227d --- /dev/null +++ b/ioncore/event.h @@ -0,0 +1,52 @@ +/* + * ion/ioncore/event.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_EVENT_H +#define ION_IONCORE_EVENT_H + +#include +#include "common.h" +#include "region.h" + +#define IONCORE_EVENTMASK_PTRGRAB (ButtonPressMask|ButtonReleaseMask| \ + ButtonMotionMask) + +#define IONCORE_EVENTMASK_PTRLOOP (IONCORE_EVENTMASK_PTRGRAB|ExposureMask| \ + KeyPressMask|KeyReleaseMask| \ + EnterWindowMask|FocusChangeMask) + +#define IONCORE_EVENTMASK_NORMAL (ExposureMask|KeyPressMask| \ + ButtonPressMask|ButtonReleaseMask| \ + FocusChangeMask|EnterWindowMask) + +#define IONCORE_EVENTMASK_CWINMGR (IONCORE_EVENTMASK_NORMAL| \ + SubstructureRedirectMask) + +#define IONCORE_EVENTMASK_ROOT (IONCORE_EVENTMASK_CWINMGR| \ + PropertyChangeMask|ColormapChangeMask) + +#define IONCORE_EVENTMASK_CLIENTWIN (ColormapChangeMask| \ + PropertyChangeMask|FocusChangeMask| \ + StructureNotifyMask|EnterWindowMask) + +extern void ioncore_x_connection_handler(int conn, void *unused); +extern void ioncore_flush(); +extern void ioncore_get_event(XEvent *ev, long mask); + +extern void ioncore_update_timestamp(XEvent *ev); +extern Time ioncore_get_timestamp(); + +/* Handlers to this hook should take XEvent* as parameter. */ +extern WHook *ioncore_handle_event_alt; + +extern void ioncore_mainloop(); + +#endif /* ION_IONCORE_EVENT_H */ diff --git a/ioncore/eventh.c b/ioncore/eventh.c new file mode 100644 index 0000000..91ab550 --- /dev/null +++ b/ioncore/eventh.c @@ -0,0 +1,534 @@ +/* + * ion/ioncore/eventh.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include + +#include +#include "common.h" +#include "global.h" +#include "rootwin.h" +#include "property.h" +#include "pointer.h" +#include "key.h" +#include "focus.h" +#include "selection.h" +#include "event.h" +#include "eventh.h" +#include "clientwin.h" +#include "colormap.h" +#include "grab.h" +#include "bindmaps.h" +#include "activity.h" +#include "netwm.h" +#include "xwindow.h" + + +/*{{{ ioncore_handle_event */ + + +#define CASE_EVENT(EV) case EV: /*\ + fprintf(stderr, "[%#lx] %s\n", ev->xany.window, #EV);*/ + + +bool ioncore_handle_event(XEvent *ev) +{ + + switch(ev->type){ + CASE_EVENT(MapRequest) + ioncore_handle_map_request(&(ev->xmaprequest)); + break; + CASE_EVENT(ConfigureRequest) + ioncore_handle_configure_request(&(ev->xconfigurerequest)); + break; + CASE_EVENT(UnmapNotify) + ioncore_handle_unmap_notify(&(ev->xunmap)); + break; + CASE_EVENT(DestroyNotify) + ioncore_handle_destroy_notify(&(ev->xdestroywindow)); + break; + CASE_EVENT(ClientMessage) + ioncore_handle_client_message(&(ev->xclient)); + break; + CASE_EVENT(PropertyNotify) + ioncore_handle_property(&(ev->xproperty)); + break; + CASE_EVENT(FocusIn) + ioncore_handle_focus_in(&(ev->xfocus), FALSE); + break; + CASE_EVENT(FocusOut) + ioncore_handle_focus_out(&(ev->xfocus)); + break; + CASE_EVENT(EnterNotify) + ioncore_handle_enter_window(ev); + break; + CASE_EVENT(Expose) + ioncore_handle_expose(&(ev->xexpose)); + break; + CASE_EVENT(KeyPress) + ioncore_handle_keyboard(ev); + break; + CASE_EVENT(KeyRelease) + ioncore_handle_keyboard(ev); + break; + CASE_EVENT(ButtonPress) + ioncore_handle_buttonpress(ev); + break; + CASE_EVENT(ColormapNotify) + ioncore_handle_colormap_notify(&(ev->xcolormap)); + break; + CASE_EVENT(MappingNotify) + ioncore_handle_mapping_notify(ev); + break; + CASE_EVENT(SelectionClear) + ioncore_clear_selection(); + break; + CASE_EVENT(SelectionNotify) + ioncore_handle_selection(&(ev->xselection)); + break; + CASE_EVENT(SelectionRequest) + ioncore_handle_selection_request(&(ev->xselectionrequest)); + break; + } + + return TRUE; +} + + +/*}}}*/ + + +/*{{{ Map, unmap, destroy */ + + +void ioncore_handle_map_request(const XMapRequestEvent *ev) +{ + WRegion *reg; + + reg=XWINDOW_REGION_OF(ev->window); + + if(reg!=NULL) + return; + + ioncore_manage_clientwin(ev->window, TRUE); +} + + +void ioncore_handle_unmap_notify(const XUnmapEvent *ev) +{ + WClientWin *cwin; + + /* We are not interested in SubstructureNotify -unmaps. */ + if(ev->event!=ev->window && ev->send_event!=True) + return; + + cwin=XWINDOW_REGION_OF_T(ev->window, WClientWin); + + if(cwin!=NULL) + clientwin_unmapped(cwin); +} + + +void ioncore_handle_destroy_notify(const XDestroyWindowEvent *ev) +{ + WClientWin *cwin; + + cwin=XWINDOW_REGION_OF_T(ev->window, WClientWin); + + if(cwin!=NULL) + clientwin_destroyed(cwin); +} + + +/*}}}*/ + + +/*{{{ Client configure/property/message */ + + +void ioncore_handle_configure_request(XConfigureRequestEvent *ev) +{ + WClientWin *cwin; + XWindowChanges wc; + + cwin=XWINDOW_REGION_OF_T(ev->window, WClientWin); + + if(cwin==NULL){ + wc.border_width=ev->border_width; + wc.sibling=ev->above; + wc.stack_mode=ev->detail; + wc.x=ev->x; + wc.y=ev->y; + wc.width=ev->width; + wc.height=ev->height; + XConfigureWindow(ioncore_g.dpy, ev->window, ev->value_mask, &wc); + return; + } + + clientwin_handle_configure_request(cwin, ev); +} + + +void ioncore_handle_client_message(const XClientMessageEvent *ev) +{ + netwm_handle_client_message(ev); + +#if 0 + WClientWin *cwin; + + if(ev->message_type!=ioncore_g.atom_wm_change_state) + return; + + cwin=XWINDOW_REGION_OF_T(ev->window, WClientWin); + + if(cwin==NULL) + return; + + if(ev->format==32 && ev->data.l[0]==IconicState){ + if(cwin->state==NormalState) + iconify_clientwin(cwin); + } +#endif +} + + +static bool pchg_mrsh_extl(ExtlFn fn, void **p) +{ + extl_call(fn, "oi", NULL, p[0], ((XPropertyEvent*)p[1])->atom); + return TRUE; +} + +static bool pchg_mrsh(void (*fn)(void *p1, void *p2), void **p) +{ + fn(p[0], p[1]); + return TRUE; +} + + +void ioncore_handle_property(const XPropertyEvent *ev) +{ + WClientWin *cwin; + + cwin=XWINDOW_REGION_OF_T(ev->window, WClientWin); + + if(cwin==NULL) + return; + + if(ev->atom==XA_WM_HINTS){ + XWMHints *hints; + hints=XGetWMHints(ioncore_g.dpy, ev->window); + /* region_notify/clear_activity take care of checking current state */ + if(hints!=NULL){ + if(hints->flags&XUrgencyHint){ + if(!region_skip_focus((WRegion*)cwin)) + region_set_activity((WRegion*)cwin, SETPARAM_SET); + }else{ + region_set_activity((WRegion*)cwin, SETPARAM_UNSET); + } + } + XFree(hints); + }else if(ev->atom==XA_WM_NORMAL_HINTS){ + clientwin_get_size_hints(cwin); + }else if(ev->atom==XA_WM_NAME){ + if(!(cwin->flags&CLIENTWIN_USE_NET_WM_NAME)) + clientwin_get_set_name(cwin); + }else if(ev->atom==XA_WM_TRANSIENT_FOR){ + clientwin_tfor_changed(cwin); + }else if(ev->atom==ioncore_g.atom_wm_protocols){ + clientwin_get_protocols(cwin); + }else{ + netwm_handle_property(cwin, ev); + } + + /* Call property hook */ + { + void *p[2]; + p[0]=(void*)cwin; + p[1]=(void*)ev; + hook_call(clientwin_property_change_hook, p, + (WHookMarshall*)pchg_mrsh, + (WHookMarshallExtl*)pchg_mrsh_extl); + } +} + + +/*}}}*/ + + +/*{{{ Misc. notifies */ + + +void ioncore_handle_mapping_notify(XEvent *ev) +{ + do{ + XRefreshKeyboardMapping(&(ev->xmapping)); + }while(XCheckTypedEvent(ioncore_g.dpy, MappingNotify, ev)); + + ioncore_refresh_bindmaps(); +} + + +/*}}}*/ + + +/*{{{ Expose */ + + +void ioncore_handle_expose(const XExposeEvent *ev) +{ + WWindow *wwin; + WRootWin *rootwin; + XEvent tmp; + + while(XCheckWindowEvent(ioncore_g.dpy, ev->window, ExposureMask, &tmp)) + /* nothing */; + + wwin=XWINDOW_REGION_OF_T(ev->window, WWindow); + + if(wwin!=NULL) + window_draw(wwin, FALSE); +} + + +/*}}}*/ + + +/*{{{ Enter window, focus */ + + +/*extern Time ioncore_focus_time;*/ + + +static void do_handle_enter_window(XEvent *ev) +{ + XEnterWindowEvent *eev=&(ev->xcrossing); + WRegion *reg=NULL; + + if(ioncore_g.input_mode!=IONCORE_INPUTMODE_NORMAL) + return; + + /*if(ioncore_g.focus_next!=NULL && ioncore_focus_time==CurrentTime) + return;*/ + + /*if(ioncore_await_warp()) + return; + + if(eev->mode!=NotifyNormal && !ioncore_g.warp_enabled) + return;*/ + + reg=XWINDOW_REGION_OF_T(eev->window, WRegion); + + if(reg==NULL) + return; + + if(REGION_IS_ACTIVE(reg)) + return; + + if(region_skip_focus(reg)) + return; + + /* If a child of 'reg' is to be focused, do not process this + * event. + */ + if(ioncore_g.focus_next!=NULL){ + WRegion *r2=ioncore_g.focus_next; + while(r2!=NULL){ + if(r2==reg) + return; + r2=REGION_PARENT_REG(r2); + } + } + + region_goto_flags(reg, (REGION_GOTO_FOCUS| + REGION_GOTO_NOWARP| + REGION_GOTO_ENTERWINDOW)); +} + + +void ioncore_handle_enter_window(XEvent *ev) +{ + do{ + /* *sigh*, it doesn't seem reasonably simply possible to + * process events in-order. + */ + do_handle_enter_window(ev); + }while(XCheckMaskEvent(ioncore_g.dpy, EnterWindowMask, ev)); +} + + +static bool pointer_in_root(Window root1) +{ + Window root2=None, win; + int x, y, wx, wy; + uint mask; + + XQueryPointer(ioncore_g.dpy, root1, &root2, &win, + &x, &y, &wx, &wy, &mask); + + return (root1==root2); +} + + + +void ioncore_handle_focus_in(const XFocusChangeEvent *ev, bool skip) +{ + WRegion *reg; + WWindow *wwin; + + reg=XWINDOW_REGION_OF_T(ev->window, WRegion); + + if(reg==NULL) + return; + + D(fprintf(stderr, "FI: %s %p %d %d\n", OBJ_TYPESTR(reg), reg, ev->mode, ev->detail);) + + if(ev->mode==NotifyGrab) + return; + + if(ev->detail==NotifyPointer) + return; + + /* Root windows appear either as WRootWins or WScreens */ + if(ev->window==region_root_of(reg)){ + D(fprintf(stderr, "scr-in %d %d %d\n", ROOTWIN_OF(reg)->xscr, + ev->mode, ev->detail)); + if((ev->detail==NotifyPointerRoot || ev->detail==NotifyDetailNone) && + pointer_in_root(ev->window) && ioncore_g.focus_next==NULL){ + /* Restore focus */ + if(!skip) + region_set_focus(reg); + return; + } + /*return;*/ + } + + /* Input contexts */ + if(OBJ_IS(reg, WWindow)){ + wwin=(WWindow*)reg; + if(wwin->xic!=NULL) + XSetICFocus(wwin->xic); + } + + if(ev->detail!=NotifyInferior) + netwm_set_active(reg); + + region_got_focus(reg); +} + + +void ioncore_handle_focus_out(const XFocusChangeEvent *ev) +{ + WRegion *reg; + WWindow *wwin; + + reg=XWINDOW_REGION_OF_T(ev->window, WRegion); + + if(reg==NULL) + return; + + D(fprintf(stderr, "FO: %s %p %d %d\n", OBJ_TYPESTR(reg), reg, ev->mode, ev->detail);) + + if(ev->mode==NotifyGrab) + return; + + if(ev->detail==NotifyPointer) + return; + + D(if(OBJ_IS(reg, WRootWin)) + fprintf(stderr, "scr-out %d %d %d\n", ((WRootWin*)reg)->xscr, ev->mode, ev->detail)); + + if(OBJ_IS(reg, WWindow)){ + wwin=(WWindow*)reg; + if(wwin->xic!=NULL) + XUnsetICFocus(wwin->xic); + } + + if(ev->detail!=NotifyInferior) + region_lost_focus(reg); + else + region_got_focus(reg); +} + + +/*}}}*/ + + +/*{{{ Pointer, keyboard */ + + +void ioncore_handle_buttonpress(XEvent *ev) +{ + XEvent tmp; + Window win_pressed; + bool finished=FALSE; + + if(ioncore_grab_held()) + return; + + win_pressed=ev->xbutton.window; + + if(!ioncore_do_handle_buttonpress(&(ev->xbutton))) + return; + + while(!finished && ioncore_grab_held()){ + XFlush(ioncore_g.dpy); + ioncore_get_event(ev, IONCORE_EVENTMASK_PTRLOOP); + + if(ev->type==MotionNotify){ + /* Handle sequences of MotionNotify (possibly followed by button + * release) as one. + */ + if(XPeekEvent(ioncore_g.dpy, &tmp)){ + if(tmp.type==MotionNotify || tmp.type==ButtonRelease) + XNextEvent(ioncore_g.dpy, ev); + } + } + + switch(ev->type){ + CASE_EVENT(ButtonRelease) + if(ioncore_do_handle_buttonrelease(&ev->xbutton)) + finished=TRUE; + break; + CASE_EVENT(MotionNotify) + ioncore_do_handle_motionnotify(&ev->xmotion); + break; + CASE_EVENT(Expose) + ioncore_handle_expose(&(ev->xexpose)); + break; + CASE_EVENT(KeyPress) + CASE_EVENT(KeyRelease) + ioncore_handle_grabs(ev); + break; + CASE_EVENT(FocusIn) + ioncore_handle_focus_in(&(ev->xfocus), FALSE); + break; + CASE_EVENT(FocusOut) + ioncore_handle_focus_out(&(ev->xfocus)); + break; + } + } +} + + +void ioncore_handle_keyboard(XEvent *ev) +{ + if(ioncore_handle_grabs(ev)) + return; + + if(ev->type==KeyPress) + ioncore_do_handle_keypress(&(ev->xkey)); +} + + +/*}}}*/ + + diff --git a/ioncore/eventh.h b/ioncore/eventh.h new file mode 100644 index 0000000..f80257b --- /dev/null +++ b/ioncore/eventh.h @@ -0,0 +1,32 @@ +/* + * ion/ioncore/eventh.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_EVENTH_H +#define ION_IONCORE_EVENTH_H + +#include "common.h" + +extern bool ioncore_handle_event(XEvent *ev); +extern void ioncore_handle_expose(const XExposeEvent *ev); +extern void ioncore_handle_map_request(const XMapRequestEvent *ev); +extern void ioncore_handle_configure_request(XConfigureRequestEvent *ev); +extern void ioncore_handle_enter_window(XEvent *ev); +extern void ioncore_handle_unmap_notify(const XUnmapEvent *ev); +extern void ioncore_handle_destroy_notify(const XDestroyWindowEvent *ev); +extern void ioncore_handle_client_message(const XClientMessageEvent *ev); +extern void ioncore_handle_focus_in(const XFocusChangeEvent *ev, bool skip); +extern void ioncore_handle_focus_out(const XFocusChangeEvent *ev); +extern void ioncore_handle_property(const XPropertyEvent *ev); +extern void ioncore_handle_buttonpress(XEvent *ev); +extern void ioncore_handle_keyboard(XEvent *ev); +extern void ioncore_handle_mapping_notify(XEvent *ev); + +#endif /* ION_IONCORE_EVENTH_H */ diff --git a/ioncore/exec.c b/ioncore/exec.c new file mode 100644 index 0000000..10c8267 --- /dev/null +++ b/ioncore/exec.c @@ -0,0 +1,286 @@ +/* + * ion/ioncore/exec.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include + +#include +#include + +#include "common.h" +#include "exec.h" +#include "property.h" +#include "global.h" +#include "ioncore.h" +#include "saveload.h" + + +/*{{{ Exec */ + + +void ioncore_setup_environ(int xscr) +{ + char *tmp, *ptr; + char *display; + + display=XDisplayName(ioncore_g.display); + + /* %ui, UINT_MAX is used to ensure there is enough space for the screen + * number + */ + libtu_asprintf(&tmp, "DISPLAY=%s.0123456789a", display); + + if(tmp==NULL) + return; + + ptr=strchr(tmp, ':'); + if(ptr!=NULL){ + ptr=strchr(ptr, '.'); + if(ptr!=NULL) + *ptr='\0'; + } + + if(xscr>=0) + snprintf(tmp+strlen(tmp), 11, ".%u", (unsigned)xscr); + + putenv(tmp); + + /* No need to free it, we'll execve soon */ + /*free(tmp);*/ + + /*XFree(display);*/ +} + + +typedef struct{ + WRootWin *rw; + const char *wd; +} ExecP; + + +static void setup_exec(void *p_) +{ + ExecP *p=(ExecP*)p_; + + close(ioncore_g.conn); + + ioncore_g.dpy=NULL; + +#ifndef CF_NO_SETPGID + setpgid(0, 0); +#endif + + ioncore_setup_environ(p->rw==NULL ? -1 : p->rw->xscr); + + if(p->wd!=NULL){ + if(chdir(p->wd)!=0) + warn_err_obj(p->wd); + } +} + + +EXTL_EXPORT +int ioncore_do_exec_rw(WRootWin *rw, const char *cmd, const char *wd, + ExtlFn errh) +{ + ExecP p; + + p.rw=rw; + p.wd=wd; + + return mainloop_popen_bgread(cmd, setup_exec, (void*)&p, + extl_fn_none(), errh); +} + + +/*EXTL_DOC + * Run \var{cmd} with the environment variable DISPLAY set to point to the + * X display the WM is running on. No specific screen is set unlike with + * \fnref{WRootWin.exec_on}. The PID of the (shell executing the) new + * process is returned. + */ +EXTL_SAFE +EXTL_EXPORT +int ioncore_exec(const char *cmd) +{ + return ioncore_do_exec_rw(NULL, cmd, NULL, extl_fn_none()); +} + + +/*EXTL_DOC + * Run \var{cmd} with a read pipe connected to its stdout. + * When data is received through the pipe, \var{handler} is called + * with that data. + */ +EXTL_SAFE +EXTL_EXPORT +int ioncore_popen_bgread(const char *cmd, ExtlFn h, ExtlFn errh) +{ + ExecP p; + + p.rw=NULL; + p.wd=NULL; + + return mainloop_popen_bgread(cmd, setup_exec, (void*)&p, h, errh); +} + + + +/*}}}*/ + + +/*{{{ Exit, restart, snapshot */ + + +static void (*smhook)(int what); + +bool ioncore_set_smhook(void (*fn)(int what)) +{ + smhook=fn; + return TRUE; +} + + +void ioncore_do_exit() +{ + ioncore_deinit(); + exit(0); +} + + +bool ioncore_do_snapshot() +{ + if(!ioncore_save_layout()) + return FALSE; + + extl_protect(NULL); + hook_call_v(ioncore_snapshot_hook); + extl_unprotect(NULL); + + return TRUE; +} + + +void ioncore_emergency_snapshot() +{ + if(smhook!=NULL) + warn(TR("Not saving state: running under session manager.")); + else + ioncore_do_snapshot(); +} + + + +static char *other=NULL; + +static void set_other(const char *s) +{ + if(other!=NULL) + free(other); + other=(s==NULL ? NULL : scopy(s)); +} + + +void ioncore_do_restart() +{ + ioncore_deinit(); + if(other!=NULL){ + if(ioncore_g.display!=NULL) + ioncore_setup_environ(-1); + mainloop_do_exec(other); + warn_err_obj(other); + } + execvp(ioncore_g.argv[0], ioncore_g.argv); + die_err_obj(ioncore_g.argv[0]); +} + + +/*EXTL_DOC + * Causes the window manager to simply exit without saving + * state/session. + */ +EXTL_EXPORT +void ioncore_resign() +{ + if(smhook!=NULL){ + smhook(IONCORE_SM_RESIGN); + }else{ + ioncore_do_exit(); + } +} + + +/*EXTL_DOC + * End session saving it first. + */ +EXTL_EXPORT +void ioncore_shutdown() +{ + if(smhook!=NULL){ + smhook(IONCORE_SM_SHUTDOWN); + }else{ + ioncore_do_snapshot(); + ioncore_do_exit(); + } +} + + +/*EXTL_DOC + * Restart, saving session first. + */ +EXTL_EXPORT +void ioncore_restart() +{ + set_other(NULL); + + if(smhook!=NULL){ + smhook(IONCORE_SM_RESTART); + }else{ + ioncore_do_snapshot(); + ioncore_do_restart(); + } +} + + +/*EXTL_DOC + * Attempt to restart another window manager \var{cmd}. + */ +EXTL_EXPORT +void ioncore_restart_other(const char *cmd) +{ + set_other(cmd); + + if(smhook!=NULL){ + smhook(IONCORE_SM_RESTART_OTHER); + }else{ + ioncore_do_snapshot(); + ioncore_do_restart(); + } +} + + +/*EXTL_DOC + * Save session. + */ +EXTL_EXPORT +void ioncore_snapshot() +{ + if(smhook!=NULL) + smhook(IONCORE_SM_SNAPSHOT); + else + ioncore_do_snapshot(); +} + + +/*}}}*/ + diff --git a/ioncore/exec.h b/ioncore/exec.h new file mode 100644 index 0000000..63c8917 --- /dev/null +++ b/ioncore/exec.h @@ -0,0 +1,47 @@ +/* + * ion/ioncore/exec.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_EXEC_H +#define ION_IONCORE_EXEC_H + +#include "common.h" +#include "rootwin.h" +#include + +enum{ + IONCORE_SM_RESIGN, + IONCORE_SM_SHUTDOWN, + IONCORE_SM_RESTART, + IONCORE_SM_RESTART_OTHER, + IONCORE_SM_SNAPSHOT +}; + +extern void ioncore_do_exec(const char *cmd); +extern bool ioncore_exec_on_rootwin(WRootWin *rootwin, const char *cmd); +extern bool ioncore_exec(const char *cmd); +extern void ioncore_setup_environ(int scr); +extern bool ioncore_popen_bgread(const char *cmd, ExtlFn h, ExtlFn errh); + +extern bool ioncore_set_smhook(void (*fn)(int what)); + +extern void ioncore_restart_other(const char *cmd); +extern void ioncore_restart(); +extern void ioncore_shutdown(); +extern void ioncore_resign(); +extern void ioncore_snapshot(); + +extern void ioncore_do_exit(); +extern void ioncore_do_restart(); +extern bool ioncore_do_snapshot(); + +extern void ioncore_emergency_snapshot(); + +#endif /* ION_IONCORE_EXEC_H */ diff --git a/ioncore/extlconv.c b/ioncore/extlconv.c new file mode 100644 index 0000000..5f23b74 --- /dev/null +++ b/ioncore/extlconv.c @@ -0,0 +1,115 @@ +/* + * ion/ioncore/extlconv.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include + +#include +#include "common.h" +#include "extlconv.h" + + +/*{{{ Object list */ + + +ExtlTab extl_obj_iterable_to_table(ObjIterator *iter, void *st) +{ + ExtlTab tab=extl_create_table(); + int i=1; + Obj *obj; + + while(1){ + obj=iter(st); + if(obj==NULL) + break; + if(extl_table_seti_o(tab, i, obj)) + i++; + } + + return tab; +} + + +/*}}}*/ + + +/*{{{ Booleans */ + + +bool extl_table_is_bool_set(ExtlTab tab, const char *entry) +{ + bool b; + + if(extl_table_gets_b(tab, entry, &b)) + return b; + return FALSE; +} + + +/*}}}*/ + + +/*{{{ Rectangles */ + + +bool extl_table_to_rectangle(ExtlTab tab, WRectangle *rectret) +{ + if(!extl_table_gets_i(tab, "x", &(rectret->x)) || + !extl_table_gets_i(tab, "y", &(rectret->y)) || + !extl_table_gets_i(tab, "w", &(rectret->w)) || + !extl_table_gets_i(tab, "h", &(rectret->h))) + return FALSE; + + return TRUE; +} + + +ExtlTab extl_table_from_rectangle(const WRectangle *rect) +{ + ExtlTab tab=extl_create_table(); + + extl_table_sets_i(tab, "x", rect->x); + extl_table_sets_i(tab, "y", rect->y); + extl_table_sets_i(tab, "w", rect->w); + extl_table_sets_i(tab, "h", rect->h); + + return tab; +} + + +void extl_table_sets_rectangle(ExtlTab tab, const char *nam, + const WRectangle *rect) +{ + ExtlTab g=extl_table_from_rectangle(rect); + extl_table_sets_t(tab, nam, g); + extl_unref_table(g); +} + + +bool extl_table_gets_rectangle(ExtlTab tab, const char *nam, + WRectangle *rect) +{ + ExtlTab g; + bool ok; + + if(!extl_table_gets_t(tab, nam, &g)) + return FALSE; + + ok=extl_table_to_rectangle(g, rect); + + extl_unref_table(g); + + return ok; +} + + +/*}}}*/ + + diff --git a/ioncore/extlconv.h b/ioncore/extlconv.h new file mode 100644 index 0000000..b4e3b80 --- /dev/null +++ b/ioncore/extlconv.h @@ -0,0 +1,34 @@ +/* + * ion/ioncore/extlconv.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_EXTLCONV_H +#define ION_IONCORE_EXTLCONV_H + +#include +#include +#include +#include "common.h" +#include "region.h" + +extern ExtlTab extl_obj_iterable_to_table(ObjIterator *iter, void *st); + +extern bool extl_table_is_bool_set(ExtlTab tab, const char *entry); + +extern bool extl_table_to_rectangle(ExtlTab tab, WRectangle *rect); +extern ExtlTab extl_table_from_rectangle(const WRectangle *rect); + +extern bool extl_table_gets_rectangle(ExtlTab tab, const char *nam, + WRectangle *rect); +extern void extl_table_sets_rectangle(ExtlTab tab, const char *nam, + const WRectangle *rect); + +#endif /* ION_IONCORE_EXTLCONV_H */ + diff --git a/ioncore/extlrx.c b/ioncore/extlrx.c new file mode 100644 index 0000000..b9a380e --- /dev/null +++ b/ioncore/extlrx.c @@ -0,0 +1,55 @@ +/* + * ion/ioncore/extlrx.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + + +#include "common.h" + + +/*{{{ libtu */ + + +/*EXTL_DOC + * Issue a warning. How the message is displayed depends on the current + * warning handler. + */ +EXTL_SAFE +EXTL_UNTRACED +EXTL_EXPORT +void ioncore_warn(const char *str) +{ + warn("%s", str); +} + + +/*EXTL_DOC + * Similar to \fnref{ioncore.warn}, but also print Lua stack trace. + */ +EXTL_SAFE +EXTL_EXPORT +void ioncore_warn_traced(const char *str) +{ + warn("%s", str); +} + + +EXTL_SAFE +EXTL_EXPORT +const char *ioncore_gettext(const char *s) +{ + if(s==NULL) + return NULL; + else + return TR(s); +} + + +/*}}}*/ + diff --git a/ioncore/float-placement.c b/ioncore/float-placement.c new file mode 100644 index 0000000..399f5ec --- /dev/null +++ b/ioncore/float-placement.c @@ -0,0 +1,170 @@ +/* + * ion/ioncore/float-placement.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include + +#include "common.h" +#include "group.h" +#include "float-placement.h" + + +WFloatPlacement ioncore_placement_method=PLACEMENT_LRUD; + + +static void random_placement(WRectangle box, WRectangle *g) +{ + box.w-=g->w; + box.h-=g->h; + g->x=box.x+(box.w<=0 ? 0 : rand()%box.w); + g->y=box.y+(box.h<=0 ? 0 : rand()%box.h); +} + + +static void ggeom(WRegion *reg, WRectangle *geom) +{ + *geom=REGION_GEOM(reg); +} + + +#define IGNORE_ST(ST, WS) ((ST)->reg==NULL || (ST)==(WS)->bottom) + +static WRegion* is_occupied(WGroup *ws, const WRectangle *r) +{ + WGroupIterTmp tmp; + WStacking *st; + WRectangle p; + + FOR_ALL_NODES_IN_GROUP(ws, st, tmp){ + if(IGNORE_ST(st, ws)) + continue; + + ggeom(st->reg, &p); + + if(r->x>=p.x+p.w) + continue; + if(r->y>=p.y+p.h) + continue; + if(r->x+r->w<=p.x) + continue; + if(r->y+r->h<=p.y) + continue; + return st->reg; + } + + return NULL; +} + + +static int next_least_x(WGroup *ws, int x) +{ + WRectangle p; + int retx=REGION_GEOM(ws).x+REGION_GEOM(ws).w; + WGroupIterTmp tmp; + WStacking *st; + + FOR_ALL_NODES_IN_GROUP(ws, st, tmp){ + if(IGNORE_ST(st, ws)) + continue; + + ggeom(st->reg, &p); + + if(p.x+p.w>x && p.x+p.wreg, &p); + + if(p.y+p.h>y && p.y+p.hw; + r.h=g->h; + + maxx=REGION_GEOM(ws).x+REGION_GEOM(ws).w; + maxy=REGION_GEOM(ws).y+REGION_GEOM(ws).h; + + if(ioncore_placement_method==PLACEMENT_UDLR){ + while(r.xx=r.x; + g->y=r.y; + return TRUE; + }else{ + r.x=next_least_x(ws, r.x); + r.y=0; + } + } + }else{ + while(r.yx=r.x; + g->y=r.y; + return TRUE; + }else{ + r.y=next_lowest_y(ws, r.y); + r.x=0; + } + } + } + + return FALSE; + +} + + +void group_calc_placement(WGroup *ws, WRectangle *geom) +{ + if(ioncore_placement_method!=PLACEMENT_RANDOM){ + if(tiling_placement(ws, geom)) + return; + } + random_placement(REGION_GEOM(ws), geom); +} + diff --git a/ioncore/float-placement.h b/ioncore/float-placement.h new file mode 100644 index 0000000..0f52daf --- /dev/null +++ b/ioncore/float-placement.h @@ -0,0 +1,27 @@ +/* + * ion/ioncore/float-placement.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_FLOAT_PLACEMENT_H +#define ION_IONCORE_FLOAT_PLACEMENT_H + +#include "common.h" +#include "group.h" + + +typedef enum{ + PLACEMENT_LRUD, PLACEMENT_UDLR, PLACEMENT_RANDOM +} WFloatPlacement; + +extern WFloatPlacement ioncore_placement_method; + +extern void group_calc_placement(WGroup *ws, WRectangle *geom); + +#endif /* ION_IONCORE_FLOAT_PLACEMENT_H */ diff --git a/ioncore/focus.c b/ioncore/focus.c new file mode 100644 index 0000000..fb2159b --- /dev/null +++ b/ioncore/focus.c @@ -0,0 +1,435 @@ +/* + * ion/ioncore/focus.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include "common.h" +#include "focus.h" +#include "global.h" +#include "window.h" +#include "region.h" +#include "colormap.h" +#include "activity.h" +#include "xwindow.h" +#include "regbind.h" + + +/*{{{ Hooks. */ + + +WHook *region_do_warp_alt=NULL; +WHook *region_activated_hook=NULL; +WHook *region_inactivated_hook=NULL; + + +/*}}}*/ + + +/*{{{ Focus list */ + + +void region_focuslist_remove(WRegion *reg) +{ + UNLINK_ITEM(ioncore_g.focus_current, reg, active_next, active_prev); +} + + +void region_focuslist_push(WRegion *reg) +{ + region_focuslist_remove(reg); + LINK_ITEM_FIRST(ioncore_g.focus_current, reg, active_next, active_prev); +} + + +void region_focuslist_move_after(WRegion *reg, WRegion *after) +{ + region_focuslist_remove(reg); + LINK_ITEM_AFTER(ioncore_g.focus_current, after, reg, + active_next, active_prev); +} + + +void region_focuslist_deinit(WRegion *reg) +{ + WRegion *replace=region_manager_or_parent(reg); + + if(replace!=NULL) + region_focuslist_move_after(replace, reg); + + region_focuslist_remove(reg); +} + + +/*EXTL_DOC + * Go to and return to a previously active region (if any). + * + * Note that this function is asynchronous; the region will not + * actually have received the focus when this function returns. + */ +EXTL_EXPORT +WRegion *ioncore_goto_previous() +{ + WRegion *next; + + if(ioncore_g.focus_current==NULL) + return NULL; + + /* Find the first region on focus history list that isn't currently + * active. + */ + for(next=ioncore_g.focus_current->active_next; + next!=NULL; + next=next->active_next){ + + if(!REGION_IS_ACTIVE(next)) + break; + } + + if(next!=NULL) + region_goto(next); + + return next; +} + + +/*}}}*/ + + +/*{{{ Await focus */ + + +static Watch await_watch=WATCH_INIT; + + +static void await_watch_handler(Watch *watch, WRegion *prev) +{ + WRegion *r; + while(1){ + r=REGION_PARENT_REG(prev); + if(r==NULL) + break; + + if(watch_setup(&await_watch, (Obj*)r, + (WatchHandler*)await_watch_handler)) + break; + prev=r; + } +} + + +void region_set_await_focus(WRegion *reg) +{ + if(reg==NULL){ + watch_reset(&await_watch); + }else{ + watch_setup(&await_watch, (Obj*)reg, + (WatchHandler*)await_watch_handler); + } +} + + +static bool region_is_await(WRegion *reg) +{ + WRegion *aw=(WRegion*)await_watch.obj; + + while(aw!=NULL){ + if(aw==reg) + return TRUE; + aw=REGION_PARENT_REG(aw); + } + + return FALSE; +} + + +/* Only keep await status if focus event is to an ancestor of the await + * region. + */ +static void check_clear_await(WRegion *reg) +{ + if(region_is_await(reg) && reg!=(WRegion*)await_watch.obj) + return; + + watch_reset(&await_watch); +} + + +bool ioncore_await_focus() +{ + return (await_watch.obj!=NULL); +} + + +/*}}}*/ + + +/*{{{ Events */ + + +void region_got_focus(WRegion *reg) +{ + WRegion *par, *mgr, *tmp; + + check_clear_await(reg); + + region_set_activity(reg, SETPARAM_UNSET); + + if(reg->active_sub==NULL){ + region_focuslist_push(reg); + /*ioncore_g.focus_current=reg;*/ + } + + if(!REGION_IS_ACTIVE(reg)){ + D(fprintf(stderr, "got focus (inact) %s [%p]\n", OBJ_TYPESTR(reg), reg);) + reg->flags|=REGION_ACTIVE; + + par=REGION_PARENT_REG(reg); + if(par!=NULL){ + par->active_sub=reg; + region_update_owned_grabs(par); + } + + region_activated(reg); + + mgr=REGION_MANAGER(reg); + tmp=reg; + while(mgr!=NULL){ + /* We need to loop over managing non-windows (workspaces) here to + * signal their managers. + */ + region_managed_activated(mgr, tmp); + + if(REGION_PARENT_REG(reg)==mgr) + break; + + tmp=mgr; + mgr=REGION_MANAGER(mgr); + } + }else{ + D(fprintf(stderr, "got focus (act) %s [%p]\n", OBJ_TYPESTR(reg), reg);) + } + + /* Install default colour map only if there is no active subregion; + * their maps should come first. WClientWins will install their maps + * in region_activated. Other regions are supposed to use the same + * default map. + */ + if(reg->active_sub==NULL && !OBJ_IS(reg, WClientWin)) + rootwin_install_colormap(region_rootwin_of(reg), None); + + extl_protect(NULL); + hook_call_o(region_activated_hook, (Obj*)reg); + extl_unprotect(NULL); +} + + +void region_lost_focus(WRegion *reg) +{ + WRegion *r, *par; + + if(!REGION_IS_ACTIVE(reg)){ + D(fprintf(stderr, "lost focus (inact) %s [%p:]\n", OBJ_TYPESTR(reg), reg);) + return; + } + + par=REGION_PARENT_REG(reg); + if(par!=NULL && par->active_sub==reg){ + par->active_sub=NULL; + region_update_owned_grabs(par); + } + + +#if 0 + if(ioncore_g.focus_current==reg){ + /* Find the closest active parent, or if none is found, stop at the + * screen and mark it "currently focused". + */ + while(par!=NULL && !REGION_IS_ACTIVE(par) && !OBJ_IS(par, WScreen)) + par=REGION_PARENT_REG(par); + ioncore_g.focus_current=par; + } +#endif + + D(fprintf(stderr, "lost focus (act) %s [%p:]\n", OBJ_TYPESTR(reg), reg);) + + reg->flags&=~REGION_ACTIVE; + region_inactivated(reg); + r=REGION_MANAGER(reg); + if(r!=NULL) + region_managed_inactivated(r, reg); + + extl_protect(NULL); + hook_call_o(region_inactivated_hook, (Obj*)reg); + extl_unprotect(NULL); +} + + +/*}}}*/ + + +/*{{{ Focus status requests */ + + +/*EXTL_DOC + * Is \var{reg} active/does it or one of it's children of focus? + */ +EXTL_SAFE +EXTL_EXPORT_MEMBER +bool region_is_active(WRegion *reg) +{ + return REGION_IS_ACTIVE(reg); +} + + +bool region_may_control_focus(WRegion *reg) +{ + WRegion *par, *r2; + + if(OBJ_IS_BEING_DESTROYED(reg)) + return FALSE; + + if(REGION_IS_ACTIVE(reg)) + return TRUE; + + if(region_is_await(reg)) + return TRUE; + + par=REGION_PARENT_REG(reg); + + if(par==NULL || !REGION_IS_ACTIVE(par)) + return FALSE; + + r2=par->active_sub; + while(r2!=NULL && r2!=par){ + if(r2==reg) + return TRUE; + r2=REGION_MANAGER(r2); + } + + return FALSE; +} + + +/*}}}*/ + + +/*{{{ set_focus, warp */ + + +/*Time ioncore_focus_time=CurrentTime;*/ + + +void region_finalise_focusing(WRegion* reg, Window win, bool warp) +{ + if(warp) + region_do_warp(reg); + + region_set_await_focus(reg); + /*xwindow_do_set_focus(win);*/ + XSetInputFocus(ioncore_g.dpy, win, RevertToParent, + CurrentTime/*ioncore_focus_time*/); + /*ioncore_focus_time=CurrentTime;*/ +} + + + +static WRegion *find_warp_to_reg(WRegion *reg) +{ + if(reg==NULL) + return NULL; + if(reg->flags®ION_PLEASE_WARP) + return reg; + return find_warp_to_reg(region_manager_or_parent(reg)); +} + + +bool region_do_warp_default(WRegion *reg) +{ + int x, y, w, h, px=0, py=0; + Window root; + + reg=find_warp_to_reg(reg); + + if(reg==NULL) + return FALSE; + + D(fprintf(stderr, "region_do_warp %p %s\n", reg, OBJ_TYPESTR(reg))); + + root=region_root_of(reg); + + region_rootpos(reg, &x, &y); + w=REGION_GEOM(reg).w; + h=REGION_GEOM(reg).h; + + if(xwindow_pointer_pos(root, &px, &py)){ + if(px>=x && py>=y && pxflags®ION_SKIP_FOCUS) + return TRUE; + reg=REGION_PARENT_REG(reg); + } + return FALSE; +} + +/*EXTL_DOC + * Returns the currently focused region, if any. + */ +EXTL_EXPORT +WRegion *ioncore_current() +{ + return ioncore_g.focus_current; +} + +/*}}}*/ diff --git a/ioncore/focus.h b/ioncore/focus.h new file mode 100644 index 0000000..e995d6d --- /dev/null +++ b/ioncore/focus.h @@ -0,0 +1,64 @@ +/* + * ion/ioncore/focus.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_FOCUS_H +#define ION_IONCORE_FOCUS_H + +#include +#include "common.h" +#include "window.h" +#include "region.h" + +DYNFUN void region_do_set_focus(WRegion *reg, bool warp); + +/* Delayed (until return to main loop) warp/focus */ +extern void region_warp(WRegion *reg); +extern void region_set_focus(WRegion *reg); +extern void region_maybewarp(WRegion *reg, bool warp); + +/* Immediate warp/focus */ +extern void region_do_warp(WRegion *reg); +extern bool region_do_warp_default(WRegion *reg); + +extern void region_finalise_focusing(WRegion* reg, Window win, bool warp); + +/* Awaiting focus state */ +extern void region_set_await_focus(WRegion *reg); +extern bool ioncore_await_focus(); + +/* Event handling */ +extern void region_got_focus(WRegion *reg); +extern void region_lost_focus(WRegion *reg); + +/* May reg transfer focus to its children? */ +extern bool region_may_control_focus(WRegion *reg); + +/* Does reg have focus? */ +extern bool region_is_active(WRegion *reg); + +/* Focus history */ +extern void region_focuslist_remove(WRegion *reg); +extern void region_focuslist_push(WRegion *reg); +extern void region_focuslist_move_after(WRegion *reg, WRegion *after); +extern void region_focuslist_deinit(WRegion *reg); + +extern WRegion *ioncore_goto_previous(); + +/* Handlers to these shook should take WRegion* as parameter. */ +extern WHook *region_do_warp_alt; +extern WHook *region_activated_hook; +extern WHook *region_inactivated_hook; + +/* Misc. */ +extern bool region_skip_focus(WRegion *reg); +WRegion *ioncore_current(); + +#endif /* ION_IONCORE_FOCUS_H */ diff --git a/ioncore/frame-draw.c b/ioncore/frame-draw.c new file mode 100644 index 0000000..116f10c --- /dev/null +++ b/ioncore/frame-draw.c @@ -0,0 +1,481 @@ +/* + * ion/ioncore/frame-draw.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include + +#include +#include +#include + +#include "common.h" +#include "frame.h" +#include "framep.h" +#include "frame-draw.h" +#include "strings.h" +#include "names.h" +#include "gr.h" + + +#define BAR_INSIDE_BORDER(FRAME) \ + ((FRAME)->barmode==FRAME_BAR_INSIDE || (FRAME)->barmode==FRAME_BAR_NONE) +#define BAR_EXISTS(FRAME) ((FRAME)->barmode!=FRAME_BAR_NONE) +#define BAR_H(FRAME) (FRAME)->bar_h + + +/*{{{ (WFrame) dynfun default implementations */ + + +static uint get_spacing(const WFrame *frame) +{ + GrBorderWidths bdw; + + if(frame->brush==NULL) + return 0; + + grbrush_get_border_widths(frame->brush, &bdw); + + return bdw.spacing; +} + + +void frame_border_geom(const WFrame *frame, WRectangle *geom) +{ + geom->x=0; + geom->y=0; + geom->w=REGION_GEOM(frame).w; + geom->h=REGION_GEOM(frame).h; + + if(!BAR_INSIDE_BORDER(frame) && frame->brush!=NULL){ + geom->y+=frame->bar_h; + geom->h=maxof(0, geom->h-frame->bar_h); + } +} + + +void frame_border_inner_geom(const WFrame *frame, WRectangle *geom) +{ + GrBorderWidths bdw; + + frame_border_geom(frame, geom); + + if(frame->brush!=NULL){ + grbrush_get_border_widths(frame->brush, &bdw); + + geom->x+=bdw.left; + geom->y+=bdw.top; + geom->w=maxof(0, geom->w-(bdw.left+bdw.right)); + geom->h=maxof(0, geom->h-(bdw.top+bdw.bottom)); + } +} + + +void frame_bar_geom(const WFrame *frame, WRectangle *geom) +{ + uint off; + + if(BAR_INSIDE_BORDER(frame)){ + off=get_spacing(frame); + frame_border_inner_geom(frame, geom); + }else{ + off=0; + geom->x=0; + geom->y=0; + geom->w=(frame->barmode==FRAME_BAR_SHAPED + ? frame->bar_w + : REGION_GEOM(frame).w); + } + geom->x+=off; + geom->y+=off; + geom->w=maxof(0, geom->w-2*off); + geom->h=BAR_H(frame); +} + + +void frame_managed_geom(const WFrame *frame, WRectangle *geom) +{ + uint spacing=get_spacing(frame); + + frame_border_inner_geom(frame, geom); + + geom->x+=spacing; + geom->y+=spacing; + geom->w-=2*spacing; + geom->h-=2*spacing; + + if(BAR_INSIDE_BORDER(frame) && BAR_EXISTS(frame)){ + geom->y+=frame->bar_h+spacing; + geom->h-=frame->bar_h+spacing; + } + + geom->w=maxof(geom->w, 0); + geom->h=maxof(geom->h, 0); +} + + +void frame_set_shape(WFrame *frame) +{ + WRectangle gs[2]; + int n=0; + + if(frame->brush!=NULL){ + if(BAR_EXISTS(frame)){ + frame_bar_geom(frame, gs+n); + n++; + } + frame_border_geom(frame, gs+n); + n++; + + grbrush_set_window_shape(frame->brush, TRUE, n, gs); + } +} + + +void frame_clear_shape(WFrame *frame) +{ + grbrush_set_window_shape(frame->brush, TRUE, 0, NULL); +} + + +#define CF_TAB_MAX_TEXT_X_OFF 10 + + +static void frame_shaped_recalc_bar_size(WFrame *frame) +{ + int bar_w=0, textw=0, tmaxw=frame->tab_min_w, tmp=0; + WLListIterTmp itmp; + WRegion *sub; + const char *p; + GrBorderWidths bdw; + char *title; + uint bdtotal; + int i, m; + + if(frame->bar_brush==NULL) + return; + + m=FRAME_MCOUNT(frame); + + if(m>0){ + grbrush_get_border_widths(frame->bar_brush, &bdw); + bdtotal=((m-1)*(bdw.tb_ileft+bdw.tb_iright) + +bdw.right+bdw.left); + + FRAME_MX_FOR_ALL(sub, frame, itmp){ + p=region_displayname(sub); + if(p==NULL) + continue; + + textw=grbrush_get_text_width(frame->bar_brush, + p, strlen(p)); + if(textw>tmaxw) + tmaxw=textw; + } + + bar_w=frame->bar_max_width_q*REGION_GEOM(frame).w; + if(bar_wtab_min_w && + REGION_GEOM(frame).w>frame->tab_min_w) + bar_w=frame->tab_min_w; + + tmp=bar_w-bdtotal-m*tmaxw; + + if(tmp>0){ + /* No label truncation needed, good. See how much can be padded. */ + tmp/=m*2; + if(tmp>CF_TAB_MAX_TEXT_X_OFF) + tmp=CF_TAB_MAX_TEXT_X_OFF; + bar_w=(tmaxw+tmp*2)*m+bdtotal; + }else{ + /* Some labels must be truncated */ + } + }else{ + bar_w=frame->tab_min_w; + if(bar_w>frame->bar_max_width_q*REGION_GEOM(frame).w) + bar_w=frame->bar_max_width_q*REGION_GEOM(frame).w; + } + + if(frame->bar_w!=bar_w){ + frame->bar_w=bar_w; + frame_set_shape(frame); + } +} + + +static int init_title(WFrame *frame, int i) +{ + int textw; + + if(frame->titles[i].text!=NULL){ + free(frame->titles[i].text); + frame->titles[i].text=NULL; + } + + textw=frame_nth_tab_iw((WFrame*)frame, i); + frame->titles[i].iw=textw; + return textw; +} + + +void frame_recalc_bar(WFrame *frame) +{ + int textw, i; + WLListIterTmp tmp; + WRegion *sub; + char *title; + + if(frame->bar_brush==NULL || frame->titles==NULL) + return; + + if(frame->barmode==FRAME_BAR_SHAPED) + frame_shaped_recalc_bar_size(frame); + + i=0; + + if(FRAME_MCOUNT(frame)==0){ + textw=init_title(frame, i); + if(textw>0){ + title=grbrush_make_label(frame->bar_brush, CF_STR_EMPTY, textw); + frame->titles[i].text=title; + } + return; + } + + FRAME_MX_FOR_ALL(sub, frame, tmp){ + textw=init_title(frame, i); + if(textw>0){ + if(frame->flags&FRAME_SHOW_NUMBERS){ + char *s=NULL; + libtu_asprintf(&s, "[%d]", i+1); + if(s!=NULL){ + title=grbrush_make_label(frame->bar_brush, s, textw); + free(s); + }else{ + title=NULL; + } + }else{ + title=region_make_label(sub, textw, frame->bar_brush); + } + frame->titles[i].text=title; + } + i++; + } +} + + +void frame_draw_bar(const WFrame *frame, bool complete) +{ + WRectangle geom; + const char *cattr=(REGION_IS_ACTIVE(frame) + ? "active" : "inactive"); + + if(frame->bar_brush==NULL + || !BAR_EXISTS(frame) + || frame->titles==NULL){ + return; + } + + frame_bar_geom(frame, &geom); + + grbrush_begin(frame->bar_brush, &geom, GRBRUSH_AMEND); + + grbrush_draw_textboxes(frame->bar_brush, &geom, frame->titles_n, + frame->titles, complete, cattr); + + grbrush_end(frame->bar_brush); +} + + +void frame_draw(const WFrame *frame, bool complete) +{ + WRectangle geom; + const char *attr=(REGION_IS_ACTIVE(frame) + ? "active" : "inactive"); + + if(frame->brush==NULL) + return; + + frame_border_geom(frame, &geom); + + grbrush_begin(frame->brush, &geom, (complete ? 0 : GRBRUSH_NO_CLEAR_OK)); + + grbrush_draw_border(frame->brush, &geom, attr); + frame_draw_bar(frame, TRUE); + + grbrush_end(frame->brush); +} + + +void frame_brushes_updated(WFrame *frame) +{ + WFrameBarMode barmode=FRAME_BAR_INSIDE; + ExtlTab tab; + char *s; + + if(frame->brush==NULL) + return; + + if(frame->mode==FRAME_MODE_FLOATING) + barmode=FRAME_BAR_SHAPED; + else if(frame->mode==FRAME_MODE_TRANSIENT) + barmode=FRAME_BAR_NONE; + else if(frame->mode==FRAME_MODE_TILED_ALT) + barmode=FRAME_BAR_NONE; + + if(grbrush_get_extra(frame->brush, "bar", 's', &s)){ + if(strcmp(s, "inside")==0) + barmode=FRAME_BAR_INSIDE; + else if(strcmp(s, "outside")==0) + barmode=FRAME_BAR_OUTSIDE; + else if(strcmp(s, "shaped")==0) + barmode=FRAME_BAR_SHAPED; + else if(strcmp(s, "none")==0) + barmode=FRAME_BAR_NONE; + free(s); + } + + frame->barmode=barmode; + + if(barmode==FRAME_BAR_NONE || frame->bar_brush==NULL){ + frame->bar_h=0; + }else{ + GrBorderWidths bdw; + GrFontExtents fnte; + + grbrush_get_border_widths(frame->bar_brush, &bdw); + grbrush_get_font_extents(frame->bar_brush, &fnte); + + frame->bar_h=bdw.top+bdw.bottom+fnte.max_height; + } + + /* shaped mode stuff */ + frame->tab_min_w=100; + frame->bar_max_width_q=0.95; + + if(grbrush_get_extra(frame->brush, "floatframe_tab_min_w", + 'i', &(frame->tab_min_w))){ + if(frame->tab_min_w<=0) + frame->tab_min_w=1; + } + + if(grbrush_get_extra(frame->brush, "floatframe_bar_max_w_q", + 'd', &(frame->bar_max_width_q))){ + if(frame->bar_max_width_q<=0.0 || frame->bar_max_width_q>1.0) + frame->bar_max_width_q=1.0; + } +} + + +/*}}}*/ + + +/*{{{ Misc. */ + + +void frame_updategr(WFrame *frame) +{ + frame_release_brushes(frame); + + frame_initialise_gr(frame); + + /* Update children */ + region_updategr_default((WRegion*)frame); + + mplex_fit_managed(&frame->mplex); + frame_recalc_bar(frame); + frame_set_background(frame, TRUE); +} + + +StringIntMap frame_tab_styles[]={ + {"tab-frame-tiled", FRAME_MODE_TILED}, + {"tab-frame-tiled-alt", FRAME_MODE_TILED_ALT}, + {"tab-frame-floating", FRAME_MODE_FLOATING}, + {"tab-frame-transient", FRAME_MODE_TRANSIENT}, + END_STRINGINTMAP +}; + + +const char *framemode_get_tab_style(WFrameMode mode) +{ + return stringintmap_key(frame_tab_styles, mode, "tab-frame"); +} + + +const char *framemode_get_style(WFrameMode mode) +{ + const char *p=framemode_get_tab_style(mode); + assert(p!=NULL); + return (p+4); +} + + +void frame_initialise_gr(WFrame *frame) +{ + Window win=frame->mplex.win.win; + WRootWin *rw=region_rootwin_of((WRegion*)frame); + const char *style=framemode_get_style(frame->mode); + const char *tab_style=framemode_get_tab_style(frame->mode); + + frame->brush=gr_get_brush(win, rw, style); + + if(frame->brush==NULL) + return; + + frame->bar_brush=grbrush_get_slave(frame->brush, rw, tab_style); + + if(frame->bar_brush==NULL) + return; + + frame_brushes_updated(frame); +} + + +void frame_release_brushes(WFrame *frame) +{ + if(frame->bar_brush!=NULL){ + grbrush_release(frame->bar_brush); + frame->bar_brush=NULL; + } + + if(frame->brush!=NULL){ + grbrush_release(frame->brush); + frame->brush=NULL; + } +} + + +bool frame_set_background(WFrame *frame, bool set_always) +{ + GrTransparency mode=GR_TRANSPARENCY_DEFAULT; + + if(FRAME_CURRENT(frame)!=NULL){ + if(OBJ_IS(FRAME_CURRENT(frame), WClientWin)){ + WClientWin *cwin=(WClientWin*)FRAME_CURRENT(frame); + mode=(cwin->flags&CLIENTWIN_PROP_TRANSPARENT + ? GR_TRANSPARENCY_YES : GR_TRANSPARENCY_NO); + }else if(!OBJ_IS(FRAME_CURRENT(frame), WGroup)){ + mode=GR_TRANSPARENCY_NO; + } + } + + if(mode!=frame->tr_mode || set_always){ + frame->tr_mode=mode; + if(frame->brush!=NULL){ + grbrush_enable_transparency(frame->brush, mode); + window_draw((WWindow*)frame, TRUE); + } + return TRUE; + } + + return FALSE; +} + + +/*}}}*/ diff --git a/ioncore/frame-draw.h b/ioncore/frame-draw.h new file mode 100644 index 0000000..4086dec --- /dev/null +++ b/ioncore/frame-draw.h @@ -0,0 +1,38 @@ +/* + * ion/ioncore/frame-draw.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_FRAME_DRAW_H +#define ION_IONCORE_FRAME_DRAW_H + +#include "frame.h" +#include "rectangle.h" + +extern void frame_draw(const WFrame *frame, bool complete); +extern void frame_draw_bar(const WFrame *frame, bool complete); +extern void frame_recalc_bar(WFrame *frame); +extern void frame_bar_geom(const WFrame *frame, WRectangle *geom); +extern void frame_border_geom(const WFrame *frame, WRectangle *geom); +extern void frame_border_inner_geom(const WFrame *frame, WRectangle *geom); +extern void frame_brushes_updated(WFrame *frame); +extern void frame_managed_geom(const WFrame *frame, WRectangle *geom); + +extern void frame_initialise_gr(WFrame *frame); +extern void frame_release_brushes(WFrame *frame); +extern bool frame_set_background(WFrame *frame, bool set_always); +extern void frame_updategr(WFrame *frame); + +extern void frame_set_shape(WFrame *frame); +extern void frame_clear_shape(WFrame *frame); + +extern const char *framemode_get_style(WFrameMode mode); +extern const char *framemode_get_tab_style(WFrameMode mode); + +#endif /* ION_IONCORE_FRAME_DRAW_H */ diff --git a/ioncore/frame-pointer.c b/ioncore/frame-pointer.c new file mode 100644 index 0000000..1d9a196 --- /dev/null +++ b/ioncore/frame-pointer.c @@ -0,0 +1,388 @@ +/* + * ion/ioncore/frame-pointer.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include + +#include + +#include "common.h" +#include "global.h" +#include "pointer.h" +#include "cursor.h" +#include "focus.h" +#include "attach.h" +#include "resize.h" +#include "grab.h" +#include "frame.h" +#include "framep.h" +#include "frame-pointer.h" +#include "frame-draw.h" +#include "bindmaps.h" +#include "infowin.h" +#include "rectangle.h" +#include "xwindow.h" +#include "names.h" +#include "presize.h" +#include "llist.h" + + +static int p_tab_x=0, p_tab_y=0, p_tabnum=-1; +static WInfoWin *tabdrag_infowin=NULL; + + +/*{{{ Frame press */ + + +static WRegion *sub_at_tab(WFrame *frame) +{ + return mplex_mx_nth((WMPlex*)frame, p_tabnum); +} + + +int frame_press(WFrame *frame, XButtonEvent *ev, WRegion **reg_ret) +{ + WRegion *sub=NULL; + WRectangle g; + + p_tabnum=-1; + + window_p_resize_prepare((WWindow*)frame, ev); + + /* Check tab */ + + frame_bar_geom(frame, &g); + + /* Borders act like tabs at top of the parent region */ + if(REGION_GEOM(frame).y==0){ + g.h+=g.y; + g.y=0; + } + + if(rectangle_contains(&g, ev->x, ev->y)){ + p_tabnum=frame_tab_at_x(frame, ev->x); + + region_rootpos((WRegion*)frame, &p_tab_x, &p_tab_y); + p_tab_x+=frame_nth_tab_x(frame, p_tabnum); + p_tab_y+=g.y; + + sub=mplex_mx_nth(&(frame->mplex), p_tabnum); + + if(reg_ret!=NULL) + *reg_ret=sub; + + return FRAME_AREA_TAB; + }else{ + WLListIterTmp tmp; + FRAME_MX_FOR_ALL(sub, frame, tmp){ + p_tabnum++; + if(sub==FRAME_CURRENT(frame)) + break; + } + + if(sub!=NULL){ + p_tab_x=ev->x_root-frame_nth_tab_w(frame, p_tabnum)/2; + p_tab_y=ev->y_root-frame->bar_h/2; + }else{ + p_tabnum=-1; + } + } + + + /* Check border */ + + frame_border_inner_geom(frame, &g); + + if(rectangle_contains(&g, ev->x, ev->y)) + return FRAME_AREA_CLIENT; + + return FRAME_AREA_BORDER; +} + + +/*}}}*/ + + +/*{{{ Tab drag */ + + +static ExtlExportedFn *tabdrag_safe_fns[]={ + (ExtlExportedFn*)&mplex_switch_nth, + (ExtlExportedFn*)&mplex_switch_next, + (ExtlExportedFn*)&mplex_switch_prev, + NULL +}; + +static ExtlSafelist tabdrag_safelist=EXTL_SAFELIST_INIT(tabdrag_safe_fns); + + +#define BUTTONS_MASK \ + (Button1Mask|Button2Mask|Button3Mask|Button4Mask|Button5Mask) + + +static bool tabdrag_kbd_handler(WRegion *reg, XEvent *xev) +{ + XKeyEvent *ev=&xev->xkey; + WBinding *binding=NULL; + WBindmap **bindptr; + + if(ev->type==KeyRelease) + return FALSE; + + assert(reg!=NULL); + + binding=bindmap_lookup_binding(ioncore_rootwin_bindmap, BINDING_KEYPRESS, + ev->state&~BUTTONS_MASK, ev->keycode); + + if(binding!=NULL && binding->func!=extl_fn_none()){ + extl_protect(&tabdrag_safelist); + extl_call(binding->func, "o", NULL, region_screen_of(reg)); + extl_unprotect(&tabdrag_safelist); + } + + return FALSE; +} + + +static void setup_dragwin(WFrame *frame, uint tab) +{ + WRectangle g; + WRootWin *rw; + WFitParams fp; + const char *tab_style=framemode_get_tab_style(frame->mode); + + assert(tabdrag_infowin==NULL); + + rw=region_rootwin_of((WRegion*)frame); + + fp.mode=REGION_FIT_EXACT; + fp.g.x=p_tab_x; + fp.g.y=p_tab_y; + fp.g.w=frame_nth_tab_w(frame, tab); + fp.g.h=frame->bar_h; + + tabdrag_infowin=create_infowin((WWindow*)rw, &fp, tab_style); + + if(tabdrag_infowin==NULL) + return; + + infowin_set_attr2(tabdrag_infowin, (REGION_IS_ACTIVE(frame) + ? "active" : "inactive"), + frame->titles[tab].attr); + + if(frame->titles[tab].text!=NULL){ + char *buf=INFOWIN_BUFFER(tabdrag_infowin); + strncpy(buf, frame->titles[tab].text, INFOWIN_BUFFER_LEN-1); + buf[INFOWIN_BUFFER_LEN-1]='\0'; + } +} + + +static void p_tabdrag_motion(WFrame *frame, XMotionEvent *ev, + int dx, int dy) +{ + WRootWin *rootwin=region_rootwin_of((WRegion*)frame); + + p_tab_x+=dx; + p_tab_y+=dy; + + if(tabdrag_infowin!=NULL){ + WRectangle g; + g.x=p_tab_x; + g.y=p_tab_y; + g.w=REGION_GEOM(tabdrag_infowin).w; + g.h=REGION_GEOM(tabdrag_infowin).h; + region_fit((WRegion*)tabdrag_infowin, &g, REGION_FIT_EXACT); + } +} + + +static void p_tabdrag_begin(WFrame *frame, XMotionEvent *ev, + int dx, int dy) +{ + WRootWin *rootwin=region_rootwin_of((WRegion*)frame); + + if(p_tabnum<0) + return; + + ioncore_change_grab_cursor(IONCORE_CURSOR_DRAG); + + setup_dragwin(frame, p_tabnum); + + frame->tab_dragged_idx=p_tabnum; + frame_update_attr_nth(frame, p_tabnum); + + frame_draw_bar(frame, FALSE); + + p_tabdrag_motion(frame, ev, dx, dy); + + if(tabdrag_infowin!=NULL) + window_map((WWindow*)tabdrag_infowin); +} + + +static WRegion *fnd(Window root, int x, int y) +{ + Window win=root; + int dstx, dsty; + WRegion *reg=NULL; + WWindow *w=NULL; + WScreen *scr; + + FOR_ALL_SCREENS(scr){ + if(region_root_of((WRegion*)scr)==root && + rectangle_contains(®ION_GEOM(scr), x, y)){ + break; + } + } + + w=(WWindow*)scr; + + while(w!=NULL){ + if(HAS_DYN(w, region_handle_drop)) + reg=(WRegion*)w; + + if(!XTranslateCoordinates(ioncore_g.dpy, root, w->win, + x, y, &dstx, &dsty, &win)){ + break; + } + + w=XWINDOW_REGION_OF_T(win, WWindow); + /*x=dstx; + y=dsty;*/ + } + + return reg; +} + + +static bool drop_ok(WRegion *mgr, WRegion *reg) +{ + WRegion *reg2=mgr; + for(reg2=mgr; reg2!=NULL; reg2=region_manager(reg2)){ + if(reg2==reg) + goto err; + } + + for(reg2=REGION_PARENT_REG(mgr); reg2!=NULL; reg2=REGION_PARENT_REG(reg2)){ + if(reg2==reg) + goto err; + } + + return TRUE; + +err: + warn(TR("Attempt to make region %s manage its ancestor %s."), + region_name(mgr), region_name(reg)); + return FALSE; +} + + +static void tabdrag_deinit(WFrame *frame) +{ + int idx=frame->tab_dragged_idx; + frame->tab_dragged_idx=-1; + frame_update_attr_nth(frame, idx); + + if(tabdrag_infowin!=NULL){ + destroy_obj((Obj*)tabdrag_infowin); + tabdrag_infowin=NULL; + } +} + + +static void tabdrag_killed(WFrame *frame) +{ + tabdrag_deinit(frame); + if(!OBJ_IS_BEING_DESTROYED(frame)) + frame_draw_bar(frame, TRUE); +} + + +static void p_tabdrag_end(WFrame *frame, XButtonEvent *ev) +{ + WRegion *sub=NULL; + WRegion *dropped_on; + Window win=None; + + sub=sub_at_tab(frame); + + tabdrag_deinit(frame); + + /* Must be same root window */ + if(sub==NULL || ev->root!=region_root_of(sub)) + return; + + dropped_on=fnd(ev->root, ev->x_root, ev->y_root); + + if(dropped_on==NULL || dropped_on==(WRegion*)frame || + dropped_on==sub || !drop_ok(dropped_on, sub)){ + frame_draw_bar(frame, TRUE); + return; + } + + if(region_handle_drop(dropped_on, p_tab_x, p_tab_y, sub)) + region_goto(dropped_on); + else + frame_draw_bar(frame, TRUE); +} + + +/*EXTL_DOC + * Start dragging the tab that the user pressed on with the pointing device. + * This function should only be used by binding it to \emph{mpress} or + * \emph{mdrag} action with area ''tab''. + */ +EXTL_EXPORT_MEMBER +void frame_p_tabdrag(WFrame *frame) +{ + if(p_tabnum<0) + return; + + ioncore_set_drag_handlers((WRegion*)frame, + (WMotionHandler*)p_tabdrag_begin, + (WMotionHandler*)p_tabdrag_motion, + (WButtonHandler*)p_tabdrag_end, + tabdrag_kbd_handler, + (GrabKilledHandler*)tabdrag_killed); +} + + +/*}}}*/ + + +/*{{{ switch_tab */ + + +/*EXTL_DOC + * Display the region corresponding to the tab that the user pressed on. + * This function should only be used by binding it to a mouse action. + */ +EXTL_EXPORT_MEMBER +void frame_p_switch_tab(WFrame *frame) +{ + WRegion *sub; + + if(ioncore_pointer_grab_region()!=(WRegion*)frame) + return; + + sub=sub_at_tab(frame); + + if(sub!=NULL){ + bool mcf=region_may_control_focus((WRegion*)frame); + region_goto_flags(sub, (mcf + ? REGION_GOTO_FOCUS|REGION_GOTO_NOWARP + : 0)); + } +} + + +/*}}}*/ + diff --git a/ioncore/frame-pointer.h b/ioncore/frame-pointer.h new file mode 100644 index 0000000..092b90a --- /dev/null +++ b/ioncore/frame-pointer.h @@ -0,0 +1,28 @@ +/* + * ion/ioncore/frame-pointer.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_FRAME_POINTER_H +#define ION_IONCORE_FRAME_POINTER_H + +#include "common.h" +#include "pointer.h" +#include "frame.h" + +extern void frame_p_resize(WFrame *frame); +extern void frame_p_tabdrag(WFrame *frame); +extern void frame_p_move(WFrame *frame); +extern void frame_p_switch_tab(WFrame *frame); + +extern int frame_press(WFrame *frame, XButtonEvent *ev, + WRegion **reg_ret); +extern void frame_release(WFrame *frame); + +#endif /* ION_IONCORE_FRAME_POINTER_H */ diff --git a/ioncore/frame.c b/ioncore/frame.c new file mode 100644 index 0000000..0f3e8f2 --- /dev/null +++ b/ioncore/frame.c @@ -0,0 +1,927 @@ +/* + * ion/ioncore/frame.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include + +#include +#include +#include +#include +#include + +#include "common.h" +#include "window.h" +#include "global.h" +#include "rootwin.h" +#include "focus.h" +#include "event.h" +#include "attach.h" +#include "resize.h" +#include "tags.h" +#include "names.h" +#include "saveload.h" +#include "framep.h" +#include "frame-pointer.h" +#include "frame-draw.h" +#include "sizehint.h" +#include "extlconv.h" +#include "mplex.h" +#include "bindmaps.h" +#include "regbind.h" +#include "gr.h" +#include "activity.h" +#include "llist.h" + + +extern bool frame_set_background(WFrame *frame, bool set_always); +extern void frame_initialise_gr(WFrame *frame); + +static bool frame_initialise_titles(WFrame *frame); +static void frame_free_titles(WFrame *frame); + +static void frame_add_mode_bindmaps(WFrame *frame); + + +WHook *frame_managed_changed_hook=NULL; + +#define IS_FLOATING_MODE(FRAME) \ + ((FRAME)->mode==FRAME_MODE_FLOATING || (FRAME)->mode==FRAME_MODE_TRANSIENT) +#define FORWARD_CWIN_RQGEOM(FRAME) IS_FLOATING_MODE(FRAME) +#define USE_MINMAX(FRAME) IS_FLOATING_MODE(FRAME) +#define DEST_EMPTY(FRAME) IS_FLOATING_MODE(FRAME) + + +/*{{{ Destroy/create frame */ + + +bool frame_init(WFrame *frame, WWindow *parent, const WFitParams *fp, + WFrameMode mode) +{ + WRectangle mg; + + frame->flags=0; + frame->saved_w=0; + frame->saved_h=0; + frame->saved_x=0; + frame->saved_y=0; + frame->tab_dragged_idx=-1; + frame->titles=NULL; + frame->titles_n=0; + frame->bar_h=0; + frame->bar_w=fp->g.w; + frame->tr_mode=GR_TRANSPARENCY_DEFAULT; + frame->brush=NULL; + frame->bar_brush=NULL; + frame->mode=mode; + frame->tab_min_w=0; + frame->bar_max_width_q=1.0; + + if(!mplex_init((WMPlex*)frame, parent, fp)) + return FALSE; + + frame_initialise_gr(frame); + frame_initialise_titles(frame); + + region_add_bindmap((WRegion*)frame, ioncore_frame_bindmap); + region_add_bindmap((WRegion*)frame, ioncore_frame_toplevel_bindmap); + + frame_add_mode_bindmaps(frame); + + mplex_managed_geom((WMPlex*)frame, &mg); + + if(mg.h<=1) + frame->flags|=FRAME_SHADED; + + ((WRegion*)frame)->flags|=REGION_PLEASE_WARP; + + return TRUE; +} + + +WFrame *create_frame(WWindow *parent, const WFitParams *fp, WFrameMode mode) +{ + CREATEOBJ_IMPL(WFrame, frame, (p, parent, fp, mode)); +} + + +void frame_deinit(WFrame *frame) +{ + frame_free_titles(frame); + frame_release_brushes(frame); + mplex_deinit((WMPlex*)frame); +} + + +/*}}}*/ + + +/*{{{ Mode switching */ + + +static void frame_add_mode_bindmaps(WFrame *frame) +{ + WFrameMode mode=frame->mode; + + if(mode==FRAME_MODE_TILED || mode==FRAME_MODE_TILED_ALT){ + region_add_bindmap((WRegion*)frame, ioncore_frame_toplevel_bindmap); + region_add_bindmap((WRegion*)frame, ioncore_frame_tiled_bindmap); + }else if(mode==FRAME_MODE_FLOATING){ + region_add_bindmap((WRegion*)frame, ioncore_frame_toplevel_bindmap); + region_add_bindmap((WRegion*)frame, ioncore_frame_floating_bindmap); + }else if(mode==FRAME_MODE_TRANSIENT){ + region_add_bindmap((WRegion*)frame, ioncore_frame_transient_bindmap); + } +} + + +void frame_set_mode(WFrame *frame, WFrameMode mode) +{ + if(frame->mode==mode) + return; + + frame_clear_shape(frame); + + frame_release_brushes(frame); + + region_remove_bindmap((WRegion*)frame, ioncore_frame_toplevel_bindmap); + region_remove_bindmap((WRegion*)frame, ioncore_frame_tiled_bindmap); + region_remove_bindmap((WRegion*)frame, ioncore_frame_floating_bindmap); + region_remove_bindmap((WRegion*)frame, ioncore_frame_transient_bindmap); + + frame->mode=mode; + + frame_add_mode_bindmaps(frame); + + frame_initialise_gr(frame); + + mplex_fit_managed(&frame->mplex); + frame_recalc_bar(frame); + frame_set_background(frame, TRUE); +} + + +WFrameMode frame_mode(WFrame *frame) +{ + return frame->mode; +} + + +StringIntMap frame_modes[]={ + {"tiled", FRAME_MODE_TILED}, + {"tiled-alt", FRAME_MODE_TILED_ALT}, + {"floating", FRAME_MODE_FLOATING}, + {"transient", FRAME_MODE_TRANSIENT}, + END_STRINGINTMAP +}; + + +/*EXTL_DOC + * Get frame mode. + */ +EXTL_EXPORT_AS(WFrame, mode) +const char *frame_mode_extl(WFrame *frame) +{ + return stringintmap_key(frame_modes, frame->mode, NULL); +} + + +/*EXTL_DOC + * Set frame mode. + */ +EXTL_EXPORT_AS(WFrame, set_mode) +bool frame_set_mode_extl(WFrame *frame, const char *modestr) +{ + WFrameMode mode; + int idx; + + idx=stringintmap_ndx(frame_modes, modestr); + if(idx<0) + return FALSE; + + frame_set_mode(frame, frame_modes[idx].value); + + return TRUE; +} + + +/*}}}*/ + + +/*{{{ Tabs */ + + +int frame_tab_at_x(WFrame *frame, int x) +{ + WRectangle bg; + int tab, tx; + + frame_bar_geom(frame, &bg); + + if(x>=bg.x+bg.w || xbar_brush!=NULL) + grbrush_get_border_widths(frame->bar_brush, &bdw); + + /* Remove borders */ + w=bg.w-bdw.left-bdw.right-(bdw.tb_ileft+bdw.tb_iright+bdw.spacing)*(m-1); + + if(w<=0) + return 0; + + /* Get n:th tab's portion of free area */ + w=(((n+1)*w)/m-(n*w)/m); + + /* Add n:th tab's borders back */ + if(!inner){ + w+=(n==0 ? bdw.left : bdw.tb_ileft); + w+=(n==m-1 ? bdw.right : bdw.tb_iright+bdw.spacing); + } + + return w; +} + + +int frame_nth_tab_w(WFrame *frame, int n) +{ + return frame_nth_tab_w_iw(frame, n, FALSE); +} + + +int frame_nth_tab_iw(WFrame *frame, int n) +{ + return frame_nth_tab_w_iw(frame, n, TRUE); +} + + + +static void update_attr(WFrame *frame, int i, WRegion *reg) +{ + int flags=0; + static char *attrs[]={ + "unselected-not_tagged-not_dragged-no_activity", + "selected-not_tagged-not_dragged-no_activity", + "unselected-tagged-not_dragged-no_activity", + "selected-tagged-not_dragged-no_activity", + "unselected-not_tagged-dragged-no_activity", + "selected-not_tagged-dragged-no_activity", + "unselected-tagged-dragged-no_activity", + "selected-tagged-dragged-no_activity", + "unselected-not_tagged-not_dragged-activity", + "selected-not_tagged-not_dragged-activity", + "unselected-tagged-not_dragged-activity", + "selected-tagged-not_dragged-activity", + "unselected-not_tagged-dragged-activity", + "selected-not_tagged-dragged-activity", + "unselected-tagged-dragged-activity", + "selected-tagged-dragged-activity" + }; + + if(i>=frame->titles_n){ + /* Might happen when deinitialising */ + return; + } + + if(reg==FRAME_CURRENT(frame)) + flags|=0x01; + if(reg!=NULL && reg->flags®ION_TAGGED) + flags|=0x02; + if(i==frame->tab_dragged_idx) + flags|=0x04; + if(reg!=NULL && region_is_activity_r(reg)) + flags|=0x08; + + frame->titles[i].attr=attrs[flags]; +} + + +void frame_update_attr_nth(WFrame *frame, int i) +{ + WRegion *reg; + + if(i<0 || i>=frame->titles_n) + return; + + update_attr(frame, i, mplex_mx_nth((WMPlex*)frame, i)); +} + + +static void update_attrs(WFrame *frame) +{ + int i=0; + WRegion *sub; + WLListIterTmp tmp; + + FRAME_MX_FOR_ALL(sub, frame, tmp){ + update_attr(frame, i, sub); + i++; + } +} + + +static void frame_free_titles(WFrame *frame) +{ + int i; + + if(frame->titles!=NULL){ + for(i=0; ititles_n; i++){ + if(frame->titles[i].text) + free(frame->titles[i].text); + } + free(frame->titles); + frame->titles=NULL; + } + frame->titles_n=0; +} + + +static void do_init_title(WFrame *frame, int i, WRegion *sub) +{ + frame->titles[i].text=NULL; + frame->titles[i].iw=frame_nth_tab_iw(frame, i); + update_attr(frame, i, sub); +} + + +static bool frame_initialise_titles(WFrame *frame) +{ + int i, n=FRAME_MCOUNT(frame); + + frame_free_titles(frame); + + if(n==0) + n=1; + + frame->titles=ALLOC_N(GrTextElem, n); + if(frame->titles==NULL) + return FALSE; + frame->titles_n=n; + + if(FRAME_MCOUNT(frame)==0){ + do_init_title(frame, 0, NULL); + }else{ + WLListIterTmp tmp; + WRegion *sub; + i=0; + FRAME_MX_FOR_ALL(sub, frame, tmp){ + do_init_title(frame, i, sub); + i++; + } + } + + frame_recalc_bar(frame); + + return TRUE; +} + + +/*}}}*/ + + +/*{{{ Resize and reparent */ + + +bool frame_fitrep(WFrame *frame, WWindow *par, const WFitParams *fp) +{ + WRectangle old_geom, mg; + bool wchg=(REGION_GEOM(frame).w!=fp->g.w); + bool hchg=(REGION_GEOM(frame).h!=fp->g.h); + + old_geom=REGION_GEOM(frame); + + window_do_fitrep(&(frame->mplex.win), par, &(fp->g)); + + mplex_managed_geom((WMPlex*)frame, &mg); + + if(hchg){ + if(mg.h<=1){ + frame->flags|=(FRAME_SHADED|FRAME_SAVED_VERT); + frame->saved_y=old_geom.y; + frame->saved_h=old_geom.h; + }else{ + frame->flags&=~FRAME_SHADED; + } + frame->flags&=~FRAME_MAXED_VERT; + } + + if(wchg){ + if(mg.w<=1){ + frame->flags|=(FRAME_MIN_HORIZ|FRAME_SAVED_HORIZ); + frame->saved_x=old_geom.x; + frame->saved_w=old_geom.w; + }else{ + frame->flags&=~FRAME_MIN_HORIZ; + } + frame->flags&=~FRAME_MAXED_HORIZ; + } + + if(wchg || hchg){ + mplex_fit_managed((WMPlex*)frame); + mplex_size_changed((WMPlex*)frame, wchg, hchg); + } + + return TRUE; +} + + +void frame_size_hints(WFrame *frame, WSizeHints *hints_ret) +{ + WRectangle subgeom; + WLListIterTmp tmp; + WRegion *sub; + int woff, hoff; + + mplex_managed_geom((WMPlex*)frame, &subgeom); + + woff=maxof(REGION_GEOM(frame).w-subgeom.w, 0); + hoff=maxof(REGION_GEOM(frame).h-subgeom.h, 0); + + if(FRAME_CURRENT(frame)!=NULL){ + region_size_hints(FRAME_CURRENT(frame), hints_ret); + if(!USE_MINMAX(frame)){ + hints_ret->max_set=0; + hints_ret->min_set=0; + hints_ret->base_set=0; + hints_ret->aspect_set=0; + hints_ret->no_constrain=FALSE; + /*hints_ret->no_constrain=TRUE;*/ + } + }else{ + sizehints_clear(hints_ret); + } + + FRAME_MX_FOR_ALL(sub, frame, tmp){ + sizehints_adjust_for(hints_ret, sub); + } + + if(!hints_ret->base_set){ + hints_ret->base_width=0; + hints_ret->base_height=0; + hints_ret->base_set=TRUE; + } + + if(!hints_ret->min_set){ + hints_ret->min_width=0; + hints_ret->min_height=0; + hints_ret->min_set=TRUE; + } + + hints_ret->base_width+=woff; + hints_ret->base_height+=hoff; + hints_ret->max_width+=woff; + hints_ret->max_height+=hoff; + hints_ret->min_width+=woff; + hints_ret->min_height+=hoff; + + if(frame->barmode==FRAME_BAR_SHAPED){ + int f=frame->flags&(FRAME_SHADED|FRAME_SHADED_TOGGLE); + + if(f==FRAME_SHADED || f==FRAME_SHADED_TOGGLE){ + hints_ret->min_height=frame->bar_h; + hints_ret->max_height=frame->bar_h; + hints_ret->base_height=frame->bar_h; + if(!hints_ret->max_set){ + hints_ret->max_width=INT_MAX; + hints_ret->max_set=TRUE; + } + } + } +} + + +/*}}}*/ + + +/*{{{ Focus */ + + +void frame_inactivated(WFrame *frame) +{ + window_draw((WWindow*)frame, FALSE); +} + + +void frame_activated(WFrame *frame) +{ + window_draw((WWindow*)frame, FALSE); +} + + +/*}}}*/ + + +/*{{{ Client window rqgeom */ + + +static void frame_managed_rqgeom_absolute(WFrame *frame, WRegion *sub, + const WRQGeomParams *rq, + WRectangle *geomret) +{ + if(!FORWARD_CWIN_RQGEOM(frame)){ + region_managed_rqgeom_absolute_default((WRegion*)frame, sub, + rq, geomret); + }else{ + WRQGeomParams rq2=RQGEOMPARAMS_INIT; + int gravity=ForgetGravity; + WRectangle off; + WRegion *par; + + rq2.geom=rq->geom; + rq2.flags=rq->flags&(REGION_RQGEOM_WEAK_ALL + |REGION_RQGEOM_TRYONLY + |REGION_RQGEOM_ABSOLUTE); + + if(rq->flags®ION_RQGEOM_GRAVITY) + gravity=rq->gravity; + + mplex_managed_geom(&frame->mplex, &off); + off.x=-off.x; + off.y=-off.y; + off.w=REGION_GEOM(frame).w-off.w; + off.h=REGION_GEOM(frame).h-off.h; + + rq2.geom.w=maxof(rq2.geom.w+off.w, 0); + rq2.geom.h=maxof(rq2.geom.h+off.h, 0); + + /*region_size_hints_correct((WRegion*)frame, &(geom.w), &(geom.h), TRUE);*/ + + /* If WEAK_? is set, then geom.(x|y) is root-relative as it was not + * requested by the client and clientwin_handle_configure_request has + * no better guess. Otherwise the coordinates are those requested by + * the client (modulo borders/gravity) and we interpret them to be + * root-relative coordinates for this frame modulo gravity. + */ + if(rq->flags®ION_RQGEOM_WEAK_X) + rq2.geom.x+=off.x; + else + rq2.geom.x+=xgravity_deltax(gravity, -off.x, off.x+off.w); + + if(rq->flags®ION_RQGEOM_WEAK_Y) + rq2.geom.y+=off.y; + else + rq2.geom.y+=xgravity_deltay(gravity, -off.y, off.y+off.h); + + region_rqgeom((WRegion*)frame, &rq2, geomret); + + if(geomret!=NULL){ + geomret->x-=off.x; + geomret->y-=off.y; + geomret->w-=off.w; + geomret->h-=off.h; + } + } +} + + +/*}}}*/ + + +/*{{{ Misc. */ + + +bool frame_set_shaded(WFrame *frame, int sp) +{ + bool set=(frame->flags&FRAME_SHADED); + bool nset=libtu_do_setparam(sp, set); + WRQGeomParams rq=RQGEOMPARAMS_INIT; + GrBorderWidths bdw; + int h; + + if(!XOR(nset, set)) + return nset; + + rq.flags=REGION_RQGEOM_H_ONLY; + rq.geom=REGION_GEOM(frame); + + if(!nset){ + if(!(frame->flags&FRAME_SAVED_VERT)) + return FALSE; + rq.geom.h=frame->saved_h; + }else{ + if(frame->barmode==FRAME_BAR_NONE){ + return FALSE; + }else if(frame->barmode==FRAME_BAR_SHAPED){ + rq.geom.h=frame->bar_h; + }else{ + WRectangle tmp; + + frame_border_inner_geom(frame, &tmp); + + rq.geom.h=rq.geom.h-tmp.h; + } + } + + frame->flags|=FRAME_SHADED_TOGGLE; + + region_rqgeom((WRegion*)frame, &rq, NULL); + + frame->flags&=~FRAME_SHADED_TOGGLE; + + return (frame->flags&FRAME_SHADED); +} + + +/*EXTL_DOC + * Set shading state according to the parameter \var{how} + * (set/unset/toggle). Resulting state is returned, which may not be + * what was requested. + */ +EXTL_EXPORT_AS(WFrame, set_shaded) +bool frame_set_shaded_extl(WFrame *frame, const char *how) +{ + return frame_set_shaded(frame, libtu_string_to_setparam(how)); +} + + +/*EXTL_DOC + * Is \var{frame} shaded? + */ +EXTL_SAFE +EXTL_EXPORT_MEMBER +bool frame_is_shaded(WFrame *frame) +{ + return ((frame->flags&FRAME_SHADED)!=0); +} + + +bool frame_set_numbers(WFrame *frame, int sp) +{ + bool set=frame->flags&FRAME_SHOW_NUMBERS; + bool nset=libtu_do_setparam(sp, set); + + if(XOR(nset, set)){ + frame->flags^=FRAME_SHOW_NUMBERS; + frame_recalc_bar(frame); + frame_draw_bar(frame, TRUE); + } + + return frame->flags&FRAME_SHOW_NUMBERS; +} + + +/*EXTL_DOC + * Control whether tabs show numbers (set/unset/toggle). + * Resulting state is returned, which may not be what was + * requested. + */ +EXTL_EXPORT_AS(WFrame, set_numbers) +bool frame_set_numbers_extl(WFrame *frame, const char *how) +{ + return frame_set_numbers(frame, libtu_string_to_setparam(how)); +} + + +/*EXTL_DOC + * Does \var{frame} show numbers for tabs? + */ +bool frame_is_numbers(WFrame *frame) +{ + return frame->flags&FRAME_SHOW_NUMBERS; +} + + +void frame_managed_notify(WFrame *frame, WRegion *sub, const char *how) +{ + update_attrs(frame); + frame_recalc_bar(frame); + frame_draw_bar(frame, FALSE); +} + + +static void frame_size_changed_default(WFrame *frame, + bool wchg, bool hchg) +{ + int bar_w=frame->bar_w; + + if(wchg) + frame_recalc_bar(frame); + + if(frame->barmode==FRAME_BAR_SHAPED && + ((!wchg && hchg) || (wchg && bar_w==frame->bar_w))){ + frame_set_shape(frame); + } +} + + +static void frame_managed_changed(WFrame *frame, int mode, bool sw, + WRegion *reg) +{ + bool need_draw=TRUE; + + if(mode!=MPLEX_CHANGE_SWITCHONLY) + frame_initialise_titles(frame); + else + update_attrs(frame); + + if(sw) + need_draw=!frame_set_background(frame, FALSE); + + if(need_draw) + frame_draw_bar(frame, mode!=MPLEX_CHANGE_SWITCHONLY); + + mplex_call_changed_hook((WMPlex*)frame, + frame_managed_changed_hook, + mode, sw, reg); +} + + +#define EMPTY_AND_SHOULD_BE_DESTROYED(FRAME) \ + (DEST_EMPTY(frame) && FRAME_MCOUNT(FRAME)==0 && \ + !OBJ_IS_BEING_DESTROYED(frame)) + + +static void frame_destroy_empty(WFrame *frame) +{ + if(EMPTY_AND_SHOULD_BE_DESTROYED(frame)) + destroy_obj((Obj*)frame); +} + + +void frame_managed_remove(WFrame *frame, WRegion *reg) +{ + mplex_managed_remove((WMPlex*)frame, reg); + if(EMPTY_AND_SHOULD_BE_DESTROYED(frame)){ + mainloop_defer_action((Obj*)frame, + (WDeferredAction*)frame_destroy_empty); + } +} + + +int frame_default_index(WFrame *frame) +{ + return ioncore_g.frame_default_index; +} + + +/*}}}*/ + + +/*{{{ Save/load */ + + +ExtlTab frame_get_configuration(WFrame *frame) +{ + ExtlTab tab=mplex_get_configuration(&frame->mplex); + + extl_table_sets_i(tab, "mode", frame->mode); + + if(frame->flags&FRAME_SAVED_VERT){ + extl_table_sets_i(tab, "saved_y", frame->saved_y); + extl_table_sets_i(tab, "saved_h", frame->saved_h); + } + + if(frame->flags&FRAME_SAVED_HORIZ){ + extl_table_sets_i(tab, "saved_x", frame->saved_x); + extl_table_sets_i(tab, "saved_w", frame->saved_w); + } + + return tab; +} + + + +void frame_do_load(WFrame *frame, ExtlTab tab) +{ + int flags=0; + int p=0, s=0; + + if(extl_table_gets_i(tab, "saved_x", &p) && + extl_table_gets_i(tab, "saved_w", &s)){ + frame->saved_x=p; + frame->saved_w=s; + frame->flags|=FRAME_SAVED_HORIZ; + } + + if(extl_table_gets_i(tab, "saved_y", &p) && + extl_table_gets_i(tab, "saved_h", &s)){ + frame->saved_y=p; + frame->saved_h=s; + frame->flags|=FRAME_SAVED_VERT; + } + + mplex_load_contents(&frame->mplex, tab); +} + + +WRegion *frame_load(WWindow *par, const WFitParams *fp, ExtlTab tab) +{ + int mode=FRAME_MODE_UNKNOWN; + WFrame *frame; + + if(!extl_table_gets_i(tab, "mode", &mode)){ + #warning "TODO: Remove backwards compatibility hack" + char *style=NULL; + if(extl_table_gets_s(tab, "frame_style", &style)){ + if(strcmp(style, "frame-tiled")==0) + mode=FRAME_MODE_TILED; + else if(strcmp(style, "frame-floating")==0) + mode=FRAME_MODE_FLOATING; + else if(strcmp(style, "frame-transientcontainer")==0) + mode=FRAME_MODE_TRANSIENT; + free(style); + } + } + + frame=create_frame(par, fp, mode); + + if(frame!=NULL) + frame_do_load(frame, tab); + + return (WRegion*)frame; +} + + +/*}}}*/ + + +/*{{{ Dynfuntab and class info */ + + +static DynFunTab frame_dynfuntab[]={ + {region_size_hints, frame_size_hints}, + + {mplex_managed_changed, frame_managed_changed}, + {mplex_size_changed, frame_size_changed_default}, + {region_managed_notify, frame_managed_notify}, + + {region_activated, frame_activated}, + {region_inactivated, frame_inactivated}, + + {(DynFun*)window_press, (DynFun*)frame_press}, + + {(DynFun*)region_get_configuration, + (DynFun*)frame_get_configuration}, + + {window_draw, + frame_draw}, + + {mplex_managed_geom, + frame_managed_geom}, + + {region_updategr, + frame_updategr}, + + {(DynFun*)region_fitrep, + (DynFun*)frame_fitrep}, + + {region_managed_rqgeom_absolute, + frame_managed_rqgeom_absolute}, + + {region_managed_remove, frame_managed_remove}, + + {(DynFun*)mplex_default_index, + (DynFun*)frame_default_index}, + + END_DYNFUNTAB +}; + + +EXTL_EXPORT +IMPLCLASS(WFrame, WMPlex, frame_deinit, frame_dynfuntab); + + +/*}}}*/ diff --git a/ioncore/frame.h b/ioncore/frame.h new file mode 100644 index 0000000..ce37814 --- /dev/null +++ b/ioncore/frame.h @@ -0,0 +1,128 @@ +/* + * ion/ioncore/frame.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_FRAME_H +#define ION_IONCORE_FRAME_H + +#include +#include +#include + +#include "common.h" +#include "window.h" +#include "attach.h" +#include "mplex.h" +#include "gr.h" +#include "rectangle.h" +#include "sizehint.h" + +#define FRAME_SAVED_VERT 0x0008 +#define FRAME_SAVED_HORIZ 0x0010 +#define FRAME_SHADED 0x0020 +#define FRAME_SHADED_TOGGLE 0x0040 +/*#define FRAME_DEST_EMPTY 0x0100*/ +#define FRAME_MAXED_VERT 0x0200 +#define FRAME_MAXED_HORIZ 0x0400 +#define FRAME_MIN_HORIZ 0x0800 + +/*#define FRAME_SZH_USEMINMAX 0x1000 */ +/*#define FRAME_FWD_CWIN_RQGEOM 0x2000 */ + +#define FRAME_SHOW_NUMBERS 0x4000 + +typedef enum{ + FRAME_MODE_UNKNOWN, + FRAME_MODE_TILED, + FRAME_MODE_TILED_ALT, + FRAME_MODE_FLOATING, + FRAME_MODE_TRANSIENT +} WFrameMode; + +typedef enum{ + FRAME_BAR_INSIDE, + FRAME_BAR_OUTSIDE, + FRAME_BAR_SHAPED, + FRAME_BAR_NONE +} WFrameBarMode; + + + +DECLCLASS(WFrame){ + WMPlex mplex; + + int flags; + WFrameMode mode; + int saved_w, saved_h; + int saved_x, saved_y; + + int tab_dragged_idx; + + GrBrush *brush; + GrBrush *bar_brush; + GrTransparency tr_mode; + GrTextElem *titles; + int titles_n; + + /* Bar stuff */ + WFrameBarMode barmode; + int bar_w, bar_h; + double bar_max_width_q; + int tab_min_w; +}; + + +/* Create/destroy */ +extern WFrame *create_frame(WWindow *parent, const WFitParams *fp, + WFrameMode mode); +extern bool frame_init(WFrame *frame, WWindow *parent, const WFitParams *fp, + WFrameMode mode); +extern void frame_deinit(WFrame *frame); +extern bool frame_rqclose(WFrame *frame); + +/* Mode */ + +extern void frame_set_mode(WFrame *frame, WFrameMode mode); +extern WFrameMode frame_mode(WFrame *frame); + +/* Resize and reparent */ +extern bool frame_fitrep(WFrame *frame, WWindow *par, const WFitParams *fp); +extern void frame_size_hints(WFrame *frame, WSizeHints *hints_ret); + +/* Focus */ +extern void frame_activated(WFrame *frame); +extern void frame_inactivated(WFrame *frame); + +/* Tabs */ +extern int frame_nth_tab_w(WFrame *frame, int n); +extern int frame_nth_tab_iw(WFrame *frame, int n); +extern int frame_nth_tab_x(WFrame *frame, int n); +extern int frame_tab_at_x(WFrame *frame, int x); +extern void frame_update_attr_nth(WFrame *frame, int i); + +extern bool frame_set_shaded(WFrame *frame, int sp); +extern bool frame_is_shaded(WFrame *frame); +extern bool frame_set_numbers(WFrame *frame, int sp); +extern bool frame_is_numbers(WFrame *frame); + +extern int frame_default_index(WFrame *frame); + +/* Misc */ +extern void frame_managed_notify(WFrame *frame, WRegion *sub, const char *how); +extern void frame_managed_remove(WFrame *frame, WRegion *reg); + +/* Save/load */ +extern ExtlTab frame_get_configuration(WFrame *frame); +extern WRegion *frame_load(WWindow *par, const WFitParams *fp, ExtlTab tab); +extern void frame_do_load(WFrame *frame, ExtlTab tab); + +extern WHook *frame_managed_changed_hook; + +#endif /* ION_IONCORE_FRAME_H */ diff --git a/ioncore/framedpholder.c b/ioncore/framedpholder.c new file mode 100644 index 0000000..43375f2 --- /dev/null +++ b/ioncore/framedpholder.c @@ -0,0 +1,217 @@ +/* + * ion/ioncore/framedpholder.c + * + * Copyright (c) Tuomo Valkonen 2005-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include + +#include "frame.h" +#include "framedpholder.h" +#include "sizehint.h" + + +/*{{{ Init/deinit */ + + +bool framedpholder_init(WFramedPHolder *ph, WPHolder *cont, + const WFramedParam *param) +{ + assert(cont!=NULL); + + pholder_init(&(ph->ph)); + + ph->cont=cont; + ph->param=*param; + + return TRUE; +} + + +WFramedPHolder *create_framedpholder(WPHolder *cont, + const WFramedParam *param) +{ + CREATEOBJ_IMPL(WFramedPHolder, framedpholder, (p, cont, param)); +} + + +void framedpholder_deinit(WFramedPHolder *ph) +{ + if(ph->cont!=NULL){ + destroy_obj((Obj*)ph->cont); + ph->cont=NULL; + } + + pholder_deinit(&(ph->ph)); +} + + +/*}}}*/ + + +/*{{{ Attach */ + + +typedef struct{ + WRegionAttachData *data; + WFramedParam *param; +} AP; + + +WRegion *framed_handler(WWindow *par, + const WFitParams *fp, + void *ap_) +{ + AP *ap=(AP*)ap_; + WMPlexAttachParams mp=MPLEXATTACHPARAMS_INIT; + WFramedParam *param=ap->param; + WRectangle rqg, mg; + WFrame *frame; + WRegion *reg; + + if(param->mkframe!=NULL) + frame=(WFrame*)(param->mkframe)(par, fp); + else + frame=create_frame(par, fp, FRAME_MODE_FLOATING); + + if(frame==NULL) + return NULL; + + if(fp->mode&(REGION_FIT_BOUNDS|REGION_FIT_WHATEVER)) + mp.flags|=MPLEX_ATTACH_WHATEVER; + + reg=mplex_do_attach(&frame->mplex, &mp, ap->data); + + if(reg==NULL){ + destroy_obj((Obj*)frame); + return NULL; + } + + if(!(fp->mode&(REGION_FIT_BOUNDS|REGION_FIT_WHATEVER))) + return (WRegion*)frame; + + mplex_managed_geom((WMPlex*)frame, &mg); + + /* Adjust geometry */ + if(!param->inner_geom_gravity_set){ + rqg.x=REGION_GEOM(frame).x; + rqg.y=REGION_GEOM(frame).y; + rqg.w=maxof(1, REGION_GEOM(reg).w+(REGION_GEOM(frame).w-mg.w)); + rqg.h=maxof(1, REGION_GEOM(reg).h+(REGION_GEOM(frame).h-mg.h)); + }else{ + int bl=mg.x; + int br=REGION_GEOM(frame).w-(mg.x+mg.w); + int bt=mg.y; + int bb=REGION_GEOM(frame).h-(mg.y+mg.h); + + rqg.x=(fp->g.x+param->inner_geom.x+ + xgravity_deltax(param->gravity, bl, br)); + rqg.y=(fp->g.y+param->inner_geom.y+ + xgravity_deltay(param->gravity, bt, bb)); + rqg.w=maxof(1, param->inner_geom.w+(REGION_GEOM(frame).w-mg.w)); + rqg.h=maxof(1, param->inner_geom.h+(REGION_GEOM(frame).h-mg.h)); + } + + if(!(fp->mode®ION_FIT_WHATEVER)) + rectangle_constrain(&rqg, &fp->g); + + region_fit((WRegion*)frame, &rqg, REGION_FIT_EXACT); + + return (WRegion*)frame; +} + + +WRegion *region_attach_framed(WRegion *reg, WFramedParam *param, + WRegionAttachFn *fn, void *fn_param, + WRegionAttachData *data) +{ + WRegionAttachData data2; + AP ap; + + data2.type=REGION_ATTACH_NEW; + data2.u.n.fn=framed_handler; + data2.u.n.param=≈ + + ap.data=data; + ap.param=param; + + return fn(reg, fn_param, &data2); +} + + +WRegion *framedpholder_do_attach(WFramedPHolder *ph, int flags, + WRegionAttachData *data) +{ + WRegionAttachData data2; + AP ap; + + if(ph->cont==NULL) + return FALSE; + + data2.type=REGION_ATTACH_NEW; + data2.u.n.fn=framed_handler; + data2.u.n.param=≈ + + ap.data=data; + ap.param=&ph->param; + + return pholder_attach_(ph->cont, flags, &data2); +} + + +/*}}}*/ + + +/*{{{ Other dynfuns */ + + +bool framedpholder_do_goto(WFramedPHolder *ph) +{ + if(ph->cont!=NULL) + return pholder_goto(ph->cont); + + return FALSE; +} + + +WRegion *framedpholder_do_target(WFramedPHolder *ph) +{ + if(ph->cont!=NULL) + return pholder_target(ph->cont); + + return NULL; +} + + +/*}}}*/ + + +/*{{{ Class information */ + + +static DynFunTab framedpholder_dynfuntab[]={ + {(DynFun*)pholder_do_attach, + (DynFun*)framedpholder_do_attach}, + + {(DynFun*)pholder_do_goto, + (DynFun*)framedpholder_do_goto}, + + {(DynFun*)pholder_do_target, + (DynFun*)framedpholder_do_target}, + + END_DYNFUNTAB +}; + +IMPLCLASS(WFramedPHolder, WPHolder, framedpholder_deinit, + framedpholder_dynfuntab); + + +/*}}}*/ + diff --git a/ioncore/framedpholder.h b/ioncore/framedpholder.h new file mode 100644 index 0000000..2bd7f1b --- /dev/null +++ b/ioncore/framedpholder.h @@ -0,0 +1,60 @@ +/* + * ion/ioncore/framedpholder.h + * + * Copyright (c) Tuomo Valkonen 2005-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_FRAMEDPHOLDER_H +#define ION_IONCORE_FRAMEDPHOLDER_H + +#include "common.h" +#include "pholder.h" +#include "attach.h" + +INTRCLASS(WFramedPHolder); +INTRSTRUCT(WFramedParam); + + +#define FRAMEDPARAM_INIT {0, 0, {0, 0, 0, 0}, NULL} + + +DECLSTRUCT(WFramedParam){ + uint inner_geom_gravity_set:1; + int gravity; + WRectangle inner_geom; + WRegionSimpleCreateFn *mkframe; +}; + + +DECLCLASS(WFramedPHolder){ + WPHolder ph; + WPHolder *cont; + WFramedParam param; +}; + + +extern WFramedPHolder *create_framedpholder(WPHolder *cont, + const WFramedParam *param); + +extern bool framedpholder_init(WFramedPHolder *ph, WPHolder *cont, + const WFramedParam *param); + +extern void framedpholder_deinit(WFramedPHolder *ph); + +extern bool framedpholder_do_goto(WFramedPHolder *ph); + +extern WRegion *framedpholder_do_target(WFramedPHolder *ph); + +extern WRegion *framedpholder_do_attach(WFramedPHolder *ph, int flags, + WRegionAttachData *data); + +extern WRegion *region_attach_framed(WRegion *reg, WFramedParam *param, + WRegionAttachFn *fn, void *fn_param, + WRegionAttachData *data); + +#endif /* ION_IONCORE_FRAMEDPHOLDER_H */ diff --git a/ioncore/framep.h b/ioncore/framep.h new file mode 100644 index 0000000..a3b1283 --- /dev/null +++ b/ioncore/framep.h @@ -0,0 +1,39 @@ +/* + * ion/ioncore/framep.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_FRAMEP_H +#define ION_IONCORE_FRAMEP_H + +#include "frame.h" +#include "llist.h" + +#define FRAME_MCOUNT(FRAME) mplex_mx_count(&(FRAME)->mplex) +#define FRAME_CURRENT(FRAME) mplex_mx_current(&(FRAME)->mplex) + +#define FRAME_MX_FOR_ALL(REG, FRAME, TMP) \ + FOR_ALL_REGIONS_ON_LLIST(REG, (FRAME)->mplex.mx_list, TMP) + +enum{ + FRAME_AREA_NONE=0, + FRAME_AREA_BORDER=1, + FRAME_AREA_TAB=2, + FRAME_AREA_CLIENT=3 +}; + +#define IONCORE_EVENTMASK_FRAME (FocusChangeMask| \ + ButtonPressMask| \ + ButtonReleaseMask| \ + KeyPressMask| \ + EnterWindowMask| \ + ExposureMask| \ + SubstructureRedirectMask) + +#endif /* ION_IONCORE_FRAMEP_H */ diff --git a/ioncore/fullscreen.c b/ioncore/fullscreen.c new file mode 100644 index 0000000..e11a2d9 --- /dev/null +++ b/ioncore/fullscreen.c @@ -0,0 +1,212 @@ +/* + * ion/ioncore/fullscreen.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include "common.h" +#include "global.h" +#include "sizehint.h" +#include "clientwin.h" +#include "attach.h" +#include "screen.h" +#include "manage.h" +#include "fullscreen.h" +#include "mwmhints.h" +#include "focus.h" +#include "group-cw.h" + + +bool clientwin_fullscreen_may_switchto(WClientWin *cwin) +{ + return (region_may_control_focus((WRegion*)cwin) + || !REGION_IS_ACTIVE(region_screen_of((WRegion*)cwin))); +} + + +bool clientwin_check_fullscreen_request(WClientWin *cwin, int w, int h, + bool sw) +{ + WScreen *scr; + WMwmHints *mwm; + WRectangle *rwgeom; + + mwm=xwindow_get_mwmhints(cwin->win); + if(mwm==NULL || !(mwm->flags&MWM_HINTS_DECORATIONS) || + mwm->decorations!=0) + return FALSE; + + FOR_ALL_SCREENS(scr){ + if(!region_same_rootwin((WRegion*)scr, (WRegion*)cwin)) + continue; + /* TODO: if there are multiple possible rootwins, use the one with + * requested position, if any. + */ + if(REGION_GEOM(scr).w==w && REGION_GEOM(scr).h==h){ + cwin->flags|=CLIENTWIN_FS_RQ; + if(!clientwin_fullscreen_scr(cwin, (WScreen*)scr, sw)){ + cwin->flags&=~CLIENTWIN_FS_RQ; + return FALSE; + } + return TRUE; + } + } + + rwgeom=®ION_GEOM(region_rootwin_of((WRegion*)cwin)); + + /* Catch Xinerama-unaware apps here */ + if(rwgeom->w==w && rwgeom->h==h){ + cwin->flags|=CLIENTWIN_FS_RQ; + if(clientwin_enter_fullscreen(cwin, sw)) + return TRUE; + cwin->flags&=~CLIENTWIN_FS_RQ; + } + + return FALSE; +} + + +static void destroy_pholder(WPHolder **fs_pholder) +{ + WPHolder *ph=*fs_pholder; + *fs_pholder=NULL; + destroy_obj((Obj*)ph); +} + + +static bool do_fullscreen_scr(WRegion *reg, WPHolder **fs_pholder, + WScreen *scr, bool switchto) +{ + int rootx, rooty; + bool wasfs=TRUE; + int swf=(switchto ? MPLEX_ATTACH_SWITCHTO : 0); + WRegion *mgr=REGION_MANAGER(reg); + + if(*fs_pholder!=NULL) + destroy_pholder(fs_pholder); + + if(*fs_pholder==NULL && mgr!=NULL) + *fs_pholder=region_managed_get_pholder(mgr, reg); + + if(!mplex_attach_simple((WMPlex*)scr, reg, swf)){ + warn(TR("Failed to enter full screen mode.")); + if(*fs_pholder!=NULL) + destroy_pholder(fs_pholder); + return FALSE; + } + + return TRUE; +} + + +static bool do_leave_fullscreen(WRegion *reg, WPHolder **fs_pholder, + bool switchto) +{ + bool cf; + int swf=(switchto ? PHOLDER_ATTACH_SWITCHTO : 0); + + if(*fs_pholder==NULL) + return FALSE; + + cf=region_may_control_focus(reg); + + if(!pholder_attach(*fs_pholder, swf, reg)){ + warn(TR("Failed to return from full screen mode; remaining manager " + "or parent from previous location refused to manage us.")); + return FALSE; + } + + if(*fs_pholder!=NULL) + destroy_pholder(fs_pholder); + + if(cf) + region_goto(reg); + + return TRUE; +} + + +static WRegion *get_group(WClientWin *cwin) +{ + WGroupCW *cwg=OBJ_CAST(REGION_MANAGER(cwin), WGroupCW); + + return ((cwg!=NULL && group_bottom(&cwg->grp)==(WRegion*)cwin) + ? (WRegion*)cwg + : (WRegion*)cwin); +} + + +bool clientwin_fullscreen_scr(WClientWin *cwin, WScreen *scr, bool switchto) +{ + WRegion *reg=get_group(cwin); + return do_fullscreen_scr(reg, &cwin->fs_pholder, scr, switchto); +} + + +bool clientwin_enter_fullscreen(WClientWin *cwin, bool switchto) +{ + WScreen *scr=region_screen_of((WRegion*)cwin); + + if(scr==NULL){ + scr=rootwin_current_scr(region_rootwin_of((WRegion*)cwin)); + if(scr==NULL) + return FALSE; + } + + return clientwin_fullscreen_scr(cwin, scr, switchto); +} + +bool clientwin_leave_fullscreen(WClientWin *cwin, bool switchto) +{ + WRegion *reg=get_group(cwin); + return do_leave_fullscreen(reg, &cwin->fs_pholder, switchto); +} + + +bool clientwin_set_fullscreen(WClientWin *cwin, int sp) +{ + bool set=REGION_IS_FULLSCREEN(cwin); + bool nset=libtu_do_setparam(sp, set); + + if(!XOR(nset, set)) + return set; + + if(nset) + clientwin_enter_fullscreen(cwin, TRUE); + else + clientwin_leave_fullscreen(cwin, TRUE); + + return REGION_IS_FULLSCREEN(cwin); +} + + +/*EXTL_DOC + * Set client window \var{cwin} full screen state according to the + * parameter \var{how} (set/unset/toggle). Resulting state is returned, + * which may not be what was requested. + */ +EXTL_EXPORT_AS(WClientWin, set_fullscreen) +bool clientwin_set_fullscreen_extl(WClientWin *cwin, const char *how) +{ + return clientwin_set_fullscreen(cwin, libtu_string_to_setparam(how)); +} + + +/*EXTL_DOC + * Is \var{cwin} in full screen mode? + */ +EXTL_SAFE +EXTL_EXPORT_MEMBER +bool clientwin_is_fullscreen(WClientWin *cwin) +{ + return REGION_IS_FULLSCREEN(cwin); +} + + + diff --git a/ioncore/fullscreen.h b/ioncore/fullscreen.h new file mode 100644 index 0000000..8c820a4 --- /dev/null +++ b/ioncore/fullscreen.h @@ -0,0 +1,32 @@ +/* + * ion/ioncore/fullscreen.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_FULLSCREEN_H +#define ION_IONCORE_FULLSCREEN_H + +#include +#include "common.h" +#include "screen.h" +#include "clientwin.h" + +#define REGION_IS_FULLSCREEN(REG) OBJ_IS(REGION_PARENT(REG), WScreen) + +extern bool clientwin_check_fullscreen_request(WClientWin *cwin, + int w, int h, bool switchto); +extern bool clientwin_fullscreen_scr(WClientWin *cwin, WScreen *vp, + bool switchto); +extern bool clientwin_enter_fullscreen(WClientWin *cwin, bool switchto); +extern bool clientwin_leave_fullscreen(WClientWin *cwin, bool switchto); +extern bool clientwin_set_fullscreen(WClientWin *cwin, int sp); +extern bool clientwin_is_fullscreen(WClientWin *cwin); +extern bool clientwin_fullscreen_may_switchto(WClientWin *cwin); + +#endif /* ION_IONCORE_FULLSCREEN_H */ diff --git a/ioncore/global.h b/ioncore/global.h new file mode 100644 index 0000000..3756910 --- /dev/null +++ b/ioncore/global.h @@ -0,0 +1,93 @@ +/* + * ion/ioncore/global.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_GLOBAL_H +#define ION_IONCORE_GLOBAL_H + +#include "common.h" + +#include +#include + +#include "rootwin.h" +#include "screen.h" +#include "window.h" +#include "clientwin.h" + + +enum{ + IONCORE_INPUTMODE_NORMAL, + IONCORE_INPUTMODE_GRAB, + IONCORE_INPUTMODE_WAITRELEASE +}; + +enum{ + IONCORE_OPMODE_INIT, + IONCORE_OPMODE_NORMAL, + IONCORE_OPMODE_DEINIT +}; + +INTRSTRUCT(WGlobal); + +DECLSTRUCT(WGlobal){ + int argc; + char **argv; + + Display *dpy; + const char *display; + int conn; + + XContext win_context; + Atom atom_wm_state; + Atom atom_wm_change_state; + Atom atom_wm_protocols; + Atom atom_wm_delete; + Atom atom_wm_take_focus; + Atom atom_wm_colormaps; + Atom atom_wm_window_role; + Atom atom_checkcode; + Atom atom_selection; + Atom atom_mwm_hints; + + WRootWin *rootwins; + WScreen *screens; + WRegion *focus_next; + bool warp_next; + + /* We could have a display WRegion but the screen-link could impose + * some problems so these are handled as a special case. + */ + WRegion *focus_current; + + int input_mode; + int opmode; + + Time dblclick_delay; + int opaque_resize; + bool warp_enabled; + bool switchto_new; + bool screen_notify; + int frame_default_index; + bool framed_transients; + + /*bool save_enabled;*/ + + bool use_mb; /* use mb routines? */ + bool enc_sb; /* 8-bit charset? If unset, use_mb must be set. */ + bool enc_utf8; /* mb encoding is utf8? */ + + const char *sm_client_id; +}; + + +extern WGlobal ioncore_g; + +#endif /* ION_IONCORE_GLOBAL_H */ diff --git a/ioncore/gr.c b/ioncore/gr.c new file mode 100644 index 0000000..d4f5a15 --- /dev/null +++ b/ioncore/gr.c @@ -0,0 +1,443 @@ +/* + * ion/ioncore/gr.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include + +#include +#include +#include +#include "common.h" +#include "global.h" +#include "modules.h" +#include "gr.h" + + +/*{{{ Lookup and registration */ + +INTRSTRUCT(GrEngine); + +DECLSTRUCT(GrEngine){ + char *name; + GrGetBrushFn *fn; + GrEngine *next, *prev; +}; + + +static GrEngine *engines=NULL, *current_engine=NULL; + + +bool gr_register_engine(const char *engine, GrGetBrushFn *fn) +{ + GrEngine *eng; + + if(engine==NULL || fn==NULL) + return FALSE; + + eng=ALLOC(GrEngine); + + if(eng==NULL) + return FALSE; + + eng->name=scopy(engine); + + if(eng->name==NULL){ + free(eng); + return FALSE; + } + + eng->fn=fn; + + LINK_ITEM(engines, eng, next, prev); + + return TRUE; +} + + +void gr_unregister_engine(const char *engine) +{ + GrEngine *eng; + + for(eng=engines; eng!=NULL; eng=eng->next){ + if(strcmp(eng->name, engine)==0) + break; + } + + if(eng==NULL) + return; + + UNLINK_ITEM(engines, eng, next, prev); + free(eng->name); + if(current_engine==eng) + current_engine=NULL; + free(eng); +} + + +static bool gr_do_select_engine(const char *engine) +{ + GrEngine *eng; + + for(eng=engines; eng!=NULL; eng=eng->next){ + if(strcmp(eng->name, engine)==0){ + current_engine=eng; + return TRUE; + } + } + + return FALSE; +} + + +/*EXTL_DOC + * Future requests for ''brushes'' are to be forwarded to the drawing engine + * \var{engine}. If no engine of such name is known, a module with that name + * is attempted to be loaded. This function is only intended to be called from + * colour scheme etc. configuration files and can not be used to change the + * look of existing objects; for that use \fnref{gr.read_config}. + */ +EXTL_EXPORT_AS(gr, select_engine) +bool gr_select_engine(const char *engine) +{ + if(engine==NULL) + return FALSE; + + if(gr_do_select_engine(engine)) + return TRUE; + + if(!ioncore_load_module(engine)) + return FALSE; + + if(!gr_do_select_engine(engine)){ + warn(TR("Drawing engine %s is not registered!"), engine); + return FALSE; + } + + return TRUE; +} + + +GrBrush *gr_get_brush(Window win, WRootWin *rootwin, const char *style) +{ + GrEngine *eng=(current_engine!=NULL ? current_engine : engines); + GrBrush *ret; + + if(eng==NULL || eng->fn==NULL) + return NULL; + + ret=(eng->fn)(win, rootwin, style); + + if(ret==NULL) + warn(TR("Unable to find brush for style '%s'."), style); + + return ret; +} + + +/*}}}*/ + + +/*{{{ Scoring */ + + +uint gr_stylespec_score2(const char *spec, const char *attrib, + const char *attrib_p2) +{ + uint score=0; + uint a=0; + uint mult=1; + + if(attrib==NULL){ + if(spec==NULL || strcmp(spec, "*")==0) + return 1; + return 0; + } + + while(1){ + if(*spec=='*'){ + score=score+mult; + spec++; + attrib=strchr(attrib, '-'); + }else{ + while(1){ + if(*attrib=='\0'){ + attrib=NULL; + break; + } + if(*attrib=='-') + break; + if(*spec!=*attrib) + return 0; + attrib++; + spec++; + } + score=score+2*mult; + } + + if(*spec=='\0') + return score; + else if(*spec!='-') + return 0; + + if(attrib==NULL){ + if(a==0 && attrib_p2!=NULL){ + attrib=attrib_p2; + a++; + }else{ + return 0; + } + }else{ + attrib++; + } + + spec++; + mult=mult*3; + } +} + + +uint gr_stylespec_score(const char *spec, const char *attrib) +{ + return gr_stylespec_score2(spec, attrib, NULL); +} + + +/*}}}*/ + + +/*{{{ Init, deinit */ + + +bool grbrush_init(GrBrush *brush) +{ + return TRUE; +} + + +void grbrush_deinit(GrBrush *brush) +{ +} + + +void grbrush_release(GrBrush *brush) +{ + CALL_DYN(grbrush_release, brush, (brush)); +} + + +GrBrush *grbrush_get_slave(GrBrush *brush, WRootWin *rootwin, + const char *style) +{ + GrBrush *slave=NULL; + CALL_DYN_RET(slave, GrBrush*, grbrush_get_slave, brush, + (brush, rootwin, style)); + return slave; +} + + +/*}}}*/ + + +/*{{{ Dynfuns/begin/end/replay */ + + +void grbrush_begin(GrBrush *brush, const WRectangle *geom, int flags) +{ + CALL_DYN(grbrush_begin, brush, (brush, geom, flags)); +} + + +void grbrush_end(GrBrush *brush) +{ + CALL_DYN(grbrush_end, brush, (brush)); +} + + +/*}}}*/ + + +/*{{{ Dynfuns/values */ + + +void grbrush_get_font_extents(GrBrush *brush, GrFontExtents *fnte) +{ + CALL_DYN(grbrush_get_font_extents, brush, (brush, fnte)); +} + + +void grbrush_get_border_widths(GrBrush *brush, GrBorderWidths *bdw) +{ + CALL_DYN(grbrush_get_border_widths, brush, (brush, bdw)); +} + + +DYNFUN bool grbrush_get_extra(GrBrush *brush, const char *key, + char type, void *data) +{ + bool ret=FALSE; + CALL_DYN_RET(ret, bool, grbrush_get_extra, brush, + (brush, key, type, data)); + return ret; +} + + +/*}}}*/ + + +/*{{{ Dynfuns/Borders */ + + +void grbrush_draw_border(GrBrush *brush, const WRectangle *geom, + const char *attrib) +{ + CALL_DYN(grbrush_draw_border, brush, (brush, geom, attrib)); +} + + +void grbrush_draw_borderline(GrBrush *brush, const WRectangle *geom, + const char *attrib, GrBorderLine line) +{ + CALL_DYN(grbrush_draw_borderline, brush, (brush, geom, attrib, line)); +} + + +/*}}}*/ + + +/*{{{ Dynfuns/Strings */ + + +void grbrush_draw_string(GrBrush *brush, int x, int y, + const char *str, int len, bool needfill, + const char *attrib) +{ + CALL_DYN(grbrush_draw_string, brush, + (brush, x, y, str, len, needfill, attrib)); +} + + +uint grbrush_get_text_width(GrBrush *brush, const char *text, uint len) +{ + uint ret=0; + CALL_DYN_RET(ret, uint, grbrush_get_text_width, brush, + (brush, text, len)); + return ret; +} + + +/*}}}*/ + + +/*{{{ Dynfuns/Textboxes */ + + +void grbrush_draw_textbox(GrBrush *brush, const WRectangle *geom, + const char *text, const char *attr, + bool needfill) +{ + CALL_DYN(grbrush_draw_textbox, brush, + (brush, geom, text, attr, needfill)); +} + +void grbrush_draw_textboxes(GrBrush *brush, const WRectangle *geom, + int n, const GrTextElem *elem, + bool needfill, const char *common_attrib) +{ + CALL_DYN(grbrush_draw_textboxes, brush, + (brush, geom, n, elem, needfill, common_attrib)); +} + + +/*}}}*/ + + +/*{{{ Dynfuns/Misc */ + + +void grbrush_set_window_shape(GrBrush *brush, bool rough, + int n, const WRectangle *rects) +{ + CALL_DYN(grbrush_set_window_shape, brush, (brush, rough, n, rects)); +} + + +void grbrush_enable_transparency(GrBrush *brush, GrTransparency tr) +{ + CALL_DYN(grbrush_enable_transparency, brush, (brush, tr)); +} + + +void grbrush_fill_area(GrBrush *brush, const WRectangle *geom, + const char *attr) +{ + CALL_DYN(grbrush_fill_area, brush, (brush, geom, attr)); +} + + +void grbrush_clear_area(GrBrush *brush, const WRectangle *geom) +{ + CALL_DYN(grbrush_clear_area, brush, (brush, geom)); +} + + +/*}}}*/ + + +/*{{{ ioncore_read_config/refresh */ + + +/*EXTL_DOC + * Read drawing engine configuration file \file{draw.lua}. + */ +EXTL_EXPORT_AS(gr, read_config) +void gr_read_config() +{ + extl_read_config("look", NULL, TRUE); + + /* If nothing has been loaded, try the default engine with + * default settings. + */ + if(engines==NULL){ + warn(TR("No drawing engines loaded, trying \"de\".")); + gr_select_engine("de"); + } +} + + +/*EXTL_DOC + * Refresh objects' brushes to update them to use newly loaded style. + */ +EXTL_EXPORT_AS(gr, refresh) +void gr_refresh() +{ + WRootWin *rootwin; + + FOR_ALL_ROOTWINS(rootwin){ + region_updategr((WRegion*)rootwin); + } +} + + +/*}}}*/ + + +/*{{{ Class implementation */ + + +static DynFunTab grbrush_dynfuntab[]={ + END_DYNFUNTAB +}; + + +IMPLCLASS(GrBrush, Obj, grbrush_deinit, grbrush_dynfuntab); + + +/*}}}*/ + diff --git a/ioncore/gr.h b/ioncore/gr.h new file mode 100644 index 0000000..911edc6 --- /dev/null +++ b/ioncore/gr.h @@ -0,0 +1,165 @@ +/* + * ion/ioncore/gr.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_GR_H +#define ION_IONCORE_GR_H + +#include "common.h" +#include "rectangle.h" + + +INTRCLASS(GrBrush); +DECLCLASS(GrBrush){ + Obj obj; +}; + + +#include "rootwin.h" + +/* Types */ + +#define GR_FONT_EXTENTS_INIT {0, 0, 0} + +typedef struct{ + uint max_height; + uint max_width; + uint baseline; +} GrFontExtents; + +#define GR_BORDER_WIDTHS_INIT {0, 0, 0, 0, 0, 0, 0} + +typedef struct{ + uint top, bottom, left, right; + uint tb_ileft, tb_iright; + uint spacing; +} GrBorderWidths; + +typedef struct{ + char *text; + int iw; + char *attr; +} GrTextElem; + +typedef enum{ + GR_TRANSPARENCY_NO, + GR_TRANSPARENCY_YES, + GR_TRANSPARENCY_DEFAULT +} GrTransparency; + +typedef enum{ + GR_BORDERLINE_NONE, + GR_BORDERLINE_LEFT, + GR_BORDERLINE_RIGHT, + GR_BORDERLINE_TOP, + GR_BORDERLINE_BOTTOM +} GrBorderLine; + +/* Flags to grbrush_begin */ +#define GRBRUSH_AMEND 0x0001 +#define GRBRUSH_NEED_CLIP 0x0004 +#define GRBRUSH_NO_CLEAR_OK 0x0008 /* implied by GRBRUSH_AMEND */ + +/* Engines etc. */ + +typedef GrBrush *GrGetBrushFn(Window win, WRootWin *rootwin, + const char *style); + +extern bool gr_register_engine(const char *engine, GrGetBrushFn *fn); +extern void gr_unregister_engine(const char *engine); +extern bool gr_select_engine(const char *engine); +extern void gr_refresh(); +extern void gr_read_config(); + +/* Stylespecs are of the from attr1-attr2-etc. We require that each attr in + * 'spec' matches the one at same index in 'attrib' when '*' matches anything. + * The score increment for exact match is 2*3^index and 1*3^index for '*' + * match. If all elements of 'spec' match those of 'attrib' exactly, the + * accumulated score is returned. Otherwise the matching fails and zero is + * returned. For example: + * + * spec attrib score + * foo-*-baz foo-bar-baz 2+1*3+2*3^2 = 23 + * foo-bar foo-bar-baz 2+2*3 = 8 + * foo-baz foo-bar-baz 0 + * + * gr_stylespec_score2 continues matching from attrib_p2 (if not NULL) when + * it has reached end of attrib. + */ +extern uint gr_stylespec_score(const char *spec, const char *attrib); +extern uint gr_stylespec_score2(const char *spec, const char *attrib, + const char *attrib_p2); + +/* GrBrush */ + +extern GrBrush *gr_get_brush(Window win, WRootWin *rootwin, + const char *style); + +extern GrBrush *grbrush_get_slave(GrBrush *brush, WRootWin *rootwin, + const char *style); + +extern void grbrush_release(GrBrush *brush); + +extern bool grbrush_init(GrBrush *brush); +extern void grbrush_deinit(GrBrush *brush); + +extern void grbrush_begin(GrBrush *brush, const WRectangle *geom, + int flags); +extern void grbrush_end(GrBrush *brush); + +/* Border drawing */ + +DYNFUN void grbrush_get_border_widths(GrBrush *brush, GrBorderWidths *bdi); + +DYNFUN void grbrush_draw_border(GrBrush *brush, const WRectangle *geom, + const char *attrib); +DYNFUN void grbrush_draw_borderline(GrBrush *brush, const WRectangle *geom, + const char *attrib, GrBorderLine line); + +/* String drawing */ + +DYNFUN void grbrush_get_font_extents(GrBrush *brush, GrFontExtents *fnti); + +DYNFUN uint grbrush_get_text_width(GrBrush *brush, const char *text, uint len); + +DYNFUN void grbrush_draw_string(GrBrush *brush, int x, int y, + const char *str, int len, bool needfill, + const char *attrib); + +/* Textbox drawing */ + +DYNFUN void grbrush_draw_textbox(GrBrush *brush, const WRectangle *geom, + const char *text, const char *attr, + bool needfill); + +DYNFUN void grbrush_draw_textboxes(GrBrush *brush, const WRectangle *geom, + int n, const GrTextElem *elem, + bool needfill, const char *common_attrib); + +/* Misc */ + +/* Behaviour of the following two functions for "slave brushes" is undefined. + * If the parameter rough to grbrush_set_window_shape is set, the actual + * shape may be changed for corner smoothing and other superfluous effects. + * (This feature is only used by floatframes.) + */ +DYNFUN void grbrush_set_window_shape(GrBrush *brush, bool rough, + int n, const WRectangle *rects); + +DYNFUN void grbrush_enable_transparency(GrBrush *brush, GrTransparency mode); + +DYNFUN void grbrush_fill_area(GrBrush *brush, const WRectangle *geom, + const char *attr); +DYNFUN void grbrush_clear_area(GrBrush *brush, const WRectangle *geom); + +DYNFUN bool grbrush_get_extra(GrBrush *brush, const char *key, + char type, void *data); + +#endif /* ION_IONCORE_GR_H */ diff --git a/ioncore/grab.c b/ioncore/grab.c new file mode 100644 index 0000000..d3bbcad --- /dev/null +++ b/ioncore/grab.c @@ -0,0 +1,289 @@ +/* + * ion/ioncore/grab.c + * + * Copyright (c) Lukas Schroeder 2002, + * Tuomo Valkonen 2003. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include + +#define XK_MISCELLANY +#include + +#include "common.h" +#include "global.h" +#include "event.h" +#include "cursor.h" +#include "grab.h" + + +/*{{{ Definitions */ + + +typedef struct _grab_status{ + WRegion *holder; + GrabHandler *handler; + GrabKilledHandler *killedhandler; + Watch watch; + long eventmask; + long flags; + + bool remove; /* TRUE, if entry marked for removal by do_grab_remove() */ + int cursor; + Window confine_to; + int sqid; +}GrabStatus; + +#define MAX_GRABS 4 +static GrabStatus grabs[MAX_GRABS]; +static GrabStatus *current_grab; +static int idx_grab=0; +static int last_sqid=0; + + +/*}}}*/ + + +/*{{{ do_grab/ungrab */ + + +static void grab_kb_ptr(Window win, Window confine_to, int cursor, + long eventmask) +{ + ioncore_g.input_mode=IONCORE_INPUTMODE_GRAB; + + XSelectInput(ioncore_g.dpy, win, IONCORE_EVENTMASK_ROOT&~eventmask); + XGrabPointer(ioncore_g.dpy, win, True, IONCORE_EVENTMASK_PTRGRAB, + GrabModeAsync, GrabModeAsync, confine_to, + ioncore_xcursor(cursor), CurrentTime); + XGrabKeyboard(ioncore_g.dpy, win, False, GrabModeAsync, + GrabModeAsync, CurrentTime); + XSync(ioncore_g.dpy, False); + XSelectInput(ioncore_g.dpy, win, IONCORE_EVENTMASK_ROOT); +} + + +static void ungrab_kb_ptr() +{ + XUngrabKeyboard(ioncore_g.dpy, CurrentTime); + XUngrabPointer(ioncore_g.dpy, CurrentTime); + + ioncore_g.input_mode=IONCORE_INPUTMODE_NORMAL; +} + + +/*}}}*/ + + +/*{{{ Functions for installing grabs */ + + +static void do_holder_remove(WRegion *holder, bool killed); + + +static void grab_watch_handler(Watch *w, Obj *obj) +{ + do_holder_remove((WRegion*)obj, TRUE); +} + + +static void do_grab_install(GrabStatus *grab) +{ + watch_setup(&grab->watch, (Obj*)grab->holder, grab_watch_handler); + grab_kb_ptr(region_root_of(grab->holder), grab->confine_to, + grab->cursor, grab->eventmask); + current_grab=grab; +} + + +void ioncore_grab_establish(WRegion *reg, GrabHandler *func, + GrabKilledHandler *kh, + long eventmask) +{ + assert((~eventmask)&(KeyPressMask|KeyReleaseMask)); + + if(idx_grabholder=reg; + current_grab->handler=func; + current_grab->killedhandler=kh; + current_grab->eventmask=eventmask; + current_grab->remove=FALSE; + current_grab->cursor=IONCORE_CURSOR_DEFAULT; + current_grab->confine_to=region_root_of(reg); + current_grab->sqid=last_sqid++; + do_grab_install(current_grab); + } +} + + +/*}}}*/ + + +/*{{{ Grab removal functions */ + + +static void do_grab_remove() +{ + current_grab=NULL; + ungrab_kb_ptr(); + + while(idx_grab>0 && grabs[idx_grab-1].remove==TRUE){ + watch_reset(&grabs[idx_grab-1].watch); + idx_grab--; + } + + assert(idx_grab>=0); + + if(idx_grab>0){ + current_grab=&grabs[idx_grab-1]; + do_grab_install(current_grab); + } +} + + +static void mark_for_removal(GrabStatus *grab, bool killed) +{ + if(!grab->remove){ + grab->remove=TRUE; + if(killed && grab->killedhandler!=NULL && grab->holder!=NULL) + grab->killedhandler(grab->holder); + } + + if(grabs[idx_grab-1].remove) + do_grab_remove(); +} + + +static void do_holder_remove(WRegion *holder, bool killed) +{ + int i; + + for(i=idx_grab-1; i>=0; i--){ + if(grabs[i].holder==holder) + mark_for_removal(grabs+i, killed); + } +} + + +void ioncore_grab_holder_remove(WRegion *holder) +{ + do_holder_remove(holder, FALSE); +} + + +void ioncore_grab_remove(GrabHandler *func) +{ + int i; + for(i=idx_grab-1; i>=0; i--){ + if(grabs[i].handler==func){ + mark_for_removal(grabs+i, FALSE); + break; + } + } +} + + +/*}}}*/ + + +/*{{{ Grab handler calling */ + + +bool ioncore_handle_grabs(XEvent *ev) +{ + GrabStatus *gr; + int gr_sqid; + + while(current_grab && current_grab->remove) + do_grab_remove(); + + if(current_grab==NULL || current_grab->holder==NULL || + current_grab->handler==NULL){ + return FALSE; + } + + /* Escape key is harcoded to always kill active grab. */ + if(ev->type==KeyPress && XLookupKeysym(&(ev->xkey), 0)==XK_Escape){ + mark_for_removal(current_grab, TRUE); + return TRUE; + } + + if(ev->type!=KeyRelease && ev->type!=KeyPress) + return FALSE; + + /* We must check that the grab pointed to by current_grab still + * is the same grab and not already released or replaced by + * another grab. + */ + gr=current_grab; + gr_sqid=gr->sqid; + if(gr->handler(gr->holder, ev) && gr->sqid==gr_sqid) + mark_for_removal(gr, FALSE); + + return TRUE; +} + + +/*}}}*/ + + +/*{{{ Misc. */ + + +bool ioncore_grab_held() +{ + return idx_grab>0; +} + + +void ioncore_change_grab_cursor(int cursor) +{ + if(current_grab!=NULL){ + current_grab->cursor=cursor; + XChangeActivePointerGrab(ioncore_g.dpy, IONCORE_EVENTMASK_PTRGRAB, + ioncore_xcursor(cursor), CurrentTime); + } +} + + +void ioncore_grab_confine_to(Window confine_to) +{ + if(current_grab!=NULL){ + current_grab->confine_to=confine_to; + XGrabPointer(ioncore_g.dpy, region_root_of(current_grab->holder), + True, IONCORE_EVENTMASK_PTRGRAB, GrabModeAsync, + GrabModeAsync, confine_to, + ioncore_xcursor(IONCORE_CURSOR_DEFAULT), + CurrentTime); + } +} + + +WRegion *ioncore_grab_get_holder() +{ + if (ioncore_grab_held()) + return grabs[idx_grab-1].holder; + return NULL; +} + + +WRegion *ioncore_grab_get_my_holder(GrabHandler *func) +{ + int i; + for(i=idx_grab-1; i>=0; i--) + if(grabs[i].handler==func) + return grabs[i].holder; + return NULL; +} + + +/*}}}*/ + diff --git a/ioncore/grab.h b/ioncore/grab.h new file mode 100644 index 0000000..0b09c0a --- /dev/null +++ b/ioncore/grab.h @@ -0,0 +1,43 @@ +/* + * ion/ioncore/grab.h + * + * Copyright (c) Lukas Schroeder 2002, + * Tuomo Valkonen 2003. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_GRAB_H +#define ION_IONCORE_GRAB_H + +#include "global.h" /* for InputHandler and InputHandlerContext */ +#include "common.h" +#include "region.h" + +/* GrabHandler: + the default_keyboard_handler now simplifies access to subsequent keypresses + when you establish a grab using grab_establish(). + + if your GrabHandler returns TRUE, your grab will be removed, otherwise it's + kept active and you get more grabbed events passed to your handler. + */ +typedef bool GrabHandler(WRegion *reg, XEvent *ev); +typedef void GrabKilledHandler(WRegion *reg); + +extern void ioncore_grab_establish(WRegion *reg, GrabHandler *func, + GrabKilledHandler *kh,long eventmask); +extern void ioncore_grab_remove(GrabHandler *func); +extern void ioncore_grab_holder_remove(WRegion *holder); +extern WRegion *ioncore_grab_get_holder(); +extern WRegion *ioncore_grab_get_my_holder(GrabHandler *func); +extern bool ioncore_grab_held(); +extern void ioncore_change_grab_cursor(int cursor); +extern void ioncore_grab_confine_to(Window confine_to); + +extern bool ioncore_handle_grabs(XEvent *ev); + +#endif /* ION_IONCORE_GRAB_H */ + diff --git a/ioncore/group-cw.c b/ioncore/group-cw.c new file mode 100644 index 0000000..dc5cc69 --- /dev/null +++ b/ioncore/group-cw.c @@ -0,0 +1,302 @@ +/* + * ion/ioncore/group-cw.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include + +#include +#include + +#include "common.h" +#include "group-cw.h" +#include "clientwin.h" +#include "regbind.h" +#include "bindmaps.h" +#include "frame.h" +#include "resize.h" +#include "pholder.h" +#include "names.h" +#include "framedpholder.h" +#include "grouppholder.h" + + +#define DFLT_SZPLCY SIZEPOLICY_FREE_GLUE__SOUTH + + +/*{{{ Add/remove managed */ + + +WRegion *create_transient_frame(WWindow *par, + const WFitParams *fp) +{ + return (WRegion*)create_frame(par, fp, FRAME_MODE_TRANSIENT); +} + + +static WPHolder *groupcw_transient_pholder(WGroupCW *cwg, + const WClientWin *cwin, + const WManageParams *mp) +{ + WGroupAttachParams param=GROUPATTACHPARAMS_INIT; + WFramedParam fp=FRAMEDPARAM_INIT; + WPHolder *ph; + + param.level_set=1; + param.level=STACKING_LEVEL_MODAL1; + + param.szplcy_set=1; + param.szplcy=cwg->transient_szplcy; + + param.switchto_set=1; + param.switchto=1; + + param.geom_weak_set=1; + param.geom_weak=REGION_RQGEOM_WEAK_ALL; + + if(!ioncore_g.framed_transients){ + param.geom_set=TRUE; + param.geom=mp->geom; + + return (WPHolder*)create_grouppholder(&cwg->grp, NULL, ¶m); + }else{ + fp.inner_geom_gravity_set=1; + fp.inner_geom=mp->geom; + fp.gravity=ForgetGravity; + fp.mkframe=create_transient_frame; + + ph=(WPHolder*)create_grouppholder(&cwg->grp, NULL, ¶m); + + return pholder_either((WPHolder*)create_framedpholder(ph, &fp), ph); + } +} + + +WPHolder *groupcw_prepare_manage(WGroupCW *cwg, const WClientWin *cwin, + const WManageParams *param, int redir) +{ + if(redir==MANAGE_REDIR_STRICT_YES) + return NULL; + + /* Only catch windows with transient mode set to current here. */ + if(clientwin_get_transient_mode(cwin)!=TRANSIENT_MODE_CURRENT) + return NULL; + + return groupcw_transient_pholder(cwg, cwin, param); +} + + +static bool groupcw_should_manage_transient(WGroupCW *cwg, + WClientWin *tfor) +{ + WRegion *mgr; + + if(group_find_stacking(&cwg->grp, (WRegion*)tfor)) + return TRUE; + + mgr=REGION_MANAGER(tfor); + + if(mgr!=NULL && ioncore_g.framed_transients && OBJ_IS(mgr, WFrame)) + return (group_find_stacking(&cwg->grp, mgr)!=NULL); + + return FALSE; +} + + +WPHolder *groupcw_prepare_manage_transient(WGroupCW *cwg, + const WClientWin *transient, + const WManageParams *param, + int unused) +{ + WPHolder *ph=region_prepare_manage_transient_default((WRegion*)cwg, + transient, + param, + unused); + + if(ph==NULL && groupcw_should_manage_transient(cwg, param->tfor)) + ph=groupcw_transient_pholder(cwg, transient, param); + + return ph; +} + + +/*}}}*/ + + +/*{{{ Misc. */ + + +/*_EXTL_DOC + * Toggle transients managed by \var{cwin} between top/bottom + * of the window. + */ +EXTL_EXPORT_MEMBER +void groupcw_toggle_transients_pos(WGroupCW *cwg) +{ + WStacking *st; + WGroupIterTmp tmp; + + if((cwg->transient_szplcy&SIZEPOLICY_VERT_MASK)==SIZEPOLICY_VERT_TOP){ + cwg->transient_szplcy&=~SIZEPOLICY_VERT_MASK; + cwg->transient_szplcy|=SIZEPOLICY_VERT_BOTTOM; + }else{ + cwg->transient_szplcy&=~SIZEPOLICY_VERT_MASK; + cwg->transient_szplcy|=SIZEPOLICY_VERT_TOP; + } + + FOR_ALL_NODES_IN_GROUP(&cwg->grp, st, tmp){ + st->szplcy&=~SIZEPOLICY_VERT_MASK; + st->szplcy|=(cwg->transient_szplcy&SIZEPOLICY_VERT_MASK); + + if(st->reg!=NULL){ + WFitParams fp; + + fp.g=REGION_GEOM(cwg); + + sizepolicy(&st->szplcy, st->reg, NULL, + REGION_RQGEOM_WEAK_ALL, &fp); + region_fitrep(st->reg, NULL, &fp); + } + } +} + + +const char *groupcw_displayname(WGroupCW *cwg) +{ + const char *name=NULL; + + if(cwg->grp.bottom!=NULL && cwg->grp.bottom->reg!=NULL) + name=region_name(cwg->grp.bottom->reg); + + if(name==NULL) + name=region_name((WRegion*)cwg); + + return name; +} + + +void groupcw_managed_notify(WGroupCW *cwg, WRegion *reg, const char *how) +{ + if(group_bottom(&cwg->grp)==reg + && strcmp(how, "name")==0){ + /* Title has changed */ + region_notify_change((WRegion*)cwg, how); + } +} + + +/*}}}*/ + + +/*{{{ WGroupCW class */ + + +bool groupcw_init(WGroupCW *cwg, WWindow *parent, const WFitParams *fp) +{ + cwg->transient_szplcy=DFLT_SZPLCY; + /*cwg->fs_pholder=NULL;*/ + + if(!group_init(&(cwg->grp), parent, fp)) + return FALSE; + + cwg->grp.bottom_last_close=TRUE; + + region_add_bindmap((WRegion*)cwg, ioncore_groupcw_bindmap); + + return TRUE; +} + + +WGroupCW *create_groupcw(WWindow *parent, const WFitParams *fp) +{ + CREATEOBJ_IMPL(WGroupCW, groupcw, (p, parent, fp)); +} + + +void groupcw_deinit(WGroupCW *cwg) +{ + group_deinit(&(cwg->grp)); +} + + +WRegion *groupcw_load(WWindow *par, const WFitParams *fp, ExtlTab tab) +{ + WGroupCW *ws; + ExtlTab substab, subtab; + int i, n; + + ws=create_groupcw(par, fp); + + if(ws==NULL) + return NULL; + + if(!extl_table_gets_t(tab, "managed", &substab)) + return (WRegion*)ws; + + n=extl_table_get_n(substab); + for(i=1; i<=n; i++){ + if(extl_table_geti_t(substab, i, &subtab)){ + group_attach_new(&ws->grp, subtab); + extl_unref_table(subtab); + } + } + + extl_unref_table(substab); + + if(ws->grp.managed_list==NULL){ + destroy_obj((Obj*)ws); + return NULL; + } + + return (WRegion*)ws; +} + + +static DynFunTab groupcw_dynfuntab[]={ + {(DynFun*)region_prepare_manage, + (DynFun*)groupcw_prepare_manage}, + + {(DynFun*)region_prepare_manage_transient, + (DynFun*)groupcw_prepare_manage_transient}, + + /* + {(DynFun*)region_handle_drop, + (DynFun*)groupcw_handle_drop}, + + {(DynFun*)group_do_add_managed, + (DynFun*)groupcw_do_add_managed}, + */ + + /* + {(DynFun*)region_get_rescue_pholder_for, + (DynFun*)groupcw_get_rescue_pholder_for}, + */ + + {(DynFun*)region_prepare_manage, + (DynFun*)groupcw_prepare_manage}, + + {(DynFun*)region_prepare_manage_transient, + (DynFun*)groupcw_prepare_manage_transient}, + + {(DynFun*)region_displayname, + (DynFun*)groupcw_displayname}, + + {region_managed_notify, + groupcw_managed_notify}, + + END_DYNFUNTAB +}; + + +EXTL_EXPORT +IMPLCLASS(WGroupCW, WGroup, groupcw_deinit, groupcw_dynfuntab); + + +/*}}}*/ + diff --git a/ioncore/group-cw.h b/ioncore/group-cw.h new file mode 100644 index 0000000..b389d46 --- /dev/null +++ b/ioncore/group-cw.h @@ -0,0 +1,45 @@ +/* + * ion/ioncore/group-cw.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_GROUPCW_H +#define ION_IONCORE_GROUPCW_H + +#include "common.h" +#include "group.h" +#include "clientwin.h" + + +DECLCLASS(WGroupCW){ + WGroup grp; + /*WPHolder *fs_pholder;*/ + WSizePolicy transient_szplcy; /* default transient size policy */ +}; + +extern bool groupcw_init(WGroupCW *cwg, WWindow *parent, const WFitParams *fp); +extern WGroupCW *create_groupcw(WWindow *parent, const WFitParams *fp); +extern void groupcw_deinit(WGroupCW *cwg); + +extern WPHolder *groupcw_prepare_manage(WGroupCW *cwg, + const WClientWin *cwin2, + const WManageParams *param, + int redir); + +extern WPHolder *groupcw_prepare_manage_transient(WGroupCW *cwg, + const WClientWin *transient, + const WManageParams *param, + int unused); + +extern WRegion *groupcw_load(WWindow *par, const WFitParams *fp, ExtlTab tab); + +extern WRegion *create_transient_frame(WWindow *par, + const WFitParams *fp); + +#endif /* ION_IONCORE_GROUPCW_H */ diff --git a/ioncore/group-ws.c b/ioncore/group-ws.c new file mode 100644 index 0000000..b50402c --- /dev/null +++ b/ioncore/group-ws.c @@ -0,0 +1,430 @@ +/* + * ion/ioncore/group-ws.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include + +#include +#include + +#include "common.h" +#include "global.h" +#include "region.h" +#include "focus.h" +#include "group.h" +#include "regbind.h" +#include "bindmaps.h" +#include "xwindow.h" +#include "group-ws.h" +#include "group-cw.h" +#include "grouppholder.h" +#include "groupedpholder.h" +#include "framedpholder.h" +#include "float-placement.h" +#include "resize.h" + + +/*{{{ Settings */ + + +static bool default_ws_params_set=FALSE; +static ExtlTab default_ws_params; + + +/*EXTL_DOC + * Set module basic settings. Currently only the \code{placement_method} + * parameter is supported. + * + * The method can be one of ''udlr'', ''lrud'' (default) and ''random''. + * The ''udlr'' method looks for free space starting from top the top left + * corner of the workspace moving first down keeping the x coordinate fixed. + * If it find no free space, it start looking similarly at next x coordinate + * unoccupied by other objects and so on. ''lrud' is the same but with the + * role of coordinates changed and both fall back to ''random'' placement + * if no free area was found. + */ + +void ioncore_groupws_set(ExtlTab tab) +{ + char *method=NULL; + ExtlTab t; + + if(extl_table_gets_s(tab, "float_placement_method", &method)){ + if(strcmp(method, "udlr")==0) + ioncore_placement_method=PLACEMENT_UDLR; + else if(strcmp(method, "lrud")==0) + ioncore_placement_method=PLACEMENT_LRUD; + else if(strcmp(method, "random")==0) + ioncore_placement_method=PLACEMENT_RANDOM; + else + warn(TR("Unknown placement method \"%s\"."), method); + free(method); + } + + if(extl_table_gets_t(tab, "default_ws_params", &t)){ + if(default_ws_params_set) + extl_unref_table(default_ws_params); + default_ws_params=t; + default_ws_params_set=TRUE; + } +} + + +void ioncore_groupws_get(ExtlTab t) +{ + extl_table_sets_s(t, "float_placement_method", + (ioncore_placement_method==PLACEMENT_UDLR + ? "udlr" + : (ioncore_placement_method==PLACEMENT_LRUD + ? "lrud" + : "random"))); + + if(default_ws_params_set) + extl_table_sets_t(t, "default_ws_params", default_ws_params); +} + + +/*}}}*/ + + +/*{{{ Attach stuff */ + + +static bool groupws_attach_framed(WGroupWS *ws, + WGroupAttachParams *ap, + WFramedParam *fp, + WRegion *reg) +{ + WRegionAttachData data; + + data.type=REGION_ATTACH_REPARENT; + data.u.reg=reg; + + return (region_attach_framed((WRegion*)ws, fp, + (WRegionAttachFn*)group_do_attach, + ap, &data)!=NULL); +} + + +bool groupws_handle_drop(WGroupWS *ws, int x, int y, + WRegion *dropped) +{ + WGroupAttachParams ap=GROUPATTACHPARAMS_INIT; + WFramedParam fp=FRAMEDPARAM_INIT; + + ap.switchto_set=TRUE; + ap.switchto=TRUE; + + fp.inner_geom_gravity_set=TRUE; + fp.inner_geom.x=x; + fp.inner_geom.y=y; + fp.inner_geom.w=REGION_GEOM(dropped).w; + fp.inner_geom.h=REGION_GEOM(dropped).h; + fp.gravity=NorthWestGravity; + + return groupws_attach_framed(ws, &ap, &fp, dropped); +} + + +/*EXTL_DOC + * Attach region \var{reg} on \var{ws}. + * At least the following fields in \var{t} are supported: + * + * \begin{tabularx}{\linewidth}{lX} + * \tabhead{Field & Description} + * \var{switchto} & Should the region be switched to (boolean)? Optional. \\ + * \var{geom} & Geometry; \var{x} and \var{y}, if set, indicates top-left of + * the frame to be created while \var{width} and \var{height}, if set, indicate + * the size of the client window within that frame. Optional. + * \end{tabularx} + */ +EXTL_EXPORT_AS(WGroupWS, attach_framed) +bool groupws_attach_framed_extl(WGroupWS *ws, WRegion *reg, ExtlTab t) +{ + WGroupAttachParams ap=GROUPATTACHPARAMS_INIT; + WFramedParam fp=FRAMEDPARAM_INIT; + ExtlTab gt; + + if(reg==NULL) + return FALSE; + + fp.gravity=ForgetGravity; + + if(extl_table_is_bool_set(t, "switchto")){ + ap.switchto_set=TRUE; + ap.switchto=TRUE; + } + + if(extl_table_gets_t(t, "geom", >)){ + int pos=0, size=0; + + fp.inner_geom.x=0; + fp.inner_geom.y=0; + + if(extl_table_gets_i(gt, "x", &(ap.geom.x))) + pos++; + if(extl_table_gets_i(gt, "y", &(ap.geom.y))) + pos++; + + if(extl_table_gets_i(gt, "w", &(ap.geom.w))) + size++; + if(extl_table_gets_i(gt, "h", &(ap.geom.h))) + size++; + + fp.inner_geom.w=maxof(fp.inner_geom.w, 1); + fp.inner_geom.h=maxof(fp.inner_geom.h, 1); + + fp.inner_geom_gravity_set=(size==2 && pos==2); + + extl_unref_table(gt); + } + + return groupws_attach_framed(ws, &ap, &fp, reg); +} + + +/*}}}*/ + + +/*{{{ groupws_prepare_manage */ + + +#define REG_OK(R) OBJ_IS(R, WMPlex) + + +static WMPlex *find_existing(WGroupWS *ws) +{ + WGroupIterTmp tmp; + WRegion *r=(ws->grp.current_managed!=NULL + ? ws->grp.current_managed->reg + : NULL); + + if(r!=NULL && REG_OK(r)) + return (WMPlex*)r; + + FOR_ALL_MANAGED_BY_GROUP(&ws->grp, r, tmp){ + if(REG_OK(r)) + return (WMPlex*)r; + } + + return NULL; +} + + +static WPHolder *groupws_do_prepare_manage(WGroupWS *ws, + const WClientWin *cwin, + const WManageParams *param, + int redir, int geom_weak) +{ + WGroupAttachParams ap=GROUPATTACHPARAMS_INIT; + WFramedParam fp=FRAMEDPARAM_INIT; + WPHolder *ph; + + if(redir==MANAGE_REDIR_PREFER_YES){ + WMPlex *m=find_existing(ws); + if(m!=NULL){ + WPHolder *ph; + ph=region_prepare_manage((WRegion*)m, cwin, param, + MANAGE_REDIR_STRICT_YES); + if(ph!=NULL) + return ph; + } + } + + if(redir==MANAGE_REDIR_STRICT_YES) + return NULL; + + fp.inner_geom_gravity_set=TRUE; + fp.inner_geom=param->geom; + fp.gravity=param->gravity; + + ap.geom_weak_set=1; + ap.geom_weak=geom_weak; + + ph=(WPHolder*)create_grouppholder(&ws->grp, NULL, &ap); + + if(ph!=NULL) + ph=pholder_either((WPHolder*)create_framedpholder(ph, &fp), ph); + + if(ph!=NULL) + ph=pholder_either((WPHolder*)create_groupedpholder((WPHolder*)ph), ph); + + return ph; +} + + +WPHolder *groupws_prepare_manage(WGroupWS *ws, const WClientWin *cwin, + const WManageParams *param, + int redir) +{ + WRegion *b=(ws->grp.bottom!=NULL ? ws->grp.bottom->reg : NULL); + WPHolder *ph=NULL; + bool act_b=(ws->grp.bottom==ws->grp.current_managed); + bool always_float, use_bottom; + int weak=0; + + if(param->maprq && ioncore_g.opmode!=IONCORE_OPMODE_INIT + && !param->userpos){ + /* When the window is mapped by application request, position + * request is only honoured if the position was given by the user + * and in case of a transient (the app may know better where to + * place them) or if we're initialising. + */ + weak=REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_Y; + } + + if(b!=NULL && !HAS_DYN(b, region_prepare_manage)) + b=NULL; + + use_bottom=(act_b + ? !extl_table_is_bool_set(cwin->proptab, "float") + : act_b); + + if(b!=NULL && use_bottom) + ph=region_prepare_manage(b, cwin, param, redir); + + if(ph==NULL) + ph=groupws_do_prepare_manage(ws, cwin, param, redir, weak); + + if(ph==NULL && b!=NULL && !use_bottom) + ph=region_prepare_manage(b, cwin, param, redir); + + return ph; +} + + +WPHolder *groupws_prepare_manage_transient(WGroupWS *ws, const WClientWin *cwin, + const WManageParams *param, + int unused) +{ + WGroupAttachParams ap=GROUPATTACHPARAMS_INIT; + WFramedParam fp=FRAMEDPARAM_INIT; + WPHolder *ph; + + ap.stack_above=OBJ_CAST(REGION_PARENT(param->tfor), WRegion); + if(ap.stack_above==NULL) + return NULL; + + fp.inner_geom_gravity_set=TRUE; + fp.inner_geom=param->geom; + fp.gravity=param->gravity; + fp.mkframe=create_transient_frame; + + ap.geom_weak_set=1; + ap.geom_weak=0; + + ph=(WPHolder*)create_grouppholder(&ws->grp, NULL, &ap); + + return pholder_either((WPHolder*)create_framedpholder(ph, &fp), ph); +} + + +WPHolder *groupws_get_rescue_pholder_for(WGroupWS *ws, + WRegion *forwhat) +{ + WGroupAttachParams ap=GROUPATTACHPARAMS_INIT; + WFramedParam fp=FRAMEDPARAM_INIT; + WPHolder *ph; + + ap.geom_set=TRUE; + ap.geom=REGION_GEOM(forwhat); + + ap.geom_weak_set=1; + ap.geom_weak=(REGION_PARENT(forwhat)!=REGION_PARENT(ws) + ? REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_Y + : 0); + + ph=(WPHolder*)create_grouppholder(&ws->grp, NULL, &ap); + + return pholder_either((WPHolder*)create_framedpholder(ph, &fp), ph); +} + + +/*}}}*/ + + +/*{{{ WGroupWS class */ + + +bool groupws_init(WGroupWS *ws, WWindow *parent, const WFitParams *fp) +{ + if(!group_init(&(ws->grp), parent, fp)) + return FALSE; + + ((WRegion*)ws)->flags|=REGION_GRAB_ON_PARENT; + + region_add_bindmap((WRegion*)ws, ioncore_groupws_bindmap); + + return TRUE; +} + + +WGroupWS *create_groupws(WWindow *parent, const WFitParams *fp) +{ + CREATEOBJ_IMPL(WGroupWS, groupws, (p, parent, fp)); +} + + +void groupws_deinit(WGroupWS *ws) +{ + group_deinit(&(ws->grp)); +} + + +WRegion *groupws_load(WWindow *par, const WFitParams *fp, + ExtlTab tab) +{ + WGroupWS *ws; + + ws=create_groupws(par, fp); + + if(ws==NULL) + return NULL; + + group_do_load(&ws->grp, tab); + + return (WRegion*)ws; +} + + +WRegion *groupws_load_default(WWindow *par, const WFitParams *fp) +{ + return groupws_load(par, fp, (default_ws_params_set + ? default_ws_params + : extl_table_none())); +} + + +static DynFunTab groupws_dynfuntab[]={ + {(DynFun*)region_prepare_manage, + (DynFun*)groupws_prepare_manage}, + + {(DynFun*)region_prepare_manage_transient, + (DynFun*)groupws_prepare_manage_transient}, + + {(DynFun*)region_handle_drop, + (DynFun*)groupws_handle_drop}, + + {(DynFun*)region_get_rescue_pholder_for, + (DynFun*)groupws_get_rescue_pholder_for}, + + {region_manage_stdisp, + group_manage_stdisp}, + + END_DYNFUNTAB +}; + + +EXTL_EXPORT +IMPLCLASS(WGroupWS, WGroup, groupws_deinit, groupws_dynfuntab); + + +/*}}}*/ + diff --git a/ioncore/group-ws.h b/ioncore/group-ws.h new file mode 100644 index 0000000..cfc43a9 --- /dev/null +++ b/ioncore/group-ws.h @@ -0,0 +1,49 @@ +/* + * ion/ioncore/groupws.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_GROUPWS_H +#define ION_IONCORE_GROUPWS_H + +#include +#include +#include +#include "classes.h" + + +DECLCLASS(WGroupWS){ + WGroup grp; +}; + + +extern WPHolder *groupws_prepare_manage(WGroupWS *ws, + const WClientWin *cwin, + const WManageParams *param, + int redir); + +extern WPHolder *groupws_prepare_manage_transient(WGroupWS *ws, + const WClientWin *cwin, + const WManageParams *param, + int unused); + +extern bool groupws_handle_drop(WGroupWS *ws, int x, int y, + WRegion *dropped); + +extern WGroupWS *create_groupws(WWindow *parent, const WFitParams *fp); +extern bool groupws_init(WGroupWS *ws, WWindow *parent, const WFitParams *fp); +extern void groupws_deinit(WGroupWS *ws); + +extern WRegion *groupws_load_default(WWindow *par, const WFitParams *fp); +extern WRegion *groupws_load(WWindow *par, const WFitParams *fp, ExtlTab tab); + +extern void ioncore_groupws_set(ExtlTab tab); +extern void ioncore_groupws_get(ExtlTab t); + +#endif /* ION_IONCORE_GROUPWS_H */ diff --git a/ioncore/group.c b/ioncore/group.c new file mode 100644 index 0000000..591fcd7 --- /dev/null +++ b/ioncore/group.c @@ -0,0 +1,1361 @@ +/* + * ion/ioncore/group.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include + +#include +#include +#include + +#include "common.h" +#include "rootwin.h" +#include "focus.h" +#include "global.h" +#include "region.h" +#include "manage.h" +#include "screen.h" +#include "names.h" +#include "saveload.h" +#include "attach.h" +#include "regbind.h" +#include "extlconv.h" +#include "xwindow.h" +#include "resize.h" +#include "stacking.h" +#include "sizepolicy.h" +#include "bindmaps.h" +#include "navi.h" +#include "sizehint.h" +#include "llist.h" +#include "mplex.h" +#include "group.h" +#include "grouppholder.h" +#include "frame.h" +#include "float-placement.h" + + +static void group_place_stdisp(WGroup *ws, WWindow *parent, + int pos, WRegion *stdisp); + + + +/*{{{ Stacking list stuff */ + + +WStacking *group_get_stacking(WGroup *ws) +{ + WWindow *par=REGION_PARENT(ws); + + return (par==NULL + ? NULL + : window_get_stacking(par)); +} + + +WStacking **group_get_stackingp(WGroup *ws) +{ + WWindow *par=REGION_PARENT(ws); + + return (par==NULL + ? NULL + : window_get_stackingp(par)); +} + + +static bool wsfilt(WStacking *st, void *ws) +{ + return (st->reg!=NULL && REGION_MANAGER(st->reg)==(WRegion*)ws); +} + + +static bool wsfilt_nostdisp(WStacking *st, void *ws) +{ + return (wsfilt(st, ws) && ((WGroup*)ws)->managed_stdisp!=st); +} + + +void group_iter_init(WGroupIterTmp *tmp, WGroup *ws) +{ + stacking_iter_mgr_init(tmp, ws->managed_list, NULL, ws); +} + + +void group_iter_init_nostdisp(WGroupIterTmp *tmp, WGroup *ws) +{ + stacking_iter_mgr_init(tmp, ws->managed_list, wsfilt_nostdisp, ws); +} + + +WRegion *group_iter(WGroupIterTmp *tmp) +{ + return stacking_iter_mgr(tmp); +} + + +WStacking *group_iter_nodes(WGroupIterTmp *tmp) +{ + return stacking_iter_mgr_nodes(tmp); +} + + +WGroupIterTmp group_iter_default_tmp; + + +/*}}}*/ + + +/*{{{ region dynfun implementations */ + + +static void group_fit(WGroup *ws, const WRectangle *geom) +{ + REGION_GEOM(ws)=*geom; +} + + +bool group_fitrep(WGroup *ws, WWindow *par, const WFitParams *fp) +{ + WGroupIterTmp tmp; + WStacking *unweaved=NULL; + int xdiff=0, ydiff=0; + WStacking *st; + WWindow *oldpar; + WRectangle g; + + oldpar=REGION_PARENT(ws); + + if(par==NULL){ + if(fp->mode®ION_FIT_WHATEVER) + return TRUE; + REGION_GEOM(ws)=fp->g; + }else{ + if(!region_same_rootwin((WRegion*)ws, (WRegion*)par)) + return FALSE; + + if(ws->managed_stdisp!=NULL && ws->managed_stdisp->reg!=NULL) + region_detach_manager(ws->managed_stdisp->reg); + + assert(ws->managed_stdisp==NULL); + + xdiff=fp->g.x-REGION_GEOM(ws).x; + ydiff=fp->g.y-REGION_GEOM(ws).y; + + region_unset_parent((WRegion*)ws); + XReparentWindow(ioncore_g.dpy, ws->dummywin, par->win, -1, -1); + region_set_parent((WRegion*)ws, par); + + REGION_GEOM(ws).x=fp->g.x; + REGION_GEOM(ws).y=fp->g.y; + if(!(fp->mode®ION_FIT_WHATEVER)){ + REGION_GEOM(ws).w=fp->g.w; + REGION_GEOM(ws).h=fp->g.h; + } + + if(oldpar!=NULL) + unweaved=stacking_unweave(&oldpar->stacking, wsfilt, (void*)ws); + } + + FOR_ALL_NODES_IN_GROUP(ws, st, tmp){ + WFitParams fp2=*fp; + + if(st->reg==NULL) + continue; + + g=REGION_GEOM(st->reg); + g.x+=xdiff; + g.y+=ydiff; + + if(fp->mode®ION_FIT_WHATEVER){ + fp2.g=g; + }else{ + fp2.g=REGION_GEOM(ws); + sizepolicy(&st->szplcy, st->reg, &g, REGION_RQGEOM_WEAK_ALL, &fp2); + } + + if(!region_fitrep(st->reg, par, &fp2)){ + warn(TR("Error reparenting %s."), region_name(st->reg)); + region_detach_manager(st->reg); + } + } + + if(unweaved!=NULL) + stacking_weave(&par->stacking, &unweaved, FALSE); + + return TRUE; +} + + +static void group_map(WGroup *ws) +{ + WRegion *reg; + WGroupIterTmp tmp; + + REGION_MARK_MAPPED(ws); + XMapWindow(ioncore_g.dpy, ws->dummywin); + + FOR_ALL_MANAGED_BY_GROUP(ws, reg, tmp){ + region_map(reg); + } +} + + +static void group_unmap(WGroup *ws) +{ + WRegion *reg; + WGroupIterTmp tmp; + + REGION_MARK_UNMAPPED(ws); + XUnmapWindow(ioncore_g.dpy, ws->dummywin); + + FOR_ALL_MANAGED_BY_GROUP(ws, reg, tmp){ + region_unmap(reg); + } +} + + +static WStacking *find_to_focus(WGroup *ws, WStacking *st, bool group_only) +{ + WStacking *stacking=group_get_stacking(ws); + + if(stacking==NULL) + return st; + + return stacking_find_to_focus_mapped(stacking, st, + (group_only ? (WRegion*)ws : NULL)); +} + + +static bool group_refocus_(WGroup *ws, WStacking *st) +{ + if(st!=ws->current_managed && st->reg!=NULL){ + if(region_may_control_focus((WRegion*)ws)) + region_set_focus(st->reg); + else + ws->current_managed=st; + return TRUE; + } + + return FALSE; +} + + +static void group_do_set_focus(WGroup *ws, bool warp) +{ + WStacking *st=ws->current_managed; + + if(st==NULL || st->reg==NULL) + st=find_to_focus(ws, NULL, TRUE); + + if(st!=NULL && st->reg!=NULL) + region_do_set_focus(st->reg, warp); + else + region_finalise_focusing((WRegion*)ws, ws->dummywin, warp); +} + + +static bool group_managed_prepare_focus(WGroup *ws, WRegion *reg, + int flags, WPrepareFocusResult *res) +{ + WMPlex *mplex=OBJ_CAST(REGION_MANAGER(ws), WMPlex); + WStacking *st=group_find_stacking(ws, reg); + + if(st==NULL) + return FALSE; + + if(mplex!=NULL){ + WStacking *node=mplex_find_stacking(mplex, (WRegion*)ws); + + if(node==NULL) + return FALSE; + + return mplex_do_prepare_focus(mplex, node, st, + flags, res); + }else{ + WStacking *stacking; + + if(!region_prepare_focus((WRegion*)ws, flags, res)) + return FALSE; + + stacking=group_get_stacking(ws); + st=find_to_focus(ws, st, FALSE); + +#warning "TODO: raise in some cases (not enter-window)?" + + if(st==NULL) + return FALSE; + + res->reg=st->reg; + res->flags=flags; + + return (res->reg==reg); + } +} + + +static bool group_essentially_empty(WGroup *ws) +{ + WGroupIterTmp tmp; + WStacking *st; + + FOR_ALL_NODES_IN_GROUP(ws, st, tmp){ + if(st!=ws->managed_stdisp) + return FALSE; + } + + return TRUE; +} + + +void group_managed_remove(WGroup *ws, WRegion *reg) +{ + bool mcf=region_may_control_focus((WRegion*)ws); + bool ds=OBJ_IS_BEING_DESTROYED(ws); + WStacking *st, *next_st=NULL; + bool was_stdisp=FALSE, was_bottom=FALSE; + bool dest=FALSE; + bool cur=FALSE; + + st=group_find_stacking(ws, reg); + + if(st!=NULL){ + next_st=stacking_unstack(REGION_PARENT(ws), st); + + UNLINK_ITEM(ws->managed_list, st, mgr_next, mgr_prev); + + if(st==ws->managed_stdisp){ + ws->managed_stdisp=NULL; + was_stdisp=TRUE; + } + + if(st==ws->bottom){ + ws->bottom=NULL; + was_bottom=TRUE; + if(ws->bottom_last_close && group_essentially_empty(ws)) + dest=TRUE; + } + + if(st==ws->current_managed){ + cur=TRUE; + ws->current_managed=NULL; + } + + stacking_unassoc(st); + stacking_free(st); + } + + region_unset_manager(reg, (WRegion*)ws); + + if(!dest && !ds){ + if(was_bottom && !was_stdisp && ws->managed_stdisp==NULL){ + /* We should probably be managing any stdisp, that 'bottom' + * was managing. + */ + WMPlex *mplex=OBJ_CAST(REGION_MANAGER(ws), WMPlex); + + if(mplex!=NULL + && mplex->mx_current!=NULL + && mplex->mx_current->st->reg==(WRegion*)ws){ + mplex_remanage_stdisp(mplex); + } + } + + if(cur){ + WStacking *stf=find_to_focus(ws, next_st, TRUE); + if(stf!=NULL) + region_warp(stf->reg); + } + }else if(dest && !ds){ + mainloop_defer_destroy((Obj*)ws); + } +} + + +static void group_managed_activated(WGroup *ws, WRegion *reg) +{ + ws->current_managed=group_find_stacking(ws, reg); +} + + +/*}}}*/ + + +/*{{{ Create/destroy */ + + +bool group_init(WGroup *ws, WWindow *par, const WFitParams *fp) +{ + ws->current_managed=NULL; + ws->managed_stdisp=NULL; + ws->bottom=NULL; + ws->managed_list=NULL; + + ws->dummywin=XCreateWindow(ioncore_g.dpy, par->win, + fp->g.x, fp->g.y, 1, 1, 0, + CopyFromParent, InputOnly, + CopyFromParent, 0, NULL); + if(ws->dummywin==None) + return FALSE; + + region_init(&ws->reg, par, fp); + region_register(&ws->reg); + + XSelectInput(ioncore_g.dpy, ws->dummywin, + FocusChangeMask|KeyPressMask|KeyReleaseMask| + ButtonPressMask|ButtonReleaseMask); + XSaveContext(ioncore_g.dpy, ws->dummywin, ioncore_g.win_context, + (XPointer)ws); + + ((WRegion*)ws)->flags|=REGION_GRAB_ON_PARENT; + + region_add_bindmap((WRegion*)ws, ioncore_group_bindmap); + + return TRUE; +} + + +WGroup *create_group(WWindow *par, const WFitParams *fp) +{ + CREATEOBJ_IMPL(WGroup, group, (p, par, fp)); +} + + +void group_deinit(WGroup *ws) +{ + WGroupIterTmp tmp; + WRegion *reg; + + if(ws->managed_stdisp!=NULL && ws->managed_stdisp->reg!=NULL){ + group_managed_remove(ws, ws->managed_stdisp->reg); + assert(ws->managed_stdisp==NULL); + } + + FOR_ALL_MANAGED_BY_GROUP(ws, reg, tmp){ + destroy_obj((Obj*)reg); + } + + assert(ws->managed_list==NULL); + + XDeleteContext(ioncore_g.dpy, ws->dummywin, ioncore_g.win_context); + XDestroyWindow(ioncore_g.dpy, ws->dummywin); + ws->dummywin=None; + + region_deinit(&ws->reg); +} + + + +bool group_rescue_clientwins(WGroup *ws, WPHolder *ph) +{ + WGroupIterTmp tmp; + + group_iter_init_nostdisp(&tmp, ws); + + return region_rescue_some_clientwins((WRegion*)ws, ph, + (WRegionIterator*)group_iter, + &tmp); +} + + +bool group_may_destroy(WGroup *ws) +{ + bool ret=group_essentially_empty(ws); + if(!ret) + warn(TR("Workspace not empty - refusing to destroy.")); + return ret; +} + + +static bool group_managed_may_destroy(WGroup *ws, WRegion *reg) +{ + return TRUE; +} + + +/*}}}*/ + + +/*{{{ attach */ + + +WStacking *group_do_add_managed(WGroup *ws, WRegion *reg, int level, + WSizePolicy szplcy) +{ + WStacking *st=NULL; + CALL_DYN_RET(st, WStacking*, group_do_add_managed, ws, + (ws, reg, level, szplcy)); + return st; +} + + +WStacking *group_do_add_managed_default(WGroup *ws, WRegion *reg, int level, + WSizePolicy szplcy) +{ + WStacking *st=NULL, *tmp=NULL; + Window bottom=None, top=None; + WStacking **stackingp=group_get_stackingp(ws); + WFrame *frame; + + if(stackingp==NULL) + return NULL; + + st=create_stacking(); + + if(st==NULL) + return NULL; + + if(!stacking_assoc(st, reg)){ + stacking_free(st); + return NULL; + } + + frame=OBJ_CAST(reg, WFrame); + if(frame!=NULL){ + WFrameMode m=frame_mode(frame); + if(m!=FRAME_MODE_FLOATING && m!=FRAME_MODE_TRANSIENT) + frame_set_mode(frame, FRAME_MODE_FLOATING); + } + + st->level=level; + st->szplcy=szplcy; + + LINK_ITEM_FIRST(tmp, st, next, prev); + stacking_weave(stackingp, &tmp, FALSE); + assert(tmp==NULL); + + LINK_ITEM(ws->managed_list, st, mgr_next, mgr_prev); + region_set_manager(reg, (WRegion*)ws); + + if(region_is_fully_mapped((WRegion*)ws)) + region_map(reg); + + return st; +} + + +static void geom_group_to_parent(WGroup *ws, const WRectangle *g, + WRectangle *wg) +{ + wg->x=g->x+REGION_GEOM(ws).x; + wg->y=g->y+REGION_GEOM(ws).y; + wg->w=maxof(1, g->w); + wg->h=maxof(1, g->h); +} + + +bool group_do_attach_final(WGroup *ws, + WRegion *reg, + const WGroupAttachParams *param) +{ + WStacking *st, *stabove=NULL; + WSizePolicy szplcy; + WFitParams fp; + WRectangle g; + uint level; + int weak; + bool sw; + + /* Fit */ + szplcy=(param->szplcy_set + ? param->szplcy + : (param->bottom + ? SIZEPOLICY_FULL_EXACT + : SIZEPOLICY_UNCONSTRAINED)); + + weak=(param->geom_weak_set + ? param->geom_weak + : (param->geom_set + ? 0 + : REGION_RQGEOM_WEAK_ALL)); + + if(param->geom_set) + geom_group_to_parent(ws, ¶m->geom, &g); + else + g=REGION_GEOM(reg); + + /* If the requested geometry does not overlap the workspaces's geometry, + * position request is never honoured. + */ + if((g.x+g.w<=REGION_GEOM(ws).x) || + (g.y+g.h<=REGION_GEOM(ws).y) || + (g.x>=REGION_GEOM(ws).x+REGION_GEOM(ws).w) || + (g.y>=REGION_GEOM(ws).y+REGION_GEOM(ws).h)){ + weak|=REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_X; + } + + if((weak&(REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_Y)) + ==(REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_Y) && + (szplcy==SIZEPOLICY_UNCONSTRAINED || + szplcy==SIZEPOLICY_FREE || + szplcy==SIZEPOLICY_FREE_GLUE /* without flags */)){ + group_calc_placement(ws, &g); + } + + fp.g=REGION_GEOM(ws); + fp.mode=REGION_FIT_EXACT; + + sizepolicy(&szplcy, reg, &g, weak, &fp); + + if(rectangle_compare(&fp.g, ®ION_GEOM(reg))!=RECTANGLE_SAME) + region_fitrep(reg, NULL, &fp); + + /* Stacking & add */ + if(param->stack_above!=NULL) + stabove=group_find_stacking(ws, param->stack_above); + + level=(stabove!=NULL + ? stabove->level + : (param->level_set + ? param->level + : STACKING_LEVEL_NORMAL)); + + st=group_do_add_managed(ws, reg, level, szplcy); + + if(st==NULL) + return FALSE; + + if(stabove!=NULL) + st->above=stabove; + + /* Misc. */ + if(param->bottom){ + ws->bottom=st; + + if(HAS_DYN(reg, region_manage_stdisp) && ws->managed_stdisp!=NULL){ + WMPlex *mplex=OBJ_CAST(REGION_MANAGER(ws), WMPlex); + if(mplex!=NULL){ /* should always hold */ + WMPlexSTDispInfo di; + WRegion *stdisp=NULL; + mplex_get_stdisp(mplex, &stdisp, &di); + if(stdisp!=NULL){ + assert(stdisp==ws->managed_stdisp->reg); + /* WARNING! Calls back to group code (managed_remove). */ + region_manage_stdisp(reg, stdisp, &di); + } + } + } + } + + sw=(param->switchto_set ? param->switchto : ioncore_g.switchto_new); + + if(sw || st->level>=STACKING_LEVEL_MODAL1){ + WStacking *stf=find_to_focus(ws, st, FALSE); + + if(stf==st){ + /* Ok, the new region can be focused */ + group_refocus_(ws, stf); + } + } + + return TRUE; +} + + +WRegion *group_do_attach(WGroup *ws, + /*const*/ WGroupAttachParams *param, + WRegionAttachData *data) +{ + WFitParams fp; + WWindow *par; + WRegion *reg; + + if(ws->bottom!=NULL && param->bottom){ + warn(TR("'bottom' already set.")); + return NULL; + } + + par=REGION_PARENT(ws); + assert(par!=NULL); + + if(param->geom_set){ + geom_group_to_parent(ws, ¶m->geom, &fp.g); + fp.mode=REGION_FIT_EXACT; + }else{ + fp.g=REGION_GEOM(ws); + fp.mode=REGION_FIT_BOUNDS|REGION_FIT_WHATEVER; + } + + return region_attach_helper((WRegion*) ws, par, &fp, + (WRegionDoAttachFn*)group_do_attach_final, + /*(const WRegionAttachParams*)*/param, data); + /* ^^^^ doesn't seem to work. */ +} + + +static void get_params(WGroup *ws, ExtlTab tab, WGroupAttachParams *par) +{ + int tmp; + char *tmps; + ExtlTab g; + + par->switchto_set=0; + par->level_set=0; + par->szplcy_set=0; + par->geom_set=0; + par->bottom=0; + + if(extl_table_gets_i(tab, "level", &tmp)){ + if(tmp>=0){ + par->level_set=STACKING_LEVEL_NORMAL; + par->level=tmp; + } + } + + if(extl_table_is_bool_set(tab, "bottom")){ + par->level=STACKING_LEVEL_BOTTOM; + par->level_set=1; + par->bottom=1; + } + + if(!par->level_set && extl_table_is_bool_set(tab, "modal")){ + par->level=STACKING_LEVEL_MODAL1; + par->level_set=1; + } + + if(extl_table_is_bool_set(tab, "switchto")) + par->switchto=1; + + if(extl_table_gets_i(tab, "sizepolicy", &tmp)){ + par->szplcy_set=1; + par->szplcy=tmp; + }else if(extl_table_gets_s(tab, "sizepolicy", &tmps)){ + if(string2sizepolicy(tmps, &par->szplcy)) + par->szplcy_set=1; + free(tmps); + } + + if(extl_table_gets_t(tab, "geom", &g)){ + int n=0; + + if(extl_table_gets_i(g, "x", &(par->geom.x))) + n++; + if(extl_table_gets_i(g, "y", &(par->geom.y))) + n++; + if(extl_table_gets_i(g, "w", &(par->geom.w))) + n++; + if(extl_table_gets_i(g, "h", &(par->geom.h))) + n++; + + if(n==4) + par->geom_set=1; + + extl_unref_table(g); + } +} + + + +/*EXTL_DOC + * Attach and reparent existing region \var{reg} to \var{ws}. + * The table \var{param} may contain the fields \var{index} and + * \var{switchto} that are interpreted as for \fnref{WMPlex.attach_new}. + */ +EXTL_EXPORT_MEMBER +WRegion *group_attach(WGroup *ws, WRegion *reg, ExtlTab param) +{ + WGroupAttachParams par=GROUPATTACHPARAMS_INIT; + WRegionAttachData data; + + if(reg==NULL) + return NULL; + + get_params(ws, param, &par); + + data.type=REGION_ATTACH_REPARENT; + data.u.reg=reg; + + return group_do_attach(ws, &par, &data); +} + + +/*EXTL_DOC + * Create a new region to be managed by \var{ws}. At least the following + * fields in \var{param} are understood: + * + * \begin{tabularx}{\linewidth}{lX} + * \tabhead{Field & Description} + * \var{type} & Class name (a string) of the object to be created. Mandatory. \\ + * \var{name} & Name of the object to be created (a string). Optional. \\ + * \var{switchto} & Should the region be switched to (boolean)? Optional. \\ + * \var{level} & Stacking level; default is 1. \\ + * \var{modal} & Make object modal; ignored if level is set. \\ + * \var{sizepolicy} & Size policy. \\ + * \end{tabularx} + * + * In addition parameters to the region to be created are passed in this + * same table. + */ +EXTL_EXPORT_MEMBER +WRegion *group_attach_new(WGroup *ws, ExtlTab param) +{ + WGroupAttachParams par=GROUPATTACHPARAMS_INIT; + WRegionAttachData data; + + get_params(ws, param, &par); + + data.type=REGION_ATTACH_LOAD; + data.u.tab=param; + + return group_do_attach(ws, &par, &data); +} + + +/*}}}*/ + + +/*{{{ Status display support */ + + +static int stdisp_szplcy(const WMPlexSTDispInfo *di, WRegion *stdisp) +{ + int pos=di->pos; + + if(di->fullsize){ + if(region_orientation(stdisp)==REGION_ORIENTATION_VERTICAL){ + if(pos==MPLEX_STDISP_TL || pos==MPLEX_STDISP_BL) + return SIZEPOLICY_STRETCH_LEFT; + else + return SIZEPOLICY_STRETCH_RIGHT; + }else{ + if(pos==MPLEX_STDISP_TL || pos==MPLEX_STDISP_TR) + return SIZEPOLICY_STRETCH_TOP; + else + return SIZEPOLICY_STRETCH_BOTTOM; + } + }else{ + if(pos==MPLEX_STDISP_TL) + return SIZEPOLICY_GRAVITY_NORTHWEST; + else if(pos==MPLEX_STDISP_BL) + return SIZEPOLICY_GRAVITY_SOUTHWEST; + else if(pos==MPLEX_STDISP_TR) + return SIZEPOLICY_GRAVITY_NORTHEAST; + else /*if(pos=MPLEX_STDISP_BR)*/ + return SIZEPOLICY_GRAVITY_SOUTHEAST; + } +} + + +void group_manage_stdisp(WGroup *ws, WRegion *stdisp, + const WMPlexSTDispInfo *di) +{ + WFitParams fp; + uint szplcy; + WRegion *b=(ws->bottom==NULL ? NULL : ws->bottom->reg); + + /* Check if 'bottom' wants to manage the stdisp. */ + if(b!=NULL + && !OBJ_IS_BEING_DESTROYED(b) + && HAS_DYN(b, region_manage_stdisp)){ + region_manage_stdisp(b, stdisp, di); + if(REGION_MANAGER(stdisp)==b) + return; + } + + /* No. */ + + szplcy=stdisp_szplcy(di, stdisp)|SIZEPOLICY_SHRUNK; + + if(ws->managed_stdisp!=NULL && ws->managed_stdisp->reg==stdisp){ + if(ws->managed_stdisp->szplcy==szplcy) + return; + ws->managed_stdisp->szplcy=szplcy; + }else{ + region_detach_manager(stdisp); + ws->managed_stdisp=group_do_add_managed(ws, stdisp, + STACKING_LEVEL_ON_TOP, + szplcy); + } + + fp.g=REGION_GEOM(ws); + sizepolicy(&ws->managed_stdisp->szplcy, stdisp, NULL, 0, &fp); + + region_fitrep(stdisp, NULL, &fp); +} + + +void group_managed_rqgeom(WGroup *ws, WRegion *reg, + const WRQGeomParams *rq, + WRectangle *geomret) +{ + WFitParams fp; + WStacking *st; + + st=group_find_stacking(ws, reg); + + if(st==NULL){ + fp.g=rq->geom; + fp.mode=REGION_FIT_EXACT; + }else{ + fp.g=REGION_GEOM(ws); + sizepolicy(&st->szplcy, reg, &rq->geom, rq->flags, &fp); + } + + if(geomret!=NULL) + *geomret=fp.g; + + if(!(rq->flags®ION_RQGEOM_TRYONLY)) + region_fitrep(reg, NULL, &fp); +} + + +void group_managed_rqgeom_absolute(WGroup *grp, WRegion *sub, + const WRQGeomParams *rq, + WRectangle *geomret) +{ + if(grp->bottom!=NULL && grp->bottom->reg==sub){ + region_rqgeom((WRegion*)grp, rq, geomret); + if(!(rq->flags®ION_RQGEOM_TRYONLY) && geomret!=NULL) + *geomret=REGION_GEOM(sub); + }else{ + WRQGeomParams rq2=*rq; + rq2.flags&=~REGION_RQGEOM_ABSOLUTE; + + region_managed_rqgeom((WRegion*)grp, sub, &rq2, geomret); + } +} + + +/*}}}*/ + + +/*{{{ Navigation */ + + +static WStacking *nxt(WGroup *ws, WStacking *st, bool wrap) +{ + return (st->mgr_next!=NULL + ? st->mgr_next + : (wrap ? ws->managed_list : NULL)); +} + + +static WStacking *prv(WGroup *ws, WStacking *st, bool wrap) +{ + return (st!=ws->managed_list + ? st->mgr_prev + : (wrap ? st->mgr_prev : NULL)); +} + + +typedef WStacking *NxtFn(WGroup *ws, WStacking *st, bool wrap); + + +static bool mapped_filt(WStacking *st, void *unused) +{ + return (st->reg!=NULL && REGION_IS_MAPPED(st->reg)); +} + + +static bool focusable(WGroup *ws, WStacking *st, uint min_level) +{ + return (st->reg!=NULL + && REGION_IS_MAPPED(st->reg) + && !(st->reg->flags®ION_SKIP_FOCUS) + && st->level>=min_level); +} + + +static WStacking *do_get_next(WGroup *ws, WStacking *sti, + NxtFn *fn, bool wrap, bool sti_ok) +{ + WStacking *st, *stacking; + uint min_level=0; + + stacking=group_get_stacking(ws); + + if(stacking!=NULL) + min_level=stacking_min_level(stacking, mapped_filt, NULL); + + st=sti; + while(1){ + st=fn(ws, st, wrap); + + if(st==NULL || st==sti) + break; + + if(focusable(ws, st, min_level)) + return st; + } + + if(sti_ok && focusable(ws, sti, min_level)) + return st; + + return NULL; +} + + +static WStacking *group_do_navi_first(WGroup *ws, WRegionNavi nh) +{ + WStacking *lst=ws->managed_list; + + if(lst==NULL) + return NULL; + + if(nh==REGION_NAVI_ANY && + ws->current_managed!=NULL && + ws->current_managed->reg!=NULL){ + return ws->current_managed; + } + + if(nh==REGION_NAVI_ANY || nh==REGION_NAVI_END || + nh==REGION_NAVI_BOTTOM || nh==REGION_NAVI_RIGHT){ + return do_get_next(ws, lst, prv, TRUE, TRUE); + }else{ + return do_get_next(ws, lst->mgr_prev, nxt, TRUE, TRUE); + } +} + + +static WRegion *group_navi_first(WGroup *ws, WRegionNavi nh, + WRegionNaviData *data) +{ + WStacking *st=group_do_navi_first(ws, nh); + + return region_navi_cont(&ws->reg, (st!=NULL ? st->reg : NULL), data); +} + + +static WStacking *group_do_navi_next(WGroup *ws, WStacking *st, + WRegionNavi nh, bool wrap) +{ + if(st==NULL) + return group_do_navi_first(ws, nh); + + if(nh==REGION_NAVI_ANY || nh==REGION_NAVI_END || + nh==REGION_NAVI_BOTTOM || nh==REGION_NAVI_RIGHT){ + return do_get_next(ws, st, nxt, wrap, FALSE); + }else{ + return do_get_next(ws, st, prv, wrap, FALSE); + } +} + +static WRegion *group_navi_next(WGroup *ws, WRegion *reg, + WRegionNavi nh, WRegionNaviData *data) +{ + WStacking *st=group_find_stacking(ws, reg); + + st=group_do_navi_next(ws, st, nh, FALSE); + + return region_navi_cont(&ws->reg, (st!=NULL ? st->reg : NULL), data); +} + + +/*}}}*/ + + +/*{{{ Stacking */ + + +/* + * Note: Managed objects are considered to be stacked separately from the + * group, slightly violating expectations. + */ + +void group_stacking(WGroup *ws, Window *bottomret, Window *topret) +{ + Window win=region_xwindow((WRegion*)ws); + + *bottomret=win; + *topret=win; +} + + +void group_restack(WGroup *ws, Window other, int mode) +{ + Window win; + + win=region_xwindow((WRegion*)ws); + if(win!=None){ + xwindow_restack(win, other, mode); + other=win; + mode=Above; + } +} + + +WStacking *group_find_stacking(WGroup *ws, WRegion *r) +{ + WStacking *st; + + if(r==NULL || REGION_MANAGER(r)!=(WRegion*)ws) + return NULL; + + return ioncore_find_stacking(r); +} + + +static WStacking *find_stacking_if_not_on_ws(WGroup *ws, Window w) +{ + WRegion *r=xwindow_region_of(w); + WStacking *st=NULL; + + while(r!=NULL){ + if(REGION_MANAGER(r)==(WRegion*)ws) + break; + st=group_find_stacking(ws, r); + if(st!=NULL) + break; + r=REGION_MANAGER(r); + } + + return st; +} + + +bool group_managed_rqorder(WGroup *grp, WRegion *reg, WRegionOrder order) +{ + WStacking **stackingp=group_get_stackingp(grp); + WStacking *st; + + if(stackingp==NULL || *stackingp==NULL) + return FALSE; + + st=group_find_stacking(grp, reg); + + if(st==NULL) + return FALSE; + + stacking_restack(stackingp, st, None, NULL, NULL, + (order!=REGION_ORDER_FRONT)); + + return TRUE; +} + + +/*}}}*/ + + +/*{{{ Misc. */ + + +/*EXTL_DOC + * Returns the 'bottom' of \var{ws}. + */ +EXTL_EXPORT_MEMBER +WRegion *group_bottom(WGroup *ws) +{ + return (ws->bottom!=NULL ? ws->bottom->reg : NULL); +} + + +/*EXTL_DOC + * Returns a list of regions managed by the workspace (frames, mostly). + */ +EXTL_SAFE +EXTL_EXPORT_MEMBER +ExtlTab group_managed_list(WGroup *ws) +{ + WGroupIterTmp tmp; + group_iter_init(&tmp, ws); + + return extl_obj_iterable_to_table((ObjIterator*)group_iter, &tmp); +} + + +WRegion* group_current(WGroup *ws) +{ + return (ws->current_managed!=NULL ? ws->current_managed->reg : NULL); +} + + +void group_size_hints(WGroup *ws, WSizeHints *hints_ret) +{ + if(ws->bottom==NULL || ws->bottom->reg==NULL){ + sizehints_clear(hints_ret); + }else{ + region_size_hints(ws->bottom->reg, hints_ret); + hints_ret->no_constrain=TRUE; + } +} + + +Window group_xwindow(const WGroup *ws) +{ + return ws->dummywin; +} + + +/*}}}*/ + + +/*{{{ Save/load */ + + +static ExtlTab group_get_configuration(WGroup *ws) +{ + ExtlTab tab, mgds, subtab, g; + WStacking *st; + WGroupIterTmp tmp; + WMPlex *par; + int n=0; + WRectangle tmpg; + + tab=region_get_base_configuration((WRegion*)ws); + + mgds=extl_create_table(); + + extl_table_sets_t(tab, "managed", mgds); + + /* TODO: stacking order messed up */ + + FOR_ALL_NODES_IN_GROUP(ws, st, tmp){ + if(st->reg==NULL) + continue; + + subtab=region_get_configuration(st->reg); + + if(subtab!=extl_table_none()){ + extl_table_sets_i(subtab, "sizepolicy", st->szplcy); + extl_table_sets_i(subtab, "level", st->level); + + tmpg=REGION_GEOM(st->reg); + tmpg.x-=REGION_GEOM(ws).x; + tmpg.y-=REGION_GEOM(ws).y; + + g=extl_table_from_rectangle(&tmpg); + extl_table_sets_t(subtab, "geom", g); + extl_unref_table(g); + + if(ws->bottom==st) + extl_table_sets_b(subtab, "bottom", TRUE); + + extl_table_seti_t(mgds, ++n, subtab); + extl_unref_table(subtab); + } + } + + extl_unref_table(mgds); + + return tab; +} + + +void group_do_load(WGroup *ws, ExtlTab tab) +{ + ExtlTab substab, subtab; + int i, n; + + if(extl_table_gets_t(tab, "managed", &substab)){ + n=extl_table_get_n(substab); + for(i=1; i<=n; i++){ + if(extl_table_geti_t(substab, i, &subtab)){ + group_attach_new(ws, subtab); + extl_unref_table(subtab); + } + } + + extl_unref_table(substab); + } + + ws->bottom_last_close=extl_table_is_bool_set(tab, "bottom_last_close"); +} + + +WRegion *group_load(WWindow *par, const WFitParams *fp, ExtlTab tab) +{ + WGroup *ws; + + ws=create_group(par, fp); + + if(ws==NULL) + return NULL; + + group_do_load(ws, tab); + + return (WRegion*)ws; +} + + +/*}}}*/ + + +/*{{{ Dynamic function table and class implementation */ + + +static DynFunTab group_dynfuntab[]={ + {(DynFun*)region_fitrep, + (DynFun*)group_fitrep}, + + {region_map, + group_map}, + + {region_unmap, + group_unmap}, + + {(DynFun*)region_managed_prepare_focus, + (DynFun*)group_managed_prepare_focus}, + + {region_do_set_focus, + group_do_set_focus}, + + {region_managed_activated, + group_managed_activated}, + + {region_managed_remove, + group_managed_remove}, + + {(DynFun*)region_get_configuration, + (DynFun*)group_get_configuration}, + + {(DynFun*)region_may_destroy, + (DynFun*)group_may_destroy}, + + {(DynFun*)region_managed_may_destroy, + (DynFun*)group_managed_may_destroy}, + + {(DynFun*)region_current, + (DynFun*)group_current}, + + {(DynFun*)region_rescue_clientwins, + (DynFun*)group_rescue_clientwins}, + + {region_restack, + group_restack}, + + {region_stacking, + group_stacking}, + + {(DynFun*)region_managed_get_pholder, + (DynFun*)group_managed_get_pholder}, + + {region_managed_rqgeom, + group_managed_rqgeom}, + + {region_managed_rqgeom_absolute, + group_managed_rqgeom_absolute}, + + {(DynFun*)group_do_add_managed, + (DynFun*)group_do_add_managed_default}, + + {region_size_hints, + group_size_hints}, + + {(DynFun*)region_xwindow, + (DynFun*)group_xwindow}, + + {(DynFun*)region_navi_first, + (DynFun*)group_navi_first}, + + {(DynFun*)region_navi_next, + (DynFun*)group_navi_next}, + + {(DynFun*)region_managed_rqorder, + (DynFun*)group_managed_rqorder}, + + END_DYNFUNTAB +}; + + +EXTL_EXPORT +IMPLCLASS(WGroup, WRegion, group_deinit, group_dynfuntab); + + +/*}}}*/ + diff --git a/ioncore/group.h b/ioncore/group.h new file mode 100644 index 0000000..0de372a --- /dev/null +++ b/ioncore/group.h @@ -0,0 +1,133 @@ +/* + * ion/ioncore/group.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_GROUP_H +#define ION_IONCORE_GROUP_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +INTRSTRUCT(WGroupAttachParams); + +typedef WRegionSimpleCreateFn WGroupMkFrameFn; + + +DECLSTRUCT(WGroupAttachParams){ + uint level_set:1; + uint szplcy_set:1; + uint geom_set:1; + uint geom_weak_set:1; + uint switchto_set:1; + + uint switchto:1; + uint modal:1; + uint bottom:1; + + int geom_weak; + WRectangle geom; + + uint level; + WSizePolicy szplcy; + WRegion *stack_above; +}; + +#define GROUPATTACHPARAMS_INIT \ + {0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, 0, 0, NULL} + + +DECLCLASS(WGroup){ + WRegion reg; + WStacking *managed_list; + WStacking *managed_stdisp; + WStacking *current_managed; + WStacking *bottom; + Window dummywin; + uint bottom_last_close:1; +}; + + +extern bool group_init(WGroup *grp, WWindow *parent, const WFitParams *fp); +extern WGroup *create_group(WWindow *parent, const WFitParams *fp); +extern void group_deinit(WGroup *grp); + +extern WRegion *group_load(WWindow *par, const WFitParams *fp, + ExtlTab tab); +extern void group_do_load(WGroup *ws, ExtlTab tab); + +extern WRegion* group_current(WGroup *group); + +DYNFUN WStacking *group_do_add_managed(WGroup *ws, WRegion *reg, + int level, WSizePolicy szplcy); +extern WStacking *group_do_add_managed_default(WGroup *ws, WRegion *reg, + int level, WSizePolicy szplcy); + +extern WRegion *group_do_attach(WGroup *ws, + WGroupAttachParams *param, + WRegionAttachData *data); +extern bool group_do_attach_final(WGroup *ws, + WRegion *reg, + const WGroupAttachParams *param); + +extern WRegion *group_attach(WGroup *ws, WRegion *reg, ExtlTab param); +extern WRegion *group_attach_new(WGroup *ws, ExtlTab param); + +extern void group_manage_stdisp(WGroup *ws, WRegion *stdisp, + const WMPlexSTDispInfo *di); + +extern void group_managed_remove(WGroup *ws, WRegion *reg); + +extern WRegion *group_bottom(WGroup *ws); + +extern bool group_rescue_clientwins(WGroup *ws, WPHolder *ph); + +extern bool group_rqclose(WGroup *ws); +extern bool group_rqclose_relocate(WGroup *ws); + +extern bool group_managed_rqorder(WGroup *grp, WRegion *sub, + WRegionOrder order); + +extern WStacking *group_find_stacking(WGroup *ws, WRegion *r); +extern WStacking *group_find_to_focus(WGroup *ws, WStacking *to_try); + +typedef WStackingFilter WGroupIterFilter; +typedef WStackingIterTmp WGroupIterTmp; + +extern void group_iter_init(WGroupIterTmp *tmp, WGroup *ws); +extern WRegion *group_iter(WGroupIterTmp *tmp); +extern WStacking *group_iter_nodes(WGroupIterTmp *tmp); + +extern WStacking *group_get_stacking(WGroup *ws); +extern WStacking **group_get_stackingp(WGroup *ws); + +#define FOR_ALL_MANAGED_BY_GROUP(WS, VAR, TMP) \ + for(group_iter_init(&(TMP), WS), \ + VAR=group_iter(&(TMP)); \ + VAR!=NULL; \ + VAR=group_iter(&(TMP))) + +#define FOR_ALL_NODES_IN_GROUP(WS, VAR, TMP) \ + for(group_iter_init(&(TMP), WS), \ + VAR=group_iter_nodes(&(TMP)); \ + VAR!=NULL; \ + VAR=group_iter_nodes(&(TMP))) + +extern WGroupIterTmp group_iter_default_tmp; + + +#endif /* ION_IONCORE_GROUP_H */ diff --git a/ioncore/groupedpholder.c b/ioncore/groupedpholder.c new file mode 100644 index 0000000..ac3b692 --- /dev/null +++ b/ioncore/groupedpholder.c @@ -0,0 +1,182 @@ +/* + * ion/ioncore/groupedpholder.c + * + * Copyright (c) Tuomo Valkonen 2005-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include + +#include "group.h" +#include "group-cw.h" +#include "groupedpholder.h" + + +/*{{{ Init/deinit */ + + +bool groupedpholder_init(WGroupedPHolder *ph, WPHolder *cont) +{ + assert(cont!=NULL); + + pholder_init(&(ph->ph)); + + ph->cont=cont; + + return TRUE; +} + + +WGroupedPHolder *create_groupedpholder(WPHolder *cont) +{ + CREATEOBJ_IMPL(WGroupedPHolder, groupedpholder, (p, cont)); +} + + +void groupedpholder_deinit(WGroupedPHolder *ph) +{ + if(ph->cont!=NULL){ + destroy_obj((Obj*)ph->cont); + ph->cont=NULL; + } + + pholder_deinit(&(ph->ph)); +} + + +/*}}}*/ + + +/*{{{ Attach */ + + +static bool grouped_do_attach_final(WGroupCW *cwg, + WRegion *reg, + WGroupAttachParams *param) +{ + if(!param->geom_set){ + /* Comm. hack */ + REGION_GEOM(cwg)=REGION_GEOM(reg); + } + + param->geom_set=1; + param->geom.x=0; + param->geom.y=0; + param->geom.w=REGION_GEOM(reg).w; + param->geom.h=REGION_GEOM(reg).h; + param->szplcy=SIZEPOLICY_FULL_EXACT; + param->szplcy_set=TRUE; + + return group_do_attach_final(&cwg->grp, reg, param); +} + + +WRegion *grouped_handler(WWindow *par, + const WFitParams *fp, + void *frp_) +{ + WRegionAttachData *data=(WRegionAttachData*)frp_; + WGroupAttachParams param=GROUPATTACHPARAMS_INIT; + WGroupCW *cwg; + WRegion *reg; + WStacking *st; + + cwg=create_groupcw(par, fp); + + if(cwg==NULL) + return NULL; + + param.level_set=1; + param.level=STACKING_LEVEL_BOTTOM; + param.switchto_set=1; + param.switchto=1; + param.bottom=1; + + if(!(fp->mode®ION_FIT_WHATEVER)){ + /* Comm. hack */ + param.geom_set=TRUE; + } + + reg=region_attach_helper((WRegion*)cwg, par, fp, + (WRegionDoAttachFn*)grouped_do_attach_final, + ¶m, data); + + if(reg==NULL){ + destroy_obj((Obj*)cwg); + return NULL; + } + + return (WRegion*)cwg; +} + + +WRegion *groupedpholder_do_attach(WGroupedPHolder *ph, int flags, + WRegionAttachData *data) +{ + WRegionAttachData data2; + + if(ph->cont==NULL) + return FALSE; + + data2.type=REGION_ATTACH_NEW; + data2.u.n.fn=grouped_handler; + data2.u.n.param=data; + + return pholder_attach_(ph->cont, flags, &data2); +} + + +/*}}}*/ + + +/*{{{ Other dynfuns */ + + +bool groupedpholder_do_goto(WGroupedPHolder *ph) +{ + if(ph->cont!=NULL) + return pholder_goto(ph->cont); + + return FALSE; +} + + +WRegion *groupedpholder_do_target(WGroupedPHolder *ph) +{ + if(ph->cont!=NULL) + return pholder_target(ph->cont); + + return NULL; +} + + +/*}}}*/ + + +/*{{{ Class information */ + + +static DynFunTab groupedpholder_dynfuntab[]={ + {(DynFun*)pholder_do_attach, + (DynFun*)groupedpholder_do_attach}, + + {(DynFun*)pholder_do_goto, + (DynFun*)groupedpholder_do_goto}, + + {(DynFun*)pholder_do_target, + (DynFun*)groupedpholder_do_target}, + + END_DYNFUNTAB +}; + +IMPLCLASS(WGroupedPHolder, WPHolder, groupedpholder_deinit, + groupedpholder_dynfuntab); + + +/*}}}*/ + diff --git a/ioncore/groupedpholder.h b/ioncore/groupedpholder.h new file mode 100644 index 0000000..4feb699 --- /dev/null +++ b/ioncore/groupedpholder.h @@ -0,0 +1,39 @@ +/* + * ion/ioncore/groupedpholder.h + * + * Copyright (c) Tuomo Valkonen 2005-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_GROUPEDPHOLDER_H +#define ION_IONCORE_GROUPEDPHOLDER_H + +#include "common.h" +#include "pholder.h" +#include "attach.h" + +INTRCLASS(WGroupedPHolder); + +DECLCLASS(WGroupedPHolder){ + WPHolder ph; + WPHolder *cont; +}; + +extern WGroupedPHolder *create_groupedpholder(WPHolder *cont); + +extern bool groupedpholder_init(WGroupedPHolder *ph, WPHolder *cont); + +extern void groupedpholder_deinit(WGroupedPHolder *ph); + +extern bool groupedpholder_do_goto(WGroupedPHolder *ph); + +extern WRegion *groupedpholder_do_target(WGroupedPHolder *ph); + +extern WRegion *groupedpholder_do_attach(WGroupedPHolder *ph, int flags, + WRegionAttachData *data); + +#endif /* ION_IONCORE_GROUPEDPHOLDER_H */ diff --git a/ioncore/grouppholder.c b/ioncore/grouppholder.c new file mode 100644 index 0000000..d92c5b4 --- /dev/null +++ b/ioncore/grouppholder.c @@ -0,0 +1,199 @@ +/* + * ion/ioncore/grouppholder.c + * + * Copyright (c) Tuomo Valkonen 2005-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include + +#include +#include "group.h" +#include "grouppholder.h" + + +static void group_watch_handler(Watch *watch, Obj *ws); + + +/*{{{ Init/deinit */ + + +static void group_watch_handler(Watch *watch, Obj *ws) +{ + WGroupPHolder *ph=FIELD_TO_STRUCT(WGroupPHolder, + group_watch, watch); + pholder_redirect(&(ph->ph), (WRegion*)ws); +} + + +static WGroupAttachParams dummy_param=GROUPATTACHPARAMS_INIT; + + +bool grouppholder_init(WGroupPHolder *ph, WGroup *ws, + const WStacking *st, + const WGroupAttachParams *param) +{ + pholder_init(&(ph->ph)); + + watch_init(&(ph->group_watch)); + watch_init(&(ph->stack_above_watch)); + + if(ws!=NULL){ + if(!watch_setup(&(ph->group_watch), (Obj*)ws, + group_watch_handler)){ + pholder_deinit(&(ph->ph)); + return FALSE; + } + } + + if(param==NULL) + param=&dummy_param; + + if(st!=NULL){ + /* TODO? Just link to the stacking structure to remember + * stacking order? + */ + + ph->param.szplcy_set=TRUE; + ph->param.szplcy=st->szplcy; + ph->param.level_set=TRUE; + ph->param.level=st->level; + + if(st->reg!=NULL){ + ph->param.geom_set=TRUE; + ph->param.geom=REGION_GEOM(st->reg); + } + + if(st->above!=NULL && st->above->reg!=NULL) + ph->param.stack_above=st->above->reg; + + ph->param.modal=FALSE; + ph->param.bottom=(st==ws->bottom); + /*ph->passive=FALSE;*/ + }else{ + ph->param=*param; + } + + ph->param.switchto_set=FALSE; + + if(ph->param.stack_above!=NULL){ + /* We must move stack_above pointer into a Watch. */ + watch_setup(&(ph->stack_above_watch), + (Obj*)ph->param.stack_above, NULL); + ph->param.stack_above=NULL; + } + + return TRUE; +} + + +WGroupPHolder *create_grouppholder(WGroup *ws, + const WStacking *st, + const WGroupAttachParams *param) +{ + CREATEOBJ_IMPL(WGroupPHolder, grouppholder, (p, ws, st, param)); +} + + +void grouppholder_deinit(WGroupPHolder *ph) +{ + watch_reset(&(ph->group_watch)); + watch_reset(&(ph->stack_above_watch)); + pholder_deinit(&(ph->ph)); +} + + +/*}}}*/ + + +/*{{{ Dynfuns */ + + +WRegion *grouppholder_do_attach(WGroupPHolder *ph, int flags, + WRegionAttachData *data) +{ + WGroup *ws=(WGroup*)ph->group_watch.obj; + WRegion *reg; + + if(ws==NULL) + return FALSE; + + ph->param.switchto_set=1; + ph->param.switchto=(flags&PHOLDER_ATTACH_SWITCHTO ? 1 : 0); + + /* Get stack_above from Watch. */ + ph->param.stack_above=(WRegion*)ph->stack_above_watch.obj; + + reg=group_do_attach(ws, &ph->param, data); + + ph->param.stack_above=NULL; + + return reg; +} + + +bool grouppholder_do_goto(WGroupPHolder *ph) +{ + WGroup *ws=(WGroup*)ph->group_watch.obj; + + if(ws!=NULL) + return region_goto((WRegion*)ws); + + return FALSE; +} + + +WRegion *grouppholder_do_target(WGroupPHolder *ph) +{ + return (WRegion*)ph->group_watch.obj; +} + + +/*}}}*/ + + +/*{{{ WGroup stuff */ + + +WGroupPHolder *group_managed_get_pholder(WGroup *ws, WRegion *mgd) +{ + WStacking *st=group_find_stacking(ws, mgd); + + if(mgd==NULL) + return NULL; + else + return create_grouppholder(ws, st, NULL); +} + + +/*}}}*/ + + +/*{{{ Class information */ + + +static DynFunTab grouppholder_dynfuntab[]={ + {(DynFun*)pholder_do_attach, + (DynFun*)grouppholder_do_attach}, + + {(DynFun*)pholder_do_goto, + (DynFun*)grouppholder_do_goto}, + + {(DynFun*)pholder_do_target, + (DynFun*)grouppholder_do_target}, + + END_DYNFUNTAB +}; + +IMPLCLASS(WGroupPHolder, WPHolder, grouppholder_deinit, + grouppholder_dynfuntab); + + +/*}}}*/ + diff --git a/ioncore/grouppholder.h b/ioncore/grouppholder.h new file mode 100644 index 0000000..4b0208a --- /dev/null +++ b/ioncore/grouppholder.h @@ -0,0 +1,49 @@ +/* + * ion/ioncore/grouppholder.h + * + * Copyright (c) Tuomo Valkonen 2005-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_GROUPPHOLDER_H +#define ION_IONCORE_GROUPPHOLDER_H + +#include +#include +#include "group.h" + +INTRCLASS(WGroupPHolder); + +DECLCLASS(WGroupPHolder){ + WPHolder ph; + Watch group_watch; + Watch stack_above_watch; + WGroupAttachParams param; +}; + +extern WGroupPHolder *create_grouppholder(WGroup *group, + const WStacking *either_st, + const WGroupAttachParams *or_param); + +extern bool grouppholder_init(WGroupPHolder *ph, + WGroup *group, + const WStacking *either_st, + const WGroupAttachParams *or_param); + +extern void grouppholder_deinit(WGroupPHolder *ph); + +extern bool grouppholder_do_goto(WGroupPHolder *ph); + +extern WRegion *grouppholder_do_target(WGroupPHolder *ph); + +extern WRegion *grouppholder_do_attach(WGroupPHolder *ph, int flags, + WRegionAttachData *data); + +extern WGroupPHolder *group_managed_get_pholder(WGroup *group, + WRegion *mgd); + +#endif /* ION_IONCORE_GROUPPHOLDER_H */ diff --git a/ioncore/infowin.c b/ioncore/infowin.c new file mode 100644 index 0000000..95622b4 --- /dev/null +++ b/ioncore/infowin.c @@ -0,0 +1,275 @@ +/* + * ion/ioncore/infowin.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include + +#include +#include "common.h" +#include "global.h" +#include "window.h" +#include "infowin.h" +#include "resize.h" +#include "gr.h" +#include "event.h" + + +/*{{{ Init/deinit */ + + +bool infowin_init(WInfoWin *p, WWindow *parent, const WFitParams *fp, + const char *style) +{ + XSetWindowAttributes attr; + + if(!window_init(&(p->wwin), parent, fp)) + return FALSE; + + p->buffer=ALLOC_N(char, INFOWIN_BUFFER_LEN); + if(p->buffer==NULL) + goto fail; + p->buffer[0]='\0'; + + if(style==NULL) + p->style=scopy("*"); + else + p->style=scopy(style); + if(p->style==NULL) + goto fail2; + + p->brush=NULL; + p->attr=NULL; + + infowin_updategr(p); + + if(p->brush==NULL) + goto fail3; + + p->wwin.region.flags|=REGION_SKIP_FOCUS; + + /* Enable save unders */ + attr.save_under=True; + XChangeWindowAttributes(ioncore_g.dpy, p->wwin.win, CWSaveUnder, &attr); + + window_select_input(&(p->wwin), IONCORE_EVENTMASK_NORMAL); + + return TRUE; + +fail3: + free(p->style); +fail2: + free(p->buffer); +fail: + window_deinit(&(p->wwin)); + return FALSE; +} + + +WInfoWin *create_infowin(WWindow *parent, const WFitParams *fp, + const char *style) +{ + CREATEOBJ_IMPL(WInfoWin, infowin, (p, parent, fp, style)); +} + + +void infowin_deinit(WInfoWin *p) +{ + if(p->buffer!=NULL){ + free(p->buffer); + p->buffer=NULL; + } + + if(p->attr!=NULL){ + free(p->attr); + p->attr=NULL; + } + + if(p->style!=NULL){ + free(p->style); + p->style=NULL; + } + + if(p->brush!=NULL){ + grbrush_release(p->brush); + p->brush=NULL; + } + + window_deinit(&(p->wwin)); +} + + +/*}}}*/ + + +/*{{{ Drawing and geometry */ + + +void infowin_draw(WInfoWin *p, bool complete) +{ + WRectangle g; + + if(p->brush==NULL) + return; + + g.x=0; + g.y=0; + g.w=REGION_GEOM(p).w; + g.h=REGION_GEOM(p).h; + + grbrush_begin(p->brush, &g, GRBRUSH_NO_CLEAR_OK); + grbrush_draw_textbox(p->brush, &g, p->buffer, p->attr, TRUE); + grbrush_end(p->brush); +} + + +void infowin_updategr(WInfoWin *p) +{ + GrBrush *nbrush; + + assert(p->style!=NULL); + + nbrush=gr_get_brush(p->wwin.win, + region_rootwin_of((WRegion*)p), + p->style); + if(nbrush==NULL) + return; + + if(p->brush!=NULL) + grbrush_release(p->brush); + + p->brush=nbrush; + + window_draw(&(p->wwin), TRUE); +} + + + +/*}}}*/ + + +/*{{{ Content-setting */ + + +bool infowin_set_attr2(WInfoWin *p, const char *attr1, const char *attr2) +{ + char *p2=NULL; + + if(attr1!=NULL){ + if(attr2==NULL) + p2=scopy(attr1); + else + libtu_asprintf(&p2, "%s-%s", attr1, attr2); + if(p2==NULL) + return FALSE; + } + + if(p->attr) + free(p->attr); + + p->attr=p2; + + return TRUE; +} + + +static void infowin_do_set_text(WInfoWin *p, const char *str) +{ + strncpy(INFOWIN_BUFFER(p), str, INFOWIN_BUFFER_LEN); + INFOWIN_BUFFER(p)[INFOWIN_BUFFER_LEN-1]='\0'; +} + + +static void infowin_resize(WInfoWin *p) +{ + WRQGeomParams rq=RQGEOMPARAMS_INIT; + const char *str=INFOWIN_BUFFER(p); + GrBorderWidths bdw; + GrFontExtents fnte; + + rq.flags=REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_Y; + + rq.geom.x=REGION_GEOM(p).x; + rq.geom.y=REGION_GEOM(p).y; + + grbrush_get_border_widths(p->brush, &bdw); + grbrush_get_font_extents(p->brush, &fnte); + + rq.geom.w=bdw.left+bdw.right; + rq.geom.w+=grbrush_get_text_width(p->brush, str, strlen(str)); + rq.geom.h=fnte.max_height+bdw.top+bdw.bottom; + + if(rectangle_compare(&rq.geom, ®ION_GEOM(p))!=RECTANGLE_SAME) + region_rqgeom((WRegion*)p, &rq, NULL); +} + + +/*EXTL_DOC + * Set contents of the info window. + */ +EXTL_EXPORT_MEMBER +void infowin_set_text(WInfoWin *p, const char *str) +{ + infowin_do_set_text(p, str); + + infowin_resize(p); + + /* sometimes unnecessary */ + window_draw((WWindow*)p, TRUE); +} + + +/*}}}*/ + + +/*{{{ Load */ + + +WRegion *infowin_load(WWindow *par, const WFitParams *fp, ExtlTab tab) +{ + char *style=NULL, *text=NULL; + WInfoWin *p; + + extl_table_gets_s(tab, "style", &style); + + p=create_infowin(par, fp, style); + + free(style); + + if(p==NULL) + return NULL; + + if(extl_table_gets_s(tab, "text", &text)){ + infowin_do_set_text(p, text); + free(text); + } + + return (WRegion*)p; +} + + +/*}}}*/ + + +/*{{{ Dynamic function table and class implementation */ + + +static DynFunTab infowin_dynfuntab[]={ + {window_draw, infowin_draw}, + {region_updategr, infowin_updategr}, + END_DYNFUNTAB +}; + + +EXTL_EXPORT +IMPLCLASS(WInfoWin, WWindow, infowin_deinit, infowin_dynfuntab); + + +/*}}}*/ + diff --git a/ioncore/infowin.h b/ioncore/infowin.h new file mode 100644 index 0000000..c57dd01 --- /dev/null +++ b/ioncore/infowin.h @@ -0,0 +1,47 @@ +/* + * ion/ioncore/infowin.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_INFOWIN_H +#define ION_IONCORE_INFOWIN_H + +#include "common.h" +#include "window.h" +#include "gr.h" +#include "rectangle.h" + +#define INFOWIN_BUFFER_LEN 256 + +DECLCLASS(WInfoWin){ + WWindow wwin; + GrBrush *brush; + char *buffer; + char *attr; + char *style; +}; + +#define INFOWIN_BRUSH(INFOWIN) ((INFOWIN)->brush) +#define INFOWIN_BUFFER(INFOWIN) ((INFOWIN)->buffer) + +extern bool infowin_init(WInfoWin *p, WWindow *parent, const WFitParams *fp, + const char *style); +extern WInfoWin *create_infowin(WWindow *parent, const WFitParams *fp, + const char *style); + +extern void infowin_deinit(WInfoWin *p); + +extern void infowin_set_text(WInfoWin *p, const char *s); +extern bool infowin_set_attr2(WInfoWin *p, const char *a1, const char *a2); + +extern WRegion *infowin_load(WWindow *par, const WFitParams *fp, ExtlTab tab); + +extern void infowin_updategr(WInfoWin *p); + +#endif /* ION_IONCORE_INFOWIN_H */ diff --git a/ioncore/ioncore.c b/ioncore/ioncore.c new file mode 100644 index 0000000..5c88803 --- /dev/null +++ b/ioncore/ioncore.c @@ -0,0 +1,662 @@ +/* + * ion/ioncore/ioncore.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef CF_NO_LOCALE +#include +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "rootwin.h" +#include "event.h" +#include "cursor.h" +#include "global.h" +#include "modules.h" +#include "eventh.h" +#include "ioncore.h" +#include "manage.h" +#include "conf.h" +#include "binding.h" +#include "bindmaps.h" +#include "strings.h" +#include "gr.h" +#include "xic.h" +#include "netwm.h" +#include "focus.h" +#include "frame.h" +#include "saveload.h" +#include "infowin.h" +#include "activity.h" +#include "group-cw.h" +#include "group-ws.h" +#include "llist.h" + +#include "../version.h" +#include "exports.h" + + +/*{{{ Variables */ + + +WGlobal ioncore_g; + +static const char *progname="ion"; + +static const char ioncore_copy[]= + "Ion " ION_VERSION ", copyright (c) Tuomo Valkonen 1999-2006."; + +static const char ioncore_license[]=DUMMY_TR( + "This program is free software; you can redistribute it and/or\n" + "modify it under the terms of the GNU Lesser General Public\n" + "License as published by the Free Software Foundation; either\n" + "version 2.1 of the License, or (at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n" + "Lesser General Public License for more details.\n"); + +static const char *ioncore_about=NULL; + +WHook *ioncore_post_layout_setup_hook=NULL; +WHook *ioncore_snapshot_hook=NULL; +WHook *ioncore_deinit_hook=NULL; + + +/*}}}*/ + + +/*{{{ warn_nolog */ + + +void ioncore_warn_nolog(const char *str) +{ + fprintf(stderr, "%s: %s\n", prog_execname(), str); +} + + +/*}}}*/ + + +/*{{{ init_locale */ + + +#ifndef CF_NO_LOCALE + + +static bool check_encoding() +{ + int i; + char chs[8]=" "; + wchar_t wc; + const char *langi, *ctype, *a, *b; + bool enc_check_ok=FALSE; + + langi=nl_langinfo(CODESET); + ctype=setlocale(LC_CTYPE, NULL); + + if(langi==NULL || ctype==NULL) + goto integr_err; + + if(strcmp(ctype, "C")==0 || strcmp(ctype, "POSIX")==0) + return TRUE; + + /* Compare encodings case-insensitively, ignoring dashes (-) */ + a=langi; + b=strchr(ctype, '.'); + if(b!=NULL){ + b=b+1; + while(1){ + if(*a=='-'){ + a++; + continue; + } + if(*b=='-'){ + b++; + continue; + } + if(*b=='\0' || *b=='@'){ + enc_check_ok=(*a=='\0'); + break; + } + if(*a=='\0' || tolower(*a)!=tolower(*b)) + break; + a++; + b++; + } + if(!enc_check_ok) + goto integr_err; + }else{ + ioncore_warn_nolog(TR("No encoding given in LC_CTYPE.")); + } + + if(strcasecmp(langi, "UTF-8")==0 || strcasecmp(langi, "UTF8")==0){ + ioncore_g.enc_sb=FALSE; + ioncore_g.enc_utf8=TRUE; + ioncore_g.use_mb=TRUE; + return TRUE; + } + + for(i=0; i<256; i++){ + chs[0]=i; + if(mbtowc(&wc, chs, 8)==-1){ + /* Doesn't look like a single-byte encoding. */ + break; + } + + } + + if(i==256){ + /* Seems like a single-byte encoding... */ + ioncore_g.use_mb=TRUE; + return TRUE; + } + + if(mbtowc(NULL, NULL, 0)!=0){ + warn("Statefull encodings are unsupported."); + return FALSE; + } + + ioncore_g.enc_sb=FALSE; + ioncore_g.use_mb=TRUE; + + return TRUE; + +integr_err: + warn("Cannot verify locale encoding setting integrity " + "(LC_CTYPE=%s, nl_langinfo(CODESET)=%s). " + "The LC_CTYPE environment variable should be of the form " + "language_REGION.encoding (e.g. en_GB.UTF-8), and encoding " + "should match the nl_langinfo value above.", ctype, langi); + return FALSE; +} + + +static bool init_locale() +{ + const char *p; + + p=setlocale(LC_ALL, ""); + + if(p==NULL){ + warn("setlocale() call failed."); + return FALSE; + } + + /*if(strcmp(p, "C")==0 || strcmp(p, "POSIX")==0) + return TRUE;*/ + + if(!XSupportsLocale()){ + warn("XSupportsLocale() failed."); + }else{ + if(check_encoding()) + return TRUE; + } + + warn("Reverting locale settings to \"C\"."); + + if(setlocale(LC_ALL, "C")==NULL) + warn("setlocale() call failed."); + + return FALSE; +} + +#define TEXTDOMAIN "ion3" + +static bool init_messages(const char *localedir) +{ + if(bindtextdomain(TEXTDOMAIN, localedir)==NULL){ + warn_err_obj("bindtextdomain"); + return FALSE; + }else if(textdomain(TEXTDOMAIN)==NULL){ + warn_err_obj("textdomain"); + return FALSE; + } + return TRUE; +} + + +#endif + + +/*}}}*/ + + +/*{{{ ioncore_init */ + + +#define INIT_HOOK_(NM) \ + NM=mainloop_register_hook(#NM, create_hook()); \ + if(NM==NULL) return FALSE; + +#define INIT_HOOK(NM, DFLT) \ + INIT_HOOK_(NM) \ + if(!hook_add(NM, (void (*)())DFLT)) return FALSE; + + +static bool init_hooks() +{ + INIT_HOOK_(ioncore_post_layout_setup_hook); + INIT_HOOK_(ioncore_snapshot_hook); + INIT_HOOK_(ioncore_deinit_hook); + INIT_HOOK_(screen_managed_changed_hook); + INIT_HOOK_(frame_managed_changed_hook); + INIT_HOOK_(region_activated_hook); + INIT_HOOK_(region_inactivated_hook); + INIT_HOOK_(clientwin_mapped_hook); + INIT_HOOK_(clientwin_unmapped_hook); + INIT_HOOK_(clientwin_property_change_hook); + INIT_HOOK_(region_notify_hook); + + INIT_HOOK(clientwin_do_manage_alt, clientwin_do_manage_default); + INIT_HOOK(ioncore_handle_event_alt, ioncore_handle_event); + INIT_HOOK(region_do_warp_alt, region_do_warp_default); + + mainloop_sigchld_hook=mainloop_register_hook("ioncore_sigchld_hook", + create_hook()); + mainloop_sigusr2_hook=mainloop_register_hook("ioncore_sigusr2_hook", + create_hook()); + + return TRUE; +} + + +static bool register_classes() +{ + int fail=0; + + fail|=!ioncore_register_regclass(&CLASSDESCR(WClientWin), + (WRegionLoadCreateFn*)clientwin_load); + fail|=!ioncore_register_regclass(&CLASSDESCR(WMPlex), + (WRegionLoadCreateFn*)mplex_load); + fail|=!ioncore_register_regclass(&CLASSDESCR(WFrame), + (WRegionLoadCreateFn*)frame_load); + fail|=!ioncore_register_regclass(&CLASSDESCR(WInfoWin), + (WRegionLoadCreateFn*)infowin_load); + fail|=!ioncore_register_regclass(&CLASSDESCR(WGroupCW), + (WRegionLoadCreateFn*)groupcw_load); + fail|=!ioncore_register_regclass(&CLASSDESCR(WGroupWS), + (WRegionLoadCreateFn*)groupws_load); + + return !fail; +} + + +static void init_global() +{ + /* argc, argv must be set be the program */ + ioncore_g.dpy=NULL; + ioncore_g.display=NULL; + + ioncore_g.sm_client_id=NULL; + ioncore_g.rootwins=NULL; + ioncore_g.screens=NULL; + ioncore_g.focus_next=NULL; + ioncore_g.warp_next=FALSE; + + ioncore_g.focus_current=NULL; + + ioncore_g.input_mode=IONCORE_INPUTMODE_NORMAL; + ioncore_g.opmode=IONCORE_OPMODE_INIT; + ioncore_g.dblclick_delay=CF_DBLCLICK_DELAY; + ioncore_g.opaque_resize=0; + ioncore_g.warp_enabled=TRUE; + ioncore_g.switchto_new=TRUE; + + ioncore_g.enc_utf8=FALSE; + ioncore_g.enc_sb=TRUE; + ioncore_g.use_mb=FALSE; + + ioncore_g.screen_notify=TRUE; + + ioncore_g.frame_default_index=LLIST_INDEX_AFTER_CURRENT_ACT; + + ioncore_g.framed_transients=TRUE; +} + + +bool ioncore_init(const char *prog, int argc, char *argv[], + const char *localedir) +{ + init_global(); + + progname=prog; + ioncore_g.argc=argc; + ioncore_g.argv=argv; + +#ifndef CF_NO_LOCALE + init_locale(); + init_messages(localedir); +#endif + + ioncore_about=scat3(ioncore_copy, "\n\n", TR(ioncore_license)); + + if(!ioncore_init_bindmaps()) + return FALSE; + + if(!register_classes()) + return FALSE; + + if(!init_hooks()) + return FALSE; + + if(!ioncore_init_module_support()) + return FALSE; + + return TRUE; +} + + +/*}}}*/ + + +/*{{{ ioncore_startup */ + + +static void ioncore_init_session(const char *display) +{ + const char *dpyend=NULL; + char *tmp=NULL, *colon=NULL; + const char *sm=getenv("SESSION_MANAGER"); + + if(sm!=NULL) + ioncore_load_module("mod_sm"); + + if(extl_sessiondir()!=NULL) + return; + + /* Not running under SM; use display-specific directory */ + dpyend=strchr(display, ':'); + if(dpyend!=NULL) + dpyend=strchr(dpyend, '.'); + if(dpyend==NULL){ + libtu_asprintf(&tmp, "default-session-%s", display); + }else{ + libtu_asprintf(&tmp, "default-session-%.*s", + (int)(dpyend-display), display); + } + + if(tmp==NULL) + return; + + colon=tmp; + while(1){ + colon=strchr(colon, ':'); + if(colon==NULL) + break; + *colon='-'; + } + + extl_set_sessiondir(tmp); + free(tmp); +} + + +static bool ioncore_init_x(const char *display, int stflags) +{ + Display *dpy; + int i, drw, nrw; + static bool called=FALSE; + + /* Sorry, this function can not be re-entered due to laziness + * towards implementing checking of things already initialized. + * Nobody would call this twice anyway. + */ + assert(!called); + called=TRUE; + + /* Open the display. */ + dpy=XOpenDisplay(display); + + if(dpy==NULL){ + warn(TR("Could not connect to X display '%s'"), + XDisplayName(display)); + return FALSE; + } + + if(stflags&IONCORE_STARTUP_ONEROOT){ + drw=DefaultScreen(dpy); + nrw=drw+1; + }else{ + drw=0; + nrw=ScreenCount(dpy); + } + + /* Initialize */ + if(display!=NULL){ + ioncore_g.display=scopy(display); + if(ioncore_g.display==NULL){ + XCloseDisplay(dpy); + return FALSE; + } + } + + ioncore_g.dpy=dpy; + ioncore_g.win_context=XUniqueContext(); + ioncore_g.conn=ConnectionNumber(dpy); + + cloexec_braindamage_fix(ioncore_g.conn); + + ioncore_g.atom_wm_state=XInternAtom(dpy, "WM_STATE", False); + ioncore_g.atom_wm_change_state=XInternAtom(dpy, "WM_CHANGE_STATE", False); + ioncore_g.atom_wm_protocols=XInternAtom(dpy, "WM_PROTOCOLS", False); + ioncore_g.atom_wm_delete=XInternAtom(dpy, "WM_DELETE_WINDOW", False); + ioncore_g.atom_wm_take_focus=XInternAtom(dpy, "WM_TAKE_FOCUS", False); + ioncore_g.atom_wm_colormaps=XInternAtom(dpy, "WM_COLORMAP_WINDOWS", False); + ioncore_g.atom_wm_window_role=XInternAtom(dpy, "WM_WINDOW_ROLE", False); + ioncore_g.atom_checkcode=XInternAtom(dpy, "_ION_CWIN_RESTART_CHECKCODE", False); + ioncore_g.atom_selection=XInternAtom(dpy, "_ION_SELECTION_STRING", False); + ioncore_g.atom_mwm_hints=XInternAtom(dpy, "_MOTIF_WM_HINTS", False); + + ioncore_init_xim(); + ioncore_init_bindings(); + ioncore_init_cursors(); + + netwm_init(); + + ioncore_init_session(XDisplayName(display)); + + for(i=drw; i1) + warn(TR("Could not find a screen to manage.")); + return FALSE; + } + + if(!mainloop_register_input_fd(ioncore_g.conn, NULL, + ioncore_x_connection_handler)){ + return FALSE; + } + + return TRUE; +} + + +static void set_initial_focus() +{ + Window root=None, win=None; + int x, y, wx, wy; + uint mask; + WScreen *scr; + WWindow *wwin; + + XQueryPointer(ioncore_g.dpy, None, &root, &win, + &x, &y, &wx, &wy, &mask); + + FOR_ALL_SCREENS(scr){ + Window scrroot=region_root_of((WRegion*)scr); + if(scrroot==root && rectangle_contains(®ION_GEOM(scr), x, y)){ + break; + } + } + + if(scr==NULL) + scr=ioncore_g.screens; + + region_focuslist_push((WRegion*)scr); + region_do_set_focus((WRegion*)scr, FALSE); +} + + +bool ioncore_startup(const char *display, const char *cfgfile, + int stflags) +{ + WRootWin *rootwin; + sigset_t inittrap; + + /* Don't trap termination signals just yet. */ + sigemptyset(&inittrap); + sigaddset(&inittrap, SIGALRM); + sigaddset(&inittrap, SIGCHLD); + sigaddset(&inittrap, SIGPIPE); + sigaddset(&inittrap, SIGUSR2); + mainloop_trap_signals(&inittrap); + + if(!extl_init()) + return FALSE; + + ioncore_register_exports(); + + if(!ioncore_init_x(display, stflags)) + return FALSE; + + gr_read_config(); + + if(!extl_read_config("ioncore_ext", NULL, TRUE)) + return FALSE; + + ioncore_read_main_config(cfgfile); + + if(!ioncore_init_layout()) + return FALSE; + + hook_call_v(ioncore_post_layout_setup_hook); + + FOR_ALL_ROOTWINS(rootwin) + rootwin_manage_initial_windows(rootwin); + + set_initial_focus(); + + return TRUE; +} + + +/*}}}*/ + + +/*{{{ ioncore_deinit */ + + +void ioncore_deinit() +{ + Display *dpy; + WRootWin *rootwin; + + ioncore_g.opmode=IONCORE_OPMODE_DEINIT; + + if(ioncore_g.dpy==NULL) + return; + + hook_call_v(ioncore_deinit_hook); + + while(ioncore_g.screens!=NULL) + destroy_obj((Obj*)ioncore_g.screens); + + /*ioncore_unload_modules();*/ + + while(ioncore_g.rootwins!=NULL) + destroy_obj((Obj*)ioncore_g.rootwins); + + ioncore_deinit_bindmaps(); + + mainloop_unregister_input_fd(ioncore_g.conn); + + dpy=ioncore_g.dpy; + ioncore_g.dpy=NULL; + + XSync(dpy, True); + XCloseDisplay(dpy); + + extl_deinit(); +} + + +/*}}}*/ + + +/*{{{ Miscellaneous */ + + +/*EXTL_DOC + * Is Ion supporting locale-specifically multibyte-encoded strings? + */ +EXTL_SAFE +EXTL_EXPORT +bool ioncore_is_i18n() +{ + return ioncore_g.use_mb; +} + + +/*EXTL_DOC + * Returns Ioncore version string. + */ +EXTL_SAFE +EXTL_EXPORT +const char *ioncore_version() +{ + return ION_VERSION; +} + +/*EXTL_DOC + * Returns the name of program using Ioncore. + */ +EXTL_SAFE +EXTL_EXPORT +const char *ioncore_progname() +{ + return progname; +} + + +/*EXTL_DOC + * Returns an about message (version, author, copyright notice). + */ +EXTL_SAFE +EXTL_EXPORT +const char *ioncore_aboutmsg() +{ + return ioncore_about; +} + + +/*}}}*/ + diff --git a/ioncore/ioncore.h b/ioncore/ioncore.h new file mode 100644 index 0000000..658e966 --- /dev/null +++ b/ioncore/ioncore.h @@ -0,0 +1,37 @@ +/* + * ion/ioncore/focus.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_IONCORE_H +#define ION_IONCORE_IONCORE_H + +#include +#include "common.h" + +#define IONCORE_STARTUP_ONEROOT 0x0001 +#define IONCORE_STARTUP_NOXINERAMA 0x0002 + +extern bool ioncore_init(const char *prog, int argc, char *argv[], + const char *localedir); +extern bool ioncore_startup(const char *display, const char *cfgfile, + int flags); +extern void ioncore_deinit(); + +extern const char *ioncore_aboutmsg(); +extern const char *ioncore_version(); + +/* These hooks have no parameters. */ +extern WHook *ioncore_post_layout_setup_hook; +extern WHook *ioncore_snapshot_hook; +extern WHook *ioncore_deinit_hook; + +extern void ioncore_warn_nolog(const char *str); + +#endif /* ION_IONCORE_IONCORE_H */ diff --git a/ioncore/ioncore_bindings.lua b/ioncore/ioncore_bindings.lua new file mode 100644 index 0000000..bf5c9fe --- /dev/null +++ b/ioncore/ioncore_bindings.lua @@ -0,0 +1,233 @@ +-- +-- ion/share/ioncore-bindings.lua +-- +-- Copyright (c) Tuomo Valkonen 2004-2006. +-- +-- Ion is free software; you can redistribute it and/or modify it under +-- the terms of the GNU Lesser General Public License as published by +-- the Free Software Foundation; either version 2.1 of the License, or +-- (at your option) any later version. +-- + +local ioncore=_G.ioncore + +local warn=ioncore.warn + +local compiled2str=setmetatable({}, {__mode="k"}) + +--DOC +-- Compile string \var{cmd} into a bindable function. Within \var{cmd}, the +-- variable ''\var{_}'' (underscore) can be used to refer to the object +-- that was selecting for the bound action and chosen to handle it. +-- The variable ''\var{_sub}'' refers to a ''currently active'' sub-object +-- of \var{_}, or a sub-object where the action loading to the binding +-- being called actually occured. +-- +-- The string \var{guard} maybe set to pose limits on \code{_sub}. Currently +-- supported guards are \code{_sub:non-nil} and \code{_sub:WFoobar}, where +-- \type{WFoobar} is a class. +function ioncore.compile_cmd(cmd, guard) + local guardcode="" + local gfn=nil + + if guard then + local st, en, condition=string.find(guard, "^_sub:([%w-_]+)$") + local sub='_sub' + if not condition then + st, en, condition=string.find(guard, "^_chld:([%w-_]+)$") + if condition then + sub='_chld' + end + end + + if not condition then + ioncore.warn_traced(TR("Invalid guard %s.", guard)) + elseif condition=="non-nil" then + guardcode='if not '..sub..' then return end; ' + else + guardcode='if not obj_is('..sub..', "'..condition..'") then return end; ' + end + + local gfncode="return function(_, _sub, _chld) "..guardcode.." return true end" + local gfn, gerr=loadstring(gfncode, guardcode) + if not gfn then + ioncore.warn_traced(TR("Error compiling guard: %s", gerr)) + end + gfn=gfn() + end + + local function guarded(gfn, fn) + if not gfn then + return fn + else + return function(_, _sub, _chld) + if gfn(_, _sub, _chld) then + cmd(_, _sub, _chld) + end + end + end + end + + if type(cmd)=="string" then + local fncode=("return function(_, _sub, _chld) local d = " + ..cmd.." end") + local fn, err=loadstring(fncode, cmd) + if not fn then + ioncore.warn_traced(TR("Error in command string: ")..err) + return + end + compiled2str[fn]=cmd + return guarded(gfn, fn()) + elseif type(cmd)=="function" then + return guarded(gfn, cmd) + end + + ioncore.warn_traced(TR("Invalid command")) +end + + +local function putcmd(cmd, guard, tab) + local func + if cmd then + func=ioncore.compile_cmd(cmd, guard) + if type(func)~="function" then + return + end + end + + tab.func=func + tab.cmdstr=cmd + tab.guard=guard + + return tab +end + +--DOC +-- Used to enter documentation among bindings so that other programs +-- can read it. Does nothing. +function ioncore.bdoc(text) + return {action = "doc", text = text} +end + +--DOC +-- Returns a function that creates a submap binding description table. +-- When the key press action \var{keyspec} occurs, Ioncore will wait for +-- a further key presse and act according to the submap. +-- For details, see section \ref{sec:bindings}. +function ioncore.submap(kcb_, list) + if not list then + return function(lst) + return submap(kcb_, lst) + end + end + return {action = "kpress", kcb = kcb_, submap = list} +end + +--DOC +-- Creates a binding description table for the action of pressing a key given +-- by \var{keyspec} (with possible modifiers) to the function \var{func}. +-- For more information on bindings, see section \ref{sec:bindings}. +function ioncore.kpress(keyspec, cmd, guard) + return putcmd(cmd, guard, {action = "kpress", kcb = keyspec}) +end + +--DOC +-- This is similar to \fnref{kpress} but after calling \var{cmd}, +-- Ioncore waits for all modifiers to be released before processing +-- any further actions. +-- For more information on bindings, see section \ref{sec:bindings}. +function ioncore.kpress_wait(keyspec, cmd, guard) + return putcmd(cmd, guard, {action = "kpress_wait", kcb = keyspec}) +end + +local function mact(act_, kcb_, cmd, guard) + local st, en, kcb2_, area_=string.find(kcb_, "([^@]*)@(.*)") + return putcmd(cmd, guard, { + action = act_, + kcb = (kcb2_ or kcb_), + area = area_, + }) +end + +--DOC +-- Creates a binding description table for the action of clicking a mouse +-- button while possible modifier keys are pressed, +-- both given by \var{buttonspec}, to the function \var{func}. +-- For more information, see section \ref{sec:bindings}. +function ioncore.mclick(buttonspec, cmd, guard) + return mact("mclick", buttonspec, cmd, guard) +end + +--DOC +-- Similar to \fnref{mclick} but for double-click. +-- Also see section \ref{sec:bindings}. +function ioncore.mdblclick(buttonspec, cmd, guard) + return mact("mdblclick", buttonspec, cmd, guard) +end + +--DOC +-- Similar to \fnref{mclick} but for just pressing the mouse button. +-- Also see section \ref{sec:bindings}. +function ioncore.mpress(buttonspec, cmd, guard) + return mact("mpress", buttonspec, cmd, guard) +end + +--DOC +-- Creates a binding description table for the action of moving the mouse +-- (or other pointing device) while the button given by \var{buttonspec} +-- is held pressed and the modifiers given by \var{buttonspec} were pressed +-- when the button was initially pressed. +-- Also see section \ref{sec:bindings}. +function ioncore.mdrag(buttonspec, cmd, guard) + return mact("mdrag", buttonspec, cmd, guard) +end + +--DOC +-- Define bindings for context \var{context}. Here \var{binding} is +-- a table composed of entries created with \fnref{ioncore.kpress}, +-- etc.; see section \ref{sec:bindings} for details. +function ioncore.defbindings(context, bindings) + local function filterdoc(b) + local t={} + for k, v in ipairs(b) do + local v2=v + if v2.submap then + v2=table.copy(v) + v2.submap=filterdoc(v2.submap) + end + if v2.action~="doc" then + table.insert(t, v2) + end + end + return t + end + return ioncore.do_defbindings(context, filterdoc(bindings)) +end + +local function bindings_get_cmds(map) + for k, v in pairs(map) do + if v.func then + v.cmd=compiled2str[v.func] + end + if v.submap then + bindings_get_cmds(v.submap) + end + end +end + +--DOC +-- Get a table of all bindings. +function ioncore.getbindings(maybe_context) + local bindings=ioncore.do_getbindings() + if maybe_context then + bindings_get_cmds(bindings[maybe_context]) + return bindings[maybe_context] + else + for k, v in pairs(bindings) do + bindings_get_cmds(v) + end + return bindings + end +end + + diff --git a/ioncore/ioncore_efbb.lua b/ioncore/ioncore_efbb.lua new file mode 100644 index 0000000..156a95d --- /dev/null +++ b/ioncore/ioncore_efbb.lua @@ -0,0 +1,37 @@ +-- +-- ion/share/ioncore_efbb.lua -- Minimal emergency fallback bindings. +-- +-- Copyright (c) Tuomo Valkonen 2004-2006. +-- +-- Ion is free software; you can redistribute it and/or modify it under +-- the terms of the GNU Lesser General Public License as published by +-- the Free Software Foundation; either version 2.1 of the License, or +-- (at your option) any later version. +-- + +warn(TR([[ +Making the following minimal emergency mappings: + F2 -> xterm + F11 -> restart + F12 -> exit + Mod1+C -> close + Mod1+K P/N -> WFrame.switch_next/switch_prev +]])) + + +defbindings("WScreen", { + kpress("F2", function() ioncore.exec('xterm') end), + kpress("F11", function() ioncore.restart() end), + kpress("F12", function() ioncore.exit() end), +}) + +defbindings("WMPlex", { + kpress_wait("Mod1+C", WRegion.rqclose_propagate), +}) + +defbindings("WFrame", { + submap("Mod1+K", { + kpress("AnyModifier+N", function(f) f:switch_next() end), + kpress("AnyModifier+P", function(f) f:switch_prev() end), + }) +}) diff --git a/ioncore/ioncore_ext.lua b/ioncore/ioncore_ext.lua new file mode 100644 index 0000000..cc56732 --- /dev/null +++ b/ioncore/ioncore_ext.lua @@ -0,0 +1,94 @@ +-- +-- ion/share/ioncore_ext.lua -- Ioncore Lua library +-- +-- Copyright (c) Tuomo Valkonen 2004-2006. +-- +-- Ion is free software; you can redistribute it and/or modify it under +-- the terms of the GNU Lesser General Public License as published by +-- the Free Software Foundation; either version 2.1 of the License, or +-- (at your option) any later version. +-- + +-- This is a slight abuse of the package.loaded variable perhaps, but +-- library-like packages should handle checking if they're loaded instead of +-- confusing the user with require/includer differences. +if package.loaded["ioncore"] then return end + +-- Default modifiers +--MOD1="Mod1+" +--MOD2="" + +-- Maximum number of bytes to read from pipes +ioncore.RESULT_DATA_LIMIT=1024^2 + +-- Bindings, winprops, hooks, menu database and extra commands +dopath('ioncore_luaext') +dopath('ioncore_bindings') +dopath('ioncore_winprops') +dopath('ioncore_misc') +dopath('ioncore_wd') +dopath('ioncore_menudb') + +-- Modifier setup compatibility kludge +local oldindex + +local function getmod(t, s) + if s=="META" then + return rawget(t, "MOD1") or "Mod1+" + elseif s=="MOD1" then + return rawget(t, "META") or "Mod1+" + elseif s=="ALTMETA" then + return rawget(t, "MOD2") or "" + elseif s=="MOD2" then + return rawget(t, "ALTMETA") or "" + elseif oldindex then + return oldindex(t, s) + end +end + +local oldmeta, newmeta=getmetatable(_G), {} +if oldmeta then + newmeta=table.copy(oldmeta) + oldindex=oldmeta.__index +end +newmeta.__index=getmod +setmetatable(_G, newmeta) + +-- Export some important functions into global namespace. +export(ioncore, + "submap", + "kpress", + "kpress_wait", + "mpress", + "mclick", + "mdblclick", + "mdrag", + "defbindings", + "defwinprop", + "warn", + "exec", + "TR", + "bdoc", + "defmenu", + "defctxmenu", + "menuentry", + "submenu") + +-- Mark ourselves loaded. +package.loaded["ioncore"]=true + + + +local function dummy_gettext_hack() + -- Extra translations for context menus etc. I don't want extra + -- TR calls in the configuration files, or parsing the string + -- parameters to kpress etc. for translations. + TR("Frame") + TR("Screen") + TR("Workspace") + TR("Tiling") + TR("Tiled frame") + TR("Floating frame") + TR("Context menu:") + TR("Main menu:") +end diff --git a/ioncore/ioncore_luaext.lua b/ioncore/ioncore_luaext.lua new file mode 100644 index 0000000..cb52b97 --- /dev/null +++ b/ioncore/ioncore_luaext.lua @@ -0,0 +1,92 @@ +-- +-- ion/share/ioncore_luaext.lua +-- +-- Copyright (c) Tuomo Valkonen 2004-2006. +-- +-- Ion is free software; you can redistribute it and/or modify it under +-- the terms of the GNU Lesser General Public License as published by +-- the Free Software Foundation; either version 2.1 of the License, or +-- (at your option) any later version. +-- + + +--DOC +-- Make \var{str} shell-safe. +function string.shell_safe(str) + return "'"..string.gsub(str, "'", "'\\''").."'" +end + + +--DOC +-- Make copy of \var{table}. If \var{deep} is unset, shallow one-level +-- copy is made, otherwise a deep copy is made. +function table.copy(t, deep) + local function docopy(t, deep, seen) + local nt={} + for k, v in pairs(t) do + local v2=v + if deep and type(v)=="table" then + if seen[v] then + error(TR("Recursive table - unable to deepcopy")) + end + seen[v]=true + v2=docopy(v, deep, seen) + seen[v]=nil + end + nt[k]=v2 + end + return nt + end + return docopy(t, deep, deep and {}) +end + + +--DOC +-- Add entries that do not exist in \var{t1} from \var{t2} to \var{t1}. +function table.append(t1, t2) + for k, v in pairs(t2) do + if t1[k]==nil then + t1[k]=v + end + end + return t1 +end + + +--DOC +-- Create a table containing all entries from \var{t1} and those from +-- \var{t2} that are missing from \var{t1}. +function table.join(t1, t2) + return table.append(table.copy(t1, false), t2) +end + + +--DOC +-- Insert all positive integer entries from t2 into t1. +function table.icat(t1, t2) + for _, v in ipairs(t2) do + table.insert(t1, v) + end + return t1 +end + + +--DOC +-- Map all entries of \var{t} by \var{f}. +function table.map(f, t) + local res={} + for k, v in pairs(t) do + res[k]=f(v) + end + return res +end + + +--DOC +-- Export a list of functions from \var{lib} into global namespace. +function export(lib, ...) + for k, v in pairs(arg) do + _G[v]=lib[v] + end +end + diff --git a/ioncore/ioncore_menudb.lua b/ioncore/ioncore_menudb.lua new file mode 100644 index 0000000..6032b94 --- /dev/null +++ b/ioncore/ioncore_menudb.lua @@ -0,0 +1,354 @@ +-- +-- ion/ioncore/ioncore_menudb.lua -- Routines for defining menus. +-- +-- Copyright (c) Tuomo Valkonen 2004-2006. +-- +-- Ion is free software; you can redistribute it and/or modify it under +-- the terms of the GNU Lesser General Public License as published by +-- the Free Software Foundation; either version 2.1 of the License, or +-- (at your option) any later version. +-- + +local ioncore=_G.ioncore + + +-- Table to hold defined menus. +local menus={} + + +-- Menu construction {{{ + +--DOC +-- Define a new menu with \var{name} being the menu's name and \var{tab} +-- being a table of menu entries. If \var{tab.append} is set, the entries +-- are appended to previously-defined ones, if possible. +function ioncore.defmenu(name, tab) + if menus[name] and type(tab)=="table" and tab.append then + if type(menus[name])~="table" then + ioncore.warn(TR("Unable to append to non-table menu")) + return + else + table.append(menus[name], tab) + end + else + menus[name]=tab + end +end + +--DOC +-- Returns a menu defined with \fnref{ioncore.defmenu}. +function ioncore.getmenu(name) + return menus[name] +end + +--DOC +-- Define context menu for context \var{ctx}, \var{tab} being a table +-- of menu entries. +function ioncore.defctxmenu(ctx, ...) + local tab, add + if #arg>1 and type(arg[1])=="string" then + tab=arg[2] + tab.label=ioncore.gettext(arg[1]) + else + tab=arg[1] + end + ioncore.defmenu("ctxmenu-"..ctx, tab) +end + +--DOC +-- Returns a context menu defined with \fnref{ioncore.defctxmenu}. +function ioncore.getctxmenu(name) + return menus["ctxmenu-"..name] +end + + +function ioncore.evalmenu(menu, args) + if type(menu)=="string" then + return ioncore.evalmenu(menus[menu], args) + elseif type(menu)=="function" then + if args then + return menu(unpack(args)) + else + return menu() + end + elseif type(menu)=="table" then + return menu + end +end + + +--DOC +-- Use this function to define normal menu entries. The string \var{name} +-- is the string shown in the visual representation of menu, and the +-- parameter \var{cmd} and \var{guard} are similar to those of +-- \fnref{ioncore.defbindings}. +function ioncore.menuentry(name, cmd, guard) + local fn, gfn=ioncore.compile_cmd(cmd, guard) + if fn then + return {name=ioncore.gettext(name), func=fn, guard_func=gfn} + end +end + +--DOC +-- Use this function to define menu entries for submenus. The parameter +-- \fnref{sub_or_name} is either a table of menu entries or the name +-- of an already defined menu. The initial menu entry to highlight can be +-- specified by \var{options.initial} as either an integer starting from 1, +-- or a function that returns such a number. Another option supported is +-- \var{options.noautoexpand} that will cause \fnref{mod_query.query_menu} +-- to not automatically expand this submenu. +function ioncore.submenu(name, sub_or_name, options) + if not options then + options={} + end + return { + name=ioncore.gettext(name), + submenu_fn=function() + return ioncore.evalmenu(sub_or_name) + end, + initial=options.initial, + noautoexpand=options.noautoexpand, + } +end + + +-- }}} + + +-- Workspace and window lists {{{ + +local function makelist(list) + local function mkentry(tgt) + return menuentry(tgt:name(), function() tgt:goto() end) + end + local entries=table.map(mkentry, list) + table.sort(entries, function(a, b) return a.name < b.name end) + return entries +end + +function menus.windowlist() + return makelist(ioncore.clientwin_list()) +end + +function menus.workspacelist() + return makelist(ioncore.region_list("WGenWS")) +end + +-- }}} + + +-- Style menu {{{ + + +local function mplex_of(reg) + while reg and not obj_is(reg, "WMPlex") do + reg=reg:parent() + end + return reg +end + +local function selectstyle(look, where) + dopath(look) + + local fname=ioncore.get_savefile('look') + + local function writeit() + local f, err=io.open(fname, 'w') + if not f then + mod_query.message(where, err) + else + f:write(string.format('dopath("%s")\n', look)) + f:close() + end + end + + if not mod_query then + if fname then + writeit() + end + return + end + + where=mplex_of(where) + if not where then + return + end + + if not fname then + query_message(where, TR("Cannot save selection.")) + return + end + + mod_query.query_yesno(where, TR("Save look selection in %s?", fname), + writeit) +end + +local function receive_styles(str) + local data="" + + while str do + data=data .. str + if string.len(data)>ioncore.RESULT_DATA_LIMIT then + error(TR("Too much result data")) + end + str=coroutine.yield() + end + + local found={} + local styles={} + local stylemenu={} + + for look in string.gfind(data, "(look[-_][^\n]*)%.lua\n") do + if not found[look] then + found[look]=true + table.insert(styles, look) + end + end + + table.sort(styles) + + for _, look in ipairs(styles) do + local look_=look + table.insert(stylemenu, menuentry(look, + function(where) + selectstyle(look_, where) + end)) + end + + table.insert(stylemenu, menuentry(TR("Refresh list"), + ioncore.refresh_stylelist)) + + menus.stylemenu=stylemenu +end + + +--DOC +-- Refresh list of known style files. +function ioncore.refresh_stylelist() + local cmd=ioncore.lookup_script("ion-completefile") + if cmd then + local path=ioncore.get_paths().searchpath + local function mkarg(s) + if s=="" then + return "" + else + return (" "..string.shell_safe(s).."/look_".. + " "..string.shell_safe(s).."/look-") + end + return "" + end + + cmd=cmd..string.gsub(path..":", "([^:]*):", mkarg) + + ioncore.popen_bgread(cmd, coroutine.wrap(receive_styles)) + end +end + +-- }}} + + +-- Context menu {{{ + + +local function classes(reg) + local function classes_(t) + if t.__parentclass then + classes_(t.__parentclass) + end + coroutine.yield(t.__typename) + end + return coroutine.wrap(function() classes_(reg) end) +end + + +local function modeparts(mode) + if not mode then + return function() return end + end + + local f, s, v=string.gmatch(mode, "(%-?[^-]+)"); + + local function nxt(_, m) + v = f(s, v) + return (v and (m .. v)) + end + + return nxt, nil, "" +end + + +local function get_ctxmenu(reg, sub, is_par) + local m={} + + local function cp(m2) + local m3={} + for k, v in ipairs(m2) do + local v2=table.copy(v) + + if v2.func then + local ofunc=v2.func + v2.func=function() return ofunc(reg, sub) end + end + + if v2.submenu_fn then + local ofn=v2.submenu_fn + v2.submenu_fn=function() return cp(ofn()) end + end + + m3[k]=v2 + end + m3.label=m2.label + return m3 + end + + local function add_ctxmenu(m2, use_label) + if m2 then + if is_par then + m2=cp(m2) + end + + m=table.icat(m, m2) + m.label=(use_label and m2.label) or m.label + end + end + + local mgr=reg:manager() + local mgrname=(mgr and mgr:name()) or nil + local mode=(reg.mode and reg:mode()) + + for s in classes(reg) do + local nm="ctxmenu-"..s + add_ctxmenu(ioncore.evalmenu(nm), true) + for m in modeparts(mode) do + add_ctxmenu(ioncore.evalmenu(nm.."."..m), false) + end + if mgrname then + add_ctxmenu(ioncore.evalmenu(nm.."@"..mgrname), false) + end + end + return m +end + +function menus.ctxmenu(reg, sub) + local m=get_ctxmenu(reg, sub, false); + + sub=reg + reg=reg:manager() + + while reg do + local mm = get_ctxmenu(reg, sub, true) + if #mm>0 then + local nm=mm.label or obj_typename(reg) + table.insert(m, ioncore.submenu(nm, mm)) + end + sub=reg + reg=reg:manager() + end + + return m +end + +-- }}} + +ioncore.refresh_stylelist() + diff --git a/ioncore/ioncore_misc.lua b/ioncore/ioncore_misc.lua new file mode 100644 index 0000000..fd72b4d --- /dev/null +++ b/ioncore/ioncore_misc.lua @@ -0,0 +1,53 @@ +-- +-- ion/share/ioncore_misc.lua +-- +-- Copyright (c) Tuomo Valkonen 2004-2006. +-- +-- Ion is free software; you can redistribute it and/or modify it under +-- the terms of the GNU Lesser General Public License as published by +-- the Free Software Foundation; either version 2.1 of the License, or +-- (at your option) any later version. +-- + + +local group_tmpl={type="WGroupWS", switchto=true} + +--DOC +-- Create new workspace on screen \var{scr}. The table \var{tmpl} +-- may be used to override parts of \code{default_ws_params}, +-- and \var{no_default} may be set to \code{true} to complete ignore it. +function ioncore.create_ws(scr, tmpl, no_default) + local dflt=(not no_default and ioncore.get().default_ws_params) or {} + local t=table.join(table.join(tmpl or {}, dflt), group_tmpl) + + return scr:attach_new(t) +end + + +--DOC +-- Find an object with type name \var{t} managing \var{obj} or one of +-- its managers. +function ioncore.find_manager(obj, t) + while obj~=nil do + if obj_is(obj, t) then + return obj + end + obj=obj:manager() + end +end + + +--DOC +-- gettext+string.format +function ioncore.TR(s, ...) + return string.format(ioncore.gettext(s), unpack(arg)) +end + + +--[[DOC +-- Run \var{cmd} with the environment variable DISPLAY set to point to the +-- root window of the X screen \var{reg} is on. +function ioncore.exec_on(reg, cmd) + return ioncore.do_exec_rw(reg:rootwin_of(), cmd) +end +--]] diff --git a/ioncore/ioncore_wd.lua b/ioncore/ioncore_wd.lua new file mode 100644 index 0000000..84107b8 --- /dev/null +++ b/ioncore/ioncore_wd.lua @@ -0,0 +1,167 @@ +-- +-- ion/share/ioncore_wd.lua +-- +-- Copyright (c) Tuomo Valkonen 2004-2006. +-- +-- Ion is free software; you can redistribute it and/or modify it under +-- the terms of the GNU Lesser General Public License as published by +-- the Free Software Foundation; either version 2.1 of the License, or +-- (at your option) any later version. +-- + +local savefile="saved_wd" +local dirs={} +local px + +if pcall(function() return require('posix') end) then + px=posix +end + +local function checkdir(d) + if not px then + return true + else + local t, err=px.stat(d, "type") + if not t then + return nil, err + elseif t=="link" then + local d2, err=px.readlink(d) + if not d2 then + return nil, err + else + print('follow') + return checkdir(d2) + end + elseif t=="directory" then + return true + else + return TR("Not a directory.") + end + end +end + +local function regtreepath_i(reg) + local function f(s, v) + if v then + return v:manager() + else + return s + end + end + return f, reg, nil +end + +--DOC +-- Change default working directory for new programs started in \var{reg}. +function ioncore.chdir_for(reg, dir) + assert(type(dir)=="string") + if dir=="" or dir==nil then + dirs[reg]=nil + return true + else + local ok, err=checkdir(dir) + if ok then + dirs[reg]=dir + end + return ok, err + end +end + +--DOC +-- Get default working directory for new programs started in \var{reg}. +function ioncore.get_dir_for(reg) + for r in regtreepath_i(reg) do + if dirs[r] then + return dirs[r] + end + end +end + + +local function lookup_script_warn(script) + local script=ioncore.lookup_script(script) + if not script then + warn(TR("Could not find %s", script)) + end + return script +end + + +local function lookup_runinxterm_warn(prog, title, wait) + local rx=lookup_script_warn("ion-runinxterm") + if rx then + if wait then + rx=rx.." -w" + end + if title then + rx=rx.." -T "..string.shell_safe(title) + end + if prog then + rx=rx.." -- "..prog + end + end + return rx +end + + +--DOC +-- Run \var{cmd} with the environment variable DISPLAY set to point to the +-- root window of the X screen \var{reg} is on. If \var{cmd} is prefixed +-- by a colon (\code{:}), the following command is executed in an xterm +-- (or other terminal emulator) with the help of the \command{ion-runinxterm} +-- script. If the command is prefixed by two colons, \command{ion-runinxterm} +-- will ask you to press enter after the command is finished, even if it +-- returns succesfully. +function ioncore.exec_on(reg, cmd, merr_internal) + local _, _, col, c=string.find(cmd, "^[%s]*(:+)(.*)") + if col then + cmd=lookup_runinxterm_warn(c, nil, string.len(col)>1) + if not cmd then + return + end + if XTERM then + cmd='XTERMCMD='..string.shell_safe(XTERM)..' '..cmd + end + end + return ioncore.do_exec_rw(reg:rootwin_of(), cmd, + ioncore.get_dir_for(reg), + merr_internal) +end + + +local function load_config() + local d=ioncore.read_savefile(savefile) + if d then + dirs={} + for nm, d in pairs(d) do + local r=ioncore.lookup_region(nm) + if r then + local ok, err=checkdir(d) + if ok then + dirs[r]=d + else + warn(err) + end + end + end + end +end + + +local function save_config() + local t={} + for r, d in pairs(dirs) do + t[r:name()]=d + end + ioncore.write_savefile(savefile, t) +end + + +local function init() + load_config() + ioncore.get_hook("ioncore_snapshot_hook"):add(save_config) + ioncore.get_hook("ioncore_post_layout_setup_hook"):add(load_config) +end + +init() + diff --git a/ioncore/ioncore_winprops.lua b/ioncore/ioncore_winprops.lua new file mode 100644 index 0000000..333ff69 --- /dev/null +++ b/ioncore/ioncore_winprops.lua @@ -0,0 +1,129 @@ +-- +-- ion/share/ioncore_winprops.lua +-- +-- Copyright (c) Tuomo Valkonen 2004-2006. +-- +-- Ion is free software; you can redistribute it and/or modify it under +-- the terms of the GNU Lesser General Public License as published by +-- the Free Software Foundation; either version 2.1 of the License, or +-- (at your option) any later version. +-- + +local ioncore=_G.ioncore + +local winprops={} + +local function ifnil(...) + local n=#arg + local function nxt(_, i) + local j=i+1 + if i==n then + return nil + else + local j=i+1 + if not arg[j] then + return nxt(nil, j) + else + return j, arg[j] + end + end + end + + return nxt, nil, 0 +end + +local function ipairs_r(tab) + local function nxt(_, n) + if n==1 then + return nil + else + return n-1, tab[n-1] + end + end + return nxt, nil, #tab+1 +end + +--DOC +-- Find winprop table for \var{cwin}. +function ioncore.getwinprop(cwin) + local id=cwin:get_ident() + local props, prop + + for _, c in ifnil(id.class, "*") do + for _, r in ifnil(id.role, "*") do + for _, i in ifnil(id.instance, "*") do + --printpp(c, r, i) + props={} + pcall(function() props=winprops[c][r][i] or {} end) + for idx, prop in ipairs_r(props) do + if prop:match(cwin) then + if prop.oneshot then + table.remove(props, idx) + end + return prop + end + end + end + end + end +end + +ioncore.set_get_winprop_fn(ioncore.getwinprop) + +local function ensure_winproptab(class, role, instance) + if not winprops[class] then + winprops[class]={} + end + if not winprops[class][role] then + winprops[class][role]={} + end + if not winprops[class][role][instance] then + winprops[class][role][instance]={} + end +end + +local function do_add_winprop(class, role, instance, name, prop) + ensure_winproptab(class, role, instance) + table.insert(winprops[class][role][instance], prop) +end + + +--DOC +-- The basic name-based winprop matching criteria. +function ioncore.match_winprop_name(prop, cwin) + local nm=cwin:name() + if not prop.name then + return true + elseif nm then + local st, en=string.find(nm, prop.name) + return (st and en) + else + return false + end +end + + +--DOC +-- Define a winprop. For more information, see section \ref{sec:winprops}. +function ioncore.defwinprop(list) + local list2 = {} + local class, role, instance = "*", "*", "*" + + for k, v in pairs(list) do + if k == "class" then + class = v + elseif k == "role" then + role = v + elseif k == "instance" then + instance = v + end + list2[k] = v + end + + if not list2.match then + list2.match=ioncore.match_winprop_name + end + + do_add_winprop(class, role, instance, name, list2) +end + diff --git a/ioncore/kbresize.c b/ioncore/kbresize.c new file mode 100644 index 0000000..b427228 --- /dev/null +++ b/ioncore/kbresize.c @@ -0,0 +1,380 @@ +/* + * ion/ioncore/kbresize.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include + +#include + +#include + +#include "global.h" +#include "resize.h" +#include "kbresize.h" +#include "grab.h" +#include "binding.h" +#include "focus.h" +#include "bindmaps.h" + + +/*{{{ Resize accelerator */ + + +static struct timeval last_action_tv={-1, 0}; +static struct timeval last_update_tv={-1, 0}; +static int last_accel_mode=0; +static double accel=1, accelinc=30, accelmax=100*100; +static long actmax=200, uptmin=50; +static int resize_delay=CF_RESIZE_DELAY; + + +static void accel_reset() +{ + last_accel_mode=0; + accel=1.0; + last_action_tv.tv_sec=-1; + last_action_tv.tv_usec=-1; +} + + +void ioncore_set_moveres_accel(ExtlTab tab) +{ + int t_max, t_min, rd; + double step, maxacc; + + if(extl_table_gets_i(tab, "kbresize_t_max", &t_max)) + actmax=(t_max>0 ? t_max : INT_MAX); + if(extl_table_gets_i(tab, "kbresize_t_min", &t_min)) + uptmin=(t_min>0 ? t_min : INT_MAX); + if(extl_table_gets_d(tab, "kbresize_step", &step)) + accelinc=(step>0 ? step : 1); + if(extl_table_gets_d(tab, "kbresize_maxacc", &maxacc)) + accelmax=(maxacc>0 ? maxacc*maxacc : 1); + if(extl_table_gets_i(tab, "kbresize_delay", &rd)) + resize_delay=maxof(0, rd); +} + + +void ioncore_get_moveres_accel(ExtlTab tab) +{ + extl_table_sets_i(tab, "kbresize_t_max", actmax), + extl_table_sets_i(tab, "kbresize_t_min", uptmin); + extl_table_sets_d(tab, "kbresize_step", accelinc); + extl_table_sets_d(tab, "kbresize_maxacc", accelmax); + extl_table_sets_d(tab, "kbresize_delay", resize_delay); +} + + +static int sign(int x) +{ + return (x>0 ? 1 : (x<0 ? -1 : 0)); +} + + +static long tvdiffmsec(struct timeval *tv1, struct timeval *tv2) +{ + double t1=1000*(double)tv1->tv_sec+(double)tv1->tv_usec/1000; + double t2=1000*(double)tv2->tv_sec+(double)tv2->tv_usec/1000; + + return (int)(t1-t2); +} + +#define SIGN_NZ(X) ((X) < 0 ? -1 : 1) + +static double max(double a, double b) +{ + return (auptmin){ + accel+=accelinc; + if(accel>accelmax) + accel=accelmax; + last_update_tv=tv; + } + }else{ + accel=1.0; + last_update_tv=tv; + } + + last_accel_mode=accel_mode; + last_action_tv=tv; + + if(*wu!=0) + *wu=(*wu)*ceil(sqrt(accel)/abs(*wu)); + if(*hu!=0) + *hu=(*hu)*ceil(sqrt(accel)/abs(*hu)); +} + + +/*}}}*/ + + +/*{{{ Keyboard resize handler */ + + +static ExtlExportedFn *moveres_safe_fns[]={ + (ExtlExportedFn*)&moveresmode_resize, + (ExtlExportedFn*)&moveresmode_move, + (ExtlExportedFn*)&moveresmode_finish, + (ExtlExportedFn*)&moveresmode_cancel, + NULL +}; + +static ExtlSafelist moveres_safelist=EXTL_SAFELIST_INIT(moveres_safe_fns); + + +static bool resize_handler(WRegion *reg, XEvent *xev) +{ + XKeyEvent *ev=&xev->xkey; + WBinding *binding=NULL; + WBindmap **bindptr; + WMoveresMode *mode; + + if(ev->type==KeyRelease) + return FALSE; + + if(reg==NULL) + return FALSE; + + mode=moveres_mode(reg); + + if(mode==NULL) + return FALSE; + + binding=bindmap_lookup_binding(ioncore_moveres_bindmap, + BINDING_KEYPRESS, + ev->state, ev->keycode); + + if(!binding) + return FALSE; + + if(binding!=NULL){ + extl_protect(&moveres_safelist); + extl_call(binding->func, "oo", NULL, mode, reg); + extl_unprotect(&moveres_safelist); + } + + return (moveres_mode(reg)==NULL); +} + + +/*}}}*/ + + +/*{{{ Resize timer */ + + +static WTimer *resize_timer=NULL; + + +static void tmr_end_resize(WTimer *unused, WMoveresMode *mode) +{ + if(mode!=NULL) + moveresmode_cancel(mode); +} + + +static bool setup_resize_timer(WMoveresMode *mode) +{ + if(resize_timer==NULL){ + resize_timer=create_timer(); + if(resize_timer==NULL) + return FALSE; + } + + timer_set(resize_timer, resize_delay, + (WTimerHandler*)tmr_end_resize, (Obj*)mode); + + return TRUE; +} + + +static void reset_resize_timer() +{ + if(resize_timer!=NULL){ + timer_reset(resize_timer); + destroy_obj((Obj*)resize_timer); + resize_timer=NULL; + } +} + + +/*}}}*/ + + +/*{{{ Misc. */ + + +static int limit_and_encode_mode(int *left, int *right, + int *top, int *bottom) +{ + *left=sign(*left); + *right=sign(*right); + *top=sign(*top); + *bottom=sign(*bottom); + + return (*left)+(*right)*3+(*top)*9+(*bottom)*27; +} + + +static void resize_units(WMoveresMode *mode, int *wret, int *hret) +{ + WSizeHints *h=&(mode->hints); + *wret=1; + *hret=1; + if(h->inc_set && (h->width_inc>1 || h->height_inc>1)){ + *wret=h->width_inc; + *hret=h->height_inc; + } +} + + +/*}}}*/ + + +/*{{{ Keyboard resize interface */ + + +/*EXTL_DOC + * Shrink or grow resize mode target one step in each direction. + * Acceptable values for the parameters \var{left}, \var{right}, \var{top} + * and \var{bottom} are as follows: -1: shrink along, + * 0: do not change, 1: grow along corresponding border. + */ +EXTL_EXPORT_MEMBER +void moveresmode_resize(WMoveresMode *mode, + int left, int right, int top, int bottom) +{ + int wu=0, hu=0; + int accel_mode=0; + + if(!setup_resize_timer(mode)) + return; + + accel_mode=3*limit_and_encode_mode(&left, &right, &top, &bottom); + resize_units(mode, &wu, &hu); + moveresmode_accel(mode, &wu, &hu, accel_mode); + + moveresmode_delta_resize(mode, -left*wu, right*wu, -top*hu, bottom*hu, + NULL); +} + + +/*EXTL_DOC + * Move resize mode target one step: + * + * \begin{tabular}{rl} + * \hline + * \var{horizmul}/\var{vertmul} & effect \\\hline + * -1 & Move left/up \\ + * 0 & No effect \\ + * 1 & Move right/down \\ + * \end{tabular} + */ +EXTL_EXPORT_MEMBER +void moveresmode_move(WMoveresMode *mode, int horizmul, int vertmul) +{ + int accel_mode=0, dummy=0; + + if(!setup_resize_timer(mode)) + return; + + accel_mode=1+3*limit_and_encode_mode(&horizmul, &vertmul, &dummy, &dummy); + moveresmode_accel(mode, &horizmul, &vertmul, accel_mode); + + moveresmode_delta_resize(mode, horizmul, horizmul, vertmul, vertmul, + NULL); +} + + +/*EXTL_DOC + * Return from move/resize mode and apply changes unless opaque + * move/resize is enabled. + */ +EXTL_EXPORT_MEMBER +void moveresmode_finish(WMoveresMode *mode) +{ + WRegion *reg=moveresmode_target(mode); + if(moveresmode_do_end(mode, TRUE)){ + reset_resize_timer(); + region_warp(reg); + ioncore_grab_remove(resize_handler); + } +} + + +/*EXTL_DOC + * Return from move/resize cancelling changes if opaque + * move/resize has not been enabled. + */ +EXTL_EXPORT_MEMBER +void moveresmode_cancel(WMoveresMode *mode) +{ + WRegion *reg=moveresmode_target(mode); + if(moveresmode_do_end(mode, FALSE)){ + reset_resize_timer(); + region_warp(reg); + ioncore_grab_remove(resize_handler); + } +} + + +static void cancel_moveres(WRegion *reg) +{ + WMoveresMode *mode=moveres_mode(reg); + if(mode!=NULL) + moveresmode_cancel(mode); +} + + +/*EXTL_DOC + * Enter move/resize mode for \var{reg}. The bindings set with + * \fnref{ioncore.set_bindings} for \type{WMoveresMode} are used in + * this mode. Of the functions exported by the Ion C core, only + * \fnref{WMoveresMode.resize}, \fnref{WMoveresMode.move}, + * \fnref{WMoveresMode.cancel} and \fnref{WMoveresMode.end} are + * allowed to be called while in this mode. + */ +EXTL_EXPORT_MEMBER +WMoveresMode *region_begin_kbresize(WRegion *reg) +{ + WMoveresMode *mode=region_begin_resize(reg, NULL, FALSE); + + if(mode==NULL) + return NULL; + + if(!setup_resize_timer(mode)) + return NULL; + + accel_reset(); + + ioncore_grab_establish(reg, resize_handler, + (GrabKilledHandler*)cancel_moveres, 0); + + return mode; +} + + +/*}}}*/ diff --git a/ioncore/kbresize.h b/ioncore/kbresize.h new file mode 100644 index 0000000..3af0781 --- /dev/null +++ b/ioncore/kbresize.h @@ -0,0 +1,34 @@ +/* + * ion/ioncore/kbresize.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_KBRESIZE_H +#define ION_IONCORE_KBRESIZE_H + +#include "common.h" +#include "frame.h" +#include "resize.h" +#include + +extern WMoveresMode *region_begin_kbresize(WRegion *reg); + +extern void ioncore_set_moveres_accel(ExtlTab tab); +extern void ioncore_get_moveres_accel(ExtlTab tab); + +extern void moveresmode_finish(WMoveresMode *mode); +extern void moveresmode_cancel(WMoveresMode *mode); +extern void moveresmode_move(WMoveresMode *mode, + int horizmul, int vertmul); +extern void moveresmode_resize(WMoveresMode *mode, + int left, int right, int top, int bottom); +extern void moveresmode_accel(WMoveresMode *mode, + int *wu, int *hu, int accel_mode); + +#endif /* ION_IONCORE_KBRESIZE_H */ diff --git a/ioncore/key.c b/ioncore/key.c new file mode 100644 index 0000000..00848a4 --- /dev/null +++ b/ioncore/key.c @@ -0,0 +1,279 @@ +/* + * ion/ioncore/key.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include "common.h" +#include "key.h" +#include "binding.h" +#include "global.h" +#include "event.h" +#include "cursor.h" +#include "grab.h" +#include "regbind.h" +#include +#include "strings.h" +#include "xwindow.h" + + +static void waitrelease(WRegion *reg); +static void submapgrab(WRegion *reg); + + +static void insstr(WWindow *wwin, XKeyEvent *ev) +{ + static XComposeStatus cs={NULL, 0}; + char buf[32]={0,}; + Status stat; + int n, i; + KeySym ksym; + + if(wwin->xic!=NULL){ + if(XFilterEvent((XEvent*)ev, ev->window)) + return; + n=XmbLookupString(wwin->xic, ev, buf, 16, &ksym, &stat); + if(stat!=XLookupChars && stat!=XLookupBoth) + return; + }else{ + n=XLookupString(ev, buf, 32, &ksym, &cs); + } + + if(n<=0) + return; + + /* Won't catch bad strings, but should filter out most crap. */ + if(ioncore_g.use_mb){ + if(!iswprint(str_wchar_at(buf, 32))) + return; + }else{ + if(iscntrl(*buf)) + return; + } + + window_insstr(wwin, buf, n); +} + + +static void send_key(XEvent *ev, WClientWin *cwin) +{ + Window win=cwin->win; + ev->xkey.window=win; + ev->xkey.subwindow=None; + XSendEvent(ioncore_g.dpy, win, False, KeyPressMask, ev); +} + + +static bool quote_next_handler(WRegion *reg, XEvent *xev) +{ + XKeyEvent *ev=&xev->xkey; + if(ev->type!=KeyPress) + return FALSE; + if(ioncore_ismod(ev->keycode)) + return FALSE; + assert(OBJ_IS(reg, WClientWin)); + send_key(xev, (WClientWin*)reg); + return TRUE; /* remove the grab */ +} + + +/*EXTL_DOC + * Send next key press directly to \var{cwin}. + */ +EXTL_EXPORT_MEMBER +void clientwin_quote_next(WClientWin *cwin) +{ + ioncore_grab_establish((WRegion*)cwin, quote_next_handler, NULL, 0); + ioncore_change_grab_cursor(IONCORE_CURSOR_WAITKEY); +} + + +static bool waitrelease_handler(WRegion *reg, XEvent *ev) +{ + if(!ioncore_unmod(ev->xkey.state, ev->xkey.keycode)) + return TRUE; + return FALSE; +} + + +static void waitrelease(WRegion *reg) +{ + if(ioncore_modstate()==0) + return; + + /* We need to grab on the root window as might have been + * ioncore_defer_destroy:ed by the binding handler (the most common case + * for using this kpress_wait!). In such a case the grab may + * be removed before the modifiers are released. + */ + ioncore_grab_establish((WRegion*)region_rootwin_of(reg), + waitrelease_handler, + NULL, 0); + ioncore_change_grab_cursor(IONCORE_CURSOR_WAITKEY); +} + + +static void free_subs(WSubmapState *p) +{ + WSubmapState *next; + + while(p!=NULL){ + next=p->next; + free(p); + p=next; + } +} + + +static void clear_subs(WRegion *reg) +{ + while(reg->submapstat!=NULL){ + WSubmapState *tmp=reg->submapstat; + reg->submapstat=tmp->next; + free(tmp); + } +} + + +static bool add_sub(WRegion *reg, uint key, uint state) +{ + WSubmapState **p; + WSubmapState *s; + + if(reg->submapstat==NULL){ + p=&(reg->submapstat); + }else{ + s=reg->submapstat; + while(s->next!=NULL) + s=s->next; + p=&(s->next); + } + + s=ALLOC(WSubmapState); + + if(s==NULL) + return FALSE; + + s->key=key; + s->state=state; + + *p=s; + + return TRUE; + +} + + +static XKeyEvent *current_key_event=NULL; + + +XKeyEvent *ioncore_current_key_event() +{ + return current_key_event; +} + + +/* Return value TRUE = grab needed */ +static bool do_key(WRegion *reg, XKeyEvent *ev) +{ + WBinding *binding=NULL; + WRegion *oreg=NULL, *binding_owner=NULL, *subreg=NULL; + bool grabbed; + + oreg=reg; + grabbed=(oreg->flags®ION_BINDINGS_ARE_GRABBED); + + if(grabbed){ + /* Find the deepest nested active window grabbing this key. */ + while(reg->active_sub!=NULL) + reg=reg->active_sub; + + do{ + binding=region_lookup_keybinding(reg, ev, oreg->submapstat, + &binding_owner); + + if(binding!=NULL) + break; + if(OBJ_IS(reg, WRootWin)) + break; + + subreg=reg; + reg=REGION_PARENT_REG(reg); + }while(reg!=NULL); + }else{ + binding=region_lookup_keybinding(oreg, ev, oreg->submapstat, + &binding_owner); + } + + if(binding!=NULL){ + if(binding->submap!=NULL){ + if(add_sub(oreg, ev->keycode, ev->state)) + return grabbed; + else + clear_subs(oreg); + }else if(binding_owner!=NULL){ + WRegion *mgd=region_managed_within(binding_owner, subreg); + bool subs=(oreg->submapstat!=NULL); + + clear_subs(oreg); + + if(grabbed) + XUngrabKeyboard(ioncore_g.dpy, CurrentTime); + + if(!subs) + current_key_event=ev; + + /* TODO: having to pass both mgd and subreg for some handlers + * to work is ugly and complex. + */ + extl_call(binding->func, "ooo", NULL, binding_owner, mgd, subreg); + + current_key_event=NULL; + + if(ev->state!=0 && !subs && binding->wait) + waitrelease(oreg); + } + }else if(oreg->submapstat!=NULL){ + clear_subs(oreg); + }else if(OBJ_IS(oreg, WWindow)){ + insstr((WWindow*)oreg, ev); + } + + return FALSE; +} + + +static bool submapgrab_handler(WRegion* reg, XEvent *xev) +{ + XKeyEvent *ev=&xev->xkey; + if(ev->type!=KeyPress) + return FALSE; + if(ioncore_ismod(ev->keycode)) + return FALSE; + return !do_key(reg, ev); +} + + +static void submapgrab(WRegion *reg) +{ + ioncore_grab_establish(reg, submapgrab_handler, clear_subs, 0); + ioncore_change_grab_cursor(IONCORE_CURSOR_WAITKEY); +} + + +void ioncore_do_handle_keypress(XKeyEvent *ev) +{ + WRegion *reg=(WRegion*)XWINDOW_REGION_OF(ev->window); + + if(reg!=NULL){ + if(do_key(reg, ev)) + submapgrab(reg); + } +} diff --git a/ioncore/key.h b/ioncore/key.h new file mode 100644 index 0000000..e5ba29c --- /dev/null +++ b/ioncore/key.h @@ -0,0 +1,24 @@ +/* + * ion/ioncore/key.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_KEY_H +#define ION_IONCORE_KEY_H + +#include + +#include "common.h" +#include "clientwin.h" + +extern void ioncore_do_handle_keypress(XKeyEvent *ev); +extern void clientwin_quote_next(WClientWin *cwin); +extern XKeyEvent *ioncore_current_key_event(); + +#endif /* ION_IONCORE_KEY_H */ diff --git a/ioncore/llist.c b/ioncore/llist.c new file mode 100644 index 0000000..3baca81 --- /dev/null +++ b/ioncore/llist.c @@ -0,0 +1,100 @@ +/* + * ion/ioncore/llist.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include "common.h" +#include "llist.h" +#include "activity.h" + + +/*{{{ WMPlex numbered/mut.ex. list stuff */ + + +void llist_iter_init(WLListIterTmp *tmp, WLListNode *llist) +{ + *tmp=llist; +} + + +WLListNode *llist_iter(WLListIterTmp *tmp) +{ + WLListNode *mgd=*tmp; + if(mgd!=NULL) + *tmp=mgd->next; + return mgd; +} + + +WRegion *llist_iter_regions(WLListIterTmp *tmp) +{ + WLListNode *lnode=llist_iter(tmp); + return (lnode==NULL ? NULL : lnode->st->reg); +} + + +WLListNode *llist_nth_node(WLListNode *list, uint n) +{ + WLListIterTmp tmp; + llist_iter_init(&tmp, list); + return (WLListNode*)iterable_nth(n, (VoidIterator*)llist_iter, &tmp); +} + + +void llist_link_after(WLListNode **list, + WLListNode *after, WLListNode *node) +{ + if(after!=NULL){ + LINK_ITEM_AFTER(*list, after, node, next, prev); + }else{ + LINK_ITEM_FIRST(*list, node, next, prev); + } +} + + +void llist_link_last(WLListNode **list, WLListNode *node) +{ + LINK_ITEM_LAST(*list, node, next, prev); +} + + +WLListNode *llist_index_to_after(WLListNode *list, + WLListNode *current, + int index) +{ + if(index==LLIST_INDEX_AFTER_CURRENT_ACT){ + WLListNode *after=current; + while(after!=NULL){ + WLListNode *nxt=after->next; + if(nxt==NULL || nxt->st==NULL || nxt->st->reg==NULL) + break; + if(!region_is_activity_r(nxt->st->reg)) + break; + after=nxt; + } + return after; + }else if(index==LLIST_INDEX_AFTER_CURRENT){ + return current; + }else if(index<0){ + return (list!=NULL ? list->prev : NULL); + }else if(index==0){ + return NULL; + }else{ /* index>0 */ + return llist_nth_node(list, index-1); + } +} + + +void llist_unlink(WLListNode **list, WLListNode *node) +{ + UNLINK_ITEM(*list, node, next, prev); +} + + +/*}}}*/ diff --git a/ioncore/llist.h b/ioncore/llist.h new file mode 100644 index 0000000..83d148d --- /dev/null +++ b/ioncore/llist.h @@ -0,0 +1,56 @@ +/* + * ion/ioncore/llist.h + * + * Copyright (c) Tuomo Valkonen 2005-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_LLIST_H +#define ION_IONCORE_LLIST_H + +#include + +#include "mplex.h" +#include "mplexpholder.h" +#include "extlconv.h" +#include "stacking.h" + + +DECLSTRUCT(WLListNode){ + WLListNode *next, *prev; + WMPlexPHolder *phs; + WStacking *st; +}; + +typedef WLListNode *WLListIterTmp; + + +#define FOR_ALL_NODES_ON_LLIST(NODE, LL, TMP) \ + FOR_ALL_ITER(llist_iter_init, llist_iter, NODE, LL, &(TMP)) + +#define FOR_ALL_REGIONS_ON_LLIST(NODE, LL, TMP) \ + FOR_ALL_ITER(llist_iter_init, llist_iter_regions, NODE, LL, &(TMP)) + + +extern void llist_iter_init(WLListIterTmp *tmp, WLListNode *llist); +extern WLListNode *llist_iter(WLListIterTmp *tmp); +extern WRegion *llist_iter_regions(WLListIterTmp *tmp); +extern WLListNode *llist_nth_node(WLListNode *list, uint n); +extern ExtlTab llist_to_table(WLListNode *list); +extern void llist_link_after(WLListNode **list, + WLListNode *after, WLListNode *node); +extern void llist_link_last(WLListNode **list, WLListNode *node); +extern WLListNode *llist_index_to_after(WLListNode *list, + WLListNode *current, + int index); +extern void llist_unlink(WLListNode **list, WLListNode *node); + +#define LLIST_INDEX_LAST (-1) +#define LLIST_INDEX_AFTER_CURRENT (-2) +#define LLIST_INDEX_AFTER_CURRENT_ACT (-3) + +#endif /* ION_IONCORE_LLIST_H */ diff --git a/ioncore/manage.c b/ioncore/manage.c new file mode 100644 index 0000000..f1d78a0 --- /dev/null +++ b/ioncore/manage.c @@ -0,0 +1,284 @@ +/* + * ion/ioncore/manage.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include "global.h" +#include "common.h" +#include "region.h" +#include "manage.h" +#include "names.h" +#include "fullscreen.h" +#include "pointer.h" +#include "netwm.h" +#include "extlconv.h" + + +/*{{{ Add */ + + +WScreen *clientwin_find_suitable_screen(WClientWin *cwin, + const WManageParams *param) +{ + WScreen *scr=NULL, *found=NULL; + bool respectpos=(param->tfor!=NULL || param->userpos); + + FOR_ALL_SCREENS(scr){ + if(!region_same_rootwin((WRegion*)scr, (WRegion*)cwin)) + continue; + if(REGION_IS_ACTIVE(scr)){ + found=scr; + if(!respectpos) + break; + } + + if(rectangle_contains(®ION_GEOM(scr), param->geom.x, param->geom.y)){ + found=scr; + if(respectpos) + break; + } + + if(found==NULL) + found=scr; + } + + return found; +} + + +bool clientwin_do_manage_default(WClientWin *cwin, + const WManageParams *param) +{ + WRegion *r=NULL, *r2; + WScreen *scr=NULL; + WPHolder *ph=NULL; + int fs=-1; + int swf; + bool ok, tmp; + + /* Check if param->tfor or any of its managers want to manage cwin. */ + if(param->tfor!=NULL){ + assert(param->tfor!=cwin); + ph=region_prepare_manage_transient((WRegion*)param->tfor, cwin, + param, 0); + } + + /* Find a suitable screen */ + scr=clientwin_find_suitable_screen(cwin, param); + if(scr==NULL){ + warn(TR("Unable to find a screen for a new client window.")); + return FALSE; + } + + if(ph==NULL){ + /* Find a placeholder for non-fullscreen state */ + ph=region_prepare_manage((WRegion*)scr, cwin, param, + MANAGE_REDIR_PREFER_YES); + } + + /* Check fullscreen mode */ + if(extl_table_gets_b(cwin->proptab, "fullscreen", &tmp)) + fs=tmp; + + if(fs<0) + fs=netwm_check_initial_fullscreen(cwin, param->switchto); + + if(fs<0){ + fs=clientwin_check_fullscreen_request(cwin, + param->geom.w, + param->geom.h, + param->switchto); + } + + if(fs>0){ + /* Full-screen mode succesfull. */ + if(pholder_target(ph)==(WRegion*)scr){ + /* Discard useless placeholder. */ + destroy_obj((Obj*)ph); + return TRUE; + } + assert(cwin->fs_pholder==NULL); + cwin->fs_pholder=ph; + return TRUE; + } + + if(ph==NULL) + return FALSE; + + /* Not in full-screen mode; use the placeholder to attach. */ + + swf=(param->switchto ? PHOLDER_ATTACH_SWITCHTO : 0); + ok=pholder_attach(ph, swf, (WRegion*)cwin); + destroy_obj((Obj*)ph); + + return ok; +} + + +/*}}}*/ + + +/*{{{ region_prepare_manage/region_manage_clientwin/etc. */ + + +WPHolder *region_prepare_manage(WRegion *reg, const WClientWin *cwin, + const WManageParams *param, int redir) +{ + WPHolder *ret=NULL; + CALL_DYN_RET(ret, WPHolder*, region_prepare_manage, reg, + (reg, cwin, param, redir)); + return ret; +} + + +WPHolder *region_prepare_manage_default(WRegion *reg, const WClientWin *cwin, + const WManageParams *param, int redir) +{ + WRegion *curr; + + if(redir==MANAGE_REDIR_STRICT_NO) + return NULL; + + curr=region_current(reg); + + if(curr==NULL) + return NULL; + + return region_prepare_manage(curr, cwin, param, MANAGE_REDIR_PREFER_YES); +} + + +WPHolder *region_prepare_manage_transient(WRegion *reg, + const WClientWin *cwin, + const WManageParams *param, + int unused) +{ + WPHolder *ret=NULL; + CALL_DYN_RET(ret, WPHolder*, region_prepare_manage_transient, reg, + (reg, cwin, param, unused)); + return ret; +} + + +WPHolder *region_prepare_manage_transient_default(WRegion *reg, + const WClientWin *cwin, + const WManageParams *param, + int unused) +{ + WRegion *mgr=REGION_MANAGER(reg); + + if(mgr!=NULL) + return region_prepare_manage_transient(mgr, cwin, param, unused); + else + return NULL; +} + + +bool region_manage_clientwin(WRegion *reg, WClientWin *cwin, + const WManageParams *par, int redir) +{ + bool ret; + WPHolder *ph=region_prepare_manage(reg, cwin, par, redir); + int swf=(par->switchto ? PHOLDER_ATTACH_SWITCHTO : 0); + + if(ph==NULL) + return FALSE; + + ret=pholder_attach(ph, swf, (WRegion*)cwin); + + destroy_obj((Obj*)ph); + + return ret; +} + + +/*}}}*/ + + +/*{{{ Rescue */ + + +static WRegion *iter_children(void *st) +{ + WRegion **nextp=(WRegion**)st; + WRegion *next=*nextp; + *nextp=(next==NULL ? NULL : next->p_next); + return next; +} + + +bool region_rescue_child_clientwins(WRegion *reg, WPHolder *ph) +{ + WRegion *next=reg->children; + return region_rescue_some_clientwins(reg, ph, iter_children, &next); +} + + +bool region_rescue_some_clientwins(WRegion *reg, WPHolder *ph, + WRegionIterator *iter, void *st) +{ + bool res=FALSE; + int fails=0; + + reg->flags|=REGION_CWINS_BEING_RESCUED; + + while(TRUE){ + WRegion *tosave=iter(st); + + if(tosave==NULL) + break; + + if(!OBJ_IS(tosave, WClientWin)){ + if(!region_rescue_clientwins(tosave, ph)) + fails++; + }else{ + if(!pholder_attach(ph, 0, tosave)) + fails++; + } + } + + reg->flags&=~REGION_CWINS_BEING_RESCUED; + + return (fails==0); +} + + +bool region_rescue_clientwins(WRegion *reg, WPHolder *ph) +{ + bool ret=FALSE; + CALL_DYN_RET(ret, bool, region_rescue_clientwins, reg, (reg, ph)); + return ret; +} + + +/*}}}*/ + + +/*{{{ Misc. */ + + +ExtlTab manageparams_to_table(const WManageParams *mp) +{ + ExtlTab t=extl_create_table(); + extl_table_sets_b(t, "switchto", mp->switchto); + extl_table_sets_b(t, "jumpto", mp->jumpto); + extl_table_sets_b(t, "userpos", mp->userpos); + extl_table_sets_b(t, "dockapp", mp->dockapp); + extl_table_sets_b(t, "maprq", mp->maprq); + extl_table_sets_i(t, "gravity", mp->gravity); + extl_table_sets_rectangle(t, "geom", &(mp->geom)); + extl_table_sets_o(t, "tfor", (Obj*)(mp->tfor)); + + return t; +} + + +/*}}}*/ diff --git a/ioncore/manage.h b/ioncore/manage.h new file mode 100644 index 0000000..5354bea --- /dev/null +++ b/ioncore/manage.h @@ -0,0 +1,93 @@ +/* + * ion/ioncore/manage.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_MANAGE_H +#define ION_IONCORE_MANAGE_H + +#include +#include "common.h" + +INTRSTRUCT(WManageParams); + +#include "clientwin.h" +#include "attach.h" +#include "rectangle.h" +#include "extlconv.h" +#include "pholder.h" + + +#define MANAGEPARAMS_INIT \ + {FALSE, FALSE, FALSE, FALSE, FALSE, ForgetGravity, {0, 0, 0, 0}, NULL} + +enum{ + MANAGE_REDIR_PREFER_YES, + MANAGE_REDIR_PREFER_NO, + MANAGE_REDIR_STRICT_YES, + MANAGE_REDIR_STRICT_NO +}; + +DECLSTRUCT(WManageParams){ + bool switchto; + bool jumpto; + bool userpos; + bool dockapp; + bool maprq; + int gravity; + WRectangle geom; + WClientWin *tfor; +}; + + +typedef WRegion *WRegionIterator(void *st); + + +extern ExtlTab manageparams_to_table(const WManageParams *mp); + + +extern WScreen *clientwin_find_suitable_screen(WClientWin *cwin, + const WManageParams *param); + +/* Manage */ + +extern bool clientwin_do_manage_default(WClientWin *cwin, + const WManageParams *param); + +extern bool region_manage_clientwin(WRegion *reg, WClientWin *cwin, + const WManageParams *par, int redir); + +DYNFUN WPHolder *region_prepare_manage(WRegion *reg, const WClientWin *cwin, + const WManageParams *par, int redir); + +extern WPHolder *region_prepare_manage_default(WRegion *reg, + const WClientWin *cwin, + const WManageParams *par, + int redir); + + +extern WPHolder *region_prepare_manage_transient(WRegion *reg, + const WClientWin *cwin, + const WManageParams *param, + int unused); + +extern WPHolder *region_prepare_manage_transient_default(WRegion *reg, + const WClientWin *cwin, + const WManageParams *param, + int unused); + +/* Rescue */ + +extern bool region_rescue_clientwins(WRegion *reg, WPHolder *ph); +extern bool region_rescue_child_clientwins(WRegion *reg, WPHolder *ph); +extern bool region_rescue_some_clientwins(WRegion *reg, WPHolder *ph, + WRegionIterator *iter, void *st); + + +#endif /* ION_IONCORE_MANAGE_H */ diff --git a/ioncore/modules.c b/ioncore/modules.c new file mode 100644 index 0000000..f7f6242 --- /dev/null +++ b/ioncore/modules.c @@ -0,0 +1,352 @@ +/* + * ion/ioncore/modules.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include + +#include +#include + +#include "common.h" +#include "modules.h" +#include "../version.h" + + +#ifndef CF_PRELOAD_MODULES + + +/*{{{ Module list */ + + +typedef void *dlhandle; + + +static Rb_node modules=NULL; + + +static dlhandle get_handle(const char *name) +{ + int found=0; + Rb_node nd; + + nd=rb_find_key_n(modules, name, &found); + if(found) + return nd->v.val; + return NULL; +} + + +static const char *get_name(dlhandle handle) +{ + Rb_node nd; + + rb_traverse(nd, modules){ + if(nd->v.val==handle) + return (const char *)(nd->k.key); + } + return NULL; +} + + +static Rb_node add_module(char *name, dlhandle handle) +{ + return rb_insert(modules, name, handle); +} + + +/*}}}*/ + + +/*{{{ Module symbol access */ + + +static void *get_module_symbol(dlhandle handle, + const char *modulename, + const char *name) +{ + char *p; + void *ret; + + p=scat(modulename, name); + + if(p==NULL) + return NULL; + + ret=dlsym(handle, p); + + free(p); + + return ret; +} + +static bool check_version(dlhandle handle, const char *modulename) +{ + char *versionstr=(char*)get_module_symbol(handle, modulename, + "_ion_api_version"); + if(versionstr==NULL) + return FALSE; + return (strcmp(versionstr, ION_API_VERSION)==0); +} + + +static bool call_init(dlhandle handle, const char *modulename) +{ + bool (*initfn)(void); + + initfn=(bool (*)())get_module_symbol(handle, modulename, "_init"); + + if(initfn==NULL) + return TRUE; + + return initfn(); +} + + +static void call_deinit(dlhandle handle, const char *modulename) +{ + void (*deinitfn)(void); + + deinitfn=(void (*)())get_module_symbol(handle, modulename, "_deinit"); + + if(deinitfn!=NULL) + deinitfn(); +} + + +/*}}}*/ + + +/*{{{ Init */ + + +bool ioncore_init_module_support() +{ + modules=make_rb(); + return (modules!=NULL); +} + + +static int try_load(const char *file, void *param) +{ + dlhandle handle=NULL; + const char *slash, *dot; + char *name; + Rb_node mod; + + if(access(file, F_OK)!=0) + return EXTL_TRYCONFIG_NOTFOUND; + + slash=strrchr(file, '/'); + dot=strrchr(file, '.'); + + if(slash==NULL) + slash=file; + else + slash++; + + if(dot<=slash){ + warn(TR("Invalid module name.")); + goto err1; + } + + name=ALLOC_N(char, dot-slash+1); + if(name==NULL) + goto err1; + + strncpy(name, slash, dot-slash); + name[dot-slash]='\0'; + + if(get_handle(name)){ + warn_obj(file, TR("The module is already loaded.")); + goto err2; + } + + handle=dlopen(file, RTLD_NOW|RTLD_GLOBAL); + + if(handle==NULL){ + warn_obj(file, "%s", dlerror()); + goto err2; + } + + if(get_name(handle)) + return EXTL_TRYCONFIG_OK; + + if(!check_version(handle, name)){ + warn_obj(file, TR("Module version information not found or " + "version mismatch. Refusing to use.")); + goto err3; + } + + mod=add_module(name, handle); + + if(mod==NULL) + goto err3; + + if(!call_init(handle, name)){ + warn_obj(file, TR("Unable to initialise module %s."), name); + rb_delete_node(mod); + goto err3; + } + + return EXTL_TRYCONFIG_OK; + +err3: + dlclose(handle); +err2: + free(name); +err1: + return EXTL_TRYCONFIG_LOAD_FAILED; +} + + +static bool do_load_module(const char *modname) +{ + int retval; + + retval=extl_try_config(modname, NULL, (ExtlTryConfigFn*)try_load, + NULL, "so", NULL); + + if(retval==EXTL_TRYCONFIG_NOTFOUND) + warn(TR("Unable to find '%s' on search path."), modname); + + return (retval==EXTL_TRYCONFIG_OK); +} + + +/*}}}*/ + + +/*{{{ Deinit */ + + +static void do_unload_module(Rb_node mod) +{ + char *name=(char*)mod->k.key; + dlhandle handle=mod->v.val; + + call_deinit(handle, name); + + dlclose(handle); + free(name); +} + + +void ioncore_unload_modules() +{ + Rb_node mod; + + rb_traverse(mod, modules){ + do_unload_module(mod); + } +} + + +/*}}}*/ + + +#else + + +/*{{{ Static module support */ + + +static bool call_init(WStaticModuleInfo *handle) +{ + if(handle->init!=NULL) + return handle->init(); + return TRUE; +} + + +static void call_deinit(WStaticModuleInfo *handle) +{ + if(handle->deinit!=NULL) + handle->deinit(); +} + + +extern WStaticModuleInfo ioncore_static_modules[]; + + +static bool do_load_module(const char *name) +{ + WStaticModuleInfo *mod; + + for(mod=ioncore_static_modules; mod->name!=NULL; mod++){ + if(strcmp(mod->name, name)==0) + break; + } + + if(mod->name==NULL){ + warn_obj(name, TR("Unknown module.")); + return FALSE; + } + + if(mod->loaded) + return TRUE; + + if(!call_init(mod)){ + warn_obj(name, TR("Unable to initialise module.")); + return FALSE; + } + + mod->loaded=TRUE; + + return TRUE; +} + + +void ioncore_unload_modules() +{ + WStaticModuleInfo *mod; + + for(mod=ioncore_static_modules; mod->name!=NULL; mod++){ + if(mod->loaded){ + call_deinit(mod); + mod->loaded=FALSE; + } + } +} + + +bool ioncore_init_module_support() +{ + return TRUE; +} + + +/*}}}*/ + + +#endif + + +/*{{{ Exports */ + + +/*EXTL_DOC + * Attempt to load a C-side module. + */ +EXTL_EXPORT +bool ioncore_load_module(const char *modname) +{ + if(modname==NULL){ + warn(TR("No module to load given.")); + return FALSE; + } + return do_load_module(modname); +} + + +/*}}}*/ + diff --git a/ioncore/modules.h b/ioncore/modules.h new file mode 100644 index 0000000..6cc565e --- /dev/null +++ b/ioncore/modules.h @@ -0,0 +1,28 @@ +/* + * ion/ioncore/modules.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_MODULES_H +#define ION_IONCORE_MODULES_H + +#include "common.h" + +extern bool ioncore_load_module(const char *name); +extern void ioncore_unload_modules(); +extern bool ioncore_init_module_support(); + +typedef struct{ + const char *name; + bool (*init)(); + void (*deinit)(); + bool loaded; +} WStaticModuleInfo; + +#endif /* ION_IONCORE_MODULES_H */ diff --git a/ioncore/mplex.c b/ioncore/mplex.c new file mode 100644 index 0000000..158b9ff --- /dev/null +++ b/ioncore/mplex.c @@ -0,0 +1,2083 @@ +/* + * ion/ioncore/mplex.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include "common.h" +#include "window.h" +#include "global.h" +#include "rootwin.h" +#include "focus.h" +#include "event.h" +#include "attach.h" +#include "manage.h" +#include "resize.h" +#include "tags.h" +#include "sizehint.h" +#include "extlconv.h" +#include "frame-pointer.h" +#include "bindmaps.h" +#include "regbind.h" +#include "saveload.h" +#include "xwindow.h" +#include "mplexpholder.h" +#include "llist.h" +#include "names.h" +#include "sizepolicy.h" +#include "stacking.h" +#include "group.h" +#include "navi.h" +#include "groupedpholder.h" + + +#define SUBS_MAY_BE_MAPPED(MPLEX) \ + (REGION_IS_MAPPED(MPLEX) && !MPLEX_MGD_UNVIEWABLE(MPLEX)) + +#define PASSIVE(ST) ((ST)->level<=STACKING_LEVEL_MODAL1 \ + && ((ST)->reg==NULL \ + || (ST)->reg->flags®ION_SKIP_FOCUS)) + +#define CAN_MANAGE_STDISP(REG) HAS_DYN(REG, region_manage_stdisp) + + +/*{{{ Stacking list stuff */ + + +WStacking *mplex_get_stacking(WMPlex *mplex) +{ + return window_get_stacking(&mplex->win); +} + + +WStacking **mplex_get_stackingp(WMPlex *mplex) +{ + return window_get_stackingp(&mplex->win); +} + + +void mplex_iter_init(WMPlexIterTmp *tmp, WMPlex *mplex) +{ + stacking_iter_mgr_init(tmp, mplex->mgd, NULL, mplex); +} + + +WRegion *mplex_iter(WMPlexIterTmp *tmp) +{ + return stacking_iter_mgr(tmp); +} + + +WStacking *mplex_iter_nodes(WMPlexIterTmp *tmp) +{ + return stacking_iter_mgr_nodes(tmp); +} + + +/*}}}*/ + + +/*{{{ Destroy/create mplex */ + + +bool mplex_do_init(WMPlex *mplex, WWindow *parent, Window win, + const WFitParams *fp, bool create) +{ + mplex->flags=0; + + mplex->mx_list=NULL; + mplex->mx_current=NULL; + mplex->mx_phs=NULL; + mplex->mx_count=0; + + mplex->mgd=NULL; + + watch_init(&(mplex->stdispwatch)); + mplex->stdispinfo.pos=MPLEX_STDISP_BL; + mplex->stdispinfo.fullsize=FALSE; + + if(create){ + if(!window_init((WWindow*)mplex, parent, fp)) + return FALSE; + }else{ + if(!window_do_init((WWindow*)mplex, parent, win, fp)) + return FALSE; + } + + mplex->win.region.flags|=REGION_BINDINGS_ARE_GRABBED; + + window_select_input(&(mplex->win), IONCORE_EVENTMASK_CWINMGR); + + region_add_bindmap((WRegion*)mplex, ioncore_mplex_bindmap); + region_add_bindmap((WRegion*)mplex, ioncore_mplex_toplevel_bindmap); + + region_register((WRegion*)mplex); + + /* Call this to set MPLEX_MANAGED_UNVIEWABLE if necessary. */ + mplex_fit_managed(mplex); + + return TRUE; +} + + +bool mplex_init(WMPlex *mplex, WWindow *parent, const WFitParams *fp) +{ + return mplex_do_init(mplex, parent, None, fp, TRUE); +} + + +WMPlex *create_mplex(WWindow *parent, const WFitParams *fp) +{ + CREATEOBJ_IMPL(WMPlex, mplex, (p, parent, fp)); +} + + +void mplex_deinit(WMPlex *mplex) +{ + WMPlexIterTmp tmp; + WRegion *reg; + + FOR_ALL_MANAGED_BY_MPLEX(mplex, reg, tmp){ + destroy_obj((Obj*)reg); + } + + assert(mplex->mgd==NULL); + assert(mplex->mx_list==NULL); + + while(mplex->mx_phs!=NULL){ + assert(mplexpholder_move(mplex->mx_phs, NULL, NULL, NULL)); + } + + window_deinit((WWindow*)mplex); +} + + +bool mplex_may_destroy(WMPlex *mplex) +{ + if(mplex->mgd!=NULL){ + warn(TR("Refusing to destroy - not empty.")); + return FALSE; + } + + return TRUE; +} + + +/*}}}*/ + + +/*{{{ Node lookup etc. */ + + +WStacking *mplex_find_stacking(WMPlex *mplex, WRegion *reg) +{ + WStacking *st; + + /* Some routines that call us expect us to this check. */ + if(reg==NULL || REGION_MANAGER(reg)!=(WRegion*)mplex) + return NULL; + + st=ioncore_find_stacking(reg); + + assert(st==NULL || st->mgr_prev!=NULL); + + return st; +} + + +WStacking *mplex_current_node(WMPlex *mplex) +{ + WStacking *st=NULL; + WRegion *reg; + + reg=REGION_ACTIVE_SUB(mplex); + reg=region_managed_within((WRegion*)mplex, reg); + if(reg!=NULL) + st=mplex_find_stacking(mplex, reg); + + if(st!=NULL) + return st; + else + return (mplex->mx_current!=NULL ? mplex->mx_current->st : NULL); +} + + +WRegion *mplex_current(WMPlex *mplex) +{ + WStacking *node=mplex_current_node(mplex); + return (node==NULL ? NULL : node->reg); +} + + +/*}}}*/ + + +/*{{{ Exclusive list management and exports */ + +/*EXTL_DOC + * Returns the number of objects on the mutually exclusive list of \var{mplex}. + */ +EXTL_SAFE +EXTL_EXPORT_MEMBER +int mplex_mx_count(WMPlex *mplex) +{ + return mplex->mx_count; +} + + +/*EXTL_DOC + * Returns the managed object currently active within the mutually exclusive + * list of \var{mplex}. + */ +EXTL_SAFE +EXTL_EXPORT_MEMBER +WRegion *mplex_mx_current(WMPlex *mplex) +{ + WLListNode *lnode=mplex->mx_current; + return (lnode==NULL ? NULL : lnode->st->reg); +} + + +/*EXTL_DOC + * Returns the \var{n}:th object managed by \var{mplex} on the + * \var{l}:th layer. + */ +EXTL_SAFE +EXTL_EXPORT_MEMBER +WRegion *mplex_mx_nth(WMPlex *mplex, uint n) +{ + WLListNode *lnode=llist_nth_node(mplex->mx_list, n); + return (lnode==NULL ? NULL : lnode->st->reg); +} + + +/*EXTL_DOC + * Returns a list of regions on the numbered/mutually exclusive list of + * \var{mplex}. + */ +EXTL_SAFE +EXTL_EXPORT_MEMBER +ExtlTab mplex_mx_list(WMPlex *mplex) +{ + WLListIterTmp tmp; + llist_iter_init(&tmp, mplex->mx_list); + + return extl_obj_iterable_to_table((ObjIterator*)llist_iter_regions, &tmp); +} + + +/*EXTL_DOC + * Returns a list of all regions managed by \var{mplex}. + */ +EXTL_SAFE +EXTL_EXPORT_MEMBER +ExtlTab mplex_managed_list(WMPlex *mplex) +{ + WMPlexIterTmp tmp; + mplex_iter_init(&tmp, mplex); + + return extl_obj_iterable_to_table((ObjIterator*)mplex_iter, &tmp); +} + + +/*EXTL_DOC + * Set index of \var{reg} within the multiplexer to \var{index} within + * the mutually exclusive list. Special values for \var{index} are: + * \begin{tabularx}{\linewidth}{lX} + * $-1$ & After \fnref{WMPlex.mx_current}. \\ + * $-2$ & Last. \\ + * \end{tabularx} + */ +EXTL_EXPORT_MEMBER +void mplex_set_index(WMPlex *mplex, WRegion *reg, int index) +{ + WLListNode *lnode, *after; + WStacking *node; + + node=mplex_find_stacking(mplex, reg); + + if(node==NULL) + return; + + lnode=node->lnode; + + if(lnode==NULL){ + lnode=ALLOC(WLListNode); + if(lnode==NULL) + return; + lnode->next=NULL; + lnode->prev=NULL; + lnode->phs=NULL; + lnode->st=node; + node->lnode=lnode; + mplex->mx_count++; + }else{ + mplex_move_phs_before(mplex, lnode); + llist_unlink(&(mplex->mx_list), lnode); + } + + /* TODO: Support remove? */ + + after=llist_index_to_after(mplex->mx_list, mplex->mx_current, index); + llist_link_after(&(mplex->mx_list), after, lnode); + mplex_managed_changed(mplex, MPLEX_CHANGE_REORDER, FALSE, reg); +} + + +/*EXTL_DOC + * Get index of \var{reg} within the multiplexer on list 1. The first region + * managed by \var{mplex} has index zero. If \var{reg} is not managed by + * \var{mplex}, -1 is returned. + */ +EXTL_SAFE +EXTL_EXPORT_MEMBER +int mplex_get_index(WMPlex *mplex, WRegion *reg) +{ + WLListIterTmp tmp; + WLListNode *lnode; + int index=0; + + FOR_ALL_NODES_ON_LLIST(lnode, mplex->mx_list, tmp){ + if(reg==lnode->st->reg) + return index; + index++; + } + + return -1; +} + + +/*EXTL_DOC + * Move \var{r} ''right'' within objects managed by \var{mplex} on list 1. + */ +EXTL_EXPORT_MEMBER +void mplex_inc_index(WMPlex *mplex, WRegion *r) +{ + if(r==NULL) + r=mplex_mx_current(mplex); + if(r!=NULL) + mplex_set_index(mplex, r, mplex_get_index(mplex, r)+1); +} + + +/*EXTL_DOC + * Move \var{r} ''right'' within objects managed by \var{mplex} on list 1. + */ +EXTL_EXPORT_MEMBER +void mplex_dec_index(WMPlex *mplex, WRegion *r) +{ + if(r==NULL) + r=mplex_mx_current(mplex); + if(r!=NULL) + mplex_set_index(mplex, r, mplex_get_index(mplex, r)-1); +} + + +/*}}}*/ + + +/*{{{ Mapping */ + + +static void mplex_map_mgd(WMPlex *mplex) +{ + WMPlexIterTmp tmp; + WStacking *node; + + FOR_ALL_NODES_IN_MPLEX(mplex, node, tmp){ + if(!STACKING_IS_HIDDEN(node)) + region_map(node->reg); + } +} + + +static void mplex_unmap_mgd(WMPlex *mplex) +{ + WMPlexIterTmp tmp; + WStacking *node; + + FOR_ALL_NODES_IN_MPLEX(mplex, node, tmp){ + if(!STACKING_IS_HIDDEN(node)) + region_unmap(node->reg); + } +} + + + +void mplex_map(WMPlex *mplex) +{ + window_map((WWindow*)mplex); + /* A lame requirement of the ICCCM is that client windows should be + * unmapped if the parent is unmapped. + */ + if(!MPLEX_MGD_UNVIEWABLE(mplex)) + mplex_map_mgd(mplex); +} + + +void mplex_unmap(WMPlex *mplex) +{ + window_unmap((WWindow*)mplex); + /* A lame requirement of the ICCCM is that client windows should be + * unmapped if the parent is unmapped. + */ + if(!MPLEX_MGD_UNVIEWABLE(mplex)) + mplex_unmap_mgd(mplex); +} + + +/*}}}*/ + + +/*{{{ Resize and reparent */ + + +bool mplex_fitrep(WMPlex *mplex, WWindow *par, const WFitParams *fp) +{ + bool wchg=(REGION_GEOM(mplex).w!=fp->g.w); + bool hchg=(REGION_GEOM(mplex).h!=fp->g.h); + + window_do_fitrep(&(mplex->win), par, &(fp->g)); + + if(wchg || hchg){ + mplex_fit_managed(mplex); + mplex_size_changed(mplex, wchg, hchg); + } + + return TRUE; +} + + +void mplex_do_fit_managed(WMPlex *mplex, WFitParams *fp) +{ + WRectangle geom; + WMPlexIterTmp tmp; + WStacking *node; + WFitParams fp2; + + if(!MPLEX_MGD_UNVIEWABLE(mplex) && (fp->g.w<=1 || fp->g.h<=1)){ + mplex->flags|=MPLEX_MANAGED_UNVIEWABLE; + if(REGION_IS_MAPPED(mplex)) + mplex_unmap_mgd(mplex); + }else if(MPLEX_MGD_UNVIEWABLE(mplex) && !(fp->g.w<=1 || fp->g.h<=1)){ + mplex->flags&=~MPLEX_MANAGED_UNVIEWABLE; + if(REGION_IS_MAPPED(mplex)) + mplex_map_mgd(mplex); + } + + if(!MPLEX_MGD_UNVIEWABLE(mplex)){ + FOR_ALL_NODES_IN_MPLEX(mplex, node, tmp){ + fp2=*fp; + sizepolicy(&node->szplcy, node->reg, NULL, 0, &fp2); + region_fitrep(node->reg, NULL, &fp2); + } + } +} + + +void mplex_fit_managed(WMPlex *mplex) +{ + WFitParams fp; + + fp.mode=REGION_FIT_EXACT; + mplex_managed_geom(mplex, &(fp.g)); + + mplex_do_fit_managed(mplex, &fp); +} + + +static void mplex_managed_rqgeom(WMPlex *mplex, WRegion *sub, + const WRQGeomParams *rq, + WRectangle *geomret) +{ + WRectangle rg; + WFitParams fp; + WStacking *node; + + node=mplex_find_stacking(mplex, sub); + + assert(node!=NULL); + + mplex_managed_geom(mplex, &fp.g); + + sizepolicy(&node->szplcy, sub, &rq->geom, &rq->flags, &fp); + + if(geomret!=NULL) + *geomret=fp.g; + + if(!(rq->flags®ION_RQGEOM_TRYONLY)) + region_fitrep(sub, NULL, &fp); +} + + +/*}}}*/ + + +/*{{{ Focus */ + + +static WRegion *mplex_do_to_focus(WMPlex *mplex, WStacking *to_try) +{ + WStacking *stacking=mplex_get_stacking(mplex); + WStacking *st=NULL; + + if(stacking==NULL) + return NULL; + + if(to_try!=NULL && (to_try->reg==NULL || !REGION_IS_MAPPED(to_try->reg))) + to_try=NULL; + + st=stacking_find_to_focus_mapped(stacking, to_try, NULL); + + if(st!=NULL) + return st->reg; + else if(mplex->mx_current!=NULL) + return mplex->mx_current->st->reg; + else + return NULL; +} + + +WRegion *mplex_to_focus(WMPlex *mplex) +{ + WRegion *reg=REGION_ACTIVE_SUB(mplex); + WStacking *to_try=NULL; + + if(reg!=NULL) + to_try=ioncore_find_stacking(reg); + + return mplex_do_to_focus(mplex, to_try); +} + + +static WRegion *mplex_do_to_focus_on(WMPlex *mplex, WStacking *node, + WStacking *to_try) +{ + WStacking *stacking=mplex_get_stacking(mplex); + WStacking *st=NULL; + + if(stacking==NULL) + return NULL; + + if(to_try!=NULL && (to_try->reg==NULL || !REGION_IS_MAPPED(to_try->reg))) + to_try=NULL; + + st=stacking_find_to_focus_mapped(stacking, to_try, node->reg); + + return (st!=NULL ? st->reg : NULL); +} + + +static WRegion *mplex_to_focus_on(WMPlex *mplex, WStacking *node, + WStacking *to_try) +{ + WRegion *reg; + WGroup *grp=OBJ_CAST(node->reg, WGroup); + + if(grp!=NULL){ + if(to_try==NULL) + to_try=grp->current_managed; + reg=mplex_do_to_focus_on(mplex, node, to_try); + if(reg!=NULL || to_try!=NULL) + return reg; + /* We don't know whether something is blocking focus here, + * or if there was nothing to focus (as node->reg itself + * isn't on the stacking list). + */ + } + + reg=mplex_do_to_focus(mplex, node); + return (reg==node->reg ? reg : NULL); +} + + +void mplex_do_set_focus(WMPlex *mplex, bool warp) +{ + if(!MPLEX_MGD_UNVIEWABLE(mplex)){ + WRegion *reg=mplex_to_focus(mplex); + + if(reg!=NULL){ + region_do_set_focus(reg, warp); + return; + } + } + + window_do_set_focus((WWindow*)mplex, warp); +} + + +/*}}}*/ + + +/*{{{ Switch */ + + +static void mplex_do_remanage_stdisp(WMPlex *mplex, WRegion *sub) +{ + WRegion *stdisp=(WRegion*)(mplex->stdispwatch.obj); + + /* Move stdisp */ + if(sub!=NULL && CAN_MANAGE_STDISP(sub)){ + if(stdisp!=NULL){ + WRegion *mgrw=region_managed_within((WRegion*)mplex, stdisp); + if(mgrw!=sub){ + WRegion *mgr=REGION_MANAGER(stdisp); + if(mgr!=NULL){ + if(CAN_MANAGE_STDISP(mgr)) + region_unmanage_stdisp(mgr, FALSE, FALSE); + region_detach_manager(stdisp); + } + + region_manage_stdisp(sub, stdisp, + &(mplex->stdispinfo)); + } + }else{ + region_unmanage_stdisp(sub, TRUE, FALSE); + } + }/*else if(stdisp!=NULL){ + region_detach_manager(stdisp); + region_unmap(stdisp); + }*/ +} + + +void mplex_remanage_stdisp(WMPlex *mplex) +{ + mplex_do_remanage_stdisp(mplex, (mplex->mx_current!=NULL + ? mplex->mx_current->st->reg + : NULL)); +} + + +static void mplex_do_node_display(WMPlex *mplex, WStacking *node, + bool call_changed) +{ + WRegion *sub=node->reg; + WLListNode *mxc=mplex->mx_current; + + if(!STACKING_IS_HIDDEN(node)) + return; + + if(node->lnode!=NULL && node->lnode!=mxc) + mplex_do_remanage_stdisp(mplex, sub); + + node->hidden=FALSE; + + if(SUBS_MAY_BE_MAPPED(mplex)) + region_map(sub); + else + region_unmap(sub); + + if(node->lnode!=NULL){ + if(mxc!=NULL){ + /* Hide current mx region. We do it after mapping the + * new one to avoid flicker. + */ + if(REGION_IS_MAPPED(mplex)) + region_unmap(mxc->st->reg); + mxc->st->hidden=TRUE; + } + + mplex->mx_current=node->lnode; + + /* Ugly hack: + * Many programs will get upset if the visible, although only + * such, client window is not the lowest window in the mplex. + * xprop/xwininfo will return the information for the lowest + * window. 'netscape -remote' will not work at all if there are + * no visible netscape windows. + */ + { + #warning "TODO: less ugly hack" + WGroup *grp=(WGroup*)OBJ_CAST(sub, WGroupCW); + if(grp!=NULL && grp->bottom!=NULL){ + region_managed_rqorder((WRegion*)grp, grp->bottom->reg, + REGION_ORDER_BACK); + } + } + + if(call_changed) + mplex_managed_changed(mplex, MPLEX_CHANGE_SWITCHONLY, TRUE, sub); + } +} + + +static bool mplex_refocus(WMPlex *mplex, WStacking *node, bool warp) +{ + WRegion *foc=NULL; + bool ret=TRUE; + + if(node!=NULL){ + foc=mplex_to_focus_on(mplex, node, NULL); + ret=(foc!=NULL); + } + + if(foc==NULL){ + ret=FALSE; + foc=mplex_to_focus(mplex); + } + + if(foc!=NULL /* && !REGION_IS_ACTIVE(foc) */ ) + region_maybewarp(foc, warp); + + return ret; +} + + +bool mplex_do_prepare_focus(WMPlex *mplex, WStacking *node, + WStacking *sub, int flags, + WPrepareFocusResult *res) +{ + WRegion *foc; + + if(sub==NULL && node==NULL) + return FALSE; + + /* Display the node in any case */ + if(node!=NULL && !(flags®ION_GOTO_ENTERWINDOW)) + mplex_do_node_display(mplex, node, TRUE); + + if(!region_prepare_focus((WRegion*)mplex, flags, res)) + return FALSE; + + if(node!=NULL) + foc=mplex_to_focus_on(mplex, node, sub); + else + foc=mplex_do_to_focus(mplex, sub); + + if(foc!=NULL){ + res->reg=foc; + res->flags=flags; + + if(sub==NULL) + return (foc==node->reg); + else + return (foc==sub->reg); + }else{ + return FALSE; + } +} + + +bool mplex_managed_prepare_focus(WMPlex *mplex, WRegion *disp, + int flags, WPrepareFocusResult *res) +{ + WStacking *node=mplex_find_stacking(mplex, disp); + + if(node==NULL) + return FALSE; + else + return mplex_do_prepare_focus(mplex, node, NULL, flags, res); +} + + +/*}}}*/ + + +/*{{{ Switch exports */ + + +static void do_switch(WMPlex *mplex, WLListNode *lnode) +{ + WStacking *node=(lnode!=NULL ? lnode->st : NULL); + + if(node!=NULL){ + bool mcf=region_may_control_focus((WRegion*)mplex); + + mplex_do_node_display(mplex, node, TRUE); + + if(mcf) + mplex_refocus(mplex, node, TRUE); + } +} + + +/*EXTL_DOC + * Have \var{mplex} display the \var{n}:th object managed by it. + */ +EXTL_EXPORT_MEMBER +void mplex_switch_nth(WMPlex *mplex, uint n) +{ + do_switch(mplex, llist_nth_node(mplex->mx_list, n)); +} + + +/*EXTL_DOC + * Have \var{mplex} display next (wrt. currently selected) object managed + * by it. + */ +EXTL_EXPORT_MEMBER +void mplex_switch_next(WMPlex *mplex) +{ + do_switch(mplex, LIST_NEXT_WRAP(mplex->mx_list, mplex->mx_current, + next, prev)); +} + + +/*EXTL_DOC + * Have \var{mplex} display previous (wrt. currently selected) object + * managed by it. + */ +EXTL_EXPORT_MEMBER +void mplex_switch_prev(WMPlex *mplex) +{ + do_switch(mplex, LIST_PREV_WRAP(mplex->mx_list, mplex->mx_current, + next, prev)); +} + + +bool mplex_set_hidden(WMPlex *mplex, WRegion *reg, int sp) +{ + bool mcf=region_may_control_focus((WRegion*)mplex); + WStacking *node=mplex_find_stacking(mplex, reg); + bool hidden, nhidden; + + if(node==NULL) + return FALSE; + + hidden=STACKING_IS_HIDDEN(node); + nhidden=libtu_do_setparam(sp, hidden); + + if(!hidden && nhidden){ + node->hidden=TRUE; + + if(REGION_IS_MAPPED(mplex) && !MPLEX_MGD_UNVIEWABLE(mplex)) + region_unmap(reg); + + /* lnode -> switch next? */ + }else if(hidden && !nhidden){ + mplex_do_node_display(mplex, node, TRUE); + } + + if(mcf && !PASSIVE(node)) + mplex_refocus(mplex, (nhidden ? NULL : node), TRUE); + + return STACKING_IS_HIDDEN(node); +} + + +/*EXTL_DOC + * Set the visibility of the region \var{reg} on \var{mplex} + * as specified with the parameter \var{how} (set/unset/toggle). + * The resulting state is returned. + */ +EXTL_EXPORT_AS(WMPlex, set_hidden) +bool mplex_set_hidden_extl(WMPlex *mplex, WRegion *reg, const char *how) +{ + return mplex_set_hidden(mplex, reg, libtu_string_to_setparam(how)); +} + + +/*EXTL_DOC + * Is \var{reg} on within \var{mplex} and hidden? + */ +EXTL_SAFE +EXTL_EXPORT_MEMBER +bool mplex_is_hidden(WMPlex *mplex, WRegion *reg) +{ + WStacking *node=mplex_find_stacking(mplex, reg); + + return (node!=NULL && STACKING_IS_HIDDEN(node)); +} + + +/*}}}*/ + + +/*{{{ Navigation */ + + +static WStacking *mplex_nxt(WMPlex *mplex, WStacking *st, bool wrap) +{ + return (st->mgr_next!=NULL + ? st->mgr_next + : (wrap ? mplex->mgd : NULL)); +} + + +static WStacking *mplex_prv(WMPlex *mplex, WStacking *st, bool wrap) +{ + return (st!=mplex->mgd + ? st->mgr_prev + : (wrap ? st->mgr_prev : NULL)); +} + + +typedef WStacking *NxtFn(WMPlex *mplex, WStacking *st, bool wrap); + + +static WRegion *do_navi(WMPlex *mplex, WStacking *sti, + NxtFn *fn, WRegionNaviData *data, bool sti_ok) +{ + WStacking *st, *stacking; + uint min_level=0; + bool wrap=FALSE; + + stacking=mplex_get_stacking(mplex); + + if(stacking!=NULL) + min_level=stacking_min_level_mapped(stacking); + + st=sti; + while(1){ + st=fn(mplex, st, wrap); + + if(st==NULL || (st==sti && !sti_ok)) + break; + + if(!st->hidden){ + if(OBJ_IS(st->reg, WGroup)){ + /* WGroup navigation code should respect modal stuff. */ + WRegion *res=region_navi_cont((WRegion*)mplex, st->reg, data); + if(res!=NULL){ + if(res!=st->reg){ + return res; + }else{ + #warning "TODO: What to do?" + } + } + }else{ + if(st->level>=min_level && !PASSIVE(st)) + return region_navi_cont((WRegion*)mplex, st->reg, data); + } + } + + if(st==sti) + break; + } + + return NULL; +} + + +WRegion *mplex_navi_first(WMPlex *mplex, WRegionNavi nh, + WRegionNaviData *data) +{ + WStacking *lst=mplex->mgd; + + if(lst==NULL) + return region_navi_cont((WRegion*)mplex, NULL, data); + + if(nh==REGION_NAVI_ANY){ + /* ? */ + } + + if(nh==REGION_NAVI_ANY || nh==REGION_NAVI_END || + nh==REGION_NAVI_BOTTOM || nh==REGION_NAVI_RIGHT){ + return do_navi(mplex, lst, mplex_prv, data, TRUE); + }else{ + return do_navi(mplex, lst->mgr_prev, mplex_nxt, data, TRUE); + } +} + + +WRegion *mplex_navi_next(WMPlex *mplex, WRegion *rel, WRegionNavi nh, + WRegionNaviData *data) +{ + WStacking *st; + + if(rel!=NULL){ + st=mplex_find_stacking(mplex, rel); + if(st==NULL) + return NULL; + }else if(mplex->mx_current!=NULL){ + st=mplex->mx_current->st; + }else{ + return mplex_navi_first(mplex, nh, data); + } + + if(nh==REGION_NAVI_ANY){ + /* ? */ + } + + if(nh==REGION_NAVI_ANY || nh==REGION_NAVI_END || + nh==REGION_NAVI_BOTTOM || nh==REGION_NAVI_RIGHT){ + return do_navi(mplex, st, mplex_nxt, data, FALSE); + }else{ + return do_navi(mplex, st, mplex_prv, data, FALSE); + } +} + + +/*}}}*/ + + +/*{{{ Stacking */ + + +bool mplex_managed_rqorder(WMPlex *mplex, WRegion *reg, WRegionOrder order) +{ + WStacking **stackingp=mplex_get_stackingp(mplex); + WStacking *st; + + if(stackingp==NULL || *stackingp==NULL) + return FALSE; + + st=mplex_find_stacking(mplex, reg); + + if(st==NULL) + return FALSE; + + stacking_restack(stackingp, st, None, NULL, NULL, + (order!=REGION_ORDER_FRONT)); + + return TRUE; +} + + +/*}}}*/ + + +/*{{{ Attach */ + + +static bool mplex_stack(WMPlex *mplex, WStacking *st) +{ + WStacking *tmp=NULL; + Window bottom=None, top=None; + WStacking **stackingp=mplex_get_stackingp(mplex); + + if(stackingp==NULL) + return FALSE; + + LINK_ITEM_FIRST(tmp, st, next, prev); + stacking_weave(stackingp, &tmp, FALSE); + assert(tmp==NULL); + + return TRUE; +} + + +static void mplex_unstack(WMPlex *mplex, WStacking *st) +{ + WStacking *stacking; + + stacking=mplex_get_stacking(mplex); + + stacking_unstack(&mplex->win, st); +} + + +/* WMPlexWPHolder is used for position marking in order to allow + * WLListNodes be safely removed in the attach handler hnd, that + * could remove something this mplex is managing. + */ +bool mplex_do_attach_final(WMPlex *mplex, WRegion *reg, WMPlexPHolder *ph) +{ + WStacking *node=NULL; + WLListNode *lnode=NULL; + WMPlexAttachParams *param=&ph->param; + bool mx_was_empty, sw, modal, mcf, hidden; + uint level; + + mcf=region_may_control_focus((WRegion*)mplex); + + mx_was_empty=(mplex->mx_list==NULL); + + modal=(param->flags&MPLEX_ATTACH_LEVEL + ? param->level>=STACKING_LEVEL_MODAL1 + : param->flags&MPLEX_ATTACH_MODAL); + + level=(param->flags&MPLEX_ATTACH_LEVEL + ? param->level + : (param->flags&MPLEX_ATTACH_MODAL + ? STACKING_LEVEL_MODAL1 + : (param->flags&MPLEX_ATTACH_UNNUMBERED + ? STACKING_LEVEL_NORMAL + : STACKING_LEVEL_BOTTOM))); + + hidden=(param->flags&MPLEX_ATTACH_HIDDEN + && (param->flags&MPLEX_ATTACH_UNNUMBERED + || !mx_was_empty)); + + sw=(!hidden && (param->flags&MPLEX_ATTACH_SWITCHTO + || (param->flags&MPLEX_ATTACH_UNNUMBERED + ? modal + : (mplex_current_node(mplex)==NULL)))); + + hidden=(hidden || (!sw && !(param->flags&MPLEX_ATTACH_UNNUMBERED))); + + + node=create_stacking(); + + if(node==NULL) + return FALSE; + + if(!(param->flags&MPLEX_ATTACH_UNNUMBERED)){ + lnode=ALLOC(WLListNode); + if(lnode==NULL){ + stacking_free(node); + return FALSE; + } + lnode->next=NULL; + lnode->prev=NULL; + lnode->phs=NULL; + lnode->st=node; + node->lnode=lnode; + } + + if(!stacking_assoc(node, reg)){ + if(lnode!=NULL){ + node->lnode=NULL; + free(lnode); + } + stacking_free(node); + return FALSE; + } + + node->hidden=TRUE; + node->szplcy=param->szplcy; + node->level=level; + + if(lnode!=NULL){ + llist_link_after(&(mplex->mx_list), + (ph!=NULL ? ph->after : NULL), + lnode); + mplex->mx_count++; + + /* Move following placeholders after new node */ + while(ph->next!=NULL) + mplexpholder_move(ph->next, mplex, NULL, lnode); + } + + LINK_ITEM(mplex->mgd, node, mgr_next, mgr_prev); + + if(!OBJ_IS(reg, WGroup)) + mplex_stack(mplex, node); + + region_set_manager(reg, (WRegion*)mplex); + + if(!hidden) + mplex_do_node_display(mplex, node, FALSE); + else + region_unmap(reg); + + if(sw && mcf) + mplex_refocus(mplex, node, FALSE); + + if(lnode!=NULL) + mplex_managed_changed(mplex, MPLEX_CHANGE_ADD, sw, reg); + + return TRUE; +} + + +WRegion *mplex_do_attach_pholder(WMPlex *mplex, WMPlexPHolder *ph, + WRegionAttachData *data) +{ + WMPlexAttachParams *param=&(ph->param); + WSizePolicy szplcy=param->szplcy; + WFitParams fp; + WRegion *reg; + + param->szplcy=(param->flags&MPLEX_ATTACH_SIZEPOLICY && + param->szplcy!=SIZEPOLICY_DEFAULT + ? param->szplcy + : (param->flags&MPLEX_ATTACH_UNNUMBERED + ? SIZEPOLICY_FULL_BOUNDS + : SIZEPOLICY_FULL_EXACT)); + + mplex_managed_geom(mplex, &(fp.g)); + + sizepolicy(¶m->szplcy, NULL, + (param->flags&MPLEX_ATTACH_GEOM + ? &(param->geom) + : NULL), + 0, &fp); + + if(param->flags&MPLEX_ATTACH_WHATEVER) + fp.mode|=REGION_FIT_WHATEVER; + + reg=region_attach_helper((WRegion*)mplex, + (WWindow*)mplex, &fp, + (WRegionDoAttachFn*)mplex_do_attach_final, + (void*)ph, data); + + /* restore */ + ph->param.szplcy=szplcy; + + return reg; +} + + +WRegion *mplex_do_attach(WMPlex *mplex, WMPlexAttachParams *param, + WRegionAttachData *data) +{ + WMPlexPHolder *ph; + WRegion *reg; + + ph=create_mplexpholder(mplex, NULL, param); + + if(ph==NULL) + return NULL; + + reg=mplex_do_attach_pholder(mplex, ph, data); + + destroy_obj((Obj*)ph); + + return reg; +} + + +WRegion *mplex_do_attach_new(WMPlex *mplex, WMPlexAttachParams *param, + WRegionCreateFn *fn, void *fn_param) +{ + WRegionAttachData data; + + data.type=REGION_ATTACH_NEW; + data.u.n.fn=fn; + data.u.n.param=fn_param; + + return mplex_do_attach(mplex, param, &data); +} + + +#define MPLEX_ATTACH_SET_FLAGS (MPLEX_ATTACH_GEOM| \ + MPLEX_ATTACH_SIZEPOLICY| \ + MPLEX_ATTACH_INDEX) + + +WRegion *mplex_attach_simple(WMPlex *mplex, WRegion *reg, int flags) +{ + WMPlexAttachParams param; + WRegionAttachData data; + + param.flags=flags&~MPLEX_ATTACH_SET_FLAGS; + + data.type=REGION_ATTACH_REPARENT; + data.u.reg=reg; + + return mplex_do_attach(mplex, ¶m, &data); +} + + +static void get_params(WMPlex *mplex, ExtlTab tab, WMPlexAttachParams *par) +{ + int layer=1; + int tmp; + + par->flags=0; + + if(extl_table_gets_i(tab, "layer", &tmp)){ + /* backwards compatibility */ + if(tmp==2){ + par->flags|=MPLEX_ATTACH_UNNUMBERED; + if(!extl_table_is_bool_set(tab, "passive")) + par->flags|=MPLEX_ATTACH_MODAL; + } + } + + if(extl_table_gets_i(tab, "level", &tmp)){ + if(tmp>=0){ + par->flags|=MPLEX_ATTACH_LEVEL; + par->level=tmp; + } + } + + if(extl_table_is_bool_set(tab, "modal")) + par->flags|=MPLEX_ATTACH_MODAL; + + if(extl_table_is_bool_set(tab, "unnumbered")) + par->flags|=MPLEX_ATTACH_UNNUMBERED; + + if(extl_table_is_bool_set(tab, "switchto")) + par->flags|=MPLEX_ATTACH_SWITCHTO; + + if(extl_table_is_bool_set(tab, "hidden")) + par->flags|=MPLEX_ATTACH_HIDDEN; + + if(extl_table_gets_i(tab, "index", &(par->index))) + par->flags|=MPLEX_ATTACH_INDEX; + + if(extl_table_gets_i(tab, "sizepolicy", &tmp)){ + par->flags|=MPLEX_ATTACH_SIZEPOLICY; + par->szplcy=tmp; + } + + if(extl_table_gets_rectangle(tab, "geom", &par->geom)) + par->flags|=MPLEX_ATTACH_GEOM; +} + + +/*EXTL_DOC + * Attach and reparent existing region \var{reg} to \var{mplex}. + * The table \var{param} may contain the fields \var{index} and + * \var{switchto} that are interpreted as for \fnref{WMPlex.attach_new}. + */ +EXTL_EXPORT_MEMBER +WRegion *mplex_attach(WMPlex *mplex, WRegion *reg, ExtlTab param) +{ + WMPlexAttachParams par; + WRegionAttachData data; + + if(reg==NULL) + return NULL; + + get_params(mplex, param, &par); + + data.type=REGION_ATTACH_REPARENT; + data.u.reg=reg; + + return mplex_do_attach(mplex, &par, &data); +} + + +/*EXTL_DOC + * Create a new region to be managed by \var{mplex}. At least the following + * fields in \var{param} are understood (all but \var{type} are optional). + * + * \begin{tabularx}{\linewidth}{lX} + * \tabhead{Field & Description} + * \var{type} & (string) Class name (a string) of the object to be created. \\ + * \var{name} & (string) Name of the object to be created (a string). \\ + * \var{switchto} & (boolean) Should the region be switched to (boolean)? \\ + * \var{unnumbered} & (boolean) Do not put on the numbered mutually + * exclusive list. \\ + * \var{index} & (integer) Index on this list, same as for + * \fnref{WMPlex.set_index}. \\ + * \var{level} & (integer) Stacking level. \\ + * \var{modal} & (boolean) Shortcut for modal stacking level. \\ + * \var{hidden} & (boolean) Attach hidden, if not prevented + * by e.g. the mutually exclusive list being empty. + * This option overrides \var{switchto}. \\ + * \var{sizepolicy} & (integer) Size policy. + * (TODO: document them somewhere.) \\ + * \var{geom} & (table) Geometry specification. \\ + * \end{tabularx} + * + * In addition parameters to the region to be created are passed in this + * same table. + */ +EXTL_EXPORT_MEMBER +WRegion *mplex_attach_new(WMPlex *mplex, ExtlTab param) +{ + WMPlexAttachParams par; + WRegionAttachData data; + + get_params(mplex, param, &par); + + data.type=REGION_ATTACH_LOAD; + data.u.tab=param; + + return mplex_do_attach(mplex, &par, &data); +} + + +/*EXTL_DOC + * Attach all tagged regions to \var{mplex}. + */ +EXTL_EXPORT_MEMBER +void mplex_attach_tagged(WMPlex *mplex) +{ + WRegion *reg; + + while((reg=ioncore_tags_take_first())!=NULL) + mplex_attach_simple(mplex, reg, 0); +} + + +static bool mplex_handle_drop(WMPlex *mplex, int x, int y, + WRegion *dropped) +{ + WRegion *curr=mplex_mx_current(mplex); + + /* This code should handle dropping tabs on floating workspaces. */ + if(curr && HAS_DYN(curr, region_handle_drop)){ + int rx, ry; + region_rootpos(curr, &rx, &ry); + if(rectangle_contains(®ION_GEOM(curr), x-rx, y-ry)){ + if(region_handle_drop(curr, x, y, dropped)) + return TRUE; + } + } + + return (NULL!=mplex_attach_simple(mplex, dropped, MPLEX_ATTACH_SWITCHTO)); +} + + +WPHolder *mplex_prepare_manage(WMPlex *mplex, const WClientWin *cwin, + const WManageParams *param, int redir) +{ + WMPlexAttachParams ap; + WPHolder *ph=NULL; + WMPlexPHolder *mph; + WLListNode *after; + + if(redir==MANAGE_REDIR_STRICT_YES || redir==MANAGE_REDIR_PREFER_YES){ + WStacking *cur=mplex_current_node(mplex); + + if(cur!=NULL){ + ph=region_prepare_manage(cur->reg, cwin, param, + MANAGE_REDIR_PREFER_YES); + if(ph!=NULL) + return ph; + } + + if(mplex->mx_current!=NULL && mplex->mx_current->st!=cur){ + ph=region_prepare_manage(mplex->mx_current->st->reg, + cwin, param, + MANAGE_REDIR_PREFER_YES); + if(ph!=NULL) + return ph; + } + } + + if(redir==MANAGE_REDIR_STRICT_YES) + return NULL; + + ap.flags=((param->switchto ? MPLEX_ATTACH_SWITCHTO : 0) + |MPLEX_ATTACH_SIZEPOLICY); + ap.szplcy=SIZEPOLICY_FULL_BOUNDS; + + mph=create_mplexpholder(mplex, NULL, &ap); + + if(mph!=NULL){ + WGroupedPHolder *gph=create_groupedpholder((WPHolder*)mph); + if(gph!=NULL) + return (WPHolder*)gph; + } + + return (WPHolder*)mph; +} + + +/*}}}*/ + + +/*{{{ Remove */ + + +void mplex_managed_remove(WMPlex *mplex, WRegion *sub) +{ + bool mx=FALSE, hadfocus=FALSE, mcf; + WRegion *stdisp=(WRegion*)(mplex->stdispwatch.obj); + WStacking *node, *next=NULL; + + mcf=region_may_control_focus((WRegion*)mplex); + + if(stdisp!=NULL){ + if(CAN_MANAGE_STDISP(sub) && + region_managed_within((WRegion*)mplex, stdisp)==sub){ + region_unmanage_stdisp(sub, TRUE, TRUE); + region_detach_manager(stdisp); + } + } + + node=mplex_find_stacking(mplex, sub); + + if(node==NULL) + return; + + hadfocus=(mplex_current_node(mplex)==node); + + if(node->lnode!=NULL){ + if(mplex->mx_current==node->lnode){ + WLListNode *lnext; + + mplex->mx_current=NULL; + lnext=LIST_PREV(mplex->mx_list, node->lnode, next, prev); + if(lnext==NULL){ + lnext=LIST_NEXT(mplex->mx_list, node->lnode, next, prev); + if(lnext==node->lnode) + lnext=NULL; + } + if(lnext!=NULL) + next=lnext->st; + } + + mplex_move_phs_before(mplex, node->lnode); + llist_unlink(&(mplex->mx_list), node->lnode); + mplex->mx_count--; + + free(node->lnode); + node->lnode=NULL; + mx=TRUE; + } + + UNLINK_ITEM(mplex->mgd, node, mgr_next, mgr_prev); + + mplex_unstack(mplex, node); + + stacking_unassoc(node); + stacking_free(node); + + region_unset_manager(sub, (WRegion*)mplex); + + if(OBJ_IS_BEING_DESTROYED(mplex)) + return; + + if(next!=NULL) + mplex_do_node_display(mplex, next, FALSE); + + if(hadfocus && mcf) + mplex_refocus(mplex, next, FALSE); + + if(mx) + mplex_managed_changed(mplex, MPLEX_CHANGE_REMOVE, next!=NULL, sub); +} + + +bool mplex_rescue_clientwins(WMPlex *mplex, WPHolder *ph) +{ + bool ret1, ret2; + WMPlexIterTmp tmp; + + mplex_iter_init(&tmp, mplex); + ret1=region_rescue_some_clientwins((WRegion*)mplex, ph, + (WRegionIterator*)mplex_iter, + &tmp); + + ret2=region_rescue_child_clientwins((WRegion*)mplex, ph); + + return (ret1 && ret2); +} + + + +void mplex_child_removed(WMPlex *mplex, WRegion *sub) +{ + if(sub!=NULL && sub==(WRegion*)(mplex->stdispwatch.obj)){ + watch_reset(&(mplex->stdispwatch)); + mplex_set_stdisp(mplex, NULL, NULL); + } +} + + +/*}}}*/ + + +/*{{{ Status display support */ + +#ifndef offsetof +# define offsetof(T,F) ((size_t)((char*)&((T*)0L)->F-(char*)0L)) +#endif + +#define STRUCTOF(T, F, FADDR) \ + ((T*)((char*)(FADDR)-offsetof(T, F))) + + +static void stdisp_watch_handler(Watch *watch, Obj *obj) +{ + /*WMPlex *mplex=STRUCTOF(WMPlex, stdispinfo, + STRUCTOF(WMPlexSTDispInfo, regwatch, watch)); + WMPlexSTDispInfo *di=&(mplex->stdispinfo); + WGenWS *ws=OBJ_CAST(REGION_MANAGER(obj), WGenWS); + * + if(ioncore_g.opmode!=IONCORE_OPMODE_DEINIT && ws!=NULL) + genws_unmanage_stdisp(ws, TRUE, FALSE);*/ +} + + +bool mplex_set_stdisp(WMPlex *mplex, WRegion *reg, + const WMPlexSTDispInfo *din) +{ + WRegion *oldstdisp=(WRegion*)(mplex->stdispwatch.obj); + WRegion *mgr=NULL; + + assert(reg==NULL || (reg==oldstdisp) || + (REGION_MANAGER(reg)==NULL && + REGION_PARENT(reg)==(WWindow*)mplex)); + + if(oldstdisp!=NULL){ + mgr=region_managed_within((WRegion*)mplex, oldstdisp); + + if(!CAN_MANAGE_STDISP(mgr)) + mgr=NULL; + + if(oldstdisp!=reg){ + mainloop_defer_destroy((Obj*)oldstdisp); + watch_reset(&(mplex->stdispwatch)); + } + } + + if(din!=NULL) + mplex->stdispinfo=*din; + + if(reg==NULL){ + if(mgr!=NULL){ + region_unmanage_stdisp(mgr, TRUE, FALSE); + if(oldstdisp!=NULL) + region_detach_manager(oldstdisp); + } + }else{ + watch_setup(&(mplex->stdispwatch), (Obj*)reg, stdisp_watch_handler); + + mplex_remanage_stdisp(mplex); + } + + return TRUE; +} + + +void mplex_get_stdisp(WMPlex *mplex, WRegion **reg, WMPlexSTDispInfo *di) +{ + *di=mplex->stdispinfo; + *reg=(WRegion*)mplex->stdispwatch.obj; +} + + +static StringIntMap pos_map[]={ + {"tl", MPLEX_STDISP_TL}, + {"tr", MPLEX_STDISP_TR}, + {"bl", MPLEX_STDISP_BL}, + {"br", MPLEX_STDISP_BR}, + {NULL, 0} +}; + + +static bool do_attach_stdisp(WRegion *mplex, WRegion *reg, void *unused) +{ + /* We do not actually manage the stdisp. */ + return TRUE; +} + + +/*EXTL_DOC + * Set/create status display for \var{mplex}. Table is a standard + * description of the object to be created (as passed to e.g. + * \fnref{WMPlex.attach_new}). In addition, the following fields are + * recognised: + * + * \begin{tabularx}{\linewidth}{lX} + * \tabhead{Field & Description} + * \var{pos} & The corner of the screen to place the status display + * in. One of \code{tl}, \code{tr}, \var{bl} or \var{br}. \\ + * \var{action} & If this field is set to \code{keep}, \var{corner} + * and \var{orientation} are changed for the existing + * status display. If this field is set to \var{remove}, + * the existing status display is removed. If this + * field is not set or is set to \code{replace}, a + * new status display is created and the old, if any, + * removed. \\ + * \end{tabularx} + */ +EXTL_EXPORT_AS(WMPlex, set_stdisp) +WRegion *mplex_set_stdisp_extl(WMPlex *mplex, ExtlTab t) +{ + WRegion *stdisp=NULL; + WMPlexSTDispInfo din=mplex->stdispinfo; + char *s; + + if(extl_table_gets_s(t, "pos", &s)){ + din.pos=stringintmap_value(pos_map, s, -1); + if(din.pos<0){ + warn(TR("Invalid position setting.")); + return NULL; + } + } + + extl_table_gets_b(t, "fullsize", &(din.fullsize)); + + s=NULL; + extl_table_gets_s(t, "action", &s); + + if(s==NULL || strcmp(s, "replace")==0){ + WRegionAttachData data; + WFitParams fp; + int o2; + + fp.g.x=0; + fp.g.y=0; + fp.g.w=REGION_GEOM(mplex).w; + fp.g.h=REGION_GEOM(mplex).h; + fp.mode=REGION_FIT_BOUNDS|REGION_FIT_WHATEVER; + + /* Full mplex size is stupid so use saved geometry initially + * if there's one. + */ + extl_table_gets_rectangle(t, "geom", &(fp.g)); + + data.type=REGION_ATTACH_LOAD; + data.u.tab=t; + + stdisp=region_attach_helper((WRegion*)mplex, + (WWindow*)mplex, &fp, + do_attach_stdisp, NULL, + &data); + + if(stdisp==NULL) + return NULL; + + }else if(strcmp(s, "keep")==0){ + stdisp=(WRegion*)(mplex->stdispwatch.obj); + }else if(strcmp(s, "remove")!=0){ + warn(TR("Invalid action setting.")); + return FALSE; + } + + if(!mplex_set_stdisp(mplex, stdisp, &din)){ + destroy_obj((Obj*)stdisp); + return NULL; + } + + return stdisp; +} + + +static ExtlTab mplex_do_get_stdisp_extl(WMPlex *mplex, bool fullconfig) +{ + WRegion *reg=(WRegion*)mplex->stdispwatch.obj; + ExtlTab t; + + if(reg==NULL) + return extl_table_none(); + + if(fullconfig){ + t=region_get_configuration(reg); + extl_table_sets_rectangle(t, "geom", ®ION_GEOM(reg)); + }else{ + t=extl_create_table(); + extl_table_sets_o(t, "reg", (Obj*)reg); + } + + if(t!=extl_table_none()){ + WMPlexSTDispInfo *di=&(mplex->stdispinfo); + extl_table_sets_s(t, "pos", stringintmap_key(pos_map, di->pos, NULL)); + extl_table_sets_b(t, "fullsize", di->fullsize); + } + return t; +} + + +/*EXTL_DOC + * Get status display information. See \fnref{WMPlex.get_stdisp} for + * information on the fields. + */ +EXTL_SAFE +EXTL_EXPORT_AS(WMPlex, get_stdisp) +ExtlTab mplex_get_stdisp_extl(WMPlex *mplex) +{ + return mplex_do_get_stdisp_extl(mplex, FALSE); +} + + +/*}}}*/ + + +/*{{{ Dynfuns */ + + +void mplex_managed_geom_default(const WMPlex *mplex, WRectangle *geom) +{ + geom->x=0; + geom->y=0; + geom->w=REGION_GEOM(mplex).w; + geom->h=REGION_GEOM(mplex).h; +} + + +void mplex_managed_geom(const WMPlex *mplex, WRectangle *geom) +{ + CALL_DYN(mplex_managed_geom, mplex, (mplex, geom)); +} + + +void mplex_size_changed(WMPlex *mplex, bool wchg, bool hchg) +{ + CALL_DYN(mplex_size_changed, mplex, (mplex, wchg, hchg)); +} + + +void mplex_managed_changed(WMPlex *mplex, int mode, bool sw, WRegion *mgd) +{ + CALL_DYN(mplex_managed_changed, mplex, (mplex, mode, sw, mgd)); +} + + +int mplex_default_index(WMPlex *mplex) +{ + int idx=LLIST_INDEX_LAST; + CALL_DYN_RET(idx, int, mplex_default_index, mplex, (mplex)); + return idx; +} + + +/* For regions managing stdisps */ + +void region_manage_stdisp(WRegion *reg, WRegion *stdisp, + const WMPlexSTDispInfo *info) +{ + CALL_DYN(region_manage_stdisp, reg, (reg, stdisp, info)); +} + + +void region_unmanage_stdisp(WRegion *reg, bool permanent, bool nofocus) +{ + CALL_DYN(region_unmanage_stdisp, reg, (reg, permanent, nofocus)); +} + + +/*}}}*/ + + +/*{{{ Changed hook helper */ + + +static const char *mode2str(int mode) +{ + if(mode==MPLEX_CHANGE_SWITCHONLY) + return "switchonly"; + else if(mode==MPLEX_CHANGE_REORDER) + return "reorder"; + else if(mode==MPLEX_CHANGE_ADD) + return "add"; + else if(mode==MPLEX_CHANGE_REMOVE) + return "remove"; + return NULL; +} + + +static bool mrsh_chg(ExtlFn fn, WMPlexChangedParams *p) +{ + ExtlTab t=extl_create_table(); + bool ret; + + extl_table_sets_o(t, "reg", (Obj*)p->reg); + extl_table_sets_s(t, "mode", mode2str(p->mode)); + extl_table_sets_b(t, "sw", p->sw); + extl_table_sets_o(t, "sub", (Obj*)p->sub); + + extl_protect(NULL); + ret=extl_call(fn, "t", NULL, t); + extl_unprotect(NULL); + + extl_unref_table(t); + + return ret; +} + + +void mplex_call_changed_hook(WMPlex *mplex, WHook *hook, + int mode, bool sw, WRegion *reg) +{ + WMPlexChangedParams p; + + p.reg=mplex; + p.mode=mode; + p.sw=sw; + p.sub=reg; + + hook_call_p(hook, &p, (WHookMarshallExtl*)mrsh_chg); +} + + +/*}}} */ + + +/*{{{ Save/load */ + + +static void save_node(WMPlex *mplex, ExtlTab subs, int *n, + WStacking *node, bool unnumbered) +{ + ExtlTab st, g; + + st=region_get_configuration(node->reg); + + if(st!=extl_table_none()){ + /*"TODO: better switchto saving? */ + if(mplex->mx_current!=NULL && node==mplex->mx_current->st) + extl_table_sets_b(st, "switchto", TRUE); + extl_table_sets_i(st, "sizepolicy", node->szplcy); + extl_table_sets_i(st, "level", node->level); + g=extl_table_from_rectangle(®ION_GEOM(node->reg)); + extl_table_sets_t(st, "geom", g); + extl_unref_table(g); + if(STACKING_IS_HIDDEN(node)) + extl_table_sets_b(st, "hidden", TRUE); + if(unnumbered) + extl_table_sets_b(st, "unnumbered", TRUE); + + extl_table_seti_t(subs, ++(*n), st); + extl_unref_table(st); + } +} + + +ExtlTab mplex_get_configuration(WMPlex *mplex) +{ + ExtlTab tab, subs, stdisptab; + WMPlexIterTmp tmp; + WLListIterTmp ltmp; + WLListNode *lnode; + WStacking *node; + int n=0; + + tab=region_get_base_configuration((WRegion*)mplex); + + subs=extl_create_table(); + extl_table_sets_t(tab, "subs", subs); + + /* First the numbered/mutually exclusive nodes */ + FOR_ALL_NODES_ON_LLIST(lnode, mplex->mx_list, ltmp){ + save_node(mplex, subs, &n, lnode->st, FALSE); + } + + FOR_ALL_NODES_IN_MPLEX(mplex, node, tmp){ + if(node->lnode==NULL) + save_node(mplex, subs, &n, node, TRUE); + } + + extl_unref_table(subs); + + /*stdisptab=mplex_do_get_stdisp_extl(mplex, TRUE); + if(stdisptab!=extl_table_none()){ + extl_table_sets_t(tab, "stdisp", stdisptab); + extl_unref_table(stdisptab); + }*/ + + return tab; +} + + +static WMPlex *tmp_mplex=NULL; +static WMPlexAttachParams *tmp_par=NULL; + +static WPHolder *pholder_callback() +{ + assert(tmp_mplex!=NULL); + return (WPHolder*)create_mplexpholder(tmp_mplex, NULL, tmp_par); +} + + +void mplex_load_contents(WMPlex *mplex, ExtlTab tab) +{ + ExtlTab substab, subtab; + int n, i; + + /*if(extl_table_gets_t(tab, "stdisp", &subtab)){ + mplex_set_stdisp_extl(mplex, subtab); + extl_unref_table(subtab); + }*/ + + if(extl_table_gets_t(tab, "subs", &substab)){ + n=extl_table_get_n(substab); + for(i=1; i<=n; i++){ + if(extl_table_geti_t(substab, i, &subtab)){ + /*mplex_attach_new(mplex, subtab);*/ + WMPlexAttachParams par; + WRegionAttachData data; + char *tmp=NULL; + + get_params(mplex, subtab, &par); + + par.flags|=MPLEX_ATTACH_INDEX; + par.index=LLIST_INDEX_LAST; + + tmp_par=∥ + tmp_mplex=mplex; + + data.type=REGION_ATTACH_LOAD; + data.u.tab=subtab; + + ioncore_set_sm_pholder_callback(pholder_callback); + + mplex_do_attach(mplex, &par, &data); + + tmp_mplex=NULL; + + extl_unref_table(subtab); + } + } + extl_unref_table(substab); + } +} + + +WRegion *mplex_load(WWindow *par, const WFitParams *fp, ExtlTab tab) +{ + WMPlex *mplex=create_mplex(par, fp); + if(mplex!=NULL) + mplex_load_contents(mplex, tab); + return (WRegion*)mplex; +} + + +/*}}}*/ + + +/*{{{ Dynfuntab and class info */ + + +static DynFunTab mplex_dynfuntab[]={ + {region_do_set_focus, + mplex_do_set_focus}, + + {region_managed_remove, + mplex_managed_remove}, + + {region_managed_rqgeom, + mplex_managed_rqgeom}, + + {(DynFun*)region_managed_prepare_focus, + (DynFun*)mplex_managed_prepare_focus}, + + {(DynFun*)region_handle_drop, + (DynFun*)mplex_handle_drop}, + + {region_map, mplex_map}, + {region_unmap, mplex_unmap}, + + {(DynFun*)region_prepare_manage, + (DynFun*)mplex_prepare_manage}, + + {(DynFun*)region_current, + (DynFun*)mplex_current}, + + {(DynFun*)region_rescue_clientwins, + (DynFun*)mplex_rescue_clientwins}, + + {(DynFun*)region_get_configuration, + (DynFun*)mplex_get_configuration}, + + {mplex_managed_geom, + mplex_managed_geom_default}, + + {(DynFun*)region_fitrep, + (DynFun*)mplex_fitrep}, + + {region_child_removed, + mplex_child_removed}, + + {(DynFun*)region_managed_get_pholder, + (DynFun*)mplex_managed_get_pholder}, + + {(DynFun*)region_get_rescue_pholder_for, + (DynFun*)mplex_get_rescue_pholder_for}, + + {(DynFun*)region_may_destroy, + (DynFun*)mplex_may_destroy}, + + {(DynFun*)region_navi_first, + (DynFun*)mplex_navi_first}, + + {(DynFun*)region_navi_next, + (DynFun*)mplex_navi_next}, + + {(DynFun*)region_managed_rqorder, + (DynFun*)mplex_managed_rqorder}, + + END_DYNFUNTAB +}; + + +EXTL_EXPORT +IMPLCLASS(WMPlex, WWindow, mplex_deinit, mplex_dynfuntab); + + +/*}}}*/ + diff --git a/ioncore/mplex.h b/ioncore/mplex.h new file mode 100644 index 0000000..889f387 --- /dev/null +++ b/ioncore/mplex.h @@ -0,0 +1,245 @@ +/* + * ion/ioncore/mplex.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_MPLEX_H +#define ION_IONCORE_MPLEX_H + +#include +#include +#include "common.h" +#include "window.h" +#include "attach.h" +#include "manage.h" +#include "rectangle.h" +#include "pholder.h" +#include "sizepolicy.h" + + +#define MPLEX_ADD_TO_END 0x0001 +#define MPLEX_MANAGED_UNVIEWABLE 0x0002 + +#define MPLEX_MGD_UNVIEWABLE(MPLEX) \ + ((MPLEX)->flags&MPLEX_MANAGED_UNVIEWABLE) + + +#define MPLEX_ATTACH_SWITCHTO 0x0001 /* switch to region */ +#define MPLEX_ATTACH_UNNUMBERED 0x0002 /* do not put on mut.ex list */ +#define MPLEX_ATTACH_HIDDEN 0x0004 /* should be hidden */ +#define MPLEX_ATTACH_MODAL 0x0008 /* shortcut.. */ +#define MPLEX_ATTACH_LEVEL 0x0010 /* level field set */ +#define MPLEX_ATTACH_GEOM 0x0020 /* geometry field is set */ +#define MPLEX_ATTACH_SIZEPOLICY 0x0040 /* size policy field is set */ +#define MPLEX_ATTACH_INDEX 0x0080 /* index field is set */ +#define MPLEX_ATTACH_WHATEVER 0x0100 /* set REGION_FIT_WHATEVER */ + + +enum{ + MPLEX_CHANGE_SWITCHONLY=0, + MPLEX_CHANGE_REORDER=1, + MPLEX_CHANGE_ADD=2, + MPLEX_CHANGE_REMOVE=3 +}; + + +enum{ + MPLEX_STDISP_TL, + MPLEX_STDISP_TR, + MPLEX_STDISP_BL, + MPLEX_STDISP_BR +}; + + +INTRSTRUCT(WMPlexSTDispInfo); +INTRSTRUCT(WMPlexChangedParams); +INTRSTRUCT(WMPlexAttachParams); + + +DECLSTRUCT(WMPlexSTDispInfo){ + int pos; + bool fullsize; +}; + + +DECLSTRUCT(WMPlexChangedParams){ + WMPlex *reg; + int mode; + bool sw; + WRegion *sub; +}; + + +#define MPLEXATTACHPARAMS_INIT {0, 0, {0, 0, 0, 0}, 0, 0} + +DECLSTRUCT(WMPlexAttachParams){ + int flags; + int index; + WRectangle geom; + WSizePolicy szplcy; + uint level; +}; + + +DECLCLASS(WMPlex){ + WWindow win; + int flags; + + WStacking *mgd; + + int mx_count; + WLListNode *mx_current; + WLListNode *mx_list; + WMPlexPHolder *mx_phs; + + Watch stdispwatch; + WMPlexSTDispInfo stdispinfo; +}; + + +/* Create/destroy */ + +extern WMPlex *create_mplex(WWindow *parent, const WFitParams *fp); +extern bool mplex_init(WMPlex *mplex, WWindow *parent, + const WFitParams *fp); +extern bool mplex_do_init(WMPlex *mplex, WWindow *parent, Window win, + const WFitParams *fp, bool create); +extern void mplex_deinit(WMPlex *mplex); + +/* Resize and reparent */ + +extern bool mplex_fitrep(WMPlex *mplex, WWindow *par, const WFitParams *fp); +extern void mplex_do_fit_managed(WMPlex *mplex, WFitParams *fp); +extern void mplex_fit_managed(WMPlex *mplex); + +/* Mapping */ + +extern void mplex_map(WMPlex *mplex); +extern void mplex_unmap(WMPlex *mplex); + +/* Attach */ + +extern WRegion *mplex_attach_simple(WMPlex *mplex, WRegion *reg, + int flags); + +extern WRegion *mplex_attach(WMPlex *mplex, WRegion *reg, ExtlTab param); +extern WRegion *mplex_attach_new(WMPlex *mplex, ExtlTab param); + +extern WRegion *mplex_do_attach_pholder(WMPlex *mplex, WMPlexPHolder *ph, + WRegionAttachData *data); +extern WRegion *mplex_do_attach(WMPlex *mplex, WMPlexAttachParams *param, + WRegionAttachData *data); +extern WRegion *mplex_do_attach_new(WMPlex *mplex, WMPlexAttachParams *param, + WRegionCreateFn *fn, void *fn_param); + +extern void mplex_attach_tagged(WMPlex *mplex); + +extern void mplex_managed_remove(WMPlex *mplex, WRegion *reg); +extern void mplex_child_removed(WMPlex *mplex, WRegion *sub); + +extern bool mplex_rescue_clientwins(WMPlex *mplex, WPHolder *ph); + +extern WPHolder *mplex_prepare_manage(WMPlex *mplex, const WClientWin *cwin, + const WManageParams *param, int redir); + +/* Switch */ + +extern bool mplex_managed_prepare_focus(WMPlex *mplex, WRegion *sub, + int flags, WPrepareFocusResult *res); +extern bool mplex_do_prepare_focus(WMPlex *mplex, WStacking *disp, + WStacking *sub, int flags, + WPrepareFocusResult *res); + +extern void mplex_switch_nth(WMPlex *mplex, uint n); +extern void mplex_switch_next(WMPlex *mplex); +extern void mplex_switch_prev(WMPlex *mplex); +extern bool mplex_is_hidden(WMPlex *mplex, WRegion *reg); +extern bool mplex_set_hidden(WMPlex *mplex, WRegion *reg, int sp); + +/* Focus */ + +extern void mplex_do_set_focus(WMPlex *mplex, bool warp); + +/* Stacking */ + +extern bool mplex_managed_rqorder(WMPlex *mplex, WRegion *sub, + WRegionOrder order); + +/* Misc */ + +extern WRegion *mplex_current(WMPlex *mplex); +extern bool mplex_may_destroy(WMPlex *mplex); + +extern int mplex_mx_count(WMPlex *mplex); +extern WRegion *mplex_mx_nth(WMPlex *mplex, uint n); +extern ExtlTab mplex_mx_list(WMPlex *mplex); +extern WRegion *mplex_mx_current(WMPlex *mplex); + +extern void mplex_call_changed_hook(WMPlex *mplex, WHook *hook, + int mode, bool sw, WRegion *reg); + +extern WLListNode *mplex_find_node(WMPlex *mplex, WRegion *reg); + +extern void mplex_remanage_stdisp(WMPlex *mplex); + +/* Dynfuns */ + +DYNFUN void mplex_managed_geom(const WMPlex *mplex, WRectangle *geom); +DYNFUN void mplex_size_changed(WMPlex *mplex, bool wchg, bool hchg); +DYNFUN void mplex_managed_changed(WMPlex *mplex, int what, bool sw, + WRegion *mgd); +DYNFUN int mplex_default_index(WMPlex *mplex); + +/* Save/load */ + +extern ExtlTab mplex_get_configuration(WMPlex *mplex); +extern WRegion *mplex_load(WWindow *par, const WFitParams *fp, ExtlTab tab); +extern void mplex_load_contents(WMPlex *frame, ExtlTab tab); + + +/* Sticky status display support */ + +extern bool mplex_set_stdisp(WMPlex *mplex, WRegion *stdisp, + const WMPlexSTDispInfo *info); +extern void mplex_get_stdisp(WMPlex *mplex, WRegion **stdisp, + WMPlexSTDispInfo *info); + +extern WRegion *mplex_set_stdisp_extl(WMPlex *mplex, ExtlTab t); +extern ExtlTab mplex_get_stdisp_extl(WMPlex *mplex); + +DYNFUN void region_manage_stdisp(WRegion *reg, WRegion *stdisp, + const WMPlexSTDispInfo *info); +DYNFUN void region_unmanage_stdisp(WRegion *reg, bool permanent, bool nofocus); + +/* Stacking list stuff */ + +typedef WStackingIterTmp WMPlexIterTmp; + +extern void mplex_iter_init(WMPlexIterTmp *tmp, WMPlex *ws); +extern WRegion *mplex_iter(WMPlexIterTmp *tmp); +extern WStacking *mplex_iter_nodes(WMPlexIterTmp *tmp); + +extern WStacking *mplex_get_stacking(WMPlex *ws); +extern WStacking **mplex_get_stackingp(WMPlex *ws); + +#define FOR_ALL_MANAGED_BY_MPLEX(MPLEX, VAR, TMP) \ + for(mplex_iter_init(&(TMP), MPLEX), \ + VAR=mplex_iter(&(TMP)); \ + VAR!=NULL; \ + VAR=mplex_iter(&(TMP))) + +#define FOR_ALL_NODES_IN_MPLEX(MPLEX, VAR, TMP) \ + for(mplex_iter_init(&(TMP), MPLEX), \ + VAR=mplex_iter_nodes(&(TMP)); \ + VAR!=NULL; \ + VAR=mplex_iter_nodes(&(TMP))) + +extern WStacking *mplex_find_stacking(WMPlex *mplex, WRegion *reg); + +#endif /* ION_IONCORE_MPLEX_H */ diff --git a/ioncore/mplexpholder.c b/ioncore/mplexpholder.c new file mode 100644 index 0000000..51166f7 --- /dev/null +++ b/ioncore/mplexpholder.c @@ -0,0 +1,313 @@ +/* + * ion/ioncore/mplexpholder.c + * + * Copyright (c) Tuomo Valkonen 2005-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include + +#include "common.h" +#include "mplex.h" +#include "mplexpholder.h" +#include "llist.h" + + +static void mplex_watch_handler(Watch *watch, Obj *mplex); + + +/*{{{ Primitives */ + + +static bool on_ph_list(WMPlexPHolder *ll, WMPlexPHolder *ph) +{ + return ph->prev!=NULL; +} + + +static void mplexpholder_do_link(WMPlexPHolder *ph, + WMPlex *mplex, + WMPlexPHolder *after, + WLListNode *or_after) +{ + assert(mplex==(WMPlex*)ph->mplex_watch.obj && mplex!=NULL); + + if(after!=NULL){ + assert(after->after==or_after); + + if(after->after!=NULL){ + LINK_ITEM_AFTER(after->after->phs, after, ph, next, prev); + }else{ + assert(on_ph_list(mplex->mx_phs, after)); + LINK_ITEM_AFTER(mplex->mx_phs, after, ph, next, prev); + } + ph->after=after->after; + }else if(or_after!=NULL){ + LINK_ITEM_FIRST(or_after->phs, ph, next, prev); + ph->after=or_after; + }else{ + LINK_ITEM_FIRST(mplex->mx_phs, ph, next, prev); + ph->after=NULL; + } +} + + +void mplexpholder_do_unlink(WMPlexPHolder *ph, WMPlex *mplex) +{ + if(ph->after!=NULL){ + UNLINK_ITEM(ph->after->phs, ph, next, prev); + }else if(mplex!=NULL && on_ph_list(mplex->mx_phs, ph)){ + UNLINK_ITEM(mplex->mx_phs, ph, next, prev); + }else{ + assert(ph->next==NULL && ph->prev==NULL); + } + + ph->after=NULL; + ph->next=NULL; + ph->prev=NULL; +} + + +/*}}}*/ + + +/*{{{ Init/deinit */ + + +static void mplex_watch_handler(Watch *watch, Obj *mplex) +{ + WMPlexPHolder *ph=FIELD_TO_STRUCT(WMPlexPHolder, mplex_watch, watch); + mplexpholder_do_unlink(ph, (WMPlex*)mplex); + pholder_redirect(&(ph->ph), (WRegion*)mplex); +} + + +static WMPlexAttachParams dummy_param={0, 0, {0, 0, 0, 0}, 0, 0}; + + +bool mplexpholder_init(WMPlexPHolder *ph, WMPlex *mplex, WStacking *st, + WMPlexAttachParams *param) +{ + pholder_init(&(ph->ph)); + + watch_init(&(ph->mplex_watch)); + ph->after=NULL; + ph->next=NULL; + ph->prev=NULL; + ph->param.flags=0; + + if(!watch_setup(&(ph->mplex_watch), (Obj*)mplex, mplex_watch_handler)){ + pholder_deinit(&(ph->ph)); + return FALSE; + } + + if(param==NULL) + param=&dummy_param; + + if(st!=NULL){ + if(st->lnode!=NULL) + mplexpholder_do_link(ph, mplex, + LIST_LAST(st->lnode->phs, next, prev), + st->lnode); + else + ph->param.flags|=MPLEX_ATTACH_UNNUMBERED; + + ph->param.flags|=(MPLEX_ATTACH_SIZEPOLICY| + MPLEX_ATTACH_GEOM| + MPLEX_ATTACH_LEVEL| + (st->hidden ? MPLEX_ATTACH_HIDDEN : 0)); + ph->param.szplcy=st->szplcy; + ph->param.geom=REGION_GEOM(st->reg); + ph->param.level=st->level; + }else{ + ph->param=*param; + + if(!(param->flags&MPLEX_ATTACH_UNNUMBERED)){ + int index=(param->flags&MPLEX_ATTACH_INDEX + ? param->index + : mplex_default_index(mplex)); + WLListNode *or_after=llist_index_to_after(mplex->mx_list, + mplex->mx_current, + index); + WMPlexPHolder *after=(index==LLIST_INDEX_LAST + ? (or_after!=NULL + ? LIST_LAST(or_after->phs, next, prev) + : LIST_LAST(mplex->mx_phs, next, prev)) + : NULL); + + mplexpholder_do_link(ph, mplex, after, or_after); + } + } + + return TRUE; +} + + +WMPlexPHolder *create_mplexpholder(WMPlex *mplex, + WStacking *st, + WMPlexAttachParams *param) +{ + CREATEOBJ_IMPL(WMPlexPHolder, mplexpholder, (p, mplex, st, param)); +} + + +void mplexpholder_deinit(WMPlexPHolder *ph) +{ + mplexpholder_do_unlink(ph, (WMPlex*)ph->mplex_watch.obj); + watch_reset(&(ph->mplex_watch)); + pholder_deinit(&(ph->ph)); +} + + +/*}}}*/ + + +/*{{{ Move, attach, layer */ + + +WRegion *mplexpholder_do_attach(WMPlexPHolder *ph, int flags, + WRegionAttachData *data) +{ + WMPlex *mplex=(WMPlex*)ph->mplex_watch.obj; + WRegion *reg=NULL; + + if(mplex==NULL) + return FALSE; + + if(flags&PHOLDER_ATTACH_SWITCHTO) + ph->param.flags|=MPLEX_ATTACH_SWITCHTO; + else + ph->param.flags&=~MPLEX_ATTACH_SWITCHTO; + + return mplex_do_attach_pholder(mplex, ph, data); +} + + +bool mplexpholder_move(WMPlexPHolder *ph, WMPlex *mplex, + WMPlexPHolder *after, + WLListNode *or_after) + +{ + mplexpholder_do_unlink(ph, (WMPlex*)ph->mplex_watch.obj); + + watch_reset(&(ph->mplex_watch)); + + if(mplex==NULL) + return TRUE; + + if(!watch_setup(&(ph->mplex_watch), (Obj*)mplex, mplex_watch_handler)) + return FALSE; + + mplexpholder_do_link(ph, mplex, after, or_after); + + return TRUE; +} + + +bool mplexpholder_do_goto(WMPlexPHolder *ph) +{ + WMPlex *mplex=(WMPlex*)ph->mplex_watch.obj; + + if(mplex!=NULL) + return region_goto((WRegion*)mplex); + + return FALSE; +} + + +WRegion *mplexpholder_do_target(WMPlexPHolder *ph) +{ + return (WRegion*)ph->mplex_watch.obj; +} + + +/*}}}*/ + + +/*{{{ WMPlex routines */ + + +void mplex_move_phs(WMPlex *mplex, WLListNode *node, + WMPlexPHolder *after, + WLListNode *or_after) +{ + WMPlexPHolder *ph; + + assert(node!=or_after); + + while(node->phs!=NULL){ + ph=node->phs; + + mplexpholder_do_unlink(ph, mplex); + mplexpholder_do_link(ph, mplex, after, or_after); + + after=ph; + } +} + +void mplex_move_phs_before(WMPlex *mplex, WLListNode *node) +{ + WMPlexPHolder *after=NULL; + WLListNode *or_after; + + or_after=LIST_PREV(mplex->mx_list, node, next, prev); + + after=(or_after!=NULL + ? LIST_LAST(or_after->phs, next, prev) + : LIST_LAST(mplex->mx_phs, next, prev)); + + mplex_move_phs(mplex, node, after, or_after); +} + + +WMPlexPHolder *mplex_managed_get_pholder(WMPlex *mplex, WRegion *mgd) +{ + WStacking *st=mplex_find_stacking(mplex, mgd); + + if(st==NULL) + return NULL; + else + return create_mplexpholder(mplex, st, NULL); +} + + +WMPlexPHolder *mplex_get_rescue_pholder_for(WMPlex *mplex, WRegion *mgd) +{ + WStacking *st=mplex_find_stacking(mplex, mgd); + + return create_mplexpholder(mplex, st, NULL); +} + + +/*}}}*/ + + +/*{{{ Class information */ + + +static DynFunTab mplexpholder_dynfuntab[]={ + {(DynFun*)pholder_do_attach, + (DynFun*)mplexpholder_do_attach}, + + {(DynFun*)pholder_do_goto, + (DynFun*)mplexpholder_do_goto}, + + {(DynFun*)pholder_do_target, + (DynFun*)mplexpholder_do_target}, + + END_DYNFUNTAB +}; + + +IMPLCLASS(WMPlexPHolder, WPHolder, mplexpholder_deinit, + mplexpholder_dynfuntab); + + +/*}}}*/ + diff --git a/ioncore/mplexpholder.h b/ioncore/mplexpholder.h new file mode 100644 index 0000000..4dce782 --- /dev/null +++ b/ioncore/mplexpholder.h @@ -0,0 +1,67 @@ +/* + * ion/ioncore/mplexpholder.h + * + * Copyright (c) Tuomo Valkonen 2005-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_MPLEXPHOLDER_H +#define ION_IONCORE_MPLEXPHOLDER_H + +#include "common.h" +#include "pholder.h" +#include "mplex.h" +#include "attach.h" + +DECLCLASS(WMPlexPHolder){ + WPHolder ph; + Watch mplex_watch; + WLListNode *after; + WMPlexPHolder *next, *prev; + WMPlexAttachParams param; +}; + + +/* If 'after' is set, it is used, otherwise 'or_after', + * and finally 'or_layer' if this is also unset + */ + +extern WMPlexPHolder *create_mplexpholder(WMPlex *mplex, + WStacking *either_st, + WMPlexAttachParams *or_param); +extern bool mplexpholder_init(WMPlexPHolder *ph, + WMPlex *mplex, + WStacking *either_st, + WMPlexAttachParams *or_param); +extern void mplexpholder_deinit(WMPlexPHolder *ph); + +extern int mplexpholder_layer(WMPlexPHolder *ph); + +extern WRegion *mplexpholder_do_attach(WMPlexPHolder *ph, int flags, + WRegionAttachData *data); + +extern bool mplexpholder_do_goto(WMPlexPHolder *ph); + +extern WRegion *mplexpholder_do_target(WMPlexPHolder *ph); + +extern bool mplexpholder_move(WMPlexPHolder *ph, WMPlex *mplex, + WMPlexPHolder *after, + WLListNode *or_after); + +extern void mplexpholder_do_unlink(WMPlexPHolder *ph, WMPlex *mplex); + +extern void mplex_move_phs(WMPlex *mplex, WLListNode *node, + WMPlexPHolder *after, + WLListNode *or_after); +extern void mplex_move_phs_before(WMPlex *mplex, WLListNode *node); + +extern WMPlexPHolder *mplex_managed_get_pholder(WMPlex *mplex, + WRegion *mgd); +extern WMPlexPHolder *mplex_get_rescue_pholder_for(WMPlex *mplex, + WRegion *mgd); + +#endif /* ION_IONCORE_MPLEXPHOLDER_H */ diff --git a/ioncore/mwmhints.c b/ioncore/mwmhints.c new file mode 100644 index 0000000..2ebc683 --- /dev/null +++ b/ioncore/mwmhints.c @@ -0,0 +1,60 @@ +/* + * ion/ioncore/mwmhints.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include "common.h" +#include "property.h" +#include "mwmhints.h" +#include "global.h" + + +WMwmHints *xwindow_get_mwmhints(Window win) +{ + WMwmHints *hints=NULL; + int n; + + n=xwindow_get_property(win, ioncore_g.atom_mwm_hints, + ioncore_g.atom_mwm_hints, + MWM_N_HINTS, FALSE, (uchar**)&hints); + + if(nflags&MWM_HINTS_DECORATIONS && + (hints->decorations&MWM_DECOR_ALL)==0){ + *nodecor=TRUE; + + if(hints->decorations&MWM_DECOR_BORDER || + hints->decorations&MWM_DECOR_TITLE) + *nodecor=FALSE; + } + + XFree((void*)hints); +} diff --git a/ioncore/mwmhints.h b/ioncore/mwmhints.h new file mode 100644 index 0000000..99ac8ad --- /dev/null +++ b/ioncore/mwmhints.h @@ -0,0 +1,63 @@ +/* + * ion/ioncore/mwmhints.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_MWMHINTS_H +#define ION_IONCORE_MWMHINTS_H + +#include + +#include "common.h" + + +#define MWM_HINTS_FUNCTIONS 0x0001 +#define MWM_HINTS_DECORATIONS 0x0002 +#define MWM_HINTS_IONCORE_INPUTMODE_MODE 0x0004 +#define MWM_HINTS_IONCORE_INPUTMODE_STATUS 0x0008 + +#define MWM_FUNC_ALL 0x0001 +#define MWM_FUNC_RESIZE 0x0002 +#define MWM_FUNC_MOVE 0x0004 +#define MWM_FUNC_ICONIFY 0x0008 +#define MWM_FUNC_MAXIMIZE 0x0010 +#define MWM_FUNC_CLOSE 0x0020 + +#define MWM_DECOR_ALL 0x0001 +#define MWM_DECOR_BORDER 0x0002 +#define MWM_DECOR_HANDLE 0x0004 +#define MWM_DECOR_TITLE 0x0008 +#define MWM_DECOR_MENU 0x0010 +#define MWM_DECOR_ICONIFY 0x0020 +#define MWM_DECOR_MAXIMIZE 0x0040 + +#define MWM_IONCORE_INPUTMODE_MODELESS 0 +#define MWM_IONCORE_INPUTMODE_PRIMARY_APPLICATION_MODAL 1 +#define MWM_IONCORE_INPUTMODE_SYSTEM_MODAL 2 +#define MWM_IONCORE_INPUTMODE_FULL_APPLICATION_MODAL 3 + +INTRSTRUCT(WMwmHints); + +DECLSTRUCT(WMwmHints){ + CARD32 flags; + CARD32 functions; + CARD32 decorations; + INT32 inputmode; + CARD32 status; +}; + +#define MWM_DECOR_NDX 3 +#define MWM_N_HINTS 5 + + +extern WMwmHints *xwindow_get_mwmhints(Window win); +extern void xwindow_check_mwmhints_nodecor(Window win, bool *nodecor); + + +#endif /* ION_IONCORE_MWMHINTS_H */ diff --git a/ioncore/names.c b/ioncore/names.c new file mode 100644 index 0000000..7085504 --- /dev/null +++ b/ioncore/names.c @@ -0,0 +1,614 @@ +/* + * ion/ioncore/names.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + + +#include +#include + +#include +#include +#include +#include + +#include "common.h" +#include "region.h" +#include "clientwin.h" +#include "names.h" +#include "strings.h" +#include "gr.h" + + +/*{{{ Implementation */ + + +WNamespace ioncore_internal_ns={NULL, FALSE}; +WNamespace ioncore_clientwin_ns={NULL, FALSE}; + + +static bool initialise_ns(WNamespace *ns) +{ + if(ns->initialised) + return TRUE; + + if(ns->rb==NULL) + ns->rb=make_rb(); + + ns->initialised=(ns->rb!=NULL); + + return ns->initialised; +} + + +static int parseinst(const char *name, const char **startinst) +{ + const char *p2, *p3=NULL; + int inst; + int l=strlen(name); + + *startinst=name+l; + + if(name[l-1]!='>') + return -1; + + p2=strrchr(name, '<'); + if(p2==NULL) + return -1; + + inst=strtoul(p2+1, (char**)&p3, 10); + + if(inst<0 || p3!=name+l-1) + return -1; + + *startinst=p2; + return inst; +} + + +static int parseinst_simple(const char *inststr) +{ + const char *end=NULL; + int inst; + + if(*inststr=='\0') + return 0; + + if(*inststr=='<'){ + inst=strtoul(inststr+1, (char**)&end, 10); + + if(inst>=0 && end!=NULL && *end=='>') + return inst; + } + + warn(TR("Corrupt instance number %s."), inststr); + return -1; +} + + +#define COMPARE_FN ((Rb_compfn*)compare_nameinfos) + +static int compare_nameinfos(const WRegionNameInfo *ni1, + const WRegionNameInfo *ni2) +{ + int l1=0, l2=0; + int i1=0, i2=0; + int mc=0; + + /* Handle unnamed regions. */ + + if(ni1->name==NULL){ + if(ni2->name!=NULL) + return 1; + return (ni1==ni2 ? 0 : (ni1name==NULL){ + return -1; + } + + /* Special case: inst_off<0 means that -inst_off-1 is the actual + * instance number and the name does not contain it. + */ + + if(ni1->inst_off>=0) + l1=ni1->inst_off; + else + l1=strlen(ni1->name); + + if(ni2->inst_off>=0) + l2=ni2->inst_off; + else + l2=strlen(ni2->name); + + /* Check name part first */ + + mc=strncmp(ni1->name, ni2->name, minof(l1, l2)); + + if(mc!=0) + return mc; + + if(l1!=l2) + return (l1inst_off>=0) + i1=parseinst_simple(ni1->name+ni1->inst_off); + else + i1=-ni1->inst_off-1; /*???*/ + + if(ni2->inst_off>=0) + i2=parseinst_simple(ni2->name+ni2->inst_off); + else + i2=-ni2->inst_off-1; /*???*/ + + if(i1!=i2) + return (i1ni.node==NULL); + reg->ni.node=(void*)rb_insertg(tree, &(reg->ni), reg, COMPARE_FN); + return (reg->ni.node!=NULL); +} + +static bool separated(const WRegionNameInfo *ni1, + const WRegionNameInfo *ni2, int *i1ret) +{ + int l1, l2; + int i1, i2; + int mc; + + assert(ni1->name!=NULL); + + if(ni2->name==NULL){ + /* Shouldn't happen the way this function is called below; unnamed + * regions are before others in the traversal order of the tree. + */ + *i1ret=parseinst_simple(ni1->name+ni1->inst_off); + return TRUE; + } + + assert(ni1->inst_off>=0 && ni2->inst_off>=0); + + l1=ni1->inst_off; + l2=ni2->inst_off; + + i1=parseinst_simple(ni1->name+ni1->inst_off); + *i1ret=i1; + + if(l1!=l2) + return TRUE; + + if(memcmp(ni1->name, ni2->name, l1)!=0) + return TRUE; + + i2=parseinst_simple(ni2->name+ni2->inst_off); + + return (i2>(i1+1)); +} + + + +void region_unregister(WRegion *reg) +{ + if(reg->ni.node!=NULL){ + rb_delete_node((Rb_node)reg->ni.node); + reg->ni.node=NULL; + } + + if(reg->ni.name!=NULL){ + free(reg->ni.name); + reg->ni.name=NULL; + reg->ni.inst_off=0; + } +} + + +static bool make_full_name(WRegionNameInfo *ni, const char *name, int inst, + bool append_always) +{ + assert(ni->name==NULL); + + if(inst==0 && !append_always) + ni->name=scopy(name); + else + libtu_asprintf(&(ni->name), "%s<%d>", name, inst); + + if(ni->name==NULL) + return FALSE; + + ni->inst_off=strlen(name); + + return TRUE; +} + + +static bool do_use_name(WRegion *reg, WNamespace *ns, const char *name, + int instrq, bool failchange) +{ + int parsed_inst=-1; + WRegionNameInfo ni={NULL, 0, NULL}; + const char *dummy=NULL; + Rb_node node; + int inst=-1; + int found=0; + + parsed_inst=parseinst(name, &dummy); + + if(!ns->initialised) + return FALSE; + + /* If there's something that looks like an instance at the end of + * name, we will append the instance number. + */ + if(parsed_inst>=0 && inst==0 && failchange) + return FALSE; + + if(instrq>=0){ + WRegionNameInfo tmpni; + tmpni.name=(char*)name; + tmpni.inst_off=-instrq-1; + node=rb_find_gkey_n(ns->rb, &tmpni, COMPARE_FN, &found); + if(found){ + if(rb_val(node)==(void*)reg){ + /* The region already has the requested name */ + return TRUE; + } + if(failchange) + return FALSE; + }else{ + inst=instrq; + } + } + + if(inst<0){ + WRegionNameInfo tmpni; + + found=0; + inst=0; + + tmpni.name=(char*)name; + tmpni.inst_off=-1; + node=rb_find_gkey_n(ns->rb, &tmpni, COMPARE_FN, &found); + + if(found){ + while(1){ + Rb_node next=rb_next(node); + + if(rb_val(node)==(void*)reg){ + /* The region already has a name of requested form */ + return TRUE; + } + + if(next==rb_nil(ns->rb) || + separated((const WRegionNameInfo*)node->k.key, + (const WRegionNameInfo*)next->k.key, &inst)){ + /* 'inst' should be next free instance after increment + * as separation was greater then one. + */ + inst++; + break; + } + + /* 'inst' should be instance of next after increment + * as separation was one. + */ + inst++; + node=next; + } + } + } + + if(!make_full_name(&ni, name, inst, parsed_inst>=0)) + return FALSE; + + /* + rb_find_gkey_n(ns->rb, &ni, COMPARE_FN, &found); + + assert(!found); + */ + + region_unregister(reg); + + reg->ni.name=ni.name; + reg->ni.inst_off=ni.inst_off; + + if(!insert_reg(ns->rb, reg)){ + free(reg->ni.name); + reg->ni.name=NULL; + reg->ni.inst_off=0; + return FALSE; + } + + return TRUE; +} + + +static bool use_name_anyinst(WRegion *reg, WNamespace *ns, const char *name) +{ + return do_use_name(reg, ns, name, -1, FALSE); +} + + +static bool use_name_exact(WRegion *reg, WNamespace *ns, const char *name) +{ + return do_use_name(reg, ns, name, 0, TRUE); +} + + +static bool use_name_parseany(WRegion *reg, WNamespace *ns, const char *name) +{ + int l, inst; + const char *startinst; + + l=strlen(name); + + inst=parseinst(name, &startinst); + if(inst>=0){ + bool retval=FALSE; + int realnamelen=startinst-name; + char *realname=ALLOC_N(char, realnamelen+1); + if(realname!=NULL){ + memcpy(realname, name, realnamelen); + realname[realnamelen]='\0'; + retval=do_use_name(reg, ns, realname, inst, FALSE); + free(realname); + } + return retval; + } + + return do_use_name(reg, ns, name, 0, FALSE); +} + + + +/*}}}*/ + + +/*{{{ Interface */ + + +/*EXTL_DOC + * Returns the name for \var{reg}. + */ +EXTL_SAFE +EXTL_EXPORT_MEMBER +const char *region_name(WRegion *reg) +{ + return reg->ni.name; +} + + +static bool do_set_name(bool (*fn)(WRegion *reg, WNamespace *ns, const char *p), + WRegion *reg, WNamespace *ns, const char *p) +{ + bool ret=TRUE; + char *nm=NULL; + + if(!initialise_ns(ns)) + return FALSE; + + if(p!=NULL){ + nm=scopy(p); + if(nm==NULL) + return FALSE; + str_stripws(nm); + } + + if(nm==NULL || *nm=='\0'){ + region_unregister(reg); + ret=insert_reg(ns->rb, reg); + }else{ + ret=fn(reg, ns, nm); + } + + if(nm!=NULL) + free(nm); + + region_notify_change(reg, "name"); + + return ret; +} + + +bool region_register(WRegion *reg) +{ + assert(reg->ni.name==NULL); + + if(!initialise_ns(&ioncore_internal_ns)) + return FALSE; + + return use_name_anyinst(reg, &ioncore_internal_ns, OBJ_TYPESTR(reg)); +} + + +bool clientwin_register(WClientWin *cwin) +{ + WRegion *reg=(WRegion*)cwin; + + assert(reg->ni.name==NULL); + + if(!initialise_ns(&ioncore_clientwin_ns)) + return FALSE; + + return insert_reg(ioncore_clientwin_ns.rb, (WRegion*)cwin); +} + + +/*EXTL_DOC + * Set the name of \var{reg} to \var{p}. If the name is already in use, + * an instance number suffix \code{} will be attempted. If \var{p} has + * such a suffix, it will be modified, otherwise such a suffix will be + * added. Setting \var{p} to nil will cause current name to be removed. + */ +EXTL_EXPORT_MEMBER +bool region_set_name(WRegion *reg, const char *p) +{ +/* return do_set_name(use_name_parseany, reg, &ioncore_internal_ns, p);*/ + return do_set_name(use_name_parseany, reg, + OBJ_IS(reg, WClientWin) ? &ioncore_clientwin_ns : &ioncore_internal_ns, + p); +} + + +/*EXTL_DOC + * Similar to \fnref{WRegion.set_name} except if the name is already in use, + * other instance numbers will not be attempted. The string \var{p} should + * not contain a \code{} suffix or this function will fail. + */ +EXTL_EXPORT_MEMBER +bool region_set_name_exact(WRegion *reg, const char *p) +{ + return do_set_name(use_name_exact, reg, &ioncore_internal_ns, p); +} + + +bool clientwin_set_name(WClientWin *cwin, const char *p) +{ + return do_set_name(use_name_anyinst, (WRegion*)cwin, + &ioncore_clientwin_ns, p); +} + + +/*}}}*/ + + +/*{{{ Lookup and list */ + + +static WRegion *do_lookup_region(WNamespace *ns, const char *cname, + const char *typenam) +{ + WRegionNameInfo ni; + Rb_node node; + int found=0; + const char *instptr=NULL; + + if(cname==NULL || !ns->initialised) + return NULL; + + parseinst(cname, &instptr); + assert(instptr!=NULL); + + ni.name=(char*)cname; + ni.inst_off=instptr-cname; + + node=rb_find_gkey_n(ns->rb, &ni, COMPARE_FN, &found); + + if(!found) + return NULL; + + return (WRegion*)node->v.val; +} + + +/*EXTL_DOC + * Attempt to find a non-client window region with name \var{name} and type + * inheriting \var{typenam}. + */ +EXTL_SAFE +EXTL_EXPORT +WRegion *ioncore_lookup_region(const char *name, const char *typenam) +{ + return do_lookup_region(&ioncore_internal_ns, name, typenam); +} + + +/*EXTL_DOC + * Attempt to find a client window with name \var{name}. + */ +EXTL_SAFE +EXTL_EXPORT +WClientWin *ioncore_lookup_clientwin(const char *name) +{ + return (WClientWin*)do_lookup_region(&ioncore_clientwin_ns, name, + "WClientWin"); +} + + +static ExtlTab do_list(WNamespace *ns, const char *typenam) +{ + ExtlTab tab; + Rb_node node; + int n=0; + + if(!ns->initialised) + return extl_table_none(); + + tab=extl_create_table(); + + rb_traverse(node, ns->rb){ + WRegion *reg=(WRegion*)node->v.val; + + assert(reg!=NULL); + + if(typenam!=NULL && !obj_is_str((Obj*)reg, typenam)) + continue; + + if(extl_table_seti_o(tab, n+1, (Obj*)reg)) + n++; + } + + return tab; +} + + +/*EXTL_DOC + * Find all non-client window regions inheriting \var{typenam}. + */ +EXTL_SAFE +EXTL_EXPORT +ExtlTab ioncore_region_list(const char *typenam) +{ + return do_list(&ioncore_internal_ns, typenam); +} + + +/*EXTL_DOC + * Return a list of all client windows. + */ +EXTL_SAFE +EXTL_EXPORT +ExtlTab ioncore_clientwin_list() +{ + return do_list(&ioncore_clientwin_ns, NULL); +} + + +/*}}}*/ + + +/*{{{ Displayname */ + + +const char *region_displayname(WRegion *reg) +{ + const char *ret=NULL; + CALL_DYN_RET(ret, const char *, region_displayname, reg, (reg)); + return ret; +} + + +char *region_make_label(WRegion *reg, int maxw, GrBrush *brush) +{ + const char *name=region_displayname(reg); + + if(name==NULL) + return NULL; + + return grbrush_make_label(brush, name, maxw); +} + + +/*}}}*/ diff --git a/ioncore/names.h b/ioncore/names.h new file mode 100644 index 0000000..5a2cc36 --- /dev/null +++ b/ioncore/names.h @@ -0,0 +1,51 @@ +/* + * ion/ioncore/names.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_NAMES_H +#define ION_IONCORE_NAMES_H + +#include "region.h" +#include "clientwin.h" +#include "gr.h" +#include + + +typedef struct{ + struct rb_node *rb; + bool initialised; +} WNamespace; + + +extern WNamespace ioncore_internal_ns; +extern WNamespace ioncore_clientwin_ns; + + +extern bool region_register(WRegion *reg); +extern bool region_set_name(WRegion *reg, const char *name); +extern bool region_set_name_exact(WRegion *reg, const char *name); + +extern bool clientwin_register(WClientWin *cwin); +extern bool clientwin_set_name(WClientWin *cwin, const char *name); + +extern void region_unregister(WRegion *reg); +extern void region_do_unuse_name(WRegion *reg, bool insert_unnamed); + +extern const char *region_name(WRegion *reg); +DYNFUN const char *region_displayname(WRegion *reg); + +extern char *region_make_label(WRegion *reg, int maxw, GrBrush *brush); + +extern ExtlTab ioncore_region_list(const char *typenam); +extern ExtlTab ioncore_clientwin_list(); +extern WRegion *ioncore_lookup_region(const char *cname, const char *typenam); +extern WClientWin *ioncore_lookup_clientwin(const char *cname); + +#endif /* ION_IONCORE_NAMES_H */ diff --git a/ioncore/navi.c b/ioncore/navi.c new file mode 100644 index 0000000..f52a44b --- /dev/null +++ b/ioncore/navi.c @@ -0,0 +1,327 @@ +/* + * ion/ioncore/navi.c + * + * Copyright (c) Tuomo Valkonen 2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include + +#include + +#include "common.h" +#include "extlconv.h" +#include "region.h" +#include "navi.h" + + +WRegion *region_navi_first(WRegion *reg, WRegionNavi nh, + WRegionNaviData *data) +{ + WRegion *ret=NULL; + CALL_DYN_RET(ret, WRegion*, region_navi_first, reg, + (reg, nh, data)); + return ret; +} + + +WRegion *region_navi_next(WRegion *reg, WRegion *mgd, WRegionNavi nh, + WRegionNaviData *data) +{ + WRegion *ret=NULL; + CALL_DYN_RET(ret, WRegion*, region_navi_next, reg, + (reg, mgd, nh, data)); + return ret; +} + + +bool ioncore_string_to_navi(const char *str, WRegionNavi *nh) +{ + if(str==NULL){ + warn(TR("Invalid parameter.")); + return FALSE; + } + + if(!strcmp(str, "any")){ + *nh=REGION_NAVI_ANY; + }else if (!strcmp(str, "end") || + !strcmp(str, "last") || + !strcmp(str, "next")){ + *nh=REGION_NAVI_END; + }else if (!strcmp(str, "beg") || + !strcmp(str, "first") || + !strcmp(str, "prev")){ + *nh=REGION_NAVI_BEG; + }else if(!strcmp(str, "left")){ + *nh=REGION_NAVI_LEFT; + }else if(!strcmp(str, "right")){ + *nh=REGION_NAVI_RIGHT; + }else if(!strcmp(str, "top") || + !strcmp(str, "above") || + !strcmp(str, "up")){ + *nh=REGION_NAVI_TOP; + }else if(!strcmp(str, "bottom") || + !strcmp(str, "below") || + !strcmp(str, "down")){ + *nh=REGION_NAVI_BOTTOM; + }else{ + warn(TR("Invalid direction parameter.")); + return FALSE; + } + + return TRUE; +} + + +WRegionNavi ioncore_navi_reverse(WRegionNavi nh) +{ + if(nh==REGION_NAVI_BEG) + return REGION_NAVI_END; + else if(nh==REGION_NAVI_END) + return REGION_NAVI_BEG; + else if(nh==REGION_NAVI_LEFT) + return REGION_NAVI_RIGHT; + else if(nh==REGION_NAVI_RIGHT) + return REGION_NAVI_LEFT; + else if(nh==REGION_NAVI_TOP) + return REGION_NAVI_BOTTOM; + else if(nh==REGION_NAVI_BOTTOM) + return REGION_NAVI_TOP; + else + return REGION_NAVI_ANY; +} + + +DECLSTRUCT(WRegionNaviData){ + WRegionNavi nh; + bool descend; + ExtlFn ascend_filter; + ExtlFn descend_filter; + WRegion *startpoint; + bool nowrap; + Obj *no_ascend; + Obj *no_descend; + bool nofront; +}; + + +static bool may_ascend(WRegion *to, WRegion *from, WRegionNaviData *data) +{ + if(data->ascend_filter!=extl_fn_none()){ + bool r, v; + extl_protect(NULL); + r=extl_call(data->ascend_filter, "oo", "b", to, from, &v); + extl_unprotect(NULL); + return (r && v); + }else if(data->no_ascend!=NULL){ + return (data->no_ascend!=(Obj*)from); + }else{ + return !OBJ_IS(from, WMPlex); + } +} + + +static bool may_descend(WRegion *to, WRegion *from, WRegionNaviData *data) +{ + if(data->descend_filter!=extl_fn_none()){ + bool r, v; + extl_protect(NULL); + r=extl_call(data->descend_filter, "oo", "b", to, from, &v); + extl_unprotect(NULL); + return (r && v); + }else if(data->no_descend!=NULL){ + return (data->no_descend!=(Obj*)from); + }else{ + return !OBJ_IS(to, WMPlex); + } +} + + +static WRegion *region_navi_descend(WRegion *reg, WRegionNaviData *data) +{ + if(data->descend){ + return region_navi_first(reg, data->nh, data); + }else{ + WRegion *nxt; + + data->descend=TRUE; + data->nh=ioncore_navi_reverse(data->nh); + + nxt=region_navi_first(reg, data->nh, data); + + data->descend=FALSE; + data->nh=ioncore_navi_reverse(data->nh); + + return nxt; + } +} + + +WRegion *region_navi_cont(WRegion *reg, WRegion *res, WRegionNaviData *data) +{ + if(res==NULL){ + if(data->descend){ + return (reg==data->startpoint ? NULL : reg); + }else{ + WRegion *mgr=REGION_MANAGER(reg); + WRegion *nxt=NULL; + + if(mgr!=NULL && may_ascend(mgr, reg, data)){ + if(data->nowrap){ + /* tail-recursive case */ + return region_navi_next(mgr, reg, data->nh, data); + }else{ + nxt=region_navi_next(mgr, reg, data->nh, data); + } + } + + if(nxt==NULL && !data->nowrap){ + /* wrap */ + nxt=region_navi_descend(reg, data); + } + + return nxt; + } + }else{ + if(may_descend(res, reg, data)){ + return region_navi_descend(res, data); + }else{ + return res; + } + } +} + + +static bool get_param(WRegionNaviData *data, const char *dirstr, ExtlTab param) +{ + if(!ioncore_string_to_navi(dirstr, &data->nh)) + return FALSE; + + data->ascend_filter=extl_fn_none(); + data->descend_filter=extl_fn_none(); + data->no_ascend=NULL; + data->no_descend=NULL; + + extl_table_gets_o(param, "no_ascend", &data->no_ascend); + extl_table_gets_o(param, "no_descend", &data->no_descend); + extl_table_gets_f(param, "ascend_filter", &data->ascend_filter); + extl_table_gets_f(param, "descend_filter", &data->descend_filter); + data->nowrap=extl_table_is_bool_set(param, "nowrap"); + data->nofront=extl_table_is_bool_set(param, "nofront"); + + return TRUE; +} + + +static WRegion *release(WRegionNaviData *data, WRegion *res) +{ + extl_unref_fn(data->ascend_filter); + extl_unref_fn(data->descend_filter); + + return res; +} + + +/*EXTL_DOC + * Find region next from \var{reg} in direction \var{dirstr} + * (up/down/left/right/next/prev/any). The table param may + * contain the boolean field \var{nowrap}, instructing not to wrap + * around, and the \type{WRegion}s \var{no_ascend} and \var{no_descend}, + * and functions \var{ascend_filter} and \var{descend_filter} from + * \var{WRegion}s (\var{to}, \var{from}), used to decide when to descend + * or ascend into another region. (TODO: more detailed explanation.) + */ +EXTL_EXPORT +WRegion *ioncore_navi_next(WRegion *reg, const char *dirstr, ExtlTab param) +{ + WRegionNaviData data; + WRegion *mgr; + + if(reg==NULL){ + /* ??? */ + return NULL; + } + + if(!get_param(&data, dirstr, param)) + return NULL; + + mgr=REGION_MANAGER(reg); + + if(mgr==NULL) + return FALSE; + + data.startpoint=reg; + data.descend=FALSE; + + return release(&data, region_navi_next(mgr, reg, data.nh, &data)); +} + + +/*EXTL_DOC + * Find first region within \var{reg} in direction \var{dirstr} + * (up/down/left/right/beg/end/any). For information on \var{param}, + * see \fnref{ioncore.navi_next}. + */ +EXTL_EXPORT +WRegion *ioncore_navi_first(WRegion *reg, const char *dirstr, ExtlTab param) +{ + WRegionNaviData data; + + if(reg==NULL){ + /* ??? */ + return NULL; + } + + if(!get_param(&data, dirstr, param)) + return NULL; + + data.startpoint=reg; + data.descend=TRUE; + + return release(&data, region_navi_first(reg, data.nh, &data)); +} + + +static WRegion *do_goto(WRegion *res) +{ + if(res!=NULL){ + /* TODO: deep rqorder? */ + region_rqorder(res, REGION_ORDER_FRONT); + region_goto(res); + } + return res; +} + + +/*EXTL_DOC + * Go to region next from \var{reg} in direction \var{dirstr} + * (up/down/left/right/next/prev/any). For information on \var{param}, + * see \fnref{ioncore.navi_next}. Additionally this function supports + * the boolean \var{nofront} field, for not bringing the object to + * front. + */ +EXTL_EXPORT +WRegion *ioncore_goto_next(WRegion *reg, const char *dirstr, ExtlTab param) +{ + return do_goto(ioncore_navi_next(reg, dirstr, param)); +} + + +/*EXTL_DOC + * Go to first region within \var{reg} in direction \var{dirstr} + * (up/down/left/right/beg/end/any). For information on \var{param}, + * see \fnref{ioncore.navi_next}. Additionally this function supports + * the boolean \var{nofront} field, for not bringing the object to + * front. + */ +EXTL_EXPORT +WRegion *ioncore_goto_first(WRegion *reg, const char *dirstr, ExtlTab param) +{ + return do_goto(ioncore_navi_first(reg, dirstr, param)); +} + + diff --git a/ioncore/navi.h b/ioncore/navi.h new file mode 100644 index 0000000..3c38973 --- /dev/null +++ b/ioncore/navi.h @@ -0,0 +1,52 @@ +/* + * ion/ioncore/navi.h + * + * Copyright (c) Tuomo Valkonen 2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_NAVI_H +#define ION_IONCORE_NAVI_H + +#include "common.h" +#include "region.h" + + +typedef enum{ + REGION_NAVI_ANY, + REGION_NAVI_BEG, /* FIRST, PREV */ + REGION_NAVI_END, /* LAST, NEXT */ + REGION_NAVI_LEFT, + REGION_NAVI_RIGHT, + REGION_NAVI_TOP, + REGION_NAVI_BOTTOM +} WRegionNavi; + +INTRSTRUCT(WRegionNaviData); + +DYNFUN WRegion *region_navi_next(WRegion *reg, WRegion *rel, WRegionNavi nh, + WRegionNaviData *data); +DYNFUN WRegion *region_navi_first(WRegion *reg, WRegionNavi nh, + WRegionNaviData *data); + +extern WRegion *region_navi_cont(WRegion *reg, WRegion *res, + WRegionNaviData *data); + +extern bool ioncore_string_to_navi(const char *str, WRegionNavi *nv); + +extern WRegionNavi ioncore_navi_reverse(WRegionNavi nh); + +extern WRegion *ioncore_goto_next(WRegion *reg, const char *dirstr, + ExtlTab param); +extern WRegion *ioncore_goto_first(WRegion *reg, const char *dirstr, + ExtlTab param); +extern WRegion *ioncore_navi_next(WRegion *reg, const char *dirstr, + ExtlTab param); +extern WRegion *ioncore_navi_first(WRegion *reg, const char *dirstr, + ExtlTab param); + +#endif /* ION_IONCORE_NAVI_H */ diff --git a/ioncore/netwm.c b/ioncore/netwm.c new file mode 100644 index 0000000..cd0cb85 --- /dev/null +++ b/ioncore/netwm.c @@ -0,0 +1,255 @@ +/* + * ion/ioncore/netwm.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include + +#include +#include "common.h" +#include "global.h" +#include "fullscreen.h" +#include "clientwin.h" +#include "netwm.h" +#include "property.h" +#include "focus.h" +#include "xwindow.h" +#include "extlconv.h" + + +/*{{{ Atoms */ + +static Atom atom_net_wm_name=0; +static Atom atom_net_wm_state=0; +static Atom atom_net_wm_state_fullscreen=0; +static Atom atom_net_supporting_wm_check=0; +static Atom atom_net_virtual_roots=0; +static Atom atom_net_active_window=0; + +#define N_NETWM 6 + +static Atom atom_net_supported=0; + +/*}}}*/ + + +/*{{{ Initialisation */ + + +void netwm_init() +{ + atom_net_wm_name=XInternAtom(ioncore_g.dpy, "_NET_WM_NAME", False); + atom_net_wm_state=XInternAtom(ioncore_g.dpy, "_NET_WM_STATE", False); + atom_net_wm_state_fullscreen=XInternAtom(ioncore_g.dpy, "_NET_WM_STATE_FULLSCREEN", False); + atom_net_supported=XInternAtom(ioncore_g.dpy, "_NET_SUPPORTED", False); + atom_net_supporting_wm_check=XInternAtom(ioncore_g.dpy, "_NET_SUPPORTING_WM_CHECK", False); + atom_net_virtual_roots=XInternAtom(ioncore_g.dpy, "_NET_VIRTUAL_ROOTS", False); + atom_net_active_window=XInternAtom(ioncore_g.dpy, "_NET_ACTIVE_WINDOW", False); +} + + +void netwm_init_rootwin(WRootWin *rw) +{ + Atom atoms[N_NETWM]; + const char *p[1]; + + atoms[0]=atom_net_wm_name; + atoms[1]=atom_net_wm_state; + atoms[2]=atom_net_wm_state_fullscreen; + atoms[3]=atom_net_supporting_wm_check; + atoms[4]=atom_net_virtual_roots; + atoms[5]=atom_net_active_window; + + XChangeProperty(ioncore_g.dpy, WROOTWIN_ROOT(rw), + atom_net_supporting_wm_check, XA_WINDOW, + 32, PropModeReplace, (uchar*)&(rw->dummy_win), 1); + XChangeProperty(ioncore_g.dpy, WROOTWIN_ROOT(rw), + atom_net_supported, XA_ATOM, + 32, PropModeReplace, (uchar*)atoms, N_NETWM); + /* Something else should probably be used as WM name here. */ + p[0]=prog_execname(); + xwindow_set_text_property(rw->dummy_win, atom_net_wm_name, p, 1); +} + + +/*}}}*/ + + +/*{{{ _NET_WM_STATE */ + + +int netwm_check_initial_fullscreen(WClientWin *cwin, bool sw) +{ + + int i, n; + int ret=0; + long *data; + + n=xwindow_get_property(cwin->win, atom_net_wm_state, XA_ATOM, + 1, TRUE, (uchar**)&data); + + if(n<0) + return -1; + + for(i=0; iwin, atom_net_wm_state, + XA_ATOM, 32, PropModeReplace, (uchar*)data, n); +} + + +void netwm_delete_state(WClientWin *cwin) +{ + XDeleteProperty(ioncore_g.dpy, cwin->win, atom_net_wm_state); +} + + + +static void netwm_state_change_rq(WClientWin *cwin, + const XClientMessageEvent *ev) +{ + if((ev->data.l[1]==0 || + ev->data.l[1]!=(long)atom_net_wm_state_fullscreen) && + (ev->data.l[2]==0 || + ev->data.l[2]!=(long)atom_net_wm_state_fullscreen)){ + return; + } + + /* Ok, full screen add/remove/toggle */ + if(!REGION_IS_FULLSCREEN(cwin)){ + if(ev->data.l[0]==_NET_WM_STATE_ADD || + ev->data.l[0]==_NET_WM_STATE_TOGGLE){ + bool sw=clientwin_fullscreen_may_switchto(cwin); + cwin->flags|=CLIENTWIN_FS_RQ; + if(!clientwin_enter_fullscreen(cwin, sw)) + cwin->flags&=~CLIENTWIN_FS_RQ; + }else{ + /* Should not be set.. */ + cwin->flags&=~CLIENTWIN_FS_RQ; + } + }else{ + if(ev->data.l[0]==_NET_WM_STATE_REMOVE || + ev->data.l[0]==_NET_WM_STATE_TOGGLE){ + bool sw=clientwin_fullscreen_may_switchto(cwin); + cwin->flags&=~CLIENTWIN_FS_RQ; + clientwin_leave_fullscreen(cwin, sw); + }else{ + /* Set the flag */ + cwin->flags|=CLIENTWIN_FS_RQ; + } + } +} + + +/*}}}*/ + + +/*{{{ _NET_ACTIVE_WINDOW */ + + +void netwm_set_active(WRegion *reg) +{ + CARD32 data[1]={None}; + + if(OBJ_IS(reg, WClientWin)) + data[0]=region_xwindow(reg); + + /* The spec doesn't say how multihead should be handled, so + * we just update the root window the window is on. + */ + XChangeProperty(ioncore_g.dpy, region_root_of(reg), + atom_net_active_window, XA_WINDOW, + 32, PropModeReplace, (uchar*)data, 1); +} + + +static void netwm_active_window_rq(WClientWin *cwin, + const XClientMessageEvent *ev) +{ + bool ignore=TRUE; + + extl_table_gets_b(cwin->proptab, "ignore_net_active_window", &ignore); + + if(!ignore) + region_goto((WRegion*)cwin); +} + + +/*}}}*/ + + +/*{{{ _NET_WM_NAME */ + + +char **netwm_get_name(WClientWin *cwin) +{ + return xwindow_get_text_property(cwin->win, atom_net_wm_name, NULL); +} + + +/*}}}*/ + + +/*{{{ netwm_handle_client_message */ + + +void netwm_handle_client_message(const XClientMessageEvent *ev) +{ + /* Check _NET_WM_STATE fullscreen request */ + if(ev->message_type==atom_net_wm_state && ev->format==32){ + WClientWin *cwin=XWINDOW_REGION_OF_T(ev->window, WClientWin); + if(cwin!=NULL) + netwm_state_change_rq(cwin, ev); + } + /* Check _NET_ACTIVE_WINDOW request */ + else if(ev->message_type==atom_net_active_window && ev->format==32){ + WClientWin *cwin=XWINDOW_REGION_OF_T(ev->window, WClientWin); + if(cwin!=NULL) + netwm_active_window_rq(cwin, ev); + } +} + + +/*}}}*/ + + +/*{{{ netwm_handle_property */ + + +bool netwm_handle_property(WClientWin *cwin, const XPropertyEvent *ev) +{ + if(ev->atom!=atom_net_wm_name) + return FALSE; + + clientwin_get_set_name(cwin); + return TRUE; +} + + +/*}}}*/ diff --git a/ioncore/netwm.h b/ioncore/netwm.h new file mode 100644 index 0000000..bbfa9b9 --- /dev/null +++ b/ioncore/netwm.h @@ -0,0 +1,34 @@ +/* + * ion/ioncore/netwm.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_NETWM_H +#define ION_IONCORE_NETWM_H + +#include "common.h" +#include "rootwin.h" + +#define _NET_WM_STATE_REMOVE 0 /* remove/unset property */ +#define _NET_WM_STATE_ADD 1 /* add/set property */ +#define _NET_WM_STATE_TOGGLE 2 /* toggle property */ + +extern void netwm_init(); +extern void netwm_init_rootwin(WRootWin *rw); + +extern int netwm_check_initial_fullscreen(WClientWin *cwin, bool switchto); +extern void netwm_update_state(WClientWin *cwin); +extern void netwm_delete_state(WClientWin *cwin); +extern void netwm_set_active(WRegion *reg); +extern char **netwm_get_name(WClientWin *cwin); + +extern void netwm_handle_client_message(const XClientMessageEvent *ev); +extern bool netwm_handle_property(WClientWin *cwin, const XPropertyEvent *ev); + +#endif /* ION_IONCORE_NETWM_H */ diff --git a/ioncore/pholder.c b/ioncore/pholder.c new file mode 100644 index 0000000..4aed2cc --- /dev/null +++ b/ioncore/pholder.c @@ -0,0 +1,210 @@ +/* + * ion/ioncore/pholder.c + * + * Copyright (c) Tuomo Valkonen 2005-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include "common.h" +#include "attach.h" +#include "pholder.h" + + +bool pholder_init(WPHolder *ph) +{ + ph->redirect=NULL; + return TRUE; +} + + +void pholder_deinit(WPHolder *ph) +{ + if(ph->redirect!=NULL) + destroy_obj((Obj*)ph->redirect); +} + + +WRegion *pholder_do_attach(WPHolder *ph, int flags, + WRegionAttachData *data) + +{ + WRegion *ret=NULL; + CALL_DYN_RET(ret, WRegion*, pholder_do_attach, ph, (ph, flags, data)); + return ret; +} + + +static WRegion *add_fn_reparent(WWindow *par, const WFitParams *fp, + WRegion *reg) +{ + if(!region_fitrep(reg, par, fp)){ + warn(TR("Unable to reparent.")); + return NULL; + } + region_detach_manager(reg); + return reg; +} + + +WRegion *pholder_attach_(WPHolder *ph, int flags, WRegionAttachData *data) +{ + if(ph->redirect!=NULL) + return pholder_attach_(ph->redirect, flags, data); + else + return pholder_do_attach(ph, flags, data); +} + + +bool pholder_attach(WPHolder *ph, int flags, WRegion *reg) +{ + WRegionAttachData data; + + data.type=REGION_ATTACH_REPARENT; + data.u.reg=reg; + + return (pholder_attach_(ph, flags, &data)!=NULL); +} + + +WRegion *pholder_do_target(WPHolder *ph) +{ + WRegion *ret=NULL; + CALL_DYN_RET(ret, WRegion*, pholder_do_target, ph, (ph)); + return ret; +} + + +WRegion *pholder_target(WPHolder *ph) +{ + if(ph->redirect!=NULL) + return pholder_target(ph->redirect); + else + return pholder_do_target(ph); +} + + +static bool pholder_do_check_reparent_default(WPHolder *ph, WRegion *reg) +{ + WRegion *target=pholder_do_target(ph); + + if(target==NULL) + return FALSE; + else + return region_attach_reparent_check(target, reg); +} + + +DYNFUN bool pholder_do_check_reparent(WPHolder *ph, WRegion *reg) +{ + bool ret=FALSE; + CALL_DYN_RET(ret, bool, pholder_do_check_reparent, ph, (ph, reg)); + return ret; +} + + +bool pholder_check_reparent(WPHolder *ph, WRegion *reg) +{ + if(ph->redirect!=NULL) + return pholder_check_reparent(ph->redirect, reg); + else + return pholder_do_check_reparent(ph, reg); +} + + +bool pholder_do_goto(WPHolder *ph) +{ + bool ret=FALSE; + CALL_DYN_RET(ret, bool, pholder_do_goto, ph, (ph)); + return ret; +} + + +bool pholder_goto(WPHolder *ph) +{ + if(ph->redirect!=NULL) + return pholder_goto(ph->redirect); + else + return pholder_do_goto(ph); +} + + + +bool pholder_redirect(WPHolder *ph, WRegion *old_target) +{ + WPHolder *ph2=region_get_rescue_pholder(old_target); + + if(ph2==NULL) + return FALSE; + + if(ph->redirect!=NULL) + destroy_obj((Obj*)ph->redirect); + + ph->redirect=ph2; + + return TRUE; +} + + +WPHolder *region_managed_get_pholder(WRegion *reg, WRegion *mgd) +{ + WPHolder *ret=NULL; + CALL_DYN_RET(ret, WPHolder*, region_managed_get_pholder, + reg, (reg, mgd)); + return ret; +} + + +WPHolder *region_get_rescue_pholder_for(WRegion *reg, WRegion *mgd) +{ + if(OBJ_IS_BEING_DESTROYED(reg) || reg->flags®ION_CWINS_BEING_RESCUED){ + return FALSE; + }else{ + WPHolder *ret=NULL; + + CALL_DYN_RET(ret, WPHolder*, region_get_rescue_pholder_for, + reg, (reg, mgd)); + return ret; + } +} + + +WPHolder *region_get_rescue_pholder(WRegion *reg) +{ + WRegion *mgr; + WPHolder *ph=NULL; + + while(1){ + mgr=region_manager_or_parent(reg); + if(mgr==NULL) + break; + ph=region_get_rescue_pholder_for(mgr, reg); + if(ph!=NULL) + break; + reg=mgr; + } + + return ph; +} + + +WPHolder *pholder_either(WPHolder *a, WPHolder *b) +{ + return (a!=NULL ? a : b); +} + + +static DynFunTab pholder_dynfuntab[]={ + {(DynFun*)pholder_do_check_reparent, + (DynFun*)pholder_do_check_reparent_default}, + + END_DYNFUNTAB +}; + + +IMPLCLASS(WPHolder, Obj, pholder_deinit, pholder_dynfuntab); + diff --git a/ioncore/pholder.h b/ioncore/pholder.h new file mode 100644 index 0000000..7fe2710 --- /dev/null +++ b/ioncore/pholder.h @@ -0,0 +1,63 @@ +/* + * ion/ioncore/pholder.h + * + * Copyright (c) Tuomo Valkonen 2005-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_PHOLDER_H +#define ION_IONCORE_PHOLDER_H + +#include "common.h" +#include "attach.h" +#include "extlconv.h" + + +#define PHOLDER_ATTACH_SWITCHTO 0x0001 + + +/* Note: PHolders should be destroyed by their acquirer. */ + +DECLCLASS(WPHolder){ + Obj obj; + WPHolder *redirect; +}; + + +extern bool pholder_init(WPHolder *ph); +extern void pholder_deinit(WPHolder *ph); + +DYNFUN WRegion *pholder_do_attach(WPHolder *ph, int flags, + WRegionAttachData *data); + +extern WRegion *pholder_attach_(WPHolder *ph, int flags, + WRegionAttachData *data); + +extern bool pholder_attach(WPHolder *ph, int flags, WRegion *reg); + +DYNFUN WRegion *pholder_do_target(WPHolder *ph); + +extern WRegion *pholder_target(WPHolder *ph); + +DYNFUN bool pholder_do_check_reparent(WPHolder *ph, WRegion *reg); + +extern bool pholder_check_reparent(WPHolder *ph, WRegion *reg); + +DYNFUN bool pholder_do_goto(WPHolder *ph); + +extern bool pholder_goto(WPHolder *ph); + +extern bool pholder_redirect(WPHolder *ph, WRegion *old_target); + +extern WPHolder *pholder_either(WPHolder *a, WPHolder *b); + +DYNFUN WPHolder *region_managed_get_pholder(WRegion *reg, WRegion *mgd); +DYNFUN WPHolder *region_get_rescue_pholder_for(WRegion *reg, WRegion *mgd); + +extern WPHolder *region_get_rescue_pholder(WRegion *reg); + +#endif /* ION_IONCORE_PHOLDER_H */ diff --git a/ioncore/pointer.c b/ioncore/pointer.c new file mode 100644 index 0000000..977940d --- /dev/null +++ b/ioncore/pointer.c @@ -0,0 +1,349 @@ +/* + * ion/ioncore/pointer.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include "common.h" +#include "pointer.h" +#include "cursor.h" +#include "event.h" +#include "global.h" +#include "focus.h" +#include "regbind.h" +#include "grab.h" +#include "xwindow.h" + + +/*{{{ Variables */ + + +static uint p_button=0, p_state=0; +static int p_x=-1, p_y=-1; +static int p_orig_x=-1, p_orig_y=-1; +static bool p_motion=FALSE; +static int p_clickcnt=0; +static Time p_time=0; +static int p_area=0; +static enum{ST_NO, ST_INIT, ST_HELD} p_grabstate=ST_NO; + +static WButtonHandler *p_motion_end_handler=NULL; +static WMotionHandler *p_motion_handler=NULL; +static WMotionHandler *p_motion_begin_handler=NULL; +static GrabHandler *p_key_handler=NULL; +static GrabKilledHandler *p_killed_handler=NULL; + +static Watch p_regwatch=WATCH_INIT, p_subregwatch=WATCH_INIT; + +#define p_reg ((WRegion*)p_regwatch.obj) +#define p_subreg ((WRegion*)p_subregwatch.obj) + + +/*}}}*/ + + +/*{{{ Handler setup */ + + +bool ioncore_set_drag_handlers(WRegion *reg, + WMotionHandler *begin, + WMotionHandler *motion, + WButtonHandler *end, + GrabHandler *keypress, + GrabKilledHandler *killed) +{ + if(ioncore_pointer_grab_region()==NULL || p_motion) + return FALSE; + + /* A motion handler set at this point may not set a begin handler */ + if(p_grabstate!=ST_HELD && begin!=NULL) + return FALSE; + + if(p_reg!=reg){ + watch_setup(&p_regwatch, (Obj*)reg, NULL); + watch_reset(&p_subregwatch); + } + + p_motion_begin_handler=begin; + p_motion_handler=motion; + p_motion_end_handler=end; + p_key_handler=keypress; + p_killed_handler=killed; + p_motion=TRUE; + + return TRUE; +} + + +/*}}}*/ + + +/*{{{ Misc. */ + + +static bool time_in_threshold(Time time) +{ + Time t; + + if(timep_x-CF_DRAG_TRESHOLD && xp_y-CF_DRAG_TRESHOLD && yfunc, "oo", NULL, p_reg, p_subreg); + p_curr_event=NULL; +} + + +static void call_motion(XMotionEvent *ev, int dx, int dy) +{ + if(p_motion_handler!=NULL && p_reg!=NULL){ + p_curr_event=(XEvent*)ev; + p_motion_handler(p_reg, ev, dx, dy); + p_curr_event=NULL; + } +} + + +static void call_motion_end(XButtonEvent *ev) +{ + if(p_motion_end_handler!=NULL && p_reg!=NULL){ + p_curr_event=(XEvent*)ev; + p_motion_end_handler(p_reg, ev); + p_curr_event=NULL; + } +} + + +static void call_motion_begin(WBinding *binding, XMotionEvent *ev, + int dx, int dy) +{ + WMotionHandler *fn; + + if(binding==NULL) + return; + + p_curr_event=(XEvent*)ev; + + extl_call(binding->func, "oo", NULL, p_reg, p_subreg); + + if(p_motion_begin_handler!=NULL && p_reg!=NULL) + p_motion_begin_handler(p_reg, ev, dx, dy); + + p_motion_begin_handler=NULL; + + p_curr_event=NULL; +} + + +/*}}}*/ + + +/*{{{ ioncore_handle_button_press/release/motion */ + + +static void finish_pointer() +{ + if(p_reg!=NULL) + window_release((WWindow*)p_reg); + p_grabstate=ST_NO; + watch_reset(&p_subregwatch); +} + + +static bool handle_key(WRegion *reg, XEvent *ev) +{ + if(p_key_handler!=NULL){ + if(p_key_handler(reg, ev)){ + finish_pointer(); + return TRUE; + } + } + return FALSE; +} + + +static void pointer_grab_killed(WRegion *unused) +{ + if(p_reg!=NULL && p_killed_handler!=NULL) + p_killed_handler(p_reg); + watch_reset(&p_regwatch); + finish_pointer(); +} + + +bool ioncore_do_handle_buttonpress(XButtonEvent *ev) +{ + WBinding *pressbind=NULL; + WRegion *reg=NULL; + WRegion *subreg=NULL; + uint button, state; + bool dblclick; + + state=ev->state; + button=ev->button; + + reg=(WRegion*)XWINDOW_REGION_OF_T(ev->window, WWindow); + + if(reg==NULL) + return FALSE; + + if(ev->subwindow!=None){ + XButtonEvent ev2=*ev; + ev2.window=ev->subwindow; + if(XTranslateCoordinates(ioncore_g.dpy, ev->window, ev2.window, + ev->x, ev->y, &(ev2.x), &(ev2.y), + &(ev2.subwindow))){ + if(ioncore_do_handle_buttonpress(&ev2)) + return TRUE; + } + } + + dblclick=(p_clickcnt==1 && time_in_threshold(ev->time) && + p_button==button && p_state==state && reg==p_reg); + + p_motion=FALSE; + p_motion_begin_handler=NULL; + p_motion_handler=NULL; + p_motion_end_handler=NULL; + p_key_handler=NULL; + p_killed_handler=NULL; + p_grabstate=ST_INIT; + p_button=button; + p_state=state; + p_orig_x=p_x=ev->x_root; + p_orig_y=p_y=ev->y_root; + p_time=ev->time; + p_clickcnt=0; + + watch_setup(&p_regwatch, (Obj*)reg, NULL); + + subreg=region_current(reg); + p_area=window_press((WWindow*)reg, ev, &subreg); + if(subreg!=NULL) + watch_setup(&p_subregwatch, (Obj*)subreg, NULL); + + if(dblclick){ + pressbind=region_lookup_binding(reg, BINDING_BUTTONDBLCLICK, state, + button, p_area); + } + + if(pressbind==NULL){ + pressbind=region_lookup_binding(reg, BINDING_BUTTONPRESS, state, + button, p_area); + } + + ioncore_grab_establish(reg, handle_key, pointer_grab_killed, 0); + p_grabstate=ST_HELD; + + call_button(pressbind, ev); + + return TRUE; +} + + +bool ioncore_do_handle_buttonrelease(XButtonEvent *ev) +{ + WBinding *binding=NULL; + + if(p_button!=ev->button) + return FALSE; + + if(p_reg!=NULL){ + if(p_motion==FALSE){ + p_clickcnt=1; + binding=region_lookup_binding(p_reg, BINDING_BUTTONCLICK, + p_state, p_button, p_area); + call_button(binding, ev); + }else{ + call_motion_end(ev); + } + + } + + ioncore_grab_remove(handle_key); + finish_pointer(); + + return TRUE; +} + + +void ioncore_do_handle_motionnotify(XMotionEvent *ev) +{ + int dx, dy; + WBinding *binding=NULL; + + if(p_reg==NULL) + return; + + if(!p_motion){ + if(motion_in_threshold(ev->x_root, ev->y_root)) + return; + binding=region_lookup_binding(p_reg, BINDING_BUTTONMOTION, + p_state, p_button, p_area); + } + + p_time=ev->time; + dx=ev->x_root-p_x; + dy=ev->y_root-p_y; + p_x=ev->x_root; + p_y=ev->y_root; + + if(!p_motion){ + call_motion_begin(binding, ev, dx, dy); + p_motion=TRUE; + }else{ + call_motion(ev, dx, dy); + } +} + + +/*}}}*/ + diff --git a/ioncore/pointer.h b/ioncore/pointer.h new file mode 100644 index 0000000..bf2e00a --- /dev/null +++ b/ioncore/pointer.h @@ -0,0 +1,36 @@ +/* + * ion/ioncore/pointer.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_POINTER_H +#define ION_IONCORE_POINTER_H + +#include "common.h" +#include "region.h" +#include "grab.h" + +typedef void WButtonHandler(WRegion *reg, XButtonEvent *ev); +typedef void WMotionHandler(WRegion *reg, XMotionEvent *ev, int dx, int dy); + +extern bool ioncore_do_handle_buttonpress(XButtonEvent *ev); +extern bool ioncore_do_handle_buttonrelease(XButtonEvent *ev); +extern void ioncore_do_handle_motionnotify(XMotionEvent *ev); + +extern XEvent *ioncore_current_pointer_event(); +extern WRegion *ioncore_pointer_grab_region(); + +extern bool ioncore_set_drag_handlers(WRegion *reg, + WMotionHandler *begin, + WMotionHandler *motion, + WButtonHandler *end, + GrabHandler *handler, + GrabKilledHandler *killhandler); + +#endif /* ION_IONCORE_POINTER_H */ diff --git a/ioncore/presize.c b/ioncore/presize.c new file mode 100644 index 0000000..80b7203 --- /dev/null +++ b/ioncore/presize.c @@ -0,0 +1,177 @@ +/* + * ion/ioncore/presize.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include "presize.h" +#include "resize.h" +#include "window.h" +#include "pointer.h" +#include "grab.h" + + +/*{{{ Resize */ + + +static int p_dx1mul=0, p_dx2mul=0, p_dy1mul=0, p_dy2mul=0; + + +#define MINCORNER 16 + + +void window_p_resize_prepare(WWindow *wwin, XButtonEvent *ev) +{ + int ww=REGION_GEOM(wwin).w/2; + int hh=REGION_GEOM(wwin).h/2; + int xdiv, ydiv; + int tmpx, tmpy, atmpx, atmpy; + + p_dx1mul=0; + p_dx2mul=0; + p_dy1mul=0; + p_dy2mul=0; + + tmpx=ev->x-ww; + tmpy=hh-ev->y; + xdiv=ww/2; + ydiv=hh/2; + atmpx=abs(tmpx); + atmpy=abs(tmpy); + + if(xdiv1){ + xdiv=ww-MINCORNER; + if(xdiv<1) + xdiv=1; + } + + if(ydiv1){ + ydiv=hh-MINCORNER; + if(ydiv<1) + ydiv=1; + } + + if(xdiv==0){ + p_dx2mul=1; + }else if(hh*atmpx/xdiv>=tmpy && -hh*atmpx/xdiv<=tmpy){ + p_dx1mul=(tmpx<0); + p_dx2mul=(tmpx>=0); + } + + if(ydiv==0){ + p_dy2mul=1; + }else if(ww*atmpy/ydiv>=tmpx && -ww*atmpy/ydiv<=tmpx){ + p_dy1mul=(tmpy>0); + p_dy2mul=(tmpy<=0); + } +} + + +static void p_moveres_end(WWindow *wwin, XButtonEvent *ev) +{ + WMoveresMode *mode=moveres_mode((WRegion*)wwin); + if(mode!=NULL) + moveresmode_do_end(mode, TRUE); +} + + +static void p_moveres_cancel(WWindow *wwin) +{ + WMoveresMode *mode=moveres_mode((WRegion*)wwin); + if(mode!=NULL) + moveresmode_do_end(mode, FALSE); +} + + +static void confine_to_parent(WWindow *wwin) +{ + WRegion *par=REGION_PARENT_REG(wwin); + if(par!=NULL) + ioncore_grab_confine_to(region_xwindow(par)); +} + + +static void p_resize_motion(WWindow *wwin, XMotionEvent *ev, int dx, int dy) +{ + WMoveresMode *mode=moveres_mode((WRegion*)wwin); + if(mode!=NULL){ + moveresmode_delta_resize(mode, p_dx1mul*dx, p_dx2mul*dx, + p_dy1mul*dy, p_dy2mul*dy, NULL); + } +} + + +static void p_resize_begin(WWindow *wwin, XMotionEvent *ev, int dx, int dy) +{ + region_begin_resize((WRegion*)wwin, NULL, TRUE); + p_resize_motion(wwin, ev, dx, dy); +} + + +/*EXTL_DOC + * Start resizing \var{wwin} with the mouse or other pointing device. + * This function should only be used by binding it to \emph{mpress} or + * \emph{mdrag} action. + */ +EXTL_EXPORT_MEMBER +void window_p_resize(WWindow *wwin) +{ + if(!ioncore_set_drag_handlers((WRegion*)wwin, + (WMotionHandler*)p_resize_begin, + (WMotionHandler*)p_resize_motion, + (WButtonHandler*)p_moveres_end, + NULL, + (GrabKilledHandler*)p_moveres_cancel)) + return; + + confine_to_parent(wwin); +} + + +/*}}}*/ + + +/*{{{ Move */ + + +static void p_move_motion(WWindow *wwin, XMotionEvent *ev, int dx, int dy) +{ + WMoveresMode *mode=moveres_mode((WRegion*)wwin); + if(mode!=NULL) + moveresmode_delta_move(mode, dx, dy, NULL); +} + + +static void p_move_begin(WWindow *wwin, XMotionEvent *ev, int dx, int dy) +{ + region_begin_move((WRegion*)wwin, NULL, TRUE); + p_move_motion(wwin, ev, dx, dy); +} + + +/*EXTL_DOC + * Start moving \var{wwin} with the mouse or other pointing device. + * This function should only be used by binding it to \emph{mpress} or + * \emph{mdrag} action. + */ +EXTL_EXPORT_MEMBER +void window_p_move(WWindow *wwin) +{ + if(!ioncore_set_drag_handlers((WRegion*)wwin, + (WMotionHandler*)p_move_begin, + (WMotionHandler*)p_move_motion, + (WButtonHandler*)p_moveres_end, + NULL, + (GrabKilledHandler*)p_moveres_cancel)) + return; + + confine_to_parent(wwin); +} + + +/*}}}*/ diff --git a/ioncore/presize.h b/ioncore/presize.h new file mode 100644 index 0000000..85fd889 --- /dev/null +++ b/ioncore/presize.h @@ -0,0 +1,22 @@ +/* + * ion/ioncore/presize.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_PRESIZE_H +#define ION_IONCORE_PRESIZE_H + +#include "common.h" +#include "window.h" + +extern void window_p_resize_prepare(WWindow *wwin, XButtonEvent *ev); +extern void window_p_resize(WWindow *wwin); +extern void window_p_move(WWindow *wwin); + +#endif /* ION_IONCORE_PRESIZE_H */ diff --git a/ioncore/property.c b/ioncore/property.c new file mode 100644 index 0000000..3bacca1 --- /dev/null +++ b/ioncore/property.c @@ -0,0 +1,441 @@ +/* + * ion/ioncore/property.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include + +#include "common.h" +#include "property.h" +#include "global.h" + + +/*{{{ Primitives */ + + +static ulong xwindow_get_property_(Window win, Atom atom, Atom type, + ulong n32expected, bool more, uchar **p, + int *format) +{ + Atom real_type; + ulong n=-1, extra=0; + int status; + + do{ + status=XGetWindowProperty(ioncore_g.dpy, win, atom, 0L, n32expected, + False, type, &real_type, format, &n, + &extra, p); + + if(status!=Success || *p==NULL) + return -1; + + if(extra==0 || !more) + break; + + XFree((void*)*p); + n32expected+=(extra+4)/4; + more=FALSE; + }while(1); + + if(n==0){ + XFree((void*)*p); + *p=NULL; + return -1; + } + + return n; +} + + +ulong xwindow_get_property(Window win, Atom atom, Atom type, + ulong n32expected, bool more, uchar **p) +{ + int format=0; + return xwindow_get_property_(win, atom, type, n32expected, more, p, + &format); +} + + +/*}}}*/ + + +/*{{{ String property stuff */ + + +char *xwindow_get_string_property(Window win, Atom a, int *nret) +{ + char *p; + int n; + + n=xwindow_get_property(win, a, XA_STRING, 64L, TRUE, (uchar**)&p); + + if(nret!=NULL) + *nret=n; + + return (n<=0 ? NULL : p); +} + + +void xwindow_set_string_property(Window win, Atom a, const char *value) +{ + if(value==NULL){ + XDeleteProperty(ioncore_g.dpy, win, a); + }else{ + XChangeProperty(ioncore_g.dpy, win, a, XA_STRING, + 8, PropModeReplace, (uchar*)value, strlen(value)); + } +} + + +/*}}}*/ + + +/*{{{ Integer property stuff */ + + +bool xwindow_get_integer_property(Window win, Atom a, int *vret) +{ + long *p=NULL; + ulong n; + + n=xwindow_get_property(win, a, XA_INTEGER, 1L, FALSE, (uchar**)&p); + + if(n>0 && p!=NULL){ + *vret=*p; + XFree((void*)p); + return TRUE; + } + + return FALSE; +} + + +void xwindow_set_integer_property(Window win, Atom a, int value) +{ + CARD32 data[2]; + + data[0]=value; + + XChangeProperty(ioncore_g.dpy, win, a, XA_INTEGER, + 32, PropModeReplace, (uchar*)data, 1); +} + + +/* WM_STATE + */ + +bool xwindow_get_state_property(Window win, int *state) +{ + CARD32 *p=NULL; + + if(xwindow_get_property(win, ioncore_g.atom_wm_state, + ioncore_g.atom_wm_state, + 2L, FALSE, (uchar**)&p)<=0) + return FALSE; + + *state=*p; + + XFree((void*)p); + + return TRUE; +} + + +void xwindow_set_state_property(Window win, int state) +{ + CARD32 data[2]; + + data[0]=state; + data[1]=None; + + XChangeProperty(ioncore_g.dpy, win, + ioncore_g.atom_wm_state, ioncore_g.atom_wm_state, + 32, PropModeReplace, (uchar*)data, 2); +} + + +/*}}}*/ + + +/*{{{ Text property stuff */ + + +char **xwindow_get_text_property(Window win, Atom a, int *nret) +{ + XTextProperty prop; + char **list=NULL; + int n=0; + Status st=0; + + st=XGetTextProperty(ioncore_g.dpy, win, &prop, a); + + if(nret) + *nret=(!st ? 0 : -1); + + if(!st) + return NULL; + +#ifdef CF_XFREE86_TEXTPROP_BUG_WORKAROUND + while(prop.nitems>0){ + if(prop.value[prop.nitems-1]=='\0') + prop.nitems--; + else + break; + } +#endif + + if(!ioncore_g.use_mb){ + st=XTextPropertyToStringList(&prop, &list, &n); + }else{ + st=XmbTextPropertyToTextList(ioncore_g.dpy, &prop, &list, &n); + st=!st; + } + + XFree(prop.value); + + if(!st || n==0 || list==NULL) + return NULL; + + if(nret) + *nret=n; + + return list; +} + + +void xwindow_set_text_property(Window win, Atom a, const char **ptr, int n) +{ + XTextProperty prop; + Status st; + + if(!ioncore_g.use_mb){ + st=XStringListToTextProperty((char **)&ptr, n, &prop); + }else{ +#ifdef X_HAVE_UTF8_STRING + st=XmbTextListToTextProperty(ioncore_g.dpy, (char **)ptr, n, + XUTF8StringStyle, &prop); +#else + st=XmbTextListToTextProperty(ioncore_g.dpy, (char **)ptr, n, + XTextStyle, &prop); +#endif + st=!st; + } + + if(!st) + return; + + XSetTextProperty(ioncore_g.dpy, win, &prop, a); + XFree(prop.value); +} + + +/*}}}*/ + + +/*{{{ Exports */ + + +/*EXTL_DOC + * Create a new atom. See \code{XInternAtom}(3) manual page for details. + */ +EXTL_EXPORT +int ioncore_x_intern_atom(const char *name, bool only_if_exists) +{ + return XInternAtom(ioncore_g.dpy, name, only_if_exists); +} + + +/*EXTL_DOC + * Get the name of an atom. See \code{XGetAtomName}(3) manual page for + * details. + */ +EXTL_EXPORT +char *ioncore_x_get_atom_name(int atom) +{ + return XGetAtomName(ioncore_g.dpy, atom); +} + + +#define CP(TYPE) \ + { \ + TYPE *d=(TYPE*)p; \ + for(i=0; i + +#include "common.h" + +extern ulong xwindow_get_property(Window win, Atom atom, Atom type, + ulong n32expected, bool more, uchar **p); +extern char *xwindow_get_string_property(Window win, Atom a, int *nret); +extern void xwindow_set_string_property(Window win, Atom a, const char *value); +extern bool xwindow_get_integer_property(Window win, Atom a, int *vret); +extern void xwindow_set_integer_property(Window win, Atom a, int value); +extern bool xwindow_get_state_property(Window win, int *state); +extern void xwindow_set_state_property(Window win, int state); +extern char **xwindow_get_text_property(Window win, Atom a, int *nret); +extern void xwindow_set_text_property(Window win, Atom a, + const char **p, int n); + +#endif /* ION_IONCORE_PROPERTY_H */ + diff --git a/ioncore/rectangle.c b/ioncore/rectangle.c new file mode 100644 index 0000000..00cae86 --- /dev/null +++ b/ioncore/rectangle.c @@ -0,0 +1,48 @@ +/* + * ion/ioncore/rectangle.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include + +#include "common.h" +#include "rectangle.h" + + +void rectangle_constrain(WRectangle *g, const WRectangle *bounds) +{ + const WRectangle tmpg=*g; + + g->x=minof(maxof(tmpg.x, bounds->x), tmpg.x+tmpg.w-1); + g->y=minof(maxof(tmpg.y, bounds->y), tmpg.y+tmpg.h-1); + g->w=maxof(1, minof(bounds->x+bounds->w, tmpg.x+tmpg.w)-g->x); + g->h=maxof(1, minof(bounds->y+bounds->h, tmpg.y+tmpg.h)-g->y); +} + + +bool rectangle_contains(const WRectangle *g, int x, int y) +{ + return (x>=g->x && xx+g->w && y>=g->y && yy+g->h); +} + + +void rectangle_debugprint(const WRectangle *g, const char *n) +{ + fprintf(stderr, "%s %d, %d; %d, %d\n", n, g->x, g->y, g->w, g->h); +} + + +int rectangle_compare(const WRectangle *g, const WRectangle *h) +{ + return ((g->x!=h->x ? RECTANGLE_X_DIFF : 0) | + (g->y!=h->y ? RECTANGLE_Y_DIFF : 0) | + (g->w!=h->w ? RECTANGLE_W_DIFF : 0) | + (g->h!=h->h ? RECTANGLE_H_DIFF : 0)); +} diff --git a/ioncore/rectangle.h b/ioncore/rectangle.h new file mode 100644 index 0000000..da7b305 --- /dev/null +++ b/ioncore/rectangle.h @@ -0,0 +1,41 @@ +/* + * ion/ioncore/rectangle.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_RECTANGLE_H +#define ION_IONCORE_RECTANGLE_H + +#include +#include "common.h" +#include + +INTRSTRUCT(WRectangle); + +DECLSTRUCT(WRectangle){ + int x, y; + int w, h; +}; + +#define RECTANGLE_SAME 0x00 +#define RECTANGLE_X_DIFF 0x01 +#define RECTANGLE_Y_DIFF 0x02 +#define RECTANGLE_POS_DIFF (RECTANGLE_X_DIFF|RECTANGLE_Y_DIFF) +#define RECTANGLE_W_DIFF 0x04 +#define RECTANGLE_H_DIFF 0x08 +#define RECTANGLE_SZ_DIFF (RECTANGLE_W_DIFF|RECTANGLE_H_DIFF) + +extern int rectangle_compare(const WRectangle *g, const WRectangle *h); +extern bool rectangle_contains(const WRectangle *g, int x, int y); +extern void rectangle_constrain(WRectangle *g, const WRectangle *bounds); + +extern void rectange_debugprint(const WRectangle *g, const char *n); + +#endif /* ION_IONCORE_RECTANGLE_H */ + diff --git a/ioncore/regbind.c b/ioncore/regbind.c new file mode 100644 index 0000000..43bc3a6 --- /dev/null +++ b/ioncore/regbind.c @@ -0,0 +1,337 @@ +/* + * ion/regbind.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include "common.h" +#include "region.h" +#include "binding.h" +#include "regbind.h" + + +/*{{{ Grab/ungrab */ + + +static void do_binding_grab_on_ungrab_on(const WRegion *reg, + const WBinding *binding, + const WBindmap *bindmap, bool grab) +{ + Window win=region_xwindow(reg); + WRegBindingInfo *r; + + for(r=reg->bindings; r!=NULL; r=r->next){ + if(r->bindmap==bindmap) + continue; + if(bindmap_lookup_binding(r->bindmap, binding->act, binding->state, + binding->kcb)!=NULL) + break; + } + if(r==NULL && binding->area==0){ + if(grab) + binding_grab_on(binding, win); + else + binding_ungrab_on(binding, win); + } +} + + +static void do_binding_grab_on_ungrab_ons(const WRegion *reg, + const WBindmap *bindmap, + bool grab) +{ + Rb_node node=NULL; + WBinding *binding=NULL; + + if(!(reg->flags®ION_BINDINGS_ARE_GRABBED) || + bindmap->bindings==NULL){ + return; + } + + FOR_ALL_BINDINGS(binding, node, bindmap->bindings){ + do_binding_grab_on_ungrab_on(reg, binding, bindmap, grab); + } +} + + +static void grab_ungrabbed_bindings(const WRegion *reg, const WBindmap *bindmap) +{ + do_binding_grab_on_ungrab_ons(reg, bindmap, TRUE); +} + + +static void ungrab_freed_bindings(const WRegion *reg, const WBindmap *bindmap) +{ + do_binding_grab_on_ungrab_ons(reg, bindmap, FALSE); +} + + +void rbind_binding_added(const WRegBindingInfo *rbind, + const WBinding *binding, + const WBindmap *bindmap) +{ + if(binding->area==0 && rbind->reg->flags®ION_BINDINGS_ARE_GRABBED) + do_binding_grab_on_ungrab_on(rbind->reg, binding, rbind->bindmap, TRUE); +} + + +void rbind_binding_removed(const WRegBindingInfo *rbind, + const WBinding *binding, + const WBindmap *bindmap) +{ + if(binding->area==0 && rbind->reg->flags®ION_BINDINGS_ARE_GRABBED) + do_binding_grab_on_ungrab_on(rbind->reg, binding, rbind->bindmap, FALSE); +} + + +/*}}}*/ + + +/*{{{ Find */ + + +static WRegBindingInfo *find_rbind(WRegion *reg, WBindmap *bindmap, + WRegion *owner) +{ + WRegBindingInfo *rbind; + + for(rbind=(WRegBindingInfo*)reg->bindings; rbind!=NULL; rbind=rbind->next){ + if(rbind->bindmap==bindmap && rbind->owner==owner) + return rbind; + } + + return NULL; +} + + +/*}}}*/ + + +/*{{{ Interface */ + + +static WRegBindingInfo *region_do_add_bindmap_owned(WRegion *reg, + WBindmap *bindmap, + WRegion *owner, + bool first) +{ + WRegBindingInfo *rbind; + + if(bindmap==NULL) + return NULL; + + rbind=ALLOC(WRegBindingInfo); + + if(rbind==NULL) + return NULL; + + rbind->bindmap=bindmap; + rbind->owner=owner; + rbind->reg=reg; + rbind->tmp=0; + + LINK_ITEM(bindmap->rbind_list, rbind, bm_next, bm_prev); + + if(region_xwindow(reg)!=None && !(reg->flags®ION_GRAB_ON_PARENT)) + grab_ungrabbed_bindings(reg, bindmap); + + /* Link to reg's rbind list*/ { + WRegBindingInfo *b=reg->bindings; + if(first){ + LINK_ITEM_FIRST(b, rbind, next, prev); + }else{ + LINK_ITEM_LAST(b, rbind, next, prev); + } + reg->bindings=b; + } + + return rbind; +} + + +bool region_add_bindmap(WRegion *reg, WBindmap *bindmap) +{ + if(find_rbind(reg, bindmap, NULL)!=NULL) + return FALSE; + return (region_do_add_bindmap_owned(reg, bindmap, NULL, TRUE)!=NULL); +} + + +static void remove_rbind(WRegion *reg, WRegBindingInfo *rbind) +{ + UNLINK_ITEM(rbind->bindmap->rbind_list, rbind, bm_next, bm_prev); + + /* Unlink from reg's rbind list*/ { + WRegBindingInfo *b=reg->bindings; + UNLINK_ITEM(b, rbind, next, prev); + reg->bindings=b; + } + + if(region_xwindow(reg)!=None && !(reg->flags®ION_GRAB_ON_PARENT)) + ungrab_freed_bindings(reg, rbind->bindmap); + + free(rbind); +} + + +void region_remove_bindmap(WRegion *reg, WBindmap *bindmap) +{ + WRegBindingInfo *rbind=find_rbind(reg, bindmap, NULL); + if(rbind!=NULL) + remove_rbind(reg, rbind); +} + + +void region_remove_bindings(WRegion *reg) +{ + WRegBindingInfo *rbind; + + while((rbind=(WRegBindingInfo*)reg->bindings)!=NULL) + remove_rbind(reg, rbind); +} + + +WBinding *region_lookup_keybinding(WRegion *reg, const XKeyEvent *ev, + const WSubmapState *sc, + WRegion **binding_owner_ret) +{ + WRegBindingInfo *rbind=NULL; + WBinding *binding=NULL; + const WSubmapState *s=NULL; + WBindmap *bindmap=NULL; + int i; + + *binding_owner_ret=reg; + + for(rbind=(WRegBindingInfo*)reg->bindings; rbind!=NULL; rbind=rbind->next){ + bindmap=rbind->bindmap; + + for(s=sc; s!=NULL && bindmap!=NULL; s=s->next){ + binding=bindmap_lookup_binding(bindmap, BINDING_KEYPRESS, s->state, s->key); + + if(binding==NULL){ + bindmap=NULL; + break; + } + + bindmap=binding->submap; + } + + if(bindmap==NULL){ + /* There may be no next iteration so we must reset binding here + * because we have not found a proper binding. + */ + binding=NULL; + continue; + } + + binding=bindmap_lookup_binding(bindmap, BINDING_KEYPRESS, ev->state, ev->keycode); + + if(binding!=NULL) + break; + } + + if(binding!=NULL && rbind->owner!=NULL) + *binding_owner_ret=rbind->owner; + + return binding; +} + + +WBinding *region_lookup_binding(WRegion *reg, int act, uint state, + uint kcb, int area) +{ + WRegBindingInfo *rbind; + WBinding *binding=NULL; + + for(rbind=(WRegBindingInfo*)reg->bindings; rbind!=NULL; rbind=rbind->next){ + if(rbind->owner!=NULL) + continue; + binding=bindmap_lookup_binding_area(rbind->bindmap, act, state, kcb, area); + if(binding!=NULL) + break; + } + + return binding; +} + + +/*}}}*/ + + +/*{{{ Update */ + + +static void add_bindings(WRegion *reg, WRegion *r2) +{ + WRegion *rx=REGION_MANAGER(r2); + WRegBindingInfo *rbind, *rb2; + WBinding *binding=NULL; + + if(rx!=NULL && REGION_PARENT_REG(rx)==reg){ + /* The recursion is here to get the bindmaps correctly ordered. */ + add_bindings(reg, rx); + } + + if(r2->flags®ION_GRAB_ON_PARENT){ + for(rb2=(WRegBindingInfo*)r2->bindings; rb2!=NULL; rb2=rb2->next){ + rbind=find_rbind(reg, rb2->bindmap, r2); + if(rbind==NULL){ + rbind=region_do_add_bindmap_owned(reg, rb2->bindmap, + r2, TRUE); + } + if(rbind!=NULL) + rbind->tmp=1; + } + } +} + + +void region_do_update_owned_grabs(WRegion *reg) +{ + WRegBindingInfo *rbind, *rb2; + + reg->flags&=~REGION_BINDING_UPDATE_SCHEDULED; + + /* clear flags */ + for(rbind=(WRegBindingInfo*)reg->bindings; rbind!=NULL; rbind=rbind->next) + rbind->tmp=0; + + /* make new grabs */ + if(reg->active_sub!=NULL) + add_bindings(reg, reg->active_sub); + + /* remove old grabs */ + for(rbind=(WRegBindingInfo*)reg->bindings; rbind!=NULL; rbind=rb2){ + rb2=rbind->next; + if(rbind->tmp!=1 && rbind->owner!=NULL) + remove_rbind(reg, rbind); + } +} + +void region_update_owned_grabs(WRegion *reg) +{ + if(reg->flags®ION_BINDING_UPDATE_SCHEDULED + || OBJ_IS_BEING_DESTROYED(reg) + || ioncore_g.opmode==IONCORE_OPMODE_DEINIT){ + return; + } + + if(mainloop_defer_action((Obj*)reg, + (WDeferredAction*)region_do_update_owned_grabs)){ + reg->flags|=REGION_BINDING_UPDATE_SCHEDULED; + }else{ + region_do_update_owned_grabs(reg); + } +} + + +/*}}}*/ diff --git a/ioncore/regbind.h b/ioncore/regbind.h new file mode 100644 index 0000000..b7c0412 --- /dev/null +++ b/ioncore/regbind.h @@ -0,0 +1,49 @@ +/* + * ion/ioncore/regbind.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_REGBIND_H +#define ION_IONCORE_REGBIND_H + +#include "global.h" +#include "common.h" +#include "region.h" +#include "binding.h" + + +DECLSTRUCT(WSubmapState){ + uint key, state; + WSubmapState *next; +}; + + +extern bool region_add_bindmap(WRegion *reg, WBindmap *bindmap); +extern void region_remove_bindmap(WRegion *reg, WBindmap *bindmap); + +extern void region_remove_bindings(WRegion *reg); + +extern WBinding *region_lookup_keybinding(WRegion *reg, const XKeyEvent *ev, + const WSubmapState *sc, + WRegion **binding_owner_ret); +extern WBinding *region_lookup_binding(WRegion *reg, int act, uint state, + uint kcb, int area); + +extern void rbind_binding_added(const WRegBindingInfo *rbind, + const WBinding *binding, + const WBindmap *bindmap); +extern void rbind_binding_removed(const WRegBindingInfo *rbind, + const WBinding *binding, + const WBindmap *bindmap); + +extern void region_update_owned_grabs(WRegion *reg); +extern void region_do_update_owned_grabs(WRegion *reg); + +#endif /* ION_IONCORE_REGBIND_H */ + diff --git a/ioncore/reginfo.c b/ioncore/reginfo.c new file mode 100644 index 0000000..5606b09 --- /dev/null +++ b/ioncore/reginfo.c @@ -0,0 +1,92 @@ +/* + * ion/ioncore/reginfo.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include + +#include "common.h" +#include "region.h" +#include +#include "attach.h" +#include "reginfo.h" + + +static WRegClassInfo *reg_class_infos; + + +/*{{{ Registration */ + + +bool ioncore_register_regclass(ClassDescr *descr, WRegionLoadCreateFn *lc_fn) +{ + WRegClassInfo *info; + + if(descr==NULL) + return FALSE; + + info=ALLOC(WRegClassInfo); + if(info==NULL) + return FALSE; + + info->descr=descr; + info->lc_fn=lc_fn; + LINK_ITEM(reg_class_infos, info, next, prev); + + return TRUE; +} + + +void ioncore_unregister_regclass(ClassDescr *descr) +{ + WRegClassInfo *info; + + for(info=reg_class_infos; info!=NULL; info=info->next){ + if(descr==info->descr){ + UNLINK_ITEM(reg_class_infos, info, next, prev); + free(info); + return; + } + } +} + + +/*}}}*/ + + +/*{{{ Lookup */ + + +WRegClassInfo *ioncore_lookup_regclass(const char *name, bool inheriting_ok) +{ + WRegClassInfo *info; + ClassDescr *descr; + + if(name==NULL) + return NULL; + + for(info=reg_class_infos; info!=NULL; info=info->next){ + for(descr=info->descr; + descr!=NULL; + descr=(inheriting_ok ? descr->ancestor : NULL)){ + + if(strcmp(descr->name, name)==0){ + if(info->lc_fn!=NULL) + return info; + } + } + } + return NULL; +} + + +/*}}}*/ + + diff --git a/ioncore/reginfo.h b/ioncore/reginfo.h new file mode 100644 index 0000000..a83c2e2 --- /dev/null +++ b/ioncore/reginfo.h @@ -0,0 +1,43 @@ +/* + * ion/ioncore/reginfo.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_REGINFO_H +#define ION_IONCORE_REGINFO_H + +#include "common.h" +#include +#include "region.h" +#include "window.h" +#include +#include "rectangle.h" + +typedef WRegion *WRegionLoadCreateFn(WWindow *par, const WFitParams *fp, + ExtlTab tab); +typedef WRegion *WRegionSimpleCreateFn(WWindow *par, const WFitParams *fp); + +INTRSTRUCT(WRegClassInfo); + +DECLSTRUCT(WRegClassInfo){ + ClassDescr *descr; + WRegionLoadCreateFn *lc_fn; + WRegClassInfo *next, *prev; +}; + + +extern bool ioncore_register_regclass(ClassDescr *descr, + WRegionLoadCreateFn *lc_fn); +extern void ioncore_unregister_regclass(ClassDescr *descr); + +extern WRegClassInfo *ioncore_lookup_regclass(const char *name, + bool inheriting_ok); + +#endif /* ION_IONCORE_REGINFO_H */ + diff --git a/ioncore/region-iter.h b/ioncore/region-iter.h new file mode 100644 index 0000000..bf9f135 --- /dev/null +++ b/ioncore/region-iter.h @@ -0,0 +1,37 @@ +/* + * ion/ioncore/region-iter.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_REGION_ITER_H +#define ION_IONCORE_REGION_ITER_H + +#define REGION_FIRST_CHILD(PAR) (((WRegion*)(PAR))->children) +#define REGION_LAST_CHILD(PAR) \ + (REGION_FIRST_CHILD(PAR)==NULL ? NULL \ + : REGION_PREV_CHILD_WRAP(REGION_FIRST_CHILD(PAR), REGION_FIRST_CHILD(PAR))) +#define REGION_NEXT_CHILD(PAR, REG) (((WRegion*)(REG))->p_next) +#define REGION_PREV_CHILD(PAR, REG) ((((WRegion*)(REG))->p_prev->p_next) ? \ + (((WRegion*)(REG))->p_prev) : NULL) +#define REGION_NEXT_CHILD_WRAP(PAR, REG) \ + (((REG) && ((WRegion*)(REG))->p_next) \ + ? ((WRegion*)(REG))->p_next : REGION_FIRST_CHILD(PAR)) +#define REGION_PREV_CHILD_WRAP(PAR, REG) \ + ((REG) ? ((WRegion*)(REG))->p_prev \ + : REGION_FIRST_CHILD(PAR)) + +#define FOR_ALL_CHILDREN(PAR, REG) \ + for((REG)=((WRegion*)(PAR))->children; (REG)!=NULL; (REG)=(REG)->p_next) + +#define FOR_ALL_CHILDREN_W_NEXT(PAR, REG, NEXT) \ + for((REG)=((WRegion*)(PAR))->children, (NEXT)=((REG)==NULL ? NULL : (REG)->p_next);\ + (REG)!=NULL; \ + (REG)=(NEXT), (NEXT)=((REG)==NULL ? NULL : (REG)->p_next)) + +#endif /* ION_IONCORE_REGION_ITER_H */ diff --git a/ioncore/region.c b/ioncore/region.c new file mode 100644 index 0000000..dede094 --- /dev/null +++ b/ioncore/region.c @@ -0,0 +1,914 @@ +/* + * ion/ioncore/region.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include + +#include +#include +#include + +#include "common.h" +#include "global.h" +#include "region.h" +#include "focus.h" +#include "regbind.h" +#include "names.h" +#include "resize.h" +#include "manage.h" +#include "extlconv.h" +#include "activity.h" +#include "region-iter.h" + + +#define D2(X) + + +WHook *region_notify_hook=NULL; + +static void region_notify_change_(WRegion *reg, const char *how, + Obj *detail); + + +/*{{{ Init & deinit */ + + +void region_init(WRegion *reg, WWindow *par, const WFitParams *fp) +{ + if(fp->g.w<0 || fp->g.h<0) + warn(TR("Creating region with negative width or height!")); + + reg->geom=fp->g; + reg->flags=0; + reg->bindings=NULL; + reg->rootwin=NULL; + + reg->children=NULL; + reg->parent=NULL; + reg->p_next=NULL; + reg->p_prev=NULL; + + reg->active_sub=NULL; + reg->active_prev=NULL; + reg->active_next=NULL; + + reg->ni.name=NULL; + reg->ni.inst_off=0; + reg->ni.node=NULL; + + reg->manager=NULL; + + reg->submapstat=NULL; + + reg->mgd_activity=FALSE; + + if(par!=NULL){ + reg->rootwin=((WRegion*)par)->rootwin; + region_set_parent(reg, par); + }else{ + assert(OBJ_IS(reg, WRootWin));/* || OBJ_IS(reg, WScreen));*/ + } +} + + +static void destroy_children(WRegion *reg) +{ + WRegion *sub, *prev=NULL; + bool complained=FALSE; + + /* destroy children */ + while(1){ + sub=reg->children; + if(sub==NULL) + break; + assert(!OBJ_IS_BEING_DESTROYED(sub)); + assert(sub!=prev); + if(ioncore_g.opmode!=IONCORE_OPMODE_DEINIT && !complained && OBJ_IS(reg, WClientWin)){ + warn(TR("Destroying object \"%s\" with client windows as " + "children."), region_name(reg)); + complained=TRUE; + } + prev=sub; + destroy_obj((Obj*)sub); + } +} + + +void region_deinit(WRegion *reg) +{ + destroy_children(reg); + + if(ioncore_g.focus_next==reg){ + D(warn("Region to be focused next destroyed[1].")); + ioncore_g.focus_next=NULL; + } + + region_detach_manager(reg); + region_unset_parent(reg); + region_remove_bindings(reg); + + region_unregister(reg); + + region_focuslist_deinit(reg); + + if(ioncore_g.focus_next==reg){ + D(warn("Region to be focused next destroyed[2].")); + ioncore_g.focus_next=NULL; + } +} + + +/*}}}*/ + + +/*{{{ Dynfuns */ + + +bool region_fitrep(WRegion *reg, WWindow *par, const WFitParams *fp) +{ + bool ret=FALSE; + CALL_DYN_RET(ret, bool, region_fitrep, reg, (reg, par, fp)); + return ret; +} + + +void region_updategr(WRegion *reg) +{ + CALL_DYN(region_updategr, reg, (reg)); +} + + +void region_map(WRegion *reg) +{ + CALL_DYN(region_map, reg, (reg)); +} + + +void region_unmap(WRegion *reg) +{ + CALL_DYN(region_unmap, reg, (reg)); +} + + +void region_notify_rootpos(WRegion *reg, int x, int y) +{ + CALL_DYN(region_notify_rootpos, reg, (reg, x, y)); +} + + +Window region_xwindow(const WRegion *reg) +{ + Window ret=None; + CALL_DYN_RET(ret, Window, region_xwindow, reg, (reg)); + return ret; +} + + +void region_activated(WRegion *reg) +{ + CALL_DYN(region_activated, reg, (reg)); +} + + +void region_inactivated(WRegion *reg) +{ + CALL_DYN(region_inactivated, reg, (reg)); +} + + +void region_do_set_focus(WRegion *reg, bool warp) +{ + CALL_DYN(region_do_set_focus, reg, (reg, warp)); +} + + +/*{{{ Manager region dynfuns */ + + +void region_managed_activated(WRegion *mgr, WRegion *reg) +{ + CALL_DYN(region_managed_activated, mgr, (mgr, reg)); +} + + +void region_managed_inactivated(WRegion *mgr, WRegion *reg) +{ + CALL_DYN(region_managed_inactivated, mgr, (mgr, reg)); +} + + +static bool region_managed_prepare_focus_default(WRegion *mgr, WRegion *reg, + int flags, + WPrepareFocusResult *res) +{ + if(!region_prepare_focus(mgr, flags, res)) + return FALSE; + + res->reg=reg; + res->flags=flags; + return TRUE; +} + + +bool region_managed_prepare_focus(WRegion *mgr, WRegion *reg, + int flags, + WPrepareFocusResult *res) +{ + bool ret=TRUE; + CALL_DYN_RET(ret, bool, region_managed_prepare_focus, mgr, + (mgr, reg, flags, res)); + return ret; +} + + +void region_managed_notify(WRegion *mgr, WRegion *reg, const char *how) +{ + CALL_DYN(region_managed_notify, mgr, (mgr, reg, how)); +} + + +void region_managed_remove(WRegion *mgr, WRegion *reg) +{ + CALL_DYN(region_managed_remove, mgr, (mgr, reg)); +} + + +/*EXTL_DOC + * Return the object, if any, that is considered ''currently active'' + * within the objects managed by \var{mplex}. + */ +EXTL_SAFE +EXTL_EXPORT_MEMBER +WRegion *region_current(WRegion *mgr) +{ + WRegion *ret=NULL; + CALL_DYN_RET(ret, WRegion*, region_current, mgr, (mgr)); + return ret; +} + + +void region_child_removed(WRegion *reg, WRegion *sub) +{ + CALL_DYN(region_child_removed, reg, (reg, sub)); +} + + +/*}}}*/ + + +/*{{{ Dynfun defaults */ + + +void region_updategr_default(WRegion *reg) +{ + WRegion *sub=NULL; + + FOR_ALL_CHILDREN(reg, sub){ + region_updategr(sub); + } +} + + +/*}}}*/ + + +/*}}}*/ + + +/*{{{ Goto */ + + +bool region_prepare_focus(WRegion *reg, int flags, + WPrepareFocusResult *res) +{ + WRegion *mgr=REGION_MANAGER(reg); + WRegion *par=REGION_PARENT_REG(reg); + + if(REGION_IS_MAPPED(reg) && region_may_control_focus(reg)){ + res->reg=reg; + res->flags=0; + return TRUE; + }else{ + if(mgr!=NULL){ + return region_managed_prepare_focus(mgr, reg, flags, res); + }else if(par!=NULL){ + if(!region_prepare_focus(par, flags, res)) + return FALSE; + /* Just focus reg, if it has no manager, and parent can be + * focused. + */ + } + res->reg=reg; + res->flags=flags; + return TRUE; + } +} + + +bool region_goto_flags(WRegion *reg, int flags) +{ + WPrepareFocusResult res; + bool ret; + + ret=region_prepare_focus(reg, flags, &res); + + if(res.reg!=NULL){ + if(res.flags®ION_GOTO_FOCUS) + region_maybewarp(res.reg, !(res.flags®ION_GOTO_NOWARP)); + } + + return ret; +} + + +/*EXTL_DOC + * Attempt to display \var{reg}, save region activity status and then + * warp to (or simply set focus to if warping is disabled) \var{reg}. + * + * Note that this function is asynchronous; the region will not + * actually have received the focus when this function returns. + */ +EXTL_EXPORT_MEMBER +bool region_goto(WRegion *reg) +{ + return region_goto_flags(reg, REGION_GOTO_FOCUS); +} + + +/*}}}*/ + + +/*{{{ Fit/reparent */ + + +void region_fit(WRegion *reg, const WRectangle *geom, WRegionFitMode mode) +{ + WFitParams fp; + fp.g=*geom; + fp.mode=mode&~REGION_FIT_GRAVITY; + fp.gravity=ForgetGravity; + region_fitrep(reg, NULL, &fp); +} + + +bool region_reparent(WRegion *reg, WWindow *par, + const WRectangle *geom, WRegionFitMode mode) +{ + WFitParams fp; + fp.g=*geom; + fp.mode=mode; + return region_fitrep(reg, par, &fp); +} + + +/*}}}*/ + + +/*{{{ Close */ + + +static bool region_rqclose_default(WRegion *reg, bool relocate) +{ + WPHolder *ph; + bool refuse=TRUE; + + if((!relocate && !region_may_destroy(reg)) || + !region_manager_allows_destroying(reg)){ + return FALSE; + } + + ph=region_get_rescue_pholder(reg); + + if(ph!=NULL){ + refuse=!region_rescue_clientwins(reg, ph); + destroy_obj((Obj*)ph); + } + + if(refuse){ + warn(TR("Failed to rescue some client windows - not closing.")); + return FALSE; + } + + mainloop_defer_destroy((Obj*)reg); + + return TRUE; +} + + +/*EXTL_DOC + * Attempt to close/destroy \var{reg}. Whether this operation works + * depends on whether the particular type of region in question has + * implemented the feature and, in case of client windows, whether + * the client supports the \code{WM_DELETE} protocol (see also + * \fnref{WClientWin.kill}). If the operation is likely to succeed, + * \code{true} is returned, otherwise \code{false}. In most cases the + * region will not have been actually destroyed when this function returns. + * If \var{relocate} is not set, and \var{reg} manages other regions, it + * will not be closed. Otherwise the managed regions will be attempted + * to be relocated. + */ +EXTL_EXPORT_MEMBER +bool region_rqclose(WRegion *reg, bool relocate) +{ + bool ret=FALSE; + CALL_DYN_RET(ret, bool, region_rqclose, reg, (reg, relocate)); + return ret; +} + + +static WRegion *region_rqclose_propagate_default(WRegion *reg, + WRegion *maybe_sub) +{ + if(maybe_sub==NULL) + maybe_sub=region_current(reg); + if(maybe_sub!=NULL) + return region_rqclose_propagate(maybe_sub, NULL); + return (region_rqclose(reg, FALSE) ? reg : NULL); +} + + +/*EXTL_DOC + * Recursively attempt to close a region or one of the regions managed by + * it. If \var{sub} is set, it will be used as the managed region, otherwise + * \fnref{WRegion.current}\code{(reg)}. The object to be closed is + * returned or NULL if nothing can be closed. Also see notes for + * \fnref{WRegion.rqclose}. + */ +EXTL_EXPORT_MEMBER +WRegion *region_rqclose_propagate(WRegion *reg, WRegion *maybe_sub) +{ + WRegion *ret=NULL; + CALL_DYN_RET(ret, WRegion*, region_rqclose_propagate, reg, + (reg, maybe_sub)); + return ret; +} + + +bool region_may_destroy(WRegion *reg) +{ + bool ret=TRUE; + CALL_DYN_RET(ret, bool, region_may_destroy, reg, (reg)); + return ret; +} + + +bool region_managed_may_destroy(WRegion *mgr, WRegion *reg) +{ + bool ret=TRUE; + CALL_DYN_RET(ret, bool, region_managed_may_destroy, mgr, (mgr, reg)); + return ret; +} + + +bool region_manager_allows_destroying(WRegion *reg) +{ + WRegion *mgr=REGION_MANAGER(reg); + + if(mgr==NULL) + return TRUE; + + return region_managed_may_destroy(mgr, reg); +} + + +/*}}}*/ + + +/*{{{ Manager/parent stuff */ + + +/* Routine to call to unmanage a region */ +void region_detach_manager(WRegion *reg) +{ + WRegion *mgr; + + mgr=REGION_MANAGER(reg); + + if(mgr==NULL) + return; + + /* Restore activity state to non-parent manager */ + if(region_may_control_focus(reg)){ + WRegion *par=REGION_PARENT_REG(reg); + if(par!=NULL && mgr!=par && REGION_PARENT_REG(mgr)==par){ + /* REGION_ACTIVE shouldn't be set for windowless regions + * but make the parent's active_sub point to it + * nevertheless so that region_may_control_focus can + * be made to work. + */ + par->active_sub=mgr; + /*if(region_xwindow(mgr)!=None){*/ + region_do_set_focus(mgr, FALSE); + /*}*/ + } + } + + region_set_activity(reg, SETPARAM_UNSET); + + region_managed_remove(mgr, reg); + + assert(REGION_MANAGER(reg)==NULL); +} + + +/* This should only be called within region_managed_remove, + * _after_ any managed lists and other essential structures + * of mgr have been broken. + */ +void region_unset_manager(WRegion *reg, WRegion *mgr) +{ + if(reg->manager!=mgr) + return; + + reg->manager=NULL; + + if(region_is_activity_r(reg)) + region_clear_mgd_activity(mgr); + + region_notify_change_(reg, "unset_manager", (Obj*)mgr); +} + + +/* This should be called within region attach routines, + * _after_ any managed lists and other essential structures + * of mgr have been set up. + */ +void region_set_manager(WRegion *reg, WRegion *mgr) +{ + assert(reg->manager==NULL); + + reg->manager=mgr; + + if(region_is_activity_r(reg)) + region_mark_mgd_activity(mgr); + + region_notify_change_(reg, "set_manager", (Obj*)mgr); +} + + +void region_set_parent(WRegion *reg, WWindow *parent) +{ + assert(reg->parent==NULL && parent!=NULL); + LINK_ITEM(((WRegion*)parent)->children, reg, p_next, p_prev); + reg->parent=parent; +} + + +void region_unset_parent(WRegion *reg) +{ + WRegion *p=REGION_PARENT_REG(reg); + + if(p==NULL || p==reg) + return; + + UNLINK_ITEM(p->children, reg, p_next, p_prev); + reg->parent=NULL; + + if(p->active_sub==reg){ + p->active_sub=NULL; + region_update_owned_grabs(p); + } + + region_child_removed(p, reg); +} + + +/*EXTL_DOC + * Returns the region that manages \var{reg}. + */ +EXTL_SAFE +EXTL_EXPORT_MEMBER +WRegion *region_manager(WRegion *reg) +{ + return reg->manager; +} + + +/*EXTL_DOC + * Returns the parent region of \var{reg}. + */ +EXTL_SAFE +EXTL_EXPORT_MEMBER +WWindow *region_parent(WRegion *reg) +{ + return reg->parent; +} + + +WRegion *region_manager_or_parent(WRegion *reg) +{ + if(reg->manager!=NULL) + return reg->manager; + else + return (WRegion*)(reg->parent); +} + + +WRegion *region_get_manager_chk(WRegion *p, const ClassDescr *descr) +{ + WRegion *mgr=NULL; + + if(p!=NULL){ + mgr=REGION_MANAGER(p); + if(obj_is((Obj*)mgr, descr)) + return mgr; + } + + return NULL; +} + +/*}}}*/ + + +/*{{{ Stacking and ordering */ + + +static void region_stacking_default(WRegion *reg, + Window *bottomret, Window *topret) +{ + Window win=region_xwindow(reg); + *bottomret=win; + *topret=win; +} + + +void region_stacking(WRegion *reg, Window *bottomret, Window *topret) +{ + CALL_DYN(region_stacking, reg, (reg, bottomret, topret)); +} + + +void region_restack(WRegion *reg, Window other, int mode) +{ + CALL_DYN(region_restack, reg, (reg, other, mode)); +} + + + +bool region_managed_rqorder(WRegion *reg, WRegion *sub, WRegionOrder order) +{ + bool ret=FALSE; + CALL_DYN_RET(ret, bool, region_managed_rqorder, reg, (reg, sub, order)); + return ret; +} + + +bool region_rqorder(WRegion *reg, WRegionOrder order) +{ + WRegion *mgr=REGION_MANAGER(reg); + + if(mgr==NULL) + return FALSE; + else + return region_managed_rqorder(mgr, reg, order); +} + + +/*EXTL_DOC + * Request ordering. Currently supported values for \var{ord} + * are 'front' and 'back'. + */ +EXTL_EXPORT_AS(WRegion, rqorder) +bool region_rqorder_extl(WRegion *reg, const char *ord) +{ + WRegionOrder order; + + if(strcmp(ord, "front")==0){ + order=REGION_ORDER_FRONT; + }else if(strcmp(ord, "back")==0){ + order=REGION_ORDER_BACK; + }else{ + return FALSE; + } + + return region_rqorder(reg, order); +} + + +/*}}}*/ + + +/*{{{ Misc. */ + + +/*EXTL_DOC + * Returns the root window \var{reg} is on. + */ +EXTL_SAFE +EXTL_EXPORT_MEMBER +WRootWin *region_rootwin_of(const WRegion *reg) +{ + WRootWin *rw; + assert(reg!=NULL); /* Lua interface should not pass NULL reg. */ + rw=(WRootWin*)(reg->rootwin); + assert(rw!=NULL); + return rw; +} + + +/*EXTL_DOC + * Returns the screen \var{reg} is on. + */ +EXTL_SAFE +EXTL_EXPORT_MEMBER +WScreen *region_screen_of(WRegion *reg) +{ + while(reg!=NULL){ + if(OBJ_IS(reg, WScreen)) + return (WScreen*)reg; + reg=REGION_PARENT_REG(reg); + } + return NULL; +} + + +Window region_root_of(const WRegion *reg) +{ + return WROOTWIN_ROOT(region_rootwin_of(reg)); +} + + +bool region_same_rootwin(const WRegion *reg1, const WRegion *reg2) +{ + return (reg1->rootwin==reg2->rootwin); +} + + +/*EXTL_DOC + * Is \var{reg} visible/is it and all it's ancestors mapped? + */ +EXTL_SAFE +EXTL_EXPORT_AS(WRegion, is_mapped) +bool region_is_fully_mapped(WRegion *reg) +{ + for(; reg!=NULL; reg=REGION_PARENT_REG(reg)){ + if(!REGION_IS_MAPPED(reg)) + return FALSE; + } + + return TRUE; +} + + +void region_rootpos(WRegion *reg, int *xret, int *yret) +{ + WRegion *par; + + par=REGION_PARENT_REG(reg); + + if(par==NULL || par==reg){ + *xret=0; + *yret=0; + return; + } + + region_rootpos(par, xret, yret); + + *xret+=REGION_GEOM(reg).x; + *yret+=REGION_GEOM(reg).y; +} + + +static bool mrsh_not(WHookDummy *fn, void *p) +{ + WRegion *reg=(WRegion*)((void**)p)[0]; + const char *how=(const char*)((void**)p)[1]; + Obj *detail=(Obj*)((void**)p)[2]; + + fn(reg, how, detail); + + return TRUE; +} + + +static bool mrshe_not(ExtlFn fn, void *p) +{ + WRegion *reg=(WRegion*)((void**)p)[0]; + const char *how=(const char*)((void**)p)[1]; + Obj *detail=(Obj*)((void**)p)[2]; + + extl_call(fn, "oso", NULL, reg, how, detail); + + return TRUE; +} + + +static void region_notify_change_(WRegion *reg, const char *how, + Obj *detail) +{ + const void *p[3]; + + p[0]=reg; + p[1]=how; + p[2]=detail; + + extl_protect(NULL); + hook_call(region_notify_hook, p, mrsh_not, mrshe_not), + extl_unprotect(NULL); + +} + + +void region_notify_change(WRegion *reg, const char *how) +{ + WRegion *mgr=REGION_MANAGER(reg); + + if(mgr!=NULL) + region_managed_notify(mgr, reg, how); + + region_notify_change_(reg, how, NULL); +} + + +/*EXTL_DOC + * Returns the geometry of \var{reg} within its parent; a table with fields + * \var{x}, \var{y}, \var{w} and \var{h}. + */ +EXTL_SAFE +EXTL_EXPORT_MEMBER +ExtlTab region_geom(WRegion *reg) +{ + return extl_table_from_rectangle(®ION_GEOM(reg)); +} + + +bool region_handle_drop(WRegion *reg, int x, int y, WRegion *dropped) +{ + bool ret=FALSE; + CALL_DYN_RET(ret, bool, region_handle_drop, reg, (reg, x, y, dropped)); + return ret; +} + + +WRegion *region_managed_within(WRegion *reg, WRegion *mgd) +{ + while(mgd!=NULL && + (REGION_PARENT_REG(mgd)==reg || + REGION_PARENT_REG(mgd)==REGION_PARENT_REG(reg))){ + + if(REGION_MANAGER(mgd)==reg) + return mgd; + mgd=REGION_MANAGER(mgd); + } + + return NULL; +} + + +/*}}}*/ + + +/*{{{ Dynamic function table and class implementation */ + + +static DynFunTab region_dynfuntab[]={ + {region_managed_rqgeom, + region_managed_rqgeom_allow}, + + {region_managed_rqgeom_absolute, + region_managed_rqgeom_absolute_default}, + + {region_updategr, + region_updategr_default}, + + {(DynFun*)region_rescue_clientwins, + (DynFun*)region_rescue_child_clientwins}, + + {(DynFun*)region_prepare_manage, + (DynFun*)region_prepare_manage_default}, + + {(DynFun*)region_prepare_manage_transient, + (DynFun*)region_prepare_manage_transient_default}, + + {(DynFun*)region_managed_prepare_focus, + (DynFun*)region_managed_prepare_focus_default}, + + {(DynFun*)region_rqclose_propagate, + (DynFun*)region_rqclose_propagate_default}, + + {(DynFun*)region_rqclose, + (DynFun*)region_rqclose_default}, + + {(DynFun*)region_displayname, + (DynFun*)region_name}, + + {region_stacking, + region_stacking_default}, + + END_DYNFUNTAB +}; + + +EXTL_EXPORT +IMPLCLASS(WRegion, Obj, region_deinit, region_dynfuntab); + + +/*}}}*/ + diff --git a/ioncore/region.h b/ioncore/region.h new file mode 100644 index 0000000..b2feff7 --- /dev/null +++ b/ioncore/region.h @@ -0,0 +1,193 @@ +/* + * ion/ioncore/region.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_REGION_H +#define ION_IONCORE_REGION_H + +#include +#include +#include "common.h" +#include "rectangle.h" + +#define REGION_MAPPED 0x0001 +#define REGION_ACTIVE 0x0002 +#define REGION_HAS_GRABS 0x0004 +#define REGION_TAGGED 0x0008 +#define REGION_BINDINGS_ARE_GRABBED 0x0020 +#define REGION_GRAB_ON_PARENT 0x0040 +#define REGION_ACTIVITY 0x0100 +#define REGION_SKIP_FOCUS 0x0200 +#define REGION_CWINS_BEING_RESCUED 0x0400 +#define REGION_PLEASE_WARP 0x0800 +#define REGION_BINDING_UPDATE_SCHEDULED 0x1000 + +#define REGION_GOTO_FOCUS 0x0001 +#define REGION_GOTO_NOWARP 0x0002 +#define REGION_GOTO_ENTERWINDOW 0x0004 + +/* Use region_is_fully_mapped instead for most cases. */ +#define REGION_IS_MAPPED(R) (((WRegion*)(R))->flags®ION_MAPPED) +#define REGION_MARK_MAPPED(R) (((WRegion*)(R))->flags|=REGION_MAPPED) +#define REGION_MARK_UNMAPPED(R) (((WRegion*)(R))->flags&=~REGION_MAPPED) +#define REGION_IS_ACTIVE(R) (((WRegion*)(R))->flags®ION_ACTIVE) +#define REGION_IS_TAGGED(R) (((WRegion*)(R))->flags®ION_TAGGED) +#define REGION_IS_URGENT(R) (((WRegion*)(R))->flags®ION_URGENT) +#define REGION_GEOM(R) (((WRegion*)(R))->geom) +#define REGION_ACTIVE_SUB(R) (((WRegion*)(R))->active_sub) + +#define REGION_MANAGER(R) (((WRegion*)(R))->manager) +#define REGION_MANAGER_CHK(R, TYPE) OBJ_CAST(REGION_MANAGER(R), TYPE) + +#define REGION_PARENT(REG) (((WRegion*)(REG))->parent) +#define REGION_PARENT_REG(REG) ((WRegion*)REGION_PARENT(REG)) + +#define REGION_FIT_BOUNDS 0x0001 /* Geometry is maximum bounds */ +#define REGION_FIT_ROTATE 0x0002 /* for Xrandr */ +#define REGION_FIT_WHATEVER 0x0004 /* for attach routines; g is not final */ +#define REGION_FIT_GRAVITY 0x0008 /* just a hint; for use with BOUNDS */ +#define REGION_FIT_EXACT 0x0000 /* No flags; exact fit */ + + +typedef int WRegionFitMode; + + +typedef enum{ + REGION_ORDER_FRONT, + REGION_ORDER_BACK +} WRegionOrder; + + +INTRSTRUCT(WFitParams); +DECLSTRUCT(WFitParams){ + WRectangle g; + WRegionFitMode mode; + int gravity; + int rotation; +}; + + +INTRSTRUCT(WRegionNameInfo); +DECLSTRUCT(WRegionNameInfo){ + char *name; + int inst_off; + void *node; +}; + + +INTRSTRUCT(WPrepareFocusResult); +DECLSTRUCT(WPrepareFocusResult){ + WRegion *reg; + int flags; +}; + + +DECLCLASS(WRegion){ + Obj obj; + + WRectangle geom; + void *rootwin; + bool flags; + + WWindow *parent; + WRegion *children; + WRegion *p_next, *p_prev; + + void *bindings; + WSubmapState *submapstat; + + WRegion *active_sub; + WRegion *active_prev, *active_next; + + WRegionNameInfo ni; + + WRegion *manager; + + int mgd_activity; +}; + + + + +extern void region_init(WRegion *reg, WWindow *par, const WFitParams *fp); +extern void region_deinit(WRegion *reg); + +DYNFUN bool region_fitrep(WRegion *reg, WWindow *par, const WFitParams *fp); +DYNFUN void region_map(WRegion *reg); +DYNFUN void region_unmap(WRegion *reg); +DYNFUN Window region_xwindow(const WRegion *reg); +DYNFUN void region_activated(WRegion *reg); +DYNFUN void region_inactivated(WRegion *reg); +DYNFUN void region_updategr(WRegion *reg); +DYNFUN bool region_rqclose(WRegion *reg, bool relocate); +DYNFUN WRegion *region_rqclose_propagate(WRegion *reg, WRegion *maybe_sub); +DYNFUN WRegion *region_current(WRegion *mgr); +DYNFUN void region_notify_rootpos(WRegion *reg, int x, int y); +DYNFUN bool region_may_destroy(WRegion *reg); +DYNFUN WRegion *region_managed_control_focus(WRegion *mgr, WRegion *reg); +DYNFUN void region_managed_remove(WRegion *reg, WRegion *sub); +DYNFUN bool region_managed_prepare_focus(WRegion *reg, WRegion *sub, + int flags, WPrepareFocusResult *res); +DYNFUN void region_managed_activated(WRegion *reg, WRegion *sub); +DYNFUN void region_managed_inactivated(WRegion *reg, WRegion *sub); +DYNFUN void region_managed_notify(WRegion *reg, WRegion *sub, const char *how); +DYNFUN bool region_managed_may_destroy(WRegion *mgr, WRegion *reg); +DYNFUN bool region_managed_rqorder(WRegion *reg, WRegion *sub, + WRegionOrder order); + +DYNFUN void region_child_removed(WRegion *reg, WRegion *sub); + +DYNFUN void region_restack(WRegion *reg, Window other, int mode); +DYNFUN void region_stacking(WRegion *reg, Window *bottomret, Window *topret); + +DYNFUN bool region_handle_drop(WRegion *reg, int x, int y, WRegion *dropped); + +extern bool region_rqorder(WRegion *reg, WRegionOrder order); + +extern bool region_prepare_focus(WRegion *reg, int flags, + WPrepareFocusResult *res); + +extern void region_fit(WRegion *reg, const WRectangle *geom, + WRegionFitMode mode); +extern bool region_reparent(WRegion *reg, WWindow *target, + const WRectangle *geom, WRegionFitMode mode); + +extern void region_updategr_default(WRegion *reg); + +extern void region_rootpos(WRegion *reg, int *xret, int *yret); +extern void region_notify_change(WRegion *reg, const char *how); + +extern bool region_goto(WRegion *reg); +extern bool region_goto_flags(WRegion *reg, int flags); + +extern bool region_is_fully_mapped(WRegion *reg); + +extern void region_detach_manager(WRegion *reg); + +extern WWindow *region_parent(WRegion *reg); +extern WRegion *region_manager(WRegion *reg); +extern WRegion *region_manager_or_parent(WRegion *reg); +extern void region_set_parent(WRegion *reg, WWindow *par); +extern void region_set_manager(WRegion *reg, WRegion *mgr); +extern void region_unset_manager(WRegion *reg, WRegion *mgr); +extern void region_unset_parent(WRegion *reg); + +extern WRootWin *region_rootwin_of(const WRegion *reg); +extern Window region_root_of(const WRegion *reg); +extern WScreen *region_screen_of(WRegion *reg); +extern bool region_same_rootwin(const WRegion *reg1, const WRegion *reg2); + +extern bool region_manager_allows_destroying(WRegion *reg); + +extern WRegion *region_managed_within(WRegion *reg, WRegion *mgd); + +extern WHook *region_notify_hook; + +#endif /* ION_IONCORE_REGION_H */ diff --git a/ioncore/resize.c b/ioncore/resize.c new file mode 100644 index 0000000..a8cff5a --- /dev/null +++ b/ioncore/resize.c @@ -0,0 +1,853 @@ +/* + * ion/ioncore/resize.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include + +#include +#include +#include +#include + +#include "common.h" +#include "global.h" +#include "resize.h" +#include "gr.h" +#include "sizehint.h" +#include "event.h" +#include "cursor.h" +#include "extlconv.h" +#include "grab.h" +#include "framep.h" +#include "infowin.h" + + +#define XOR_RESIZE (!ioncore_g.opaque_resize) + + +/*{{{ Size/position display and rubberband */ + + +static void draw_rubberbox(WRootWin *rw, const WRectangle *rect) +{ + XPoint fpts[5]; + + fpts[0].x=rect->x; + fpts[0].y=rect->y; + fpts[1].x=rect->x+rect->w; + fpts[1].y=rect->y; + fpts[2].x=rect->x+rect->w; + fpts[2].y=rect->y+rect->h; + fpts[3].x=rect->x; + fpts[3].y=rect->y+rect->h; + fpts[4].x=rect->x; + fpts[4].y=rect->y; + + XDrawLines(ioncore_g.dpy, WROOTWIN_ROOT(rw), rw->xor_gc, fpts, 5, + CoordModeOrigin); +} + + +static int max_width(GrBrush *brush, const char *str) +{ + int maxw=0, w; + + while(str && *str!='\0'){ + w=grbrush_get_text_width(brush, str, 1); + if(w>maxw) + maxw=w; + str++; + } + + return maxw; +} + + +static int chars_for_num(int d) +{ + int n=0; + + do{ + n++; + d/=10; + }while(d); + + return n; +} + + +static WInfoWin *setup_moveres_display(WWindow *parent, int cx, int cy) +{ + GrBorderWidths bdw; + GrFontExtents fnte; + WInfoWin *infowin; + WFitParams fp; + + fp.mode=REGION_FIT_EXACT; + fp.g.x=0; + fp.g.y=0; + fp.g.w=1; + fp.g.h=1; + + infowin=create_infowin(parent, &fp, "moveres_display"); + + if(infowin==NULL) + return NULL; + + grbrush_get_border_widths(INFOWIN_BRUSH(infowin), &bdw); + grbrush_get_font_extents(INFOWIN_BRUSH(infowin), &fnte); + + /* Create move/resize position/size display window */ + fp.g.w=3; + fp.g.w+=chars_for_num(REGION_GEOM(parent).w); + fp.g.w+=chars_for_num(REGION_GEOM(parent).h); + fp.g.w*=max_width(INFOWIN_BRUSH(infowin), "0123456789x+"); + fp.g.w+=bdw.left+bdw.right; + fp.g.h=fnte.max_height+bdw.top+bdw.bottom;; + + fp.g.x=cx-fp.g.w/2; + fp.g.y=cy-fp.g.h/2; + + region_fitrep((WRegion*)infowin, NULL, &fp); + region_map((WRegion*)infowin); + + return infowin; +} + + +static void moveres_draw_infowin(WMoveresMode *mode) +{ + WRectangle geom; + char *buf; + + if(mode->infowin==NULL) + return; + + buf=INFOWIN_BUFFER(mode->infowin); + + if(buf==NULL) + return; + + if(mode->mode==MOVERES_SIZE){ + int w, h; + + w=mode->geom.w; + h=mode->geom.h; + + if((mode->hints.inc_set) && + (mode->hints.width_inc>1 || mode->hints.height_inc>1)){ + if(mode->hints.base_set){ + w-=mode->hints.base_width; + h-=mode->hints.base_height; + } + w/=mode->hints.width_inc; + h/=mode->hints.height_inc; + } + + snprintf(buf, INFOWIN_BUFFER_LEN, "%dx%d", w, h); + }else{ + snprintf(buf, INFOWIN_BUFFER_LEN, "%+d %+d", + mode->geom.x, mode->geom.y); + } + + window_draw((WWindow*)mode->infowin, TRUE); +} + + +static void moveres_draw_rubberband(WMoveresMode *mode) +{ + WRectangle rgeom=mode->geom; + int rx, ry; + WRootWin *rootwin=(mode->reg==NULL + ? NULL + : region_rootwin_of(mode->reg)); + + if(rootwin==NULL) + return; + + rgeom.x+=mode->parent_rx; + rgeom.y+=mode->parent_ry; + + if(mode->rubfn==NULL) + draw_rubberbox(rootwin, &rgeom); + else + mode->rubfn(rootwin, &rgeom); +} + + +/*}}}*/ + + +/*{{{ Move/resize mode */ + + +WMoveresMode *tmpmode=NULL; + + +EXTL_EXPORT +IMPLCLASS(WMoveresMode, Obj, NULL, NULL); + + +WMoveresMode *moveres_mode(WRegion *reg) +{ + if(tmpmode==NULL) + return NULL; + return ((reg==NULL || tmpmode->reg==reg) ? tmpmode : NULL); +} + + +WRegion *moveresmode_target(WMoveresMode *mode) +{ + return mode->reg; +} + + +static bool moveresmode_init(WMoveresMode *mode, WRegion *reg, + WDrawRubberbandFn *rubfn, bool cumulative) +{ + WWindow *parent; + WRegion *mgr; + + if(tmpmode!=NULL) + return FALSE; + + parent=REGION_PARENT(reg); + + if(parent==NULL) + return FALSE; + + tmpmode=mode; + + mode->snap_enabled=FALSE; + region_size_hints(reg, &mode->hints); + + region_rootpos((WRegion*)parent, &mode->parent_rx, &mode->parent_ry); + + mode->geom=REGION_GEOM(reg); + mode->origgeom=REGION_GEOM(reg); + mode->dx1=0; + mode->dx2=0; + mode->dy1=0; + mode->dy2=0; + mode->rubfn=rubfn; + mode->resize_cumulative=cumulative; + mode->rqflags=(XOR_RESIZE ? REGION_RQGEOM_TRYONLY : 0); + mode->reg=reg; + mode->mode=MOVERES_SIZE; + + /* Get snapping geometry */ + mgr=REGION_MANAGER(reg); + + if(mgr!=NULL){ + mode->snapgeom=REGION_GEOM(mgr); + + if(mgr==(WRegion*)parent){ + mode->snapgeom.x=0; + mode->snapgeom.y=0; + mode->snap_enabled=FALSE; + }else if(REGION_PARENT(mgr)==parent){ + mode->snap_enabled=TRUE; + } + } + + if(!mode->hints.min_set || mode->hints.min_width<1) + mode->hints.min_width=1; + if(!mode->hints.min_set || mode->hints.min_height<1) + mode->hints.min_height=1; + + /* Set up info window */ + { + int x=mode->geom.x+mode->geom.w/2; + int y=mode->geom.y+mode->geom.h/2; + mode->infowin=setup_moveres_display(parent, x, y); + } + + moveres_draw_infowin(mode); + + if(XOR_RESIZE){ + XGrabServer(ioncore_g.dpy); + moveres_draw_rubberband(mode); + } + + return TRUE; +} + + +static WMoveresMode *create_moveresmode(WRegion *reg, + WDrawRubberbandFn *rubfn, + bool cumulative) +{ + CREATEOBJ_IMPL(WMoveresMode, moveresmode, (p, reg, rubfn, cumulative)); +} + + +WMoveresMode *region_begin_resize(WRegion *reg, WDrawRubberbandFn *rubfn, + bool cumulative) +{ + WMoveresMode *mode=create_moveresmode(reg, rubfn, cumulative); + + if(mode!=NULL){ + mode->mode=MOVERES_SIZE; + ioncore_change_grab_cursor(IONCORE_CURSOR_RESIZE); + } + + return mode; +} + + +WMoveresMode *region_begin_move(WRegion *reg, WDrawRubberbandFn *rubfn, + bool cumulative) +{ + WMoveresMode *mode=create_moveresmode(reg, rubfn, cumulative); + + if(mode!=NULL){ + mode->mode=MOVERES_POS; + ioncore_change_grab_cursor(IONCORE_CURSOR_MOVE); + } + + return mode; +} + + +static void moveresmode_delta(WMoveresMode *mode, + int dx1, int dx2, int dy1, int dy2, + WRectangle *rret) +{ + int realdx1, realdx2, realdy1, realdy2; + WRQGeomParams rq=RQGEOMPARAMS_INIT; + int w=0, h=0; + + realdx1=(mode->dx1+=dx1); + realdx2=(mode->dx2+=dx2); + realdy1=(mode->dy1+=dy1); + realdy2=(mode->dy2+=dy2); + rq.geom=mode->origgeom; + + /* snap */ + if(mode->snap_enabled){ + WRectangle *sg=&mode->snapgeom; + int er=CF_EDGE_RESISTANCE; + + if(mode->dx1!=0 && rq.geom.x+mode->dx1x && rq.geom.x+mode->dx1>sg->x-er) + realdx1=sg->x-rq.geom.x; + if(mode->dx2!=0 && rq.geom.x+rq.geom.w+mode->dx2>sg->x+sg->w && rq.geom.x+rq.geom.w+mode->dx2x+sg->w+er) + realdx2=sg->x+sg->w-rq.geom.x-rq.geom.w; + if(mode->dy1!=0 && rq.geom.y+mode->dy1y && rq.geom.y+mode->dy1>sg->y-er) + realdy1=sg->y-rq.geom.y; + if(mode->dy2!=0 && rq.geom.y+rq.geom.h+mode->dy2>sg->y+sg->h && rq.geom.y+rq.geom.h+mode->dy2y+sg->h+er) + realdy2=sg->y+sg->h-rq.geom.y-rq.geom.h; + } + + w=mode->origgeom.w-realdx1+realdx2; + h=mode->origgeom.h-realdy1+realdy2; + + if(w<=0) + w=mode->hints.min_width; + if(h<=0) + h=mode->hints.min_height; + + sizehints_correct(&mode->hints, &w, &h, TRUE, TRUE); + + /* Do not modify coordinates and sizes that were not requested to be + * changed. + */ + + if(mode->dx1==mode->dx2){ + if(mode->dx1==0 || realdx1!=mode->dx1) + rq.geom.x+=realdx1; + else + rq.geom.x+=realdx2; + }else{ + rq.geom.w=w; + if(mode->dx1==0 || realdx1!=mode->dx1) + rq.geom.x+=realdx1; + else + rq.geom.x+=mode->origgeom.w-rq.geom.w; + } + + + if(mode->dy1==mode->dy2){ + if(mode->dy1==0 || realdy1!=mode->dy1) + rq.geom.y+=realdy1; + else + rq.geom.y+=realdy2; + }else{ + rq.geom.h=h; + if(mode->dy1==0 || realdy1!=mode->dy1) + rq.geom.y+=realdy1; + else + rq.geom.y+=mode->origgeom.h-rq.geom.h; + } + + if(XOR_RESIZE) + moveres_draw_rubberband(mode); + + if(mode->reg!=NULL){ + rq.flags=mode->rqflags; + region_rqgeom(mode->reg, &rq, &mode->geom); + } + + if(!mode->resize_cumulative){ + mode->dx1=0; + mode->dx2=0; + mode->dy1=0; + mode->dy2=0; + mode->origgeom=mode->geom; + } + + moveres_draw_infowin(mode); + + if(XOR_RESIZE) + moveres_draw_rubberband(mode); + + if(rret!=NULL) + *rret=mode->geom; +} + + +void moveresmode_delta_resize(WMoveresMode *mode, + int dx1, int dx2, int dy1, int dy2, + WRectangle *rret) +{ + mode->mode=MOVERES_SIZE; + moveresmode_delta(mode, dx1, dx2, dy1, dy2, rret); +} + + +void moveresmode_delta_move(WMoveresMode *mode, + int dx, int dy, WRectangle *rret) +{ + mode->mode=MOVERES_POS; + moveresmode_delta(mode, dx, dx, dy, dy, rret); +} + + +/* It is ugly to do this here, but it will have to do for now... */ +static void set_saved(WMoveresMode *mode, WRegion *reg) +{ + WFrame *frame; + + if(!OBJ_IS(reg, WFrame)) + return; + + frame=(WFrame*)reg; + + /* Restore saved sizes from the beginning of the resize action */ + if(mode->origgeom.w!=mode->geom.w){ + frame->saved_x=mode->origgeom.x; + frame->saved_w=mode->origgeom.w; + } + + if(mode->origgeom.h!=mode->geom.h){ + frame->saved_y=mode->origgeom.y; + frame->saved_h=mode->origgeom.h; + } +} + + +bool moveresmode_do_end(WMoveresMode *mode, bool apply) +{ + WRegion *reg=mode->reg; + + assert(reg!=NULL); + assert(tmpmode==mode); + + tmpmode=NULL; + + if(XOR_RESIZE){ + moveres_draw_rubberband(mode); + if(apply){ + WRQGeomParams rq=RQGEOMPARAMS_INIT; + + rq.geom=mode->geom; + rq.flags=mode->rqflags&~REGION_RQGEOM_TRYONLY; + + region_rqgeom(reg, &rq, &mode->geom); + } + XUngrabServer(ioncore_g.dpy); + } + if(apply) + set_saved(mode, reg); + + if(mode->infowin!=NULL){ + mainloop_defer_destroy((Obj*)mode->infowin); + mode->infowin=NULL; + } + destroy_obj((Obj*)mode); + + return TRUE; +} + + +/*}}}*/ + + +/*{{{ Request and other dynfuns */ + + +void region_rqgeom(WRegion *reg, const WRQGeomParams *rq, + WRectangle *geomret) +{ + WRegion *mgr=REGION_MANAGER(reg); + + if(mgr!=NULL){ + if(rq->flags®ION_RQGEOM_ABSOLUTE) + region_managed_rqgeom_absolute(mgr, reg, rq, geomret); + else + region_managed_rqgeom(mgr, reg, rq, geomret); + }else{ + WRectangle tmp; + + if(rq->flags®ION_RQGEOM_ABSOLUTE) + region_absolute_geom_to_parent(reg, &rq->geom, &tmp); + else + tmp=rq->geom; + + if(geomret!=NULL) + *geomret=tmp; + + if(!(rq->flags®ION_RQGEOM_TRYONLY)) + region_fit(reg, &tmp, REGION_FIT_EXACT); + } +} + + +/*EXTL_DOC + * Attempt to resize and/or move \var{reg}. The table \var{g} is a usual + * geometry specification (fields \var{x}, \var{y}, \var{w} and \var{h}), + * but may contain missing fields, in which case, \var{reg}'s manager may + * attempt to leave that attribute unchanged. + */ +EXTL_EXPORT_AS(WRegion, rqgeom) +ExtlTab region_rqgeom_extl(WRegion *reg, ExtlTab g) +{ + WRectangle ogeom=REGION_GEOM(reg); + WRQGeomParams rq=RQGEOMPARAMS_INIT; + + + rq.geom=ogeom; + rq.flags=REGION_RQGEOM_WEAK_ALL; + + + if(extl_table_gets_i(g, "x", &(rq.geom.x))) + rq.flags&=~REGION_RQGEOM_WEAK_X; + if(extl_table_gets_i(g, "y", &(rq.geom.y))) + rq.flags&=~REGION_RQGEOM_WEAK_Y; + if(extl_table_gets_i(g, "w", &(rq.geom.w))) + rq.flags&=~REGION_RQGEOM_WEAK_W; + if(extl_table_gets_i(g, "h", &(rq.geom.h))) + rq.flags&=~REGION_RQGEOM_WEAK_H; + + rq.geom.w=maxof(1, rq.geom.w); + rq.geom.h=maxof(1, rq.geom.h); + + region_rqgeom(reg, &rq, &ogeom); + + return extl_table_from_rectangle(&ogeom); +} + + +void region_managed_rqgeom(WRegion *mgr, WRegion *reg, + const WRQGeomParams *rq, + WRectangle *geomret) +{ + CALL_DYN(region_managed_rqgeom, mgr, (mgr, reg, rq, geomret)); +} + + +void region_managed_rqgeom_absolute(WRegion *mgr, WRegion *reg, + const WRQGeomParams *rq, + WRectangle *geomret) +{ + CALL_DYN(region_managed_rqgeom_absolute, mgr, + (mgr, reg, rq, geomret)); +} + + +void region_managed_rqgeom_allow(WRegion *mgr, WRegion *reg, + const WRQGeomParams *rq, + WRectangle *geomret) +{ + if(geomret!=NULL) + *geomret=rq->geom; + + if(!(rq->flags®ION_RQGEOM_TRYONLY)) + region_fit(reg, &rq->geom, REGION_FIT_EXACT); +} + + +void region_managed_rqgeom_unallow(WRegion *mgr, WRegion *reg, + const WRQGeomParams *rq, + WRectangle *geomret) +{ + if(geomret!=NULL) + *geomret=REGION_GEOM(reg); +} + + +void region_managed_rqgeom_absolute_default(WRegion *mgr, WRegion *reg, + const WRQGeomParams *rq, + WRectangle *geomret) +{ + WRQGeomParams rq2=RQGEOMPARAMS_INIT; + + rq2.flags=rq->flags&~REGION_RQGEOM_ABSOLUTE; + region_absolute_geom_to_parent(reg, &rq->geom, &rq2.geom); + + region_managed_rqgeom(mgr, reg, &rq2, geomret); +} + + +void region_size_hints(WRegion *reg, WSizeHints *hints_ret) +{ + sizehints_clear(hints_ret); + + { + CALL_DYN(region_size_hints, reg, (reg, hints_ret)); + } + + if(!hints_ret->min_set){ + hints_ret->min_width=1; + hints_ret->min_height=1; + } + if(!hints_ret->base_set){ + hints_ret->base_width=0; + hints_ret->base_height=0; + } + if(!hints_ret->max_set){ + hints_ret->max_width=INT_MAX; + hints_ret->max_height=INT_MAX; + } +} + + +void region_size_hints_correct(WRegion *reg, + int *wp, int *hp, bool min) +{ + WSizeHints hints; + + region_size_hints(reg, &hints); + + sizehints_correct(&hints, wp, hp, min, FALSE); +} + + +int region_orientation(WRegion *reg) +{ + int ret=REGION_ORIENTATION_NONE; + + CALL_DYN_RET(ret, int, region_orientation, reg, (reg)); + + return ret; +} + + +/*EXTL_DOC + * Returns size hints for \var{reg}. The returned table always contains the + * fields \code{min_?}, \code{base_?} and sometimes the fields \code{max_?}, + * \code{base_?} and \code{inc_?}, where \code{?}=\code{w}, \code{h}. + */ +EXTL_SAFE +EXTL_EXPORT_AS(WRegion, size_hints) +ExtlTab region_size_hints_extl(WRegion *reg) +{ + WSizeHints hints; + ExtlTab tab; + + region_size_hints(reg, &hints); + + tab=extl_create_table(); + + if(hints.base_set){ + extl_table_sets_i(tab, "base_w", hints.base_width); + extl_table_sets_i(tab, "base_h", hints.base_height); + } + if(hints.min_set){ + extl_table_sets_i(tab, "min_w", hints.min_width); + extl_table_sets_i(tab, "min_h", hints.min_height); + } + if(hints.max_set){ + extl_table_sets_i(tab, "max_w", hints.max_width); + extl_table_sets_i(tab, "max_h", hints.max_height); + } + if(hints.inc_set){ + extl_table_sets_i(tab, "inc_w", hints.width_inc); + extl_table_sets_i(tab, "inc_h", hints.height_inc); + } + + return tab; +} + +/*}}}*/ + + +/*{{{ Restore size, maximize, shade */ + + +static bool rqh(WFrame *frame, int y, int h) +{ + WRQGeomParams rq=RQGEOMPARAMS_INIT; + WRectangle rgeom; + int dummy_w; + + rq.flags=REGION_RQGEOM_VERT_ONLY; + + rq.geom.x=REGION_GEOM(frame).x; + rq.geom.w=REGION_GEOM(frame).w; + rq.geom.y=y; + rq.geom.h=h; + + dummy_w=rq.geom.w; + region_size_hints_correct((WRegion*)frame, &dummy_w, &(rq.geom.h), TRUE); + + region_rqgeom((WRegion*)frame, &rq, &rgeom); + + return (abs(rgeom.y-REGION_GEOM(frame).y)>1 || + abs(rgeom.h-REGION_GEOM(frame).h)>1); +} + + +/*EXTL_DOC + * Attempt to toggle vertical maximisation of \var{frame}. + */ +EXTL_EXPORT_MEMBER +void frame_maximize_vert(WFrame *frame) +{ + WRegion *mp=region_manager((WRegion*)frame); + int oy, oh; + + if(frame->flags&FRAME_SHADED || frame->flags&FRAME_MAXED_VERT){ + if(frame->flags&FRAME_SAVED_VERT) + rqh(frame, frame->saved_y, frame->saved_h); + frame->flags&=~(FRAME_MAXED_VERT|FRAME_SAVED_VERT); + return; + } + + if(mp==NULL) + return; + + oy=REGION_GEOM(frame).y; + oh=REGION_GEOM(frame).h; + + rqh(frame, 0, REGION_GEOM(mp).h); + + frame->flags|=(FRAME_MAXED_VERT|FRAME_SAVED_VERT); + frame->saved_y=oy; + frame->saved_h=oh; +} + +static bool rqw(WFrame *frame, int x, int w) +{ + WRQGeomParams rq=RQGEOMPARAMS_INIT; + WRectangle rgeom; + int dummy_h; + + rq.flags=REGION_RQGEOM_HORIZ_ONLY; + + rq.geom.x=x; + rq.geom.w=w; + rq.geom.y=REGION_GEOM(frame).y; + rq.geom.h=REGION_GEOM(frame).h; + + dummy_h=rq.geom.h; + region_size_hints_correct((WRegion*)frame, &(rq.geom.w), &dummy_h, TRUE); + + region_rqgeom((WRegion*)frame, &rq, &rgeom); + + return (abs(rgeom.x-REGION_GEOM(frame).x)>1 || + abs(rgeom.w-REGION_GEOM(frame).w)>1); +} + + +/*EXTL_DOC + * Attempt to toggle horizontal maximisation of \var{frame}. + */ +EXTL_EXPORT_MEMBER +void frame_maximize_horiz(WFrame *frame) +{ + WRegion *mp=region_manager((WRegion*)frame); + int ox, ow; + + if(frame->flags&FRAME_MIN_HORIZ || frame->flags&FRAME_MAXED_HORIZ){ + if(frame->flags&FRAME_SAVED_HORIZ) + rqw(frame, frame->saved_x, frame->saved_w); + frame->flags&=~(FRAME_MAXED_HORIZ|FRAME_SAVED_HORIZ); + return; + } + + if(mp==NULL) + return; + + ox=REGION_GEOM(frame).x; + ow=REGION_GEOM(frame).w; + + rqw(frame, 0, REGION_GEOM(mp).w); + + frame->flags|=(FRAME_MAXED_HORIZ|FRAME_SAVED_HORIZ); + frame->saved_x=ox; + frame->saved_w=ow; +} + + + +/*}}}*/ + + +/*{{{ Misc. */ + + +uint region_min_h(WRegion *reg) +{ + WSizeHints hints; + region_size_hints(reg, &hints); + return hints.min_height; +} + + +uint region_min_w(WRegion *reg) +{ + WSizeHints hints; + region_size_hints(reg, &hints); + return hints.min_width; +} + + +void region_convert_root_geom(WRegion *reg, WRectangle *geom) +{ + int rx, ry; + if(reg!=NULL){ + region_rootpos(reg, &rx, &ry); + geom->x-=rx; + geom->y-=ry; + } +} + + +void region_absolute_geom_to_parent(WRegion *reg, const WRectangle *rgeom, + WRectangle *pgeom) +{ + WRegion *parent=REGION_PARENT_REG(reg); + + pgeom->w=rgeom->w; + pgeom->h=rgeom->h; + + if(parent==NULL){ + pgeom->x=rgeom->x; + pgeom->y=rgeom->y; + }else{ + region_rootpos(reg, &pgeom->x, &pgeom->y); + pgeom->x=rgeom->x-pgeom->x; + pgeom->y=rgeom->y-pgeom->y; + } +} + +/*}}}*/ + diff --git a/ioncore/resize.h b/ioncore/resize.h new file mode 100644 index 0000000..3a4f062 --- /dev/null +++ b/ioncore/resize.h @@ -0,0 +1,150 @@ +/* + * ion/ioncore/resize.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_RESIZE_H +#define ION_IONCORE_RESIZE_H + +#include "common.h" +#include "frame.h" +#include "infowin.h" +#include "rectangle.h" +#include "sizehint.h" + + +/* To make it easier for region_managed_rqgeom handlers, the geom + * parameter contain a complete requested geometry for the region that + * wants its geometry changed. The REGION_WEAK_* flags are used to + * indicate that the respective geometry value has not been changed or + * that the requestor doesn't really care what the result is. In any case, + * managers are free to give the managed object whatever geometry it wishes. + */ +#define REGION_RQGEOM_WEAK_X 0x0001 +#define REGION_RQGEOM_WEAK_Y 0x0002 +#define REGION_RQGEOM_WEAK_W 0x0004 +#define REGION_RQGEOM_WEAK_H 0x0008 +#define REGION_RQGEOM_WEAK_ALL 0x000f +#define REGION_RQGEOM_TRYONLY 0x0010 +#define REGION_RQGEOM_ABSOLUTE 0x0020 +#define REGION_RQGEOM_GRAVITY 0x0040 + +#define REGION_RQGEOM_NORMAL 0 +#define REGION_RQGEOM_VERT_ONLY (REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_W) +#define REGION_RQGEOM_HORIZ_ONLY (REGION_RQGEOM_WEAK_Y|REGION_RQGEOM_WEAK_H) +#define REGION_RQGEOM_H_ONLY (REGION_RQGEOM_VERT_ONLY|REGION_RQGEOM_WEAK_Y) +#define REGION_RQGEOM_W_ONLY (REGION_RQGEOM_HORIZ_ONLY|REGION_RQGEOM_WEAK_X) + +#define REGION_ORIENTATION_NONE 0 +#define REGION_ORIENTATION_HORIZONTAL 1 +#define REGION_ORIENTATION_VERTICAL 2 + + +#define RQGEOMPARAMS_INIT {{0, 0, 0, 0}, 0, 0} + + +DECLSTRUCT(WRQGeomParams){ + WRectangle geom; + int flags; + int gravity; +}; + + +typedef void WDrawRubberbandFn(WRootWin *rw, const WRectangle *geom); + + +DECLCLASS(WMoveresMode){ + Obj obj; + WSizeHints hints; + int dx1, dx2, dy1, dy2; + WRectangle origgeom; + WRectangle geom; + WRegion *reg; + WDrawRubberbandFn *rubfn; + int parent_rx, parent_ry; + enum {MOVERES_SIZE, MOVERES_POS} mode; + bool resize_cumulative; + bool snap_enabled; + WRectangle snapgeom; + int rqflags; + WInfoWin *infowin; +}; + + +extern WMoveresMode *region_begin_resize(WRegion *reg, + WDrawRubberbandFn *rubfn, + bool cumulative); + +extern WMoveresMode *region_begin_move(WRegion *reg, + WDrawRubberbandFn *rubfn, + bool cumulative); + +/* dx1/dx2/dy1/dy2: left/right/top/bottom difference to previous values. + * left/top negative, bottom/right positive increases size. + */ +extern void moveresmode_delta_resize(WMoveresMode *mode, + int dx1, int dx2, int dy1, int dy2, + WRectangle *rret); +extern void moveresmode_delta_move(WMoveresMode *mode, + int dx, int dy, WRectangle *rret); +extern bool moveresmode_do_end(WMoveresMode *mode, bool apply); +extern WRegion *moveresmode_target(WMoveresMode *mode); + +extern WMoveresMode *moveres_mode(WRegion *reg); + + + +/* Note: even if REGION_RQGEOM_(X|Y|W|H) are not all specified, the + * geom parameter should contain a proper geometry! + */ +DYNFUN void region_managed_rqgeom(WRegion *reg, WRegion *sub, + const WRQGeomParams *rqgp, + WRectangle *geomret); +DYNFUN void region_managed_rqgeom_absolute(WRegion *reg, WRegion *sub, + const WRQGeomParams *rqgp, + WRectangle *geomret); + +extern void region_rqgeom(WRegion *reg, const WRQGeomParams *rqgp, + WRectangle *geomret); + +/* Implementation for regions that do not allow subregions to resize + * themselves; default is to give the size the region wants. + */ +extern void region_managed_rqgeom_unallow(WRegion *reg, WRegion *sub, + const WRQGeomParams *rq, + WRectangle *geomret); +/* default */ +extern void region_managed_rqgeom_allow(WRegion *reg, WRegion *sub, + const WRQGeomParams *rq, + WRectangle *geomret); + +extern void region_managed_rqgeom_absolute_default(WRegion *reg, WRegion *sub, + const WRQGeomParams *rq, + WRectangle *geomret); + + +DYNFUN void region_size_hints(WRegion *reg, WSizeHints *hints_ret); +DYNFUN int region_orientation(WRegion *reg); + +extern void region_size_hints_correct(WRegion *reg, + int *wp, int *hp, bool min); + +extern uint region_min_h(WRegion *reg); +extern uint region_min_w(WRegion *reg); + +extern void frame_maximize_vert(WFrame *frame); +extern void frame_maximize_horiz(WFrame *frame); + +extern void region_convert_root_geom(WRegion *reg, WRectangle *geom); + +extern void region_absolute_geom_to_parent(WRegion *reg, + const WRectangle *rgeom, + WRectangle *pgeom); + +#endif /* ION_IONCORE_RESIZE_H */ diff --git a/ioncore/rootwin.c b/ioncore/rootwin.c new file mode 100644 index 0000000..5fc4891 --- /dev/null +++ b/ioncore/rootwin.c @@ -0,0 +1,625 @@ +/* + * ion/ioncore/rootwin.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/*#include */ +#ifdef CF_XINERAMA +#include +#elif defined(CF_SUN_XINERAMA) +#include +#endif + +#include +#include "common.h" +#include "rootwin.h" +#include "cursor.h" +#include "global.h" +#include "event.h" +#include "gr.h" +#include "clientwin.h" +#include "property.h" +#include "focus.h" +#include "regbind.h" +#include "screen.h" +#include "screen.h" +#include "bindmaps.h" +#include +#include "resize.h" +#include "saveload.h" +#include "netwm.h" +#include "xwindow.h" + + +/*{{{ Error handling */ + + +static bool redirect_error=FALSE; +static bool ignore_badwindow=TRUE; + + +static int my_redirect_error_handler(Display *dpy, XErrorEvent *ev) +{ + redirect_error=TRUE; + return 0; +} + + +static int my_error_handler(Display *dpy, XErrorEvent *ev) +{ + static char msg[128], request[64], num[32]; + + /* Just ignore bad window and similar errors; makes the rest of + * the code simpler. + */ + if((ev->error_code==BadWindow || + (ev->error_code==BadMatch && ev->request_code==X_SetInputFocus) || + (ev->error_code==BadDrawable && ev->request_code==X_GetGeometry)) && + ignore_badwindow) + return 0; + +#if 0 + XmuPrintDefaultErrorMessage(dpy, ev, stderr); +#else + XGetErrorText(dpy, ev->error_code, msg, 128); + snprintf(num, 32, "%d", ev->request_code); + XGetErrorDatabaseText(dpy, "XRequest", num, "", request, 64); + + if(request[0]=='\0') + snprintf(request, 64, ""); + + if(ev->minor_code!=0){ + warn("[%d] %s (%d.%d) %#lx: %s", ev->serial, request, + ev->request_code, ev->minor_code, ev->resourceid,msg); + }else{ + warn("[%d] %s (%d) %#lx: %s", ev->serial, request, + ev->request_code, ev->resourceid,msg); + } +#endif + + kill(getpid(), SIGTRAP); + + return 0; +} + + +/*}}}*/ + + +/*{{{ Init/deinit */ + + +static void scan_initial_windows(WRootWin *rootwin) +{ + Window dummy_root, dummy_parent, *wins=NULL; + uint nwins=0, i, j; + XWMHints *hints; + + XQueryTree(ioncore_g.dpy, WROOTWIN_ROOT(rootwin), &dummy_root, &dummy_parent, + &wins, &nwins); + + for(i=0; iflags&IconWindowHint){ + for(j=0; jicon_window){ + wins[j]=None; + break; + } + } + } + if(hints!=NULL) + XFree((void*)hints); + } + + rootwin->tmpwins=wins; + rootwin->tmpnwins=nwins; +} + + +void rootwin_manage_initial_windows(WRootWin *rootwin) +{ + Window *wins=rootwin->tmpwins; + Window tfor=None; + int i, nwins=rootwin->tmpnwins; + + rootwin->tmpwins=NULL; + rootwin->tmpnwins=0; + + for(i=0; idummy_win=XCreateWindow(ioncore_g.dpy, WROOTWIN_ROOT(rootwin), + 0, 0, 1, 1, 0, + CopyFromParent, InputOnly, + CopyFromParent, 0, NULL); + + XSelectInput(ioncore_g.dpy, rootwin->dummy_win, PropertyChangeMask); +} + + +static void preinit_gr(WRootWin *rootwin) +{ + XGCValues gcv; + ulong gcvmask; + + /* Create XOR gc (for resize) */ + gcv.line_style=LineSolid; + gcv.join_style=JoinBevel; + gcv.cap_style=CapButt; + gcv.fill_style=FillSolid; + gcv.line_width=2; + gcv.subwindow_mode=IncludeInferiors; + gcv.function=GXxor; + gcv.foreground=~0L; + + gcvmask=(GCLineStyle|GCLineWidth|GCFillStyle| + GCJoinStyle|GCCapStyle|GCFunction| + GCSubwindowMode|GCForeground); + + rootwin->xor_gc=XCreateGC(ioncore_g.dpy, WROOTWIN_ROOT(rootwin), + gcvmask, &gcv); +} + + +static WRootWin *preinit_rootwin(int xscr) +{ + Display *dpy=ioncore_g.dpy; + WRootWin *rootwin; + WFitParams fp; + Window root; + int i; + + /* Try to select input on the root window */ + root=RootWindow(dpy, xscr); + + redirect_error=FALSE; + + XSetErrorHandler(my_redirect_error_handler); + XSelectInput(dpy, root, IONCORE_EVENTMASK_ROOT); + XSync(dpy, 0); + XSetErrorHandler(my_error_handler); + + if(redirect_error){ + warn(TR("Unable to redirect root window events for screen %d."), + xscr); + return NULL; + } + + rootwin=ALLOC(WRootWin); + + if(rootwin==NULL) + return NULL; + + /* Init the struct */ + OBJ_INIT(rootwin, WRootWin); + + rootwin->xscr=xscr; + rootwin->default_cmap=DefaultColormap(dpy, xscr); + rootwin->tmpwins=NULL; + rootwin->tmpnwins=0; + rootwin->dummy_win=None; + rootwin->xor_gc=None; + + fp.mode=REGION_FIT_EXACT; + fp.g.x=0; fp.g.y=0; + fp.g.w=DisplayWidth(dpy, xscr); + fp.g.h=DisplayHeight(dpy, xscr); + + if(!window_do_init((WWindow*)rootwin, NULL, root, &fp)){ + free(rootwin); + return NULL; + } + + /* Note: this mask isn't right if some WScreen auses the same window. */ + ((WWindow*)rootwin)->event_mask=IONCORE_EVENTMASK_ROOT; + + ((WRegion*)rootwin)->flags|=REGION_BINDINGS_ARE_GRABBED|REGION_PLEASE_WARP; + ((WRegion*)rootwin)->rootwin=rootwin; + + REGION_MARK_MAPPED(rootwin); + + scan_initial_windows(rootwin); + + create_wm_windows(rootwin); + preinit_gr(rootwin); + netwm_init_rootwin(rootwin); + + region_add_bindmap((WRegion*)rootwin, ioncore_rootwin_bindmap); + + return rootwin; +} + + +static Atom net_virtual_roots=None; + + +static WScreen *add_screen(WRootWin *rw, int id, const WRectangle *geom, + bool useroot) +{ + WScreen *scr; + CARD32 p[1]; + WFitParams fp; + + fp.g=*geom; + fp.mode=REGION_FIT_EXACT; + +#ifdef CF_ALWAYS_VIRTUAL_ROOT + useroot=FALSE; +#endif + + scr=create_screen(rw, id, &fp, useroot); + + if(scr==NULL) + return NULL; + + region_set_manager((WRegion*)scr, (WRegion*)rw); + + region_map((WRegion*)scr); + + if(!useroot){ + p[0]=region_xwindow((WRegion*)scr); + XChangeProperty(ioncore_g.dpy, WROOTWIN_ROOT(rw), net_virtual_roots, + XA_WINDOW, 32, PropModeAppend, (uchar*)&(p[0]), 1); + } + + return scr; +} + + +#ifdef CF_XINERAMA +static bool xinerama_sanity_check(XineramaScreenInfo *xi, int nxi) +{ + int i, j; + + for(i=0; i=xi[i].x_org && xi[j].x_org=xi[i].y_org && xi[j].y_org=monitors[i].x && + monitors[j].x=monitors[i].y && + monitors[j].yxor_gc); + + window_deinit((WWindow*)rw); +} + + +/*}}}*/ + + +/*{{{ region dynfun implementations */ + + +static void rootwin_do_set_focus(WRootWin *rootwin, bool warp) +{ + WRegion *sub; + + sub=REGION_ACTIVE_SUB(rootwin); + + if(sub==NULL || !REGION_IS_MAPPED(sub)){ + WScreen *scr; + FOR_ALL_SCREENS(scr){ + if(REGION_IS_MAPPED(scr)){ + sub=(WRegion*)scr; + break; + } + } + } + + if(sub!=NULL) + region_do_set_focus(sub, warp); + else + window_do_set_focus((WWindow*)rootwin, warp); +} + + +static bool rootwin_fitrep(WRootWin *rootwin, WWindow *par, + const WFitParams *fp) +{ + D(warn("Don't know how to reparent or fit root windows.")); + return FALSE; +} + + +static void rootwin_map(WRootWin *rootwin) +{ + D(warn("Attempt to map a root window.")); +} + + +static void rootwin_unmap(WRootWin *rootwin) +{ + D(warn("Attempt to unmap a root window -- impossible.")); +} + + +static void rootwin_managed_remove(WRootWin *rootwin, WRegion *reg) +{ + region_unset_manager(reg, (WRegion*)rootwin); +} + + +static Window rootwin_x_window(WRootWin *rootwin) +{ + return WROOTWIN_ROOT(rootwin); +} + + +/*}}}*/ + + +/*{{{ Misc */ + + +static bool scr_ok(WRegion *r) +{ + return (OBJ_IS(r, WScreen) && REGION_IS_MAPPED(r)); +} + + +/*EXTL_DOC + * Returns previously active screen on root window \var{rootwin}. + */ +EXTL_SAFE +EXTL_EXPORT_MEMBER +WScreen *rootwin_current_scr(WRootWin *rootwin) +{ + WRegion *r=REGION_ACTIVE_SUB(rootwin); + WScreen *scr; + + /* There should be no non-WScreen as children or managed by us, but... */ + + if(r!=NULL && scr_ok(r)) + return (WScreen*)r; + + FOR_ALL_SCREENS(scr){ + if(REGION_MANAGER(scr)==(WRegion*)rootwin + && REGION_IS_MAPPED(scr)){ + break; + } + } + + return scr; +} + + +/*}}}*/ + + +/*{{{ Dynamic function table and class implementation */ + + +static DynFunTab rootwin_dynfuntab[]={ + {region_map, rootwin_map}, + {region_unmap, rootwin_unmap}, + {region_do_set_focus, rootwin_do_set_focus}, + {(DynFun*)region_xwindow, (DynFun*)rootwin_x_window}, + {(DynFun*)region_fitrep, (DynFun*)rootwin_fitrep}, + {region_managed_remove, rootwin_managed_remove}, + END_DYNFUNTAB +}; + + +EXTL_EXPORT +IMPLCLASS(WRootWin, WWindow, rootwin_deinit, rootwin_dynfuntab); + + +/*}}}*/ diff --git a/ioncore/rootwin.h b/ioncore/rootwin.h new file mode 100644 index 0000000..41dd89c --- /dev/null +++ b/ioncore/rootwin.h @@ -0,0 +1,50 @@ +/* + * ion/ioncore/rootwin.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_ROOTWIN_H +#define ION_IONCORE_ROOTWIN_H + +#include "common.h" +#include "window.h" +#include "screen.h" +#include "gr.h" +#include "rectangle.h" + +#define WROOTWIN_ROOT(X) ((X)->wwin.win) +#define FOR_ALL_ROOTWINS(RW) \ + for(RW=ioncore_g.rootwins; \ + RW!=NULL; \ + RW=OBJ_CAST(((WRegion*)RW)->p_next, WRootWin)) + + +DECLCLASS(WRootWin){ + WWindow wwin; + int xscr; + + Colormap default_cmap; + + Window *tmpwins; + int tmpnwins; + + Window dummy_win; + + GC xor_gc; +}; + + +extern void rootwin_deinit(WRootWin *rootwin); +extern WScreen *rootwin_current_scr(WRootWin *rootwin); + +extern void rootwin_manage_initial_windows(WRootWin *rootwin); +extern WRootWin *ioncore_manage_rootwin(int xscr, bool noxinerama); + +#endif /* ION_IONCORE_ROOTWIN_H */ + diff --git a/ioncore/saveload.c b/ioncore/saveload.c new file mode 100644 index 0000000..e5902b3 --- /dev/null +++ b/ioncore/saveload.c @@ -0,0 +1,304 @@ +/* + * ion/ioncore/saveload.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include + +#include +#include +#include + +#include "common.h" +#include "global.h" +#include "region.h" +#include "screen.h" +#include "saveload.h" +#include "names.h" +#include "attach.h" +#include "reginfo.h" +#include "extlconv.h" +#include "group-ws.h" + + +static bool loading_layout=FALSE; +static bool layout_load_error=FALSE; + + +/*{{{ Session management module support */ + + +static SMAddCallback *add_cb; +static SMCfgCallback *cfg_cb; +static SMPHolderCallback *ph_cb; +static bool clientwin_was_missing=FALSE; + + +void ioncore_set_sm_callbacks(SMAddCallback *add, SMCfgCallback *cfg) +{ + add_cb=add; + cfg_cb=cfg; +} + + +void ioncore_get_sm_callbacks(SMAddCallback **add, SMCfgCallback **cfg) +{ + *add=add_cb; + *cfg=cfg_cb; +} + + +void ioncore_set_sm_pholder_callback(SMPHolderCallback *phcb) +{ + ph_cb=phcb; +} + + +void ioncore_clientwin_load_missing() +{ + clientwin_was_missing=TRUE; +} + + +/*}}}*/ + + +/*{{{ Load support functions */ + + +WRegion *create_region_load(WWindow *par, const WFitParams *fp, + ExtlTab tab) +{ + char *objclass=NULL, *name=NULL; + WRegionLoadCreateFn* fn=NULL; + WRegClassInfo *info=NULL; + WRegion *reg=NULL; + bool grouped=FALSE; + char *grouped_name=NULL; + + if(!extl_table_gets_s(tab, "type", &objclass)) + return NULL; + + /* Backwards compatibility hack. */ + if(strcmp(objclass, "WFloatWS")==0){ + objclass=scopy("WGroupWS"); + }else if(strcmp(objclass, "WIonWS")==0){ + WGroupWS *ws=create_groupws(par, fp); + if(ws!=NULL){ + extl_table_gets_s(tab, "name", &name); + extl_table_sets_s(tab, "type", "WTiling"); + extl_table_sets_b(tab, "bottom", TRUE); + extl_table_sets_b(tab, "bottom_last_close", TRUE); + extl_table_sets_i(tab, "sizepolicy", SIZEPOLICY_FULL_EXACT); + + if(name!=NULL) + region_set_name((WRegion*)ws, name); + + reg=group_attach_new((WGroup*)ws, tab); + + if(reg!=NULL) + return (WRegion*)ws; + + destroy_obj((Obj*)ws); + } + objclass=scopy("WTiling"); + }else if(strcmp(objclass, "WFloatFrame")==0){ + objclass=scopy("WFrame"); + } + + if(objclass==NULL) + return NULL; + + info=ioncore_lookup_regclass(objclass, FALSE); + if(info!=NULL) + fn=info->lc_fn; + + if(fn==NULL){ + warn(TR("Unknown class \"%s\", cannot create region."), + objclass); + layout_load_error=loading_layout; + return NULL; + } + + free(objclass); + + clientwin_was_missing=FALSE; + + reg=fn(par, fp, tab); + + if(reg==NULL){ + if(clientwin_was_missing && add_cb!=NULL && ph_cb!=NULL){ + WPHolder *ph=ph_cb(); + if(ph!=NULL){ + if(!add_cb(ph, tab)) + destroy_obj((Obj*)ph); + } + } + }else{ + if(!OBJ_IS(reg, WClientWin)){ + if(extl_table_gets_s(tab, "name", &name)){ + region_set_name(reg, name); + free(name); + } + } + } + + ph_cb=NULL; + + return reg; +} + + +/*}}}*/ + + +/*{{{ Save support functions */ + + +bool region_supports_save(WRegion *reg) +{ + return HAS_DYN(reg, region_get_configuration); +} + + +ExtlTab region_get_base_configuration(WRegion *reg) +{ + const char *name; + ExtlTab tab; + + tab=extl_create_table(); + + extl_table_sets_s(tab, "type", OBJ_TYPESTR(reg)); + + name=region_name(reg); + + if(name!=NULL && !OBJ_IS(reg, WClientWin)) + extl_table_sets_s(tab, "name", name); + + return tab; +} + + +ExtlTab region_get_configuration(WRegion *reg) +{ + ExtlTab tab=extl_table_none(); + CALL_DYN_RET(tab, ExtlTab, region_get_configuration, reg, (reg)); + return tab; +} + + +/*}}}*/ + + +/*{{{ save_workspaces, load_workspaces */ + + +static const char backup_msg[]=DUMMY_TR( +"There were errors loading layout. Backing up current layout savefile as\n" +"%s.\n" +"If you are _not_ running under a session manager and wish to restore your\n" +"old layout, copy this backup file over the layout savefile found in the\n" +"same directory while Ion is not running and after having fixed your other\n" +"configuration files that are causing this problem. (Maybe a missing\n" +"module?)"); + + +bool ioncore_init_layout() +{ + ExtlTab tab; + WScreen *scr; + bool ok; + int n=0; + + ok=extl_read_savefile("saved_layout", &tab); + + loading_layout=TRUE; + layout_load_error=FALSE; + + FOR_ALL_SCREENS(scr){ + ExtlTab scrtab=extl_table_none(); + bool scrok=FALSE; + if(ok) + scrok=extl_table_geti_t(tab, screen_id(scr), &scrtab); + + n+=(TRUE==screen_init_layout(scr, scrtab)); + + if(scrok) + extl_unref_table(scrtab); + } + + loading_layout=FALSE; + + if(layout_load_error){ + time_t t=time(NULL); + char tm[]="saved_layout.backup-YYYYMMDDHHMMSS\0\0\0\0"; + char *backup; + + strftime(tm+20, 15, "%Y%m%d%H%M%S", localtime(&t)); + backup=extl_get_savefile(tm); + if(backup==NULL){ + warn(TR("Unable to get file for layout backup.")); + return FALSE; + } + if(access(backup, F_OK)==0){ + warn(TR("Backup file %s already exists."), backup); + free(backup); + return FALSE; + } + warn(TR(backup_msg), backup); + if(!extl_serialise(backup, tab)) + warn(TR("Failed backup.")); + free(backup); + } + + if(n==0){ + warn(TR("Unable to initialise layout on any screen.")); + return FALSE; + }else{ + return TRUE; + } +} + + +bool ioncore_save_layout() +{ + WScreen *scr=NULL; + ExtlTab tab=extl_create_table(); + bool ret; + + if(tab==extl_table_none()) + return FALSE; + + FOR_ALL_SCREENS(scr){ + ExtlTab scrtab=region_get_configuration((WRegion*)scr); + if(scrtab==extl_table_none()){ + warn(TR("Unable to get configuration for screen %d."), + screen_id(scr)); + }else{ + extl_table_seti_t(tab, screen_id(scr), scrtab); + extl_unref_table(scrtab); + } + } + + ret=extl_write_savefile("saved_layout", tab); + + extl_unref_table(tab); + + if(!ret) + warn(TR("Unable to save layout.")); + + return ret; +} + + +/*}}}*/ + + diff --git a/ioncore/saveload.h b/ioncore/saveload.h new file mode 100644 index 0000000..27e3b3e --- /dev/null +++ b/ioncore/saveload.h @@ -0,0 +1,43 @@ +/* + * ion/ioncore/saveload.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_SAVELOAD_H +#define ION_IONCORE_SAVELOAD_H + +#include +#include "common.h" +#include "region.h" +#include "screen.h" +#include "pholder.h" + +extern WRegion *create_region_load(WWindow *par, const WFitParams *fp, + ExtlTab tab); + +extern bool region_supports_save(WRegion *reg); +DYNFUN ExtlTab region_get_configuration(WRegion *reg); +extern ExtlTab region_get_base_configuration(WRegion *reg); + +extern bool ioncore_init_layout(); +extern bool ioncore_save_layout(); + +/* Session management support */ + +typedef bool SMAddCallback(WPHolder *ph, ExtlTab tab); +typedef void SMCfgCallback(WClientWin *cwin, ExtlTab tab); +typedef WPHolder *SMPHolderCallback(); + +extern void ioncore_set_sm_callbacks(SMAddCallback *add, SMCfgCallback *cfg); +extern void ioncore_get_sm_callbacks(SMAddCallback **add, SMCfgCallback **cfg); +extern void ioncore_set_sm_pholder_callback(SMPHolderCallback *phcb); +extern void ioncore_clientwin_load_missing(); + +#endif /* ION_IONCORE_SAVELOAD_H */ + diff --git a/ioncore/screen.c b/ioncore/screen.c new file mode 100644 index 0000000..3b896bf --- /dev/null +++ b/ioncore/screen.c @@ -0,0 +1,732 @@ +/* + * ion/ioncore/screen.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include + +#include +#include +#include + +#include "common.h" +#include "global.h" +#include "screen.h" +#include "region.h" +#include "attach.h" +#include "manage.h" +#include "focus.h" +#include "property.h" +#include "names.h" +#include "reginfo.h" +#include "saveload.h" +#include "resize.h" +#include "event.h" +#include "bindmaps.h" +#include "regbind.h" +#include "frame-pointer.h" +#include "rectangle.h" +#include "infowin.h" +#include "activity.h" +#include "extlconv.h" +#include "llist.h" +#include "group-ws.h" +#include "mplex.h" +#include "tags.h" + + +WHook *screen_managed_changed_hook=NULL; + + +static void screen_update_infowin(WScreen *scr); + + + +/*{{{ Init/deinit */ + + +static bool screen_init(WScreen *scr, WRootWin *rootwin, + int id, const WFitParams *fp, bool useroot) +{ + Window win; + XSetWindowAttributes attr; + ulong attrflags=0; + + scr->id=id; + scr->atom_workspace=None; + scr->uses_root=useroot; + scr->managed_off.x=0; + scr->managed_off.y=0; + scr->managed_off.w=0; + scr->managed_off.h=0; + scr->next_scr=NULL; + scr->prev_scr=NULL; + scr->rotation=SCREEN_ROTATION_0; + + watch_init(&(scr->notifywin_watch)); + watch_init(&(scr->infowin_watch)); + + if(useroot){ + win=WROOTWIN_ROOT(rootwin); + }else{ + attr.background_pixmap=ParentRelative; + attrflags=CWBackPixmap; + + win=XCreateWindow(ioncore_g.dpy, WROOTWIN_ROOT(rootwin), + fp->g.x, fp->g.y, fp->g.w, fp->g.h, 0, + DefaultDepth(ioncore_g.dpy, rootwin->xscr), + InputOutput, + DefaultVisual(ioncore_g.dpy, rootwin->xscr), + attrflags, &attr); + if(win==None) + return FALSE; + } + + if(!mplex_do_init((WMPlex*)scr, (WWindow*)rootwin, win, fp, FALSE)) + return FALSE; + + /*scr->mplex.win.region.rootwin=rootwin; + region_set_parent((WRegion*)scr, (WRegion*)rootwin);*/ + scr->mplex.flags|=MPLEX_ADD_TO_END; + scr->mplex.win.region.flags|=REGION_BINDINGS_ARE_GRABBED; + if(useroot) + scr->mplex.win.region.flags|=REGION_MAPPED; + + window_select_input(&(scr->mplex.win), + FocusChangeMask|EnterWindowMask| + KeyPressMask|KeyReleaseMask| + ButtonPressMask|ButtonReleaseMask| + (useroot ? IONCORE_EVENTMASK_ROOT : 0)); + + if(id==0){ + scr->atom_workspace=XInternAtom(ioncore_g.dpy, + "_ION_WORKSPACE", False); + }else if(id>=0){ + char *str; + libtu_asprintf(&str, "_ION_WORKSPACE%d", id); + if(str!=NULL){ + scr->atom_workspace=XInternAtom(ioncore_g.dpy, str, False); + free(str); + } + } + + /* Add rootwin's bindings to screens (ungrabbed) so that bindings + * are called with the proper region. + */ + region_add_bindmap((WRegion*)scr, ioncore_rootwin_bindmap); + + LINK_ITEM(ioncore_g.screens, scr, next_scr, prev_scr); + + return TRUE; +} + + +WScreen *create_screen(WRootWin *rootwin, int id, const WFitParams *fp, + bool useroot) +{ + CREATEOBJ_IMPL(WScreen, screen, (p, rootwin, id, fp, useroot)); +} + + +void screen_deinit(WScreen *scr) +{ + UNLINK_ITEM(ioncore_g.screens, scr, next_scr, prev_scr); + + if(scr->uses_root) + scr->mplex.win.win=None; + + mplex_deinit((WMPlex*)scr); +} + + +/*}}}*/ + + +/*{{{ Attach/detach */ + + +void screen_managed_geom(WScreen *scr, WRectangle *geom) +{ + geom->x=scr->managed_off.x; + geom->y=scr->managed_off.y; + geom->w=REGION_GEOM(scr).w+scr->managed_off.w; + geom->h=REGION_GEOM(scr).h+scr->managed_off.h; + geom->w=maxof(geom->w, 0); + geom->h=maxof(geom->h, 0); +} + + +static bool screen_handle_drop(WScreen *scr, int x, int y, WRegion *dropped) +{ + WRegion *curr=mplex_mx_current(&(scr->mplex)); + + /* This code should handle dropping tabs on floating workspaces. */ + if(curr && HAS_DYN(curr, region_handle_drop)){ + int rx, ry; + region_rootpos(curr, &rx, &ry); + if(rectangle_contains(®ION_GEOM(curr), x-rx, y-ry)){ + if(region_handle_drop(curr, x, y, dropped)) + return TRUE; + } + } + + /* Do not attach to ourselves unlike generic WMPlex. */ + return FALSE; +} + + +/*}}}*/ + + +/*{{{ Region dynfun implementations */ + + +static bool screen_fitrep(WScreen *scr, WWindow *par, const WFitParams *fp) +{ + WRegion *sub; + + if(par==NULL) + return FALSE; + + if(scr->uses_root) + return FALSE; + + return mplex_fitrep((WMPlex*)scr, NULL, fp); +} + + + + +static void screen_managed_changed(WScreen *scr, int mode, bool sw, + WRegion *reg_) +{ + if(ioncore_g.opmode==IONCORE_OPMODE_DEINIT) + return; + + if(sw && scr->atom_workspace!=None){ + WRegion *reg=mplex_mx_current(&(scr->mplex)); + const char *n=NULL; + + if(reg!=NULL) + n=region_displayname(reg); + + xwindow_set_string_property(region_root_of((WRegion*)scr), + scr->atom_workspace, + n==NULL ? "" : n); + } + + screen_update_infowin(scr); + + mplex_call_changed_hook((WMPlex*)scr, + screen_managed_changed_hook, + mode, sw, reg_); +} + + +static void screen_map(WScreen *scr) +{ + if(scr->uses_root) + return; + mplex_map((WMPlex*)scr); +} + + +static void screen_unmap(WScreen *scr) +{ + if(scr->uses_root) + return; + mplex_unmap((WMPlex*)scr); +} + +void screen_inactivated(WScreen *scr) +{ + screen_update_infowin(scr); +} + + +void screen_activated(WScreen *scr) +{ + screen_update_infowin(scr); +} + + +/*}}}*/ + + +/*}}}*/ + + +/*{{{ Notifications */ + + +static void do_notify(WScreen *scr, Watch *watch, bool right, + const char *str, + char *style, const char *attr) +{ + + WInfoWin *iw=(WInfoWin*)(watch->obj); + WFitParams fp; + + if(iw==NULL){ + WMPlexAttachParams param=MPLEXATTACHPARAMS_INIT; + + param.flags=(MPLEX_ATTACH_UNNUMBERED| + MPLEX_ATTACH_SIZEPOLICY| + MPLEX_ATTACH_GEOM| + MPLEX_ATTACH_LEVEL); + param.level=STACKING_LEVEL_ON_TOP; + + if(!right){ + param.szplcy=SIZEPOLICY_GRAVITY_NORTHWEST; + param.geom.x=0; + }else{ + param.szplcy=SIZEPOLICY_GRAVITY_NORTHEAST; + param.geom.x=REGION_GEOM(scr).w-1; + } + + param.geom.y=0; + param.geom.w=1; + param.geom.h=1; + + iw=(WInfoWin*)mplex_do_attach_new(&scr->mplex, ¶m, + (WRegionCreateFn*)create_infowin, + style); + + if(iw==NULL) + return; + + watch_setup(watch, (Obj*)iw, NULL); + } + + infowin_set_attr2(iw, attr, NULL); + infowin_set_text(iw, str); +} + + +void screen_notify(WScreen *scr, const char *str) +{ + do_notify(scr, &scr->notifywin_watch, FALSE, str, "actnotify", NULL); +} + + +void screen_windowinfo(WScreen *scr, const char *str, const char *attr) +{ + do_notify(scr, &scr->infowin_watch, TRUE, str, "tab-info", attr); +} + + +void screen_unnotify(WScreen *scr) +{ + Obj *iw=scr->notifywin_watch.obj; + if(iw!=NULL){ + watch_reset(&(scr->notifywin_watch)); + mainloop_defer_destroy(iw); + } +} + + +void screen_nowindowinfo(WScreen *scr) +{ + Obj *iw=scr->infowin_watch.obj; + if(iw!=NULL){ + watch_reset(&(scr->infowin_watch)); + mainloop_defer_destroy(iw); + } +} + + +static char *addnot(char *str, WRegion *reg) +{ + const char *nm=region_name(reg); + char *nstr=NULL; + + if(nm==NULL) + return str; + + if(str==NULL) + return scat(TR("act: "), nm); + + nstr=scat3(str, ", ", nm); + if(nstr!=NULL) + free(str); + return nstr; +} + + +static char *screen_managed_activity(WScreen *scr) +{ + char *notstr=NULL; + WMPlexIterTmp tmp; + WRegion *reg; + + FOR_ALL_MANAGED_BY_MPLEX(&scr->mplex, reg, tmp){ + if(region_is_activity_r(reg) && !REGION_IS_MAPPED(reg)) + notstr=addnot(notstr, reg); + } + + return notstr; +} + + +static void screen_notify_activity(WScreen *scr) +{ + if(ioncore_g.screen_notify){ + char *notstr=screen_managed_activity(scr); + if(notstr!=NULL){ + screen_notify(scr, notstr); + free(notstr); + return; + } + } + + screen_unnotify(scr); + + screen_update_infowin(scr); +} + + +static void screen_notify_tag(WScreen *scr) +{ + screen_update_infowin(scr); +} + + +static void screen_update_infowin(WScreen *scr) +{ + WRegion *reg=mplex_mx_current(&(scr->mplex)); + bool tag=(reg!=NULL && region_is_tagged(reg)); + bool act=(reg!=NULL && region_is_activity_r(reg)); + + if(tag || act){ + const char *n=region_displayname(reg); + char *attr=NULL; + + libtu_asprintf(&attr, "%s-selected-%s-not_dragged-%s", + (REGION_IS_ACTIVE(scr) ? "active" : "inactive"), + (tag ? "tagged" : "not_tagged"), + (act ? "activity" : "no_activity")); + + screen_windowinfo(scr, n, attr); /* NULL attr ok */ + }else{ + screen_nowindowinfo(scr); + } +} + + +static void screen_managed_notify(WScreen *scr, WRegion *reg, const char *how) +{ + if(strcmp(how, "sub-activity")==0){ + /* TODO: multiple calls */ + mainloop_defer_action((Obj*)scr, + (WDeferredAction*)screen_notify_activity); + }else if(strcmp(how, "tag")==0){ + mainloop_defer_action((Obj*)scr, + (WDeferredAction*)screen_notify_tag); + } +} + + +/*}}}*/ + + +/*{{{ Misc. */ + + +/*EXTL_DOC + * Find the screen with numerical id \var{id}. If Xinerama is + * not present, \var{id} corresponds to X screen numbers. Otherwise + * the ids are some arbitrary ordering of Xinerama rootwins. + * If \var{id} is $-1$, the screen with the highest id is returned. + */ +EXTL_SAFE +EXTL_EXPORT +WScreen *ioncore_find_screen_id(int id) +{ + WScreen *scr, *maxscr=NULL; + + FOR_ALL_SCREENS(scr){ + if(id==-1){ + if(maxscr==NULL || scr->id>maxscr->id) + maxscr=scr; + } + if(scr->id==id) + return scr; + } + + return maxscr; +} + + +/*EXTL_DOC + * Switch focus to the screen with id \var{id} and return it. + * + * Note that this function is asynchronous; the screen will not + * actually have received the focus when this function returns. + */ +EXTL_EXPORT +WScreen *ioncore_goto_nth_screen(int id) +{ + WScreen *scr=ioncore_find_screen_id(id); + if(scr!=NULL){ + if(!region_goto((WRegion*)scr)) + return NULL; + } + return scr; +} + + +static WScreen *current_screen() +{ + if(ioncore_g.focus_current==NULL) + return ioncore_g.screens; + else + return region_screen_of(ioncore_g.focus_current); +} + + +/*EXTL_DOC + * Switch focus to the next screen and return it. + * + * Note that this function is asynchronous; the screen will not + * actually have received the focus when this function returns. + */ +EXTL_EXPORT +WScreen *ioncore_goto_next_screen() +{ + WScreen *scr=current_screen(); + + if(scr!=NULL) + scr=scr->next_scr; + if(scr==NULL) + scr=ioncore_g.screens; + if(scr!=NULL){ + if(!region_goto((WRegion*)scr)) + return NULL; + } + return scr; +} + + +/*EXTL_DOC + * Switch focus to the previous screen and return it. + * + * Note that this function is asynchronous; the screen will not + * actually have received the focus when this function returns. + */ +EXTL_EXPORT +WScreen *ioncore_goto_prev_screen() +{ + WScreen *scr=current_screen(); + + if(scr!=NULL) + scr=scr->prev_scr; + else + scr=ioncore_g.screens; + if(scr!=NULL){ + if(!region_goto((WRegion*)scr)) + return NULL; + } + return scr; +} + + +/*EXTL_DOC + * Return the numerical id for screen \var{scr}. + */ +EXTL_SAFE +EXTL_EXPORT_MEMBER +int screen_id(WScreen *scr) +{ + return scr->id; +} + + +static bool screen_managed_may_destroy(WScreen *scr, WRegion *reg) +{ + bool onmxlist=FALSE; + WLListNode *lnode; + WLListIterTmp tmp; + + if(OBJ_IS(reg, WClientWin)) + return TRUE; + + FOR_ALL_NODES_ON_LLIST(lnode, scr->mplex.mx_list, tmp){ + if(lnode->st->reg==reg) + onmxlist=TRUE; + else /*if(OBJ_IS(node->reg, WGenWS))*/ + return TRUE; + } + + if(!onmxlist) + return TRUE; + + warn(TR("Only workspace may not be destroyed.")); + + return FALSE; +} + + +static bool screen_may_destroy(WScreen *scr) +{ + warn(TR("Screens may not be destroyed.")); + return FALSE; +} + + + +void screen_set_managed_offset(WScreen *scr, const WRectangle *off) +{ + scr->managed_off=*off; + mplex_fit_managed((WMPlex*)scr); +} + + +/*EXTL_DOC + * Set offset of objects managed by the screen from actual screen geometry. + * The table \var{offset} should contain the entries \code{x}, \code{y}, + * \code{w} and \code{h} indicating offsets of that component of screen + * geometry. + */ +EXTL_EXPORT_AS(WScreen, set_managed_offset) +bool screen_set_managed_offset_extl(WScreen *scr, ExtlTab offset) +{ + WRectangle g; + + if(!extl_table_to_rectangle(offset, &g)) + goto err; + + if(-g.w>=REGION_GEOM(scr).w) + goto err; + if(-g.h>=REGION_GEOM(scr).h) + goto err; + + screen_set_managed_offset(scr, &g); + + return TRUE; +err: + warn(TR("Invalid offset.")); + return FALSE; +} + + +WPHolder *screen_get_rescue_pholder_for(WScreen *scr, WRegion *mgd) +{ +#warning "TODO: better special case handling for groups" + + return (WPHolder*)mplex_get_rescue_pholder_for(&(scr->mplex), mgd); +} + +/*}}}*/ + + +/*{{{ Save/load */ + + +ExtlTab screen_get_configuration(WScreen *scr) +{ + return mplex_get_configuration(&scr->mplex); +} + + +static WRegion *do_create_initial(WWindow *parent, const WFitParams *fp, + WRegionLoadCreateFn *fn) +{ + return fn(parent, fp, extl_table_none()); +} + + +static bool create_initial_ws(WScreen *scr) +{ + WRegion *reg=NULL; + WMPlexAttachParams par; + + par.flags=0; + + reg=mplex_do_attach_new(&scr->mplex, &par, + (WRegionCreateFn*)groupws_load_default, + NULL); + + if(reg==NULL){ + warn(TR("Unable to create a workspace on screen %d."), scr->id); + return FALSE; + } + + return TRUE; +} + + +bool screen_init_layout(WScreen *scr, ExtlTab tab) +{ + char *name; + ExtlTab substab, subtab; + int n, i; + + if(tab==extl_table_none()) + return create_initial_ws(scr); + + mplex_load_contents(&scr->mplex, tab); + + return TRUE; +} + +/*}}}*/ + + +/*{{{ Dynamic function table and class implementation */ + + +static DynFunTab screen_dynfuntab[]={ + {region_map, + screen_map}, + + {region_unmap, + screen_unmap}, + + {region_activated, + screen_activated}, + + {region_inactivated, + screen_inactivated}, + + {(DynFun*)region_managed_may_destroy, + (DynFun*)screen_managed_may_destroy}, + + {(DynFun*)region_may_destroy, + (DynFun*)screen_may_destroy}, + + {mplex_managed_changed, + screen_managed_changed}, + + {region_managed_notify, + screen_managed_notify}, + + {mplex_managed_geom, + screen_managed_geom}, + + {(DynFun*)region_get_configuration, + (DynFun*)screen_get_configuration}, + + {(DynFun*)region_handle_drop, + (DynFun*)screen_handle_drop}, + + {(DynFun*)region_fitrep, + (DynFun*)screen_fitrep}, + + {(DynFun*)region_get_rescue_pholder_for, + (DynFun*)screen_get_rescue_pholder_for}, + + END_DYNFUNTAB +}; + + +EXTL_EXPORT +IMPLCLASS(WScreen, WMPlex, screen_deinit, screen_dynfuntab); + + +/*}}}*/ diff --git a/ioncore/screen.h b/ioncore/screen.h new file mode 100644 index 0000000..11c83fa --- /dev/null +++ b/ioncore/screen.h @@ -0,0 +1,83 @@ +/* + * ion/ioncore/screen.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_SCREEN_H +#define ION_IONCORE_SCREEN_H + +#include +#include +#include "common.h" +#include "mplex.h" +#include "rectangle.h" +#include "rootwin.h" +#include "pholder.h" + +#define FOR_ALL_SCREENS(SCR) \ + for((SCR)=ioncore_g.screens; \ + (SCR)!=NULL; \ + (SCR)=(SCR)->next_scr) + +#define FOR_ALL_SCREENS_W_NEXT(SCR, NXT) \ + for((SCR)=ioncore_g.screens, NXT=((SCR) ? (SCR)->next_scr : NULL); \ + (SCR)!=NULL; \ + (SCR)=(NXT), NXT=((SCR) ? (SCR)->next_scr : NULL)) + +enum{ + SCREEN_ROTATION_0, + SCREEN_ROTATION_90, + SCREEN_ROTATION_180, + SCREEN_ROTATION_270 +}; + + +DECLCLASS(WScreen){ + WMPlex mplex; + int id; + Atom atom_workspace; + bool uses_root; + int rotation; + WRectangle managed_off; + WScreen *next_scr, *prev_scr; + Watch notifywin_watch; + Watch infowin_watch; +}; + +extern WScreen *create_screen(WRootWin *rootwin, int id, + const WFitParams *fp, + bool useroot); + +extern int screen_id(WScreen *scr); + +extern void screen_set_managed_offset(WScreen *scr, const WRectangle *off); + +extern bool screen_init_layout(WScreen *scr, ExtlTab tab); + +extern void screen_notify(WScreen *scr, const char *notstr); +extern void screen_unnotify(WScreen *scr); +extern void screen_windowinfo(WScreen *scr, const char *name, const char *attr); +extern void screen_nowindowinfo(WScreen *scr); + +extern WPHolder *screen_get_rescue_pholder_for(WScreen *scr, WRegion *mgd); + +/* For viewports corresponding to Xinerama rootwins is initially set + * to the Xinerama screen number. When Xinerama is not enabled, is + * the X screen number (which is the same for all Xinerama rootwins). + * For all other viewports is undefined. + */ +extern WScreen *ioncore_find_screen_id(int id); +extern WScreen *ioncore_goto_screen_id(int id); +extern WScreen *ioncore_goto_next_screen(); +extern WScreen *ioncore_goto_prev_screen(); + +/* Handlers of this hook receive a WScreen* as the sole parameter. */ +extern WHook *screen_managed_changed_hook; + +#endif /* ION_IONCORE_SCREEN_H */ diff --git a/ioncore/selection.c b/ioncore/selection.c new file mode 100644 index 0000000..4bb8cd7 --- /dev/null +++ b/ioncore/selection.c @@ -0,0 +1,187 @@ +/* + * ion/ioncore/selection.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include + +#include "common.h" +#include "global.h" +#include "property.h" +#include "xwindow.h" +#include + + +static char *selection_data=NULL; +static int selection_length; +static bool continuation_set=FALSE; +static ExtlFn continuation; + +void ioncore_handle_selection_request(XSelectionRequestEvent *ev) +{ + XSelectionEvent sev; + const char *p[1]; + + if(selection_data==NULL) + return; + + p[0]=selection_data; + + xwindow_set_text_property(ev->requestor, ev->property, p, 1); + + sev.type=SelectionNotify; + sev.requestor=ev->requestor; + sev.selection=ev->selection; + sev.target=ev->target; + sev.time=ev->time; + sev.property=ev->property; + XSendEvent(ioncore_g.dpy, ev->requestor, False, 0L, (XEvent*)&sev); +} + + +static void ins(Window win, const char *str, int n) +{ + if(!continuation_set){ + WWindow *wwin=XWINDOW_REGION_OF_T(win, WWindow); + if(wwin) + window_insstr(wwin, str, n); + }else{ + char *tmp=scopyn(str, n); + if(tmp!=NULL){ + extl_call(continuation, "s", NULL, tmp); + free(tmp); + } + } +} + + +static void insert_selection(Window win, Atom prop) +{ + char **p=xwindow_get_text_property(win, prop, NULL); + if(p!=NULL){ + ins(win, p[0], strlen(p[0])); + XFreeStringList(p); + } +} + + +static void insert_cutbuffer(Window win) +{ + char *p; + int n; + + p=XFetchBytes(ioncore_g.dpy, &n); + + if(n<=0 || p==NULL) + return; + + ins(win, p, n); +} + + +void ioncore_handle_selection(XSelectionEvent *ev) +{ + Atom prop=ev->property; + Window win=ev->requestor; + WWindow *wwin; + + if(prop==None){ + insert_cutbuffer(win); + }else{ + insert_selection(win, prop); + XDeleteProperty(ioncore_g.dpy, win, prop); + } + + if(continuation_set){ + extl_unref_fn(continuation); + continuation_set=FALSE; + } +} + + +void ioncore_clear_selection() +{ + if(selection_data!=NULL){ + free(selection_data); + selection_data=NULL; + } +} + + +void ioncore_set_selection_n(const char *p, int n) +{ + if(selection_data!=NULL) + free(selection_data); + + selection_data=ALLOC_N(char, n+1); + + if(selection_data==NULL) + return; + + memcpy(selection_data, p, n); + selection_data[n]='\0'; + selection_length=n; + + XStoreBytes(ioncore_g.dpy, p, n); + + XSetSelectionOwner(ioncore_g.dpy, XA_PRIMARY, + DefaultRootWindow(ioncore_g.dpy), + CurrentTime); +} + + +/*EXTL_DOC + * Set primary selection and cutbuffer0 to \var{p}. + */ +EXTL_EXPORT +void ioncore_set_selection(const char *p) +{ + if(p==NULL) + ioncore_clear_selection(); + else + ioncore_set_selection_n(p, strlen(p)); +} + + +void ioncore_request_selection_for(Window win) +{ + Atom a=XA_STRING; + + if(continuation_set){ + extl_unref_fn(continuation); + continuation_set=FALSE; + } + + if(ioncore_g.use_mb){ +#ifdef X_HAVE_UTF8_STRING + a=XInternAtom(ioncore_g.dpy, "UTF8_STRING", True); +#else + a=XInternAtom(ioncore_g.dpy, "COMPOUND_TEXT", True); +#endif + } + + XConvertSelection(ioncore_g.dpy, XA_PRIMARY, a, + ioncore_g.atom_selection, win, CurrentTime); +} + + +/*EXTL_DOC + * Request (string) selection. The function \var{fn} will be called + * with the selection when and if it is received. + */ +EXTL_EXPORT +void ioncore_request_selection(ExtlFn fn) +{ + assert(ioncore_g.rootwins!=NULL); + ioncore_request_selection_for(ioncore_g.rootwins->dummy_win); + continuation=extl_ref_fn(fn); + continuation_set=TRUE; +} + diff --git a/ioncore/selection.h b/ioncore/selection.h new file mode 100644 index 0000000..6a49786 --- /dev/null +++ b/ioncore/selection.h @@ -0,0 +1,24 @@ +/* + * ion/ioncore/selection.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_SELECTION_H +#define ION_IONCORE_SELECTION_H + +#include "common.h" + +void ioncore_handle_selection_request(XSelectionRequestEvent *ev); +void ioncore_handle_selection(XSelectionEvent *ev); +void ioncore_clear_selection(); +void ioncore_set_selection_n(const char *p, int n); +void ioncore_set_selection(const char *p); +void ioncore_request_selection_for(Window win); + +#endif /* ION_IONCORE_SELECTION_H */ diff --git a/ioncore/sizehint.c b/ioncore/sizehint.c new file mode 100644 index 0000000..50c1c99 --- /dev/null +++ b/ioncore/sizehint.c @@ -0,0 +1,311 @@ +/* + * ion/ioncore/sizehint.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include + +#include "common.h" +#include "global.h" +#include "region.h" +#include "resize.h" +#include "sizehint.h" +#include "rootwin.h" + + +/*{{{ xsizehints_correct */ + + +static void do_correct_aspect(int max_w, int max_h, int ax, int ay, + int *wret, int *hret) +{ + int w=*wret, h=*hret; + + if(ax>ay){ + h=(w*ay)/ax; + if(max_h>0 && h>max_h){ + h=max_h; + w=(h*ax)/ay; + } + }else{ + w=(h*ax)/ay; + if(max_w>0 && w>max_w){ + w=max_w; + h=(w*ay)/ax; + } + } + + *wret=w; + *hret=h; +} + + +static void correct_aspect(int max_w, int max_h, const WSizeHints *hints, + int *wret, int *hret) +{ + if(!hints->aspect_set) + return; + + if(*wret*hints->max_aspect.y>*hret*hints->max_aspect.x){ + do_correct_aspect(max_w, max_h, + hints->min_aspect.x, hints->min_aspect.y, + wret, hret); + } + + if(*wret*hints->min_aspect.y<*hret*hints->min_aspect.x){ + do_correct_aspect(max_w, max_h, + hints->max_aspect.x, hints->max_aspect.y, + wret, hret); + } +} + + +void sizehints_correct(const WSizeHints *hints, int *wp, int *hp, + bool min, bool override_no_constrain) +{ + int w=*wp; + int h=*hp; + int bs=0; + + if(min){ + w=maxof(w, hints->min_width); + h=maxof(h, hints->min_height); + } + + if(hints->no_constrain && !override_no_constrain){ + *wp=w; + *hp=h; + return; + } + + if(w>=hints->min_width && h>=hints->min_height) + correct_aspect(w, h, hints, &w, &h); + + if(hints->max_set){ + w=minof(w, hints->max_width); + h=minof(h, hints->max_height); + } + + if(hints->inc_set){ + /* base size should be set to 0 if none given by user program */ + bs=(hints->base_set ? hints->base_width : 0); + if(w>bs) + w=((w-bs)/hints->width_inc)*hints->width_inc+bs; + bs=(hints->base_set ? hints->base_height : 0); + if(h>bs) + h=((h-bs)/hints->height_inc)*hints->height_inc+bs; + } + + *wp=w; + *hp=h; +} + + +/*}}}*/ + + +/*{{{ X size hints sanity adjustment */ + + +void xsizehints_sanity_adjust(XSizeHints *hints) +{ + if(!(hints->flags&PMinSize)){ + if(hints->flags&PBaseSize){ + hints->min_width=hints->base_width; + hints->min_height=hints->base_height; + }else{ + hints->min_width=0; + hints->min_height=0; + } + } + + if(hints->min_width<0) + hints->min_width=0; + if(hints->min_height<0) + hints->min_height=0; + + if(!(hints->flags&PBaseSize) || hints->base_width<0) + hints->base_width=hints->min_width; + if(!(hints->flags&PBaseSize) || hints->base_height<0) + hints->base_height=hints->min_height; + + + if(hints->flags&PMaxSize){ + if(hints->max_widthmin_width) + hints->max_width=hints->min_width; + if(hints->max_heightmin_height) + hints->max_height=hints->min_height; + } + + hints->flags|=(PBaseSize|PMinSize); + + if(hints->flags&PResizeInc){ + if(hints->width_inc<=0 || hints->height_inc<=0){ + warn(TR("Invalid client-supplied width/height increment.")); + hints->flags&=~PResizeInc; + } + } + + if(hints->flags&PAspect){ + if(hints->min_aspect.x<=0 || hints->min_aspect.y<=0 || + hints->min_aspect.x<=0 || hints->min_aspect.y<=0){ + warn(TR("Invalid client-supplied aspect-ratio.")); + hints->flags&=~PAspect; + } + } + + if(!(hints->flags&PWinGravity)) + hints->win_gravity=ForgetGravity; +} + + +/*}}}*/ + + +/*{{{ xsizehints_adjust_for */ + + +void sizehints_adjust_for(WSizeHints *hints, WRegion *reg) +{ + WSizeHints tmp_hints; + + region_size_hints(reg, &tmp_hints); + + if(tmp_hints.min_set){ + if(!hints->min_set){ + hints->min_set=TRUE; + hints->min_width=tmp_hints.min_width; + hints->min_height=tmp_hints.min_height; + }else{ + hints->min_width=maxof(hints->min_width, + tmp_hints.min_width); + hints->min_height=maxof(hints->min_height, + tmp_hints.min_height); + } + } + + if(tmp_hints.max_set && hints->max_set){ + hints->max_width=maxof(hints->max_width, + tmp_hints.max_width); + hints->max_height=maxof(hints->max_height, + tmp_hints.max_height); + }else{ + hints->max_set=FALSE; + } +} + + +/*}}}*/ + + +/*{{{ account_gravity */ + + +int xgravity_deltax(int gravity, int left, int right) +{ + int woff=left+right; + + if(gravity==StaticGravity || gravity==ForgetGravity){ + return -left; + }else if(gravity==NorthWestGravity || gravity==WestGravity || + gravity==SouthWestGravity){ + /* */ + }else if(gravity==NorthEastGravity || gravity==EastGravity || + gravity==SouthEastGravity){ + /* geom->x=geom->w+geom->x-(geom->w+woff) */ + return -woff; + }else if(gravity==CenterGravity || gravity==NorthGravity || + gravity==SouthGravity){ + /* geom->x=geom->x+geom->w/2-(geom->w+woff)/2 */ + return -woff/2; + } + return 0; +} + + +int xgravity_deltay(int gravity, int top, int bottom) +{ + int hoff=top+bottom; + + if(gravity==StaticGravity || gravity==ForgetGravity){ + return -top; + }else if(gravity==NorthWestGravity || gravity==NorthGravity || + gravity==NorthEastGravity){ + /* */ + }else if(gravity==SouthWestGravity || gravity==SouthGravity || + gravity==SouthEastGravity){ + /* geom->y=geom->y+geom->h-(geom->h+hoff) */ + return -hoff; + }else if(gravity==CenterGravity || gravity==WestGravity || + gravity==EastGravity){ + /* geom->y=geom->y+geom->h/2-(geom->h+hoff)/2 */ + return -hoff/2; + } + return 0; +} + + +void xgravity_translate(int gravity, WRegion *reg, WRectangle *geom) +{ + int top=0, left=0, bottom=0, right=0; + WRootWin *root; + + root=region_rootwin_of(reg); + region_rootpos(reg, &left, &top); + right=REGION_GEOM(root).w-left-REGION_GEOM(reg).w; + bottom=REGION_GEOM(root).h-top-REGION_GEOM(reg).h; + + geom->x+=xgravity_deltax(gravity, left, right); + geom->y+=xgravity_deltay(gravity, top, bottom); +} + + +/*}}}*/ + + +/*{{{ Init */ + + +void xsizehints_to_sizehints(const XSizeHints *xh, WSizeHints *hints) +{ + hints->max_width=xh->max_width; + hints->max_height=xh->max_height; + hints->min_width=xh->min_width; + hints->min_height=xh->min_height; + hints->base_width=xh->base_width; + hints->base_height=xh->base_height; + hints->width_inc=xh->width_inc; + hints->height_inc=xh->height_inc; + hints->min_aspect.x=xh->min_aspect.x; + hints->min_aspect.y=xh->min_aspect.y; + hints->max_aspect.x=xh->max_aspect.x; + hints->max_aspect.y=xh->max_aspect.y; + + hints->max_set=((xh->flags&PMaxSize)!=0); + hints->min_set=((xh->flags&PMinSize)!=0); + hints->base_set=((xh->flags&PBaseSize)!=0); + hints->inc_set=((xh->flags&PResizeInc)!=0); + hints->aspect_set=((xh->flags&PAspect)!=0); + hints->no_constrain=0; +} + + +void sizehints_clear(WSizeHints *hints) +{ + hints->max_set=0; + hints->min_set=0; + hints->inc_set=0; + hints->base_set=0; + hints->aspect_set=0; + hints->no_constrain=0; +} + + +/*}}}*/ diff --git a/ioncore/sizehint.h b/ioncore/sizehint.h new file mode 100644 index 0000000..6065e74 --- /dev/null +++ b/ioncore/sizehint.h @@ -0,0 +1,54 @@ +/* + * ion/ioncore/sizehint.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_SIZEHINT_H +#define ION_IONCORE_SIZEHINT_H + +#include "common.h" +#include "region.h" + +INTRSTRUCT(WSizeHints); + +DECLSTRUCT(WSizeHints){ + uint min_set:1; + uint max_set:1; + uint inc_set:1; + uint base_set:1; + uint aspect_set:1; + uint no_constrain:1; + + int min_width, min_height; + int max_width, max_height; + int width_inc, height_inc; + struct { + int x; + int y; + } min_aspect, max_aspect; + int base_width, base_height; +}; + +extern void xsizehints_to_sizehints(const XSizeHints *xh, + WSizeHints *hints); + +extern void xsizehints_sanity_adjust(XSizeHints *hints); + +extern void sizehints_clear(WSizeHints *hints); + +extern void sizehints_adjust_for(WSizeHints *hints, WRegion *reg); + +extern void sizehints_correct(const WSizeHints *hints, int *wp, int *hp, + bool min, bool override_no_constrain); + +extern int xgravity_deltax(int gravity, int left, int right); +extern int xgravity_deltay(int gravity, int top, int bottom); +extern void xgravity_translate(int gravity, WRegion *reg, WRectangle *geom); + +#endif /* ION_IONCORE_SIZEHINT_H */ diff --git a/ioncore/sizepolicy.c b/ioncore/sizepolicy.c new file mode 100644 index 0000000..6e23875 --- /dev/null +++ b/ioncore/sizepolicy.c @@ -0,0 +1,340 @@ +/* + * ion/ioncore/sizepolicy.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include + +#include "common.h" +#include "region.h" +#include "resize.h" +#include "sizehint.h" +#include "sizepolicy.h" + + + +static int fit_x(int x, int w, const WRectangle *max_geom) +{ + int mw=maxof(max_geom->w, 1); + w=minof(mw, w); + return minof(maxof(x, max_geom->x), max_geom->x+mw-w); +} + + +static int fit_y(int y, int h, const WRectangle *max_geom) +{ + int mh=maxof(max_geom->h, 1); + h=minof(mh, h); + return minof(maxof(y, max_geom->y), max_geom->y+mh-h); +} + + +static void do_gravity(const WRectangle *max_geom, int szplcy, + WRectangle *geom) +{ + /* Assumed: width and height already adjusted within limits */ + if(geom->h<1) + geom->h=1; + if(geom->w<1) + geom->w=1; + + switch(szplcy&SIZEPOLICY_HORIZ_MASK){ + case SIZEPOLICY_HORIZ_LEFT: + geom->x=max_geom->x; + break; + + case SIZEPOLICY_HORIZ_RIGHT: + geom->x=max_geom->x+max_geom->w-geom->w; + break; + + case SIZEPOLICY_HORIZ_CENTER: + geom->x=max_geom->x+max_geom->w/2-geom->w/2; + break; + + default: + geom->x=fit_x(geom->x, geom->w, max_geom); + } + + switch(szplcy&SIZEPOLICY_VERT_MASK){ + case SIZEPOLICY_VERT_TOP: + geom->y=max_geom->y; + break; + + case SIZEPOLICY_VERT_BOTTOM: + geom->y=max_geom->y+max_geom->h-geom->h; + break; + + case SIZEPOLICY_VERT_CENTER: + geom->y=max_geom->y+max_geom->h/2-geom->h/2; + break; + + default: + geom->y=fit_x(geom->y, geom->h, max_geom); + } +} + + +static void gravity_stretch_policy(int szplcy, WRegion *reg, + const WRectangle *rq_geom, WFitParams *fp, + bool ws, bool hs) +{ + WRectangle max_geom=fp->g; + int w, h; + + fp->g=*rq_geom; + + w=(ws ? max_geom.w : minof(rq_geom->w, max_geom.w)); + h=(hs ? max_geom.h : minof(rq_geom->h, max_geom.h)); + + if(reg!=NULL) + region_size_hints_correct(reg, &w, &h, FALSE); + + fp->g.w=w; + fp->g.h=h; + + do_gravity(&max_geom, szplcy, &(fp->g)); +} + + +static void sizepolicy_free_snap(WSizePolicy *szplcy, WRegion *reg, + WRectangle *rq_geom, int rq_flags, + WFitParams *fp) +{ + WRectangle max_geom=fp->g; + bool fullw=((rq_flags®ION_RQGEOM_WEAK_W) && + (*szplcy&SIZEPOLICY_HORIZ_MASK)==SIZEPOLICY_HORIZ_CENTER); + bool fullh=((rq_flags®ION_RQGEOM_WEAK_H) && + (*szplcy&SIZEPOLICY_VERT_MASK)==SIZEPOLICY_VERT_CENTER); + + int w=(fullw ? max_geom.w : minof(rq_geom->w, max_geom.w)); + int h=(fullh ? max_geom.h : minof(rq_geom->h, max_geom.h)); + int x_=0, y_=0; + + + if(!(rq_flags®ION_RQGEOM_WEAK_X) + && rq_flags®ION_RQGEOM_WEAK_W){ + x_=fit_x(rq_geom->x, 1, &max_geom); + if(((*szplcy)&SIZEPOLICY_HORIZ_MASK)==SIZEPOLICY_HORIZ_RIGHT) + w=max_geom.x+max_geom.w-x_; + else + w=minof(w, max_geom.x+max_geom.w-x_); + } + + if(!(rq_flags®ION_RQGEOM_WEAK_Y) + && rq_flags®ION_RQGEOM_WEAK_H){ + y_=fit_x(rq_geom->y, 1, &max_geom); + if(((*szplcy)&SIZEPOLICY_VERT_MASK)==SIZEPOLICY_VERT_BOTTOM) + h=max_geom.y+max_geom.h-y_; + else + h=minof(h, max_geom.y+max_geom.h-y_); + } + + if(reg!=NULL) + region_size_hints_correct(reg, &w, &h, FALSE); + + fp->g.w=w; + fp->g.h=h; + + if(!(rq_flags®ION_RQGEOM_WEAK_X) + && rq_flags®ION_RQGEOM_WEAK_W){ + fp->g.x=x_; + }else if(rq_flags®ION_RQGEOM_WEAK_X){ + switch((*szplcy)&SIZEPOLICY_HORIZ_MASK){ + case SIZEPOLICY_HORIZ_CENTER: + fp->g.x=max_geom.x+(max_geom.w-w)/2; + break; + + case SIZEPOLICY_HORIZ_LEFT: + fp->g.x=max_geom.x; + break; + + case SIZEPOLICY_HORIZ_RIGHT: + fp->g.x=max_geom.x+max_geom.w-w; + break; + + default: + fp->g.x=fit_x(rq_geom->x, w, &max_geom); + break; + } + }else{ + fp->g.x=fit_x(rq_geom->x, w, &max_geom); + } + + if(!(rq_flags®ION_RQGEOM_WEAK_Y) + && rq_flags®ION_RQGEOM_WEAK_H){ + fp->g.y=y_; + }else if(rq_flags®ION_RQGEOM_WEAK_Y){ + switch((*szplcy)&SIZEPOLICY_VERT_MASK){ + case SIZEPOLICY_VERT_CENTER: + fp->g.y=max_geom.y+(max_geom.h-h)/2; + break; + + case SIZEPOLICY_VERT_TOP: + fp->g.y=max_geom.y; + break; + + case SIZEPOLICY_VERT_BOTTOM: + fp->g.y=max_geom.y+max_geom.h-h; + break; + + default: + fp->g.y=fit_y(rq_geom->y, h, &max_geom); + break; + } + }else{ + fp->g.y=fit_y(rq_geom->y, h, &max_geom); + } + + (*szplcy)&=~(SIZEPOLICY_VERT_MASK|SIZEPOLICY_HORIZ_MASK); + + *szplcy|=( (fullw || fp->g.x<=max_geom.x ? SIZEPOLICY_HORIZ_LEFT : 0) + |(fullw || fp->g.x+fp->g.w>=max_geom.x+max_geom.w ? SIZEPOLICY_HORIZ_RIGHT : 0) + |(fullh || fp->g.y<=max_geom.y ? SIZEPOLICY_VERT_TOP : 0) + |(fullh || fp->g.y+fp->g.h>=max_geom.y+max_geom.h ? SIZEPOLICY_VERT_BOTTOM : 0)); +} + + +void sizepolicy(WSizePolicy *szplcy, WRegion *reg, + const WRectangle *rq_geom, int rq_flags, + WFitParams *fp) +{ + uint extra=fp->mode®ION_FIT_ROTATE; + + WRectangle tmp; + if(rq_geom!=NULL) + tmp=*rq_geom; + else if(reg!=NULL) + tmp=REGION_GEOM(reg); + else + tmp=fp->g; + + if((*szplcy)&SIZEPOLICY_SHRUNK){ + if(reg!=NULL){ + tmp.w=region_min_w(reg); + tmp.h=region_min_h(reg); + }else{ + tmp.w=1; + tmp.h=1; + } + rq_flags&=~(REGION_RQGEOM_WEAK_W|REGION_RQGEOM_WEAK_H); + } + + fp->mode=REGION_FIT_EXACT|extra; + + switch((*szplcy)&SIZEPOLICY_MASK){ + case SIZEPOLICY_GRAVITY: + gravity_stretch_policy(*szplcy, reg, &tmp, fp, FALSE, FALSE); + break; + + case SIZEPOLICY_STRETCH_LEFT: + gravity_stretch_policy(SIZEPOLICY_HORIZ_LEFT|SIZEPOLICY_VERT_CENTER, + reg, &tmp, fp, FALSE, TRUE); + break; + + case SIZEPOLICY_STRETCH_RIGHT: + gravity_stretch_policy(SIZEPOLICY_HORIZ_RIGHT|SIZEPOLICY_VERT_CENTER, + reg, &tmp, fp, FALSE, TRUE); + break; + + case SIZEPOLICY_STRETCH_TOP: + gravity_stretch_policy(SIZEPOLICY_VERT_TOP|SIZEPOLICY_HORIZ_CENTER, + reg, &tmp, fp, TRUE, FALSE); + break; + + case SIZEPOLICY_STRETCH_BOTTOM: + gravity_stretch_policy(SIZEPOLICY_VERT_BOTTOM|SIZEPOLICY_HORIZ_CENTER, + reg, &tmp, fp, TRUE, FALSE); + break; + + case SIZEPOLICY_FULL_EXACT: + gravity_stretch_policy(SIZEPOLICY_VERT_CENTER|SIZEPOLICY_HORIZ_CENTER, + reg, &tmp, fp, TRUE, TRUE); + break; + + case SIZEPOLICY_FREE: + rectangle_constrain(&tmp, &(fp->g)); + if(reg!=NULL) + region_size_hints_correct(reg, &tmp.w, &tmp.h, FALSE); + fp->g=tmp; + break; + + case SIZEPOLICY_UNCONSTRAINED: + if(reg!=NULL) + region_size_hints_correct(reg, &tmp.w, &tmp.h, TRUE); + fp->g=tmp; + break; + + case SIZEPOLICY_FREE_GLUE: + sizepolicy_free_snap(szplcy, reg, &tmp, rq_flags, fp); + break; + + case SIZEPOLICY_FULL_BOUNDS: + default: + fp->mode=REGION_FIT_BOUNDS|extra; + break; + } +} + + +struct szplcy_spec { + const char *spec; + int szplcy; +}; + + +/* translation table for sizepolicy specifications */ +static struct szplcy_spec szplcy_specs[] = { + {"default", SIZEPOLICY_DEFAULT}, + {"full", SIZEPOLICY_FULL_EXACT}, + {"full_bounds", SIZEPOLICY_FULL_BOUNDS}, + {"free", SIZEPOLICY_FREE}, + {"free_glue", SIZEPOLICY_FREE_GLUE}, + {"northwest", SIZEPOLICY_GRAVITY_NORTHWEST}, + {"north", SIZEPOLICY_GRAVITY_NORTH}, + {"northeast", SIZEPOLICY_GRAVITY_NORTHEAST}, + {"west", SIZEPOLICY_GRAVITY_WEST}, + {"center", SIZEPOLICY_GRAVITY_CENTER}, + {"east", SIZEPOLICY_GRAVITY_EAST}, + {"southwest", SIZEPOLICY_GRAVITY_SOUTHWEST}, + {"south", SIZEPOLICY_GRAVITY_SOUTH}, + {"southeast", SIZEPOLICY_GRAVITY_SOUTHEAST}, + {"stretch_top", SIZEPOLICY_STRETCH_TOP}, + {"stretch_bottom", SIZEPOLICY_STRETCH_BOTTOM}, + {"stretch_left", SIZEPOLICY_STRETCH_LEFT}, + {"stretch_right", SIZEPOLICY_STRETCH_RIGHT}, + {"free_glue_northwest", SIZEPOLICY_FREE_GLUE__NORTHWEST}, + {"free_glue_north", SIZEPOLICY_FREE_GLUE__NORTH}, + {"free_glue_northeast", SIZEPOLICY_FREE_GLUE__NORTHEAST}, + {"free_glue_west", SIZEPOLICY_FREE_GLUE__WEST}, + {"free_glue_center", SIZEPOLICY_FREE_GLUE__CENTER}, + {"free_glue_east", SIZEPOLICY_FREE_GLUE__EAST}, + {"free_glue_southwest", SIZEPOLICY_FREE_GLUE__SOUTHWEST}, + {"free_glue_south", SIZEPOLICY_FREE_GLUE__SOUTH}, + {"free_glue_southeast", SIZEPOLICY_FREE_GLUE__SOUTHEAST}, + { NULL, SIZEPOLICY_DEFAULT} /* end marker */ +}; + + +bool string2sizepolicy(const char *szplcy, WSizePolicy *value) +{ + const struct szplcy_spec *sp; + + *value=SIZEPOLICY_DEFAULT; + + for(sp=szplcy_specs; sp->spec; ++sp){ + if(strcasecmp(szplcy,sp->spec)==0){ + *value=sp->szplcy; + return TRUE; + } + } + + return FALSE; +} + diff --git a/ioncore/sizepolicy.h b/ioncore/sizepolicy.h new file mode 100644 index 0000000..0984737 --- /dev/null +++ b/ioncore/sizepolicy.h @@ -0,0 +1,78 @@ +/* + * ion/ioncore/sizepolicy.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_SIZEPOLICY_H +#define ION_IONCORE_SIZEPOLICY_H + +#include "common.h" +#include "region.h" + +/* Modifiers for some policies */ +#define SIZEPOLICY_VERT_NONE 0x0000 +#define SIZEPOLICY_VERT_TOP 0x0100 +#define SIZEPOLICY_VERT_BOTTOM 0x0200 +#define SIZEPOLICY_VERT_CENTER 0x0300 +#define SIZEPOLICY_VERT_MASK 0x0300 + +#define SIZEPOLICY_HORIZ_NONE 0x0000 +#define SIZEPOLICY_HORIZ_LEFT 0x0400 +#define SIZEPOLICY_HORIZ_RIGHT 0x0800 +#define SIZEPOLICY_HORIZ_CENTER 0x0c00 +#define SIZEPOLICY_HORIZ_MASK 0x0c00 + +#define SIZEPOLICY_SHRUNK 0x1000 + +/* The policies */ +#define SIZEPOLICY_MASK 0xff +#define SIZEPOLICY_DEFAULT 0x00 +#define SIZEPOLICY_FULL_EXACT 0x01 +#define SIZEPOLICY_FULL_BOUNDS 0x02 +#define SIZEPOLICY_FREE 0x03 +#define SIZEPOLICY_GRAVITY 0x04 /* uses vert/horiz flags */ +#define SIZEPOLICY_FREE_GLUE 0x05 /* stateful; modifies v/h flags */ +#define SIZEPOLICY_STRETCH_LEFT 0x06 +#define SIZEPOLICY_STRETCH_RIGHT 0x07 +#define SIZEPOLICY_STRETCH_TOP 0x08 +#define SIZEPOLICY_STRETCH_BOTTOM 0x09 +#define SIZEPOLICY_UNCONSTRAINED 0x10 + +#define SIZEPOLICY_GRAVITY_NORTHWEST (SIZEPOLICY_GRAVITY|SIZEPOLICY_VERT_TOP|SIZEPOLICY_HORIZ_LEFT) +#define SIZEPOLICY_GRAVITY_NORTH (SIZEPOLICY_GRAVITY|SIZEPOLICY_VERT_TOP|SIZEPOLICY_HORIZ_CENTER) +#define SIZEPOLICY_GRAVITY_NORTHEAST (SIZEPOLICY_GRAVITY|SIZEPOLICY_VERT_TOP|SIZEPOLICY_HORIZ_RIGHT) +#define SIZEPOLICY_GRAVITY_WEST (SIZEPOLICY_GRAVITY|SIZEPOLICY_VERT_CENTER|SIZEPOLICY_HORIZ_LEFT) +#define SIZEPOLICY_GRAVITY_CENTER (SIZEPOLICY_GRAVITY|SIZEPOLICY_VERT_CENTER|SIZEPOLICY_HORIZ_CENTER) +#define SIZEPOLICY_GRAVITY_EAST (SIZEPOLICY_GRAVITY|SIZEPOLICY_VERT_CENTER|SIZEPOLICY_HORIZ_RIGHT) +#define SIZEPOLICY_GRAVITY_SOUTHWEST (SIZEPOLICY_GRAVITY|SIZEPOLICY_VERT_BOTTOM|SIZEPOLICY_HORIZ_LEFT) +#define SIZEPOLICY_GRAVITY_SOUTH (SIZEPOLICY_GRAVITY|SIZEPOLICY_VERT_BOTTOM|SIZEPOLICY_HORIZ_CENTER) +#define SIZEPOLICY_GRAVITY_SOUTHEAST (SIZEPOLICY_GRAVITY|SIZEPOLICY_VERT_BOTTOM|SIZEPOLICY_HORIZ_RIGHT) + +#define SIZEPOLICY_FREE_GLUE__NORTHWEST (SIZEPOLICY_FREE_GLUE|SIZEPOLICY_VERT_TOP|SIZEPOLICY_HORIZ_LEFT) +#define SIZEPOLICY_FREE_GLUE__NORTH (SIZEPOLICY_FREE_GLUE|SIZEPOLICY_VERT_TOP|SIZEPOLICY_HORIZ_CENTER) +#define SIZEPOLICY_FREE_GLUE__NORTHEAST (SIZEPOLICY_FREE_GLUE|SIZEPOLICY_VERT_TOP|SIZEPOLICY_HORIZ_RIGHT) +#define SIZEPOLICY_FREE_GLUE__WEST (SIZEPOLICY_FREE_GLUE|SIZEPOLICY_VERT_CENTER|SIZEPOLICY_HORIZ_LEFT) +#define SIZEPOLICY_FREE_GLUE__CENTER (SIZEPOLICY_FREE_GLUE|SIZEPOLICY_VERT_CENTER|SIZEPOLICY_HORIZ_CENTER) +#define SIZEPOLICY_FREE_GLUE__EAST (SIZEPOLICY_FREE_GLUE|SIZEPOLICY_VERT_CENTER|SIZEPOLICY_HORIZ_RIGHT) +#define SIZEPOLICY_FREE_GLUE__SOUTHWEST (SIZEPOLICY_FREE_GLUE|SIZEPOLICY_VERT_BOTTOM|SIZEPOLICY_HORIZ_LEFT) +#define SIZEPOLICY_FREE_GLUE__SOUTH (SIZEPOLICY_FREE_GLUE|SIZEPOLICY_VERT_BOTTOM|SIZEPOLICY_HORIZ_CENTER) +#define SIZEPOLICY_FREE_GLUE__SOUTHEAST (SIZEPOLICY_FREE_GLUE|SIZEPOLICY_VERT_BOTTOM|SIZEPOLICY_HORIZ_RIGHT) + +typedef uint WSizePolicy; + + +extern void sizepolicy(WSizePolicy *szplcy, WRegion *reg, + const WRectangle *rq_geom, int rq_flags, + WFitParams *fp); + + +bool string2sizepolicy(const char *szplcy, WSizePolicy *value); + + +#endif /* ION_IONCORE_SIZEPOLICY_H */ diff --git a/ioncore/stacking.c b/ioncore/stacking.c new file mode 100644 index 0000000..8d696cb --- /dev/null +++ b/ioncore/stacking.c @@ -0,0 +1,617 @@ +/* + * ion/ioncore/stacking.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include + +#include "common.h" +#include "region.h" +#include "stacking.h" +#include "window.h" +#include "sizepolicy.h" + + +/*{{{ Alloc */ + + +WStacking *create_stacking() +{ + WStacking *st=ALLOC(WStacking); + + if(st!=NULL){ + st->reg=NULL; + st->above=NULL; + st->level=0; + st->szplcy=SIZEPOLICY_DEFAULT; + st->hidden=FALSE; + st->lnode=NULL; + } + + return st; +} + + +void stacking_free(WStacking *st) +{ + assert(st->mgr_next==NULL && st->mgr_prev==NULL && + st->next==NULL && st->prev==NULL && + /*st->above==NULL &&*/ + st->lnode==NULL && + st->reg==NULL); + + free(st); +} + + +/*}}}*/ + + +/*{{{ Lookup */ + + +static Rb_node stacking_of_reg=NULL; + + +WStacking *ioncore_find_stacking(WRegion *reg) +{ + Rb_node node=NULL; + int found=0; + + if(stacking_of_reg!=NULL) + node=rb_find_pkey_n(stacking_of_reg, reg, &found); + + return (found ? (WStacking*)node->v.val : NULL); +} + + +void stacking_unassoc(WStacking *st) +{ + Rb_node node=NULL; + int found=0; + + if(st->reg==NULL) + return; + + if(stacking_of_reg!=NULL) + node=rb_find_pkey_n(stacking_of_reg, st->reg, &found); + + if(node!=NULL) + rb_delete_node(node); + + st->reg=NULL; +} + + +bool stacking_assoc(WStacking *st, WRegion *reg) +{ + assert(st->reg==NULL); + + if(stacking_of_reg==NULL){ + stacking_of_reg=make_rb(); + if(stacking_of_reg==NULL) + return FALSE; + } + + if(rb_insertp(stacking_of_reg, reg, st)==NULL) + return FALSE; + + st->reg=reg; + return TRUE; +} + + +/*}}}*/ + + + +/*{{{ List processing */ + + +static WStacking *link_lists(WStacking *l1, WStacking *l2) +{ + /* As everywhere, doubly-linked lists without the forward + * link in last item! + */ + WStacking *tmp=l2->prev; + l1->prev->next=l2; + l2->prev=l1->prev; + l1->prev=tmp; + return l1; +} + + +static WStacking *link_list_before(WStacking *l1, + WStacking *i1, + WStacking *l2) +{ + WStacking *tmp; + + if(i1==l1) + return link_lists(l2, l1); + + l2->prev->next=i1; + i1->prev->next=l2; + tmp=i1->prev; + i1->prev=l2->prev; + l2->prev=tmp; + + return l1; +} + + +static WStacking *link_list_after(WStacking *l1, + WStacking *i1, + WStacking *l2) +{ + WStacking *tmp; + + if(i1==l1->prev) + return link_lists(l1, l2); + + i1->next->prev=l2->prev; + l2->prev->next=i1->next; + i1->next=l2; + l2->prev=i1; + + return l1; +} + + +WStacking *stacking_unstack(WWindow *par, WStacking *regst) +{ + WStacking *nxt=NULL, *st; + + /*st=regst->next;*/ + + UNLINK_ITEM(par->stacking, regst, next, prev); + + /*while(st!=NULL){*/ + for(st=par->stacking; st!=NULL; st=st->next){ + if(st->above==regst){ + st->above=NULL; + nxt=st; + } + /*st=st->next;*/ + } + + if(nxt==NULL) + nxt=regst->above; + + if(regst->above==NULL) + regst->above=NULL; + + return nxt; +} + + +static bool cf(WStackingFilter *filt, void *filt_data, WStacking *st) +{ + return (filt==NULL || filt(st, filt_data)); +} + + +static bool check_unweave(WStacking *st) +{ + /* 2: unknown, 1: yes, 0: no */ + + if(st->to_unweave==2){ + if(st->above!=NULL) + st->to_unweave=check_unweave(st->above); + else + st->to_unweave=0; + } + + return st->to_unweave; +} + + +WStacking *stacking_unweave(WStacking **stacking, + WStackingFilter *filt, void *filt_data) +{ + WStacking *np=NULL; + WStacking *st, *next; + + for(st=*stacking; st!=NULL; st=st->next){ + st->to_unweave=2; + if(st->above==NULL && cf(filt, filt_data, st)) + st->to_unweave=1; + } + + for(st=*stacking; st!=NULL; st=st->next) + check_unweave(st); + + for(st=*stacking; st!=NULL; st=next){ + next=st->next; + if(st->to_unweave==1){ + UNLINK_ITEM(*stacking, st, next, prev); + LINK_ITEM(np, st, next, prev); + } + } + + return np; +} + + +static int check_above_lvl(WStacking *st) +{ + if(st->above==NULL) + return st->level; + st->level=check_above_lvl(st->above); + return st->level; +} + + +static void enforce_level_sanity(WStacking **np) +{ + WStacking *st; + + /* Make sure that the levels of stuff stacked 'above' match + * the level of the thing stacked above. + */ + for(st=*np; st!=NULL; st=st->next) + check_above_lvl(st); + + /* And now make sure things are ordered by levels. */ + st=*np; + while(st->next!=NULL){ + if(st->next->level < st->level){ + WStacking *st2=st->next; + UNLINK_ITEM(*np, st2, next, prev); + LINK_ITEM_BEFORE(*np, st2, st, next, prev); + if(st2->prev!=NULL) + st=st2->prev; + }else{ + st=st->next; + } + } +} + + +static void get_bottom(WStacking *st, Window fb_win, + Window *other, int *mode) +{ + Window bottom=None, top=None; + + while(st!=NULL){ + if(st->reg!=NULL){ + region_stacking(st->reg, &bottom, &top); + if(bottom!=None){ + *other=bottom; + *mode=Below; + return; + } + } + st=st->next; + } + + *other=fb_win; + *mode=Above; +} + + +static void stacking_do_weave(WStacking **stacking, WStacking **np, + bool below, Window fb_win) +{ + WStacking *st, *ab; + uint lvl; + Window other; + int mode; + + if(*np==NULL) + return; + + /* Should do nothing.. */ + enforce_level_sanity(np); + + ab=*stacking; + + while(*np!=NULL){ + lvl=(*np)->level; + + while(ab!=NULL){ + if(ab->level>lvl || (below && ab->level==lvl)) + break; + ab=ab->next; + } + get_bottom(ab, fb_win, &other, &mode); + + st=*np; + + UNLINK_ITEM(*np, st, next, prev); + + region_restack(st->reg, other, mode); + + if(ab!=NULL){ + LINK_ITEM_BEFORE(*stacking, ab, st, next, prev); + }else{ + LINK_ITEM_LAST(*stacking, st, next, prev); + } + } +} + + +void stacking_weave(WStacking **stacking, WStacking **np, bool below) +{ + stacking_do_weave(stacking, np, below, None); +} + + +/*}}}*/ + + +/*{{{ Raise/lower */ + + +static bool is_above(WStacking *st, WStacking *p) +{ + if(st->above==NULL) + return FALSE; + else if(st->above==p) + return TRUE; + else + return is_above(st->above, p); +} + + +static void collect_first(WStacking **dst, WStacking **src, WStacking *st) +{ + UNLINK_ITEM(*src, st, next, prev); + LINK_ITEM_FIRST(*dst, st, next, prev); +} + + +static void collect_last(WStacking **dst, WStacking **src, WStacking *st) +{ + UNLINK_ITEM(*src, st, next, prev); + LINK_ITEM_LAST(*dst, st, next, prev); +} + + +static void collect_above(WStacking **dst, WStacking **src, WStacking *regst) +{ + WStacking *stabove, *stnext; + + for(stabove=*src; stabove!=NULL; stabove=stnext){ + stnext=stabove->next; + + if(is_above(stabove, regst)) + collect_last(dst, src, stabove); + } +} + + +static WStacking *unweave_subtree(WStacking **stacking, WStacking *regst, + bool parents) +{ + WStacking *tmp=NULL; + + if(parents){ + WStacking *st=regst; + while(st!=NULL){ + collect_first(&tmp, stacking, st); + st=st->above; + } + }else{ + collect_first(&tmp, stacking, regst); + } + + collect_above(&tmp, stacking, regst); + + return tmp; +} + + +void stacking_restack(WStacking **stacking, WStacking *st, Window fb_win, + WStackingFilter *filt, void *filt_data, bool lower) +{ + WStacking *tmp=unweave_subtree(stacking, st, lower); + + stacking_do_weave(stacking, &tmp, lower, fb_win); + + assert(tmp==NULL); +} + + +/*}}}*/ + + +/*{{{ Stacking lists */ + + +WStacking **window_get_stackingp(WWindow *wwin) +{ + return &(wwin->stacking); +} + + +WStacking *window_get_stacking(WWindow *wwin) +{ + return wwin->stacking; +} + + +/*}}}*/ + + +/*{{{ Stacking list iteration */ + + +void stacking_iter_init(WStackingIterTmp *tmp, + WStacking *st, + WStackingFilter *filt, + void *filt_data) +{ + tmp->st=st; + tmp->filt=filt; + tmp->filt_data=filt_data; +} + + +WStacking *stacking_iter_nodes(WStackingIterTmp *tmp) +{ + WStacking *next=NULL; + + while(tmp->st!=NULL){ + next=tmp->st; + tmp->st=tmp->st->next; + if(cf(tmp->filt, tmp->filt_data, next)) + break; + next=NULL; + } + + return next; +} + + +WRegion *stacking_iter(WStackingIterTmp *tmp) +{ + WStacking *st=stacking_iter_nodes(tmp); + return (st!=NULL ? st->reg : NULL); +} + + +void stacking_iter_mgr_init(WStackingIterTmp *tmp, + WStacking *st, + WStackingFilter *filt, + void *filt_data) +{ + tmp->st=st; + tmp->filt=filt; + tmp->filt_data=filt_data; +} + + +WStacking *stacking_iter_mgr_nodes(WStackingIterTmp *tmp) +{ + WStacking *next=NULL; + + while(tmp->st!=NULL){ + next=tmp->st; + tmp->st=tmp->st->mgr_next; + if(cf(tmp->filt, tmp->filt_data, next)) + break; + next=NULL; + } + + return next; +} + + +WRegion *stacking_iter_mgr(WStackingIterTmp *tmp) +{ + WStacking *st=stacking_iter_mgr_nodes(tmp); + return (st!=NULL ? st->reg : NULL); +} + + +/*}}}*/ + + +/*{{{ Focus */ + + +uint stacking_min_level(WStacking *stacking, + WStackingFilter *include_filt, + void *filt_data) +{ + WStacking *st=NULL; + uint min_level=0; + + if(stacking==NULL) + return STACKING_LEVEL_BOTTOM; + + st=stacking; + do{ + st=st->prev; + + if(st->reg!=NULL + && !(st->reg->flags®ION_SKIP_FOCUS) + && cf(include_filt, filt_data, st)){ + + if(st->level>=STACKING_LEVEL_MODAL1) + min_level=st->level; + + break; + } + }while(st!=stacking); + + return min_level; +} + + +WStacking *stacking_find_to_focus(WStacking *stacking, WStacking *to_try, + WStackingFilter *include_filt, + WStackingFilter *approve_filt, + void *filt_data) +{ + WStacking *st=NULL; + uint min_level=0; + + if(stacking==NULL) + return NULL; + + min_level=stacking_min_level(stacking, include_filt, filt_data); + + if(to_try!=NULL && to_try->level>=min_level) + return to_try; + + st=stacking; + do{ + st=st->prev; + + if(st->levelreg!=NULL + && !(st->reg->flags®ION_SKIP_FOCUS) + && cf(include_filt, filt_data, st) + && cf(approve_filt, filt_data, st)){ + return st; + } + }while(st!=stacking); + + return NULL; +} + + +static bool mapped_filt(WStacking *st, void *unused) +{ + return (st->reg!=NULL && REGION_IS_MAPPED(st->reg)); +} + + +static bool mgr_filt(WStacking *st, void *mgr_) +{ + return (st->reg!=NULL && REGION_MANAGER(st->reg)==(WRegion*)mgr_); +} + + +WStacking *stacking_find_to_focus_mapped(WStacking *stacking, + WStacking *to_try, + WRegion *mgr) +{ + if(mgr==NULL){ + return stacking_find_to_focus(stacking, to_try, mapped_filt, + NULL, NULL); + }else{ + return stacking_find_to_focus(stacking, to_try, mapped_filt, + mgr_filt, mgr); + } +} + + +uint stacking_min_level_mapped(WStacking *stacking) +{ + return stacking_min_level(stacking, mapped_filt, NULL); +} + + +/*}}}*/ + diff --git a/ioncore/stacking.h b/ioncore/stacking.h new file mode 100644 index 0000000..a5e3464 --- /dev/null +++ b/ioncore/stacking.h @@ -0,0 +1,108 @@ +/* + * ion/ioncore/stacking.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_STACKING_H +#define ION_IONCORE_STACKING_H + +#include "common.h" +#include "region.h" +#include "sizepolicy.h" + + +#define STACKING_LEVEL_BOTTOM 0 +#define STACKING_LEVEL_NORMAL 1 +#define STACKING_LEVEL_ON_TOP 2 +#define STACKING_LEVEL_MODAL1 1024 + + +#define STACKING_IS_HIDDEN(ST) ((ST)->hidden) + + +DECLSTRUCT(WStacking){ + WRegion *reg; + WStacking *next, *prev; + WStacking *above; + uint level; + WSizePolicy szplcy; + WStacking *mgr_next, *mgr_prev; + + /* flags */ + uint to_unweave:2; + uint hidden:1; + + /* WMPlex stuff */ + WLListNode *lnode; +}; + + +typedef bool WStackingFilter(WStacking *st, void *data); +typedef WStacking *WStackingIterator(void *data); + + +DECLSTRUCT(WStackingIterTmp){ + WStacking *st; + WStackingFilter *filt; + void *filt_data; +}; + + +WStacking **window_get_stackingp(WWindow *wwin); +WStacking *window_get_stacking(WWindow *wwin); + + +WStacking *create_stacking(); + +void stacking_free(WStacking *st); + +/* Returns the topmost node with 'above' pointing to st. */ +WStacking *stacking_unstack(WWindow *par, WStacking *st); + +void stacking_iter_init(WStackingIterTmp *tmp, + WStacking *st, + WStackingFilter *filt, + void *filt_data); +WRegion *stacking_iter(WStackingIterTmp *tmp); +WStacking *stacking_iter_nodes(WStackingIterTmp *tmp); + +void stacking_iter_mgr_init(WStackingIterTmp *tmp, + WStacking *st, + WStackingFilter *filt, + void *filt_data); +WRegion *stacking_iter_mgr(WStackingIterTmp *tmp); +WStacking *stacking_iter_mgr_nodes(WStackingIterTmp *tmp); + +void stacking_weave(WStacking **stacking, WStacking **np, bool below); +WStacking *stacking_unweave(WStacking **stacking, + WStackingFilter *filt, void *filt_data); + +void stacking_restack(WStacking **stacking, WStacking *st, Window fb_win, + WStackingFilter *filt, void *filt_data, bool lower); + +WStacking *stacking_find_to_focus(WStacking *stacking, WStacking *to_try, + WStackingFilter *include_filt, + WStackingFilter *approve_filt, + void *filt_data); +WStacking *stacking_find_to_focus_mapped(WStacking *stacking, + WStacking *to_try, + WRegion *mgr); + +uint stacking_min_level(WStacking *stacking, + WStackingFilter *include_filt, + void *filt_data); + +uint stacking_min_level_mapped(WStacking *stacking); + + +WStacking *ioncore_find_stacking(WRegion *reg); +void stacking_unassoc(WStacking *stacking); +bool stacking_assoc(WStacking *stacking, WRegion *reg); + +#endif /* ION_IONCORE_STACKING_H */ diff --git a/ioncore/strings.c b/ioncore/strings.c new file mode 100644 index 0000000..9f2bbd3 --- /dev/null +++ b/ioncore/strings.c @@ -0,0 +1,470 @@ +/* + * ion/ioncore/strings.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include "common.h" +#include "global.h" +#include "strings.h" + + +/*{{{ String scanning */ + + +wchar_t str_wchar_at(char *p, int max) +{ + wchar_t wc; + if(mbtowc(&wc, p, max)>0) + return wc; + return 0; +} + + +char *str_stripws(char *p) +{ + mbstate_t ps; + wchar_t wc; + int first=-1, pos=0; + int n=strlen(p); + int ret; + + memset(&ps, 0, sizeof(ps)); + + while(1){ + ret=mbrtowc(&wc, p+pos, n-pos, &ps); + if(ret<=0) + break; + if(!iswspace(wc)) + break; + pos+=ret; + } + + if(pos!=0) + memmove(p, p+pos, n-pos+1); + + if(ret<=0) + return p; + + pos=ret; + + while(1){ + ret=mbrtowc(&wc, p+pos, n-pos, &ps); + if(ret<=0) + break; + if(iswspace(wc)){ + if(first==-1) + first=pos; + }else{ + first=-1; + } + pos+=ret; + } + + if(first!=-1) + p[first]='\0'; + + return p; +} + + +int str_prevoff(const char *p, int pos) +{ + if(ioncore_g.enc_sb) + return (pos>0 ? 1 : 0); + + if(ioncore_g.enc_utf8){ + int opos=pos; + + while(pos>0){ + pos--; + if((p[pos]&0xC0)!=0x80) + break; + } + return opos-pos; + } + + assert(ioncore_g.use_mb); + { + /* *sigh* */ + int l, prev=0; + mbstate_t ps; + + memset(&ps, 0, sizeof(ps)); + + while(1){ + l=mbrlen(p+prev, pos-prev, &ps); + if(l<0){ + warn(TR("Invalid multibyte string.")); + return 0; + } + if(prev+l>=pos) + return pos-prev; + prev+=l; + } + + } +} + + +int str_nextoff(const char *p, int opos) +{ + if(ioncore_g.enc_sb) + return (*(p+opos)=='\0' ? 0 : 1); + + if(ioncore_g.enc_utf8){ + int pos=opos; + + while(p[pos]){ + pos++; + if((p[pos]&0xC0)!=0x80) + break; + } + return pos-opos; + } + + assert(ioncore_g.use_mb); + { + mbstate_t ps; + int l; + memset(&ps, 0, sizeof(ps)); + + l=mbrlen(p+opos, strlen(p+opos), &ps); + if(l<0){ + warn(TR("Invalid multibyte string.")); + return 0; + } + return l; + } +} + + +int str_len(const char *p) +{ + if(ioncore_g.enc_sb) + return strlen(p); + + if(ioncore_g.enc_utf8){ + int len=0; + + while(*p){ + if(((*p)&0xC0)!=0x80) + len++; + p++; + } + return len; + } + + assert(ioncore_g.use_mb); + { + mbstate_t ps; + int len=0, bytes=strlen(p), l; + memset(&ps, 0, sizeof(ps)); + + while(bytes>0){ + l=mbrlen(p, bytes, &ps); + if(l<=0){ + warn(TR("Invalid multibyte string.")); + break; + } + len++; + bytes-=l; + } + return len; + } + +} + +/*}}}*/ + + +/*{{{ Title shortening */ + + +static char *scatn3(const char *p1, int l1, + const char *p2, int l2, + const char *p3, int l3) +{ + char *p=ALLOC_N(char, l1+l2+l3+1); + + if(p!=NULL){ + strncat(p, p1, l1); + strncat(p, p2, l2); + strncat(p, p3, l3); + } + return p; +} + +INTRSTRUCT(SR); + +DECLSTRUCT(SR){ + regex_t re; + char *rule; + SR *next, *prev; + bool always; +}; + + +static SR *shortenrules=NULL; + + +/*EXTL_DOC + * Add a rule describing how too long titles should be shortened to fit in tabs. + * The regular expression \var{rx} (POSIX, not Lua!) is used to match titles + * and when \var{rx} matches, \var{rule} is attempted to use as a replacement + * for title. If \var{always} is set, the rule is used even if no shortening + * is necessary. + * + * Similarly to sed's 's' command, \var{rule} may contain characters that are + * inserted in the resulting string and specials as follows: + * + * \begin{tabularx}{\linewidth}{lX} + * \tabhead{Special & Description} + * \$0 & Place the original string here. \\ + * \$1 to \$9 & Insert n:th capture here (as usual,captures are surrounded + * by parentheses in the regex). \\ + * \$| & Alternative shortening separator. The shortening described + * before the first this kind of separator is tried first and + * if it fails to make the string short enough, the next is + * tried, and so on. \\ + * \$< & Remove characters on the left of this marker to shorten the + * string. \\ + * \$> & Remove characters on the right of this marker to shorten the + * string. Only the first \$< or \$> within an alternative + * shortening is used. \\ + * \end{tabularx} + */ +EXTL_EXPORT +bool ioncore_defshortening(const char *rx, const char *rule, bool always) +{ + SR *si; + int ret; + #define ERRBUF_SIZE 256 + static char errbuf[ERRBUF_SIZE]; + + if(rx==NULL || rule==NULL) + return FALSE; + + si=ALLOC(SR); + + if(si==NULL) + return FALSE; + + ret=regcomp(&(si->re), rx, REG_EXTENDED); + + if(ret!=0){ + errbuf[0]='\0'; + regerror(ret, &(si->re), errbuf, ERRBUF_SIZE); + warn(TR("Error compiling regular expression: %s"), errbuf); + goto fail2; + } + + si->rule=scopy(rule); + si->always=always; + + if(si->rule==NULL) + goto fail; + + LINK_ITEM(shortenrules, si, next, prev); + + return TRUE; + +fail: + regfree(&(si->re)); +fail2: + free(si); + return FALSE; +} + + +static char *shorten(GrBrush *brush, const char *str, uint maxw, + const char *rule, int nmatch, regmatch_t *pmatch) +{ + char *s; + int rulelen, slen, i, j, k, ll; + int strippt=0; + int stripdir=-1; + bool more=FALSE; + + /* Ensure matches are at character boundaries */ + if(!ioncore_g.enc_sb){ + int pos=0, len, strl; + mbstate_t ps; + memset(&ps, 0, sizeof(ps)); + + strl=strlen(str); + + while(pospos && pmatch[i].rm_sopos && pmatch[i].rm_eo'){ + strippt=j; + stripdir=1; + continue; + } + + if(rule[i]>='0' && rule[i]<='9'){ + k=(int)(rule[i]-'0'); + if(k>=nmatch) + continue; + if(pmatch[k].rm_so==-1) + continue; + ll=(pmatch[k].rm_eo-pmatch[k].rm_so); + strncpy(s+j, str+pmatch[k].rm_so, ll); + j+=ll; + } + } + + slen=j; + s[slen]='\0'; + + i=strippt; + j=strippt; + + /* shorten */ + { + uint bl=grbrush_get_text_width(brush, s, i); + uint el=grbrush_get_text_width(brush, s+j, slen-j); + + while(1){ + /* el+bl may not be the actual length, but close enough. */ + if(el+bl<=maxw){ + memmove(s+i, s+j, slen-j+1); + return s; + } + + if(stripdir==-1){ + ll=str_prevoff(s, i); + if(ll==0) + break; + i-=ll; + bl=grbrush_get_text_width(brush, s, i); + }else{ + ll=str_nextoff(s, j); + if(ll==0) + break; + j+=ll; + el=grbrush_get_text_width(brush, s+j, slen-j); + } + } + } + }while(more); + + free(s); + + return NULL; +} + + +char *grbrush_make_label(GrBrush *brush, const char *str, uint maxw) +{ + size_t nmatch=10; + regmatch_t pmatch[10]; + SR *rule; + int ret; + char *retstr; + bool fits=FALSE; + + if(grbrush_get_text_width(brush, str, strlen(str))<=maxw) + fits=TRUE; + + /*return scopy(str);*/ + + for(rule=shortenrules; rule!=NULL; rule=rule->next){ + if(fits && !rule->always) + continue; + ret=regexec(&(rule->re), str, nmatch, pmatch, 0); + if(ret!=0) + continue; + retstr=shorten(brush, str, maxw, rule->rule, nmatch, pmatch); + goto rettest; + } + + if(fits){ + retstr=scopy(str); + }else{ + pmatch[0].rm_so=0; + pmatch[0].rm_eo=strlen(str)-1; + retstr=shorten(brush, str, maxw, "$1$<...", 1, pmatch); + } + +rettest: + if(retstr!=NULL) + return retstr; + return scopy(""); +} + + +/*}}}*/ + diff --git a/ioncore/strings.h b/ioncore/strings.h new file mode 100644 index 0000000..0f2a1f4 --- /dev/null +++ b/ioncore/strings.h @@ -0,0 +1,37 @@ +/* + * ion/ioncore/strings.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_STRINGS_H +#define ION_IONCORE_STRINGS_H + +#include "common.h" + +#ifdef CF_NO_LOCALE +#include "dummywc.h" +#else +#include +#include +#endif + +#include "gr.h" + +extern bool ioncore_defshortening(const char *rx, const char *rule, + bool always); + +extern char *grbrush_make_label(GrBrush *brush, const char *str, uint maxw); + +extern int str_nextoff(const char *p, int pos); +extern int str_prevoff(const char *p, int pos); +extern int str_len(const char *p); +extern wchar_t str_wchar_at(char *p, int max); +extern char *str_stripws(char *p); + +#endif /* ION_IONCORE_STRINGS_H */ diff --git a/ioncore/tags.c b/ioncore/tags.c new file mode 100644 index 0000000..6b7d5c2 --- /dev/null +++ b/ioncore/tags.c @@ -0,0 +1,134 @@ +/* + * ion/ioncore/tags.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include "region.h" +#include "tags.h" + + +static ObjList *taglist=NULL; + + +/*{{{ Adding/removing tags */ + + +bool region_set_tagged(WRegion *reg, int sp) +{ + bool set=(reg->flags®ION_TAGGED); + bool nset=libtu_do_setparam(sp, set); + + if(XOR(nset, set)){ + if(reg->flags®ION_TAGGED){ + reg->flags&=~REGION_TAGGED; + objlist_remove(&taglist, (Obj*)reg); + }else{ + reg->flags|=REGION_TAGGED; + objlist_insert_last(&taglist, (Obj*)reg); + } + region_notify_change(reg, "tag"); + } + + return nset; +} + + +/*EXTL_DOC + * Change tagging state of \var{reg} as defined by \var{how} + * (set/unset/toggle). Resulting state is returned. + */ +EXTL_EXPORT_AS(WRegion, set_tagged) +bool region_set_tagged_extl(WRegion *reg, const char *how) +{ + return region_set_tagged(reg, libtu_string_to_setparam(how)); +} + + +/*EXTL_DOC + * Is \var{reg} tagged? + */ +EXTL_SAFE +EXTL_EXPORT_MEMBER +bool region_is_tagged(WRegion *reg) +{ + return ((reg->flags®ION_TAGGED)!=0); +} + + +/*EXTL_DOC + * Untag all regions. + */ +EXTL_EXPORT +void ioncore_clear_tags() +{ + while(ioncore_tags_take_first()!=NULL) + /* nothing */; +} + + +/*}}}*/ + + +/*{{{ Iteration */ + + +/*EXTL_DOC + * Returns first tagged object. + */ +EXTL_SAFE +EXTL_EXPORT +WRegion *ioncore_tags_first() +{ + return (WRegion*)OBJLIST_FIRST(WRegion*, taglist); +} + + +WRegion *ioncore_tags_take_first() +{ + WRegion *reg=(WRegion*)objlist_take_first(&taglist); + + if(reg!=NULL){ + reg->flags&=~REGION_TAGGED; + region_notify_change(reg, "tag"); + } + + return reg; +} + +/*EXTL_DOC + * Returns a list of tagged regions. + */ +EXTL_SAFE +EXTL_EXPORT +ExtlTab ioncore_tagged_list() +{ + int n=0; + ExtlTab tab; + WRegion *region; + ObjListIterTmp tmp; + + region=ioncore_tags_first(); + if(!region) + return extl_table_none(); + + tab=extl_create_table(); + + FOR_ALL_ON_OBJLIST(WRegion*, region, taglist, tmp){ + if(extl_table_seti_o(tab, n+1, (Obj*)region)) + n++; + } + + return tab; +} + + +/*}}}*/ + diff --git a/ioncore/tags.h b/ioncore/tags.h new file mode 100644 index 0000000..478d47b --- /dev/null +++ b/ioncore/tags.h @@ -0,0 +1,25 @@ +/* + * ion/ioncore/tags.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_TAGS_H +#define ION_IONCORE_TAGS_H + +#include +#include "region.h" + +extern bool region_set_tagged(WRegion *reg, int sp); +extern bool region_is_tagged(WRegion *reg); + +extern void ioncore_clear_tags(); +extern WRegion *ioncore_tags_first(); +extern WRegion *ioncore_tags_take_first(); + +#endif /* ION_IONCORE_TAGS_H */ diff --git a/ioncore/window.c b/ioncore/window.c new file mode 100644 index 0000000..8d2986f --- /dev/null +++ b/ioncore/window.c @@ -0,0 +1,247 @@ +/* + * ion/ioncore/window.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include + +#include "common.h" +#include "global.h" +#include "window.h" +#include "focus.h" +#include "rootwin.h" +#include "region.h" +#include "xwindow.h" +#include "region-iter.h" + + +/*{{{ Dynfuns */ + + +void window_draw(WWindow *wwin, bool complete) +{ + CALL_DYN(window_draw, wwin, (wwin, complete)); +} + + +void window_insstr(WWindow *wwin, const char *buf, size_t n) +{ + CALL_DYN(window_insstr, wwin, (wwin, buf, n)); +} + + +int window_press(WWindow *wwin, XButtonEvent *ev, WRegion **reg_ret) +{ + int area=0; + CALL_DYN_RET(area, int, window_press, wwin, (wwin, ev, reg_ret)); + return area; +} + + +void window_release(WWindow *wwin) +{ + CALL_DYN(window_release, wwin, (wwin)); +} + + +/*}}}*/ + + +/*{{{ Init, create */ + + +bool window_do_init(WWindow *wwin, WWindow *par, Window win, + const WFitParams *fp) +{ + wwin->win=win; + wwin->xic=NULL; + wwin->event_mask=0; + wwin->stacking=NULL; + + region_init(&(wwin->region), par, fp); + + if(win!=None){ + XSaveContext(ioncore_g.dpy, win, ioncore_g.win_context, + (XPointer)wwin); + } + + return TRUE; +} + + +bool window_init(WWindow *wwin, WWindow *par, const WFitParams *fp) +{ + Window win; + + win=create_xwindow(region_rootwin_of((WRegion*)par), + par->win, &(fp->g)); + if(win==None) + return FALSE; + /* window_init does not fail */ + return window_do_init(wwin, par, win, fp); +} + + +void window_deinit(WWindow *wwin) +{ + region_deinit((WRegion*)wwin); + + if(wwin->xic!=NULL) + XDestroyIC(wwin->xic); + + if(wwin->win!=None){ + XDeleteContext(ioncore_g.dpy, wwin->win, ioncore_g.win_context); + XDestroyWindow(ioncore_g.dpy, wwin->win); + } + + /* There are no backlinks from WStacking to us, so it is not + * necessary to do any deinitialisation there. + */ +} + + +/*}}}*/ + + +/*{{{ Region dynfuns */ + + +static void window_notify_subs_rootpos(WWindow *wwin, int x, int y) +{ + WRegion *sub; + + FOR_ALL_CHILDREN(wwin, sub){ + region_notify_rootpos(sub, + x+REGION_GEOM(sub).x, + y+REGION_GEOM(sub).y); + } +} + + +void window_notify_subs_move(WWindow *wwin) +{ + int x=0, y=0; + region_rootpos(&(wwin->region), &x, &y); + window_notify_subs_rootpos(wwin, x, y); +} + + +void window_do_fitrep(WWindow *wwin, WWindow *par, const WRectangle *geom) +{ + bool move=(REGION_GEOM(wwin).x!=geom->x || + REGION_GEOM(wwin).y!=geom->y); + int w=maxof(1, geom->w); + int h=maxof(1, geom->h); + + if(par!=NULL){ + region_unset_parent((WRegion*)wwin); + XReparentWindow(ioncore_g.dpy, wwin->win, par->win, geom->x, geom->y); + XResizeWindow(ioncore_g.dpy, wwin->win, w, h); + region_set_parent((WRegion*)wwin, par); + }else{ + XMoveResizeWindow(ioncore_g.dpy, wwin->win, geom->x, geom->y, w, h); + } + + REGION_GEOM(wwin)=*geom; + + if(move) + window_notify_subs_move(wwin); +} + + +bool window_fitrep(WWindow *wwin, WWindow *par, const WFitParams *fp) +{ + if(par!=NULL && !region_same_rootwin((WRegion*)wwin, (WRegion*)par)) + return FALSE; + window_do_fitrep(wwin, par, &(fp->g)); + return TRUE; +} + + +void window_map(WWindow *wwin) +{ + XMapWindow(ioncore_g.dpy, wwin->win); + REGION_MARK_MAPPED(wwin); +} + + +void window_unmap(WWindow *wwin) +{ + XUnmapWindow(ioncore_g.dpy, wwin->win); + REGION_MARK_UNMAPPED(wwin); +} + + +void window_do_set_focus(WWindow *wwin, bool warp) +{ + region_finalise_focusing((WRegion*)wwin, wwin->win, warp); +} + + +void window_restack(WWindow *wwin, Window other, int mode) +{ + xwindow_restack(wwin->win, other, mode); +} + + +Window window_xwindow(const WWindow *wwin) +{ + return wwin->win; +} + + +/*}}}*/ + + +/*{{{ Misc. */ + + +/*EXTL_DOC + * Return the X window id for \var{wwin}. + */ +EXTL_SAFE +EXTL_EXPORT_MEMBER +double window_xid(WWindow *wwin) +{ + return wwin->win; +} + + +void window_select_input(WWindow *wwin, long event_mask) +{ + XSelectInput(ioncore_g.dpy, wwin->win, event_mask); + wwin->event_mask=event_mask; +} + + +/*}}}*/ + + +/*{{{ Dynamic function table and class implementation */ + + +static DynFunTab window_dynfuntab[]={ + {region_map, window_map}, + {region_unmap, window_unmap}, + {region_do_set_focus, window_do_set_focus}, + {(DynFun*)region_fitrep, (DynFun*)window_fitrep}, + {(DynFun*)region_xwindow, (DynFun*)window_xwindow}, + {region_notify_rootpos, window_notify_subs_rootpos}, + {region_restack, window_restack}, + END_DYNFUNTAB +}; + + +EXTL_EXPORT +IMPLCLASS(WWindow, WRegion, window_deinit, window_dynfuntab); + + +/*}}}*/ + diff --git a/ioncore/window.h b/ioncore/window.h new file mode 100644 index 0000000..b9f4ffa --- /dev/null +++ b/ioncore/window.h @@ -0,0 +1,57 @@ +/* + * ion/ioncore/window.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_WINDOW_H +#define ION_IONCORE_WINDOW_H + +#include "common.h" +#include "region.h" +#include "binding.h" +#include "rectangle.h" + + +DECLCLASS(WWindow){ + WRegion region; + Window win; + XIC xic; + long event_mask; + WStacking *stacking; +}; + + +extern bool window_init(WWindow *p, WWindow *parent, + const WFitParams *fp); +extern bool window_do_init(WWindow *p, WWindow *parent, Window win, + const WFitParams *fp); +extern void window_deinit(WWindow *win); + +DYNFUN void window_draw(WWindow *wwin, bool complete); +DYNFUN void window_insstr(WWindow *wwin, const char *buf, size_t n); +DYNFUN int window_press(WWindow *wwin, XButtonEvent *ev, WRegion **reg_ret); +DYNFUN void window_release(WWindow *wwin); + +/* Only to be used by regions that inherit this */ +extern void window_map(WWindow *wwin); +extern void window_unmap(WWindow *wwin); + +extern void window_do_set_focus(WWindow *wwin, bool warp); + +extern void window_do_fitrep(WWindow *wwin, WWindow *parent, + const WRectangle *geom); +extern bool window_fitrep(WWindow *wwin, WWindow *parent, + const WFitParams *fp); +extern void window_notify_subs_move(WWindow *wwin); + +extern void window_restack(WWindow *wwin, Window other, int mode); + +extern void window_select_input(WWindow *wwin, long event_mask); + +#endif /* ION_IONCORE_WINDOW_H */ diff --git a/ioncore/xic.c b/ioncore/xic.c new file mode 100644 index 0000000..304adf4 --- /dev/null +++ b/ioncore/xic.c @@ -0,0 +1,97 @@ +/* + * ion/ioncore/xic.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include "common.h" +#include "global.h" +#include "ioncore.h" + + +static XIM input_method=NULL; +static XIMStyle input_style=(XIMPreeditNothing|XIMStatusNothing); + + +void ioncore_init_xim(void) +{ + char *p; + int i; + XIM xim=NULL; + XIMStyles *xim_styles = NULL; + bool found=FALSE; + + if((p=XSetLocaleModifiers(""))!=NULL && *p) + xim=XOpenIM(ioncore_g.dpy, NULL, NULL, NULL); + + if(xim==NULL && (p=XSetLocaleModifiers("@im=none"))!=NULL && *p) + xim=XOpenIM(ioncore_g.dpy, NULL, NULL, NULL); + + if(xim==NULL){ + ioncore_warn_nolog(TR("Failed to open input method.")); + return; + } + + if(XGetIMValues(xim, XNQueryInputStyle, &xim_styles, NULL) || !xim_styles) { + ioncore_warn_nolog(TR("Input method doesn't support any style.")); + XCloseIM(xim); + return; + } + + for(i=0; (ushort)icount_styles; i++){ + if(input_style==xim_styles->supported_styles[i]){ + found=TRUE; + break; + } + } + + XFree(xim_styles); + + if(!found){ + ioncore_warn_nolog(TR("input method doesn't support my preedit type.")); + XCloseIM(xim); + return; + } + + input_method=xim; +} + + +XIC xwindow_create_xic(Window win) +{ + /*static bool tried=FALSE;*/ + XIC xic; + + /* + if(input_method==NULL && !tried){ + init_xlocale(); + tried=TRUE; + }*/ + + if(input_method==NULL) + return NULL; + + xic=XCreateIC(input_method, XNInputStyle, input_style, + XNClientWindow, win, XNFocusWindow, win, + NULL); + + if(xic==NULL) + warn(TR("Failed to create input context.")); + + return xic; +} + + +bool window_create_xic(WWindow *wwin) +{ + if(wwin->xic==NULL) + wwin->xic=xwindow_create_xic(wwin->win); + return (wwin->xic!=NULL); +} diff --git a/ioncore/xic.h b/ioncore/xic.h new file mode 100644 index 0000000..dcc3de6 --- /dev/null +++ b/ioncore/xic.h @@ -0,0 +1,24 @@ +/* + * ion/ioncore/xic.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_XIC_H +#define ION_IONCORE_XIC_H + +#include "common.h" +#include "window.h" + +extern XIC xwindow_create_xic(Window win); + +extern bool window_create_xic(WWindow *wwin); + +extern void ioncore_init_xim(void); + +#endif /* ION_IONCORE_XIC_H */ diff --git a/ioncore/xwindow.c b/ioncore/xwindow.c new file mode 100644 index 0000000..16491c7 --- /dev/null +++ b/ioncore/xwindow.c @@ -0,0 +1,140 @@ +/* + * ion/ioncore/xwindow.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include + +#include +#include "common.h" +#include "global.h" +#include "xwindow.h" +#include "cursor.h" +#include "sizehint.h" + + +/*{{{ X window->region mapping */ + + +WRegion *xwindow_region_of(Window win) +{ + WRegion *reg; + + if(XFindContext(ioncore_g.dpy, win, ioncore_g.win_context, + (XPointer*)®)!=0) + return NULL; + + return reg; +} + + +WRegion *xwindow_region_of_t(Window win, const ClassDescr *descr) +{ + WRegion *reg=xwindow_region_of(win); + + if(reg==NULL) + return NULL; + + if(!obj_is((Obj*)reg, descr)) + return NULL; + + return reg; +} + + +/*}}}*/ + + +/*{{{ Create */ + + +Window create_xwindow(WRootWin *rw, Window par, const WRectangle *geom) +{ + int w=maxof(1, geom->w); + int h=maxof(1, geom->h); + + return XCreateSimpleWindow(ioncore_g.dpy, par, geom->x, geom->y, w, h, + 0, 0, BlackPixel(ioncore_g.dpy, rw->xscr)); +} + + +/*}}}*/ + + +/*{{{ Restack */ + + +void xwindow_restack(Window win, Window other, int stack_mode) +{ + XWindowChanges wc; + int wcmask; + + wcmask=CWStackMode; + wc.stack_mode=stack_mode; + if(other!=None){ + wc.sibling=other; + wcmask|=CWSibling; + } + + XConfigureWindow(ioncore_g.dpy, win, wcmask, &wc); +} + + +/*}}}*/ + + +/*{{{ Focus */ + + +void xwindow_do_set_focus(Window win) +{ + XSetInputFocus(ioncore_g.dpy, win, RevertToParent, CurrentTime); +} + + +/*}}}*/ + + +/*{{{ Pointer and cursors */ + +void xwindow_set_cursor(Window win, int cursor) +{ + XDefineCursor(ioncore_g.dpy, win, ioncore_xcursor(cursor)); +} + + +bool xwindow_pointer_pos(Window rel, int *px, int *py) +{ + Window win=None, realroot=None; + int wx=0, wy=0; + uint mask=0; + return XQueryPointer(ioncore_g.dpy, rel, &realroot, &win, + px, py, &wx, &wy, &mask); +} + +/*}}}*/ + + +/*{{{ Size hints */ + + +void xwindow_get_sizehints(Window win, XSizeHints *hints) +{ + int minh, minw; + long supplied=0; + + memset(hints, 0, sizeof(*hints)); + XGetWMNormalHints(ioncore_g.dpy, win, hints, &supplied); + + xsizehints_sanity_adjust(hints); +} + + +/*}}}*/ + diff --git a/ioncore/xwindow.h b/ioncore/xwindow.h new file mode 100644 index 0000000..50e7db5 --- /dev/null +++ b/ioncore/xwindow.h @@ -0,0 +1,37 @@ +/* + * ion/ioncore/xwindow.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_IONCORE_XWINDOW_H +#define ION_IONCORE_XWINDOW_H + +#include "common.h" +#include "rectangle.h" + +#define XWINDOW_REGION_OF_T(WIN, TYPE) (TYPE*)xwindow_region_of_t(WIN, &CLASSDESCR(TYPE)) +#define XWINDOW_REGION_OF(WIN) xwindow_region_of(WIN) + +extern Window create_xwindow(WRootWin *rw, Window par, + const WRectangle *geom); + +extern WRegion *xwindow_region_of(Window win); +extern WRegion *xwindow_region_of_t(Window win, const ClassDescr *descr); + +extern void xwindow_restack(Window win, Window other, int stack_mode); + +extern void xwindow_do_set_focus(Window win); + +extern void xwindow_set_cursor(Window win, int cursor); + +extern void xwindow_get_sizehints(Window win, XSizeHints *hints); + +extern bool xwindow_pointer_pos(Window rel, int *px, int *py); + +#endif /* ION_IONCORE_XWINDOW_H */ diff --git a/libextl/LICENSE b/libextl/LICENSE new file mode 100644 index 0000000..cf9b6b9 --- /dev/null +++ b/libextl/LICENSE @@ -0,0 +1,510 @@ + + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations +below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. +^L + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it +becomes a de-facto standard. To achieve this, non-free programs must +be allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. +^L + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control +compilation and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. +^L + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. +^L + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at least + three years, to give the same user the materials specified in + Subsection 6a, above, for a charge no more than the cost of + performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. +^L + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. +^L + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply, and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License +may add an explicit geographical distribution limitation excluding those +countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. +^L + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS +^L + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms +of the ordinary General Public License). + + To apply these terms, attach the following notices to the library. +It is safest to attach them to the start of each source file to most +effectively convey the exclusion of warranty; and each file should +have at least the "copyright" line and a pointer to where the full +notice is found. + + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or +your school, if any, to sign a "copyright disclaimer" for the library, +if necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James + Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/libextl/Makefile b/libextl/Makefile new file mode 100644 index 0000000..01f40ac --- /dev/null +++ b/libextl/Makefile @@ -0,0 +1,42 @@ +## +## Extl Makefile +## + +# System-specific configuration is in system.mk +TOPDIR=. +include $(TOPDIR)/build/system-inc.mk + +###################################### + +INCLUDES += $(LIBTU_INCLUDES) $(LUA_INCLUDES) + +CFLAGS += $(XOPEN_SOURCE) $(C99_SOURCE) + +SOURCES=readconfig.c luaextl.c misc.c + +HEADERS=readconfig.h extl.h luaextl.h private.h types.h + +TARGETS=libextl.a libextl-mkexports + +###################################### + +include $(TOPDIR)/build/rules.mk + +###################################### + +libextl.a: $(OBJS) + $(AR) $(ARFLAGS) $@ $+ + $(RANLIB) $@ + +libextl-mkexports: libextl-mkexports.in + sed "1s:LUA50:$(LUA):" $< > $@ + +install: + $(INSTALLDIR) $(BINDIR) + $(INSTALL) -m $(BIN_MODE) libextl-mkexports $(BINDIR) + $(INSTALLDIR) $(LIBDIR) + $(INSTALL) -m $(DATA_MODE) libextl.a $(LIBDIR) + $(INSTALLDIR) $(INCDIR) + for h in $(HEADERS); do \ + $(INSTALL) -m $(DATA_MODE) $$h $(INCDIR); \ + done diff --git a/libextl/README b/libextl/README new file mode 100644 index 0000000..dee3de7 --- /dev/null +++ b/libextl/README @@ -0,0 +1,116 @@ + +libextl + +Copyright (c) Tuomo Valkonen 2003-2005. + + +Libextl is a small library for very easily extending programs with Lua +scripting. By default the library depends on my libtu available from the +same repository. However, this can be changed by redefining things in +private.h and types.h. + +Libextl supports exporting functions that operate on basic data types (int, +bool, double, [const] char*) and references to Lua tables and functions +(ExtlTab, ExtlFn) simply by prefixing the function definition with the +keywords EXTL_EXPORT, EXTL_EXPORT_AS or EXTL_EXPORT_MEMBER. More complex +data must, however, either be proxied libtu objects (or objects of some +other object system with the appropriate macros redefined), or Lua tables. +The binding glue is, however, generated as painlessly as for functions that +operate on basic data types with all pointers to a type with a name that +begins with an uppercase letter considered as such objects. Libextl also +provides functions to manipulate Lua tables through references to these, and +ways to call and load Lua code. + + +BASIC USAGE + +Include in your source files. + +In your Makefile, process source files with libextl-mkexports to generate +exports.c. Pass it the option '-module modname' if you want non-class +functions to be put in a table "modname". (This will however introduce +naming restrictions on the C side; see below.) + +Call 'exit_init' and '[modname_]register_exports' at the beginning of your +program and '[modname_]unregister_exports' and 'extl_deinit' at the end of +it. + +Mark exported functions as follows in your source files: + + * Global or module functions: + + EXTL_EXPORT + int foobar(const char *s) + { + ... + } + + * Classes and methods: + + EXTL_CLASS(Foo, Obj) + + EXTL_EXPORT_MEMBER + void foo_set_callback(Foo *cls, ExtlFn fn) + + 'Obj' here stands for the base class of the object system (one in libtu + by default), but more generally the second parameter to EXTL_CLASS must + be the name of parent class of the first parameter. + + * Export in given module (plain table) or class: + + EXTL_EXPORT_AS(baz, somefun) + ExtlTab just_some_name_we_dont_use_on_the_lua_side() + + +If you pass libextl-mkexport the option '-module modname', then all +EXTL_EXPORTed functions must be prefixed with 'modname_' and will be put in +the global table 'modname'. If you want to export a function directly in the +global namespace when building a module, use EXTL_EXPORT_AS(global, +funcname). + + +ADDITIONAL ROUTINES + +'luaextl.h' lists a number of routines for accessing references to tables, +and calling Lua functions. How to use them should be fairly obvious. + +'readconfig.h' lists a number of routines to load source or compiled files +from a specified path. Their usage should, again, be fairly obvious. These +lookup routines are exported to the Lua side as well in the form of the +function 'dopath'. + + +USING ANOTHER OBJECT SYSTEM/USING WITHOUT LIBTU + +Redefine appropriate macros in private.h and types.h. + + +NOTES ON DATA REFERENCES + +* CHAR* VS. CONST CHAR* + +libextl follows the following conventions with regard to const and non-const +char* pointers: + +'const char*' as parameter is considered owned by the caller, and the called +function must do nothing beyond accessing it during its execution. + +'char*' as parameter is considered owned by the called function, and it must +take care of freeing the parameter when no longer needed. + +'const char*' as return value is considered owned by the called function, +and caller must consider it unsafe to use after subsequent calls to related +code. + +'char*' as return value is considered owned by the caller, and it must take +care of freeing the value when no longer needed. + + +* EXTLTAB AND EXTLFN + +These references are always owned as a caller. Thus, if a called function +wants to store a reference to such a parameter, it must create a new one +with extl_ref_fn or extl_ref_table. Note that these functions do not return +the same reference as passed; there are no reference counters, just a table +of references to hold the garbage collector from claiming the object +(luaL_ref/luaL_unref). diff --git a/libextl/build/system-inc.mk b/libextl/build/system-inc.mk new file mode 100644 index 0000000..5390bc9 --- /dev/null +++ b/libextl/build/system-inc.mk @@ -0,0 +1,2 @@ +TOPDIR := $(TOPDIR)/.. +include $(TOPDIR)/build/system-inc.mk diff --git a/libextl/exact-version b/libextl/exact-version new file mode 100644 index 0000000..3ff2401 --- /dev/null +++ b/libextl/exact-version @@ -0,0 +1,81 @@ + +Context: + +[Updated *.mk locations. +Tuomo Valkonen **20060803211018] + +[Empty initialiser in libextl-mkexports was missing 'untraced'. +Tuomo Valkonen **20060516162021] + +[lua5.1 changes that aren't backwards compatible +Etan Reisner **20060322164425] + +[lua5.1 changes that should be backwards compatible +Etan Reisner **20060322163746] + +[Added untraced-option for exports. +Tuomo Valkonen **20060506211657] + +[Oops. Path lookup order was ignoring user cfgfiles after previous changes. +Tuomo Valkonen **20050928194026] + +[Added protect mode check function protected(fn|nil). +Tuomo Valkonen **20050903145957] + +[Oops, do go through files even if cfdir is NULL. +Tuomo Valkonen **20050818171629] + +[Changes in directory lookup order/policy. +Tuomo Valkonen **20050817215809] + +[Also try to read config files without an extension. +Per Olofsson **20050816144735 + + This change is needed so that /etc/default/ion3 can be read by the + ion3 Debian package. +] + +[Improvements to dead object handling. +Tuomo Valkonen **20050703204209] + +[Improved error reporting on passing around dead objects. +Tuomo Valkonen **20050702205641] + +[Documentation in Lua code wasn't being parsed anymore. +Tuomo Valkonen **20050605194016 + (Dynamic typing and changes in part of code...) +] + +[Added -reexport option. +Tuomo Valkonen **20050421223739] + +[libextl-mkexports can now generate exports.h. +Tuomo Valkonen **20050319191302] + +[README improvements. +Tuomo Valkonen **20050316110102] + +[It's 2005 already! (Updated copyright notices.) +Tuomo Valkonen **20050314101157] + +[Exported classes must now be marked for libextl-mkexports. +Tuomo Valkonen **20050308082415 + + - EXTL_EXPORT before IMPLCLASS. + +] + +[Added "protected mode". +Tuomo Valkonen **20050301224511 + + - Exports can be marked "safe" to be called at any time with EXTL_EXPORT. + + - New "safelist" API. + +] + +[Execute install-sh through sh. +Tuomo Valkonen **20050301214534] + +[TAG libextl-3-svn2darcs +Tuomo Valkonen **20050215180651] diff --git a/libextl/extl.h b/libextl/extl.h new file mode 100644 index 0000000..8de38d3 --- /dev/null +++ b/libextl/extl.h @@ -0,0 +1,26 @@ +/* + * libextl/extl.h + * + * Copyright (c) Tuomo Valkonen 1999-2005. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + */ + +#ifndef LIBEXTL_EXTL_H +#define LIBEXTL_EXTL_H + +#include "luaextl.h" + +/* Keywords for libextl-mkexports script */ + +#define EXTL_EXPORT +#define EXTL_EXPORT_AS(T, F) +#define EXTL_EXPORT_MEMBER +#define EXTL_CLASS(CLS, PARCLS) +#define EXTL_SAFE +#define EXTL_UNTRACED + +#endif /* LIBEXTL_EXTL_H */ diff --git a/libextl/install-sh b/libextl/install-sh new file mode 100644 index 0000000..e9de238 --- /dev/null +++ b/libextl/install-sh @@ -0,0 +1,251 @@ +#!/bin/sh +# +# install - install a program, script, or datafile +# This comes from X11R5 (mit/util/scripts/install.sh). +# +# Copyright 1991 by the Massachusetts Institute of Technology +# +# Permission to use, copy, modify, distribute, and sell this software and its +# documentation for any purpose is hereby granted without fee, provided that +# the above copyright notice appear in all copies and that both that +# copyright notice and this permission notice appear in supporting +# documentation, and that the name of M.I.T. not be used in advertising or +# publicity pertaining to distribution of the software without specific, +# written prior permission. M.I.T. makes no representations about the +# suitability of this software for any purpose. It is provided "as is" +# without express or implied warranty. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. It can only install one file at a time, a restriction +# shared with many OS's install programs. + + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" +mkdirprog="${MKDIRPROG-mkdir}" + +transformbasename="" +transform_arg="" +instcmd="$mvprog" +chmodcmd="$chmodprog 0755" +chowncmd="" +chgrpcmd="" +stripcmd="" +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src="" +dst="" +dir_arg="" + +while [ x"$1" != x ]; do + case $1 in + -c) instcmd="$cpprog" + shift + continue;; + + -d) dir_arg=true + shift + continue;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + -s) stripcmd="$stripprog" + shift + continue;; + + -t=*) transformarg=`echo $1 | sed 's/-t=//'` + shift + continue;; + + -b=*) transformbasename=`echo $1 | sed 's/-b=//'` + shift + continue;; + + *) if [ x"$src" = x ] + then + src=$1 + else + # this colon is to work around a 386BSD /bin/sh bug + : + dst=$1 + fi + shift + continue;; + esac +done + +if [ x"$src" = x ] +then + echo "install: no input file specified" + exit 1 +else + true +fi + +if [ x"$dir_arg" != x ]; then + dst=$src + src="" + + if [ -d $dst ]; then + instcmd=: + chmodcmd="" + else + instcmd=mkdir + fi +else + +# Waiting for this to be detected by the "$instcmd $src $dsttmp" command +# might cause directories to be created, which would be especially bad +# if $src (and thus $dsttmp) contains '*'. + + if [ -f $src -o -d $src ] + then + true + else + echo "install: $src does not exist" + exit 1 + fi + + if [ x"$dst" = x ] + then + echo "install: no destination specified" + exit 1 + else + true + fi + +# If destination is a directory, append the input filename; if your system +# does not like double slashes in filenames, you may need to add some logic + + if [ -d $dst ] + then + dst="$dst"/`basename $src` + else + true + fi +fi + +## this sed command emulates the dirname command +dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` + +# Make sure that the destination directory exists. +# this part is taken from Noah Friedman's mkinstalldirs script + +# Skip lots of stat calls in the usual case. +if [ ! -d "$dstdir" ]; then +defaultIFS=' +' +IFS="${IFS-${defaultIFS}}" + +oIFS="${IFS}" +# Some sh's can't handle IFS=/ for some reason. +IFS='%' +set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` +IFS="${oIFS}" + +pathcomp='' + +while [ $# -ne 0 ] ; do + pathcomp="${pathcomp}${1}" + shift + + if [ ! -d "${pathcomp}" ] ; + then + $mkdirprog "${pathcomp}" + else + true + fi + + pathcomp="${pathcomp}/" +done +fi + +if [ x"$dir_arg" != x ] +then + $doit $instcmd $dst && + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi +else + +# If we're going to rename the final executable, determine the name now. + + if [ x"$transformarg" = x ] + then + dstfile=`basename $dst` + else + dstfile=`basename $dst $transformbasename | + sed $transformarg`$transformbasename + fi + +# don't allow the sed command to completely eliminate the filename + + if [ x"$dstfile" = x ] + then + dstfile=`basename $dst` + else + true + fi + +# Make a temp file name in the proper directory. + + dsttmp=$dstdir/#inst.$$# + +# Move or copy the file name to the temp name + + $doit $instcmd $src $dsttmp && + + trap "rm -f ${dsttmp}" 0 && + +# and set any options; do chmod last to preserve setuid bits + +# If any of these fail, we abort the whole thing. If we want to +# ignore errors from any of these, just make sure not to ignore +# errors from the above "$doit $instcmd $src $dsttmp" command. + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && + +# Now rename the file to the real destination. + + $doit $rmcmd -f $dstdir/$dstfile && + $doit $mvcmd $dsttmp $dstdir/$dstfile + +fi && + + +exit 0 diff --git a/libextl/libextl-mkexports.in b/libextl/libextl-mkexports.in new file mode 100644 index 0000000..ff1cac3 --- /dev/null +++ b/libextl/libextl-mkexports.in @@ -0,0 +1,803 @@ +#!LUA50 +-- -*- mode: lua -*- +-- ion/mkexports.lua +-- +-- Copyright (c) Tuomo Valkonen 2003-2005. +-- +-- Ion is free software; you can redistribute it and/or modify it under +-- the terms of the GNU Lesser General Public License as published by +-- the Free Software Foundation; either version 2.1 of the License, or +-- (at your option) any later version. +-- +-- +-- This is a script to automatically generate exported function registration +-- code and documentation for those from C source. +-- +-- The script can also parse documentation comments from Lua code. +-- + +-- Helper functions {{{ + +function errorf(fmt, ...) + error(string.format(fmt, unpack(arg)), 2) +end + +function matcherr(s) + error(string.format("Parse error in \"%s...\"", string.sub(s, 1, 50)), 2) +end + +function fprintf(h, fmt, ...) + h:write(string.format(fmt, unpack(arg))) +end + +function trim(str) + return string.gsub(str, "^[%s\n]*(.-)[%s\n]*$", "%1") +end + +-- }}} + + +-- Some conversion tables {{{ + +desc2ct={ + ["v"]="void", + ["i"]="int", + ["d"]="double", + ["b"]="bool", + ["t"]="ExtlTab", + ["f"]="ExtlFn", + ["o"]="Obj*", + ["s"]="char*", + ["S"]="const char*", +} + +ct2desc={ + ["uint"] = "i", +} + +for d, t in pairs(desc2ct) do + ct2desc[t]=d +end + +desc2human={ + ["v"]="void", + ["i"]="integer", + ["d"]="double", + ["b"]="bool", + ["t"]="table", + ["f"]="function", + ["o"]="object", + ["s"]="string", + ["S"]="string", +} + +-- }}} + + +-- Parser {{{ + +local classes={} +local chnds={} +local reexports={} + +function add_chnd(fnt) + local odesc=string.gsub(fnt.odesc, "S", "s") + local idesc=string.gsub(fnt.idesc, "S", "s") + local str="l2chnd_" .. odesc .. "_" .. idesc .. "_" + + for i, t in ipairs(fnt.itypes) do + str=str .. "_" .. t + end + + chnds[str]={odesc=odesc, idesc=idesc, itypes=fnt.itypes} + fnt.chnd=str +end + +function add_class(cls) + if cls~="Obj" and not classes[cls] then + classes[cls]={} + end +end + +function sort_classes(cls) + local sorted={} + local inserted={} + + local function insert(cls) + if classes[cls] and not inserted[cls] then + if classes[cls].parent then + insert(classes[cls].parent) + end + inserted[cls]=true + table.insert(sorted, cls) + end + end + + for cls in pairs(classes) do + insert(cls) + end + + return sorted +end + +function parse_type(t) + local desc, otype, varname="?", "", "" + + -- Remove whitespace at start end end of the string and compress elsewhere. + t=string.gsub(trim(t), "[%s\n]+", " ") + -- Remove spaces around asterisks. + t=string.gsub(t, " *%* *", "*") + -- Add space after asterisks. + t=string.gsub(t, "%*", "* ") + + -- Check for const + local is_const="" + local s, e=string.find(t, "^const +") + if s then + is_const="const " + t=string.sub(t, e+1) + end + + -- Find variable name part + tn=t + s, e=string.find(tn, " ") + if s then + varname=string.sub(tn, e+1) + tn=string.sub(tn, 1, s-1) + assert(not string.find(varname, " ")) + end + + -- Try to check for supported types + desc = ct2desc[is_const .. tn] + + if not desc or desc=="o" then + s, e=string.find(tn, "^[A-Z][%w_]*%*$") + if s then + desc="o" + otype=string.sub(tn, s, e-1) + add_class(otype) + else + errorf("Error parsing type from \"%s\"", t) + end + end + + return desc, otype, varname +end + +function parse(d) + local doc=nil + local safe=false + local untraced=false + + -- Handle /*EXTL_DOC ... */ + local function do_doc(s) + --s=string.gsub(s, "/%*EXTL_DOC(.-)%*/", "%1") + local st=string.len("/*EXTL_DOC") + local en, _=string.find(s, "%*/") + if not en then + errorf("Could not find end of comment in \"%s...\"", + string.sub(s, 1, 50)) + end + + s=string.sub(s, st+1, en-1) + s=string.gsub(s, "\n[%s]*%*", "\n") + doc=s + end + + -- Handle EXTL_SAFE + local function do_safe(s) + assert(not safe) + safe=true + end + + -- Handle EXTL_UNTRACED + local function do_untraced(s) + assert(not untraced) + untraced=true + end + + local function do_do_export(cls, efn, ot, fn, param) + local odesc, otype=parse_type(ot) + local idesc, itypes, ivars="", {}, {} + + -- Parse arguments + param=string.sub(param, 2, -2) + if string.find(param, "[()]") then + errorf("Error: parameters to %s contain parantheses", fn) + end + param=trim(param) + if string.len(param)>0 then + for p in string.gfind(param .. ",", "([^,]*),") do + local spec, objtype, varname=parse_type(p) + idesc=idesc .. spec + table.insert(itypes, objtype) + table.insert(ivars, varname) + end + end + + if cls=="?" then + if string.sub(idesc, 1, 1)~="o" then + error("Invalid class for " .. fn) + end + cls=itypes[1] + end + + -- Generate call handler name + + local fninfo={ + doc=doc, + safe=safe, + untraced=untraced, + odesc=odesc, + otype=otype, + idesc=idesc, + itypes=itypes, + ivars=ivars, + exported_name=efn, + class=cls, + } + + add_chnd(fninfo) + add_class(cls) + + if not classes[cls].fns then + classes[cls].fns={} + end + + assert(not classes[cls].fns[fn], "Function " .. fn .. " multiply defined!") + + classes[cls].fns[fn]=fninfo + + -- Reset + doc=nil + safe=false + untraced=false + end + + -- Handle EXTL_EXPORT otype fn(args) + local function do_export(s) + local mdl, efn + local pat="EXTL_EXPORT[%s\n]+([%w%s_*]+[%s\n*])([%w_]+)[%s\n]*(%b())" + local st, en, ot, fn, param=string.find(s, pat) + + if not st then matcherr(s) end + + if module=="global" or not module then + efn=fn + mdl=module + else + st, en, efn=string.find(fn, "^"..module.."_(.*)") + if efn then + mdl=module + else + for k in pairs(reexports) do + st, en, efn=string.find(fn, "^"..k.."_(.*)") + if efn then + mdl=module + break + end + end + end + + if not mdl then + error('"'..fn..'" is not a valid function name of format '.. + 'modulename_fnname.') + end + end + do_do_export(module, efn, ot, fn, param) + end + + -- Handle EXTL_EXPORT_MEMBER otype prefix_fn(class, args) + local function do_export_member(s) + local pat="EXTL_EXPORT_MEMBER[%s\n]+([%w%s_*]+[%s\n*])([%w_]+)[%s\n]*(%b())" + local st, en, ot, fn, param=string.find(s, pat) + if not st then matcherr(s) end + local efn=string.gsub(fn, ".-_(.*)", "%1") + do_do_export("?", efn, ot, fn, param) + end + + -- Handle EXTL_EXPORT_AS(table, member_fn) otype fn(args) + local function do_export_as(s) + local pat="EXTL_EXPORT_AS%(%s*([%w_]+)%s*,%s*([%w_]+)%s*%)[%s\n]+([%w%s_*]+[%s\n*])([%w_]+)[%s\n]*(%b())" + local st, en, cls, efn, ot, fn, param=string.find(s, pat) + if not st then matcherr(s) end + do_do_export((reexports[cls] and module or cls), efn, ot, fn, param) + end + + local function do_implobj(s) + local pat="IMPLCLASS%(%s*([%w_]+)%s*,%s*([%w_]+)%s*,[^)]*%)" + local st, en, cls, par=string.find(s, pat) + if not st then matcherr(s) end + add_class(cls) + classes[cls].parent=par + end + + local function do_class(s) + local pat="EXTL_CLASS%(%s*([%w_]+)%s*,%s*([%w_]+)%s*%)" + local st, en, cls, par=string.find(s, pat) + if not st then matcherr(s) end + add_class(cls) + classes[cls].parent=par + end + + local lookfor={ + {"/%*EXTL_DOC", do_doc}, + {"[%s\n]EXTL_SAFE[%s\n]", do_safe}, + {"[%s\n]EXTL_UNTRACED[%s\n]", do_untraced}, + {"[%s\n]EXTL_EXPORT[%s\n]+IMPLCLASS", do_implobj}, + {"[%s\n]EXTL_EXPORT[%s\n]", do_export}, + {"[%s\n]EXTL_EXPORT_AS", do_export_as}, + {"[%s\n]EXTL_EXPORT_MEMBER[%s\n]", do_export_member}, + {"[%s\n]EXTL_CLASS", do_class}, + } + + do_parse(d, lookfor) +end + +function do_parse(d, lookfor) + while true do + local mins, mine, minfn=string.len(d)+1, nil, nil + for _, lf in ipairs(lookfor) do + local s, e=string.find(d, lf[1]) + if s and s +#include + +]]) + -- end blockwrite + + -- Write class infos and check that the class is implemented in the + -- module. + for c, data in pairs(classes) do + if string.lower(c)==c then + data.module=true + else + fprintf(h, "EXTL_DEFCLASS(%s);\n", c) + if data.fns and not data.parent then + error(c..": Methods can only be registered if the class " + .. "is implemented in the module in question.") + end + end + end + + -- Write L2 call handlers + for name, info in pairs(chnds) do + writechnd(h, name, info) + end + + fprintf(h, "\n") + + for cls, data in pairs(classes) do + if data.fns then + -- Write function declarations + for fn in pairs(data.fns) do + fprintf(h, "extern void %s();\n", fn) + end + -- Write function table + write_class_fns(h, cls, data) + else + fprintf(h, "#define %s_exports NULL\n", cls) + end + end + + fprintf(h, "bool %sregister_exports()\n{\n", pfx(module)) + + local sorted_classes=sort_classes() + + for _, cls in pairs(sorted_classes) do + if cls=="global" then + fprintf(h, " if(!extl_register_functions(global_exports)) return FALSE;\n") + elseif classes[cls].module then + fprintf(h, " if(!extl_register_module(\"%s\", %s_exports)) return FALSE;\n", + cls, cls) + elseif classes[cls].parent then + fprintf(h, " if(!extl_register_class(\"%s\", %s_exports, \"%s\")) return FALSE;\n", + cls, cls, classes[cls].parent) + end + end + + fprintf(h, " return TRUE;\n}\n\nvoid %sunregister_exports()\n{\n", + pfx(module)) + + for _, cls in pairs(sorted_classes) do + if cls=="global" then + fprintf(h, " extl_unregister_functions(global_exports);\n") + elseif classes[cls].module then + fprintf(h, " extl_unregister_module(\"%s\", %s_exports);\n", + cls, cls) + elseif classes[cls].parent then + fprintf(h, " extl_unregister_class(\"%s\", %s_exports);\n", + cls, cls) + end + end + + fprintf(h, "}\n\n") +end + + +function write_header(h) + local p=pfx(module) + local u=string.upper(p) + fprintf(h, [[ +/* Automatically generated by mkexports.lua */ +#ifndef %sEXTL_EXPORTS_H +#define %sEXTL_EXPORTS_H + +#include + +extern bool %sregister_exports(); +extern void %sunregister_exports(); + +#endif /* %sEXTL_EXPORTS_H */ + +]], u, u, p, p, u) +end + +-- }}} + + +-- Documentation output {{{ + +function tohuman(desc, objtype) + if objtype~="" then + return objtype + else + return desc2human[desc] + end +end + +function texfriendly(name) + return string.gsub(name, "_", "-") +end + +function texfriendly_typeormod(nm) + if string.find(nm, "A-Z") then + return "\\type{"..string.gsub(nm, '_', '\_').."}" + else + return "\\code{"..nm.."}" + end +end + +function write_fndoc(h, fn, info) + if not info.doc then + return + end + fprintf(h, "\\begin{function}\n") + if info.exported_name then + fn=info.exported_name + end + + if info.class~="global" then + fprintf(h, "\\index{%s@%s!", texfriendly(info.class), + texfriendly_typeormod(info.class)); + fprintf(h, "%s@\\code{%s}}\n", texfriendly(fn), fn) + end + fprintf(h, "\\index{%s@\\code{%s}}\n", texfriendly(fn), fn) + + if info.class~="global" then + fprintf(h, "\\hyperlabel{fn:%s.%s}", info.class, fn) + else + fprintf(h, "\\hyperlabel{fn:%s}", fn) + end + + fprintf(h, "\\synopsis{") + if info.odesc then + h:write(tohuman(info.odesc, info.otype).." ") + end + + if info.class~="global" then + fprintf(h, "%s.", info.class) + end + + if not info.ivars then + -- Lua input + fprintf(h, "%s%s}", fn, info.paramstr) + else + fprintf(h, "%s(", fn) + local comma="" + for i, varname in pairs(info.ivars) do + fprintf(h, comma .. "%s", tohuman(string.sub(info.idesc, i, i), + info.itypes[i])) + if varname then + fprintf(h, " %s", varname) + end + comma=", " + end + fprintf(h, ")}\n") + end + h:write("\\begin{funcdesc}\n" .. trim(info.doc).. "\n\\end{funcdesc}\n") + fprintf(h, "\\end{function}\n\n") +end + + +function write_class_documentation(h, cls, in_subsect) + sorted={} + + if not classes[cls] or not classes[cls].fns then + return + end + + if in_subsect then + fprintf(h, "\n\n\\subsection{\\type{%s} functions}\n\n", cls) + end + + for fn in pairs(classes[cls].fns) do + table.insert(sorted, fn) + end + table.sort(sorted) + + for _, fn in ipairs(sorted) do + write_fndoc(h, fn, classes[cls].fns[fn]) + end +end + + +function write_documentation(h) + sorted={} + + write_class_documentation(h, module, false) + + for cls in pairs(classes) do + if cls~=module then + table.insert(sorted, cls) + end + end + table.sort(sorted) + + for _, cls in ipairs(sorted) do + write_class_documentation(h, cls, true) + end +end + +-- }}} + + +-- main {{{ + +inputs={} +outh=io.stdout +header_file=nil +output_file=nil +make_docs=false +module="global" +i=1 + +function usage() + print([[ +Usage: libextl-mkexports [options] files... + +Where options include: + -mkdoc + -help + -o outfile + -h header + -module module + -reexport module +]]) + os.exit() +end + +while arg[i] do + if arg[i]=="-help" then + usage() + elseif arg[i]=="-mkdoc" then + make_docs=true + elseif arg[i]=="-o" then + i=i+1 + output_file=arg[i] + elseif arg[i]=="-h" then + i=i+1 + header_file=arg[i] + elseif arg[i]=="-module" then + i=i+1 + module=arg[i] + if not module then + error("No module given") + end + elseif arg[i]=="-reexport" then + i=i+1 + reexports[arg[i]]=true + else + table.insert(inputs, arg[i]) + end + i=i+1 +end + +if table.getn(inputs)==0 then + usage() +end + +for _, ifnam in pairs(inputs) do + h, err=io.open(ifnam, "r") + if not h then + errorf("Could not open %s: %s", ifnam, err) + end + print("Scanning " .. ifnam .. " for exports.") + data=h:read("*a") + h:close() + if string.find(ifnam, "%.lua$") then + assert(make_docs) + parse_luadoc("\n" .. data .. "\n") + elseif string.find(ifnam, "%.c$") then + parse("\n" .. data .. "\n") + else + error('Unknown file') + end + +end + +if output_file then + outh, err=io.open(output_file, "w") + if not outh then + error(err) + end +end + +if make_docs then + write_documentation(outh) +else + write_exports(outh) + if header_file then + local hh, err=io.open(header_file, "w") + if not hh then + error(err) + end + write_header(hh) + hh:close() + end +end + +-- }}} + diff --git a/libextl/luaextl.c b/libextl/luaextl.c new file mode 100644 index 0000000..c53fde6 --- /dev/null +++ b/libextl/luaextl.c @@ -0,0 +1,2439 @@ +/* + * libextl/luaextl.c + * + * Copyright (c) Tuomo Valkonen 1999-2005. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "readconfig.h" +#include "luaextl.h" +#include "private.h" + +#define MAGIC 0xf00ba7 + +/* Maximum number of parameters and return values for calls from Lua + * and (if va_copy is not available) return value from Lua functions. + */ +#define MAX_PARAMS 16 + +static lua_State *l_st=NULL; + +static bool extl_stack_get(lua_State *st, int pos, char type, + bool copystring, bool *wasdeadobject, + void *valret); + +static int extl_protected(lua_State *st); + +#ifdef EXTL_LOG_ERRORS +static void flushtrace(); +#else +#define flushtrace() +#endif + + +/*{{{ Safer rawget/set/getn */ + + +#define CHECK_TABLE(ST, INDEX) luaL_checktype(ST, INDEX, LUA_TTABLE) + +static int luaL_getn_check(lua_State *st, int index) +{ + CHECK_TABLE(st, index); + return luaL_getn(st, index); +} + + +static void lua_rawset_check(lua_State *st, int index) +{ + CHECK_TABLE(st, index); + lua_rawset(st, index); +} + + +static void lua_rawseti_check(lua_State *st, int index, int n) +{ + CHECK_TABLE(st, index); + lua_rawseti(st, index, n); +} + + +static void lua_rawget_check(lua_State *st, int index) +{ + CHECK_TABLE(st, index); + lua_rawget(st, index); +} + + +static void lua_rawgeti_check(lua_State *st, int index, int n) +{ + CHECK_TABLE(st, index); + lua_rawgeti(st, index, n); +} + + +/*}}}*/ + + +/*{{{ A cpcall wrapper */ + + +typedef bool ExtlCPCallFn(lua_State *st, void *ptr); + + +typedef struct{ + ExtlCPCallFn *fn; + void *udata; + bool retval; +} ExtlCPCallParam; + + +static int extl_docpcall(lua_State *st) +{ + ExtlCPCallParam *param=(ExtlCPCallParam*)lua_touserdata(st, -1); + + /* Should be enough for most things */ + if(!lua_checkstack(st, 8)){ + extl_warn(TR("Lua stack full.")); + return 0; + } + + param->retval=param->fn(st, param->udata); + return 0; +} + + +static bool extl_cpcall(lua_State *st, ExtlCPCallFn *fn, void *ptr) +{ + ExtlCPCallParam param; + int oldtop=lua_gettop(st); + int err; + + param.fn=fn; + param.udata=ptr; + param.retval=FALSE; + + + err=lua_cpcall(st, extl_docpcall, ¶m); + if(err==LUA_ERRRUN){ + extl_warn("%s", lua_tostring(st, -1)); + }else if(err==LUA_ERRMEM){ + extl_warn("%s", strerror(ENOMEM)); + }else if(err!=0){ + extl_warn(TR("Unknown Lua error.")); + } + + lua_settop(st, oldtop); + + return param.retval; +} + + +/*}}}*/ + + +/*{{{ Obj userdata handling -- unsafe */ + + +static int owned_cache_ref=LUA_NOREF; + + +static Obj *extl_get_obj(lua_State *st, int pos, + bool *invalid, bool *dead) +{ + int val; + + *dead=FALSE; + *invalid=TRUE; + + if(!lua_isuserdata(st, pos)){ + *invalid=!lua_isnil(st, pos); + return NULL; + } + + if(!lua_getmetatable(st, pos)) + return NULL; + + /* If the userdata object is a proper Obj, metatable[MAGIC] must + * have been set to MAGIC. + */ + lua_pushnumber(st, MAGIC); + lua_gettable(st, -2); + val=lua_tonumber(st, -1); + lua_pop(st, 2); + + if(val==MAGIC){ + ExtlProxy *proxy=(ExtlProxy*)lua_touserdata(st, pos); + + *invalid=FALSE; + + if(proxy!=NULL){ + Obj *obj=EXTL_PROXY_OBJ(proxy); + if(obj==NULL){ + *dead=TRUE; + *invalid=TRUE; + } + return obj; + } + } + + return NULL; +} + + +static void extl_uncache_(lua_State *st, Obj *obj) +{ + if(EXTL_OBJ_OWNED(obj)){ + lua_rawgeti(st, LUA_REGISTRYINDEX, owned_cache_ref); + lua_pushlightuserdata(st, obj); + lua_pushnil(st); + lua_rawset(st, -3); + }else{ + lua_pushlightuserdata(st, obj); + lua_pushnil(st); + lua_rawset(st, LUA_REGISTRYINDEX); + } +} + + +void extl_uncache(Obj *obj) +{ + extl_cpcall(l_st, (ExtlCPCallFn*)extl_uncache_, obj); +} + + +static void extl_push_obj(lua_State *st, Obj *obj) +{ + ExtlProxy *proxy; + + if(obj==NULL){ + lua_pushnil(st); + return; + } + + if(EXTL_OBJ_CACHED(obj)){ + if(EXTL_OBJ_OWNED(obj)){ + lua_rawgeti(st, LUA_REGISTRYINDEX, owned_cache_ref); + lua_pushlightuserdata(st, obj); + lua_rawget(st, -2); + lua_remove(st, -2); /* owned_cache */ + }else{ + lua_pushlightuserdata(st, obj); + lua_rawget(st, LUA_REGISTRYINDEX); + } + if(lua_isuserdata(st, -1)){ + D(fprintf(stderr, "found %p cached\n", obj)); + return; + } + lua_pop(st, 1); + } + + D(fprintf(stderr, "Creating %p\n", obj)); + + proxy=(ExtlProxy*)lua_newuserdata(st, sizeof(ExtlProxy)); + + /* Lua shouldn't return if the allocation fails */ + + lua_pushfstring(st, "luaextl_%s_metatable", OBJ_TYPESTR(obj)); + lua_gettable(st, LUA_REGISTRYINDEX); + if(lua_isnil(st, -1)){ + lua_pop(st, 2); + lua_pushnil(st); + }else{ + lua_setmetatable(st, -2); + + /* Store in cache */ + if(EXTL_OBJ_OWNED(obj)){ + lua_rawgeti(st, LUA_REGISTRYINDEX, owned_cache_ref); + lua_pushlightuserdata(st, obj); + lua_pushvalue(st, -3); /* the WWatch */ + lua_rawset_check(st, -3); + lua_pop(st, 1); /* owned_cache */ + }else{ + lua_pushlightuserdata(st, obj); + lua_pushvalue(st, -2); /* the WWatch */ + lua_rawset_check(st, LUA_REGISTRYINDEX); + } + EXTL_BEGIN_PROXY_OBJ(proxy, obj); + } +} + + +/*{{{ Functions available to Lua code */ + + +static int extl_obj_gc_handler(lua_State *st) +{ + ExtlProxy *proxy; + bool dead=FALSE, invalid=FALSE; + Obj *obj; + + obj=extl_get_obj(st, 1, &invalid, &dead); + + if(obj==NULL){ + /* This should not happen, actually. Our object cache should + * hold references to all objects seen on the Lua side until + * they are destroyed. + */ + return 0; + } + + proxy=(ExtlProxy*)lua_touserdata(st, 1); + + if(proxy!=NULL) + EXTL_END_PROXY_OBJ(proxy, obj); + + if(EXTL_OBJ_OWNED(obj)) + EXTL_DESTROY_OWNED_OBJ(obj); + + return 0; +} + + +static int extl_obj_typename(lua_State *st) +{ + Obj *obj=NULL; + + if(!extl_stack_get(st, 1, 'o', FALSE, NULL, &obj) || obj==NULL) + return 0; + + lua_pushstring(st, EXTL_OBJ_TYPENAME(obj)); + return 1; +} + +/* Dummy code for documentation generation. */ + +/*EXTL_DOC + * Return type name of \var{obj}. + */ +EXTL_EXPORT_AS(global, obj_typename) +const char *__obj_typename(Obj *obj); + + +static int extl_obj_exists(lua_State *st) +{ + Obj *obj=NULL; + + extl_stack_get(st, 1, 'o', FALSE, NULL, &obj); + + lua_pushboolean(st, obj!=NULL); + + return 1; +} + +/* Dummy code for documentation generation. */ + +/*EXTL_DOC + * Does \var{obj} still exist on the C side of Ion? + */ +EXTL_EXPORT_AS(global, obj_exists) +bool __obj_exists(Obj *obj); + + +static int extl_obj_is(lua_State *st) +{ + Obj *obj=NULL; + const char *tn; + + extl_stack_get(st, 1, 'o', FALSE, NULL, &obj); + + if(obj==NULL){ + lua_pushboolean(st, 0); + }else{ + tn=lua_tostring(st, 2); + lua_pushboolean(st, EXTL_OBJ_IS(obj, tn)); + } + + return 1; +} + +/* Dummy code for documentation generation. */ + +/*EXTL_DOC + * Is \var{obj} of type \var{typename}. + */ +EXTL_EXPORT_AS(global, obj_is) +bool __obj_is(Obj *obj, const char *typename); + + +static int extl_current_file_or_dir(lua_State *st, bool dir) +{ + int r; + lua_Debug ar; + const char *s, *p; + + if(lua_getstack(st, 1, &ar)!=1) + goto err; + if(lua_getinfo(st, "S", &ar)==0) + goto err; + + if(ar.source==NULL || ar.source[0]!='@') + return 0; /* not a file */ + + s=ar.source+1; + + if(!dir){ + lua_pushstring(st, s); + }else{ + p=strrchr(s, '/'); + if(p==NULL){ + lua_pushstring(st, "."); + }else{ + lua_pushlstring(st, s, p-s); + } + } + return 1; + +err: + extl_warn("Unable to get caller file from stack."); + return 0; +} + + +static int extl_dopath(lua_State *st) +{ + const char *toincl, *cfdir; + bool res, complain; + + toincl=luaL_checkstring(st, 1); + complain=!lua_toboolean(st, 2); + + if(extl_current_file_or_dir(st, TRUE)!=1){ + res=extl_read_config(toincl, NULL, complain); + }else{ + cfdir=lua_tostring(st, -1); + res=extl_read_config(toincl, cfdir, complain); + lua_pop(st, 1); + } + lua_pushboolean(st, res); + return 1; +} + +/* Dummy code for documentation generation. */ + +/*EXTL_DOC + * Look up and execute another file with Lua code. + */ +EXTL_EXPORT_AS(global, dopath) +bool dopath(const char *what); + + +/*}}}*/ + + +static bool extl_init_obj_info(lua_State *st) +{ + static ExtlExportedFnSpec dummy[]={ + {NULL, NULL, NULL, NULL, NULL, FALSE, FALSE} + }; + + extl_register_class("Obj", dummy, NULL); + + /* Create cache for proxies to objects owned by Lua-side. + * These need to be in a weak table to ever be collected. + */ + lua_newtable(st); + lua_newtable(st); + lua_pushstring(st, "__mode"); + lua_pushstring(st, "v"); + lua_rawset_check(st, -3); + lua_setmetatable(st, -2); + owned_cache_ref=lua_ref(st, -1); + + lua_pushcfunction(st, extl_obj_typename); + lua_setglobal(st, "obj_typename"); + lua_pushcfunction(st, extl_obj_is); + lua_setglobal(st, "obj_is"); + lua_pushcfunction(st, extl_obj_exists); + lua_setglobal(st, "obj_exists"); + lua_pushcfunction(st, extl_dopath); + lua_setglobal(st, "dopath"); + lua_pushcfunction(st, extl_protected); + lua_setglobal(st, "protected"); + + return TRUE; +} + + +/*}}}*/ + + +/*{{{ Error handling and reporting -- unsafe */ + + +static int extl_stack_trace(lua_State *st) +{ + lua_Debug ar; + int lvl=0; + int n_skip=0; + + lua_pushstring(st, TR("Stack trace:")); + + for( ; lua_getstack(st, lvl, &ar); lvl++){ + bool is_c=FALSE; + + if(lua_getinfo(st, "Sln", &ar)==0){ + lua_pushfstring(st, + TR("\n(Unable to get debug info for level %d)"), + lvl); + lua_concat(st, 2); + continue; + } + + is_c=(ar.what!=NULL && strcmp(ar.what, "C")==0); + + if(!is_c || ar.name!=NULL){ + lua_pushfstring(st, "\n%d %s", lvl, ar.short_src); + if(ar.currentline!=-1) + lua_pushfstring(st, ":%d", ar.currentline); + if(ar.name!=NULL) + lua_pushfstring(st, ": in '%s'", ar.name); + lua_concat(st, 2+(ar.currentline!=-1)+(ar.name!=NULL)); + n_skip=0; + }else{ + if(n_skip==0){ + lua_pushstring(st, TR("\n [Skipping unnamed C functions.]")); + /*lua_pushstring(st, "\n...skipping...");*/ + lua_concat(st, 2); + } + n_skip++; + } + } + return 1; +} + + +#ifdef EXTL_LOG_ERRORS + +static int extl_do_collect_errors(lua_State *st) +{ + int n, err; + ErrorLog *el=(ErrorLog*)lua_touserdata(st, -1); + + lua_pop(st, 1); + + n=lua_gettop(st)-1; + err=lua_pcall(st, n, 0, 0); + + if(err!=0) + extl_warn("%s", lua_tostring(st, -1)); + + if(el->msgs_len==0) + return 0; + lua_pushstring(st, el->msgs); + return 1; +} + + +int extl_collect_errors(lua_State *st) +{ + ErrorLog el; + int n=lua_gettop(st); + int err; + + lua_pushcfunction(st, extl_do_collect_errors); + lua_insert(st, 1); + lua_pushlightuserdata(st, &el); + + errorlog_begin(&el); + + err=lua_pcall(st, n+1, 1, 0); + + errorlog_end(&el); + errorlog_deinit(&el); + + if(err!=0) + extl_warn(TR("Internal error.")); + + return 1; +} + +#endif + + +/*}}}*/ + + +/*{{{ Init -- unsafe, but it doesn't matter at this point */ + + +bool extl_init() +{ + l_st=luaL_newstate(); + + if(l_st==NULL){ + extl_warn(TR("Unable to initialize Lua.")); + return FALSE; + } + + /* This is equivalent to calling all the ones below but it also includes + * the debug library, so I went with those in case there was a reason not + * to include the debug library. + luaL_openlibs(l_st); + */ + + lua_pushcfunction(l_st, luaopen_base); + lua_call(l_st, 0, 0); + + lua_pushcfunction(l_st, luaopen_table); + lua_call(l_st, 0, 0); + + lua_pushcfunction(l_st, luaopen_io); + lua_call(l_st, 0, 0); + + lua_pushcfunction(l_st, luaopen_os); + lua_call(l_st, 0, 0); + + lua_pushcfunction(l_st, luaopen_string); + lua_call(l_st, 0, 0); + + lua_pushcfunction(l_st, luaopen_math); + lua_call(l_st, 0, 0); + + lua_pushcfunction(l_st, luaopen_package); + lua_call(l_st, 0, 0); + + if(!extl_init_obj_info(l_st)){ + lua_close(l_st); + return FALSE; + } + +#ifdef EXTL_LOG_ERRORS + lua_pushcfunction(l_st, extl_collect_errors); + lua_setglobal(l_st, "collect_errors"); +#endif + + return TRUE; +} + + +void extl_deinit() +{ + lua_close(l_st); + l_st=NULL; +} + + +/*}}}*/ + + +/*{{{ Stack get/push -- all unsafe */ + + +static bool extl_stack_get(lua_State *st, int pos, char type, + bool copystring, bool *wasdeadobject, + void *valret) +{ + double d=0; + const char *str; + + if(wasdeadobject!=NULL) + *wasdeadobject=FALSE; + + if(type=='i' || type=='d'){ + if(lua_type(st, pos)!=LUA_TNUMBER) + return FALSE; + + d=lua_tonumber(st, pos); + + if(type=='i'){ + if(d-floor(d)!=0) + return FALSE; + if(valret) + *((int*)valret)=d; + }else{ + if(valret) + *((double*)valret)=d; + } + return TRUE; + } + + if(type=='b'){ + if(valret) + *((bool*)valret)=lua_toboolean(st, pos); + return TRUE; + } + + if(lua_type(st, pos)==LUA_TNIL || lua_type(st, pos)==LUA_TNONE){ + if(type=='t' || type=='f'){ + if(valret) + *((int*)valret)=LUA_NOREF; + }else if(type=='s' || type=='S'){ + if(valret) + *((char**)valret)=NULL; + }else if(type=='o'){ + if(valret) + *((Obj**)valret)=NULL; + }else{ + return FALSE; + } + return TRUE; + } + + if(type=='s' || type=='S'){ + if(lua_type(st, pos)!=LUA_TSTRING) + return FALSE; + if(valret){ + str=lua_tostring(st, pos); + if(str!=NULL && copystring){ + str=extl_scopy(str); + if(str==NULL) + return FALSE; + } + *((const char**)valret)=str; + } + return TRUE; + } + + if(type=='f'){ + if(!lua_isfunction(st, pos)) + return FALSE; + if(valret){ + lua_pushvalue(st, pos); + *((int*)valret)=lua_ref(st, 1); + } + return TRUE; + } + + if(type=='t'){ + if(!lua_istable(st, pos)) + return FALSE; + if(valret){ + lua_pushvalue(st, pos); + *((int*)valret)=lua_ref(st, 1); + } + return TRUE; + } + + if(type=='o'){ + bool invalid=FALSE, dead=FALSE; + Obj *obj=extl_get_obj(st, pos, &invalid, &dead); + if(wasdeadobject!=NULL) + *wasdeadobject=dead; + if(valret){ + *((Obj**)valret)=obj; + D(fprintf(stderr, "Got obj %p, ", obj); + fprintf(stderr, "%s\n", OBJ_TYPESTR(obj))); + } + return !invalid; + } + + return FALSE; +} + + +static void extl_stack_push(lua_State *st, char spec, void *ptr) +{ + if(spec=='i'){ + lua_pushnumber(st, *(int*)ptr); + }else if(spec=='d'){ + lua_pushnumber(st, *(double*)ptr); + }else if(spec=='b'){ + lua_pushboolean(st, *(bool*)ptr); + }else if(spec=='o'){ + extl_push_obj(st, *(Obj**)ptr); + }else if(spec=='s' || spec=='S'){ + lua_pushstring(st, *(char**)ptr); + }else if(spec=='t' || spec=='f'){ + lua_rawgeti(st, LUA_REGISTRYINDEX, *(int*)ptr); + }else{ + lua_pushnil(st); + } +} + + +static bool extl_stack_push_vararg(lua_State *st, char spec, va_list *argsp) +{ + switch(spec){ + case 'i': + lua_pushnumber(st, (double)va_arg(*argsp, int)); + break; + case 'd': + lua_pushnumber(st, va_arg(*argsp, double)); + break; + case 'b': + lua_pushboolean(st, va_arg(*argsp, bool)); + break; + case 'o': + extl_push_obj(st, va_arg(*argsp, Obj*)); + break; + case 'S': + case 's': + lua_pushstring(st, va_arg(*argsp, char*)); + break; + case 'f': + case 't': + lua_rawgeti(st, LUA_REGISTRYINDEX, va_arg(*argsp, int)); + break; + default: + return FALSE; + } + + return TRUE; +} + + +/*}}}*/ + + +/*{{{ Free */ + + +enum{STRINGS_NONE, STRINGS_NONCONST, STRINGS_ALL}; + + +static void extl_free(void *ptr, char spec, int strings) +{ + if(((spec=='s' && strings!=STRINGS_NONE) || + (spec=='S' && strings==STRINGS_ALL)) && *(char**)ptr!=NULL){ + if(*(char**)ptr!=NULL) + free(*(char**)ptr); + *(char**)ptr=NULL; + }else if(spec=='t'){ + extl_unref_table(*(ExtlTab*)ptr); + }else if(spec=='f'){ + extl_unref_fn(*(ExtlFn*)ptr); + } +} + + +/*}}}*/ + + +/*{{{ Table and function references. */ + + +static bool extl_getref(lua_State *st, int ref) +{ + lua_rawgeti(st, LUA_REGISTRYINDEX, ref); + if(lua_isnil(st, -1)){ + lua_pop(st, 1); + return FALSE; + } + return TRUE; +} + +/* Unref */ + +static bool extl_do_unref(lua_State *st, int *refp) +{ + lua_unref(st, *refp); + return TRUE; +} + + +ExtlFn extl_unref_fn(ExtlFn ref) +{ + extl_cpcall(l_st, (ExtlCPCallFn*)extl_do_unref, &ref); + return LUA_NOREF; +} + + +ExtlFn extl_unref_table(ExtlTab ref) +{ + extl_cpcall(l_st, (ExtlCPCallFn*)extl_do_unref, &ref); + return LUA_NOREF; +} + + +/* noref */ + +ExtlFn extl_fn_none() +{ + return LUA_NOREF; +} + + +ExtlTab extl_table_none() +{ + return LUA_NOREF; +} + + +/* ref */ + +static bool extl_do_ref(lua_State *st, int *refp) +{ + if(!extl_getref(st, *refp)) + return FALSE; + *refp=lua_ref(st, 1); + return TRUE; +} + + +ExtlTab extl_ref_table(ExtlTab ref) +{ + if(extl_cpcall(l_st, (ExtlCPCallFn*)extl_do_ref, &ref)) + return ref; + return LUA_NOREF; +} + + +ExtlFn extl_ref_fn(ExtlFn ref) +{ + if(extl_cpcall(l_st, (ExtlCPCallFn*)extl_do_ref, &ref)) + return ref; + return LUA_NOREF; +} + + +/* create_table */ + +static bool extl_do_create_table(lua_State *st, int *refp) +{ + lua_newtable(st); + *refp=lua_ref(st, 1); + return TRUE; +} + + +ExtlTab extl_create_table() +{ + ExtlTab ref; + if(extl_cpcall(l_st, (ExtlCPCallFn*)extl_do_create_table, &ref)) + return ref; + return LUA_NOREF; +} + + +/* eq */ + +typedef struct{ + int o1, o2; + bool ret; +} EqParams; + + +static bool extl_do_eq(lua_State *st, EqParams *ep) +{ + if(!extl_getref(st, ep->o1)) + return FALSE; + if(!extl_getref(st, ep->o2)) + return FALSE; + ep->ret=lua_equal(st, -1, -2); + return TRUE; +} + + +bool extl_fn_eq(ExtlFn fn1, ExtlFn fn2) +{ + EqParams ep; + ep.o1=fn1; + ep.o2=fn2; + ep.ret=FALSE; + extl_cpcall(l_st, (ExtlCPCallFn*)extl_do_eq, &ep); + return ep.ret; +} + + +bool extl_table_eq(ExtlTab t1, ExtlTab t2) +{ + EqParams ep; + ep.o1=t1; + ep.o2=t2; + ep.ret=FALSE; + extl_cpcall(l_st, (ExtlCPCallFn*)extl_do_eq, &ep); + return ep.ret; +} + + +/*}}}*/ + + +/*{{{ Table/get */ + + +typedef struct{ + ExtlTab ref; + char type; + char itype; + va_list *argsp; +} TableParams2; + + +static bool extl_table_dodo_get2(lua_State *st, TableParams2 *params) +{ + if(params->ref<0) + return FALSE; + + lua_rawgeti(st, LUA_REGISTRYINDEX, params->ref); + extl_stack_push_vararg(st, params->itype, params->argsp); + lua_gettable(st, -2); + if(lua_isnil(st, -1)) + return FALSE; + + return extl_stack_get(st, -1, params->type, TRUE, NULL, + va_arg(*(params->argsp), void*)); +} + + +bool extl_table_get_vararg(ExtlTab ref, char itype, char type, va_list *args) +{ + TableParams2 params; + + params.ref=ref; + params.itype=itype; + params.type=type; + params.argsp=args; + + return extl_cpcall(l_st, (ExtlCPCallFn*)extl_table_dodo_get2, ¶ms); +} + + +bool extl_table_get(ExtlTab ref, char itype, char type, ...) +{ + va_list args; + bool retval; + + va_start(args, type); + retval=extl_table_get_vararg(ref, itype, type, &args); + va_end(args); + + return retval; +} + + +static bool extl_table_do_gets(ExtlTab ref, const char *entry, + char type, void *valret) +{ + return extl_table_get(ref, 's', type, entry, valret); +} + +bool extl_table_gets_o(ExtlTab ref, const char *entry, Obj **ret) +{ + return extl_table_do_gets(ref, entry, 'o', (void*)ret); +} + +bool extl_table_gets_i(ExtlTab ref, const char *entry, int *ret) +{ + return extl_table_do_gets(ref, entry, 'i', (void*)ret); +} + +bool extl_table_gets_d(ExtlTab ref, const char *entry, double *ret) +{ + return extl_table_do_gets(ref, entry, 'd', (void*)ret); +} + +bool extl_table_gets_b(ExtlTab ref, const char *entry, bool *ret) +{ + return extl_table_do_gets(ref, entry, 'b', (void*)ret); +} + +bool extl_table_gets_s(ExtlTab ref, const char *entry, char **ret) +{ + return extl_table_do_gets(ref, entry, 's', (void*)ret); +} + +bool extl_table_gets_f(ExtlTab ref, const char *entry, ExtlFn *ret) +{ + return extl_table_do_gets(ref, entry, 'f', (void*)ret); +} + +bool extl_table_gets_t(ExtlTab ref, const char *entry, ExtlTab *ret) +{ + return extl_table_do_gets(ref, entry, 't', (void*)ret); +} + + +static bool extl_table_do_geti(ExtlTab ref, int entry, char type, void *valret) +{ + return extl_table_get(ref, 'i', type, entry, valret); +} + +bool extl_table_geti_o(ExtlTab ref, int entry, Obj **ret) +{ + return extl_table_do_geti(ref, entry, 'o', (void*)ret); +} + +bool extl_table_geti_i(ExtlTab ref, int entry, int *ret) +{ + return extl_table_do_geti(ref, entry, 'i', (void*)ret); +} + +bool extl_table_geti_d(ExtlTab ref, int entry, double *ret) +{ + return extl_table_do_geti(ref, entry, 'd', (void*)ret); +} + +bool extl_table_geti_b(ExtlTab ref, int entry, bool *ret) +{ + return extl_table_do_geti(ref, entry, 'b', (void*)ret); +} + +bool extl_table_geti_s(ExtlTab ref, int entry, char **ret) +{ + return extl_table_do_geti(ref, entry, 's', (void*)ret); +} + +bool extl_table_geti_f(ExtlTab ref, int entry, ExtlFn *ret) +{ + return extl_table_do_geti(ref, entry, 'f', (void*)ret); +} + +bool extl_table_geti_t(ExtlTab ref, int entry, ExtlTab *ret) +{ + return extl_table_do_geti(ref, entry, 't', (void*)ret); +} + + +typedef struct{ + int ref; + int n; +} GetNParams; + + +static bool extl_table_do_get_n(lua_State *st, GetNParams *params) +{ + lua_rawgeti(st, LUA_REGISTRYINDEX, params->ref); + params->n=luaL_getn_check(st, -1); + return TRUE; +} + + +int extl_table_get_n(ExtlTab ref) +{ + GetNParams params; + int oldtop; + + params.ref=ref; + params.n=0; + + extl_cpcall(l_st, (ExtlCPCallFn*)extl_table_do_get_n, ¶ms); + + return params.n; +} + + +/*}}}*/ + + +/*{{{ Table/set */ + + +static bool extl_table_dodo_set2(lua_State *st, TableParams2 *params) +{ + lua_rawgeti(st, LUA_REGISTRYINDEX, params->ref); + extl_stack_push_vararg(st, params->itype, params->argsp); + extl_stack_push_vararg(st, params->type, params->argsp); + lua_rawset_check(st, -3); + return TRUE; +} + + +bool extl_table_set_vararg(ExtlTab ref, char itype, char type, va_list *args) +{ + TableParams2 params; + + params.ref=ref; + params.itype=itype; + params.type=type; + params.argsp=args; + + return extl_cpcall(l_st, (ExtlCPCallFn*)extl_table_dodo_set2, ¶ms); +} + + +bool extl_table_set(ExtlTab ref, char itype, char type, ...) +{ + va_list args; + bool retval; + + va_start(args, type); + retval=extl_table_set_vararg(ref, itype, type, &args); + va_end(args); + + return retval; +} + + +bool extl_table_sets_o(ExtlTab ref, const char *entry, Obj *val) +{ + return extl_table_set(ref, 's', 'o', entry, val); +} + +bool extl_table_sets_i(ExtlTab ref, const char *entry, int val) +{ + return extl_table_set(ref, 's', 'i', entry, val); +} + +bool extl_table_sets_d(ExtlTab ref, const char *entry, double val) +{ + return extl_table_set(ref, 's', 'd', entry, val); +} + +bool extl_table_sets_b(ExtlTab ref, const char *entry, bool val) +{ + return extl_table_set(ref, 's', 'b', entry, val); +} + +bool extl_table_sets_s(ExtlTab ref, const char *entry, const char *val) +{ + return extl_table_set(ref, 's', 'S', entry, val); +} + +bool extl_table_sets_f(ExtlTab ref, const char *entry, ExtlFn val) +{ + return extl_table_set(ref, 's', 'f', entry, val); +} + +bool extl_table_sets_t(ExtlTab ref, const char *entry, ExtlTab val) +{ + return extl_table_set(ref, 's', 't', entry, val); +} + + +bool extl_table_seti_o(ExtlTab ref, int entry, Obj *val) +{ + return extl_table_set(ref, 'i', 'o', entry, val); +} + +bool extl_table_seti_i(ExtlTab ref, int entry, int val) +{ + return extl_table_set(ref, 'i', 'i', entry, val); +} + +bool extl_table_seti_d(ExtlTab ref, int entry, double val) +{ + return extl_table_set(ref, 'i', 'd', entry, val); +} + +bool extl_table_seti_b(ExtlTab ref, int entry, bool val) +{ + return extl_table_set(ref, 'i', 'b', entry, val); +} + +bool extl_table_seti_s(ExtlTab ref, int entry, const char *val) +{ + return extl_table_set(ref, 'i', 'S', entry, val); +} + +bool extl_table_seti_f(ExtlTab ref, int entry, ExtlFn val) +{ + return extl_table_set(ref, 'i', 'f', entry, val); +} + +bool extl_table_seti_t(ExtlTab ref, int entry, ExtlTab val) +{ + return extl_table_set(ref, 'i', 't', entry, val); +} + + +/*}}}*/ + + +/*{{{ Table/clear entry */ + + +static bool extl_table_dodo_clear2(lua_State *st, TableParams2 *params) +{ + lua_rawgeti(st, LUA_REGISTRYINDEX, params->ref); + extl_stack_push_vararg(st, params->itype, params->argsp); + lua_pushnil(st); + lua_rawset_check(st, -3); + return TRUE; +} + +bool extl_table_clear_vararg(ExtlTab ref, char itype, va_list *args) +{ + TableParams2 params; + + params.ref=ref; + params.itype=itype; + /*params.type='?';*/ + params.argsp=args; + + return extl_cpcall(l_st, (ExtlCPCallFn*)extl_table_dodo_clear2, ¶ms); +} + +bool extl_table_clear(ExtlTab ref, char itype, ...) +{ + va_list args; + bool retval; + + va_start(args, itype); + retval=extl_table_clear_vararg(ref, itype, &args); + va_end(args); + + return retval; +} + + +bool extl_table_clears(ExtlTab ref, const char *entry) +{ + return extl_table_clear(ref, 's', entry); +} + +bool extl_table_cleari(ExtlTab ref, int entry) +{ + return extl_table_clear(ref, 'i', entry); +} + + + +/*}}}*/ + + +/*{{{ Function calls to Lua */ + + +static bool extl_push_args(lua_State *st, const char *spec, va_list *argsp) +{ + int i=1; + + while(*spec!='\0'){ + if(!extl_stack_push_vararg(st, *spec, argsp)) + return FALSE; + i++; + spec++; + } + + return TRUE; +} + + +typedef struct{ + const char *spec; + const char *rspec; + va_list *args; + void *misc; + int nret; +#ifndef CF_HAS_VA_COPY + void *ret_ptrs[MAX_PARAMS]; +#endif +} ExtlDoCallParam; + + +static bool extl_get_retvals(lua_State *st, int m, ExtlDoCallParam *param) +{ + void *ptr; + const char *spec=param->rspec; + +#ifdef CF_HAS_VA_COPY + va_list args; + va_copy(args, *(param->args)); +#else + if(m>MAX_PARAMS){ + extl_warn(TR("Too many return values. Use a C compiler that has " + "va_copy to support more.")); + return FALSE; + } +#endif + + while(m>0){ + bool dead=FALSE; +#ifdef CF_HAS_VA_COPY + ptr=va_arg(args, void*); +#else + ptr=va_arg(*(param->args), void*); + param->ret_ptrs[param->nret]=ptr; +#endif + if(!extl_stack_get(st, -m, *spec, TRUE, &dead, ptr)){ + /* This is the only place where we allow nil-objects */ + /*if(*spec=='o' && lua_isnil(st, -m)){ + *(Obj**)ptr=NULL; + }else*/ + if(dead){ + extl_warn(TR("Returned dead object.")); + return FALSE; + }else{ + extl_warn(TR("Invalid return value (expected '%c', " + "got lua type \"%s\")."), + *spec, lua_typename(st, lua_type(st, -m))); + return FALSE; + } + } + + (param->nret)++; + spec++; + m--; + } + +#ifdef CF_HAS_VA_COPY + va_end(args); +#endif + + return TRUE; +} + + +/* The function to be called is expected on the top of stack st. + * This function should be cpcalled through extl_cpcall_call (below), which + * will take care that we don't leak anything in case of error. + */ +static bool extl_dodo_call_vararg(lua_State *st, ExtlDoCallParam *param) +{ + bool ret=TRUE; + int n=0, m=0; + + if(lua_isnil(st, -1)) + return FALSE; + + if(param->spec!=NULL) + n=strlen(param->spec); + + if(!lua_checkstack(st, n+8)){ + extl_warn(TR("Stack full.")); + return FALSE; + } + + if(n>0){ + if(!extl_push_args(st, param->spec, param->args)) + return FALSE; + } + + if(param->rspec!=NULL) + m=strlen(param->rspec); + + flushtrace(); + + if(lua_pcall(st, n, m, 0)!=0){ + extl_warn("%s", lua_tostring(st, -1)); + return FALSE; + } + + if(m>0) + return extl_get_retvals(st, m, param); + + return TRUE; +} + + +static bool extl_cpcall_call(lua_State *st, ExtlCPCallFn *fn, + ExtlDoCallParam *param) +{ + void *ptr; + int i; + + param->nret=0; + + if(extl_cpcall(st, fn, param)) + return TRUE; + + /* If param.nret>0, there was an error getting some return value and + * we must free what we got. + */ + + for(i=0; inret; i++){ +#ifdef CF_HAS_VA_COPY + ptr=va_arg(*(param->args), void*); +#else + ptr=param->ret_ptrs[i]; +#endif + extl_free(ptr, *(param->rspec+i), STRINGS_ALL); + } + + return FALSE; +} + + +static bool extl_do_call_vararg(lua_State *st, ExtlDoCallParam *param) +{ + if(!extl_getref(st, *(ExtlFn*)(param->misc))) + return FALSE; + return extl_dodo_call_vararg(st, param); +} + + +bool extl_call_vararg(ExtlFn fnref, const char *spec, + const char *rspec, va_list *args) +{ + ExtlDoCallParam param; + + if(fnref==LUA_NOREF || fnref==LUA_REFNIL) + return FALSE; + + param.spec=spec; + param.rspec=rspec; + param.args=args; + param.misc=(void*)&fnref; + + return extl_cpcall_call(l_st, (ExtlCPCallFn*)extl_do_call_vararg, ¶m); +} + + +bool extl_call(ExtlFn fnref, const char *spec, const char *rspec, ...) +{ + bool retval; + va_list args; + + va_start(args, rspec); + retval=extl_call_vararg(fnref, spec, rspec, &args); + va_end(args); + + return retval; +} + + +/*}}}*/ + + +/*{{{ extl_loadfile/string */ + + +static int call_loaded(lua_State *st) +{ + int i, nargs=lua_gettop(st); + + /* Get the loaded file/string as function */ + lua_pushvalue(st, lua_upvalueindex(1)); + + /* Fill 'arg' */ + lua_getfenv(st, -1); + lua_pushstring(st, "arg"); + + if(nargs>0){ + lua_newtable(st); + for(i=1; i<=nargs; i++){ + lua_pushvalue(st, i); + lua_rawseti_check(st, -2, i); + } + }else{ + lua_pushnil(st); + } + + lua_rawset_check(st, -3); + lua_pop(st, 1); + lua_call(st, 0, LUA_MULTRET); + return (lua_gettop(st)-nargs); +} + + +typedef struct{ + const char *src; + bool isfile; + ExtlFn *resptr; +} ExtlLoadParam; + + +static bool extl_do_load(lua_State *st, ExtlLoadParam *param) +{ + int res=0; + + if(param->isfile){ + res=luaL_loadfile(st, param->src); + }else{ + res=luaL_loadbuffer(st, param->src, strlen(param->src), param->src); + } + + if(res!=0){ + extl_warn("%s", lua_tostring(st, -1)); + return FALSE; + } + + lua_newtable(st); /* Create new environment */ + /* Now there's fn, newenv in stack */ + lua_newtable(st); /* Create metatable */ + lua_pushstring(st, "__index"); + lua_getfenv(st, -4); /* Get old environment */ + lua_rawset_check(st, -3); /* Set metatable.__index */ + lua_pushstring(st, "__newindex"); + lua_getfenv(st, -4); /* Get old environment */ + lua_rawset_check(st, -3); /* Set metatable.__newindex */ + /* Now there's fn, newenv, meta in stack */ + lua_setmetatable(st, -2); /* Set metatable for new environment */ + lua_setfenv(st, -2); + /* Now there should be just fn in stack */ + + /* Callloaded will put any parameters it gets in the table 'arg' in + * the newly created environment. + */ + lua_pushcclosure(st, call_loaded, 1); + *(param->resptr)=lua_ref(st, -1); + + return TRUE; +} + + +bool extl_loadfile(const char *file, ExtlFn *ret) +{ + ExtlLoadParam param; + param.src=file; + param.isfile=TRUE; + param.resptr=ret; + + return extl_cpcall(l_st, (ExtlCPCallFn*)extl_do_load, ¶m); +} + + +bool extl_loadstring(const char *str, ExtlFn *ret) +{ + ExtlLoadParam param; + param.src=str; + param.isfile=FALSE; + param.resptr=ret; + + return extl_cpcall(l_st, (ExtlCPCallFn*)extl_do_load, ¶m); +} + + +/*}}}*/ + + +/*{{{ L1 CH error logging */ + +#ifdef EXTL_LOG_ERRORS + +INTRSTRUCT(WarnChain); +DECLSTRUCT(WarnChain){ + bool need_trace; + lua_State *st; + WarnHandler *old_handler; + WarnChain *prev; +}; + + +static WarnChain *warnchain=NULL; +static int notrace=0; + + +static void l1_warn_handler(const char *message) +{ + WarnChain *ch=warnchain; + static int called=0; + + assert(warnchain!=NULL); + + if(called==0 && notrace==0) + ch->need_trace=TRUE; + + called++; + warnchain=ch->prev; + ch->old_handler(message); + warnchain=ch; + called--; +} + + +static void do_trace(WarnChain *ch) +{ + const char *p; + + if(notrace!=0) + return; + + extl_stack_trace(ch->st); + p=lua_tostring(ch->st, -1); + notrace++; + extl_warn(p); + notrace--; + ch->need_trace=FALSE; + lua_pop(ch->st, 1); +} + +static void flushtrace() +{ + if(warnchain && warnchain->need_trace) + do_trace(warnchain); +} + +#endif + +/*}}}*/ + + +/*{{{ L1-CH safe functions */ + + +static int protect_count=0; +static ExtlSafelist *safelists=NULL; + + +void extl_protect(ExtlSafelist *l) +{ + protect_count++; + if(l!=NULL){ + if(l->count==0){ + LINK_ITEM(safelists, l, next, prev); + } + l->count++; + } +} + + +void extl_unprotect(ExtlSafelist *l) +{ + assert(protect_count>0); + protect_count--; + + if(l!=NULL){ + assert(l->count>0); + l->count--; + if(l->count==0){ + UNLINK_ITEM(safelists, l, next, prev); + } + } +} + + +static bool extl_check_protected(ExtlExportedFnSpec *spec) +{ + ExtlSafelist *l; + bool ok=FALSE; + int j; + + if(protect_count>0 && !spec->safe){ + for(l=safelists; l!=NULL; l=l->next){ + ok=TRUE; + for(j=0; l->list[j]!=NULL; j++){ + if(l->list[j]==spec->fn) + break; + } + if(l->list[j]==NULL){ + ok=FALSE; + break; + } + } + }else{ + ok=TRUE; + } + + return ok; +} + + +/*}}}*/ + + +/*{{{ L1 call handler */ + +/* To get around potential memory leaks and corruption that could be caused + * by Lua's longjmp-on-error lameness, The L1 call handler is divided into + * two steps. In the first step we first setup a call to the second step. + * At this point it is still fine if Lua raises an error. Then we set up + * our warning handlers and stuff--at which point Lua's raising an error + * would corrupt our data--and finally call the second step with lua_pcall. + * Now the second step can safely call Lua's functions and do what is needed. + * When the second step returns, we deallocate our data in the L1Param + * structure that was passed to the second step and reset warning handlers. + * After that it is again safe to call Lua's functions. + */ + +typedef struct{ + ExtlL2Param ip[MAX_PARAMS]; + ExtlL2Param op[MAX_PARAMS]; + ExtlExportedFnSpec *spec; + int ii, ni, no; +} L1Param; + +static L1Param *current_param=NULL; + + +static int extl_l1_call_handler2(lua_State *st) +{ + L1Param *param=current_param; + ExtlExportedFnSpec *spec=param->spec; + int i; + + D(fprintf(stderr, "%s called\n", spec->name)); + + if(!lua_checkstack(st, MAX_PARAMS+1)){ + extl_warn(TR("Stack full.")); + return 0; + } + + param->ni=(spec->ispec==NULL ? 0 : strlen(spec->ispec)); + + for(i=0; ini; i++){ + bool dead=FALSE; + if(!extl_stack_get(st, i+1, spec->ispec[i], FALSE, &dead, + (void*)&(param->ip[i]))){ + if(dead){ + extl_warn(TR("Argument %d to %s is a dead object."), + i+1, spec->name); + }else{ + extl_warn(TR("Argument %d to %s is of invalid type. " + "(Argument template is '%s', got lua type %s)."), + i+1, spec->name, spec->ispec, + lua_typename(st, lua_type(st, i+1))); + } + return 0; + } + + param->ii=i+1; + } + + if(spec->untraced) + notrace++; + + if(!spec->l2handler(spec->fn, param->ip, param->op)) + return 0; + + if(spec->untraced) + notrace--; + + param->no=(spec->ospec==NULL ? 0 : strlen(spec->ospec)); + + for(i=0; ino; i++) + extl_stack_push(st, spec->ospec[i], (void*)&(param->op[i])); + + return param->no; +} + + +static void extl_l1_finalize(L1Param *param) +{ + ExtlExportedFnSpec *spec=param->spec; + int i; + + for(i=0; iii; i++) + extl_free((void*)&(param->ip[i]), spec->ispec[i], STRINGS_NONE); + + for(i=0; ino; i++) + extl_free((void*)&(param->op[i]), spec->ospec[i], STRINGS_NONCONST); +} + + + +static bool extl_l1_just_check_protected=FALSE; + + +static int extl_l1_call_handler(lua_State *st) +{ +#ifdef EXTL_LOG_ERRORS + WarnChain ch; +#endif + L1Param param={{NULL, }, {NULL, }, NULL, 0, 0, 0}; + L1Param *old_param; + int ret; + int n=lua_gettop(st); + + + /* Get the info we need on the function, check it's ok, and then set + * up a safe environment for extl_l1_call_handler2. + */ + param.spec=(ExtlExportedFnSpec*)lua_touserdata(st, lua_upvalueindex(1)); + + if(param.spec==NULL){ + extl_warn(TR("L1 call handler upvalues corrupt.")); + return 0; + } + + if(param.spec->fn==NULL){ + extl_warn(TR("Called function has been unregistered.")); + return 0; + } + + if(extl_l1_just_check_protected){ + /* Just checking whether the function may be called. */ + lua_pushboolean(st, !extl_check_protected(param.spec)); + return 1; + } + + if(!extl_check_protected(param.spec)){ + extl_warn(TR("Attempt to call an unsafe function \"%s\" in " + "restricted mode."), param.spec->name); + return 0; + } + + + lua_pushcfunction(st, extl_l1_call_handler2); + lua_insert(st, 1); + + old_param=current_param; + current_param=¶m; + +#ifdef EXTL_LOG_ERRORS + ch.old_handler=set_warn_handler(l1_warn_handler); + ch.need_trace=FALSE; + ch.st=st; + ch.prev=warnchain; + warnchain=&ch; +#endif + + /* Ok, Lua may now freely fail in extl_l1_call_handler2, we can handle + * that. + */ + ret=lua_pcall(st, n, LUA_MULTRET, 0); + + /* Now that the actual call handler has returned, we need to free + * any of our data before calling Lua again. + */ + current_param=old_param; + extl_l1_finalize(¶m); + +#ifdef EXTL_LOG_ERRORS + warnchain=ch.prev; + set_warn_handler(ch.old_handler); + + /* Ok, we can now safely use Lua functions again without fear of + * leaking. + */ + if(ret!=0){ + const char *p; + param.no=0; + p=lua_tostring(st, -1); + notrace++; + extl_warn("%s", p); + notrace--; + } + + if(ret!=0 || ch.need_trace) + do_trace(&ch); +#else + if(ret!=0) + lua_error(st); +#endif + + return param.no; +} + + +/*EXTL_DOC + * Is calling the function \var{fn} not allowed now? If \var{fn} is nil, + * tells if some functions are not allowed to be called now due to + * protected mode. + */ +EXTL_EXPORT_AS(global, protected) +bool __protected(ExtlFn fn); + +static int extl_protected(lua_State *st) +{ + int ret; + + if(lua_isnil(st, 1)){ + lua_pushboolean(st, protect_count>0); + return 1; + } + + if(!lua_isfunction(st, 1)){ + lua_pushboolean(st, TRUE); + return 1; + } + + if(lua_tocfunction(st, 1)!=(lua_CFunction)extl_l1_call_handler){ + lua_pushboolean(st, FALSE); + return 1; + } + + extl_l1_just_check_protected=TRUE; + ret=lua_pcall(st, 0, 1, 0); + extl_l1_just_check_protected=FALSE; + if(ret!=0) + lua_pushboolean(st, TRUE); + return 1; +} + +/*}}}*/ + + +/*{{{ Function registration */ + + +typedef struct{ + ExtlExportedFnSpec *spec; + const char *cls; + ExtlTab table; +} RegData; + + +static bool extl_do_register_function(lua_State *st, RegData *data) +{ + ExtlExportedFnSpec *spec=data->spec, *spec2; + int ind=LUA_GLOBALSINDEX; + + if((spec->ispec!=NULL && strlen(spec->ispec)>MAX_PARAMS) || + (spec->ospec!=NULL && strlen(spec->ospec)>MAX_PARAMS)){ + extl_warn(TR("Function '%s' has more parameters than the level 1 " + "call handler can handle"), spec->name); + return FALSE; + } + + if(data->table!=LUA_NOREF){ + lua_rawgeti(st, LUA_REGISTRYINDEX, data->table); + ind=-3; + } + + lua_pushstring(st, spec->name); + + spec2=lua_newuserdata(st, sizeof(ExtlExportedFnSpec)); + + memcpy(spec2, spec, sizeof(ExtlExportedFnSpec)); + + lua_getregistry(st); + lua_pushvalue(st, -2); /* Get spec2 */ + lua_pushfstring(st, "luaextl_%s_%s_upvalue", + data->cls, spec->name); + lua_rawset_check(st, -3); /* Set registry.luaextl_fn_upvalue=spec2 */ + lua_pop(st, 1); /* Pop registry */ + lua_pushcclosure(st, extl_l1_call_handler, 1); + lua_rawset_check(st, ind); + + return TRUE; +} + + +static bool extl_do_register_functions(ExtlExportedFnSpec *spec, int max, + const char *cls, int table) +{ + int i; + + RegData regdata; + regdata.spec=spec; + regdata.cls=cls; + regdata.table=table; + + for(i=0; spec[i].name && ispec, *spec2; + int ind=LUA_GLOBALSINDEX; + + lua_getregistry(st); + lua_pushfstring(st, "luaextl_%s_%s_upvalue", + data->cls, spec->name); + lua_pushvalue(st, -1); + lua_gettable(st, -3); /* Get registry.luaextl_fn_upvalue */ + spec2=lua_touserdata(st, -1); + + if(spec2==NULL) + return FALSE; + + spec2->ispec=NULL; + spec2->ospec=NULL; + spec2->fn=NULL; + spec2->name=NULL; + spec2->l2handler=NULL; + + lua_pop(st, 1); /* Pop the upvalue */ + lua_pushnil(st); + lua_rawset_check(st, -3); /* Clear registry.luaextl_fn_upvalue */ + + if(data->table!=LUA_NOREF){ + lua_rawgeti(st, LUA_REGISTRYINDEX, data->table); + ind=-3; + } + + /* Clear table.fn */ + lua_pushstring(st, spec->name); + lua_pushnil(st); + lua_rawset_check(st, ind); + + return TRUE; +} + + +static void extl_do_unregister_functions(ExtlExportedFnSpec *spec, int max, + const char *cls, int table) +{ + int i; + + RegData regdata; + regdata.spec=spec; + regdata.cls=cls; + regdata.table=table; + + for(i=0; spec[i].name && icls); + lua_settable(st, -3); + + /* If we have a parent class (i.e. class!=Obj), we want also the parent's + * functions visible in this table so set up a metatable to do so. + */ + if(data->parent!=NULL){ + /* Get luaextl_ParentClass_metatable */ + lua_pushfstring(st, "luaextl_%s_metatable", data->parent); + lua_gettable(st, LUA_REGISTRYINDEX); + if(!lua_istable(st, -1)){ + extl_warn("Could not find metatable for parent class %s of %s.\n", + data->parent, data->cls); + return FALSE; + } + /* Create our metatable */ + lua_newtable(st); + /* Get parent_metatable.__index */ + lua_pushstring(st, "__index"); + lua_pushvalue(st, -1); + /* Stack: cls, parent_meta, meta, "__index", "__index" */ + lua_gettable(st, -4); + /* Stack: cls, parent_meta, meta, "__index", parent_meta.__index */ + lua_pushvalue(st, -1); + lua_insert(st, -3); + /* Stack: cls, parent_meta, meta, parent_meta.__index, "__index", parent_meta.__index */ + lua_rawset_check(st, -4); + /* Stack: cls, parent_meta, meta, parent_meta.__index */ + lua_pushstring(st, "__parentclass"); + lua_insert(st, -2); + /* Stack: cls, parent_meta, meta, "__parentclass", parent_meta.__index */ + lua_settable(st, -5); + /* Stack: cls, parent_meta, meta, */ + lua_setmetatable(st, -3); + lua_pop(st, 1); + /* Stack: cls */ + } + + /* Set the global WFoobar */ + lua_pushvalue(st, -1); + data->refret=lua_ref(st, 1); /* TODO: free on failure */ + if(!data->hide){ + lua_pushstring(st, data->cls); + lua_pushvalue(st, -2); + lua_rawset(st, LUA_GLOBALSINDEX); + } + + /* New we create a metatable for the actual objects with __gc metamethod + * and __index pointing to the table created above. The MAGIC entry is + * used to check that userdatas passed to us really are Watches with a + * high likelihood. + */ + lua_newtable(st); + + lua_pushnumber(st, MAGIC); + lua_pushnumber(st, MAGIC); + lua_rawset_check(st, -3); + + lua_pushstring(st, "__index"); + lua_pushvalue(st, -3); + lua_rawset_check(st, -3); /* set metatable.__index=WFoobar created above */ + lua_pushstring(st, "__gc"); + lua_pushcfunction(st, extl_obj_gc_handler); + lua_rawset_check(st, -3); /* set metatable.__gc=extl_obj_gc_handler */ + lua_pushfstring(st, "luaextl_%s_metatable", data->cls); + lua_insert(st, -2); + lua_rawset(st, LUA_REGISTRYINDEX); + + return TRUE; +} + + +bool extl_register_class(const char *cls, ExtlExportedFnSpec *fns, + const char *parent) +{ + ClassData clsdata; + clsdata.cls=cls; + clsdata.parent=parent; + clsdata.refret=LUA_NOREF; + clsdata.hide=(strcmp(cls, "Obj")==0);/*(fns==NULL);*/ + + D(assert(strcmp(cls, "Obj")==0 || parent!=NULL)); + + if(!extl_cpcall(l_st, (ExtlCPCallFn*)extl_do_register_class, &clsdata)) + return FALSE; + + if(fns==NULL) + return TRUE; + + return extl_do_register_functions(fns, INT_MAX, cls, clsdata.refret); +} + + +static void extl_do_unregister_class(lua_State *st, ClassData *data) +{ + /* Get reference from registry to the metatable. */ + lua_pushfstring(st, "luaextl_%s_metatable", data->cls); + lua_pushvalue(st, -1); + lua_gettable(st, LUA_REGISTRYINDEX); + /* Get __index and return it for resetting the functions. */ + lua_pushstring(st, "__index"); + lua_gettable(st, -2); + data->refret=lua_ref(st, -1); + lua_pop(st, 1); + /* Set the entry from registry to nil. */ + lua_pushnil(st); + lua_rawset(st, LUA_REGISTRYINDEX); + + /* Reset the global reference to the class to nil. */ + lua_pushstring(st, data->cls); + lua_pushnil(st); + lua_rawset(st, LUA_GLOBALSINDEX); +} + + +void extl_unregister_class(const char *cls, ExtlExportedFnSpec *fns) +{ + ClassData clsdata; + clsdata.cls=cls; + clsdata.parent=NULL; + clsdata.refret=LUA_NOREF; + clsdata.hide=FALSE; /* unused, but initialise */ + + if(!extl_cpcall(l_st, (ExtlCPCallFn*)extl_do_unregister_class, + &clsdata)) + return; + + /* We still need to reset function upvalues. */ + if(fns!=NULL) + extl_do_unregister_functions(fns, INT_MAX, cls, clsdata.refret); +} + + +/*}}}*/ + + +/*{{{ Module registration */ + + +static bool extl_do_register_module(lua_State *st, ClassData *clsdata) +{ + lua_getglobal(st, clsdata->cls); + + if(!lua_istable(st, -1)){ + lua_newtable(st); + lua_pushvalue(st, -1); + lua_setglobal(st, clsdata->cls); + } + lua_pushfstring(st, "luaextl_module_%s", clsdata->cls); + lua_pushvalue(st, -2); + lua_rawset(st, LUA_REGISTRYINDEX); + + clsdata->refret=lua_ref(st, -1); + + return TRUE; +} + + +bool extl_register_module(const char *mdl, ExtlExportedFnSpec *fns) +{ + ClassData clsdata; + + clsdata.cls=mdl; + clsdata.parent=NULL; + clsdata.refret=LUA_NOREF; + clsdata.hide=FALSE; /* unused, but initialise */ + + if(!extl_cpcall(l_st, (ExtlCPCallFn*)extl_do_register_module, &clsdata)) + return FALSE; + + if(fns==NULL) + return TRUE; + + return extl_do_register_functions(fns, INT_MAX, mdl, clsdata.refret); +} + + +static bool extl_do_unregister_module(lua_State *st, ClassData *clsdata) +{ + lua_pushfstring(st, "luaextl_module_%s", clsdata->cls); + lua_pushvalue(st, -1); + lua_pushnil(st); + lua_rawset(st, LUA_REGISTRYINDEX); + clsdata->refret=lua_ref(st, -1); + + return TRUE; +} + + +void extl_unregister_module(const char *mdl, ExtlExportedFnSpec *fns) +{ + ClassData clsdata; + + clsdata.cls=mdl; + clsdata.parent=NULL; + clsdata.refret=LUA_NOREF; + clsdata.hide=FALSE; /* unused, but initialise */ + + if(!extl_cpcall(l_st, (ExtlCPCallFn*)extl_do_unregister_module, &clsdata)) + return; + + if(fns!=NULL) + extl_do_unregister_functions(fns, INT_MAX, mdl, clsdata.refret); +} + + +/*}}}*/ + + +/*{{{ Serialise */ + +typedef struct{ + FILE *f; + ExtlTab tab; +} SerData; + + +static void write_escaped_string(FILE *f, const char *str) +{ + fputc('"', f); + + while(str && *str){ + if(((*str)&0x7f)<32 || *str=='"' || *str=='\\'){ + /* Lua uses decimal in escapes */ + fprintf(f, "\\%03d", (int)(uchar)(*str)); + }else{ + fputc(*str, f); + } + str++; + } + + fputc('"', f); +} + + +static void indent(FILE *f, int lvl) +{ + int i; + for(i=0; i=EXTL_MAX_SERIALISE_DEPTH){ + extl_warn(TR("Maximal serialisation depth reached.")); + fprintf(f, "nil"); + lua_pop(st, 1); + return FALSE; + } + + fprintf(f, "{\n"); + lua_pushnil(st); + while(lua_next(st, -2)!=0){ + lua_pushvalue(st, -2); + indent(f, lvl+1); + fprintf(f, "["); + ser(st, f, lvl+1); + fprintf(f, "] = "); + ser(st, f, lvl+1); + fprintf(f, ",\n"); + } + indent(f, lvl); + fprintf(f, "}"); + break; + default: + extl_warn(TR("Unable to serialise type %s."), + lua_typename(st, lua_type(st, -1))); + } + lua_pop(st, 1); + return TRUE; +} + + +static bool extl_do_serialise(lua_State *st, SerData *d) +{ + if(!extl_getref(st, d->tab)) + return FALSE; + + return ser(st, d->f, 0); +} + + +/* Tab must not contain recursive references! */ +extern bool extl_serialise(const char *file, ExtlTab tab) +{ + SerData d; + bool ret; + + d.tab=tab; + d.f=fopen(file, "w"); + + if(d.f==NULL){ + extl_warn_err_obj(file); + return FALSE; + } + + fprintf(d.f, TR("-- This file has been generated by Ion. Do not edit.\n")); + fprintf(d.f, "return "); + + ret=extl_cpcall(l_st, (ExtlCPCallFn*)extl_do_serialise, &d); + + fprintf(d.f, "\n\n"); + + fclose(d.f); + + return ret; +} + + +/*}}}*/ + diff --git a/libextl/luaextl.h b/libextl/luaextl.h new file mode 100644 index 0000000..28cfde8 --- /dev/null +++ b/libextl/luaextl.h @@ -0,0 +1,171 @@ +/* + * libextl/luaextl.h + * + * Copyright (c) Tuomo Valkonen 1999-2005. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + */ + +#ifndef LIBEXTL_LUAEXTL_H +#define LIBEXTL_LUAEXTL_H + +#include + +#include "types.h" + +#define EXTL_EXTENSION "lua" +#define EXTL_COMPILED_EXTENSION "lc" +#define EXTL_MAX_SERIALISE_DEPTH 128 + +/* o: userdata/Obj + * i: integer + * d: double + * b: boolean + * s: string + * t: table + * f: function (c or lua) + * v: void + */ + +typedef int ExtlTab; +typedef int ExtlFn; + +typedef union{ + Obj *o; + int i; + double d; + bool b; + const char *s; + ExtlFn f; + ExtlTab t; +} ExtlL2Param; + +typedef bool ExtlL2CallHandler(void (*fn)(), ExtlL2Param *in, + ExtlL2Param *out); + +typedef void ExtlExportedFn(void); + +typedef struct{ + const char *name; + ExtlExportedFn *fn; + const char *ispec; + const char *ospec; + ExtlL2CallHandler *l2handler; + bool safe; + bool untraced; +} ExtlExportedFnSpec; + +typedef struct ExtlSafelist_struct{ + int count; + struct ExtlSafelist_struct *next, *prev; + ExtlExportedFn **list; +} ExtlSafelist; + +#define EXTL_SAFELIST_INIT(L) {0, NULL, NULL, L} + +extern ExtlFn extl_unref_fn(ExtlFn ref); +extern ExtlTab extl_unref_table(ExtlTab ref); +extern ExtlFn extl_fn_none(); +extern ExtlTab extl_table_none(); + +extern bool extl_fn_eq(ExtlFn fn1, ExtlFn fn2); +extern bool extl_table_eq(ExtlTab fn1, ExtlTab fn2); + +/* These should be called to store function and table references gotten + * as arguments to functions. + */ +extern ExtlFn extl_ref_fn(ExtlFn ref); +extern ExtlTab extl_ref_table(ExtlTab ref); + +extern ExtlTab extl_create_table(); + +/* Table/get */ +extern bool extl_table_get_vararg(ExtlTab ref, char itype, char type, + va_list *args); +extern bool extl_table_get(ExtlTab ref, char itype, char type, ...); + +extern bool extl_table_gets_o(ExtlTab ref, const char *entry, Obj **ret); +extern bool extl_table_gets_i(ExtlTab ref, const char *entry, int *ret); +extern bool extl_table_gets_d(ExtlTab ref, const char *entry, double *ret); +extern bool extl_table_gets_b(ExtlTab ref, const char *entry, bool *ret); +extern bool extl_table_gets_s(ExtlTab ref, const char *entry, char **ret); +extern bool extl_table_gets_f(ExtlTab ref, const char *entry, ExtlFn *ret); +extern bool extl_table_gets_t(ExtlTab ref, const char *entry, ExtlTab *ret); + +extern int extl_table_get_n(ExtlTab ref); +extern bool extl_table_geti_o(ExtlTab ref, int entry, Obj **ret); +extern bool extl_table_geti_i(ExtlTab ref, int entry, int *ret); +extern bool extl_table_geti_d(ExtlTab ref, int entry, double *ret); +extern bool extl_table_geti_b(ExtlTab ref, int entry, bool *ret); +extern bool extl_table_geti_s(ExtlTab ref, int entry, char **ret); +extern bool extl_table_geti_f(ExtlTab ref, int entry, ExtlFn *ret); +extern bool extl_table_geti_t(ExtlTab ref, int entry, ExtlTab *ret); + +/* Table/set */ +extern bool extl_table_set_vararg(ExtlTab ref, char itype, char type, + va_list *args); +extern bool extl_table_set(ExtlTab ref, char itype, char type, ...); + +extern bool extl_table_sets_o(ExtlTab ref, const char *entry, Obj *val); +extern bool extl_table_sets_i(ExtlTab ref, const char *entry, int val); +extern bool extl_table_sets_d(ExtlTab ref, const char *entry, double val); +extern bool extl_table_sets_b(ExtlTab ref, const char *entry, bool val); +extern bool extl_table_sets_s(ExtlTab ref, const char *entry, const char *val); +extern bool extl_table_sets_f(ExtlTab ref, const char *entry, ExtlFn val); +extern bool extl_table_sets_t(ExtlTab ref, const char *entry, ExtlTab val); + +extern bool extl_table_seti_o(ExtlTab ref, int entry, Obj *val); +extern bool extl_table_seti_i(ExtlTab ref, int entry, int val); +extern bool extl_table_seti_d(ExtlTab ref, int entry, double val); +extern bool extl_table_seti_b(ExtlTab ref, int entry, bool val); +extern bool extl_table_seti_s(ExtlTab ref, int entry, const char *val); +extern bool extl_table_seti_f(ExtlTab ref, int entry, ExtlFn val); +extern bool extl_table_seti_t(ExtlTab ref, int entry, ExtlTab val); + +/* Table/clear */ + +extern bool extl_table_clear_vararg(ExtlTab ref, char itype, va_list *args); +extern bool extl_table_clear(ExtlTab ref, char itype, ...); + +extern bool extl_table_clears(ExtlTab ref, const char *entry); +extern bool extl_table_cleari(ExtlTab ref, int entry); + +/* Call */ + +extern void extl_protect(ExtlSafelist *sl); +extern void extl_unprotect(ExtlSafelist *sl); + +extern bool extl_call_vararg(ExtlFn fnref, const char *spec, + const char *rspec, va_list *args); +extern bool extl_call(ExtlFn fnref, const char *spec, + const char *rspec, ...); + +/* Files */ + +extern bool extl_loadfile(const char *file, ExtlFn *ret); +extern bool extl_loadstring(const char *str, ExtlFn *ret); +extern bool extl_serialise(const char *file, ExtlTab tab); + +/* Register */ + +extern bool extl_register_function(ExtlExportedFnSpec *spec); +extern void extl_unregister_function(ExtlExportedFnSpec *spec); +extern bool extl_register_functions(ExtlExportedFnSpec *spec); +extern void extl_unregister_functions(ExtlExportedFnSpec *spec); + +bool extl_register_class(const char *cls, ExtlExportedFnSpec *fns, + const char *parent); +void extl_unregister_class(const char *cls, ExtlExportedFnSpec *fns); + +bool extl_register_module(const char *cls, ExtlExportedFnSpec *fns); +void extl_unregister_module(const char *cls, ExtlExportedFnSpec *fns); + +/* Misc. */ + +extern bool extl_init(); +extern void extl_deinit(); + +#endif /* LIBEXTL_LUAEXTL_H */ diff --git a/libextl/misc.c b/libextl/misc.c new file mode 100644 index 0000000..1cdda45 --- /dev/null +++ b/libextl/misc.c @@ -0,0 +1,23 @@ +/* + * libextl/misc.c + * + * Copyright (c) Tuomo Valkonen 2004-2005. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + */ + +#include "private.h" + + +bool extl_obj_error(int ndx, const char *got, const char *wanted) +{ + extl_warn(TR("Type checking failed in level 2 call handler for " + "parameter %d (got %s, expected %s)."), + ndx, got ? got : "nil", wanted); + + return FALSE; +} + diff --git a/libextl/private.h b/libextl/private.h new file mode 100644 index 0000000..b0f576b --- /dev/null +++ b/libextl/private.h @@ -0,0 +1,115 @@ +/* + * libextl/private.h + * + * Copyright (c) Tuomo Valkonen 2004-2005. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + */ + +#ifndef LIBEXTL_PRIVATE_H +#define LIBEXTL_PRIVATE_H + +#include +#include +#include +#include +#include +#include + +#include "types.h" + +/* + * String routines + */ + +#define extl_scopy(S) scopy(S) +#define extl_scopyn(S, N) scopyn(S, N) +#define extl_scat3(S1, S2, S3) scat3(S1, S2, S3) +#define extl_asprintf libtu_asprintf + +/* + * Error display + */ + +#if 1 +#include +#define EXTL_LOG_ERRORS +/* This part has not been abstracted away from libtu dependence. */ +#endif + +#define extl_warn warn +#define extl_warn_err_obj(NM) warn_err_obj(NM) + +/*#define extl_warn(FMT, ...) + ({fprintf(stderr, FMT, __VA_ARGS__); fputc('\n', stderr);})*/ +/*#define extl_warn_obj_err(O) perror(O) */ + +/* + * Level2 type checking + */ + +/* Always returns FALSE. */ +extern bool extl_obj_error(int ndx, const char *got, const char *wanted); + +#define EXTL_CHKO1(IN, NDX, TYPE) \ + (OBJ_IS(IN[NDX].o, TYPE) \ + ? TRUE \ + : extl_obj_error(NDX, OBJ_TYPESTR(IN[NDX].o), #TYPE)) + +#define EXTL_CHKO(IN, NDX, TYPE) \ + (IN[NDX].o==NULL || OBJ_IS(IN[NDX].o, TYPE) \ + ? TRUE \ + : extl_obj_error(NDX, OBJ_TYPESTR(IN[NDX].o), #TYPE)) + +#define EXTL_DEFCLASS(C) INTRCLASS(C) + + +/* + * Objects. + */ + +typedef Watch ExtlProxy; + +#define EXTL_OBJ_CACHED(OBJ) ((OBJ)->flags&OBJ_EXTL_CACHED) +#define EXTL_OBJ_OWNED(OBJ) ((OBJ)->flags&OBJ_EXTL_OWNED) +#define EXTL_OBJ_TYPENAME(OBJ) OBJ_TYPESTR(OBJ) +#define EXTL_OBJ_IS(OBJ, NAME) obj_is_str(OBJ, NAME) + +#define EXTL_PROXY_OBJ(PROXY) ((PROXY)->obj) + +#define EXTL_BEGIN_PROXY_OBJ(PROXY, OBJ) \ + watch_init(PROXY); \ + watch_setup(PROXY, OBJ, obj_dest_handler); \ + (OBJ)->flags|=OBJ_EXTL_CACHED; + +#define EXTL_END_PROXY_OBJ(PROXY, OBJ) \ + assert((PROXY)->obj==OBJ); \ + watch_reset(PROXY); \ + (OBJ)->flags&=~OBJ_EXTL_CACHED; + +#define EXTL_DESTROY_OWNED_OBJ(OBJ) destroy_obj(OBJ) + +extern void extl_uncache(Obj *obj); + +static void obj_dest_handler(Watch *watch, Obj *obj) +{ + extl_uncache(obj); + obj->flags&=~OBJ_EXTL_CACHED; +} + + +/* + * Miscellaneous. + */ + +/* Translate string X to locale. */ +/*#define TR(X) X */ + +/* Debugging. */ +/*#define D(X) */ + + +#endif /* LIBEXTL_PRIVATE_H */ diff --git a/libextl/readconfig.c b/libextl/readconfig.c new file mode 100644 index 0000000..b9c6061 --- /dev/null +++ b/libextl/readconfig.c @@ -0,0 +1,505 @@ +/* + * libextl/readconfig.c + * + * Copyright (c) Tuomo Valkonen 1999-2005. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include + +#include "readconfig.h" +#include "extl.h" +#include "private.h" + + +typedef struct{ + ExtlFn fn; + ExtlTab tab; + int status; +} TryCallParam; + + +/*{{{ Path setup */ + + +static char *userdir=NULL; +static char *sessiondir=NULL; +static char *scriptpath=NULL; + + +bool extl_add_searchdir(const char *dir) +{ + if(scriptpath==NULL){ + scriptpath=extl_scopy(dir); + if(scriptpath==NULL) + return FALSE; + }else{ + char *p=extl_scat3(dir, ":", scriptpath); + if(p==NULL) + return FALSE; + free(scriptpath); + scriptpath=p; + } + + return TRUE; +} + + +bool extl_set_searchpath(const char *path) +{ + char *s=NULL; + + if(path!=NULL){ + s=extl_scopy(path); + if(s==NULL) + return FALSE; + } + + if(scriptpath!=NULL) + free(scriptpath); + + scriptpath=s; + return TRUE; +} + + +bool extl_set_userdirs(const char *appname) +{ + const char *home; + char *tmp; + int fails=2; + + if(userdir!=NULL) + return FALSE; + + home=getenv("HOME"); + + if(home==NULL){ + extl_warn(TR("$HOME not set")); + }else{ + libtu_asprintf(&userdir, "%s/.%s", home, appname); + if(userdir!=NULL) + fails-=extl_add_searchdir(userdir); + + libtu_asprintf(&tmp, "%s/.%s/lib", home, appname); + if(tmp!=NULL){ + fails-=extl_add_searchdir(tmp); + free(tmp); + } + } + + return (fails==0); +} + + +bool extl_set_sessiondir(const char *session) +{ + char *tmp; + bool ret=FALSE; + + if(strchr(session, '/')!=NULL){ + tmp=extl_scopy(session); + }else if(userdir!=NULL){ + libtu_asprintf(&tmp, "%s/%s", userdir, session); + }else{ + extl_warn(TR("User directory not set. " + "Unable to set session directory.")); + return FALSE; + } + + if(tmp==NULL) + return FALSE; + + if(sessiondir!=NULL) + free(sessiondir); + + sessiondir=tmp; + + return TRUE; +} + + +const char *extl_userdir() +{ + return userdir; +} + + +const char *extl_sessiondir() +{ + return sessiondir; +} + + +const char *extl_searchpath() +{ + return scriptpath; +} + + +/*}}}*/ + + +/*{{{ try_etcpath, do_include, etc. */ + + +static int do_try(const char *dir, const char *file, ExtlTryConfigFn *tryfn, + void *tryfnparam) +{ + char *tmp=NULL; + int ret; + + libtu_asprintf(&tmp, "%s/%s", dir, file); + if(tmp==NULL) + return EXTL_TRYCONFIG_MEMERROR; + + ret=tryfn(tmp, tryfnparam); + free(tmp); + return ret; +} + + +static int try_dir(const char *const *files, const char *cfdir, + ExtlTryConfigFn *tryfn, void *tryfnparam) +{ + const char *const *file; + int ret, ret2=EXTL_TRYCONFIG_NOTFOUND; + + for(file=files; *file!=NULL; file++){ + if(cfdir==NULL) + ret=tryfn(*file, tryfnparam); + else + ret=do_try(cfdir, *file, tryfn, tryfnparam); + if(ret>=0) + return ret; + if(ret2==EXTL_TRYCONFIG_NOTFOUND) + ret2=ret; + } + + return ret2; +} + + +static int try_etcpath(const char *const *files, + ExtlTryConfigFn *tryfn, void *tryfnparam) +{ + const char *const *file=NULL; + int i, ret, ret2=EXTL_TRYCONFIG_NOTFOUND; + char *path, *colon, *dir; + + if(sessiondir!=NULL){ + for(file=files; *file!=NULL; file++){ + ret=do_try(sessiondir, *file, tryfn, tryfnparam); + if(ret>=0) + return ret; + if(ret2==EXTL_TRYCONFIG_NOTFOUND) + ret2=ret; + } + } + + path=scriptpath; + while(path!=NULL){ + colon=strchr(path, ':'); + if(colon!=NULL){ + dir=extl_scopyn(path, colon-path); + path=colon+1; + }else{ + dir=extl_scopy(path); + path=NULL; + } + + if(dir!=NULL){ + if(*dir!='\0'){ + for(file=files; *file!=NULL; file++){ + ret=do_try(dir, *file, tryfn, tryfnparam); + if(ret>=0){ + free(dir); + return ret; + } + if(ret2==EXTL_TRYCONFIG_NOTFOUND) + ret2=ret; + } + } + free(dir); + } + } + + return ret2; +} + + +static int try_lookup(const char *file, char **ptr) +{ + if(access(file, F_OK)!=0) + return EXTL_TRYCONFIG_NOTFOUND; + *ptr=extl_scopy(file); + return (*ptr!=NULL); +} + + +static int try_load(const char *file, TryCallParam *param) +{ + if(access(file, F_OK)!=0) + return EXTL_TRYCONFIG_NOTFOUND; + + if(param->status==1) + extl_warn(TR("Falling back to %s."), file); + + if(!extl_loadfile(file, &(param->fn))){ + param->status=1; + return EXTL_TRYCONFIG_LOAD_FAILED; + } + + return EXTL_TRYCONFIG_OK; +} + + +static int try_call(const char *file, TryCallParam *param) +{ + int ret=try_load(file, param); + + if(ret!=EXTL_TRYCONFIG_OK) + return ret; + + ret=extl_call(param->fn, NULL, NULL); + + extl_unref_fn(param->fn); + + return (ret ? EXTL_TRYCONFIG_OK : EXTL_TRYCONFIG_CALL_FAILED); +} + + +static int try_read_savefile(const char *file, TryCallParam *param) +{ + int ret=try_load(file, param); + + if(ret!=EXTL_TRYCONFIG_OK) + return ret; + + ret=extl_call(param->fn, NULL, "t", &(param->tab)); + + extl_unref_fn(param->fn); + + return (ret ? EXTL_TRYCONFIG_OK : EXTL_TRYCONFIG_CALL_FAILED); +} + + +int extl_try_config(const char *fname, const char *cfdir, + ExtlTryConfigFn *tryfn, void *tryfnparam, + const char *ext1, const char *ext2) +{ + char *files[]={NULL, NULL, NULL, NULL}; + int n=0, ret=EXTL_TRYCONFIG_NOTFOUND, ret2; + bool search=FALSE, has_ext; + + /* Search etcpath only if path is not absolute */ + search=(fname[0]!='/'); + + /* Build list of files to look for */ + has_ext=strrchr(fname, '.')>strrchr(fname, '/'); + + if(!has_ext){ + if(ext1!=NULL){ + files[n]=extl_scat3(fname, ".", ext1); + if(files[n]!=NULL) + n++; + } + + if(ext2!=NULL){ + files[n]=extl_scat3(fname, ".", ext2); + if(files[n]!=NULL) + n++; + } + } + + if(has_ext || !search){ + files[n]=extl_scopy(fname); + if(files[n]!=NULL) + n++; + } + + /* NOTE for future changes: cfdir must not be scanned first for + * user configuration files to take precedence. + */ + + /* Scan through all possible files */ + if(search){ + ret2=try_etcpath((const char**)&files, tryfn, tryfnparam); + if(ret==EXTL_TRYCONFIG_NOTFOUND) + ret=ret2; + if(ret<0) + ret=try_dir((const char**)&files, cfdir, tryfn, tryfnparam); + }else{ + ret=try_dir((const char**)&files, NULL, tryfn, tryfnparam); + } + + while(n>0) + free(files[--n]); + + return ret; +} + + +/*EXTL_DOC + * Lookup script \var{file}. If \var{try_in_dir} is set, it is tried + * before the standard search path. + */ +EXTL_EXPORT +char *extl_lookup_script(const char *file, const char *sp) +{ + const char *files[]={NULL, NULL}; + char* tmp=NULL; + + if(file!=NULL){ + files[0]=file; + + if(sp!=NULL) + try_dir(files, sp, (ExtlTryConfigFn*)try_lookup, &tmp); + if(tmp==NULL) + try_etcpath(files, (ExtlTryConfigFn*)try_lookup, &tmp); + } + + return tmp; +} + + +bool extl_read_config(const char *file, const char *sp, bool warn_nx) +{ + TryCallParam param; + int retval; + + if(file==NULL) + return FALSE; + + param.status=0; + + retval=extl_try_config(file, sp, (ExtlTryConfigFn*)try_call, ¶m, + EXTL_COMPILED_EXTENSION, EXTL_EXTENSION); + + if(retval==EXTL_TRYCONFIG_NOTFOUND && warn_nx) + extl_warn(TR("Unable to find '%s' on search path."), file); + + return (retval==EXTL_TRYCONFIG_OK); +} + + +bool extl_read_savefile(const char *basename, ExtlTab *tabret) +{ + TryCallParam param; + int retval; + + param.status=0; + param.tab=extl_table_none(); + + retval=extl_try_config(basename, NULL, (ExtlTryConfigFn*)try_read_savefile, + ¶m, EXTL_EXTENSION, NULL); + + *tabret=param.tab; + + return (retval==EXTL_TRYCONFIG_OK); +} + + +/*EXTL_DOC + * Read a savefile. + */ +EXTL_EXPORT_AS(extl, read_savefile) +ExtlTab extl_extl_read_savefile(const char *basename) +{ + ExtlTab tab; + if(!extl_read_savefile(basename, &tab)) + return extl_table_none(); + return tab; +} + + +/*}}}*/ + + +/*{{{ extl_get_savefile */ + + +static bool ensuredir(char *f) +{ + char *p; + int tryno=0; + bool ret=TRUE; + + if(access(f, F_OK)==0) + return TRUE; + + if(mkdir(f, 0700)==0) + return TRUE; + + p=strrchr(f, '/'); + if(p==NULL){ + extl_warn_err_obj(f); + return FALSE; + } + + *p='\0'; + if(!ensuredir(f)) + return FALSE; + *p='/'; + + if(mkdir(f, 0700)==0) + return TRUE; + + extl_warn_err_obj(f); + return FALSE; +} + + +/*EXTL_DOC + * Get a file name to save (session) data in. The string \var{basename} + * should contain no path or extension components. + */ +EXTL_EXPORT +char *extl_get_savefile(const char *basename) +{ + char *res=NULL; + + if(sessiondir==NULL) + return NULL; + + if(!ensuredir(sessiondir)){ + extl_warn(TR("Unable to create session directory \"%s\"."), + sessiondir); + return NULL; + } + + libtu_asprintf(&res, "%s/%s." EXTL_EXTENSION, sessiondir, basename); + + return res; +} + + +/*EXTL_DOC + * Write \var{tab} in file with basename \var{basename} in the + * session directory. + */ +EXTL_EXPORT +bool extl_write_savefile(const char *basename, ExtlTab tab) +{ + bool ret=FALSE; + char *fname=extl_get_savefile(basename); + + if(fname!=NULL){ + ret=extl_serialise(fname, tab); + free(fname); + } + + return ret; +} + + +/*}}}*/ + diff --git a/libextl/readconfig.h b/libextl/readconfig.h new file mode 100644 index 0000000..9bd2f30 --- /dev/null +++ b/libextl/readconfig.h @@ -0,0 +1,51 @@ +/* + * libextl/readconfig.h + * + * Copyright (c) Tuomo Valkonen 1999-2005. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + */ + +#ifndef LIBEXTL_READCONFIG_H +#define LIBEXTL_READCONFIG_H + +#include "extl.h" + +typedef int ExtlTryConfigFn(const char *file, void *param); + +enum{ + EXTL_TRYCONFIG_MEMERROR=-3, + EXTL_TRYCONFIG_NOTFOUND=-2, + EXTL_TRYCONFIG_LOAD_FAILED=-1, + EXTL_TRYCONFIG_CALL_FAILED=0, + EXTL_TRYCONFIG_OK=1 +}; + + +extern bool extl_set_userdirs(const char *appname); +extern bool extl_set_sessiondir(const char *session); +extern bool extl_set_searchpath(const char *path); +extern bool extl_add_searchdir(const char *dir); + +extern const char *extl_userdir(); +extern const char *extl_sessiondir(); +extern const char *extl_searchpath(); + +extern int extl_try_config(const char *fname, const char *cfdir, + ExtlTryConfigFn *tryfn, void *tryfnparam, + const char *ext1, const char *ext2); + +extern char *extl_lookup_script(const char *file, const char *sp); + +extern bool extl_read_config(const char *file, const char *sp, + bool warn_nx); + +extern char *extl_get_savefile(const char *module); +extern bool extl_read_savefile(const char *module, ExtlTab *tabret); +extern ExtlTab extl_extl_read_savefile(const char *module); +extern bool extl_write_savefile(const char *module, ExtlTab tab); + +#endif /* LIBEXTL_READCONFIG_H */ diff --git a/libextl/types.h b/libextl/types.h new file mode 100644 index 0000000..f6236e0 --- /dev/null +++ b/libextl/types.h @@ -0,0 +1,32 @@ +/* + * libextl/types.h + * + * Copyright (c) Tuomo Valkonen 2004-2005. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + */ + +#ifndef LIBEXTL_TYPES_H +#define LIBEXTL_TYPES_H + +#if 1 + +#include +#include + +#else + +#ifndef bool +#define bool int +#define TRUE 1 +#define FALSE 0 +#endif + +/*typedef ? Obj;*/ + +#endif + +#endif /* LIBEXTL_TYPES_H */ diff --git a/libmainloop/Makefile b/libmainloop/Makefile new file mode 100644 index 0000000..6f887fa --- /dev/null +++ b/libmainloop/Makefile @@ -0,0 +1,31 @@ +## +## Libmainloop Makefile +## + +# System-specific configuration is in system.mk +TOPDIR=.. +include $(TOPDIR)/build/system-inc.mk + +###################################### + +INCLUDES += $(LIBTU_INCLUDES) $(LIBEXTL_INCLUDES) -I.. + +CFLAGS += $(XOPEN_SOURCE) $(C99_SOURCE) + +SOURCES = select.c defer.c signal.c hooks.c exec.c + +#MAKE_EXPORTS=mainloop + +TARGETS = libmainloop.a + +###################################### + +include $(TOPDIR)/build/rules.mk + +###################################### + +libmainloop.a: $(OBJS) + $(AR) $(ARFLAGS) $@ $+ + $(RANLIB) $@ + +_install: diff --git a/libmainloop/defer.c b/libmainloop/defer.c new file mode 100644 index 0000000..c79e2ce --- /dev/null +++ b/libmainloop/defer.c @@ -0,0 +1,198 @@ +/* + * ion/libmainloop/defer.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +/* This file contains routines for deferred execution of potentially + * dangerous actions. They're called upon returning to the main + * loop. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "defer.h" + + +DECLSTRUCT(WDeferred){ + Watch watch; + WDeferredAction *action; + ExtlFn fn; + WDeferred *next, *prev; + WDeferred **list; +}; + + +static WDeferred *deferred=NULL; + + +#define N_DBUF 16 + +/* To avoid allocating memory all the time, we use a small + * buffer that should be able to contain the small expected + * number of simultaneous deferred actions. + */ +static WDeferred dbuf[N_DBUF]; +static int dbuf_used=0; + + +static WDeferred *alloc_defer() +{ + int i; + + /* Keeping it simple -- this naive loop should do it + * as N_DBUF is small. + */ + for(i=0; i=dbuf && dlist), d, next, prev); + + free_defer(d); + + warn(TR("Object destroyed while deferred actions are still pending.")); +} + + +bool mainloop_defer_action_on_list(Obj *obj, WDeferredAction *action, + WDeferred **list) +{ + WDeferred *d; + + d=alloc_defer(); + + if(d==NULL){ + warn_err(); + return FALSE; + } + + d->action=action; + d->list=list; + d->fn=extl_fn_none(); + + if(obj!=NULL) + watch_setup(&(d->watch), obj, defer_watch_handler); + + LINK_ITEM(*list, d, next, prev); + + return TRUE; +} + + +bool mainloop_defer_action(Obj *obj, WDeferredAction *action) +{ + return mainloop_defer_action_on_list(obj, action, &deferred); +} + + +bool mainloop_defer_destroy(Obj *obj) +{ + if(OBJ_IS_BEING_DESTROYED(obj)) + return FALSE; + + return mainloop_defer_action(obj, destroy_obj); +} + + +bool mainloop_defer_extl_on_list(ExtlFn fn, WDeferred **list) +{ + WDeferred *d; + + d=alloc_defer(); + + if(d==NULL){ + warn_err(); + return FALSE; + } + + d->action=NULL; + d->list=list; + d->fn=extl_ref_fn(fn); + + watch_init(&(d->watch)); + + LINK_ITEM(*list, d, next, prev); + + return TRUE; +} + + +/*EXTL_DOC + * Defer execution of \var{fn} until the main loop. + */ +EXTL_SAFE +EXTL_EXPORT_AS(mainloop, defer) +bool mainloop_defer_extl(ExtlFn fn) +{ + return mainloop_defer_extl_on_list(fn, &deferred); +} + + +static void do_execute(WDeferred *d) +{ + Obj *obj=d->watch.obj; + WDeferredAction *a=d->action; + ExtlFn fn=d->fn; + + watch_reset(&(d->watch)); + free_defer(d); + + if(a!=NULL){ + if(obj!=NULL) + a(obj); + }else if(fn!=extl_fn_none()){ + extl_call(fn, NULL, NULL); + extl_unref_fn(fn); + } +} + + +void mainloop_execute_deferred_on_list(WDeferred **list) +{ + Obj *obj; + void (*action)(Obj*); + + while(*list!=NULL){ + WDeferred *d=*list; + UNLINK_ITEM(*list, d, next, prev); + do_execute(d); + } +} + + +void mainloop_execute_deferred() +{ + mainloop_execute_deferred_on_list(&deferred); +} + diff --git a/libmainloop/defer.h b/libmainloop/defer.h new file mode 100644 index 0000000..3c6807e --- /dev/null +++ b/libmainloop/defer.h @@ -0,0 +1,35 @@ +/* + * ion/libmainloop/defer.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_LIBMAINLOOP_DEFER_H +#define ION_LIBMAINLOOP_DEFER_H + +#include +#include +#include + +INTRSTRUCT(WDeferred); + +typedef void WDeferredAction(Obj*); + +extern void mainloop_execute_deferred(); +extern void mainloop_execute_deferred_on_list(WDeferred **list); + +extern bool mainloop_defer_action(Obj *obj, WDeferredAction *action); +extern bool mainloop_defer_action_on_list(Obj *obj, WDeferredAction *action, + WDeferred **list); + +extern bool mainloop_defer_destroy(Obj *obj); + +extern bool mainloop_defer_extl(ExtlFn fn); +extern bool mainloop_defer_extl_on_list(ExtlFn fn, WDeferred **list); + +#endif /* ION_LIBMAINLOOP_DEFER_H */ diff --git a/libmainloop/exec.c b/libmainloop/exec.c new file mode 100644 index 0000000..3f0f264 --- /dev/null +++ b/libmainloop/exec.c @@ -0,0 +1,308 @@ +/* + * ion/mainloop/exec.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "select.h" +#include "exec.h" + + +/*{{{ Exec/spawn/fork */ + +#define SHELL_PATH "/bin/sh" +#define SHELL_NAME "sh" +#define SHELL_ARG "-c" + + +void mainloop_do_exec(const char *cmd) +{ + char *argv[4]; + + argv[0]=SHELL_NAME; + argv[1]=SHELL_ARG; + argv[2]=(char*)cmd; /* stupid execve... */ + argv[3]=NULL; + execvp(SHELL_PATH, argv); +} + + +static int mypipe(int *fds) +{ + int r=pipe(fds); + if(r==0){ + cloexec_braindamage_fix(fds[0]); + cloexec_braindamage_fix(fds[1]); + }else{ + warn_err_obj("pipe()"); + } + return r; +} + + +static bool unblock(int fd) +{ + int fl=fcntl(fd, F_GETFL); + if(fl!=-1) + fl=fcntl(fd, F_SETFL, fl|O_NONBLOCK); + return (fd!=-1); +} + + +static void duppipe(int fd, int idx, int *fds) +{ + close(fd); + dup(fds[idx]); + close(fds[0]); + close(fds[1]); +} + + +pid_t mainloop_fork(void (*fn)(void *p), void *fnp, + int *infd, int *outfd, int *errfd) +{ + int pid; + int infds[2]; + int outfds[2]; + int errfds[2]; + + if(infd!=NULL){ + if(mypipe(infds)!=0) + return -1; + } + + if(outfd!=NULL){ + if(mypipe(outfds)!=0) + goto err1; + } + + if(errfd!=NULL){ + if(mypipe(errfds)!=0) + goto err2; + } + + + pid=fork(); + + if(pid<0) + goto err3; + + if(pid!=0){ + if(outfd!=NULL){ + if(!unblock(outfds[0])) + goto err3; + *outfd=outfds[0]; + close(outfds[1]); + } + + if(errfd!=NULL){ + if(!unblock(errfds[0])) + goto err3; + *errfd=errfds[0]; + close(errfds[1]); + } + + if(infd!=NULL){ + *infd=infds[1]; + close(infds[0]); + } + + return pid; + } + + if(infd!=NULL) + duppipe(0, 0, infds); + if(outfd!=NULL) + duppipe(1, 1, outfds); + if(errfd!=NULL) + duppipe(2, 1, errfds); + + fn(fnp); + + abort(); + +err3: + warn_err(); + if(errfd!=NULL){ + close(errfds[0]); + close(errfds[1]); + } +err2: + if(outfd!=NULL){ + close(outfds[0]); + close(outfds[1]); + } +err1: + if(infd!=NULL){ + close(infds[0]); + close(infds[1]); + } + return -1; +} + + +typedef struct{ + const char *cmd; + void (*initenv)(void *p); + void *initenvp; +} SpawnP; + + +static void do_spawn(void *spawnp) +{ + SpawnP *p=(SpawnP*)spawnp; + + if(p->initenv) + p->initenv(p->initenvp); + mainloop_do_exec(p->cmd); +} + + +pid_t mainloop_do_spawn(const char *cmd, + void (*initenv)(void *p), void *p, + int *infd, int *outfd, int *errfd) +{ + SpawnP spawnp; + + spawnp.cmd=cmd; + spawnp.initenv=initenv; + spawnp.initenvp=p; + + return mainloop_fork(do_spawn, (void*)&spawnp, infd, outfd, errfd); +} + + +pid_t mainloop_spawn(const char *cmd) +{ + return mainloop_do_spawn(cmd, NULL, NULL, NULL, NULL, NULL); +} + + +/*}}}*/ + + +/*{{{ popen_bgread */ + + +#define BL 1024 + +bool mainloop_process_pipe_extlfn(int fd, ExtlFn fn) +{ + char buf[BL]; + int n; + + n=read(fd, buf, BL-1); + if(n<0){ + if(errno==EAGAIN || errno==EINTR) + return TRUE; + n=0; + warn_err_obj(TR("reading a pipe")); + return FALSE; + }else if(n>0){ + buf[n]='\0'; + extl_call(fn, "s", NULL, &buf); + return TRUE; + }else/* if(n==0)*/{ + /* Call with no argument/NULL string to signify EOF */ + extl_call(fn, NULL, NULL); + return FALSE; + } +} + + +static void process_pipe(int fd, void *p) +{ + if(!mainloop_process_pipe_extlfn(fd, *(ExtlFn*)p)){ + /* We get here on EOL or if the handler failed */ + mainloop_unregister_input_fd(fd); + close(fd); + extl_unref_fn(*(ExtlFn*)p); + free(p); + } +} + + +bool mainloop_register_input_fd_extlfn(int fd, ExtlFn fn) +{ + ExtlFn *p=ALLOC(ExtlFn); + if(p!=NULL){ + *(ExtlFn*)p=extl_ref_fn(fn); + if(mainloop_register_input_fd(fd, p, process_pipe)) + return TRUE; + extl_unref_fn(*(ExtlFn*)p); + free(p); + } + return FALSE; +} + + +pid_t mainloop_popen_bgread(const char *cmd, + void (*initenv)(void *p), void *p, + ExtlFn handler, ExtlFn errhandler) +{ + pid_t pid=-1; + int fd=-1, errfd=-1; + ExtlFn none=extl_fn_none(); + + pid=mainloop_do_spawn(cmd, initenv, p, NULL, + (handler!=none ? &fd : NULL), + (errhandler!=none ? &errfd : NULL)); + + if(pid>0){ + if(handler!=none){ + if(!mainloop_register_input_fd_extlfn(fd, handler)) + goto err; + } + if(errhandler!=extl_fn_none()){ + if(!mainloop_register_input_fd_extlfn(errfd, errhandler)) + goto err; + } + } + + return pid; + +err: + if(fd>=0) + close(fd); + if(errfd>=0) + close(errfd); + return -1; +} + + +/*}}}*/ + + +/*{{{ Misc. */ + + +void cloexec_braindamage_fix(int fd) +{ + fcntl(fd, F_SETFD, FD_CLOEXEC); +} + + +/*}}}*/ + diff --git a/libmainloop/exec.h b/libmainloop/exec.h new file mode 100644 index 0000000..c9278b3 --- /dev/null +++ b/libmainloop/exec.h @@ -0,0 +1,37 @@ +/* + * ion/libmainloop/exec.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_LIBMAINLOOP_EXEC_H +#define ION_LIBMAINLOOP_EXEC_H + +#include +#include + +#include + +extern void mainloop_do_exec(const char *cmd); +extern pid_t mainloop_fork(void (*fn)(void *p), void *p, + int *infd, int *outfd, int *errfd); +extern pid_t mainloop_do_spawn(const char *cmd, + void (*initenv)(void *p), void *p, + int *infd, int *outfd, int *errfd); +extern pid_t mainloop_spawn(const char *cmd); + +extern pid_t mainloop_popen_bgread(const char *cmd, + void (*initenv)(void *p), void *p, + ExtlFn handler, ExtlFn errhandler); + +extern bool mainloop_register_input_fd_extlfn(int fd, ExtlFn fn); +extern bool mainloop_process_pipe_extlfn(int fd, ExtlFn fn); + +extern void cloexec_braindamage_fix(int fd); + +#endif /* ION_LIBMAINLOOP_EXEC_H */ diff --git a/libmainloop/hooks.c b/libmainloop/hooks.c new file mode 100644 index 0000000..1a15bc9 --- /dev/null +++ b/libmainloop/hooks.c @@ -0,0 +1,423 @@ +/* + * ion/mainloop/hooks.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "hooks.h" + + +EXTL_EXPORT +IMPLCLASS(WHook, Obj, hook_deinit, NULL); + +static Rb_node named_hooks=NULL; + + +/*{{{ Named hooks */ + + +/* If hk==NULL to register, new is attempted to be created. */ +WHook *mainloop_register_hook(const char *name, WHook *hk) +{ + bool created=FALSE; + char *nnm; + + if(hk==NULL) + return NULL; + + if(named_hooks==NULL){ + named_hooks=make_rb(); + if(named_hooks==NULL) + return NULL; + } + + nnm=scopy(name); + + if(nnm==NULL) + return NULL; + + if(!rb_insert(named_hooks, nnm, hk)){ + free(nnm); + destroy_obj((Obj*)hk); + } + + return hk; +} + + +WHook *mainloop_unregister_hook(const char *name, WHook *hk) +{ + bool found=FALSE; + Rb_node node; + + if(named_hooks==NULL) + return NULL; + + if(hk==NULL){ + assert(name!=NULL); + node=rb_find_key_n(named_hooks, name, &found); + }else{ + rb_traverse(node, named_hooks){ + if((WHook*)rb_val(node)==hk){ + found=TRUE; + break; + } + } + } + + if(found){ + hk=(WHook*)rb_val(node); + free((char*)node->k.key); + rb_delete_node(node); + } + + return hk; +} + + +/*EXTL_DOC + * Find named hook \var{name}. + */ +EXTL_SAFE +EXTL_EXPORT +WHook *mainloop_get_hook(const char *name) +{ + if(named_hooks!=NULL){ + bool found=FALSE; + Rb_node node=rb_find_key_n(named_hooks, name, &found); + if(found) + return (WHook*)rb_val(node); + } + + return NULL; +} + + +/*}}}*/ + + +/*{{{ Init/deinit */ + + +static void destroy_item(WHook *hk, WHookItem *item) +{ + if(item->fn==NULL) + extl_unref_fn(item->efn); + UNLINK_ITEM(hk->items, item, next, prev); + free(item); +} + + +static WHookItem *create_item(WHook *hk) +{ + WHookItem *item=ALLOC(WHookItem); + if(item!=NULL){ + LINK_ITEM_FIRST(hk->items, item, next, prev); + item->fn=NULL; + item->efn=extl_fn_none(); + } + + return item; +} + + +bool hook_init(WHook *hk) +{ + hk->items=NULL; + return TRUE; +} + + +WHook *create_hook() +{ + CREATEOBJ_IMPL(WHook, hook, (p)); +} + + +void hook_deinit(WHook *hk) +{ + mainloop_unregister_hook(NULL, hk); + while(hk->items!=NULL) + destroy_item(hk, hk->items); +} + + +/*}}}*/ + + +/*{{{ Find/add/remove */ + + +WHookItem *hook_find(WHook *hk, WHookDummy *fn) +{ + WHookItem *hi; + + for(hi=hk->items; hi!=NULL; hi=hi->next){ + if(hi->fn==fn) + return hi; + } + + return NULL; +} + + +WHookItem *hook_find_extl(WHook *hk, ExtlFn efn) +{ + WHookItem *hi; + + for(hi=hk->items; hi!=NULL; hi=hi->next){ + if(extl_fn_eq(hi->efn, efn)) + return hi; + } + + return NULL; +} + + +/*EXTL_DOC + * Is \var{fn} hooked to hook \var{hk}? + */ +EXTL_SAFE +EXTL_EXPORT_MEMBER +bool hook_listed(WHook *hk, ExtlFn efn) +{ + return hook_find_extl(hk, efn)!=NULL; +} + + +bool hook_add(WHook *hk, WHookDummy *fn) +{ + WHookItem *item; + + if(hook_find(hk, fn)) + return FALSE; + + item=create_item(hk); + if(item==NULL) + return FALSE; + item->fn=fn; + return TRUE; +} + + +/*EXTL_DOC + * Add \var{efn} to the list of functions to be called when the + * hook \var{hk} is triggered. + */ +EXTL_EXPORT_AS(WHook, add) +bool hook_add_extl(WHook *hk, ExtlFn efn) +{ + WHookItem *item; + + if(efn==extl_fn_none()){ + warn(TR("No function given.")); + return FALSE; + } + + if(hook_find_extl(hk, efn)) + return FALSE; + + item=create_item(hk); + + if(item==NULL) + return FALSE; + + item->efn=extl_ref_fn(efn); + + return TRUE; +} + + +bool hook_remove(WHook *hk, WHookDummy *fn) +{ + WHookItem *item=hook_find(hk, fn); + if(item!=NULL) + destroy_item(hk, item); + return (item!=NULL); +} + + +/*EXTL_DOC + * Remove \var{efn} from the list of functions to be called when the + * hook \var{hk} is triggered. + */ +EXTL_EXPORT_AS(WHook, remove) +bool hook_remove_extl(WHook *hk, ExtlFn efn) +{ + WHookItem *item=hook_find_extl(hk, efn); + if(item!=NULL) + destroy_item(hk, item); + return (item!=NULL); +} + + +/*}}}*/ + + +/*{{{ Basic marshallers */ + + +static bool marshall_v(WHookDummy *fn, void *param) +{ + fn(); + return TRUE; +} + + +static bool marshall_extl_v(ExtlFn fn, void *param) +{ + extl_call(fn, NULL, NULL); + return TRUE; +} + + +static bool marshall_o(WHookDummy *fn, void *param) +{ + fn((Obj*)param); + return TRUE; +} + + +static bool marshall_extl_o(ExtlFn fn, void *param) +{ + return extl_call(fn, "o", NULL, (Obj*)param); +} + + +static bool marshall_p(WHookDummy *fn, void *param) +{ + fn(param); + return TRUE; +} + + +static bool marshall_alt_v(bool (*fn)(), void *param) +{ + return fn(); +} + + +static bool marshall_extl_alt_v(ExtlFn fn, void *param) +{ + bool ret=FALSE; + extl_call(fn, NULL, "b", &ret); + return ret; +} + + +static bool marshall_alt_o(bool (*fn)(), void *param) +{ + return fn((Obj*)param); +} + + +static bool marshall_extl_alt_o(ExtlFn fn, void *param) +{ + bool ret=FALSE; + extl_call(fn, "o", "b", (Obj*)param, &ret); + return ret; +} + + +static bool marshall_alt_p(bool (*fn)(), void *param) +{ + return fn(param); +} + + +/*}}}*/ + + +/*{{{ Call */ + + +void hook_call(const WHook *hk, void *p, + WHookMarshall *m, WHookMarshallExtl *em) +{ + WHookItem *hi, *next; + + for(hi=hk->items; hi!=NULL; hi=next){ + next=hi->next; + if(hi->fn!=NULL) + m(hi->fn, p); + else if(em!=NULL) + em(hi->efn, p); + } +} + + +bool hook_call_alt(const WHook *hk, void *p, + WHookMarshall *m, WHookMarshallExtl *em) +{ + WHookItem *hi, *next; + bool ret=FALSE; + + for(hi=hk->items; hi!=NULL; hi=next){ + next=hi->next; + if(hi->fn!=NULL) + ret=m(hi->fn, p); + else if(em!=NULL) + ret=em(hi->efn, p); + if(ret) + break; + } + + return ret; +} + + +void hook_call_v(const WHook *hk) +{ + hook_call(hk, NULL, marshall_v, marshall_extl_v); +} + + +void hook_call_o(const WHook *hk, Obj *o) +{ + hook_call(hk, o, marshall_o, marshall_extl_o); +} + + +void hook_call_p(const WHook *hk, void *p, WHookMarshallExtl *em) +{ + hook_call(hk, p, marshall_p, em); +} + + +bool hook_call_alt_v(const WHook *hk) +{ + return hook_call_alt(hk, NULL, (WHookMarshall*)marshall_alt_v, + (WHookMarshallExtl*)marshall_extl_alt_v); +} + + +bool hook_call_alt_o(const WHook *hk, Obj *o) +{ + return hook_call_alt(hk, o, (WHookMarshall*)marshall_alt_o, + (WHookMarshallExtl*)marshall_extl_alt_o); +} + + +bool hook_call_alt_p(const WHook *hk, void *p, WHookMarshallExtl *em) +{ + return hook_call_alt(hk, p, (WHookMarshall*)marshall_alt_p, em); +} + + +/*}}}*/ + + + diff --git a/libmainloop/hooks.h b/libmainloop/hooks.h new file mode 100644 index 0000000..63d3554 --- /dev/null +++ b/libmainloop/hooks.h @@ -0,0 +1,67 @@ +/* + * ion/mainloop/hooks.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_LIBMAINLOOP_HOOKS_H +#define ION_LIBMAINLOOP_HOOKS_H + +#include +#include + +INTRSTRUCT(WHookItem); +INTRCLASS(WHook); + +typedef void WHookDummy(); +typedef bool WHookMarshall(WHookDummy *fn, void *param); +typedef bool WHookMarshallExtl(ExtlFn fn, void *param); + +DECLSTRUCT(WHookItem){ + WHookDummy *fn; + ExtlFn efn; + WHookItem *next, *prev; +}; + +DECLCLASS(WHook){ + Obj obj; + WHookItem *items; +}; + + +/* If hk==NULL to register, new is attempted to be created. */ +extern WHook *mainloop_register_hook(const char *name, WHook *hk); +extern WHook *mainloop_unregister_hook(const char *name, WHook *hk); +extern WHook *mainloop_get_hook(const char *name); + +extern WHook *create_hook(); +extern bool hook_init(WHook *hk); +extern void hook_deinit(WHook *hk); + +extern bool hook_add(WHook *hk, WHookDummy *fn); +extern bool hook_remove(WHook *hk, WHookDummy *fn); +extern WHookItem *hook_find(WHook *hk, WHookDummy *fn); + +extern bool hook_add_extl(WHook *hk, ExtlFn fn); +extern bool hook_remove_extl(WHook *hk, ExtlFn fn); +extern WHookItem *hook_find_extl(WHook *hk, ExtlFn efn); + +extern void hook_call(const WHook *hk, void *p, + WHookMarshall *m, WHookMarshallExtl *em); +extern void hook_call_v(const WHook *hk); +extern void hook_call_o(const WHook *hk, Obj *o); +extern void hook_call_p(const WHook *hk, void *p, WHookMarshallExtl *em); + +extern bool hook_call_alt(const WHook *hk, void *p, + WHookMarshall *m, WHookMarshallExtl *em); +extern bool hook_call_alt_v(const WHook *hk); +extern bool hook_call_alt_o(const WHook *hk, Obj *o); +extern bool hook_call_alt_p(const WHook *hk, void *p, WHookMarshallExtl *em); + + +#endif /* ION_LIBMAINLOOP_HOOKS_H */ diff --git a/libmainloop/rx.mk b/libmainloop/rx.mk new file mode 100644 index 0000000..ba54cc9 --- /dev/null +++ b/libmainloop/rx.mk @@ -0,0 +1,11 @@ +## +## Rules for re-exporting libmainloop exports. +## + +MAINLOOP_DIR = $(TOPDIR)/libmainloop + +MAINLOOP_SOURCES_ = select.c defer.c signal.c hooks.c exec.c + +MAINLOOP_SOURCES = $(patsubst %,$(MAINLOOP_DIR)/%, $(MAINLOOP_SOURCES_)) + +MKEXPORTS_EXTRAS += -reexport mainloop $(MAINLOOP_SOURCES) diff --git a/libmainloop/select.c b/libmainloop/select.c new file mode 100644 index 0000000..2876222 --- /dev/null +++ b/libmainloop/select.c @@ -0,0 +1,110 @@ +/* + * ion/libmainloop/mainloop.c + * + * Partly based on a contributed code. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include + +#include "select.h" + + +/*{{{ File descriptor management */ + + +static WInputFd *input_fds=NULL; + +static WInputFd *find_input_fd(int fd) +{ + WInputFd *tmp=input_fds; + + while(tmp){ + if(tmp->fd==fd) + break; + tmp=tmp->next; + } + return tmp; +} + +bool mainloop_register_input_fd(int fd, void *data, + void (*callback)(int fd, void *d)) +{ + WInputFd *tmp; + + if(find_input_fd(fd)!=NULL) + return FALSE; + + tmp=ALLOC(WInputFd); + if(tmp==NULL) + return FALSE; + + tmp->fd=fd; + tmp->data=data; + tmp->process_input_fn=callback; + + LINK_ITEM(input_fds, tmp, next, prev); + + return TRUE; +} + +void mainloop_unregister_input_fd(int fd) +{ + WInputFd *tmp=find_input_fd(fd); + + if(tmp!=NULL){ + UNLINK_ITEM(input_fds, tmp, next, prev); + free(tmp); + } +} + +static void set_input_fds(fd_set *rfds, int *nfds) +{ + WInputFd *tmp=input_fds; + + while(tmp){ + FD_SET(tmp->fd, rfds); + if(tmp->fd>*nfds) + *nfds=tmp->fd; + tmp=tmp->next; + } +} + +static void check_input_fds(fd_set *rfds) +{ + WInputFd *tmp=input_fds, *next=NULL; + + while(tmp){ + next=tmp->next; + if(FD_ISSET(tmp->fd, rfds)) + tmp->process_input_fn(tmp->fd, tmp->data); + tmp=next; + } +} + +/*}}}*/ + + +/*{{{ Select */ + +void mainloop_select() +{ + fd_set rfds; + int nfds=0; + + FD_ZERO(&rfds); + + set_input_fds(&rfds, &nfds); + + if(select(nfds+1, &rfds, NULL, NULL, NULL)>0) + check_input_fds(&rfds); +} + + +/*}}}*/ diff --git a/libmainloop/select.h b/libmainloop/select.h new file mode 100644 index 0000000..465f134 --- /dev/null +++ b/libmainloop/select.h @@ -0,0 +1,36 @@ +/* + * ion/mainloop/select.h + * + * Based on a contributed readfds code. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_LIBMAINLOOP_SELECT_H +#define ION_LIBMAINLOOP_SELECT_H + +#include +#include +#include +#include +#include + +INTRSTRUCT(WInputFd); + +DECLSTRUCT(WInputFd){ + int fd; + void *data; + void (*process_input_fn)(int fd, void *data); + WInputFd *next, *prev; +}; + +extern bool mainloop_register_input_fd(int fd, void *data, + void (*callback)(int fd, void *data)); +extern void mainloop_unregister_input_fd(int fd); + +extern void mainloop_select(); + +#endif /* ION_LIBMAINLOOP_SELECT_H */ diff --git a/libmainloop/signal.c b/libmainloop/signal.c new file mode 100644 index 0000000..ddf6aba --- /dev/null +++ b/libmainloop/signal.c @@ -0,0 +1,529 @@ +/* + * ion/libmainloop/signal.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "signal.h" +#include "hooks.h" + +static int kill_sig=0; +#if 1 +static int wait_sig=0; +#endif + +static int usr2_sig=0; +static bool had_tmr=FALSE; + +WHook *mainloop_sigchld_hook=NULL; +WHook *mainloop_sigusr2_hook=NULL; + + +/*{{{ Timers */ + + +static WTimer *queue=NULL; + + +#define TIMEVAL_LATER(a, b) \ + ((a.tv_sec > b.tv_sec) || \ + ((a.tv_sec == b.tv_sec) && \ + (a.tv_usec > b.tv_usec))) + +#define USECS_IN_SEC 1000000 + + +static void do_timer_set() +{ + struct itimerval val={{0, 0}, {0, 0}}; + + if(queue==NULL){ + setitimer(ITIMER_REAL, &val, NULL); + return; + } + + /* Subtract queue time from current time, don't go below zero */ + gettimeofday(&(val.it_value), NULL); + if(TIMEVAL_LATER((queue)->when, val.it_value)){ + if(queue->when.tv_usecwhen.tv_usec+=USECS_IN_SEC; + queue->when.tv_sec--; + } + val.it_value.tv_usec=queue->when.tv_usec-val.it_value.tv_usec; + val.it_value.tv_sec=queue->when.tv_sec-val.it_value.tv_sec; + if(val.it_value.tv_usec<0) + val.it_value.tv_usec=0; + /* POSIX and some kernels have been designed by absolute morons and + * contain idiotic artificial restrictions on the value of tv_usec, + * that will only cause more code being run and clock cycles being + * spent to do the same thing, as the kernel will in any case convert + * the seconds to some other units. + */ + val.it_value.tv_sec+=val.it_value.tv_usec/USECS_IN_SEC; + val.it_value.tv_usec%=USECS_IN_SEC; + }else{ + had_tmr=TRUE; + return; + } + + val.it_interval.tv_usec=val.it_value.tv_usec; + val.it_interval.tv_sec=val.it_value.tv_sec; + + if((setitimer(ITIMER_REAL, &val, NULL))){ + had_tmr=TRUE; + } +} + + +typedef struct{ + pid_t pid; + int code; +} ChldParams; + + +static bool mrsh_chld(void (*fn)(pid_t, int), ChldParams *p) +{ + fn(p->pid, p->code); + return TRUE; +} + + +static bool mrsh_chld_extl(ExtlFn fn, ChldParams *p) +{ + ExtlTab t=extl_create_table(); + bool ret; + + extl_table_sets_i(t, "pid", (int)p->pid); + + if(WIFEXITED(p->code)){ + extl_table_sets_b(t, "exited", TRUE); + extl_table_sets_i(t, "exitstatus", WEXITSTATUS(p->code)); + } + if(WIFSIGNALED(p->code)){ + extl_table_sets_b(t, "signaled", TRUE); + extl_table_sets_i(t, "termsig", WTERMSIG(p->code)); +#ifdef WCOREDUMP + extl_table_sets_i(t, "coredump", WCOREDUMP(p->code)); +#endif + } + if(WIFSTOPPED(p->code)){ + extl_table_sets_b(t, "stopped", TRUE); + extl_table_sets_i(t, "stopsig", WSTOPSIG(p->code)); + } + /*if(WIFCONTINUED(p->code)){ + extl_table_sets_b(t, "continued", TRUE); + }*/ + + ret=extl_call(fn, "t", NULL, t); + + extl_unref_table(t); + + return ret; +} + +static bool mrsh_usr2(void (*fn)(void), void *p) +{ + fn(); + return TRUE; +} + +static bool mrsh_usr2_extl(ExtlFn fn, void *p) +{ + bool ret; + ExtlTab t=extl_create_table(); + ret=extl_call(fn, "t", NULL, t); + extl_unref_table(t); + return ret; +} + + +bool mainloop_check_signals() +{ + struct timeval current_time; + WTimer *q; + int ret=0; + + if(usr2_sig!=0){ + usr2_sig=0; + if(mainloop_sigusr2_hook!=NULL){ + hook_call(mainloop_sigusr2_hook, NULL, + (WHookMarshall*)mrsh_usr2, + (WHookMarshallExtl*)mrsh_usr2_extl); + } + } + +#if 1 + if(wait_sig!=0){ + ChldParams p; + wait_sig=0; + while((p.pid=waitpid(-1, &p.code, WNOHANG|WUNTRACED))>0){ + if(mainloop_sigchld_hook!=NULL && + (WIFEXITED(p.code) || WIFSIGNALED(p.code))){ + hook_call(mainloop_sigchld_hook, &p, + (WHookMarshall*)mrsh_chld, + (WHookMarshallExtl*)mrsh_chld_extl); + } + } + } +#endif + + if(kill_sig!=0) + return kill_sig; + + /* Check for timer events in the queue */ + while(had_tmr && queue!=NULL){ + had_tmr=FALSE; + gettimeofday(¤t_time, NULL); + while(queue!=NULL){ + if(TIMEVAL_LATER(current_time, queue->when)){ + q=queue; + queue=q->next; + q->next=NULL; + if(q->handler!=NULL){ + WTimerHandler *handler=q->handler; + Obj *obj=q->objwatch.obj; + q->handler=NULL; + watch_reset(&(q->objwatch)); + handler(q, obj); + }else if(q->extl_handler!=extl_fn_none()){ + ExtlFn fn=q->extl_handler; + Obj *obj=q->objwatch.obj; + watch_reset(&(q->objwatch)); + q->extl_handler=extl_fn_none(); + extl_call(fn, "o", NULL, obj); + extl_unref_fn(fn); + } + }else{ + break; + } + } + do_timer_set(); + } + + return ret; +} + + +static void add_to_current_time(struct timeval *when, uint msecs) +{ + long tmp_usec; + + gettimeofday(when, NULL); + tmp_usec=when->tv_usec + (msecs * 1000); + when->tv_usec=tmp_usec % 1000000; + when->tv_sec+=tmp_usec / 1000000; +} + + +/*EXTL_DOC + * Is timer set? + */ +EXTL_EXPORT_MEMBER +bool timer_is_set(WTimer *timer) +{ + WTimer *tmr; + for(tmr=queue; tmr!=NULL; tmr=tmr->next){ + if(tmr==timer) + return TRUE; + } + return FALSE; +} + + +void timer_do_set(WTimer *timer, uint msecs, WTimerHandler *handler, + Obj *obj, ExtlFn fn) +{ + WTimer *q, **qptr; + bool set=FALSE; + + timer_reset(timer); + + /* Initialize the new queue timer event */ + add_to_current_time(&(timer->when), msecs); + timer->next=NULL; + timer->handler=handler; + timer->extl_handler=fn; + if(obj!=NULL) + watch_setup(&(timer->objwatch), obj, NULL); + else + watch_reset(&(timer->objwatch)); + + /* Add timerevent in place to queue */ + q=queue; + qptr=&queue; + + while(q!=NULL){ + if(TIMEVAL_LATER(q->when, timer->when)) + break; + qptr=&(q->next); + q=q->next; + } + + timer->next=q; + *qptr=timer; + + do_timer_set(); +} + + +void timer_set(WTimer *timer, uint msecs, WTimerHandler *handler, + Obj *obj) +{ + timer_do_set(timer, msecs, handler, obj, extl_fn_none()); +} + + +/*EXTL_DOC + * Set \var{timer} to call \var{fn} in \var{msecs} milliseconds. + */ +EXTL_EXPORT_AS(WTimer, set) +void timer_set_extl(WTimer *timer, uint msecs, ExtlFn fn) +{ + timer_do_set(timer, msecs, NULL, NULL, extl_ref_fn(fn)); +} + + +/*EXTL_DOC + * Reset timer. + */ +EXTL_EXPORT_MEMBER +void timer_reset(WTimer *timer) +{ + WTimer *q=queue, **qptr=&queue; + + while(q!=NULL){ + if(q==timer){ + *qptr=timer->next; + do_timer_set(); + break; + } + qptr=&(q->next); + q=q->next; + + } + + timer->handler=NULL; + extl_unref_fn(timer->extl_handler); + timer->extl_handler=extl_fn_none(); + watch_reset(&(timer->objwatch)); +} + + +bool timer_init(WTimer *timer) +{ + timer->when.tv_sec=0; + timer->when.tv_usec=0; + timer->next=NULL; + timer->handler=NULL; + timer->extl_handler=extl_fn_none(); + watch_init(&(timer->objwatch)); + return TRUE; +} + +void timer_deinit(WTimer *timer) +{ + timer_reset(timer); +} + + +WTimer *create_timer() +{ + CREATEOBJ_IMPL(WTimer, timer, (p)); +} + +/*EXTL_DOC + * Create a new timer. + */ +EXTL_EXPORT_AS(mainloop, create_timer) +WTimer *create_timer_extl_owned() +{ + WTimer *timer=create_timer(); + if(timer!=NULL) + ((Obj*)timer)->flags|=OBJ_EXTL_OWNED; + return timer; +} + + +EXTL_EXPORT +IMPLCLASS(WTimer, Obj, timer_deinit, NULL); + + +/*}}}*/ + + +/*{{{ Signal handling */ + + +static void fatal_signal_handler(int signal_num) +{ + set_warn_handler(NULL); + warn(TR("Caught fatal signal %d. Dying without deinit."), signal_num); + signal(signal_num, SIG_DFL); + kill(getpid(), signal_num); +} + + +static void deadly_signal_handler(int signal_num) +{ + set_warn_handler(NULL); + warn(TR("Caught signal %d. Dying."), signal_num); + signal(signal_num, SIG_DFL); + /*if(ioncore_g.opmode==IONCORE_OPMODE_INIT) + kill(getpid(), signal_num); + else*/ + kill_sig=signal_num; +} + + +static void chld_handler(int signal_num) +{ +#if 0 + pid_t pid; + + while((pid=waitpid(-1, NULL, WNOHANG|WUNTRACED))>0){ + /* nothing */ + } +#else + wait_sig=1; +#endif +} + +static void usr2_handler(int signal_num) +{ + usr2_sig=1; +} + + +static void exit_handler(int signal_num) +{ + if(kill_sig>0){ + warn(TR("Got signal %d while %d is still to be handled."), + signal_num, kill_sig); + } + kill_sig=signal_num; +} + + +static void timer_handler(int signal_num) +{ + had_tmr=TRUE; +} + + +static void ignore_handler(int signal_num) +{ + +} + + +#ifndef SA_RESTART + /* glibc is broken (?) and does not define SA_RESTART with + * '-ansi -D_XOPEN_SOURCE -D_XOPEN_SOURCE_EXTENDED', so just try to live + * without it. + */ +#define SA_RESTART 0 +#endif + +#define IFTRAP(X) if(sigismember(which, X)) +#define DEADLY(X) IFTRAP(X) signal(X, deadly_signal_handler); +#define FATAL(X) IFTRAP(X) signal(X, fatal_signal_handler); +#define IGNORE(X) IFTRAP(X) signal(X, SIG_IGN) + +void mainloop_trap_signals(const sigset_t *which) +{ + struct sigaction sa; + sigset_t set, oldset; + sigset_t dummy; + + if(which==NULL){ + sigfillset(&dummy); + which=&dummy; + } + + sigemptyset(&set); + sigemptyset(&oldset); + sigprocmask(SIG_SETMASK, &set, &oldset); + + DEADLY(SIGHUP); + DEADLY(SIGQUIT); + DEADLY(SIGINT); + DEADLY(SIGABRT); + + FATAL(SIGILL); + FATAL(SIGSEGV); + FATAL(SIGFPE); + FATAL(SIGBUS); + + IGNORE(SIGTRAP); + /*IGNORE(SIGWINCH);*/ + + sigemptyset(&(sa.sa_mask)); + + IFTRAP(SIGALRM){ + sa.sa_handler=timer_handler; + sa.sa_flags=SA_RESTART; + sigaction(SIGALRM, &sa, NULL); + } + + IFTRAP(SIGCHLD){ + sa.sa_handler=chld_handler; + sa.sa_flags=SA_NOCLDSTOP|SA_RESTART; + sigaction(SIGCHLD, &sa, NULL); + } + + IFTRAP(SIGUSR2){ + sa.sa_handler=usr2_handler; + sa.sa_flags=SA_RESTART; + sigaction(SIGUSR2, &sa, NULL); + } + + IFTRAP(SIGTERM){ + sa.sa_handler=exit_handler; + sa.sa_flags=SA_RESTART; + sigaction(SIGTERM, &sa, NULL); + } + + IFTRAP(SIGUSR1){ + sa.sa_handler=exit_handler; + sa.sa_flags=SA_RESTART; + sigaction(SIGUSR1, &sa, NULL); + } + + /* SIG_IGN is preserved over execve and since the the default action + * for SIGPIPE is not to ignore it, some programs may get upset if + * the behaviour is not the default. + */ + IFTRAP(SIGPIPE){ + sa.sa_handler=ignore_handler; + sigaction(SIGPIPE, &sa, NULL); + } + +} + +#undef IGNORE +#undef FATAL +#undef DEADLY + + +/*}}}*/ + diff --git a/libmainloop/signal.h b/libmainloop/signal.h new file mode 100644 index 0000000..7867c18 --- /dev/null +++ b/libmainloop/signal.h @@ -0,0 +1,60 @@ +/* + * ion/mainloop/signal.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_LIBMAINLOOP_SIGNAL_H +#define ION_LIBMAINLOOP_SIGNAL_H + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "hooks.h" + +INTRCLASS(WTimer); + +typedef void WTimerHandler(WTimer *timer, Obj *obj); + + +DECLCLASS(WTimer){ + Obj obj; + struct timeval when; + WTimer *next; + WTimerHandler *handler; + Watch objwatch; + ExtlFn extl_handler; +}; + +extern bool timer_init(WTimer *timer); +extern void timer_deinit(WTimer *timer); + +extern WTimer *create_timer(); +extern WTimer *create_timer_extl_owned(); + +extern void timer_set(WTimer *timer, uint msecs, WTimerHandler *handler, + Obj *obj); +extern void timer_set_extl(WTimer *timer, uint msecs, ExtlFn fn); + +extern void timer_reset(WTimer *timer); +extern bool timer_is_set(WTimer *timer); + +extern bool mainloop_check_signals(); +extern void mainloop_trap_signals(const sigset_t *set); + +extern WHook *mainloop_sigchld_hook; +extern WHook *mainloop_sigusr2_hook; + +#endif /* ION_LIBMAINLOOP_SIGNAL_H */ diff --git a/libtu/LICENSE b/libtu/LICENSE new file mode 100644 index 0000000..1ddc996 --- /dev/null +++ b/libtu/LICENSE @@ -0,0 +1,640 @@ + +The Red-Black tree code is under the GNU LGPL. The rest of the files +you may distribute and modify under either under the Clarified Artistic +License or the GNU LGPL, version 2.1 or later, both reproduced below. + + +------------------- + + +The Clarified Artistic License + +Preamble + +The intent of this document is to state the conditions under which a +Package may be copied, such that the Copyright Holder maintains some +semblance of artistic control over the development of the package, while +giving the users of the package the right to use and distribute the Package +in a more-or-less customary fashion, plus the right to make reasonable +modifications. + +Definitions: + +"Package" refers to the collection of files distributed by the Copyright +Holder, and derivatives of that collection of files created through textual +modification. +"Standard Version" refers to such a Package if it has not been modified, or +has been modified in accordance with the wishes of the Copyright Holder as +specified below. +"Copyright Holder" is whoever is named in the copyright or copyrights for +the package. +"You" is you, if you're thinking about copying or distributing this +Package. +"Distribution fee" is a fee you charge for providing a copy of this Package +to another party. +"Freely Available" means that no fee is charged for the right to use the +item, though there may be fees involved in handling the item. It also means +that recipients of the item may redistribute it under the same conditions +they received it. + + 1. You may make and give away verbatim copies of the source form of the + Standard Version of this Package without restriction, provided that you + duplicate all of the original copyright notices and associated + disclaimers. + 2. You may apply bug fixes, portability fixes and other modifications + derived from the Public Domain, or those made Freely Available, or from + the Copyright Holder. A Package modified in such a way shall still be + considered the Standard Version. + 3. You may otherwise modify your copy of this Package in any way, provided + that you insert a prominent notice in each changed file stating how and + when you changed that file, and provided that you do at least ONE of + the following: + + 1. place your modifications in the Public Domain or otherwise make them + Freely Available, such as by posting said modifications to Usenet + or an equivalent medium, or placing the modifications on a major + network archive site allowing unrestricted access to them, or by + allowing the Copyright Holder to include your modifications in the + Standard Version of the Package. + 2. use the modified Package only within your corporation or + organization. + 3. rename any non-standard executables so the names do not conflict + with standard executables, which must also be provided, and provide + a separate manual page for each non-standard executable that + clearly documents how it differs from the Standard Version. + 4. make other distribution arrangements with the Copyright Holder. + 5. permit and encourge anyone who receives a copy of the modified + Package permission to make your modifications Freely Available in + some specific way. + + 4. You may distribute the programs of this Package in object code or + executable form, provided that you do at least ONE of the following: + + 1. distribute a Standard Version of the executables and library files, + together with instructions (in the manual page or equivalent) on + where to get the Standard Version. + 2. accompany the distribution with the machine-readable source of the + Package with your modifications. + 3. give non-standard executables non-standard names, and clearly + document the differences in manual pages (or equivalent), together + with instructions on where to get the Standard Version. + 4. make other distribution arrangements with the Copyright Holder. + 5. offer the machine-readable source of the Package, with your + modifications, by mail order. + + 5. You may charge a distribution fee for any distribution of this Package. + If you offer support for this Package, you may charge any fee you + choose for that support. You may not charge a license fee for the right + to use this Package itself. You may distribute this Package in + aggregate with other (possibly commercial and possibly nonfree) + programs as part of a larger (possibly commercial and possibly nonfree) + software distribution, and charge license fees for other parts of that + software distribution, provided that you do not advertise this Package + as a product of your own. If the Package includes an interpreter, You + may embed this Package's interpreter within an executable of yours (by + linking); this shall be construed as a mere form of aggregation, + provided that the complete Standard Version of the interpreter is so + embedded. + 6. The scripts and library files supplied as input to or produced as output + from the programs of this Package do not automatically fall under the + copyright of this Package, but belong to whoever generated them, and + may be sold commercially, and may be aggregated with this Package. If + such scripts or library files are aggregated with this Package via the + so-called "undump" or "unexec" methods of producing a binary executable + image, then distribution of such an image shall neither be construed as + a distribution of this Package nor shall it fall under the restrictions + of Paragraphs 3 and 4, provided that you do not represent such an + executable image as a Standard Version of this Package. + 7. C subroutines (or comparably compiled subroutines in other languages) + supplied by you and linked into this Package in order to emulate + subroutines and variables of the language defined by this Package shall + not be considered part of this Package, but are the equivalent of input + as in Paragraph 6, provided these subroutines do not change the + language in any way that would cause it to fail the regression tests + for the language. + 8. Aggregation of the Standard Version of the Package with a commercial + distribution is always permitted provided that the use of this Package + is embedded; that is, when no overt attempt is made to make this + Package's interfaces visible to the end user of the commercial + distribution. Such use shall not be construed as a distribution of this + Package. + 9. The name of the Copyright Holder may not be used to endorse or promote + products derived from this software without specific prior written + permission. +10. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + + +------------------- + + + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations +below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. +^L + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it +becomes a de-facto standard. To achieve this, non-free programs must +be allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. +^L + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control +compilation and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. +^L + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. +^L + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at least + three years, to give the same user the materials specified in + Subsection 6a, above, for a charge no more than the cost of + performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. +^L + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. +^L + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply, and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License +may add an explicit geographical distribution limitation excluding those +countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. +^L + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS +^L + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms +of the ordinary General Public License). + + To apply these terms, attach the following notices to the library. +It is safest to attach them to the start of each source file to most +effectively convey the exclusion of warranty; and each file should +have at least the "copyright" line and a pointer to where the full +notice is found. + + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or +your school, if any, to sign a "copyright disclaimer" for the library, +if necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James + Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/libtu/Makefile b/libtu/Makefile new file mode 100644 index 0000000..88b7a28 --- /dev/null +++ b/libtu/Makefile @@ -0,0 +1,58 @@ +## +## libtu Makefile +## + +# System-specific configuration is in system.mk +TOPDIR=. +include $(TOPDIR)/build/system-inc.mk + +###################################### + +CFLAGS += $(C89_SOURCE) $(POSIX_SOURCE) + +SOURCES=misc.c output.c util.c optparser.c parser.c tokenizer.c \ + map.c obj.c objlist.c errorlog.c ptrlist.c rb.c \ + stringstore.c iterable.c setparam.c + +ifdef LIBTU_NO_ERRMSG +DEFINES += -DLIBTU_NO_ERRMSG +else +ifndef HAS_SYSTEM_ASPRINTF +SOURCES += snprintf_2.2/snprintf.c +DEFINES += -DHAVE_SNPRINTF +else +DEFINES += -DHAS_SYSTEM_ASPRINTF +endif +endif + +TARGETS=libtu.a +TESTERS=tester tester2 tester3 + +###################################### + +include $(TOPDIR)/build/rules.mk + +###################################### + +testers: $(TESTERS) + +libtu.a: $(OBJS) + $(AR) $(ARFLAGS) $@ $+ + $(RANLIB) $@ + +tester: tester.c libtu.a + $(CC) $(CFLAGS) $< -L. -ltu -lm -o $@ + +tester2: tester2.c libtu.a + $(CC) $(CFLAGS) $< -L. -ltu -lm -o $@ + +tester3: tester3.c libtu.a + $(CC) $(CFLAGS) $< -L. -ltu -lm -o $@ + +_install: + $(INSTALLDIR) $(LIBDIR) + $(INSTALLDIR) $(INCDIR)/libtu + $(INSTALL) -m $(DATA_MODE) libtu.a $(LIBDIR) + for i in *.h; do \ + $(INSTALL) -m $(DATA_MODE) $$i $(INCDIR)/libtu; \ + done diff --git a/libtu/README b/libtu/README new file mode 100644 index 0000000..cce76e5 --- /dev/null +++ b/libtu/README @@ -0,0 +1,31 @@ + +libtu + +Copyright (c) Tuomo Valkonen 1999-2004. + + + +Libtu is a small utility library for programs written in C. + +Most of this library may be distributed and modified under either under +the Clarified Artistic License or the GNU LGPL, version 2.1 or later, +both reproduced in the file LICENSE. The red-black tree code is under +the GNU LGPL; see README.rb for details. + +To build the library, first edit system.mk to customize it for your +system if necessary. Then 'make depend && make'. +To install it run 'make install' (perhaps as root depending on +where you are installing it). + +If you want to use it in your programs, you may try to figure out how +by reading the header files (include/*.h) and test programs (tester*.c). +Or you could try urging me to write some sort of a manual. + +--- + +Libtu needs the functions asprintf and vasprintf. These do not +exist on most platforms. One implementation by Mark Martinec +is included in snprintf_2.2/ and is used by default. To use the +system's versions of these functions, if available, modify +system.mk. + diff --git a/libtu/README.rb b/libtu/README.rb new file mode 100644 index 0000000..471ef7f --- /dev/null +++ b/libtu/README.rb @@ -0,0 +1,113 @@ +Generic C code for red-black trees. +Copyright (C) 2000 James S. Plank + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +--------------------------------------------------------------------------- +Jim Plank +plank@cs.utk.edu +http://www.cs.utk.edu/~plank + +Department of Computer Science +University of Tennessee +107 Ayres Hall +Knoxville, TN 37996 + +615-974-4397 +$Revision: 1.2 $ +--------------------------------------------------------------------------- + +Rb.c and rb.h are files for doing general red-black trees. + +The directory ansi contains ansi-standard c code for rb-trees (there +are some gross pointer casts there but don't worry about them. They +work). The directory non-ansi contains straight c code for rb-trees. +The ansi version can also be used by c++ (it has been tested). + +Rb.h contains the typedef for red-black tree structures. Basically, +red-black trees are balanced trees whose external nodes are sorted +by a key, and connected in a linked list. The following is how +you use rb.c and rb.h: + +Include rb.h in your source. + +Make_rb() returns the head of a red-black tree. It serves two functions: +Its p.root pointer points to the root of the red-black tree. Its +c.list.flink and c.list.blink pointers point to the first and last +external nodes of the tree. When the tree is empty, all these pointers +point to itself. + +The external nodes can be traversed in sorted order with their +c.list.flink and c.list.blink pointers. The macros rb_first, rb_last, +rb_next, rb_prev, and rb_traverse can be used to traverse external node lists. + +External nodes hold two pieces of information: the key and the value +(in k.key and v.val, respectively). The key can be a character +string, an integer, or a general pointer. Val is typed as a character +pointer, but can be any pointer. If the key is a character string, +then one can insert it, and a value into a tree with rb_insert(). If +it is an integer, then rb_inserti() should be used. If it is a general +pointer, then rb_insertg() must be used, with a comparison function +passed as the fourth argument. This function takes two keys as arguments, +and returns a negative value, positive value, or 0, depending on whether +the first key is less than, greater than or equal to the second. Thus, +one could use rb_insertg(t, s, v, strcmp) to insert the value v with +a character string s into the tree t. + +Rb_find_key(t, k) returns the external node in t whose value is equal +k or whose value is the smallest value greater than k. (Here, k is +a string). If there is no value greater than or equal to k, then +t is returned. Rb_find_ikey(t,k) works when k is an integer, and +Rb_find_gkey(t,k,cmp) works for general pointers. + +Rb_find_key_n(t, k, n) is the same as rb_find_key, except that it +returns whether or not k was found in n (n is an int *). Rb_find_ikey_n +and Rb_find_gkey_n are the analogous routines for integer and general +keys. + +Rb_insert_b(e, k, v) makes a new external node with key k and val v, and +inserts it before the external node e. If e is the head of a tree, +then the new node is inserted at the end of the external node list. +If this insertion violates sorted order, no error is flagged. It is +assumed that the user knows what he/she is doing. Rb_insert_a(e,k,v) +inserts the new node after e (if e = t, it inserts the new node at the +beginning of the list). + +Rb_insert() is therefore really a combination of Rb_find_key() and +Rb_insert_b(). + +Rb_delete_node(e) deletes the external node e from a tree (and thus from +the linked list of external nodes). The node is free'd as well, so +don't retain pointers to it. + +Red-black trees are spiffy because find_key, insert, and delete are all +done in log(n) time. Thus, they can be freely used instead of hash-tables, +with the benifit of having the elements in sorted order at all times, and +with the guarantee of operations being in log(n) time. + +Other routines: + +Rb_print_tree() will grossly print out a red-black tree with string keys. +Rb_iprint_tree() will do the same with trees with integer keys. +Rb_nblack(e) will report the number of black nodes on the path from external + node e to the root. The path length is less than twice this value. +Rb_plength(e) reports the length of the path from e to the root. + + +You can find a general description of red-black trees in any basic algorithms +text. E.g. ``Introduction to Algorithms'', by Cormen, Leiserson and Rivest +(McGraw Hill). An excellent and complete description of red-black trees +can also be found in Chapter 1 of Heather Booth's PhD disseratation: +``Some Fast Algorithms on Graphs and Trees'', Princeton University, 1990. diff --git a/libtu/build/system-inc.mk b/libtu/build/system-inc.mk new file mode 100644 index 0000000..5390bc9 --- /dev/null +++ b/libtu/build/system-inc.mk @@ -0,0 +1,2 @@ +TOPDIR := $(TOPDIR)/.. +include $(TOPDIR)/build/system-inc.mk diff --git a/libtu/debug.h b/libtu/debug.h new file mode 100644 index 0000000..729222d --- /dev/null +++ b/libtu/debug.h @@ -0,0 +1,19 @@ +/* + * libtu/debug.h + * + * Copyright (c) Tuomo Valkonen 2004. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#ifndef LIBTU_DEBUG_H +#define LIBTU_DEBUG_H + +#ifdef CF_DEBUG +#define D(X) X +#else +#define D(X) +#endif + +#endif /* LIBTU_DEBUG_H */ diff --git a/libtu/dlist.h b/libtu/dlist.h new file mode 100644 index 0000000..9576771 --- /dev/null +++ b/libtu/dlist.h @@ -0,0 +1,124 @@ +/* + * libtu/common.h + * + * Copyright (c) Tuomo Valkonen 1999-2005. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#ifndef LIBTU_DLIST_H +#define LIBTU_DLIST_H + + +/*{{{ Linking */ + + +#define LINK_ITEM(LIST, ITEM, NEXT, PREV) \ + (ITEM)->NEXT=NULL; \ + if((LIST)==NULL){ \ + (LIST)=(ITEM); \ + (ITEM)->PREV=(ITEM); \ + }else{ \ + (ITEM)->PREV=(LIST)->PREV; \ + (ITEM)->PREV->NEXT=(ITEM); \ + (LIST)->PREV=(ITEM); \ + } + + +#define LINK_ITEM_FIRST(LIST, ITEM, NEXT, PREV) \ + (ITEM)->NEXT=(LIST); \ + if((LIST)==NULL){ \ + (ITEM)->PREV=(ITEM); \ + }else{ \ + (ITEM)->PREV=(LIST)->PREV; \ + (LIST)->PREV=(ITEM); \ + } \ + (LIST)=(ITEM); + + +#define LINK_ITEM_LAST LINK_ITEM + + +#define LINK_ITEM_BEFORE(LIST, BEFORE, ITEM, NEXT, PREV) \ + (ITEM)->NEXT=(BEFORE); \ + (ITEM)->PREV=(BEFORE)->PREV; \ + (BEFORE)->PREV=(ITEM); \ + if((BEFORE)==(LIST)) \ + (LIST)=(ITEM); \ + else \ + (ITEM)->PREV->NEXT=(ITEM) + + +#define LINK_ITEM_AFTER(LIST, AFTER, ITEM, NEXT, PREV) \ + (ITEM)->NEXT=(AFTER)->NEXT; \ + (ITEM)->PREV=(AFTER); \ + (AFTER)->NEXT=(ITEM); \ + if((ITEM)->NEXT==NULL) \ + (LIST)->PREV=(ITEM); \ + else \ + (ITEM)->NEXT->PREV=ITEM; + + +#define UNLINK_ITEM(LIST, ITEM, NEXT, PREV) \ + if((ITEM)->PREV!=NULL){ \ + if((ITEM)==(LIST)){ \ + (LIST)=(ITEM)->NEXT; \ + if((LIST)!=NULL) \ + (LIST)->PREV=(ITEM)->PREV; \ + }else if((ITEM)->NEXT==NULL){ \ + (ITEM)->PREV->NEXT=NULL; \ + (LIST)->PREV=(ITEM)->PREV; \ + }else{ \ + (ITEM)->PREV->NEXT=(ITEM)->NEXT; \ + (ITEM)->NEXT->PREV=(ITEM)->PREV; \ + } \ + } \ + (ITEM)->NEXT=NULL; \ + (ITEM)->PREV=NULL; + + +/*}}}*/ + + +/*{{{ Iteration */ + + +#define LIST_FIRST(LIST, NEXT, PREV) \ + (LIST) +#define LIST_LAST(LIST, NEXT, PREV) \ + ((LIST)==NULL ? NULL : LIST_PREV_WRAP(LIST, LIST, NEXT, PREV)) +#define LIST_NEXT(LIST, REG, NEXT, PREV) \ + ((REG)->NEXT) +#define LIST_PREV(LIST, REG, NEXT, PREV) \ + ((REG)->PREV->NEXT ? (REG)->PREV : NULL) +#define LIST_NEXT_WRAP(LIST, REG, NEXT, PREV) \ + (((REG) && (REG)->NEXT) ? (REG)->NEXT : (LIST)) +#define LIST_PREV_WRAP(LIST, REG, NEXT, PREV) \ + ((REG) ? (REG)->PREV : (LIST)) + +#define LIST_FOR_ALL(LIST, NODE, NEXT, PREV) \ + for(NODE=LIST; NODE!=NULL; NODE=(NODE)->NEXT) + +#define LIST_FOR_ALL_REV(LIST, NODE, NEXT, PREV) \ + for(NODE=((LIST)==NULL ? NULL : (LIST)->PREV); \ + NODE!=NULL; \ + NODE=((NODE)==(LIST) ? NULL : (NODE)->PREV)) + +#define LIST_FOR_ALL_W_NEXT(LIST, NODE, NXT, NEXT, PREV) \ + for(NODE=LL, NXT=(NODE==NULL ? NULL : (NODE)->NEXT); \ + NODE!=NULL; \ + NODE=NXT, NXT=(NODE==NULL ? NULL : (NODE)->NEXT)) + +#define LIST_FOR_ALL_W_NEXT_REV(LIST, NODE, NXT, NEXT, PREV) \ + for(NODE=((LIST)==NULL ? NULL : (LIST)->PREV), \ + NXT=((NODE)==(LIST) ? NULL : (NODE)->PREV); \ + NODE!=NULL; \ + NODE=NXT, \ + NXT=((NODE)==(LIST) ? NULL : (NODE)->PREV)) + + +/*}}}*/ + + +#endif /* LIBTU_DLIST_H */ diff --git a/libtu/errorlog.c b/libtu/errorlog.c new file mode 100644 index 0000000..da97713 --- /dev/null +++ b/libtu/errorlog.c @@ -0,0 +1,127 @@ +/* + * libtu/errorlog.c + * + * Copyright (c) Tuomo Valkonen 1999-2004. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#include +#include +#include + +#include "util.h" +#include "types.h" +#include "output.h" +#include "misc.h" +#include "errorlog.h" + +static ErrorLog *current_log=NULL; + +static void add_to_log(ErrorLog *el, const char *message, int l) +{ + if(message==NULL) + return; + + /* Also write to stderr */ + fwrite(message, sizeof(char), l, stderr); + + if(el==NULL) + return; + + if(el->file!=NULL){ + el->errors=TRUE; + fwrite(message, sizeof(char), l, el->file); + return; + } + + if(el->msgs==NULL){ + el->msgs=ALLOC_N(char, ERRORLOG_MAX_SIZE); + if(el->msgs==NULL){ + fprintf(stderr, "%s: %s\n", prog_execname(), strerror(errno)); + return; + } + el->msgs[0]=0; + el->msgs_len=0; + } + + el->errors=TRUE; + + if(l+el->msgs_len>ERRORLOG_MAX_SIZE-1){ + int n=0; + if(lmsgs, el->msgs+el->msgs_len-n, n); + } + memcpy(el->msgs+n, message+l-(ERRORLOG_MAX_SIZE-1-n), + ERRORLOG_MAX_SIZE-1-n); + el->msgs[ERRORLOG_MAX_SIZE]='\0'; + el->msgs_len=ERRORLOG_MAX_SIZE-1; + }else{ + memcpy(el->msgs+el->msgs_len, message, l); + el->msgs[el->msgs_len+l]='\0'; + el->msgs_len+=l; + } +} + + +static void log_warn_handler(const char *message) +{ + const char *p=strchr(message, '\n'); + static int lineno=0; + int alternat=0; + + add_to_log(current_log, lineno==0 ? ">> " : " ", 3); + + if(p!=NULL){ + add_to_log(current_log, message, p-message+1); + lineno++; + log_warn_handler(p+1); + lineno--; + return; + } + + add_to_log(current_log, message, strlen(message)); + add_to_log(current_log, "\n", 1); +} + + +void errorlog_begin_file(ErrorLog *el, FILE *file) +{ + el->msgs=NULL; + el->msgs_len=0; + el->file=file; + el->prev=current_log; + el->errors=FALSE; + el->old_handler=set_warn_handler(log_warn_handler); + current_log=el; +} + + +void errorlog_begin(ErrorLog *el) +{ + errorlog_begin_file(el, NULL); +} + + +bool errorlog_end(ErrorLog *el) +{ + current_log=el->prev; + set_warn_handler(el->old_handler); + el->prev=NULL; + el->old_handler=NULL; + return el->errors; +} + + +void errorlog_deinit(ErrorLog *el) +{ + if(el->msgs!=NULL) + free(el->msgs); + el->msgs=NULL; + el->msgs_len=0; + el->file=NULL; + el->errors=FALSE; +} + diff --git a/libtu/errorlog.h b/libtu/errorlog.h new file mode 100644 index 0000000..93da8ba --- /dev/null +++ b/libtu/errorlog.h @@ -0,0 +1,38 @@ +/* + * libtu/errorlog.h + * + * Copyright (c) Tuomo Valkonen 1999-2004. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#ifndef LIBTU_ERRORLOG_H +#define LIBTU_ERRORLOG_H + +#include + +#include "types.h" +#include "obj.h" +#include "output.h" + +#define ERRORLOG_MAX_SIZE (1024*4) + +INTRSTRUCT(ErrorLog); +DECLSTRUCT(ErrorLog){ + char *msgs; + int msgs_len; + FILE *file; + bool errors; + ErrorLog *prev; + WarnHandler *old_handler; +}; + +/* el is assumed to be uninitialised */ +extern void errorlog_begin(ErrorLog *el); +extern void errorlog_begin_file(ErrorLog *el, FILE *file); +/* For errorlog_end el Must be the one errorlog_begin was last called with */ +extern bool errorlog_end(ErrorLog *el); +extern void errorlog_deinit(ErrorLog *el); + +#endif /* LIBTU_ERRORLOG_H */ diff --git a/libtu/exact-version b/libtu/exact-version new file mode 100644 index 0000000..20aff08 --- /dev/null +++ b/libtu/exact-version @@ -0,0 +1,56 @@ + +Context: + +[Path fix +Tuomo Valkonen **20061016223205] + +[Updated locations of *.mk. +Tuomo Valkonen **20060803210914] + +[*list_remove return true if the item was found (and removed). +Tuomo Valkonen **20060107210154] + +[Added some coercions to remove gcc complaints in snprintf_2.2. +Tuomo Valkonen **20050514202320] + +[Added plain dlist reverse forall. +Tuomo Valkonen **20050325164819] + +[Added libtu_setparam_invert. +Tuomo Valkonen **20050319210755] + +[Added setparam.c. +Tuomo Valkonen **20050319202730] + +[Added XOR macro. +Tuomo Valkonen **20050319195625] + +[Use install-sh. +Tuomo Valkonen **20050301215757] + +[Added routinesn for generic iterables. +Tuomo Valkonen **20050226230912] + +[Increased FOR_ALL macro reuse. +Tuomo Valkonen **20050226221051] + +[Added routines to take first/last elements of objlist and ptrlist. +Tuomo Valkonen **20050226210933] + +[ObjList changes. +Tuomo Valkonen **20050226205819] + +[Renamed Symlist PtrList. +Tuomo Valkonen **20050226203855] + +[Added struct field address macros. +Tuomo Valkonen **20050226093720] + +[Symlist improvements. +Tuomo Valkonen **20050224081221] + +[Added dlist iteration macros. +Tuomo Valkonen **20050223180501] + +[TAG libtu-3-svn2darcs +Tuomo Valkonen **20050215180637] diff --git a/libtu/install-sh b/libtu/install-sh new file mode 100644 index 0000000..e9de238 --- /dev/null +++ b/libtu/install-sh @@ -0,0 +1,251 @@ +#!/bin/sh +# +# install - install a program, script, or datafile +# This comes from X11R5 (mit/util/scripts/install.sh). +# +# Copyright 1991 by the Massachusetts Institute of Technology +# +# Permission to use, copy, modify, distribute, and sell this software and its +# documentation for any purpose is hereby granted without fee, provided that +# the above copyright notice appear in all copies and that both that +# copyright notice and this permission notice appear in supporting +# documentation, and that the name of M.I.T. not be used in advertising or +# publicity pertaining to distribution of the software without specific, +# written prior permission. M.I.T. makes no representations about the +# suitability of this software for any purpose. It is provided "as is" +# without express or implied warranty. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. It can only install one file at a time, a restriction +# shared with many OS's install programs. + + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" +mkdirprog="${MKDIRPROG-mkdir}" + +transformbasename="" +transform_arg="" +instcmd="$mvprog" +chmodcmd="$chmodprog 0755" +chowncmd="" +chgrpcmd="" +stripcmd="" +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src="" +dst="" +dir_arg="" + +while [ x"$1" != x ]; do + case $1 in + -c) instcmd="$cpprog" + shift + continue;; + + -d) dir_arg=true + shift + continue;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + -s) stripcmd="$stripprog" + shift + continue;; + + -t=*) transformarg=`echo $1 | sed 's/-t=//'` + shift + continue;; + + -b=*) transformbasename=`echo $1 | sed 's/-b=//'` + shift + continue;; + + *) if [ x"$src" = x ] + then + src=$1 + else + # this colon is to work around a 386BSD /bin/sh bug + : + dst=$1 + fi + shift + continue;; + esac +done + +if [ x"$src" = x ] +then + echo "install: no input file specified" + exit 1 +else + true +fi + +if [ x"$dir_arg" != x ]; then + dst=$src + src="" + + if [ -d $dst ]; then + instcmd=: + chmodcmd="" + else + instcmd=mkdir + fi +else + +# Waiting for this to be detected by the "$instcmd $src $dsttmp" command +# might cause directories to be created, which would be especially bad +# if $src (and thus $dsttmp) contains '*'. + + if [ -f $src -o -d $src ] + then + true + else + echo "install: $src does not exist" + exit 1 + fi + + if [ x"$dst" = x ] + then + echo "install: no destination specified" + exit 1 + else + true + fi + +# If destination is a directory, append the input filename; if your system +# does not like double slashes in filenames, you may need to add some logic + + if [ -d $dst ] + then + dst="$dst"/`basename $src` + else + true + fi +fi + +## this sed command emulates the dirname command +dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` + +# Make sure that the destination directory exists. +# this part is taken from Noah Friedman's mkinstalldirs script + +# Skip lots of stat calls in the usual case. +if [ ! -d "$dstdir" ]; then +defaultIFS=' +' +IFS="${IFS-${defaultIFS}}" + +oIFS="${IFS}" +# Some sh's can't handle IFS=/ for some reason. +IFS='%' +set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` +IFS="${oIFS}" + +pathcomp='' + +while [ $# -ne 0 ] ; do + pathcomp="${pathcomp}${1}" + shift + + if [ ! -d "${pathcomp}" ] ; + then + $mkdirprog "${pathcomp}" + else + true + fi + + pathcomp="${pathcomp}/" +done +fi + +if [ x"$dir_arg" != x ] +then + $doit $instcmd $dst && + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi +else + +# If we're going to rename the final executable, determine the name now. + + if [ x"$transformarg" = x ] + then + dstfile=`basename $dst` + else + dstfile=`basename $dst $transformbasename | + sed $transformarg`$transformbasename + fi + +# don't allow the sed command to completely eliminate the filename + + if [ x"$dstfile" = x ] + then + dstfile=`basename $dst` + else + true + fi + +# Make a temp file name in the proper directory. + + dsttmp=$dstdir/#inst.$$# + +# Move or copy the file name to the temp name + + $doit $instcmd $src $dsttmp && + + trap "rm -f ${dsttmp}" 0 && + +# and set any options; do chmod last to preserve setuid bits + +# If any of these fail, we abort the whole thing. If we want to +# ignore errors from any of these, just make sure not to ignore +# errors from the above "$doit $instcmd $src $dsttmp" command. + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && + +# Now rename the file to the real destination. + + $doit $rmcmd -f $dstdir/$dstfile && + $doit $mvcmd $dsttmp $dstdir/$dstfile + +fi && + + +exit 0 diff --git a/libtu/iterable.c b/libtu/iterable.c new file mode 100644 index 0000000..63b00c1 --- /dev/null +++ b/libtu/iterable.c @@ -0,0 +1,51 @@ +/* + * libtu/iterable.c + * + * Copyright (c) Tuomo Valkonen 2005. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#include "iterable.h" + + +void *iterable_nth(uint n, VoidIterator *iter, void *st) +{ + void *p; + + while(1){ + p=iter(st); + if(p==NULL || n==0) + break; + n--; + } + + return p; +} + + +bool iterable_is_on(void *p, VoidIterator *iter, void *st) +{ + while(1){ + void *p2=iter(st); + if(p2==NULL) + return FALSE; + if(p==p2) + return TRUE; + } +} + + +void *iterable_find(BoolFilter *f, void *fparam, + VoidIterator *iter, void *st) +{ + while(1){ + void *p=iter(st); + if(p==NULL) + return NULL; + if(f(p, fparam)) + return p; + } +} + diff --git a/libtu/iterable.h b/libtu/iterable.h new file mode 100644 index 0000000..8b70b09 --- /dev/null +++ b/libtu/iterable.h @@ -0,0 +1,29 @@ +/* + * libtu/iterable.h + * + * Copyright (c) Tuomo Valkonen 2005. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#ifndef LIBTU_ITERABLE_H +#define LIBTU_ITERABLE_H + +#include "types.h" +#include "obj.h" + +typedef void *VoidIterator(void *); +typedef Obj *ObjIterator(void *); + +typedef bool BoolFilter(void *p, void *param); + +#define FOR_ALL_ITER(INIT, ITER, VAR, LL, TMP) \ + for(INIT(TMP, LL), (VAR)=ITER(TMP); (VAR)!=NULL; VAR=ITER(TMP)) + +extern void *iterable_nth(uint n, VoidIterator *iter, void *st); +extern bool iterable_is_on(void *p, VoidIterator *iter, void *st); +extern void *iterable_find(BoolFilter *f, void *fparam, + VoidIterator *iter, void *st); + +#endif /* LIBTU_ITERABLE_H */ diff --git a/libtu/locale.h b/libtu/locale.h new file mode 100644 index 0000000..b50f528 --- /dev/null +++ b/libtu/locale.h @@ -0,0 +1,27 @@ +/* + * libtu/locale.h + * + * Copyright (c) Tuomo Valkonen 2004. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#ifndef LIBTU_LOCALE_H +#define LIBTU_LOCALE_H + +#ifdef CF_NO_LOCALE + +#define TR(X) X +#define DUMMY_TR(X) X + +#else + +#include + +#define TR(X) gettext(X) +#define DUMMY_TR(X) X + +#endif + +#endif /* LIBTU_LOCALE_H */ diff --git a/libtu/map.c b/libtu/map.c new file mode 100644 index 0000000..7e5f202 --- /dev/null +++ b/libtu/map.c @@ -0,0 +1,47 @@ +/* + * libtu/map.c + * + * Copyright (c) Tuomo Valkonen 1999-2002. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#include +#include "map.h" + + +int stringintmap_ndx(const StringIntMap *map, const char *str) +{ + int i; + + for(i=0; map[i].string!=NULL; i++){ + if(strcmp(str, map[i].string)==0) + return i; + } + + return -1; +} + + +int stringintmap_value(const StringIntMap *map, const char *str, int dflt) +{ + int i=stringintmap_ndx(map, str); + return (i==-1 ? dflt : map[i].value); +} + + +const char *stringintmap_key(const StringIntMap *map, int value, + const char *dflt) +{ + int i; + + for(i=0; map[i].string!=NULL; ++i){ + if(map[i].value==value){ + return map[i].string; + } + } + + return dflt; + +} diff --git a/libtu/map.h b/libtu/map.h new file mode 100644 index 0000000..d66ae74 --- /dev/null +++ b/libtu/map.h @@ -0,0 +1,27 @@ +/* + * libtu/map.h + * + * Copyright (c) Tuomo Valkonen 1999-2002. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#ifndef LIBTU_MAP_H +#define LIBTU_MAP_H + +typedef struct _StringIntMap{ + const char *string; + int value; +} StringIntMap; + +#define END_STRINGINTMAP {NULL, 0} + +/* Return the index of str in map or -1 if not found. */ +extern int stringintmap_ndx(const StringIntMap *map, const char *str); +extern int stringintmap_value(const StringIntMap *map, const char *str, + int dflt); +extern const char *stringintmap_key(const StringIntMap *map, + int value, const char *dflt); + +#endif /* LIBTU_MAP_H */ diff --git a/libtu/minmax.h b/libtu/minmax.h new file mode 100644 index 0000000..5866d7f --- /dev/null +++ b/libtu/minmax.h @@ -0,0 +1,27 @@ +/* + * libtu/minmax.h + * + * Copyright (c) Tuomo Valkonen 1999-2004. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#ifndef LIBTU_MINMAX_H +#define LIBTU_MINMAX_H + + +static int minof(int x, int y) +{ + return (xy ? x : y); +} + + +#endif /* LIBTU_MINMAX_H */ + diff --git a/libtu/misc.c b/libtu/misc.c new file mode 100644 index 0000000..39137c3 --- /dev/null +++ b/libtu/misc.c @@ -0,0 +1,211 @@ +/* + * libtu/misc.c + * + * Copyright (c) Tuomo Valkonen 1999-2002. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#include +#include +#include +#include + +#include "misc.h" +#include "output.h" + + +void *malloczero(size_t size) +{ + void *p=malloc(size); + + if(p!=NULL) + memset(p, 0, size); + else + warn_err(); + + return p; +} + + +void *remalloczero(void *ptr, size_t oldsize, size_t newsize) +{ + void *p=NULL; + + if(newsize!=0){ + p=realloc(ptr, newsize); + + if(p==NULL){ + warn_err(); + return NULL; + } + + if(newsize>oldsize) + memset((char*)p+oldsize, 0, newsize-oldsize); + } + + return p; +} + + +char *scopyn(const char *p, size_t l) +{ + char *pn=ALLOC_N(char, l+1); + + if(pn==NULL) + return NULL; + + memcpy(pn, p, l); + pn[l]='\0'; + + return pn; +} + + +char *scopy(const char *p) +{ + size_t l=strlen(p); + return scopyn(p, l); +} + + +char *scat(const char *p1, const char *p2) +{ + size_t l1, l2; + char*pn; + + l1=strlen(p1); + l2=strlen(p2); + + pn=ALLOC_N(char, l1+l2+1); + + if(pn==NULL) + return NULL; + + memcpy(pn, p1, l1); + memcpy(pn+l1, p2, l2+1); + + return pn; +} + + +char *scat3(const char *p1, const char *p2, const char *p3) +{ + size_t l1, l2, l3; + char *pn; + + l1=strlen(p1); + l2=strlen(p2); + l3=strlen(p3); + + pn=ALLOC_N(char, l1+l2+l3+1); + + if(pn==NULL) + return NULL; + + memcpy(pn, p1, l1); + memcpy(pn+l1, p2, l2); + memcpy(pn+l1+l2, p3, l3+1); + + return pn; +} + + +char *scatn(const char *s1, ssize_t l1, const char *s2, ssize_t l2) +{ + size_t tlen=1; + char *s; + + if(l1<0) + l1=strlen(s1); + + if(l2<0) + l2=strlen(s2); + + tlen+=l1+l2; + + s=ALLOC_N(char, tlen); + + if(s==NULL) + return NULL; + + memcpy(s, s1, l1); + memcpy(s+l1, s2, l2); + s[l1+l2]='\0'; + + return s; +} + + +/* */ + + +const char *simple_basename(const char *name) +{ + const char *p; + + p=name+strlen(name)-1; + + /* Skip any trailing slashes */ + while(*p=='/'){ + /* root? */ + if(p==name) + return name; + p--; + } + + while(p!=name){ + if(*p=='/') + return p+1; + p--; + } + + return name; +} + + +void stripws(char *p) +{ + int l; + + l=strspn(p, " "); + if(l!=0) + strcpy(p, p+l); + l=strlen(p); + + while(--l>=0){ + if(*(p+l)!=' ') + break; + } + *(p+l+1)='\0'; +} + + +const char *libtu_strcasestr(const char *str, const char *ptn) +{ + const char *s2, *p2; + for(; *str; str++) { + for(s2=str, p2=ptn; ; s2++, p2++) { + if(!*p2) + return str; + if(toupper(*s2)!=toupper(*p2)) + break; + } + } + return NULL; +} + +/* */ + + +bool readf(FILE *f, void *buf, size_t n) +{ + return fread(buf, 1, n, f)!=1; +} + + +bool writef(FILE *f, const void *buf, size_t n) +{ + return fwrite(buf, 1, n, f)!=1; +} diff --git a/libtu/misc.h b/libtu/misc.h new file mode 100644 index 0000000..a8e6d3f --- /dev/null +++ b/libtu/misc.h @@ -0,0 +1,43 @@ +/* + * libtu/misc.h + * + * Copyright (c) Tuomo Valkonen 1999-2005. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#ifndef LIBTU_MISC_H +#define LIBTU_MISC_H + +#include +#include +#include +#include "types.h" + +#define ALLOC(X) (X*)malloczero(sizeof(X)) +#define ALLOC_N(X, N) (X*)malloczero(sizeof(X)*(N)) +#define REALLOC_N(PTR, X, S, N) (X*)remalloczero(PTR, sizeof(X)*(S), sizeof(X)*(N)) + +#define FREE(X) do{if(X!=NULL)free(X);}while(0) + +#define XOR(X, Y) (((X)==0) != ((Y)==0)) + +extern void* malloczero(size_t size); +extern void* remalloczero(void *ptr, size_t oldsize, size_t newsize); + +extern char* scopy(const char *p); +extern char* scopyn(const char *p, size_t n); +extern char* scat(const char *p1, const char *p2); +extern char* scatn(const char *p1, ssize_t n1, const char *p2, ssize_t n2); +extern char* scat3(const char *p1, const char *p2, const char *p3); +extern void stripws(char *p); +extern const char *libtu_strcasestr(const char *str, const char *ptn); + +extern const char* simple_basename(const char *name); + +/* I dislike fread and fwrite... */ +extern bool readf(FILE *fd, void *buf, size_t n); +extern bool writef(FILE *fd, const void *buf, size_t n); + +#endif /* LIBTU_MISC_H */ diff --git a/libtu/np-conv.h b/libtu/np-conv.h new file mode 100644 index 0000000..c6c13c7 --- /dev/null +++ b/libtu/np-conv.h @@ -0,0 +1,121 @@ +/* + * libtu/np-conv.h + * + * Copyright (c) Tuomo Valkonen 1999-2002. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#include + +#ifdef NP_SIMPLE_IMPL + +#define FN_NUM_TO_SIGNED(T, UMAX, MAX, MIN) \ + static int num_to_##T(T *ret, const NPNum *num, bool allow_uns_big) \ + { \ + if(num->type!=NPNUM_INT) \ + return E_TOKZ_NOTINT; \ + \ + if(!num->negative){ \ + *ret=num->ival; \ + if(allow_uns_big?num->ival>UMAX:num->ival>MAX) \ + return E_TOKZ_RANGE; \ + }else{ \ + *ret=-num->ival; \ + if(num->ival>-(ulong)MIN) \ + return E_TOKZ_RANGE; \ + } \ + return 0; \ + } + +#define FN_NUM_TO_UNSIGNED(T, UMAX, MIN) \ + static int num_to_##T(T *ret, const NPNum *num, bool allow_neg) \ + { \ + if(num->type!=NPNUM_INT) \ + return E_TOKZ_NOTINT; \ + \ + if(!num->negative){ \ + *ret=num->ival; \ + if(num->ival>UMAX) \ + return E_TOKZ_RANGE; \ + }else{ \ + *ret=-num->ival; \ + if(!allow_neg || num->ival>(ulong)-MIN) \ + return E_TOKZ_RANGE; \ + } \ + return 0; \ + } + +#define FN_NUM_TO_FLOAT(T, POW) \ + static int num_to_##T(T *ret, const NPNum *num) \ + { \ + *ret=(num->negative?-num->fval:num->fval); \ + return 0; \ + } + +#else /* NP_SIMPLE_IMPL */ + +#define FN_NUM_TO_SIGNED(T, UMAX, MAX, MIN) \ + static int num_to_##T(T *ret, const NPNum *num, bool allow_uns_big) \ + { \ + if(num->exponent) \ + return E_TOKZ_NOTINT; \ + if(num->nmantissa>0) \ + return E_TOKZ_RANGE; \ + \ + if(!num->negative){ \ + *ret=num->mantissa[0]; \ + if(allow_uns_big?num->mantissa[0]>UMAX:num->mantissa[0]>MAX) \ + return E_TOKZ_RANGE; \ + }else{ \ + *ret=-num->mantissa[0]; \ + if(num->mantissa[0]>-(ulong)MIN) \ + return E_TOKZ_RANGE; \ + } \ + return 0; \ +} + +#define FN_NUM_TO_UNSIGNED(T, UMAX, MIN) \ + static int num_to_##T(T *ret, const NPNum *num, bool allow_neg) \ + { \ + if(num->exponent) \ + return E_TOKZ_NOTINT; \ + if(num->nmantissa>0) \ + return E_TOKZ_RANGE; \ + \ + if(!num->negative){ \ + *ret=num->mantissa[0]; \ + if(num->mantissa[0]>UMAX) \ + return E_TOKZ_RANGE; \ + }else{ \ + *ret=-num->mantissa[0]; \ + if(!allow_neg || num->mantissa[0]>(ulong)-MIN) \ + return E_TOKZ_RANGE; \ + } \ + return 0; \ +} + + +#define FN_NUM_TO_FLOAT(T, POW) \ + static int num_to_##T(T *ret, const NPNum *num) \ + { \ + T d=0; \ + int i; \ + \ + for(i=num->nmantissa;i>=0;i--) \ + d=d*(T)(ULONG_MAX+1.0)+num->mantissa[i]; \ + \ + d*=POW(num->base, num->exponent); \ + *ret=d; \ + \ + return 0; \ + } + +#endif /* NP_SIMPLE_IMPL */ + +FN_NUM_TO_SIGNED(long, ULONG_MAX, LONG_MAX, LONG_MIN) +FN_NUM_TO_SIGNED(char, UCHAR_MAX, CHAR_MAX, CHAR_MIN) +FN_NUM_TO_FLOAT(double, pow) + +#undef NEG diff --git a/libtu/np/np-conv.h b/libtu/np/np-conv.h new file mode 100644 index 0000000..7a1e537 --- /dev/null +++ b/libtu/np/np-conv.h @@ -0,0 +1,121 @@ +/* + * libtu/np-conv.h + * + * Copyright (c) Tuomo Valkonen 1999-2002. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#include + +#ifdef NP_SIMPLE_IMPL + +#define FN_NUM_TO_SIGNED(T, UMAX, MAX, MIN) \ + static int num_to_##T(T *ret, const NPNum *num, bool allow_uns_big) \ + { \ + if(num->type!=NPNUM_INT) \ + return E_TOKZ_NOTINT; \ + \ + if(!num->negative){ \ + *ret=num->ival; \ + if(allow_uns_big?num->ival>UMAX:num->ival>MAX) \ + return E_TOKZ_RANGE; \ + }else{ \ + *ret=-num->ival; \ + if(num->ival>-(ulong)MIN) \ + return E_TOKZ_RANGE; \ + } \ + return 0; \ + } + +#define FN_NUM_TO_UNSIGNED(T, UMAX, MIN) \ + static int num_to_##T(T *ret, const NPNum *num, bool allow_neg) \ + { \ + if(num->type!=NPNUM_INT) \ + return E_TOKZ_NOTINT; \ + \ + if(!num->negative){ \ + *ret=num->ival; \ + if(num->ival>UMAX) \ + return E_TOKZ_RANGE; \ + }else{ \ + *ret=-num->ival; \ + if(!allow_neg || num->ival>(ulong)-MIN) \ + return E_TOKZ_RANGE; \ + } \ + return 0; \ + } + +#define FN_NUM_TO_FLOAT(T, POW) \ + static int num_to_##T(T *ret, const NPNum *num) \ + { \ + *ret=(num->negative?-num->fval:num->fval); \ + return 0; \ + } + +#else /* NP_SIMPLE_IMPL */ + +#define FN_NUM_TO_SIGNED(T, UMAX, MAX, MIN) \ + static int num_to_##T(T *ret, const NPNum *num, bool allow_uns_big) \ + { \ + if(num->exponent) \ + return E_TOKZ_NOTINT; \ + if(num->nmantissa>0) \ + return E_TOKZ_RANGE; \ + \ + if(!num->negative){ \ + *ret=num->mantissa[0]; \ + if(allow_uns_big?num->mantissa[0]>UMAX:num->mantissa[0]>MAX) \ + return E_TOKZ_RANGE; \ + }else{ \ + *ret=-num->mantissa[0]; \ + if(num->mantissa[0]>-(ulong)MIN) \ + return E_TOKZ_RANGE; \ + } \ + return 0; \ +} + +#define FN_NUM_TO_UNSIGNED(T, UMAX, MIN) \ + static int num_to_##T(T *ret, const NPNum *num, bool allow_neg) \ + { \ + if(num->exponent) \ + return E_TOKZ_NOTINT; \ + if(num->nmantissa>0) \ + return E_TOKZ_RANGE; \ + \ + if(!num->negative){ \ + *ret=num->mantissa[0]; \ + if(num->mantissa[0]>UMAX) \ + return E_TOKZ_RANGE; \ + }else{ \ + *ret=-num->mantissa[0]; \ + if(!allow_neg || num->mantissa[0]>(ulong)-MIN) \ + return E_TOKZ_RANGE; \ + } \ + return 0; \ +} + + +#define FN_NUM_TO_FLOAT(T, POW) \ + static int num_to_##T(T *ret, const NPNum *num) \ + { \ + T d=0; \ + int i; \ + \ + for(i=num->nmantissa;i>=0;i--) \ + d=d*(T)(ULONG_MAX+1.0)+num->mantissa[i]; \ + \ + d*=POW(num->base, num->exponent); \ + *ret=d; \ + \ + return 0; \ + } + +#endif /* NP_SIMPLE_IMPL */ + +FN_NUM_TO_SIGNED(long, ULONG_MAX, LONG_MAX, LONG_MIN) +FN_NUM_TO_SIGNED(char, UCHAR_MAX, CHAR_MAX, CHAR_MIN) +FN_NUM_TO_FLOAT(double, pow) + +#undef NEG diff --git a/libtu/np/numparser2.h b/libtu/np/numparser2.h new file mode 100644 index 0000000..213c4e0 --- /dev/null +++ b/libtu/np/numparser2.h @@ -0,0 +1,272 @@ +/* + * libtu/numparser2.h + * + * Copyright (c) Tuomo Valkonen 1999-2002. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#define MAX_MANTISSA 10 /* should be enough for our needs */ +#define ULONG_SIZE (sizeof(ulong)*8) + +enum{ + NPNUM_INT, + NPNUM_FLOAT +}; + +#ifdef NP_SIMPLE_IMPL + +typedef struct _NPNum{ + int type; + int base; + bool negative; + double fval; + ulong ival; +} NPNum; + +#define NUM_INIT {0, 0, 0, 0.0, 0} + +static int npnum_mulbase_add(NPNum *num, long base, long v) +{ + double iold=num->ival; + + num->fval=num->fval*base+(double)v; + + num->ival*=base; + + if(num->ivaltype=NPNUM_FLOAT; + + num->ival+=v; + + return 0; +} + +#else /* NP_SIMPLE_IMPL */ + +typedef struct _NPNum{ + unsigned char nmantissa; + int type; + int base; + bool negative; + ulong mantissa[MAX_MANTISSA]; + long exponent; +} NPNum; + +#define NUM_INIT {0, 0, 0, 0, {0,}, 0} + +#define ADD_EXP(NUM, X) (NUM)->exponent+=(X); + +#if defined(__GNUG__) && defined(i386) && !defined(NP_NO_I386_ASM) + #define NP_I386_ASM +#endif + +static int npnum_mulbase_add(NPNum *num, long base, long v) +{ + long i, j; + ulong overflow; +#ifndef NP_I386_ASM + ulong val; +#endif + + for(i=num->nmantissa;i>=0;i--){ +#ifdef NP_I386_ASM + __asm__("mul %4\n" + : "=a" (num->mantissa[i]), "=d" (overflow) + : "0" (num->mantissa[i]), "1" (0), "q" (base) + : "eax", "edx"); +#else + overflow=0; + val=num->mantissa[i]; + + if(valmantissa[i]=val; +#endif + if(overflow){ + if(i==num->nmantissa){ + if(num->nmantissa==MAX_MANTISSA) + return E_TOKZ_TOOBIG; + num->nmantissa++; + } + num->mantissa[i+1]+=overflow; + } + } + num->mantissa[0]+=v; + + return 0; +} + +#undef NP_I386_ASM + +#endif /* NP_SIMPLE_IMPL */ + + +/* */ + + +static bool is_end(int c) +{ + /* oops... EOF was missing */ + return (c==EOF || (c!='.' && ispunct(c)) || isspace(c) || iscntrl(c)); +} + + +/* */ + + +static int parse_exponent(NPNum *num, Tokenizer *tokz, int c) +{ + long exp=0; + bool neg=FALSE; + int err=0; + + c=GETCH(); + + if(c=='-' || c=='+'){ + if(c=='-') + neg=TRUE; + c=GETCH(); + } + + for(; 1; c=GETCH()){ + if(isdigit(c)){ + exp*=10; + exp+=c-'0'; + }else if(is_end(c)){ + UNGETCH(c); + break; + }else{ + err=E_TOKZ_NUMFMT; + } + } + + if(neg) + exp*=-1; + +#ifndef NP_SIMPLE_IMPL + ADD_EXP(num, exp); +#else + num->fval*=pow(num->base, exp); +#endif + return err; +} + + +static int parse_number(NPNum *num, Tokenizer *tokz, int c) +{ + int base=10; + int dm=1; + int err=0; + int tmp; +#ifdef NP_SIMPLE_IMPL + double divisor=base; +#endif + + if(c=='-' || c=='+'){ + if(c=='-') + num->negative=TRUE; + c=GETCH(); + if(!isdigit(c)) + err=E_TOKZ_NUMFMT; + } + + if(c=='0'){ + dm=0; + c=GETCH(); + if(c=='x' || c=='X'){ + base=16; + c=GETCH(); + }else if(c=='b' || c=='B'){ + base=2; + c=GETCH(); + }else if('0'<=c && c<='7'){ + base=8; + }else{ + dm=2; + } + } + + num->base=base; + + for(; 1; c=GETCH()){ + if((c=='e' || c=='E') && dm!=0){ + if(dm<2){ + err=E_TOKZ_NUMFMT; + continue; + } + tmp=parse_exponent(num, tokz, c); + if(err==0) + err=tmp; + break; + } + + if(isxdigit(c)){ + if('0'<=c && c<='9') + c-='0'; + else if(isupper(c)) + c-='A'-10; + else + c-='a'-10; + + if(c>=base) + err=E_TOKZ_NUMFMT; + +#ifdef NP_SIMPLE_IMPL + if(dm==3){ + num->fval+=(double)c/divisor; + divisor*=base; + }else +#endif + { + tmp=npnum_mulbase_add(num, base, c); + if(err==0) + err=tmp; + } + + if(dm==1) + dm=2; +#ifndef NP_SIMPLE_IMPL + else if(dm==3) + ADD_EXP(num, -1); +#endif + continue; + } + + if(c=='.'){ + if(dm!=2){ + err=E_TOKZ_NUMFMT; + } + dm=3; +#ifdef NP_SIMPLE_IMPL + num->type=NPNUM_FLOAT; + divisor=base; +#endif + continue; + } + + if(is_end(c)){ + UNGETCH(c); + break; + } + + err=E_TOKZ_NUMFMT; + } + +#ifndef NP_SIMPLE_IMPL + num->type=(num->exponent==0 ? NPNUM_INT : NPNUM_FLOAT); +#endif + + return err; +} diff --git a/libtu/numparser2.h b/libtu/numparser2.h new file mode 100644 index 0000000..b43e2ec --- /dev/null +++ b/libtu/numparser2.h @@ -0,0 +1,272 @@ +/* + * libtu/numparser2.h + * + * Copyright (c) Tuomo Valkonen 1999-2002. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#define MAX_MANTISSA 10 /* should be enough for our needs */ +#define ULONG_SIZE (sizeof(ulong)*8) + +enum{ + NPNUM_INT, + NPNUM_FLOAT +}; + +#ifdef NP_SIMPLE_IMPL + +typedef struct _NPNum{ + int type; + int base; + bool negative; + double fval; + ulong ival; +} NPNum; + +#define NUM_INIT {0, 0, 0, 0.0, 0} + +static int npnum_mulbase_add(NPNum *num, long base, long v) +{ + double iold=num->ival; + + num->fval=num->fval*base+(double)v; + + num->ival*=base; + + if(num->ivaltype=NPNUM_FLOAT; + + num->ival+=v; + + return 0; +} + +#else /* NP_SIMPLE_IMPL */ + +typedef struct _NPNum{ + unsigned char nmantissa; + int type; + int base; + bool negative; + ulong mantissa[MAX_MANTISSA]; + long exponent; +} NPNum; + +#define NUM_INIT {0, 0, 0, 0, {0,}, 0} + +#define ADD_EXP(NUM, X) (NUM)->exponent+=(X); + +#if defined(__GNUG__) && defined(i386) && !defined(NP_NO_I386_ASM) + #define NP_I386_ASM +#endif + +static int npnum_mulbase_add(NPNum *num, long base, long v) +{ + long i, j; + ulong overflow; +#ifndef NP_I386_ASM + ulong val; +#endif + + for(i=num->nmantissa;i>=0;i--){ +#ifdef NP_I386_ASM + __asm__("mul %4\n" + : "=a" (num->mantissa[i]), "=d" (overflow) + : "0" (num->mantissa[i]), "1" (0), "q" (base) + : "eax", "edx"); +#else + overflow=0; + val=num->mantissa[i]; + + if(valmantissa[i]=val; +#endif + if(overflow){ + if(i==num->nmantissa){ + if(num->nmantissa==MAX_MANTISSA) + return E_TOKZ_TOOBIG; + num->nmantissa++; + } + num->mantissa[i+1]+=overflow; + } + } + num->mantissa[0]+=v; + + return 0; +} + +#undef NP_I386_ASM + +#endif /* NP_SIMPLE_IMPL */ + + +/* */ + + +static bool is_end(int c) +{ + /* oops... EOF was missing */ + return (c==EOF || (c!='.' && ispunct(c)) || isspace(c) || iscntrl(c)); +} + + +/* */ + + +static int parse_exponent(NPNum *num, Tokenizer *tokz, int c) +{ + long exp=0; + bool neg=FALSE; + int err=0; + + c=GETCH(); + + if(c=='-' || c=='+'){ + if(c=='-') + neg=TRUE; + c=GETCH(); + } + + for(; 1; c=GETCH()){ + if(isdigit(c)){ + exp*=10; + exp+=c-'0'; + }else if(is_end(c)){ + UNGETCH(c); + break; + }else{ + err=E_TOKZ_NUMFMT; + } + } + + if(neg) + exp*=-1; + +#ifndef NP_SIMPLE_IMPL + ADD_EXP(num, exp); +#else + num->fval*=pow(num->base, exp); +#endif + return err; +} + + +static int parse_number(NPNum *num, Tokenizer *tokz, int c) +{ + int base=10; + int dm=1; + int err=0; + int tmp; +#ifdef NP_SIMPLE_IMPL + double divisor=base; +#endif + + if(c=='-' || c=='+'){ + if(c=='-') + num->negative=TRUE; + c=GETCH(); + if(!isdigit(c)) + err=E_TOKZ_NUMFMT; + } + + if(c=='0'){ + dm=0; + c=GETCH(); + if(c=='x' || c=='X'){ + base=16; + c=GETCH(); + }else if(c=='b' || c=='B'){ + base=2; + c=GETCH(); + }else if('0'<=c && c<='7'){ + base=8; + }else{ + dm=2; + } + } + + num->base=base; + + for(; 1; c=GETCH()){ + if((c=='e' || c=='E') && dm!=0){ + if(dm<2){ + err=E_TOKZ_NUMFMT; + continue; + } + tmp=parse_exponent(num, tokz, c); + if(err==0) + err=tmp; + break; + } + + if(isxdigit(c)){ + if('0'<=c && c<='9') + c-='0'; + else if(isupper(c)) + c-='A'-10; + else + c-='a'-10; + + if(c>=base) + err=E_TOKZ_NUMFMT; + +#ifdef NP_SIMPLE_IMPL + if(dm==3){ + num->fval+=(double)c/divisor; + divisor*=base; + }else +#endif + { + tmp=npnum_mulbase_add(num, base, c); + if(err==0) + err=tmp; + } + + if(dm==1) + dm=2; +#ifndef NP_SIMPLE_IMPL + else if(dm==3) + ADD_EXP(num, -1); +#endif + continue; + } + + if(c=='.'){ + if(dm!=2){ + err=E_TOKZ_NUMFMT; + } + dm=3; +#ifdef NP_SIMPLE_IMPL + num->type=NPNUM_FLOAT; + divisor=base; +#endif + continue; + } + + if(is_end(c)){ + UNGETCH(c); + break; + } + + err=E_TOKZ_NUMFMT; + } + +#ifndef NP_SIMPLE_IMPL + num->type=(num->exponent==0 ? NPNUM_INT : NPNUM_FLOAT); +#endif + + return err; +} diff --git a/libtu/obj.c b/libtu/obj.c new file mode 100644 index 0000000..0f4910d --- /dev/null +++ b/libtu/obj.c @@ -0,0 +1,297 @@ +/* + * libtu/obj.c + * + * Copyright (c) Tuomo Valkonen 1999-2004. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#include + +#include "types.h" +#include "obj.h" +#include "objp.h" +#include "misc.h" +#include "dlist.h" + + +ClassDescr CLASSDESCR(Obj)={"Obj", NULL, 0, NULL, NULL}; + + +static void do_watches(Obj *obj, bool call); + + +/*{{{ Destroy */ + + +void destroy_obj(Obj *obj) +{ + ClassDescr *d; + + if(OBJ_IS_BEING_DESTROYED(obj)) + return; + + obj->flags|=OBJ_DEST; + + do_watches(obj, TRUE); + + d=obj->obj_type; + + while(d!=NULL){ + if(d->destroy_fn!=NULL){ + d->destroy_fn(obj); + break; + } + d=d->ancestor; + } + + do_watches(obj, FALSE); + + free(obj); +} + + +/*}}}*/ + + +/*{{{ is/cast */ + + +bool obj_is(const Obj *obj, const ClassDescr *descr) +{ + ClassDescr *d; + + if(obj==NULL) + return FALSE; + + d=obj->obj_type; + + while(d!=NULL){ + if(d==descr) + return TRUE; + d=d->ancestor; + } + return FALSE; +} + + +bool obj_is_str(const Obj *obj, const char *str) +{ + ClassDescr *d; + + if(obj==NULL || str==NULL) + return FALSE; + + d=obj->obj_type; + + while(d!=NULL){ + if(strcmp(d->name, str)==0) + return TRUE; + d=d->ancestor; + } + return FALSE; +} + + +const void *obj_cast(const Obj *obj, const ClassDescr *descr) +{ + ClassDescr *d; + + if(obj==NULL) + return NULL; + + d=obj->obj_type; + + while(d!=NULL){ + if(d==descr) + return (void*)obj; + d=d->ancestor; + } + return NULL; +} + + +/*}}}*/ + + +/*{{{ Dynamic functions */ + + +/* This function is called when no handler is found. + */ +static void dummy_dyn() +{ +} + + +static int comp_fun(const void *a, const void *b) +{ + void *af=(void*)((DynFunTab*)a)->func; + void *bf=(void*)((DynFunTab*)b)->func; + + return (afobj_type; + + for(; descr!=&Obj_classdescr; descr=descr->ancestor){ + if(descr->funtab==NULL) + continue; + + if(descr->funtab_n==-1){ + /* Need to sort the table. */ + descr->funtab_n=0; + df=descr->funtab; + while(df->func!=NULL){ + descr->funtab_n++; + df++; + } + + if(descr->funtab_n>0){ + qsort(descr->funtab, descr->funtab_n, sizeof(DynFunTab), + comp_fun); + } + } + + /* + if(descr->funtab_n==0) + continue; + + df=(DynFunTab*)bsearch(&dummy, descr->funtab, descr->funtab_n, + sizeof(DynFunTab), comp_fun); + if(df!=NULL){ + *funnotfound=FALSE; + return df->handler; + } + */ + + /* Using custom bsearch instead of the one in libc is probably + * faster as the overhead of calling a comparison function would + * be significant given that the comparisons are simple and + * funtab_n not that big. + */ + { + int min=0, max=descr->funtab_n-1; + int ndx; + df=descr->funtab; + while(max>=min){ + ndx=(max+min)/2; + if((void*)df[ndx].func==(void*)func){ + *funnotfound=FALSE; + return df[ndx].handler; + } + if((void*)df[ndx].func<(void*)func) + min=ndx+1; + else + max=ndx-1; + } + } + } + + *funnotfound=TRUE; + return dummy_dyn; +} + + +bool has_dynfun(const Obj *obj, DynFun *func) +{ + bool funnotfound; + lookup_dynfun(obj, func, &funnotfound); + return !funnotfound; +} + + +/*}}}*/ + + +/*{{{ Watches */ + + +bool watch_setup(Watch *watch, Obj *obj, WatchHandler *handler) +{ + if(OBJ_IS_BEING_DESTROYED(obj)) + return FALSE; + + watch_reset(watch); + + watch->handler=handler; + LINK_ITEM(obj->obj_watches, watch, next, prev); + watch->obj=obj; + + return TRUE; +} + +void do_watch_reset(Watch *watch, bool call) +{ + WatchHandler *handler=watch->handler; + Obj *obj=watch->obj; + + watch->handler=NULL; + + if(obj==NULL) + return; + + UNLINK_ITEM(obj->obj_watches, watch, next, prev); + watch->obj=NULL; + + if(call && handler!=NULL) + handler(watch, obj); +} + + +void watch_reset(Watch *watch) +{ + do_watch_reset(watch, FALSE); +} + + +bool watch_ok(Watch *watch) +{ + return watch->obj!=NULL; +} + + +static void do_watches(Obj *obj, bool call) +{ + Watch *watch, *next; + + watch=obj->obj_watches; + + while(watch!=NULL){ + next=watch->next; + do_watch_reset(watch, call); + watch=next; + } +} + + +void watch_call(Obj *obj) +{ + do_watches(obj, FALSE); +} + + +void watch_init(Watch *watch) +{ + watch->obj=NULL; + watch->next=NULL; + watch->prev=NULL; + watch->handler=NULL; +} + + +/*}}}*/ + diff --git a/libtu/obj.h b/libtu/obj.h new file mode 100644 index 0000000..68ee3df --- /dev/null +++ b/libtu/obj.h @@ -0,0 +1,69 @@ +/* + * libtu/obj.h + * + * Copyright (c) Tuomo Valkonen 1999-2004. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#ifndef LIBTU_OBJ_H +#define LIBTU_OBJ_H + +#include "types.h" + +#define CLASSDESCR(TYPE) TYPE##_classdescr + +#define OBJ_IS(OBJ, TYPE) obj_is((Obj*)OBJ, &CLASSDESCR(TYPE)) +#define OBJ_CAST(OBJ, TYPE) (TYPE*)obj_cast((Obj*)OBJ, &CLASSDESCR(TYPE)) + +#define INTRSTRUCT(STRU) \ + struct STRU##_struct; typedef struct STRU##_struct STRU +#define DECLSTRUCT(STRU) \ + struct STRU##_struct + +#define INTRCLASS(OBJ) INTRSTRUCT(OBJ); extern ClassDescr CLASSDESCR(OBJ) +#define DECLCLASS(OBJ) DECLSTRUCT(OBJ) + +INTRSTRUCT(ClassDescr); +INTRCLASS(Obj); +INTRSTRUCT(Watch); + +extern bool obj_is(const Obj *obj, const ClassDescr *descr); +extern bool obj_is_str(const Obj *obj, const char *str); +extern const void *obj_cast(const Obj *obj, const ClassDescr *descr); + +extern void destroy_obj(Obj *obj); + +DECLCLASS(Obj){ + ClassDescr *obj_type; + Watch *obj_watches; + int flags; +}; + +#define OBJ_DEST 0x0001 +#define OBJ_EXTL_CACHED 0x0002 +#define OBJ_EXTL_OWNED 0x0004 + +#define OBJ_IS_BEING_DESTROYED(OBJ) (((Obj*)(OBJ))->flags&OBJ_DEST) + +#define DYNFUN + +typedef void WatchHandler(Watch *watch, Obj *obj); + +#define WATCH_INIT {NULL, NULL, NULL, NULL} + +DECLSTRUCT(Watch){ + Obj *obj; + Watch *next, *prev; + WatchHandler *handler; +}; + +extern bool watch_setup(Watch *watch, Obj *obj, + WatchHandler *handler); +extern void watch_reset(Watch *watch); +extern bool watch_ok(Watch *watch); +extern void watch_init(Watch *watch); +extern void watch_call(Obj *obj); + +#endif /* LIBTU_OBJ_H */ diff --git a/libtu/objlist.c b/libtu/objlist.c new file mode 100644 index 0000000..643f3fa --- /dev/null +++ b/libtu/objlist.c @@ -0,0 +1,326 @@ +/* + * libtu/objlist.c + * + * Copyright (c) Tuomo Valkonen 1999-2005. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#include "obj.h" +#include "types.h" +#include "objlist.h" +#include "dlist.h" +#include "misc.h" + + +static ObjList *reuse_first(ObjList **objlist) +{ + ObjList *node=*objlist; + + if(node!=NULL && node->watch.obj==NULL){ + UNLINK_ITEM(*objlist, node, next, prev); + return node; + } + + return NULL; +} + + +static ObjList *reuse_last(ObjList **objlist) +{ + ObjList *node=*objlist; + + if(node==NULL) + return NULL; + + node=node->prev; + + if(node!=NULL && node->watch.obj==NULL){ + UNLINK_ITEM(*objlist, node, next, prev); + return node; + } + + return NULL; +} + + +static ObjList *reuse(ObjList **objlist) +{ + ObjList *first=reuse_first(objlist); + ObjList *last=reuse_first(objlist); + + if(first==NULL){ + return last; + }else{ + if(last!=NULL) + free(last); + return first; + } +} + + +static void optimise(ObjList **objlist) +{ + ObjList *first=reuse_first(objlist); + ObjList *last=reuse_first(objlist); + + if(first!=NULL) + free(first); + if(last!=NULL) + free(last); +} + + +void watch_handler(Watch *watch, Obj *obj) +{ + ObjList *node=(ObjList*)watch; + + assert(node->prev!=NULL); + + if(node->next==NULL){ + /* Last item - can't free */ + }else if(node->prev->next==NULL){ + /* First item - can't free cheaply */ + }else{ + ObjList *tmp=node->prev; + node->next->prev=node->prev; + tmp->next=node->next; + free(node); + } +} + + +static void free_node(ObjList **objlist, ObjList *node) +{ + watch_reset(&(node->watch)); + UNLINK_ITEM(*objlist, node, next, prev); + free(node); +} + + +static ObjList *mknode(void *obj) +{ + ObjList *node; + + if(obj==NULL) + return NULL; + + node=ALLOC(ObjList); + + if(node==NULL) + return FALSE; + + watch_init(&(node->watch)); + + if(!watch_setup(&(node->watch), obj, watch_handler)){ + free(node); + return NULL; + } + + return node; +} + + +static ObjList *objlist_find_node(ObjList *objlist, Obj *obj) +{ + ObjList *node=objlist; + + while(node!=NULL){ + if(node->watch.obj==obj) + break; + node=node->next; + } + + return node; +} + + +bool objlist_insert_last(ObjList **objlist, Obj *obj) +{ + ObjList *node=reuse(objlist); + + if(node==NULL) + node=mknode(obj); + + if(node==NULL) + return FALSE; + + LINK_ITEM_LAST(*objlist, node, next, prev); + + return TRUE; +} + + +bool objlist_insert_first(ObjList **objlist, Obj *obj) +{ + ObjList *node=reuse(objlist); + + if(node==NULL) + node=mknode(obj); + + if(node==NULL) + return FALSE; + + LINK_ITEM_FIRST(*objlist, node, next, prev); + + return TRUE; +} + + +bool objlist_reinsert_last(ObjList **objlist, Obj *obj) +{ + ObjList *node; + + optimise(objlist); + + node=objlist_find_node(*objlist, obj); + + if(node==NULL) + return FALSE; + + UNLINK_ITEM(*objlist, node, next, prev); + LINK_ITEM_LAST(*objlist, node, next, prev); + + return TRUE; +} + + +bool objlist_reinsert_first(ObjList **objlist, Obj *obj) +{ + ObjList *node; + + optimise(objlist); + + node=objlist_find_node(*objlist, obj); + + if(node==NULL) + return FALSE; + + UNLINK_ITEM(*objlist, node, next, prev); + LINK_ITEM_FIRST(*objlist, node, next, prev); + + return TRUE; +} + + +bool objlist_remove(ObjList **objlist, Obj *obj) +{ + ObjList *node=objlist_find_node(*objlist, obj); + + if(node!=NULL) + free_node(objlist, node); + + optimise(objlist); + + return (node!=NULL); +} + + +void objlist_clear(ObjList **objlist) +{ + while(*objlist!=NULL) + free_node(objlist, *objlist); +} + + +ObjListIterTmp objlist_iter_tmp=NULL; + + +void objlist_iter_init(ObjListIterTmp *state, ObjList *objlist) +{ + *state=objlist; +} + + +Obj *objlist_iter(ObjListIterTmp *state) +{ + Obj *obj=NULL; + + while(obj==NULL && *state!=NULL){ + obj=(*state)->watch.obj; + (*state)=(*state)->next; + } + + return obj; +} + + +void objlist_iter_rev_init(ObjListIterTmp *state, ObjList *objlist) +{ + *state=(objlist==NULL ? NULL : objlist->prev); +} + + +Obj *objlist_iter_rev(ObjListIterTmp *state) +{ + Obj *obj=NULL; + + while(obj==NULL && *state!=NULL){ + obj=(*state)->watch.obj; + *state=(*state)->prev; + if((*state)->next==NULL) + *state=NULL; + } + + return obj; +} + + +bool objlist_empty(ObjList *objlist) +{ + ObjListIterTmp tmp; + Obj *obj; + + FOR_ALL_ON_OBJLIST(Obj*, obj, objlist, tmp){ + return FALSE; + } + + return TRUE; +} + + +Obj *objlist_take_first(ObjList **objlist) +{ + ObjList *node; + Obj*obj; + + optimise(objlist); + + node=*objlist; + + if(node==NULL) + return NULL; + + obj=node->watch.obj; + + assert(obj!=NULL); + + free_node(objlist, node); + + return obj; +} + + +Obj *objlist_take_last(ObjList **objlist) +{ + ObjList *node; + Obj*obj; + + optimise(objlist); + + node=*objlist; + + if(node==NULL) + return NULL; + + node=node->prev; + + obj=node->watch.obj; + + assert(obj!=NULL); + + free_node(objlist, node); + + return obj; +} diff --git a/libtu/objlist.h b/libtu/objlist.h new file mode 100644 index 0000000..678706f --- /dev/null +++ b/libtu/objlist.h @@ -0,0 +1,60 @@ +/* + * libtu/objlist.h + * + * Copyright (c) Tuomo Valkonen 1999-2005. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#ifndef LIBTU_OBJLIST_H +#define LIBTU_OBJLIST_H + +#include "types.h" +#include "iterable.h" +#include "obj.h" + + +INTRSTRUCT(ObjList); + + +DECLSTRUCT(ObjList){ + Watch watch; /* Must be kept at head of structure */ + ObjList *next, *prev; + ObjList **list; +}; + + +typedef ObjList* ObjListIterTmp; + +#define OBJLIST_FIRST(TYPE, LL) ((LL)==NULL ? NULL : (TYPE)(LL)->watch.obj) +#define OBJLIST_LAST(TYPE, LL) ((LL)==NULL ? NULL : (TYPE)(LL)->prev->watch.obj) +#define OBJLIST_EMPTY(LIST) objlist_empty(LIST) + +#define FOR_ALL_ON_OBJLIST(TYPE, VAR, LL, TMP) \ + FOR_ALL_ITER(objlist_iter_init, (TYPE)objlist_iter, VAR, LL, &(TMP)) + +#define FOR_ALL_ON_OBJLIST_REV(TYPE, VAR, LL, TMP) \ + FOR_ALL_ITER(objlist_iter_rev_init, \ + (TYPE)objlist_iter_rev, VAR, LL, &(TMP)) + +#define FOR_ALL_ON_OBJLIST_UNSAFE(TYPE, VAR, LL) \ + FOR_ALL_ON_OBJLIST(TYPE, VAR, LL, objlist_iter_tmp) + +extern ObjListIterTmp objlist_iter_tmp; + +extern bool objlist_insert_last(ObjList **objlist, Obj *obj); +extern bool objlist_insert_first(ObjList **objlist, Obj *obj); +extern bool objlist_reinsert_last(ObjList **objlist, Obj *obj); +extern bool objlist_reinsert_first(ObjList **objlist, Obj *obj); +extern bool objlist_remove(ObjList **objlist, Obj *obj); +extern void objlist_clear(ObjList **objlist); +extern void objlist_iter_init(ObjListIterTmp *state, ObjList *objlist); +extern Obj *objlist_iter(ObjListIterTmp *state); +extern void objlist_iter_rev_init(ObjListIterTmp *state, ObjList *objlist); +extern Obj *objlist_iter_rev(ObjListIterTmp *state); +extern bool objlist_empty(ObjList *objlist); +extern Obj *objlist_take_first(ObjList **objlist); +extern Obj *objlist_take_last(ObjList **objlist); + +#endif /* LIBTU_OBJLIST_H */ diff --git a/libtu/objp.h b/libtu/objp.h new file mode 100644 index 0000000..54a7b19 --- /dev/null +++ b/libtu/objp.h @@ -0,0 +1,72 @@ +/* + * libtu/objp.h + * + * Copyright (c) Tuomo Valkonen 1999-2004. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#ifndef LIBTU_OBJP_H +#define LIBTU_OBJP_H + +#include "types.h" +#include "obj.h" + +typedef void DynFun(); + +INTRSTRUCT(DynFunTab); + +DECLSTRUCT(DynFunTab){ + DynFun *func, *handler; +}; + +DECLSTRUCT(ClassDescr){ + const char *name; + ClassDescr *ancestor; + int funtab_n; + DynFunTab *funtab; + void (*destroy_fn)(); +}; + +#define OBJ_TYPESTR(OBJ) ((OBJ) ? ((Obj*)OBJ)->obj_type->name : NULL) + +#define IMPLCLASS(CLS, ANCESTOR, DFN, DYN) \ + ClassDescr CLASSDESCR(CLS)={ \ + #CLS, &CLASSDESCR(ANCESTOR), -1, DYN, (void (*)())DFN} + +#define OBJ_INIT(O, TYPE) {((Obj*)(O))->obj_type=&CLASSDESCR(TYPE); \ + ((Obj*)(O))->obj_watches=NULL; ((Obj*)(O))->flags=0;} + +#define CREATEOBJ_IMPL(OBJ, LOWOBJ, INIT_ARGS) \ + OBJ *p; p=ALLOC(OBJ); if(p==NULL){ warn_err(); return NULL; } \ + OBJ_INIT(p, OBJ); \ + if(!LOWOBJ ## _init INIT_ARGS) { free((void*)p); return NULL; } return p + +#define SIMPLECREATEOBJ_IMPL(OBJ, LOWOBJ) \ + OBJ *p; p=ALLOC(OBJ); if(p==NULL){ warn_err(); return NULL; } \ + OBJ_INIT(p, OBJ); \ + return p; + +#define END_DYNFUNTAB {NULL, NULL} + +extern DynFun *lookup_dynfun(const Obj *obj, DynFun *func, + bool *funnotfound); +extern bool has_dynfun(const Obj *obj, DynFun *func); + +#define CALL_DYN(FUNC, OBJ, ARGS) \ + bool funnotfound; \ + lookup_dynfun((Obj*)OBJ, (DynFun*)FUNC, &funnotfound) ARGS; + +#define CALL_DYN_RET(RETV, RET, FUNC, OBJ, ARGS) \ + typedef RET ThisDynFun(); \ + bool funnotfound; \ + ThisDynFun *funtmp; \ + funtmp=(ThisDynFun*)lookup_dynfun((Obj*)OBJ, (DynFun*)FUNC, \ + &funnotfound); \ + if(!funnotfound) \ + RETV=funtmp ARGS; + +#define HAS_DYN(OBJ, FUNC) has_dynfun((Obj*)OBJ, (DynFun*)FUNC) + +#endif /* LIBTU_OBJP_H */ diff --git a/libtu/optparser.c b/libtu/optparser.c new file mode 100644 index 0000000..fc65523 --- /dev/null +++ b/libtu/optparser.c @@ -0,0 +1,471 @@ +/* + * libtu/optparser.c + * + * Copyright (c) Tuomo Valkonen 1999-2004. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#include +#include + +#include "util.h" +#include "misc.h" +#include "optparser.h" +#include "output.h" +#include "private.h" + + +#define O_ARGS(o) (o->flags&OPT_OPT_ARG) +#define O_ARG(o) (o->flasg&OPT_ARG) +#define O_OPT_ARG(o) (O_ARGS(o)==OPT_OPT_ARG) +#define O_ID(o) (o->optid) + + +static const OptParserOpt *o_opts=NULL; +static char *const *o_current=NULL; +static int o_left=0; +static const char* o_chain_ptr=NULL; +static int o_args_left=0; +static const char*o_tmp=NULL; +static int o_error=0; +static int o_mode=OPTP_CHAIN; + + +/* */ + + +void optparser_init(int argc, char *const argv[], int mode, + const OptParserOpt *opts) +{ + o_mode=mode; + o_opts=opts; + o_current=argv+1; + o_left=argc-1; + o_chain_ptr=NULL; + o_args_left=0; + o_tmp=NULL; +} + + +/* */ + + +static const OptParserOpt *find_chain_opt(char p, const OptParserOpt *o) +{ + for(;O_ID(o);o++){ + if((O_ID(o)&~OPT_ID_RESERVED_FLAG)==p) + return o; + } + return NULL; +} + + +static bool is_option(const char *p) +{ + if(p==NULL) + return FALSE; + if(*p++!='-') + return FALSE; + if(*p++!='-') + return FALSE; + if(*p=='\0') + return FALSE; + return TRUE; +} + + +/* */ + +enum{ + SHORT, MIDLONG, LONG +}; + + +int optparser_get_opt() +{ +#define RET(X) return o_tmp=p, o_error=X + const char *p, *p2=NULL; + bool dash=TRUE; + int type=SHORT; + const OptParserOpt *o; + int l; + + while(o_args_left) + optparser_get_arg(); + + o_tmp=NULL; + + /* Are we doing a chain (i.e. opt. of style 'tar xzf')? */ + if(o_chain_ptr!=NULL){ + p=o_chain_ptr++; + if(!*o_chain_ptr) + o_chain_ptr=NULL; + + o=find_chain_opt(*p, o_opts); + + if(o==NULL) + RET(E_OPT_INVALID_CHAIN_OPTION); + + goto do_arg; + } + + if(o_left<1) + return OPT_ID_END; + + o_left--; + p=*o_current++; + + if(*p!='-'){ + dash=FALSE; + if(o_mode!=OPTP_NO_DASH) + RET(OPT_ID_ARGUMENT); + p2=p; + }else if(*(p+1)=='-'){ + /* --foo */ + if(*(p+2)=='\0'){ + /* -- arguments */ + o_args_left=o_left; + RET(OPT_ID_ARGUMENT); + } + type=LONG; + p2=p+2; + }else{ + /* -foo */ + if(*(p+1)=='\0'){ + /* - */ + o_args_left=1; + RET(OPT_ID_ARGUMENT); + } + if(*(p+2)!='\0' && o_mode==OPTP_MIDLONG) + type=MIDLONG; + + p2=p+1; + } + + o=o_opts; + + for(; O_ID(o); o++){ + if(type==LONG){ + /* Do long option (--foo=bar) */ + if(o->longopt==NULL) + continue; + l=strlen(o->longopt); + if(strncmp(p2, o->longopt, l)!=0) + continue; + + if(p2[l]=='\0'){ + if(O_ARGS(o)==OPT_ARG) + RET(E_OPT_MISSING_ARGUMENT); + return O_ID(o); + }else if(p2[l]=='='){ + if(!O_ARGS(o)) + RET(E_OPT_UNEXPECTED_ARGUMENT); + if(p2[l+1]=='\0') + RET(E_OPT_MISSING_ARGUMENT); + o_tmp=p2+l+1; + o_args_left=1; + return O_ID(o); + } + continue; + }else if(type==MIDLONG){ + if(o->longopt==NULL) + continue; + + if(strcmp(p2, o->longopt)!=0) + continue; + }else{ /* type==SHORT */ + if(*p2!=(O_ID(o)&~OPT_ID_RESERVED_FLAG)) + continue; + + if(*(p2+1)!='\0'){ + if(o_mode==OPTP_CHAIN || o_mode==OPTP_NO_DASH){ + /*valid_chain(p2+1, o_opts)*/ + o_chain_ptr=p2+1; + p++; + }else if(o_mode==OPTP_IMMEDIATE){ + if(!O_ARGS(o)){ + if(*(p2+1)!='\0') + RET(E_OPT_UNEXPECTED_ARGUMENT); + }else{ + if(*(p2+1)=='\0') + RET(E_OPT_MISSING_ARGUMENT); + o_tmp=p2+1; + o_args_left=1; + } + return O_ID(o); + }else{ + RET(E_OPT_SYNTAX_ERROR); + } + } + } + + do_arg: + + if(!O_ARGS(o)) + return O_ID(o); + + if(!o_left || is_option(*o_current)){ + if(O_ARGS(o)==OPT_OPT_ARG) + return O_ID(o); + RET(E_OPT_MISSING_ARGUMENT); + } + + o_args_left=1; + return O_ID(o); + } + + if(dash) + RET(E_OPT_INVALID_OPTION); + + RET(OPT_ID_ARGUMENT); +#undef RET +} + + +/* */ + + +const char* optparser_get_arg() +{ + const char *p; + + if(o_tmp!=NULL){ + /* If o_args_left==0, then were returning an invalid option + * otherwise an immediate argument (e.g. -funsigned-char + * where '-f' is the option and 'unsigned-char' the argument) + */ + if(o_args_left>0) + o_args_left--; + p=o_tmp; + o_tmp=NULL; + return p; + } + + if(o_args_left<1 || o_left<1) + return NULL; + + o_left--; + o_args_left--; + return *o_current++; +} + + +/* */ + +static void warn_arg(const char *e) +{ + const char *p=optparser_get_arg(); + + if(p==NULL) + warn("%s (null)", e); + else + warn("%s \'%s\'", e, p); +} + + +static void warn_opt(const char *e) +{ + if(o_tmp!=NULL && o_chain_ptr!=NULL) + warn("%s \'-%c\'", e, *o_tmp); + else + warn_arg(e); +} + + +void optparser_print_error() +{ + switch(o_error){ + case E_OPT_INVALID_OPTION: + case E_OPT_INVALID_CHAIN_OPTION: + warn_opt(TR("Invalid option")); + break; + + case E_OPT_SYNTAX_ERROR: + warn_arg(TR("Syntax error while parsing")); + break; + + case E_OPT_MISSING_ARGUMENT: + warn_opt(TR("Missing argument to")); + break; + + case E_OPT_UNEXPECTED_ARGUMENT: + warn_opt(TR("No argument expected:")); + break; + + case OPT_ID_ARGUMENT: + warn(TR("Unexpected argument")); + break; + + default: + warn(TR("(unknown error)")); + } + + o_tmp=NULL; + o_error=0; +} + + +/* */ + + +static uint opt_w(const OptParserOpt *opt, bool midlong) +{ + uint w=0; + + if((opt->optid&OPT_ID_NOSHORT_FLAG)==0){ + w+=2; /* "-o" */ + if(opt->longopt!=NULL) + w+=2; /* ", " */ + } + + if(opt->longopt!=NULL) + w+=strlen(opt->longopt)+(midlong ? 1 : 2); + + if(O_ARGS(opt)){ + if(opt->argname==NULL) + w+=4; + else + w+=1+strlen(opt->argname); /* "=ARG" or " ARG" */ + + if(O_OPT_ARG(opt)) + w+=2; /* [ARG] */ + } + + return w; +} + + +#define TERM_W 80 +#define OFF1 2 +#define OFF2 2 +#define SPACER1 " " +#define SPACER2 " " + +static void print_opt(const OptParserOpt *opt, bool midlong, + uint maxw, uint tw) +{ + FILE *f=stdout; + const char *p, *p2, *p3; + uint w=0; + + fprintf(f, SPACER1); + + /* short opt */ + + if((O_ID(opt)&OPT_ID_NOSHORT_FLAG)==0){ + fprintf(f, "-%c", O_ID(opt)&~OPT_ID_RESERVED_FLAG); + w+=2; + + if(opt->longopt!=NULL){ + fprintf(f, ", "); + w+=2; + } + } + + /* long opt */ + + if(opt->longopt!=NULL){ + if(midlong){ + w++; + fprintf(f, "-%s", opt->longopt); + }else{ + w+=2; + fprintf(f, "--%s", opt->longopt); + } + w+=strlen(opt->longopt); + } + + /* arg */ + + if(O_ARGS(opt)){ + w++; + if(opt->longopt!=NULL && !midlong) + putc('=', f); + else + putc(' ', f); + + if(O_OPT_ARG(opt)){ + w+=2; + putc('[', f); + } + + if(opt->argname!=NULL){ + fprintf(f, "%s", opt->argname); + w+=strlen(opt->argname); + }else{ + w+=3; + fprintf(f, "ARG"); + } + + if(O_OPT_ARG(opt)) + putc(']', f); + } + + while(w++descr; + + if(p==NULL){ + putc('\n', f); + return; + } + + fprintf(f, SPACER2); + + maxw+=OFF1+OFF2; + tw-=maxw; + + while(strlen(p)>tw){ + p3=p2=p+tw-2; + + while(*p2!=' ' && p2!=p) + p2--; + + while(*p3!=' ' && *p3!='\0') + p3++; + + if((uint)(p3-p2)>tw){ + /* long word - just wrap */ + p2=p+tw-2; + } + + writef(f, p, p2-p); + if(*p2==' ') + putc('\n', f); + else + fprintf(f, "\\\n"); + + p=p2+1; + + w=maxw; + while(w--) + putc(' ', f); + } + + fprintf(f, "%s\n", p); +} + + +void optparser_printhelp(int mode, const OptParserOpt *opts) +{ + uint w, maxw=0; + const OptParserOpt *o; + bool midlong=mode&OPTP_MIDLONG; + + o=opts; + for(; O_ID(o); o++){ + w=opt_w(o, midlong); + if(w>maxw) + maxw=w; + } + + o=opts; + + for(; O_ID(o); o++) + print_opt(o, midlong, maxw, TERM_W); +} diff --git a/libtu/optparser.h b/libtu/optparser.h new file mode 100644 index 0000000..3b6ed5f --- /dev/null +++ b/libtu/optparser.h @@ -0,0 +1,78 @@ +/* + * libtu/optparser.h + * + * Copyright (c) Tuomo Valkonen 1999-2004. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#ifndef LIBTU_OPTPARSER_H +#define LIBTU_OPTPARSER_H + +#include "types.h" + + +#define OPT_ID_NOSHORT_FLAG 0x10000 +#define OPT_ID_RESERVED_FLAG 0x20000 + +#define OPT_ID(X) ((X)|OPT_ID_NOSHORT_FLAG) +#define OPT_ID_RESERVED(X) ((X)|OPT_ID_RESERVED_FLAG) + +/* OPTP_CHAIN is the normal behavior, i.e. single-letter options can be + * "chained" together: 'lr -lR'. Use for normal command line programs. + * OPTP_MIDLONG allows '-display foo' -like args but disables chaining + * of single-letter options. X programs should probably use this. + * OPTP_IMMEDIATE allows immediate arguments (-I/usr/include) (and disables + * chaining and midlong options). + * OPTP_NO_DASH is the same as OPTP_CHAIN but allows the dash to be omitted + * for 'tar xzf foo' -like behavior. + * Long '--foo=bar' options are supported in all of the modes. + */ + +enum{ + OPTP_CHAIN=0, + OPTP_DEFAULT=0, + OPTP_MIDLONG=1, + OPTP_IMMEDIATE=2, + OPTP_NO_DASH=3 +}; + +enum{ + OPT_ARG=1, /* option has an argument */ + OPT_OPT_ARG=3 /* option may have an argument */ +}; + + +#define END_OPTPARSEROPTS {0, NULL, 0, NULL, NULL} + +typedef struct _OptParserOpt{ + int optid; + const char *longopt; + int flags; + const char *argname; + const char *descr; +} OptParserOpt; + +enum{ + OPT_ID_END=0, + OPT_ID_ARGUMENT=1, + + E_OPT_INVALID_OPTION=-1, + E_OPT_INVALID_CHAIN_OPTION=-2, + E_OPT_SYNTAX_ERROR=-3, + E_OPT_MISSING_ARGUMENT=-4, + E_OPT_UNEXPECTED_ARGUMENT=-5 +}; + + +extern void optparser_init(int argc, char *const argv[], int mode, + const OptParserOpt *opts); + +extern void optparser_printhelp(int mode, const OptParserOpt *opts); + +extern int optparser_get_opt(); +extern const char* optparser_get_arg(); +extern void optparser_print_error(); + +#endif /* LIBTU_OPTPARSER_H */ diff --git a/libtu/output.c b/libtu/output.c new file mode 100644 index 0000000..6c70186 --- /dev/null +++ b/libtu/output.c @@ -0,0 +1,405 @@ +/* + * libtu/output.c + * + * Copyright (c) Tuomo Valkonen 1999-2002. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#if defined(HAS_SYSTEM_ASPRINTF) +#define _GNU_SOURCE +#endif + +#include +#include +#include +#include +#include + +#include "misc.h" +#include "output.h" +#include "util.h" +#include "private.h" + +#if !defined(HAS_SYSTEM_ASPRINTF) +#include "snprintf_2.2/snprintf.h" +#endif + + +static void default_warn_handler(const char *message); + + +static bool verbose_mode=FALSE; +static int verbose_indent_lvl=0; +static bool progname_enable=TRUE; +static WarnHandler *current_warn_handler=default_warn_handler; + +#define INDENTATOR_LENGTH 4 + +static char indentator[]={' ', ' ', ' ', ' '}; + +static void do_dispatch_message(const char *message); + + +void verbose(const char *p, ...) +{ + va_list args; + + va_start(args, p); + + verbose_v(p, args); + + va_end(args); +} + + +void verbose_v(const char *p, va_list args) +{ + int i; + + if(verbose_mode){ + for(i=0; i=0) + verbose_indent_lvl=depth; + + return old; +} + + +void warn_progname_enable(bool enable) +{ + progname_enable=enable; +} + + +static void put_prog_name() +{ + const char*progname; + + if(!progname_enable) + return; + + progname=prog_execname(); + + if(progname==NULL) + return; + + fprintf(stderr, "%s: ", (char*)progname); +} + +/* warn + */ + + +static void fallback_warn() +{ + put_prog_name(); + fprintf(stderr, TR("Oops. Error string compilation failed: %s"), + strerror(errno)); +} + + +#define CALL_V(NAME, ARGS) \ + do { va_list args; va_start(args, p); NAME ARGS; va_end(args); } while(0) + +#define DO_DISPATCH(NAME, ARGS) \ + do{ \ + char *msg; \ + if((msg=NAME ARGS)!=NULL){ \ + do_dispatch_message(msg); \ + free(msg);\ + }else{ \ + fallback_warn(); \ + } \ + }while(0) + + +void libtu_asprintf(char **ret, const char *p, ...) +{ + *ret=NULL; + CALL_V(vasprintf, (ret, p, args)); + if(*ret==NULL) + warn_err(); +} + + +void libtu_vasprintf(char **ret, const char *p, va_list args) +{ + *ret=NULL; + vasprintf(ret, p, args); + if(*ret==NULL) + warn_err(); +} + + +void warn(const char *p, ...) +{ + CALL_V(warn_v, (p, args)); +} + + +void warn_obj(const char *obj, const char *p, ...) +{ + CALL_V(warn_obj_v, (obj, p, args)); +} + + +void warn_obj_line(const char *obj, int line, const char *p, ...) +{ + CALL_V(warn_obj_line_v, (obj, line, p, args)); +} + + +void warn_obj_v(const char *obj, const char *p, va_list args) +{ + warn_obj_line_v(obj, -1, p, args); +} + + +void warn_v(const char *p, va_list args) +{ + DO_DISPATCH(errmsg_v, (p, args)); +} + + +void warn_obj_line_v(const char *obj, int line, const char *p, va_list args) +{ + DO_DISPATCH(errmsg_obj_line_v, (obj, line, p, args)); +} + + +void warn_err() +{ + DO_DISPATCH(errmsg_err, ()); +} + + +void warn_err_obj(const char *obj) +{ + DO_DISPATCH(errmsg_err_obj, (obj)); +} + +void warn_err_obj_line(const char *obj, int line) +{ + DO_DISPATCH(errmsg_err_obj_line, (obj, line)); +} + + +/* errmsg + */ + +#define CALL_V_RET(NAME, ARGS) \ + char *ret; va_list args; va_start(args, p); ret=NAME ARGS; \ + va_end(args); return ret; + + +char* errmsg(const char *p, ...) +{ + CALL_V_RET(errmsg_v, (p, args)); +} + + +char *errmsg_obj(const char *obj, const char *p, ...) +{ + CALL_V_RET(errmsg_obj_v, (obj, p, args)); +} + + +char *errmsg_obj_line(const char *obj, int line, const char *p, ...) +{ + CALL_V_RET(errmsg_obj_line_v, (obj, line, p, args)); +} + + +char* errmsg_obj_v(const char *obj, const char *p, va_list args) +{ + return errmsg_obj_line_v(obj, -1, p, args); +} + + +char *errmsg_v(const char *p, va_list args) +{ + char *res; + libtu_vasprintf(&res, p, args); + return res; +} + + +char *errmsg_obj_line_v(const char *obj, int line, const char *p, va_list args) +{ + char *res1=NULL, *res2, *res3; + + if(obj!=NULL){ + if(line>0) + libtu_asprintf(&res1, "%s:%d: ", obj, line); + else + libtu_asprintf(&res1, "%s: ", obj); + }else{ + if(line>0) + libtu_asprintf(&res1, "%d: ", line); + } + libtu_vasprintf(&res2, p, args); + if(res1!=NULL){ + if(res2==NULL) + return NULL; + res3=scat(res1, res2); + free(res1); + free(res2); + return res3; + } + return res2; +} + + +char *errmsg_err() +{ + char *res; + libtu_asprintf(&res, "%s\n", strerror(errno)); + return res; +} + + +char *errmsg_err_obj(const char *obj) +{ + char *res; + if(obj!=NULL) + libtu_asprintf(&res, "%s: %s\n", obj, strerror(errno)); + else + libtu_asprintf(&res, "%s\n", strerror(errno)); + return res; +} + + +char *errmsg_err_obj_line(const char *obj, int line) +{ + char *res; + if(obj!=NULL){ + if(line>0) + libtu_asprintf(&res, "%s:%d: %s\n", obj, line, strerror(errno)); + else + libtu_asprintf(&res, "%s: %s\n", obj, strerror(errno)); + }else{ + if(line>0) + libtu_asprintf(&res, "%d: %s\n", line, strerror(errno)); + else + libtu_asprintf(&res, "%s\n", strerror(errno)); + } + return res; +} + + +/* die + */ + + +void die(const char *p, ...) +{ + set_warn_handler(NULL); + CALL_V(die_v, (p, args)); +} + + +void die_v(const char *p, va_list args) +{ + set_warn_handler(NULL); + warn_v(p, args); + exit(EXIT_FAILURE); +} + + +void die_obj(const char *obj, const char *p, ...) +{ + set_warn_handler(NULL); + CALL_V(die_obj_v, (obj, p, args)); +} + + +void die_obj_line(const char *obj, int line, const char *p, ...) +{ + set_warn_handler(NULL); + CALL_V(die_obj_line_v, (obj, line, p, args)); +} + + +void die_obj_v(const char *obj, const char *p, va_list args) +{ + set_warn_handler(NULL); + warn_obj_v(obj, p, args); + exit(EXIT_FAILURE); +} + + +void die_obj_line_v(const char *obj, int line, const char *p, va_list args) +{ + set_warn_handler(NULL); + warn_obj_line_v(obj, line, p, args); + exit(EXIT_FAILURE); +} + + +void die_err() +{ + set_warn_handler(NULL); + warn_err(); + exit(EXIT_FAILURE); +} + + +void die_err_obj(const char *obj) +{ + set_warn_handler(NULL); + warn_err_obj(obj); + exit(EXIT_FAILURE); +} + + +void die_err_obj_line(const char *obj, int line) +{ + set_warn_handler(NULL); + warn_err_obj_line(obj, line); + exit(EXIT_FAILURE); +} + + +static void default_warn_handler(const char *message) +{ + put_prog_name(); + fprintf(stderr, "%s", message); + putc('\n', stderr); +} + + +static void do_dispatch_message(const char *message) +{ + if(current_warn_handler!=NULL) + current_warn_handler(message); + else + default_warn_handler(message); +} + + +WarnHandler *set_warn_handler(WarnHandler *handler) +{ + WarnHandler *old=current_warn_handler; + current_warn_handler=(handler!=NULL ? handler : default_warn_handler); + return old; +} diff --git a/libtu/output.h b/libtu/output.h new file mode 100644 index 0000000..8e75de9 --- /dev/null +++ b/libtu/output.h @@ -0,0 +1,76 @@ +/* + * libtu/output.h + * + * Copyright (c) Tuomo Valkonen 1999-2002. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#ifndef LIBTU_OUTPUT_H +#define LIBTU_OUTPUT_H + +#include + +#include "types.h" + +#if __STDC_VERSION__ >= 199901L +#define WARN_FUNC(...) warn_obj(__func__, __VA_ARGS__) +#define WARN_ERR_FUNC() warn_err_obj(__func__) +#else +#define WARN_FUNC warn +#define WARN_ERR_FUNC() warn_err() +#endif + +typedef void WarnHandler(const char *); +extern WarnHandler *set_warn_handler(WarnHandler *handler); + +extern void verbose(const char *p, ...); +extern void verbose_v(const char *p, va_list args); +extern void verbose_enable(bool enable); +extern int verbose_indent(int depth); + +extern void warn_progname_enable(bool enable); + +extern void die(const char *p, ...); +extern void die_v(const char *p, va_list args); + +extern void die_obj(const char *obj, const char *p, ...); +extern void die_obj_v(const char *obj, const char *p, va_list args); +extern void die_obj_line(const char *obj, int line, const char *p, ...); +extern void die_obj_line_v(const char *obj, int line, const char *p, va_list args); + +extern void die_err(); +extern void die_err_obj(const char *obj); +extern void die_err_obj_line(const char *obj, int line); + + +extern void warn(const char *p, ...); +extern void warn_v(const char *p, va_list args); + +extern void warn_obj(const char *obj, const char *p, ...); +extern void warn_obj_v(const char *obj, const char *p, va_list args); +extern void warn_obj_line(const char *obj, int line, const char *p, ...); +extern void warn_obj_line_v(const char *obj, int line, const char *p, va_list args); + +extern void warn_err(); +extern void warn_err_obj(const char *obj); +extern void warn_err_obj_line(const char *obj, int line); + + +extern char *errmsg(const char *p, ...); +extern char *errmsg_v(const char *p, va_list args); + +extern char *errmsg_obj(const char *obj, const char *p, ...); +extern char *errmsg_obj_v(const char *obj, const char *p, va_list args); +extern char *errmsg_obj_line(const char *obj, int line, const char *p, ...); +extern char *errmsg_obj_line_v(const char *obj, int line, const char *p, va_list args); + +extern char *errmsg_err(); +extern char *errmsg_err_obj(const char *obj); +extern char *errmsg_err_obj_line(const char *obj, int line); + +extern void libtu_asprintf(char **ret, const char *fmt, ...); +extern void libtu_vasprintf(char **ret, const char *fmt, va_list args); + +#endif /* LIBTU_OUTPUT_H */ diff --git a/libtu/parser.c b/libtu/parser.c new file mode 100644 index 0000000..3a29dd3 --- /dev/null +++ b/libtu/parser.c @@ -0,0 +1,717 @@ +/* + * libtu/parser.c + * + * Copyright (c) Tuomo Valkonen 1999-2002. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#include +#include + +#include "parser.h" +#include "misc.h" +#include "output.h" + +#define MAX_TOKENS 256 +#define MAX_NEST 256 + + +enum{ + P_NONE=1, + P_EOF, + P_STMT, + P_STMT_NS, + P_STMT_SECT, + P_BEG_SECT, + P_END_SECT +}; + + +/* */ + + +static bool opt_include(Tokenizer *tokz, int n, Token *toks); + + +static ConfOpt common_opts[]={ + {"include", "s", opt_include, NULL}, + {NULL, NULL, NULL, NULL} +}; + + +/* */ + + +static int read_statement(Tokenizer *tokz, Token *tokens, int *ntok_ret) +{ + int ntokens=0; + Token *tok=NULL; + int had_comma=0; /* 0 - no, 1 - yes, 2 - not had, not expected */ + int retval=0; + int e=0; + + while(1){ + tok=&tokens[ntokens]; + + if(!tokz_get_token(tokz, tok)){ + e=1; + continue; + } + + if(ntokens==MAX_TOKENS-1){ + e=E_TOKZ_TOKEN_LIMIT; + tokz_warn_error(tokz, tok->line, e); + if(!(tokz->flags&TOKZ_ERROR_TOLERANT)) + break; + }else{ + ntokens++; + } + + if(!TOK_IS_OP(tok)){ + if(ntokens==1 && !had_comma){ + /* first token */ + had_comma=2; + }else{ + if(had_comma==0) + goto syntax; + + had_comma=0; + } + continue; + } + + /* It is an operator */ + ntokens--; + + switch(TOK_OP_VAL(tok)){ + case OP_SCOLON: + retval=(ntokens==0 ? P_NONE : P_STMT_NS); + break; + + case OP_NEXTLINE: + retval=(ntokens==0 ? P_NONE : P_STMT); + break; + + case OP_L_BRC: + retval=(ntokens==0 ? P_BEG_SECT : P_STMT_SECT); + break; + + case OP_R_BRC: + if(ntokens==0){ + retval=P_END_SECT; + }else{ + tokz_unget_token(tokz, tok); + retval=P_STMT_NS; + } + break; + + case OP_EOF: + retval=(ntokens==0 ? P_EOF : P_STMT_NS); + + if(had_comma==1){ + e=E_TOKZ_UNEXPECTED_EOF; + goto handle_error; + } + + goto end; + + case OP_COMMA: + if(had_comma!=0) + goto syntax; + + had_comma=1; + continue; + + default: + goto syntax; + } + + if(had_comma!=1) + break; + + syntax: + e=E_TOKZ_SYNTAX; + handle_error: + tokz_warn_error(tokz, tok->line, e); + + if(!(tokz->flags&TOKZ_ERROR_TOLERANT) || retval!=0) + break; + } + +end: + if(e!=0) + retval=-retval; + + *ntok_ret=ntokens; + + return retval; +} + + +static bool find_beg_sect(Tokenizer *tokz) +{ + Token tok=TOK_INIT; + + while(tokz_get_token(tokz, &tok)){ + if(TOK_IS_OP(&tok)){ + if(TOK_OP_VAL(&tok)==OP_NEXTLINE) + continue; + + if(TOK_OP_VAL(&tok)==OP_SCOLON) + return FALSE; + + if(TOK_OP_VAL(&tok)==OP_L_BRC) + return TRUE; + } + + tokz_unget_token(tokz, &tok); + break; + } + return FALSE; +} + + +/* */ + + +static const ConfOpt* lookup_option(const ConfOpt *opts, const char *name) +{ + while(opts->optname!=NULL){ + if(strcmp(opts->optname, name)==0) + return opts; + opts++; + } + return NULL; +} + + +static bool call_end_sect(Tokenizer *tokz, const ConfOpt *opts) +{ + opts=lookup_option(opts, "#end"); + if(opts!=NULL) + return opts->fn(tokz, 0, NULL); + + return TRUE; +} + + +static bool call_cancel_sect(Tokenizer *tokz, const ConfOpt *opts) +{ + opts=lookup_option(opts, "#cancel"); + if(opts!=NULL) + return opts->fn(tokz, 0, NULL); + + return TRUE; +} + + +/* */ + + +bool parse_config_tokz(Tokenizer *tokz, const ConfOpt *options) +{ + Token tokens[MAX_TOKENS]; + bool alloced_optstack=FALSE; + int i, t, ntokens=0; + int init_nest_lvl; + bool had_error; + int errornest=0; + bool is_default=FALSE; + + /* Allocate tokz->optstack if it does not yet exist (if it does, + * we have been called from an option handler) + */ + if(!tokz->optstack){ + tokz->optstack=ALLOC_N(const ConfOpt*, MAX_NEST); + if(!tokz->optstack){ + warn_err(); + return FALSE; + } + + memset(tokz->optstack, 0, sizeof(ConfOpt*)*MAX_NEST); + init_nest_lvl=tokz->nest_lvl=0; + alloced_optstack=TRUE; + }else{ + init_nest_lvl=tokz->nest_lvl; + } + + tokz->optstack[init_nest_lvl]=options; + + for(i=0; iflags&TOKZ_PARSER_INDENT_MODE) + verbose_indent(tokz->nest_lvl); + + if(!TOK_IS_IDENT(tokens+0)){ + had_error=TRUE; + tokz_warn_error(tokz, tokens->line, + E_TOKZ_IDENTIFIER_EXPECTED); + } + + if(t==P_STMT){ + if(find_beg_sect(tokz)) + t=P_STMT_SECT; + } + + if(had_error) + break; + + /* Got the statement and its type */ + + options=lookup_option(tokz->optstack[tokz->nest_lvl], + TOK_IDENT_VAL(tokens+0)); + if(options==NULL) + options=lookup_option(common_opts, TOK_IDENT_VAL(tokens+0)); + if(options==NULL && (tokz->flags&TOKZ_DEFAULT_OPTION)){ + options=lookup_option(tokz->optstack[tokz->nest_lvl], "#default"); + is_default=(options!=NULL); + } + + if(options==NULL){ + had_error=TRUE; + tokz_warn_error(tokz, tokens->line, E_TOKZ_UNKNOWN_OPTION); + }else if(!is_default) { + had_error=!check_args(tokz, tokens, ntokens, options->argfmt); + } + + if(had_error) + break; + + /* Found the option and arguments are ok */ + + if(options->opts!=NULL){ + if(t!=P_STMT_SECT){ + had_error=TRUE; + tokz_warn_error(tokz, tokz->line, E_TOKZ_LBRACE_EXPECTED); + }else if(tokz->nest_lvl==MAX_NEST-1){ + tokz_warn_error(tokz, tokz->line, E_TOKZ_MAX_NEST); + had_error=TRUE; + }else{ + tokz->nest_lvl++; + tokz->optstack[tokz->nest_lvl]=options->opts; + } + }else if(t==P_STMT_SECT){ + had_error=TRUE; + tokz_warn_error(tokz, tokz->line, E_TOKZ_SYNTAX); + } + + if(!had_error && options->fn!=NULL){ + had_error=!options->fn(tokz, ntokens, tokens); + if(t==P_STMT_SECT && had_error) + tokz->nest_lvl--; + } + break; + + case P_EOF: + if(tokz_popf(tokz)){ + break; + }else if(tokz->nest_lvl>0 || errornest>0){ + tokz_warn_error(tokz, 0, E_TOKZ_UNEXPECTED_EOF); + had_error=TRUE; + } + goto eof; + + case P_BEG_SECT: + had_error=TRUE; + errornest++; + tokz_warn_error(tokz, tokz->line, E_TOKZ_SYNTAX); + break; + + case P_END_SECT: + if(tokz->nest_lvl+errornest==0){ + tokz_warn_error(tokz, tokz->line, E_TOKZ_SYNTAX); + had_error=TRUE; + } + + if(had_error) + break; + + if(errornest!=0){ + errornest--; + }else{ + had_error=!call_end_sect(tokz, tokz->optstack[tokz->nest_lvl]); + tokz->nest_lvl--; + } + + if(tokz->nest_lvlflags&TOKZ_ERROR_TOLERANT)) + break; + } + +eof: + /* free the tokens */ + while(ntokens--) + tok_free(&tokens[ntokens]); + + while(tokz->nest_lvl>=init_nest_lvl){ + if(tokz->flags&TOKZ_ERROR_TOLERANT || !had_error) + call_end_sect(tokz, tokz->optstack[tokz->nest_lvl]); + else + call_cancel_sect(tokz, tokz->optstack[tokz->nest_lvl]); + tokz->nest_lvl--; + } + + /* Free optstack if it was alloced by this call */ + if(alloced_optstack){ + free(tokz->optstack); + tokz->optstack=NULL; + tokz->nest_lvl=0; + } + + if(tokz->flags&TOKZ_PARSER_INDENT_MODE) + verbose_indent(init_nest_lvl); + + return !had_error; +} + + +/* */ + + +bool parse_config(const char *fname, const ConfOpt *options, int flags) +{ + Tokenizer *tokz; + bool ret; + + tokz=tokz_open(fname); + + if(tokz==NULL) + return FALSE; + + tokz->flags|=flags&~TOKZ_READ_COMMENTS; + + ret=parse_config_tokz(tokz, options); + + tokz_close(tokz); + + return ret; +} + + +bool parse_config_file(FILE *file, const ConfOpt *options, int flags) +{ + Tokenizer *tokz; + bool ret; + + tokz=tokz_open_file(file, NULL); + + if(tokz==NULL) + return FALSE; + + tokz->flags|=flags&~TOKZ_READ_COMMENTS; + + ret=parse_config_tokz(tokz, options); + + tokz_close(tokz); + + return ret; +} + + +/* + * Argument validity checking stuff + */ + + +static int arg_match(Token *tok, char c, bool si) +{ + char c2=tok->type; + + if(c=='.' || c=='*') + return 0; + + if(c2==c) + return 0; + + if(si){ + if(c2=='i' && c=='s'){ + TOK_SET_IDENT(tok, TOK_STRING_VAL(tok)); + return 0; + } + + if(c2=='s' && c=='i'){ + TOK_SET_STRING(tok, TOK_IDENT_VAL(tok)); + return 0; + } + } + + if(c2=='c' && c=='l'){ + TOK_SET_LONG(tok, TOK_CHAR_VAL(tok)); + return 0; + } + + if(c2=='l' && c=='c'){ + TOK_SET_CHAR(tok, TOK_LONG_VAL(tok)); + return 0; + } + + if(c2=='l' && c=='d'){ + TOK_SET_DOUBLE(tok, TOK_LONG_VAL(tok)); + return 0; + } + + if(c=='b'){ + if(c2=='l'){ + TOK_SET_BOOL(tok, TOK_LONG_VAL(tok)); + return 0; + }else if(c2=='i'){ + if(strcmp(TOK_IDENT_VAL(tok), "TRUE")==0){ + tok_free(tok); + TOK_SET_BOOL(tok, TRUE); + return 0; + }else if(strcmp(TOK_IDENT_VAL(tok), "FALSE")==0){ + tok_free(tok); + TOK_SET_BOOL(tok, FALSE); + return 0; + } + } + } + + return E_TOKZ_INVALID_ARGUMENT; +} + + +static int check_argument(const char **pret, Token *tok, const char *p, + bool si) +{ + int mode; + int e=E_TOKZ_TOO_MANY_ARGS; + +again: + mode=0; + + if(*p=='*'){ + *pret=p; + return 0; + }else if(*p=='?'){ + mode=1; + p++; + }else if(*p==':'){ + mode=2; + p++; + }else if(*p=='+'){ + *pret=p; + return arg_match(tok, *(p-1), si); + } + + while(*p!='\0'){ + e=arg_match(tok, *p, si); + if(e==0){ + p++; + while(mode==2 && *p==':'){ + if(*++p=='\0') + break; /* Invalid argument format string, though... */ + p++; + } + *pret=p; + return 0; + } + + if(mode==0) + break; + + p++; + + if(mode==1) + goto again; + + /* mode==2 */ + + if(*p!=':') + break; + p++; + e=E_TOKZ_TOO_MANY_ARGS; + } + + *pret=p; + return e; +} + + +static bool args_at_end(const char *p) +{ + if(p==NULL) + return TRUE; + + while(*p!='\0'){ + if(*p=='*' || *p=='+') + p++; + else if(*p=='?') + p+=2; + else + return FALSE; + } + + return TRUE; +} + + +bool do_check_args(const Tokenizer *tokz, Token *tokens, int ntokens, + const char *fmt, bool si) +{ + int i; + int e; + + if(fmt==NULL){ + if(ntokens!=1) + tokz_warn_error(tokz, tokens[0].line, E_TOKZ_TOO_MANY_ARGS); + return ntokens==1; + } + + for(i=1; iname!=NULL) + lastndx=strrchr(tokz->name, '/'); + + if(lastndx==NULL) + retval=try_include(tokz, fname); + else + retval=try_include_dir(tokz, tokz->name, lastndx-tokz->name+1, fname); + + if(retval==TRUE) + return TRUE; + + e=errno; + + if(tokz->includepaths!=NULL){ + while(tokz->includepaths[i]!=NULL){ + if(try_include_dir(tokz, tokz->includepaths[i], -1, fname)) + return TRUE; + i++; + } + } + + warn_obj(fname, "%s", strerror(e)); + + return FALSE; +} + + +extern void tokz_set_includepaths(Tokenizer *tokz, char **paths) +{ + tokz->includepaths=paths; +} + + + +ConfOpt libtu_dummy_confopts[]={ + END_CONFOPTS +}; + + + +bool parse_config_tokz_skip_section(Tokenizer *tokz) +{ + return parse_config_tokz(tokz, libtu_dummy_confopts); +} diff --git a/libtu/parser.h b/libtu/parser.h new file mode 100644 index 0000000..7358053 --- /dev/null +++ b/libtu/parser.h @@ -0,0 +1,54 @@ +/* + * libtu/parser.h + * + * Copyright (c) Tuomo Valkonen 1999-2002. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#ifndef LIBTU_PARSER_H +#define LIBTU_PARSER_H + +#include "tokenizer.h" + +/* + * format: + * l = long + * d = double + * i = identifier + * s = string + * c = char + * . = 1 times any ("l.d") + * * = 0 or more times any (must be the last, "sd*") + * ? = optional ("?c") + * : = conditional (":c:s") + * + = 1 or more times last (most be the last, "l+") + * special entries: + * + * "#end" call this handler at the end of section. + * "#cancel" call this handler when recovering from error + */ + +#define END_CONFOPTS {NULL, NULL, NULL, NULL} + +typedef struct _ConfOpt{ + const char *optname; + const char *argfmt; + bool (*fn)(Tokenizer *tokz, int n, Token *toks); + struct _ConfOpt *opts; +} ConfOpt; + +#define CONFOPTS_NOT_SET libtu_dummy_confopts +extern ConfOpt libtu_dummy_confopts[]; + +extern bool parse_config_tokz(Tokenizer *tokz, const ConfOpt *options); +extern bool parse_config_tokz_skip_section(Tokenizer *tokz); +extern bool parse_config(const char *fname, const ConfOpt *options, int flags); +extern bool parse_config_file(FILE *file, const ConfOpt *options, int flags); +extern bool check_args(const Tokenizer *tokz, Token *tokens, int ntokens, + const char *fmt); +extern bool check_args_loose(const Tokenizer *tokz, Token *tokens, int ntokens, + const char *fmt); + +#endif /* LIBTU_PARSER_H */ diff --git a/libtu/pointer.h b/libtu/pointer.h new file mode 100644 index 0000000..f22ecf5 --- /dev/null +++ b/libtu/pointer.h @@ -0,0 +1,16 @@ +/* + * libtu/pointer.h + * + * Copyright (c) Tuomo Valkonen 2005. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#ifndef LIBTU_POINTER_H +#define LIBTU_POINTER_H + +#define FIELD_OFFSET(T, F) ((long)((char*)&((T*)0)->F)) +#define FIELD_TO_STRUCT(T, F, A) ((T*)(((char*)A)-FIELD_OFFSET(T, F))) + +#endif /* LIBTU_POINTER_H */ diff --git a/libtu/private.h b/libtu/private.h new file mode 100644 index 0000000..20068ed --- /dev/null +++ b/libtu/private.h @@ -0,0 +1,27 @@ +/* + * libtu/private.h + * + * Copyright (c) Tuomo Valkonen 2004. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#ifndef LIBTU_PRIVATE_H +#define LIBTU_PRIVATE_H + +#ifndef CF_NO_LOCALE + +#include + +#define TR(X) dgettext("libtu", X) + +#else + +#define TR(X) (X) + +#endif + +#define DUMMY_TR(X) X + +#endif /* LIBTU_PRIVATE_H */ diff --git a/libtu/ptrlist.c b/libtu/ptrlist.c new file mode 100644 index 0000000..1ea6726 --- /dev/null +++ b/libtu/ptrlist.c @@ -0,0 +1,202 @@ +/* + * libtu/ptrlist.c + * + * Copyright (c) Tuomo Valkonen 1999-2005. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#include "obj.h" +#include "ptrlist.h" +#include "types.h" +#include "dlist.h" +#include "misc.h" + + +static void free_node(PtrList **ptrlist, PtrList *node) +{ + UNLINK_ITEM(*ptrlist, node, next, prev); + free(node); +} + + +static PtrList *mknode(void *ptr) +{ + PtrList *node; + + if(ptr==NULL) + return NULL; + + node=ALLOC(PtrList); + + if(node==NULL) + return FALSE; + + node->ptr=ptr; + + return node; +} + + +static PtrList *ptrlist_find_node(PtrList *ptrlist, void *ptr) +{ + PtrList *node=ptrlist; + + while(node!=NULL){ + if(node->ptr==ptr) + break; + node=node->next; + } + + return node; +} + + +bool ptrlist_insert_last(PtrList **ptrlist, void *ptr) +{ + PtrList *node=mknode(ptr); + + if(node==NULL) + return FALSE; + + LINK_ITEM_LAST(*ptrlist, node, next, prev); + + return TRUE; +} + + +bool ptrlist_insert_first(PtrList **ptrlist, void *ptr) +{ + PtrList *node=mknode(ptr); + + if(node==NULL) + return FALSE; + + LINK_ITEM_FIRST(*ptrlist, node, next, prev); + + return TRUE; +} + + +bool ptrlist_reinsert_last(PtrList **ptrlist, void *ptr) +{ + PtrList *node=ptrlist_find_node(*ptrlist, ptr); + + if(node==NULL) + return FALSE; + + UNLINK_ITEM(*ptrlist, node, next, prev); + LINK_ITEM_LAST(*ptrlist, node, next, prev); + + return TRUE; +} + + +bool ptrlist_reinsert_first(PtrList **ptrlist, void *ptr) +{ + PtrList *node=ptrlist_find_node(*ptrlist, ptr); + + if(node==NULL) + return FALSE; + + UNLINK_ITEM(*ptrlist, node, next, prev); + LINK_ITEM_FIRST(*ptrlist, node, next, prev); + + return TRUE; +} + + +bool ptrlist_remove(PtrList **ptrlist, void *ptr) +{ + PtrList *node=ptrlist_find_node(*ptrlist, ptr); + + if(node!=NULL) + free_node(ptrlist, node); + + return (node!=NULL); +} + + +void ptrlist_clear(PtrList **ptrlist) +{ + while(*ptrlist!=NULL) + free_node(ptrlist, *ptrlist); +} + + +PtrListIterTmp ptrlist_iter_tmp=NULL; + + +void ptrlist_iter_init(PtrListIterTmp *state, PtrList *ptrlist) +{ + *state=ptrlist; +} + + +void *ptrlist_iter(PtrListIterTmp *state) +{ + void *ptr=NULL; + + if(*state!=NULL){ + ptr=(*state)->ptr; + (*state)=(*state)->next; + } + + return ptr; +} + + +void ptrlist_iter_rev_init(PtrListIterTmp *state, PtrList *ptrlist) +{ + *state=(ptrlist==NULL ? NULL : ptrlist->prev); +} + + +void *ptrlist_iter_rev(PtrListIterTmp *state) +{ + void *ptr=NULL; + + if(*state!=NULL){ + ptr=(*state)->ptr; + *state=(*state)->prev; + if((*state)->next==NULL) + *state=NULL; + } + + return ptr; +} + + +void *ptrlist_take_first(PtrList **ptrlist) +{ + PtrList *node=*ptrlist; + void *ptr; + + if(node==NULL) + return NULL; + + ptr=node->ptr; + + free_node(ptrlist, node); + + return ptr; +} + + +void *ptrlist_take_last(PtrList **ptrlist) +{ + PtrList *node=*ptrlist; + void *ptr; + + if(node==NULL) + return NULL; + + node=node->prev; + + ptr=node->ptr; + + free_node(ptrlist, node); + + return ptr; +} diff --git a/libtu/ptrlist.h b/libtu/ptrlist.h new file mode 100644 index 0000000..cadec02 --- /dev/null +++ b/libtu/ptrlist.h @@ -0,0 +1,57 @@ +/* + * libtu/ptrlist.h + * + * Copyright (c) Tuomo Valkonen 1999-2005. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#ifndef LIBTU_PTRLIST_H +#define LIBTU_PTRLIST_H + +#include "types.h" +#include "iterable.h" + + +INTRSTRUCT(PtrList); + + +DECLSTRUCT(PtrList){ + void *ptr; + PtrList *next, *prev; +}; + + +typedef PtrList* PtrListIterTmp; + +#define PTRLIST_FIRST(TYPE, LL) ((LL)==NULL ? NULL : (TYPE)(LL)->ptr) +#define PTRLIST_LAST(TYPE, LL) ((LL)==NULL ? NULL : (TYPE)(LL)->prev->ptr) +#define PTRLIST_EMPTY(LIST) ((LIST)==NULL) + +#define FOR_ALL_ON_PTRLIST(TYPE, VAR, LL, TMP) \ + FOR_ALL_ITER(ptrlist_iter_init, (TYPE)ptrlist_iter, VAR, LL, &(TMP)) + +#define FOR_ALL_ON_PTRLIST_REV(TYPE, VAR, LL, TMP) \ + FOR_ALL_ITER(ptrlist_iter_rev_init, \ + (TYPE)ptrlist_iter_rev, VAR, LL, &(TMP)) + +#define FOR_ALL_ON_PTRLIST_UNSAFE(TYPE, VAR, LL) \ + FOR_ALL_ON_PTRLIST(TYPE, VAR, LL, ptrlist_iter_tmp) + +extern PtrListIterTmp ptrlist_iter_tmp; + +extern bool ptrlist_insert_last(PtrList **ptrlist, void *ptr); +extern bool ptrlist_insert_first(PtrList **ptrlist, void *ptr); +extern bool ptrlist_reinsert_last(PtrList **ptrlist, void *ptr); +extern bool ptrlist_reinsert_first(PtrList **ptrlist, void *ptr); +extern bool ptrlist_remove(PtrList **ptrlist, void *ptr); +extern void ptrlist_clear(PtrList **ptrlist); +extern void ptrlist_iter_init(PtrListIterTmp *state, PtrList *ptrlist); +extern void *ptrlist_iter(PtrListIterTmp *state); +extern void ptrlist_iter_rev_init(PtrListIterTmp *state, PtrList *ptrlist); +extern void *ptrlist_iter_rev(PtrListIterTmp *state); +extern void *ptrlist_take_first(PtrList **ptrlist); +extern void *ptrlist_take_last(PtrList **ptrlist); + +#endif /* LIBTU_PTRLIST_H */ diff --git a/libtu/rb-test.c b/libtu/rb-test.c new file mode 100644 index 0000000..96ea161 --- /dev/null +++ b/libtu/rb-test.c @@ -0,0 +1,98 @@ +/* +Generic C code for red-black trees. +Copyright (C) 2000 James S. Plank + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* Revision 1.2. Jim Plank */ + +#include "rb.h" +#include + +/* an example -- this allocates a red-black tree for integers. For a + * user-specified number of iterations, it does the following: + + * delete a random element in the tree. + * make two new random elements, and insert them into the tree + + * At the end, it prints the sorted list of elements, and then prints + * stats of the number of black nodes in any path in the tree, and + * the minimum and maximum path lengths. + + * Rb_find_ikey and rb_inserti could have been used, but instead + * rb_find_gkey and rb_insertg were used to show how they work. + + */ + +int icomp(char *i, char *j) +{ + int I, J; + + I = (int) i; + J = (int) j; + if (I == J) return 0; + if (I > J) return -1; else return 1; +} + +main(int argc, char **argv) +{ + int i, j, tb, nb, mxp, mnp, p; + int iterations; + Rb_node argt, t; + int *a; + + if (argc != 2) { + fprintf(stderr, "usage: main #iterations\n"); + exit(1); + } + argt = make_rb(); + srandom(time(0)); + iterations = atoi(argv[1]); + a = (int *) malloc (iterations*sizeof(int)); + + for (i = 0; i < atoi(argv[1]); i++) { + if (i > 0) { + j = random()%i; + + rb_delete_node(rb_find_gkey(argt, (char *) (a[j]), icomp)); + a[j] = random() % 1000; + rb_insertg(argt, (char *) (a[j]), NULL, icomp); + } + a[i] = random() % 1000; + rb_insertg(argt, (char *) (a[i]), NULL, icomp); + } + tb = 0; + mxp = 0; + mnp = 0; + rb_traverse(t, argt) { + printf("%d ", t->k.ikey); + nb = rb_nblack(t); + p = rb_plength(t); + if (tb == 0) { + tb = nb; + } else if (tb != nb) { + printf("Conflict: tb=%d, nb=%d\n", tb, nb); + exit(1); + } + mxp = (mxp == 0 || mxp < p) ? p : mxp; + mnp = (mnp == 0 || mxp > p) ? p : mnp; + } + printf("\n"); + + printf("Nblack = %d\n", tb); + printf("Max = %d\n", mxp); + printf("Min = %d\n", mnp); +} diff --git a/libtu/rb.c b/libtu/rb.c new file mode 100644 index 0000000..d842a27 --- /dev/null +++ b/libtu/rb.c @@ -0,0 +1,626 @@ +/* +Generic C code for red-black trees. +Copyright (C) 2000 James S. Plank, + 2004 Tuomo Valkonen. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +/* Revision 1.2. Jim Plank */ + +/* Original code by Jim Plank (plank@cs.utk.edu) */ +/* modified for THINK C 6.0 for Macintosh by Chris Bartley */ + +#include +#include +#include +#include +#include "rb.h" + +static void mk_new_int(Rb_node l, Rb_node r, Rb_node p, int il); +static Rb_node lprev(Rb_node n); +static Rb_node rprev(Rb_node n); +static void recolor(Rb_node n); +static void single_rotate(Rb_node y, int l); +static void rb_print_tree(Rb_node t, int level); +static void rb_iprint_tree(Rb_node t, int level); + +/* Gcc complains if non-char* pointer is passed to printf %p */ +#define DONT_COMPLAIN (char*) + +#define isred(n) (n->s.red) +#define isblack(n) (!isred(n)) +#define isleft(n) (n->s.left) +#define isright(n) (!isleft(n)) +#define isint(n) (n->s.internal) +#define isext(n) (!isint(n)) +#define ishead(n) (n->s.head) +#define isroot(n) (n->s.root) +#define setred(n) n->s.red = 1 +#define setblack(n) n->s.red = 0 +#define setleft(n) n->s.left = 1 +#define setright(n) n->s.left = 0 +#define sethead(n) n->s.head = 1 +#define setroot(n) n->s.root = 1 +#define setint(n) n->s.internal = 1 +#define setext(n) n->s.internal = 0 +#define setnormal(n) { n->s.root = 0; n ->s.head = 0; } +#define sibling(n) ((isleft(n)) ? n->p.parent->c.child.right \ + : n->p.parent->c.child.left) + +static void insert(Rb_node item, Rb_node list) /* Inserts to the end of a list */ +{ + Rb_node last_node; + + last_node = list->c.list.blink; + + list->c.list.blink = item; + last_node->c.list.flink = item; + item->c.list.blink = last_node; + item->c.list.flink = list; +} + +static void delete_item(Rb_node item) /* Deletes an arbitrary iterm */ +{ + item->c.list.flink->c.list.blink = item->c.list.blink; + item->c.list.blink->c.list.flink = item->c.list.flink; +} + +#define mk_new_ext(new, kkkey, vvval) {\ + new = (Rb_node) malloc(sizeof(struct rb_node));\ + if(new==NULL) return NULL;\ + new->v.val = vvval;\ + new->k.key = kkkey;\ + setext(new);\ + setblack(new);\ + setnormal(new);\ +} + +static void mk_new_int(Rb_node l, Rb_node r, Rb_node p, int il) +{ + Rb_node newnode; + + newnode = (Rb_node) malloc(sizeof(struct rb_node)); + setint(newnode); + setred(newnode); + setnormal(newnode); + newnode->c.child.left = l; + newnode->c.child.right = r; + newnode->p.parent = p; + newnode->k.lext = l; + newnode->v.rext = r; + l->p.parent = newnode; + r->p.parent = newnode; + setleft(l); + setright(r); + if (ishead(p)) { + p->p.root = newnode; + setroot(newnode); + } else if (il) { + setleft(newnode); + p->c.child.left = newnode; + } else { + setright(newnode); + p->c.child.right = newnode; + } + recolor(newnode); +} + + +Rb_node lprev(Rb_node n) +{ + if (ishead(n)) return n; + while (!isroot(n)) { + if (isright(n)) return n->p.parent; + n = n->p.parent; + } + return n->p.parent; +} + +Rb_node rprev(Rb_node n) +{ + if (ishead(n)) return n; + while (!isroot(n)) { + if (isleft(n)) return n->p.parent; + n = n->p.parent; + } + return n->p.parent; +} + +Rb_node make_rb() +{ + Rb_node head; + + head = (Rb_node) malloc (sizeof(struct rb_node)); + if(head!=NULL){ + head->c.list.flink = head; + head->c.list.blink = head; + head->p.root = head; + head->k.key = ""; + sethead(head); + } + return head; +} + +Rb_node rb_find_ikey_n(Rb_node n, int ikey, int *fnd) +{ + *fnd = 0; + if (!ishead(n)) { + fprintf(stderr, "rb_find_ikey_n called on non-head %p\n", + DONT_COMPLAIN n); + exit(1); + } + if (n->p.root == n) return n; + if (ikey == n->c.list.blink->k.ikey) { + *fnd = 1; + return n->c.list.blink; + } + if (ikey > n->c.list.blink->k.ikey) return n; + else n = n->p.root; + while (1) { + if (isext(n)) return n; + if (ikey == n->k.lext->k.ikey) { + *fnd = 1; + return n->k.lext; + } + n = (ikey < n->k.lext->k.ikey) ? n->c.child.left : n->c.child.right; + } +} + +Rb_node rb_find_ikey(Rb_node n, int ikey) +{ + int fnd; + return rb_find_ikey_n(n, ikey, &fnd); +} + +Rb_node rb_find_gkey_n(Rb_node n, const void *key, Rb_compfn *fxn, int *fnd) +{ + int cmp; + + *fnd = 0; + if (!ishead(n)) { + fprintf(stderr, "rb_find_gkey_n called on non-head %p\n", + DONT_COMPLAIN n); + exit(1); + } + if (n->p.root == n) return n; + cmp = (*fxn)(key, n->c.list.blink->k.key); + if (cmp == 0) { + *fnd = 1; + return n->c.list.blink; + } + if (cmp > 0) return n; + else n = n->p.root; + while (1) { + if (isext(n)) return n; + cmp = (*fxn)(key, n->k.lext->k.key); + if (cmp == 0) { + *fnd = 1; + return n->k.lext; + } + if (cmp < 0) n = n->c.child.left ; else n = n->c.child.right; + } +} + +Rb_node rb_find_gkey(Rb_node n, const void *key, Rb_compfn *fxn) +{ + int fnd; + return rb_find_gkey_n(n, key, fxn, &fnd); +} + +Rb_node rb_find_key_n(Rb_node n, const char *key, int *fnd) +{ + return rb_find_gkey_n(n, key, (Rb_compfn*)strcmp, fnd); +} + +Rb_node rb_find_key(Rb_node n, const char *key) +{ + int fnd; + return rb_find_gkey_n(n, key, (Rb_compfn*)strcmp, &fnd); +} + +static int ptrcmp(const void *a, const void *b) +{ + return (ap.root == n) { /* Tree is empty */ + mk_new_ext(newnode, key, val); + insert(newnode, n); + n->p.root = newnode; + newnode->p.parent = n; + setroot(newnode); + return newnode; + } else { + mk_new_ext(newright, key, val); + insert(newright, n); + newleft = newright->c.list.blink; + setnormal(newleft); + mk_new_int(newleft, newright, newleft->p.parent, isleft(newleft)); + p = rprev(newright); + if (!ishead(p)) p->k.lext = newright; + return newright; + } + } else { + mk_new_ext(newleft, key, val); + insert(newleft, n); + setnormal(n); + mk_new_int(newleft, n, n->p.parent, isleft(n)); + p = lprev(newleft); + if (!ishead(p)) p->v.rext = newleft; + return newleft; + } +} + +static void recolor(Rb_node n) +{ + Rb_node p, gp, s; + int done = 0; + + while(!done) { + if (isroot(n)) { + setblack(n); + return; + } + + p = n->p.parent; + + if (isblack(p)) return; + + if (isroot(p)) { + setblack(p); + return; + } + + gp = p->p.parent; + s = sibling(p); + if (isred(s)) { + setblack(p); + setred(gp); + setblack(s); + n = gp; + } else { + done = 1; + } + } + /* p's sibling is black, p is red, gp is black */ + + if ((isleft(n) == 0) == (isleft(p) == 0)) { + single_rotate(gp, isleft(n)); + setblack(p); + setred(gp); + } else { + single_rotate(p, isleft(n)); + single_rotate(gp, isleft(n)); + setblack(n); + setred(gp); + } +} + +static void single_rotate(Rb_node y, int l) +{ + int rl=0, ir=0; + Rb_node x=NULL, yp=NULL; + void *tmp=NULL; + + ir = isroot(y); + yp = y->p.parent; + if (!ir) { + rl = isleft(y); + } + + if (l) { + x = y->c.child.left; + y->c.child.left = x->c.child.right; + setleft(y->c.child.left); + y->c.child.left->p.parent = y; + x->c.child.right = y; + setright(y); + } else { + x = y->c.child.right; + y->c.child.right = x->c.child.left; + setright(y->c.child.right); + y->c.child.right->p.parent = y; + x->c.child.left = y; + setleft(y); + } + + x->p.parent = yp; + y->p.parent = x; + if (ir) { + yp->p.root = x; + setnormal(y); + setroot(x); + } else { + if (rl) { + yp->c.child.left = x; + setleft(x); + } else { + yp->c.child.right = x; + setright(x); + } + } +} + +void rb_delete_node(Rb_node n) +{ + Rb_node s, p, gp; + char ir; + + if (isint(n)) { + fprintf(stderr, "Cannot delete an internal node: %p\n", DONT_COMPLAIN n); + exit(1); + } + if (ishead(n)) { + fprintf(stderr, "Cannot delete the head of an rb_tree: %p\n", DONT_COMPLAIN n); + exit(1); + } + delete_item(n); /* Delete it from the list */ + p = n->p.parent; /* The only node */ + if (isroot(n)) { + p->p.root = p; + free(n); + return; + } + s = sibling(n); /* The only node after deletion */ + if (isroot(p)) { + s->p.parent = p->p.parent; + s->p.parent->p.root = s; + setroot(s); + free(p); + free(n); + return; + } + gp = p->p.parent; /* Set parent to sibling */ + s->p.parent = gp; + if (isleft(p)) { + gp->c.child.left = s; + setleft(s); + } else { + gp->c.child.right = s; + setright(s); + } + ir = isred(p); + free(p); + free(n); + + if (isext(s)) { /* Update proper rext and lext values */ + p = lprev(s); + if (!ishead(p)) p->v.rext = s; + p = rprev(s); + if (!ishead(p)) p->k.lext = s; + } else if (isblack(s)) { + fprintf(stderr, "DELETION PROB -- sib is black, internal\n"); + exit(1); + } else { + p = lprev(s); + if (!ishead(p)) p->v.rext = s->c.child.left; + p = rprev(s); + if (!ishead(p)) p->k.lext = s->c.child.right; + setblack(s); + return; + } + + if (ir) return; + + /* Recolor */ + + n = s; + p = n->p.parent; + s = sibling(n); + while(isblack(p) && isblack(s) && isint(s) && + isblack(s->c.child.left) && isblack(s->c.child.right)) { + setred(s); + n = p; + if (isroot(n)) return; + p = n->p.parent; + s = sibling(n); + } + + if (isblack(p) && isred(s)) { /* Rotation 2.3b */ + single_rotate(p, isright(n)); + setred(p); + setblack(s); + s = sibling(n); + } + + { Rb_node x, z; char il; + + if (isext(s)) { + fprintf(stderr, "DELETION ERROR: sibling not internal\n"); + exit(1); + } + + il = isleft(n); + x = il ? s->c.child.left : s->c.child.right ; + z = sibling(x); + + if (isred(z)) { /* Rotation 2.3f */ + single_rotate(p, !il); + setblack(z); + if (isred(p)) setred(s); else setblack(s); + setblack(p); + } else if (isblack(x)) { /* Recoloring only (2.3c) */ + if (isred(s) || isblack(p)) { + fprintf(stderr, "DELETION ERROR: 2.3c not quite right\n"); + exit(1); + } + setblack(p); + setred(s); + return; + } else if (isred(p)) { /* 2.3d */ + single_rotate(s, il); + single_rotate(p, !il); + setblack(x); + setred(s); + return; + } else { /* 2.3e */ + single_rotate(s, il); + single_rotate(p, !il); + setblack(x); + return; + } + } +} + + +void rb_print_tree(Rb_node t, int level) +{ + int i; + if (ishead(t) && t->p.parent == t) { + printf("tree %p is empty\n", DONT_COMPLAIN t); + } else if (ishead(t)) { + printf("Head: %p. Root = %p\n", DONT_COMPLAIN t, DONT_COMPLAIN t->p.root); + rb_print_tree(t->p.root, 0); + } else { + if (isext(t)) { + for (i = 0; i < level; i++) putchar(' '); + printf("Ext node %p: %c,%c: p=%p, k=%s\n", + DONT_COMPLAIN t, isred(t)?'R':'B', isleft(t)?'l':'r', + DONT_COMPLAIN t->p.parent, DONT_COMPLAIN t->k.key); + } else { + rb_print_tree(t->c.child.left, level+2); + rb_print_tree(t->c.child.right, level+2); + for (i = 0; i < level; i++) putchar(' '); + printf("Int node %p: %c,%c: l=%p, r=%p, p=%p, lr=(%s,%s)\n", + DONT_COMPLAIN t, isred(t)?'R':'B', isleft(t)?'l':'r', + DONT_COMPLAIN t->c.child.left, DONT_COMPLAIN t->c.child.right, + DONT_COMPLAIN t->p.parent, DONT_COMPLAIN t->k.lext->k.key, + DONT_COMPLAIN t->v.rext->k.key); + } + } +} + +void rb_iprint_tree(Rb_node t, int level) +{ + int i; + if (ishead(t) && t->p.parent == t) { + printf("tree %p is empty\n", DONT_COMPLAIN t); + } else if (ishead(t)) { + printf("Head: %p. Root = %p, < = %p, > = %p\n", + DONT_COMPLAIN t, DONT_COMPLAIN t->p.root, + DONT_COMPLAIN t->c.list.blink, + DONT_COMPLAIN t->c.list.flink); + rb_iprint_tree(t->p.root, 0); + } else { + if (isext(t)) { + for (i = 0; i < level; i++) putchar(' '); + printf("Ext node %p: %c,%c: p=%p, <=%p, >=%p k=%d\n", + DONT_COMPLAIN t, isred(t)?'R':'B', isleft(t)?'l':'r', + DONT_COMPLAIN t->p.parent, DONT_COMPLAIN t->c.list.blink, + DONT_COMPLAIN t->c.list.flink, t->k.ikey); + } else { + rb_iprint_tree(t->c.child.left, level+2); + rb_iprint_tree(t->c.child.right, level+2); + for (i = 0; i < level; i++) putchar(' '); + printf("Int node %p: %c,%c: l=%p, r=%p, p=%p, lr=(%d,%d)\n", + DONT_COMPLAIN t, isred(t)?'R':'B', isleft(t)?'l':'r', + DONT_COMPLAIN t->c.child.left, DONT_COMPLAIN t->c.child.right, + DONT_COMPLAIN t->p.parent, t->k.lext->k.ikey, t->v.rext->k.ikey); + } + } +} + +int rb_nblack(Rb_node n) +{ + int nb; + if (ishead(n) || isint(n)) { + fprintf(stderr, "ERROR: rb_nblack called on a non-external node %p\n", + DONT_COMPLAIN n); + exit(1); + } + nb = 0; + while(!ishead(n)) { + if (isblack(n)) nb++; + n = n->p.parent; + } + return nb; +} + +int rb_plength(Rb_node n) +{ + int pl; + if (ishead(n) || isint(n)) { + fprintf(stderr, "ERROR: rb_plength called on a non-external node %p\n", + DONT_COMPLAIN n); + exit(1); + } + pl = 0; + while(!ishead(n)) { + pl++; + n = n->p.parent; + } + return pl; +} + +void rb_free_tree(Rb_node n) +{ + if (!ishead(n)) { + fprintf(stderr, "ERROR: Rb_free_tree called on a non-head node\n"); + exit(1); + } + + while(rb_first(n) != rb_nil(n)) { + rb_delete_node(rb_first(n)); + } + free(n); +} + +void *rb_val(Rb_node n) +{ + return n->v.val; +} + +Rb_node rb_insert_a(Rb_node nd, const void *key, void *val) +{ + return rb_insert_b(nd->c.list.flink, key, val); +} + +Rb_node rb_insert(Rb_node tree, const char *key, void *val) +{ + return rb_insert_b(rb_find_key(tree, key), key, val); +} + +Rb_node rb_inserti(Rb_node tree, int ikey, void *val) +{ + return rb_insert_b(rb_find_ikey(tree, ikey), (void *) ikey, val); +} + +Rb_node rb_insertg(Rb_node tree, const void *key, void *val, Rb_compfn *func) +{ + return rb_insert_b(rb_find_gkey(tree, key, func), key, val); +} + +Rb_node rb_insertp(Rb_node tree, const void *key, void *val) +{ + return rb_insertg(tree, key, val, ptrcmp); +} + + diff --git a/libtu/rb.h b/libtu/rb.h new file mode 100644 index 0000000..9acc2b2 --- /dev/null +++ b/libtu/rb.h @@ -0,0 +1,143 @@ +/* +Generic C code for red-black trees. +Copyright (C) 2000 James S. Plank, + 2004 Tuomo Valkonen. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* Revision 1.2. Jim Plank */ + +/* Original code by Jim Plank (plank@cs.utk.edu) */ +/* modified for THINK C 6.0 for Macintosh by Chris Bartley */ + +#ifndef LIBTU_RB_H +#define LIBTU_RB_H + +typedef struct rb_status { + unsigned red : 1 ; + unsigned internal : 1 ; + unsigned left : 1 ; + unsigned root : 1 ; + unsigned head : 1 ; +} Rb_status; + +/* Main rb_node. You only ever use the fields + + c.list.flink + c.list.blink + + k.key or k.ikey + v.val +*/ + + +typedef struct rb_node { + union { + struct { + struct rb_node *flink; + struct rb_node *blink; + } list; + struct { + struct rb_node *left; + struct rb_node *right; + } child; + } c; + union { + struct rb_node *parent; + struct rb_node *root; + } p; + Rb_status s; + union { + int ikey; + const void *key; + struct rb_node *lext; + } k; + union { + int ival; + void *val; + struct rb_node *rext; + } v; +} *Rb_node; + + +typedef int Rb_compfn(const void *, const void *); + + +extern Rb_node make_rb(); /* Creates a new rb-tree */ + +/* Creates a node with key key and val val and inserts it into the tree. + rb_insert uses strcmp() as comparison funcion. rb_inserti uses <>=, + rb_insertg uses func() */ + +extern Rb_node rb_insert(Rb_node tree, const char *key, void *val); +extern Rb_node rb_inserti(Rb_node tree, int ikey, void *val); +extern Rb_node rb_insertp(Rb_node tree, const void *key, void *val); +extern Rb_node rb_insertg(Rb_node tree, const void *key, void *val, + Rb_compfn *func); + + +/* returns an external node in t whose value is equal + k or whose value is the smallest value greater than k. */ + +extern Rb_node rb_find_key(Rb_node root, const char *key); +extern Rb_node rb_find_ikey(Rb_node root, int ikey); +extern Rb_node rb_find_pkey(Rb_node root, const void *key); +extern Rb_node rb_find_gkey(Rb_node root, const void *key, Rb_compfn *func); + + +/* Works just like the find_key versions only it returns whether or not + it found the key in the integer variable found */ + +extern Rb_node rb_find_key_n(Rb_node root, const char *key, int *found); +extern Rb_node rb_find_ikey_n(Rb_node root, int ikey, int *found); +extern Rb_node rb_find_pkey_n(Rb_node root, const void *key, int *found); +extern Rb_node rb_find_gkey_n(Rb_node root, const void *key, + Rb_compfn *func, int *found); + + +/* Creates a node with key key and val val and inserts it into the + tree before/after node nd. Does not check to ensure that you are + keeping the correct order */ + +extern Rb_node rb_insert_b(Rb_node nd, const void *key, void *val); +extern Rb_node rb_insert_a(Rb_node nd, const void *key, void *val); + + +extern void rb_delete_node(Rb_node node); /* Deletes and frees a node (but + not the key or val) */ +extern void rb_free_tree(Rb_node root); /* Deletes and frees an entire tree */ + +extern void *rb_val(Rb_node node); /* Returns node->v.val -- this is to shut + lint up */ + +extern int rb_nblack(Rb_node n); /* returns # of black nodes in path from + n to the root */ +int rb_plength(Rb_node n); /* returns the # of nodes in path from + n to the root */ + +#define rb_first(n) (n->c.list.flink) +#define rb_last(n) (n->c.list.blink) +#define rb_next(n) (n->c.list.flink) +#define rb_prev(n) (n->c.list.blink) +#define rb_empty(t) (t->c.list.flink == t) +#ifndef rb_nil +#define rb_nil(t) (t) +#endif + +#define rb_traverse(ptr, lst) \ + for(ptr = rb_first(lst); ptr != rb_nil(lst); ptr = rb_next(ptr)) + +#endif /* LIBTU_RB_H */ diff --git a/libtu/setparam.c b/libtu/setparam.c new file mode 100644 index 0000000..3b5e5eb --- /dev/null +++ b/libtu/setparam.c @@ -0,0 +1,60 @@ +/* + * libtu/setparam.c + * + * Copyright (c) Tuomo Valkonen 2005. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#include + +#include "setparam.h" + +int libtu_string_to_setparam(const char *str) +{ + if(str!=NULL){ + if(strcmp(str, "set")==0 || strcmp(str, "true")==0) + return SETPARAM_SET; + else if(strcmp(str, "unset")==0 || strcmp(str, "false")==0) + return SETPARAM_UNSET; + else if(strcmp(str, "toggle")==0) + return SETPARAM_TOGGLE; + } + + return SETPARAM_UNKNOWN; +} + + +bool libtu_do_setparam(int sp, bool val) +{ + switch(sp){ + case SETPARAM_SET: + return TRUE; + case SETPARAM_UNSET: + return FALSE; + case SETPARAM_TOGGLE: + return (val==FALSE); + default: + return val; + } +} + +bool libtu_do_setparam_str(const char *str, bool val) +{ + return libtu_do_setparam(libtu_string_to_setparam(str), val); +} + + +int libtu_setparam_invert(int sp) +{ + switch(sp){ + case SETPARAM_SET: + return SETPARAM_UNSET; + case SETPARAM_UNSET: + return SETPARAM_SET; + default: + return sp; + } +} + diff --git a/libtu/setparam.h b/libtu/setparam.h new file mode 100644 index 0000000..8e6fad6 --- /dev/null +++ b/libtu/setparam.h @@ -0,0 +1,27 @@ +/* + * libtu/setparam.h + * + * Copyright (c) Tuomo Valkonen 2005. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#ifndef LIBTU_SETPARAM_H +#define LIBTU_SETPARAM_H + +#include "types.h" + +enum{ + SETPARAM_UNKNOWN, + SETPARAM_SET, + SETPARAM_UNSET, + SETPARAM_TOGGLE +}; + +extern int libtu_string_to_setparam(const char *str); +extern bool libtu_do_setparam_str(const char *str, bool val); +extern bool libtu_do_setparam(int sp, bool val); +extern int libtu_setparam_invert(int sp); + +#endif /* LIBTU_SETPARAM_H */ diff --git a/libtu/snprintf_2.2/INSTALL b/libtu/snprintf_2.2/INSTALL new file mode 100644 index 0000000..9cdf469 --- /dev/null +++ b/libtu/snprintf_2.2/INSTALL @@ -0,0 +1,24 @@ +HOW TO INSTALL - manually: + + 1. Read the description of macros that control the bahaviour + of the program at the beginning of the file snprintf.c, + change the definitions in snprintf.c or in Makefile if necessary. + + 2. make + + 3. move the file snprintf.o where your programs will find it + + + +HOW TO INSTALL - with autoconf: + +Contributed by Caolan McNamara : + + Though it might be overkill for snprintf I also have + an autoconf and automaked version which works out the need + for long long support and makes snprintf optionally into + a dynamic library on libtool supported platforms. + + 1. cd with_autoconf + + 2. follow instructions in the file INSTALL there. diff --git a/libtu/snprintf_2.2/LICENSE.txt b/libtu/snprintf_2.2/LICENSE.txt new file mode 100644 index 0000000..aeb06a7 --- /dev/null +++ b/libtu/snprintf_2.2/LICENSE.txt @@ -0,0 +1,121 @@ +The Frontier Artistic License Version 1.0 +Derived from the Artistic License at OpenSource.org. +Submitted to OpenSource.org for Open Source Initiative certification. + +Preamble + +The intent of this document is to state the conditions under which a +Package may be copied, such that the Copyright Holder maintains some +semblance of artistic control over the development of the package, +while giving the users of the package the right to use and distribute +the Package in a more-or-less customary fashion, plus the right to +make reasonable modifications. + +Definitions + + "Package" refers to the script, suite, file, or collection of + scripts, suites, and/or files distributed by the Copyright Holder, + and to derivatives of that Package created through textual modification. + + "Standard Version" refers to such a Package if it has not been + modified, or has been modified in accordance with the wishes of + the Copyright Holder. + + "Copyright Holder" is whoever is named in the copyright statement + or statements for the package. + + "You" is you, if you're thinking about copying or distributing + this Package. + + "Reasonable copying fee" is whatever you can justify on the basis + of media cost, duplication charges, time of people involved, and + so on. (You will not be required to justify it to the Copyright + Holder, but only to the computing community at large as a market + that must bear the fee.) + + "Freely Available" means that no fee is charged for the item + itself, though there may be fees involved in handling the item. + It also means that recipients of the item may redistribute it under + the same conditions they received it. + + +Terms + +1. You may make and give away verbatim copies of the source form of +the Standard Version of this Package without restriction, provided +that you duplicate all of the original copyright notices and +associated disclaimers. + +2. You may apply bug fixes, portability fixes, and other modifications +derived from the Public Domain or from the Copyright Holder. A Package +modified in such a way shall still be considered the Standard Version. + +3. You may otherwise modify your copy of this Package in any way, +provided that you insert a prominent notice in each changed script, +suite, or file stating how and when you changed that script, suite, +or file, and provided that you do at least ONE of the following: + + a) Use the modified Package only within your corporation or + organization, or retain the modified Package solely for personal use. + + b) Place your modifications in the Public Domain or otherwise make + them Freely Available, such as by posting said modifications to Usenet + or an equivalent medium, or placing the modifications on a major archive + site such as ftp.uu.net, or by allowing the Copyright Holder to include + your modifications in the Standard Version of the Package. + + c) Rename any non-standard executables so the names do not conflict + with standard executables, which must also be provided, and provide + a separate manual page (or equivalent) for each non-standard executable + that clearly documents how it differs from the Standard Version. + + d) Make other distribution arrangements with the Copyright Holder. + +4. You may distribute the programs of this Package in object code or +executable form, provided that you do at least ONE of the following: + + a) Distribute a Standard Version of the executables and library + files, together with instructions (in the manual page or + equivalent) on where to get the Standard Version. + + b) Accompany the distribution with the machine-readable source of + the Package with your modifications. + + c) Accompany any non-standard executables with their corresponding + Standard Version executables, give the non-standard executables + non-standard names, and clearly document the differences in manual + pages (or equivalent), together with instructions on where to get + the Standard Version. + + d) Make other distribution arrangements with the Copyright Holder. + +5. You may charge a reasonable copying fee for any distribution of +this Package. You may charge any fee you choose for support of this +Package. You may not charge a fee for this Package itself. However, +you may distribute this Package in aggregate with other (possibly +commercial) programs as part of a larger (possibly commercial) +software distribution provided that you do not advertise this Package +as a product of your own. + +6. The scripts and library files supplied as input to or produced as +output from the programs of this Package do not automatically fall +under the copyright of this Package, but belong to whomever generated +them, and may be sold commercially, and may be aggregated with this +Package. + +7. Scripts, suites, or programs supplied by you that depend on or +otherwise make use of this Package shall not be considered part of +this Package. + +8. The name of the Copyright Holder may not be used to endorse or +promote products derived from this software without specific prior +written permission. + +9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + + The End + + +http://www.spinwardstars.com/frontier/fal.html diff --git a/libtu/snprintf_2.2/Makefile.unused b/libtu/snprintf_2.2/Makefile.unused new file mode 100644 index 0000000..2e022f7 --- /dev/null +++ b/libtu/snprintf_2.2/Makefile.unused @@ -0,0 +1,43 @@ +# Make sure you include -DHAVE_SNPRINTF in CFLAGS if your system +# does have snprintf! + +# If you need (long long int) support and you sprintf supports it, +# define -DSNPRINTF_LONGLONG_SUPPORT + +CC = gcc + +CFLAGS = -DPREFER_PORTABLE_SNPRINTF -O3 \ + -Wall -Wpointer-arith -Wwrite-strings \ + -Wcast-qual -Wcast-align -Waggregate-return \ + -Wmissing-prototypes -Wmissing-declarations \ + -Wshadow -Wstrict-prototypes + +# -DNEED_ASPRINTF -DNEED_ASNPRINTF -DNEED_VASPRINTF -DNEED_VASNPRINTF +# -DNEED_SNPRINTF_ONLY + +# Digital Unix: native compiler usually produces better code than gcc +#CC = cc +#CFLAGS = -DPREFER_PORTABLE_SNPRINTF -O4 -std1 -arch host + +# Recommend to leave COMPATIBILITY empty for normal use. +# Should be set for bug compatibility when running tests +# too keep them less chatty. +COMPATIBILITY = + +#COMPATIBILITY = -DSOLARIS_BUG_COMPATIBLE +#COMPATIBILITY = -DHPUX_BUG_COMPATIBLE +#COMPATIBILITY = -DDIGITAL_UNIX_BUG_COMPATIBLE +#COMPATIBILITY = -DPERL_BUG_COMPATIBLE +#COMPATIBILITY = -DLINUX_COMPATIBLE + +.c.o: + rm -f $@ + $(CC) $(CFLAGS) $(COMPATIBILITY) -c $*.c + +all:snprintf.o Makefile + +test::snprintf.o test.c Makefile + $(CC) $(CFLAGS) $(COMPATIBILITY) snprintf.o -o $@ test.c + +clean: + /usr/bin/rm -f *.o test core diff --git a/libtu/snprintf_2.2/README b/libtu/snprintf_2.2/README new file mode 100644 index 0000000..af36f8e --- /dev/null +++ b/libtu/snprintf_2.2/README @@ -0,0 +1,283 @@ + + snprintf.c + - a portable implementation of snprintf, + including vsnprintf.c, asnprintf, vasnprintf, asprintf, vasprintf + + snprintf is a routine to convert numeric and string arguments to + formatted strings. It is similar to sprintf(3) provided in a system's + C library, yet it requires an additional argument - the buffer size - + and it guarantees never to store anything beyond the given buffer, + regardless of the format or arguments to be formatted. Some newer + operating systems do provide snprintf in their C library, but many do + not or do provide an inadequate (slow or idiosyncratic) version, which + calls for a portable implementation of this routine. + +Author + + Mark Martinec , April 1999, June 2000 + Copyright © 1999, Mark Martinec + +Terms and conditions ... + + This program is free software; you can redistribute it and/or modify + it under the terms of the Frontier Artistic License which comes with + this Kit. + +Features + + * careful adherence to specs regarding flags, field width and + precision; + * good performance for large string handling (large format, large + argument or large paddings). Performance is similar to system's + sprintf and in several cases significantly better (make sure you + compile with optimizations turned on, tell the compiler the code + is strict ANSI if necessary to give it more freedom for + optimizations); + * return value semantics per ISO/IEC 9899:1999 ("ISO C99"); + * written in standard ISO/ANSI C - requires an ANSI C compiler. + +Supported conversion specifiers and data types + + This snprintf only supports the following conversion specifiers: s, c, + d, o, u, x, X, p (and synonyms: i, D, U, O - see below) with flags: + '-', '+', ' ', '0' and '#'. An asterisk is supported for field width + as well as precision. + + Length modifiers 'h' (short int), 'l' (long int), and 'll' (long long + int) are supported. + + NOTE: + + If macro SNPRINTF_LONGLONG_SUPPORT is not defined (default) the + length modifier 'll' is recognized but treated the same as 'l', + which may cause argument value truncation! Defining + SNPRINTF_LONGLONG_SUPPORT requires that your system's sprintf also + handles length modifier 'll'. long long int is a language extension + which may not be portable. + + Conversion of numeric data (conversion specifiers d, o, u, x, X, p) + with length modifiers (none or h, l, ll) is left to the system routine + sprintf, but all handling of flags, field width and precision as well + as c and s conversions is done very carefully by this portable + routine. If a string precision (truncation) is specified (e.g. %.8s) + it is guaranteed the string beyond the specified precision will not be + referenced. + + Length modifiers h, l and ll are ignored for c and s conversions (data + types wint_t and wchar_t are not supported). + + The following common synonyms for conversion characters are supported: + * i is a synonym for d + * D is a synonym for ld, explicit length modifiers are ignored + * U is a synonym for lu, explicit length modifiers are ignored + * O is a synonym for lo, explicit length modifiers are ignored + + The D, O and U conversion characters are nonstandard, they are + supported for backward compatibility only, and should not be used for + new code. + + The following is specifically not supported: + * flag ' (thousands' grouping character) is recognized but ignored + * numeric conversion specifiers: f, e, E, g, G and synonym F, as + well as the new a and A conversion specifiers + * length modifier 'L' (long double) and 'q' (quad - use 'll' + instead) + * wide character/string conversions: lc, ls, and nonstandard + synonyms C and S + * writeback of converted string length: conversion character n + * the n$ specification for direct reference to n-th argument + * locales + + It is permitted for str_m to be zero, and it is permitted to specify + NULL pointer for resulting string argument if str_m is zero (as per + ISO C99). + + The return value is the number of characters which would be generated + for the given input, excluding the trailing null. If this value is + greater or equal to str_m, not all characters from the result have + been stored in str, output bytes beyond the (str_m-1) -th character + are discarded. If str_m is greater than zero it is guaranteed the + resulting string will be null-terminated. + + NOTE that this matches the ISO C99, OpenBSD, and GNU C library 2.1, + but is different from some older and vendor implementations, and is + also different from XPG, XSH5, SUSv2 specifications. For historical + discussion on changes in the semantics and standards of snprintf see + printf(3) man page in the Linux programmers manual. + + Routines asprintf and vasprintf return a pointer (in the ptr argument) + to a buffer sufficiently large to hold the resulting string. This + pointer should be passed to free(3) to release the allocated storage + when it is no longer needed. If sufficient space cannot be allocated, + these functions will return -1 and set ptr to be a NULL pointer. These + two routines are a GNU C library extensions (glibc). + + Routines asnprintf and vasnprintf are similar to asprintf and + vasprintf, yet, like snprintf and vsnprintf counterparts, will write + at most str_m-1 characters into the allocated output string, the last + character in the allocated buffer then gets the terminating null. If + the formatted string length (the return value) is greater than or + equal to the str_m argument, the resulting string was truncated and + some of the formatted characters were discarded. These routines + present a handy way to limit the amount of allocated memory to some + sane value. + +Availability + + http://www.ijs.si/software/snprintf/ + * snprintf_1.3.tar.gz (1999-06-30), md5 sum: snprintf_1.3.tar.gz.md5 + * snprintf_2.1.tar.gz (2000-07-14), md5 sum: snprintf_2.1.tar.gz.md5 + * snprintf_2.2.tar.gz (2000-10-18), md5 sum: snprintf_2.2.tar.gz.md5 + +Mailing list + + There is a very low-traffic mailing list snprintf-announce@ijs.si + where announcements about new versions will be posted as well as + warnings about threatening bugs if discovered. The posting is + restricted to snprintf developer(s). + + To subscribe to (or unsubscribe from) the mailing list please visit + the list server's web page + http://mailman.ijs.si/listinfo/snprintf-announce + + You can also subscribe to the list by mailing the command SUBSCRIBE + either in the subject or in the message body to the address + snprintf-announce-request@ijs.si . You will be asked for confirmation + before subscription will be effective. + + The list of members is only accessible to the list administrator, so + there is no need for concern about automatic e-mail address gatherers. + + Questions about the mailing list and concerns for the attention of a + person should be sent to snprintf-announce-admin@ijs.si + + There is no general discussion list about portable snprintf at the + moment. Please send comments and suggestion to the author. + +Revision history + + Version 1.3 fixes a runaway loop problem from 1.2. Please upgrade. + + 1999-06-30 V1.3 Mark Martinec + + + fixed runaway loop (eventually crashing when str_l wraps + beyond 2^31) while copying format string without conversion + specifiers to a buffer that is too short (thanks to Edwin + Young for spotting the problem); + + added macros PORTABLE_SNPRINTF_VERSION_(MAJOR|MINOR) to + snprintf.h + + 2000-02-14 V2.0 (never released) Mark Martinec + + + relaxed license terms: The Artistic License now applies. You + may still apply the GNU GENERAL PUBLIC LICENSE as was + distributed with previous versions, if you prefer; + + changed REVISION HISTORY dates to use ISO 8601 date format; + + added vsnprintf (patch also independently proposed by Caolán + McNamara 2000-05-04, and Keith M Willenson 2000-06-01) + + 2000-06-27 V2.1 Mark Martinec + + + removed POSIX check for str_m < 1; value 0 for str_m is + allowed by ISO C99 (and GNU C library 2.1) (pointed out on + 2000-05-04 by Caolán McNamara, caolan@ csn dot ul dot ie). + Besides relaxed license this change in standards adherence is + the main reason to bump up the major version number; + + added nonstandard routines asnprintf, vasnprintf, asprintf, + vasprintf that dynamically allocate storage for the resulting + string; these routines are not compiled by default, see + comments where NEED_V?ASN?PRINTF macros are defined; + + autoconf contributed by Caolán McNamara + + 2000-10-06 V2.2 Mark Martinec + + + BUG FIX: the %c conversion used a temporary variable that was + no longer in scope when referenced, possibly causing + incorrect resulting character; + + BUG FIX: make precision and minimal field width unsigned to + handle huge values (2^31 <= n < 2^32) correctly; also be more + careful in the use of signed/unsigned/size_t internal + variables -- probably more careful than many vendor + implementations, but there may still be a case where huge + values of str_m, precision or minimal field could cause + incorrect behaviour; + + use separate variables for signed/unsigned arguments, and for + short/int, long, and long long argument lengths to avoid + possible incompatibilities on certain computer architectures. + Also use separate variable arg_sign to hold sign of a numeric + argument, to make code more transparent; + + some fiddling with zero padding and "0x" to make it Linux + compatible; + + systematically use macros fast_memcpy and fast_memset instead + of case-by-case hand optimization; determine some breakeven + string lengths for different architectures; + + terminology change: format -> conversion specifier, C9x -> + ISO/IEC 9899:1999 ("ISO C99"), alternative form -> alternate + form, data type modifier -> length modifier; + + several comments rephrased and new ones added; + + make compiler not complain about 'credits' defined but not + used; + +Other implementations of snprintf + + I am aware of some other (more or less) portable implementations of + snprintf. I do not claim they are free software - please refer to + their respective copyright and licensing terms. If you know of other + versions please let me know. + * a very thorough implementation (src/util_snprintf.c) by the Apache + Group distributed with the Apache web server - + http://www.apache.org/ . Does its own floating point conversions + using routines ecvt(3), fcvt(3) and gcvt(3) from the standard C + library or from the GNU libc. + This is from the code: + + This software [...] was originally based on public domain software + written at the National Center for Supercomputing Applications, + University of Illinois, Urbana-Champaign. + [...] This code is based on, and used with the permission of, the + SIO stdio-replacement strx_* functions by Panos Tsirigotis + for xinetd. + * QCI Utilities use a modified version of snprintf from the Apache + group. + * implementations as distributed with OpenBSD, FreeBSD, and NetBSD + are all wrappers to vfprintf.c, which is derived from software + contributed to Berkeley by Chris Torek. + * implementation from Prof. Patrick Powell , + Dept. Electrical and Computer Engineering, San Diego State + University, San Diego, CA 92182-1309, published in Bugtraq + archives for 3rd quarter (Jul-Aug) 1995. No floating point + conversions. + * Brandon Long's modified version of Prof. + Patrick Powell's snprintf with contributions from others. With + minimal floating point support. + * implementation (src/snprintf.c) as distributed with sendmail - + http://www.sendmail.org/ is a cleaned up Prof. Patrick Powell's + version to compile properly and to support .precision and %lx. + * implementation from Caolán McNamara available at + http://www.csn.ul.ie/~caolan/publink/snprintf-1.1.tar.gz, handles + floating point. + * implementation used by newlog (a replacement for syslog(3)) made + available by the SOS Corporation. Enabling floating point support + is a compile-time option. + * implementation by Michael Richardson is + available at http://sandelman.ottawa.on.ca/SSW/snp/snp.html. It is + based on BSD44-lite's vfprintf() call, modified to function on + SunOS. Needs internal routines from the 4.4 strtod (included), + requires GCC to compile the long long (aka quad_t) portions. + * implementation from Tomi Salo distributed with SSH + 2.0 Unix Server. Not in public domain. Floating point conversions + done by system's sprintf. + * and for completeness: my portable version described in this very + document available at http://www.ijs.si/software/snprintf/ . + + In retrospect, it appears that a lot of effort was wasted by many + people for not being aware of what others are doing. Sigh. + + Also of interest: The Approved Base Working Group Resolution for XSH5, + Ref: bwg98-006, Topic: snprintf. + _________________________________________________________________ + + mm + Last updated: 2000-10-18 + + Valid HTML 4.0! diff --git a/libtu/snprintf_2.2/README.html b/libtu/snprintf_2.2/README.html new file mode 100644 index 0000000..4ecaab3 --- /dev/null +++ b/libtu/snprintf_2.2/README.html @@ -0,0 +1,382 @@ + + + + + +snprintf.c - a portable implementation of snprintf +(including vsnprintf.c, asnprintf, vasnprintf, asprintf, vasprintf) + + + + + + + + + +

    snprintf.c +
    - a portable implementation of snprintf, +
    including +vsnprintf.c, asnprintf, vasnprintf, asprintf, vasprintf +

    + +

    snprintf is a routine to convert numeric and string arguments +to formatted strings. It is similar to sprintf(3) provided in a +system's C library, yet it requires an additional argument - the buffer +size - and it guarantees never to store anything beyond the given buffer, +regardless of the format or arguments to be formatted. Some newer +operating systems do provide snprintf in their C library, +but many do not or do provide an inadequate (slow or idiosyncratic) +version, which calls for a portable implementation of this routine. + +

    Author

    + +

    Mark Martinec +<mark.martinec@ijs.si>, +April 1999, June 2000 +
    Copyright © 1999, Mark Martinec + +

    Terms and conditions ...

    + +

    This program is free software; you can redistribute it +and/or modify it under the terms of the +Frontier Artistic License +which comes with this Kit. + +

    Features

    + +
      +
    • careful adherence to specs regarding flags, field width and precision; +
    • good performance for large string handling (large format, large argument +or large paddings). Performance is similar to system's sprintf +and in several cases significantly better (make sure you compile with +optimizations turned on, tell the compiler the code is strict ANSI +if necessary to give it more freedom for optimizations); +
    • return value semantics per ISO/IEC 9899:1999 ("ISO C99"); +
    • written in standard ISO/ANSI C - requires an ANSI C compiler. +
    + +

    Supported conversion specifiers and data types

    + +

    This snprintf only supports the following conversion specifiers: +s, c, d, o, u, x, X, p (and synonyms: i, D, U, O - see below) +with flags: '-', '+', ' ', '0' and '#'. +An asterisk is supported for field width as well as precision. + +

    Length modifiers 'h' (short int), 'l' (long int), +and 'll' (long long int) are supported. + +

    NOTE: +

    +If macro SNPRINTF_LONGLONG_SUPPORT is not defined (default) +the length modifier 'll' is recognized but treated the same as 'l', +which may cause argument value truncation! +Defining SNPRINTF_LONGLONG_SUPPORT requires that your system's +sprintf also handles length modifier 'll'. +long long int is a language extension which may not be portable. +
    + +

    Conversion of numeric data (conversion specifiers d, o, u, x, X, p) +with length modifiers (none or h, l, ll) is left to the system +routine sprintf, but all handling of flags, field width and precision +as well as c and s conversions is done very carefully by this portable routine. +If a string precision (truncation) is specified (e.g. %.8s) it is +guaranteed the string beyond the specified precision will not be referenced. + +

    Length modifiers h, l and ll are ignored for c and s conversions +(data types wint_t and wchar_t are not supported). + +

    The following common synonyms for conversion characters are supported: +

      +
    • i is a synonym for d +
    • D is a synonym for ld, explicit length modifiers are ignored +
    • U is a synonym for lu, explicit length modifiers are ignored +
    • O is a synonym for lo, explicit length modifiers are ignored +
    +The D, O and U conversion characters are nonstandard, they are supported +for backward compatibility only, and should not be used for new code. + +

    The following is specifically not supported: +

      +
    • flag ' (thousands' grouping character) is recognized but ignored +
    • numeric conversion specifiers: f, e, E, g, G and synonym F, +as well as the new a and A conversion specifiers +
    • length modifier 'L' (long double) +and 'q' (quad - use 'll' instead) +
    • wide character/string conversions: lc, ls, and nonstandard +synonyms C and S +
    • writeback of converted string length: conversion character n +
    • the n$ specification for direct reference to n-th argument +
    • locales +
    + +

    It is permitted for str_m to be zero, and it is permitted to specify NULL +pointer for resulting string argument if str_m is zero (as per ISO C99). + +

    The return value is the number of characters which would be generated +for the given input, excluding the trailing null. If this value +is greater or equal to str_m, not all characters from the result +have been stored in str, output bytes beyond the (str_m-1) -th character +are discarded. If str_m is greater than zero it is guaranteed +the resulting string will be null-terminated. + +

    NOTE that this matches the ISO C99, OpenBSD, and GNU C library 2.1, +but is different from some older and vendor implementations, +and is also different from XPG, XSH5, SUSv2 specifications. +For historical discussion on changes in the semantics and standards +of snprintf see printf(3) man page in the Linux programmers manual. + +

    Routines asprintf and vasprintf return a pointer (in the ptr argument) +to a buffer sufficiently large to hold the resulting string. This pointer +should be passed to free(3) to release the allocated storage when it is +no longer needed. If sufficient space cannot be allocated, these functions +will return -1 and set ptr to be a NULL pointer. These two routines are a +GNU C library extensions (glibc). + +

    Routines asnprintf and vasnprintf are similar to asprintf and vasprintf, +yet, like snprintf and vsnprintf counterparts, will write at most str_m-1 +characters into the allocated output string, the last character in the +allocated buffer then gets the terminating null. If the formatted string +length (the return value) is greater than or equal to the str_m argument, +the resulting string was truncated and some of the formatted characters +were discarded. These routines present a handy way to limit the amount +of allocated memory to some sane value. + +

    Availability

    + +

    http://www.ijs.si/software/snprintf/ + +

    + + +

    Mailing list

    + +

    There is a very low-traffic mailing list snprintf-announce@ijs.si +where announcements about new versions will be posted +as well as warnings about threatening bugs if discovered. +The posting is restricted to snprintf developer(s). + +

    To subscribe to (or unsubscribe from) the mailing list +please visit the list server's web page +http://mailman.ijs.si/listinfo/snprintf-announce + +

    You can also subscribe to the list by mailing +the command SUBSCRIBE either in the subject or in the message body +to the address snprintf-announce-request@ijs.si . You will be asked for +confirmation before subscription will be effective. + +

    The list of members is only accessible to the list administrator, +so there is no need for concern about automatic e-mail address gatherers. + +

    Questions about the mailing list and concerns for the attention +of a person should be sent to snprintf-announce-admin@ijs.si + +

    There is no general discussion list about portable snprintf +at the moment. Please send comments and suggestion to the author. + + +

    Revision history

    + +

    Version 1.3 fixes a runaway loop problem from 1.2. Please upgrade. + +

    +
    1999-06-30 V1.3 Mark Martinec <mark.martinec@ijs.si> +
      +
    • fixed runaway loop (eventually crashing when str_l wraps + beyond 2^31) while copying format string without + conversion specifiers to a buffer that is too short + (thanks to Edwin Young <edwiny@autonomy.com> for spotting the problem); +
    • added macros PORTABLE_SNPRINTF_VERSION_(MAJOR|MINOR) to snprintf.h +
    + +
    2000-02-14 V2.0 (never released) Mark Martinec <mark.martinec@ijs.si> +
      +
    • relaxed license terms: + The Artistic License now applies. + You may still apply the GNU GENERAL PUBLIC LICENSE + as was distributed with previous versions, if you prefer; +
    • changed REVISION HISTORY dates to use + ISO 8601 + date format; +
    • added vsnprintf (patch also independently proposed by + Caolán McNamara 2000-05-04, and Keith M Willenson 2000-06-01) +
    + +
    2000-06-27 V2.1 Mark Martinec <mark.martinec@ijs.si> +
      +
    • removed POSIX check for str_m < 1; value 0 for str_m is + allowed by ISO C99 (and GNU C library 2.1) (pointed out + on 2000-05-04 by Caolán McNamara, caolan@ csn dot ul dot ie). + Besides relaxed license this change in standards adherence + is the main reason to bump up the major version number; +
    • added nonstandard routines asnprintf, vasnprintf, asprintf, + vasprintf that dynamically allocate storage for the + resulting string; these routines are not compiled by default, + see comments where NEED_V?ASN?PRINTF macros are defined; +
    • autoconf contributed by Caolán McNamara +
    + +
    2000-10-06 V2.2 Mark Martinec <mark.martinec@ijs.si> +
      +
    • BUG FIX: the %c conversion used a temporary variable + that was no longer in scope when referenced, + possibly causing incorrect resulting character; +
    • BUG FIX: make precision and minimal field width unsigned + to handle huge values (2^31 <= n < 2^32) correctly; + also be more careful in the use of signed/unsigned/size_t + internal variables -- probably more careful than many + vendor implementations, but there may still be a case + where huge values of str_m, precision or minimal field + could cause incorrect behaviour; +
    • use separate variables for signed/unsigned arguments, + and for short/int, long, and long long argument lengths + to avoid possible incompatibilities on certain + computer architectures. Also use separate variable + arg_sign to hold sign of a numeric argument, + to make code more transparent; +
    • some fiddling with zero padding and "0x" to make it + Linux compatible; +
    • systematically use macros fast_memcpy and fast_memset + instead of case-by-case hand optimization; determine some + breakeven string lengths for different architectures; +
    • terminology change: format -> conversion specifier, + C9x -> ISO/IEC 9899:1999 ("ISO C99"), + alternative form -> alternate form, + data type modifier -> length modifier; +
    • several comments rephrased and new ones added; +
    • make compiler not complain about 'credits' defined but + not used; +
    +
    + +

    Other implementations of snprintf

    + +

    I am aware of some other (more or less) portable implementations +of snprintf. I do not claim they are free software - please refer +to their respective copyright and licensing terms. +If you know of other versions please let +me know. + +

    + +In retrospect, it appears that a lot of effort was wasted by many +people for not being aware of what others are doing. Sigh. + +

    Also of interest: +The Approved Base Working Group Resolution for XSH5, +Ref: bwg98-006, Topic: snprintf. + +


    +mm +
    Last updated: 2000-10-18 + +

    Valid HTML 4.0! + + diff --git a/libtu/snprintf_2.2/snprintf-orig.c b/libtu/snprintf_2.2/snprintf-orig.c new file mode 100644 index 0000000..86f42e9 --- /dev/null +++ b/libtu/snprintf_2.2/snprintf-orig.c @@ -0,0 +1,1025 @@ +/* + * snprintf.c - a portable implementation of snprintf + * + * AUTHOR + * Mark Martinec , April 1999. + * + * Copyright 1999, Mark Martinec. All rights reserved. + * + * TERMS AND CONDITIONS + * This program is free software; you can redistribute it and/or modify + * it under the terms of the "Frontier Artistic License" which comes + * with this Kit. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Frontier Artistic License for more details. + * + * You should have received a copy of the Frontier Artistic License + * with this Kit in the file named LICENSE.txt . + * If not, I'll be glad to provide one. + * + * FEATURES + * - careful adherence to specs regarding flags, field width and precision; + * - good performance for large string handling (large format, large + * argument or large paddings). Performance is similar to system's sprintf + * and in several cases significantly better (make sure you compile with + * optimizations turned on, tell the compiler the code is strict ANSI + * if necessary to give it more freedom for optimizations); + * - return value semantics per ISO/IEC 9899:1999 ("ISO C99"); + * - written in standard ISO/ANSI C - requires an ANSI C compiler. + * + * SUPPORTED CONVERSION SPECIFIERS AND DATA TYPES + * + * This snprintf only supports the following conversion specifiers: + * s, c, d, u, o, x, X, p (and synonyms: i, D, U, O - see below) + * with flags: '-', '+', ' ', '0' and '#'. + * An asterisk is supported for field width as well as precision. + * + * Length modifiers 'h' (short int), 'l' (long int), + * and 'll' (long long int) are supported. + * NOTE: + * If macro SNPRINTF_LONGLONG_SUPPORT is not defined (default) the + * length modifier 'll' is recognized but treated the same as 'l', + * which may cause argument value truncation! Defining + * SNPRINTF_LONGLONG_SUPPORT requires that your system's sprintf also + * handles length modifier 'll'. long long int is a language extension + * which may not be portable. + * + * Conversion of numeric data (conversion specifiers d, u, o, x, X, p) + * with length modifiers (none or h, l, ll) is left to the system routine + * sprintf, but all handling of flags, field width and precision as well as + * c and s conversions is done very carefully by this portable routine. + * If a string precision (truncation) is specified (e.g. %.8s) it is + * guaranteed the string beyond the specified precision will not be referenced. + * + * Length modifiers h, l and ll are ignored for c and s conversions (data + * types wint_t and wchar_t are not supported). + * + * The following common synonyms for conversion characters are supported: + * - i is a synonym for d + * - D is a synonym for ld, explicit length modifiers are ignored + * - U is a synonym for lu, explicit length modifiers are ignored + * - O is a synonym for lo, explicit length modifiers are ignored + * The D, O and U conversion characters are nonstandard, they are supported + * for backward compatibility only, and should not be used for new code. + * + * The following is specifically NOT supported: + * - flag ' (thousands' grouping character) is recognized but ignored + * - numeric conversion specifiers: f, e, E, g, G and synonym F, + * as well as the new a and A conversion specifiers + * - length modifier 'L' (long double) and 'q' (quad - use 'll' instead) + * - wide character/string conversions: lc, ls, and nonstandard + * synonyms C and S + * - writeback of converted string length: conversion character n + * - the n$ specification for direct reference to n-th argument + * - locales + * + * It is permitted for str_m to be zero, and it is permitted to specify NULL + * pointer for resulting string argument if str_m is zero (as per ISO C99). + * + * The return value is the number of characters which would be generated + * for the given input, excluding the trailing null. If this value + * is greater or equal to str_m, not all characters from the result + * have been stored in str, output bytes beyond the (str_m-1) -th character + * are discarded. If str_m is greater than zero it is guaranteed + * the resulting string will be null-terminated. + * + * NOTE that this matches the ISO C99, OpenBSD, and GNU C library 2.1, + * but is different from some older and vendor implementations, + * and is also different from XPG, XSH5, SUSv2 specifications. + * For historical discussion on changes in the semantics and standards + * of snprintf see printf(3) man page in the Linux programmers manual. + * + * Routines asprintf and vasprintf return a pointer (in the ptr argument) + * to a buffer sufficiently large to hold the resulting string. This pointer + * should be passed to free(3) to release the allocated storage when it is + * no longer needed. If sufficient space cannot be allocated, these functions + * will return -1 and set ptr to be a NULL pointer. These two routines are a + * GNU C library extensions (glibc). + * + * Routines asnprintf and vasnprintf are similar to asprintf and vasprintf, + * yet, like snprintf and vsnprintf counterparts, will write at most str_m-1 + * characters into the allocated output string, the last character in the + * allocated buffer then gets the terminating null. If the formatted string + * length (the return value) is greater than or equal to the str_m argument, + * the resulting string was truncated and some of the formatted characters + * were discarded. These routines present a handy way to limit the amount + * of allocated memory to some sane value. + * + * AVAILABILITY + * http://www.ijs.si/software/snprintf/ + * + * REVISION HISTORY + * 1999-04 V0.9 Mark Martinec + * - initial version, some modifications after comparing printf + * man pages for Digital Unix 4.0, Solaris 2.6 and HPUX 10, + * and checking how Perl handles sprintf (differently!); + * 1999-04-09 V1.0 Mark Martinec + * - added main test program, fixed remaining inconsistencies, + * added optional (long long int) support; + * 1999-04-12 V1.1 Mark Martinec + * - support the 'p' conversion (pointer to void); + * - if a string precision is specified + * make sure the string beyond the specified precision + * will not be referenced (e.g. by strlen); + * 1999-04-13 V1.2 Mark Martinec + * - support synonyms %D=%ld, %U=%lu, %O=%lo; + * - speed up the case of long format string with few conversions; + * 1999-06-30 V1.3 Mark Martinec + * - fixed runaway loop (eventually crashing when str_l wraps + * beyond 2^31) while copying format string without + * conversion specifiers to a buffer that is too short + * (thanks to Edwin Young for + * spotting the problem); + * - added macros PORTABLE_SNPRINTF_VERSION_(MAJOR|MINOR) + * to snprintf.h + * 2000-02-14 V2.0 (never released) Mark Martinec + * - relaxed license terms: The Artistic License now applies. + * You may still apply the GNU GENERAL PUBLIC LICENSE + * as was distributed with previous versions, if you prefer; + * - changed REVISION HISTORY dates to use ISO 8601 date format; + * - added vsnprintf (patch also independently proposed by + * Caolan McNamara 2000-05-04, and Keith M Willenson 2000-06-01) + * 2000-06-27 V2.1 Mark Martinec + * - removed POSIX check for str_m<1; value 0 for str_m is + * allowed by ISO C99 (and GNU C library 2.1) - (pointed out + * on 2000-05-04 by Caolan McNamara, caolan@ csn dot ul dot ie). + * Besides relaxed license this change in standards adherence + * is the main reason to bump up the major version number; + * - added nonstandard routines asnprintf, vasnprintf, asprintf, + * vasprintf that dynamically allocate storage for the + * resulting string; these routines are not compiled by default, + * see comments where NEED_V?ASN?PRINTF macros are defined; + * - autoconf contributed by Caolan McNamara + * 2000-10-06 V2.2 Mark Martinec + * - BUG FIX: the %c conversion used a temporary variable + * that was no longer in scope when referenced, + * possibly causing incorrect resulting character; + * - BUG FIX: make precision and minimal field width unsigned + * to handle huge values (2^31 <= n < 2^32) correctly; + * also be more careful in the use of signed/unsigned/size_t + * internal variables - probably more careful than many + * vendor implementations, but there may still be a case + * where huge values of str_m, precision or minimal field + * could cause incorrect behaviour; + * - use separate variables for signed/unsigned arguments, + * and for short/int, long, and long long argument lengths + * to avoid possible incompatibilities on certain + * computer architectures. Also use separate variable + * arg_sign to hold sign of a numeric argument, + * to make code more transparent; + * - some fiddling with zero padding and "0x" to make it + * Linux compatible; + * - systematically use macros fast_memcpy and fast_memset + * instead of case-by-case hand optimization; determine some + * breakeven string lengths for different architectures; + * - terminology change: 'format' -> 'conversion specifier', + * 'C9x' -> 'ISO/IEC 9899:1999 ("ISO C99")', + * 'alternative form' -> 'alternate form', + * 'data type modifier' -> 'length modifier'; + * - several comments rephrased and new ones added; + * - make compiler not complain about 'credits' defined but + * not used; + */ + + +/* Define HAVE_SNPRINTF if your system already has snprintf and vsnprintf. + * + * If HAVE_SNPRINTF is defined this module will not produce code for + * snprintf and vsnprintf, unless PREFER_PORTABLE_SNPRINTF is defined as well, + * causing this portable version of snprintf to be called portable_snprintf + * (and portable_vsnprintf). + */ +/* #define HAVE_SNPRINTF */ + +/* Define PREFER_PORTABLE_SNPRINTF if your system does have snprintf and + * vsnprintf but you would prefer to use the portable routine(s) instead. + * In this case the portable routine is declared as portable_snprintf + * (and portable_vsnprintf) and a macro 'snprintf' (and 'vsnprintf') + * is defined to expand to 'portable_v?snprintf' - see file snprintf.h . + * Defining this macro is only useful if HAVE_SNPRINTF is also defined, + * but does does no harm if defined nevertheless. + */ +/* #define PREFER_PORTABLE_SNPRINTF */ + +/* Define SNPRINTF_LONGLONG_SUPPORT if you want to support + * data type (long long int) and length modifier 'll' (e.g. %lld). + * If undefined, 'll' is recognized but treated as a single 'l'. + * + * If the system's sprintf does not handle 'll' + * the SNPRINTF_LONGLONG_SUPPORT must not be defined! + * + * This is off by default as (long long int) is a language extension. + */ +/* #define SNPRINTF_LONGLONG_SUPPORT */ + +/* Define NEED_SNPRINTF_ONLY if you only need snprintf, and not vsnprintf. + * If NEED_SNPRINTF_ONLY is defined, the snprintf will be defined directly, + * otherwise both snprintf and vsnprintf routines will be defined + * and snprintf will be a simple wrapper around vsnprintf, at the expense + * of an extra procedure call. + */ +/* #define NEED_SNPRINTF_ONLY */ + +/* Define NEED_V?ASN?PRINTF macros if you need library extension + * routines asprintf, vasprintf, asnprintf, vasnprintf respectively, + * and your system library does not provide them. They are all small + * wrapper routines around portable_vsnprintf. Defining any of the four + * NEED_V?ASN?PRINTF macros automatically turns off NEED_SNPRINTF_ONLY + * and turns on PREFER_PORTABLE_SNPRINTF. + * + * Watch for name conflicts with the system library if these routines + * are already present there. + * + * NOTE: vasprintf and vasnprintf routines need va_copy() from stdarg.h, as + * specified by C99, to be able to traverse the same list of arguments twice. + * I don't know of any other standard and portable way of achieving the same. + * With some versions of gcc you may use __va_copy(). You might even get away + * with "ap2 = ap", in this case you must not call va_end(ap2) ! + * #define va_copy(ap2,ap) ap2 = ap + */ +/* #define NEED_ASPRINTF */ +/* #define NEED_ASNPRINTF */ +/* #define NEED_VASPRINTF */ +/* #define NEED_VASNPRINTF */ + + +/* Define the following macros if desired: + * SOLARIS_COMPATIBLE, SOLARIS_BUG_COMPATIBLE, + * HPUX_COMPATIBLE, HPUX_BUG_COMPATIBLE, LINUX_COMPATIBLE, + * DIGITAL_UNIX_COMPATIBLE, DIGITAL_UNIX_BUG_COMPATIBLE, + * PERL_COMPATIBLE, PERL_BUG_COMPATIBLE, + * + * - For portable applications it is best not to rely on peculiarities + * of a given implementation so it may be best not to define any + * of the macros that select compatibility and to avoid features + * that vary among the systems. + * + * - Selecting compatibility with more than one operating system + * is not strictly forbidden but is not recommended. + * + * - 'x'_BUG_COMPATIBLE implies 'x'_COMPATIBLE . + * + * - 'x'_COMPATIBLE refers to (and enables) a behaviour that is + * documented in a sprintf man page on a given operating system + * and actually adhered to by the system's sprintf (but not on + * most other operating systems). It may also refer to and enable + * a behaviour that is declared 'undefined' or 'implementation specific' + * in the man page but a given implementation behaves predictably + * in a certain way. + * + * - 'x'_BUG_COMPATIBLE refers to (and enables) a behaviour of system's sprintf + * that contradicts the sprintf man page on the same operating system. + * + * - I do not claim that the 'x'_COMPATIBLE and 'x'_BUG_COMPATIBLE + * conditionals take into account all idiosyncrasies of a particular + * implementation, there may be other incompatibilities. + */ + + + +/* ============================================= */ +/* NO USER SERVICABLE PARTS FOLLOWING THIS POINT */ +/* ============================================= */ + +#define PORTABLE_SNPRINTF_VERSION_MAJOR 2 +#define PORTABLE_SNPRINTF_VERSION_MINOR 2 + +#if defined(NEED_ASPRINTF) || defined(NEED_ASNPRINTF) || defined(NEED_VASPRINTF) || defined(NEED_VASNPRINTF) +# if defined(NEED_SNPRINTF_ONLY) +# undef NEED_SNPRINTF_ONLY +# endif +# if !defined(PREFER_PORTABLE_SNPRINTF) +# define PREFER_PORTABLE_SNPRINTF +# endif +#endif + +#if defined(SOLARIS_BUG_COMPATIBLE) && !defined(SOLARIS_COMPATIBLE) +#define SOLARIS_COMPATIBLE +#endif + +#if defined(HPUX_BUG_COMPATIBLE) && !defined(HPUX_COMPATIBLE) +#define HPUX_COMPATIBLE +#endif + +#if defined(DIGITAL_UNIX_BUG_COMPATIBLE) && !defined(DIGITAL_UNIX_COMPATIBLE) +#define DIGITAL_UNIX_COMPATIBLE +#endif + +#if defined(PERL_BUG_COMPATIBLE) && !defined(PERL_COMPATIBLE) +#define PERL_COMPATIBLE +#endif + +#if defined(LINUX_BUG_COMPATIBLE) && !defined(LINUX_COMPATIBLE) +#define LINUX_COMPATIBLE +#endif + +#include +#include +#include +#include +#include +#include +#include + +#ifdef isdigit +#undef isdigit +#endif +#define isdigit(c) ((c) >= '0' && (c) <= '9') + +/* For copying strings longer or equal to 'breakeven_point' + * it is more efficient to call memcpy() than to do it inline. + * The value depends mostly on the processor architecture, + * but also on the compiler and its optimization capabilities. + * The value is not critical, some small value greater than zero + * will be just fine if you don't care to squeeze every drop + * of performance out of the code. + * + * Small values favor memcpy, large values favor inline code. + */ +#if defined(__alpha__) || defined(__alpha) +# define breakeven_point 2 /* AXP (DEC Alpha) - gcc or cc or egcs */ +#endif +#if defined(__i386__) || defined(__i386) +# define breakeven_point 12 /* Intel Pentium/Linux - gcc 2.96 */ +#endif +#if defined(__hppa) +# define breakeven_point 10 /* HP-PA - gcc */ +#endif +#if defined(__sparc__) || defined(__sparc) +# define breakeven_point 33 /* Sun Sparc 5 - gcc 2.8.1 */ +#endif + +/* some other values of possible interest: */ +/* #define breakeven_point 8 */ /* VAX 4000 - vaxc */ +/* #define breakeven_point 19 */ /* VAX 4000 - gcc 2.7.0 */ + +#ifndef breakeven_point +# define breakeven_point 6 /* some reasonable one-size-fits-all value */ +#endif + +#define fast_memcpy(d,s,n) \ + { register size_t nn = (size_t)(n); \ + if (nn >= breakeven_point) memcpy((d), (s), nn); \ + else if (nn > 0) { /* proc call overhead is worth only for large strings*/\ + register char *dd; register const char *ss; \ + for (ss=(s), dd=(d); nn>0; nn--) *dd++ = *ss++; } } + +#define fast_memset(d,c,n) \ + { register size_t nn = (size_t)(n); \ + if (nn >= breakeven_point) memset((d), (int)(c), nn); \ + else if (nn > 0) { /* proc call overhead is worth only for large strings*/\ + register char *dd; register const int cc=(int)(c); \ + for (dd=(d); nn>0; nn--) *dd++ = cc; } } + +/* prototypes */ + +#if defined(NEED_ASPRINTF) +int asprintf (char **ptr, const char *fmt, /*args*/ ...); +#endif +#if defined(NEED_VASPRINTF) +int vasprintf (char **ptr, const char *fmt, va_list ap); +#endif +#if defined(NEED_ASNPRINTF) +int asnprintf (char **ptr, size_t str_m, const char *fmt, /*args*/ ...); +#endif +#if defined(NEED_VASNPRINTF) +int vasnprintf (char **ptr, size_t str_m, const char *fmt, va_list ap); +#endif + +#if defined(HAVE_SNPRINTF) +/* declare our portable snprintf routine under name portable_snprintf */ +/* declare our portable vsnprintf routine under name portable_vsnprintf */ +#else +/* declare our portable routines under names snprintf and vsnprintf */ +#define portable_snprintf snprintf +#if !defined(NEED_SNPRINTF_ONLY) +#define portable_vsnprintf vsnprintf +#endif +#endif + +#if !defined(HAVE_SNPRINTF) || defined(PREFER_PORTABLE_SNPRINTF) +int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...); +#if !defined(NEED_SNPRINTF_ONLY) +int portable_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap); +#endif +#endif + +/* declarations */ + +static char credits[] = "\n\ +@(#)snprintf.c, v2.2: Mark Martinec, \n\ +@(#)snprintf.c, v2.2: Copyright 1999, Mark Martinec. Frontier Artistic License applies.\n\ +@(#)snprintf.c, v2.2: http://www.ijs.si/software/snprintf/\n"; + +#if defined(NEED_ASPRINTF) +int asprintf(char **ptr, const char *fmt, /*args*/ ...) { + va_list ap; + size_t str_m; + int str_l; + + *ptr = NULL; + va_start(ap, fmt); /* measure the required size */ + str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap); + va_end(ap); + assert(str_l >= 0); /* possible integer overflow if str_m > INT_MAX */ + *ptr = (char *) malloc(str_m = (size_t)str_l + 1); + if (*ptr == NULL) { errno = ENOMEM; str_l = -1; } + else { + int str_l2; + va_start(ap, fmt); + str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap); + va_end(ap); + assert(str_l2 == str_l); + } + return str_l; +} +#endif + +#if defined(NEED_VASPRINTF) +int vasprintf(char **ptr, const char *fmt, va_list ap) { + size_t str_m; + int str_l; + + *ptr = NULL; + { va_list ap2; + va_copy(ap2, ap); /* don't consume the original ap, we'll need it again */ + str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap2);/*get required size*/ + va_end(ap2); + } + assert(str_l >= 0); /* possible integer overflow if str_m > INT_MAX */ + *ptr = (char *) malloc(str_m = (size_t)str_l + 1); + if (*ptr == NULL) { errno = ENOMEM; str_l = -1; } + else { + int str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap); + assert(str_l2 == str_l); + } + return str_l; +} +#endif + +#if defined(NEED_ASNPRINTF) +int asnprintf (char **ptr, size_t str_m, const char *fmt, /*args*/ ...) { + va_list ap; + int str_l; + + *ptr = NULL; + va_start(ap, fmt); /* measure the required size */ + str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap); + va_end(ap); + assert(str_l >= 0); /* possible integer overflow if str_m > INT_MAX */ + if ((size_t)str_l + 1 < str_m) str_m = (size_t)str_l + 1; /* truncate */ + /* if str_m is 0, no buffer is allocated, just set *ptr to NULL */ + if (str_m == 0) { /* not interested in resulting string, just return size */ + } else { + *ptr = (char *) malloc(str_m); + if (*ptr == NULL) { errno = ENOMEM; str_l = -1; } + else { + int str_l2; + va_start(ap, fmt); + str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap); + va_end(ap); + assert(str_l2 == str_l); + } + } + return str_l; +} +#endif + +#if defined(NEED_VASNPRINTF) +int vasnprintf (char **ptr, size_t str_m, const char *fmt, va_list ap) { + int str_l; + + *ptr = NULL; + { va_list ap2; + va_copy(ap2, ap); /* don't consume the original ap, we'll need it again */ + str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap2);/*get required size*/ + va_end(ap2); + } + assert(str_l >= 0); /* possible integer overflow if str_m > INT_MAX */ + if ((size_t)str_l + 1 < str_m) str_m = (size_t)str_l + 1; /* truncate */ + /* if str_m is 0, no buffer is allocated, just set *ptr to NULL */ + if (str_m == 0) { /* not interested in resulting string, just return size */ + } else { + *ptr = (char *) malloc(str_m); + if (*ptr == NULL) { errno = ENOMEM; str_l = -1; } + else { + int str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap); + assert(str_l2 == str_l); + } + } + return str_l; +} +#endif + +/* + * If the system does have snprintf and the portable routine is not + * specifically required, this module produces no code for snprintf/vsnprintf. + */ +#if !defined(HAVE_SNPRINTF) || defined(PREFER_PORTABLE_SNPRINTF) + +#if !defined(NEED_SNPRINTF_ONLY) +int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...) { + va_list ap; + int str_l; + + va_start(ap, fmt); + str_l = portable_vsnprintf(str, str_m, fmt, ap); + va_end(ap); + return str_l; +} +#endif + +#if defined(NEED_SNPRINTF_ONLY) +int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...) { +#else +int portable_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap) { +#endif + +#if defined(NEED_SNPRINTF_ONLY) + va_list ap; +#endif + size_t str_l = 0; + const char *p = fmt; + +/* In contrast with POSIX, the ISO C99 now says + * that str can be NULL and str_m can be 0. + * This is more useful than the old: if (str_m < 1) return -1; */ + +#if defined(NEED_SNPRINTF_ONLY) + va_start(ap, fmt); +#endif + if (!p) p = ""; + while (*p) { + if (*p != '%') { + /* if (str_l < str_m) str[str_l++] = *p++; -- this would be sufficient */ + /* but the following code achieves better performance for cases + * where format string is long and contains few conversions */ + const char *q = strchr(p+1,'%'); + size_t n = !q ? strlen(p) : (q-p); + if (str_l < str_m) { + size_t avail = str_m-str_l; + fast_memcpy(str+str_l, p, (n>avail?avail:n)); + } + p += n; str_l += n; + } else { + const char *starting_p; + size_t min_field_width = 0, precision = 0; + int zero_padding = 0, precision_specified = 0, justify_left = 0; + int alternate_form = 0, force_sign = 0; + int space_for_positive = 1; /* If both the ' ' and '+' flags appear, + the ' ' flag should be ignored. */ + char length_modifier = '\0'; /* allowed values: \0, h, l, L */ + char tmp[32];/* temporary buffer for simple numeric->string conversion */ + + const char *str_arg; /* string address in case of string argument */ + size_t str_arg_l; /* natural field width of arg without padding + and sign */ + unsigned char uchar_arg; + /* unsigned char argument value - only defined for c conversion. + N.B. standard explicitly states the char argument for + the c conversion is unsigned */ + + size_t number_of_zeros_to_pad = 0; + /* number of zeros to be inserted for numeric conversions + as required by the precision or minimal field width */ + + size_t zero_padding_insertion_ind = 0; + /* index into tmp where zero padding is to be inserted */ + + char fmt_spec = '\0'; + /* current conversion specifier character */ + + str_arg = credits;/* just to make compiler happy (defined but not used)*/ + str_arg = NULL; + starting_p = p; p++; /* skip '%' */ + /* parse flags */ + while (*p == '0' || *p == '-' || *p == '+' || + *p == ' ' || *p == '#' || *p == '\'') { + switch (*p) { + case '0': zero_padding = 1; break; + case '-': justify_left = 1; break; + case '+': force_sign = 1; space_for_positive = 0; break; + case ' ': force_sign = 1; + /* If both the ' ' and '+' flags appear, the ' ' flag should be ignored */ +#ifdef PERL_COMPATIBLE + /* ... but in Perl the last of ' ' and '+' applies */ + space_for_positive = 1; +#endif + break; + case '#': alternate_form = 1; break; + case '\'': break; + } + p++; + } + /* If the '0' and '-' flags both appear, the '0' flag should be ignored. */ + + /* parse field width */ + if (*p == '*') { + int j; + p++; j = va_arg(ap, int); + if (j >= 0) min_field_width = j; + else { min_field_width = -j; justify_left = 1; } + } else if (isdigit((int)(*p))) { + /* size_t could be wider than unsigned int; + make sure we treat argument like common implementations do */ + unsigned int uj = *p++ - '0'; + while (isdigit((int)(*p))) uj = 10*uj + (unsigned int)(*p++ - '0'); + min_field_width = uj; + } + /* parse precision */ + if (*p == '.') { + p++; precision_specified = 1; + if (*p == '*') { + int j = va_arg(ap, int); + p++; + if (j >= 0) precision = j; + else { + precision_specified = 0; precision = 0; + /* NOTE: + * Solaris 2.6 man page claims that in this case the precision + * should be set to 0. Digital Unix 4.0, HPUX 10 and BSD man page + * claim that this case should be treated as unspecified precision, + * which is what we do here. + */ + } + } else if (isdigit((int)(*p))) { + /* size_t could be wider than unsigned int; + make sure we treat argument like common implementations do */ + unsigned int uj = *p++ - '0'; + while (isdigit((int)(*p))) uj = 10*uj + (unsigned int)(*p++ - '0'); + precision = uj; + } + } + /* parse 'h', 'l' and 'll' length modifiers */ + if (*p == 'h' || *p == 'l') { + length_modifier = *p; p++; + if (length_modifier == 'l' && *p == 'l') { /* double l = long long */ +#ifdef SNPRINTF_LONGLONG_SUPPORT + length_modifier = '2'; /* double l encoded as '2' */ +#else + length_modifier = 'l'; /* treat it as a single 'l' */ +#endif + p++; + } + } + fmt_spec = *p; + /* common synonyms: */ + switch (fmt_spec) { + case 'i': fmt_spec = 'd'; break; + case 'D': fmt_spec = 'd'; length_modifier = 'l'; break; + case 'U': fmt_spec = 'u'; length_modifier = 'l'; break; + case 'O': fmt_spec = 'o'; length_modifier = 'l'; break; + default: break; + } + /* get parameter value, do initial processing */ + switch (fmt_spec) { + case '%': /* % behaves similar to 's' regarding flags and field widths */ + case 'c': /* c behaves similar to 's' regarding flags and field widths */ + case 's': + length_modifier = '\0'; /* wint_t and wchar_t not supported */ + /* the result of zero padding flag with non-numeric conversion specifier*/ + /* is undefined. Solaris and HPUX 10 does zero padding in this case, */ + /* Digital Unix and Linux does not. */ +#if !defined(SOLARIS_COMPATIBLE) && !defined(HPUX_COMPATIBLE) + zero_padding = 0; /* turn zero padding off for string conversions */ +#endif + str_arg_l = 1; + switch (fmt_spec) { + case '%': + str_arg = p; break; + case 'c': { + int j = va_arg(ap, int); + uchar_arg = (unsigned char) j; /* standard demands unsigned char */ + str_arg = (const char *) &uchar_arg; + break; + } + case 's': + str_arg = va_arg(ap, const char *); + if (!str_arg) str_arg_l = 0; + /* make sure not to address string beyond the specified precision !!! */ + else if (!precision_specified) str_arg_l = strlen(str_arg); + /* truncate string if necessary as requested by precision */ + else if (precision == 0) str_arg_l = 0; + else { + /* memchr on HP does not like n > 2^31 !!! */ + const char *q = memchr(str_arg, '\0', + precision <= 0x7fffffff ? precision : 0x7fffffff); + str_arg_l = !q ? precision : (q-str_arg); + } + break; + default: break; + } + break; + case 'd': case 'u': case 'o': case 'x': case 'X': case 'p': { + /* NOTE: the u, o, x, X and p conversion specifiers imply + the value is unsigned; d implies a signed value */ + + int arg_sign = 0; + /* 0 if numeric argument is zero (or if pointer is NULL for 'p'), + +1 if greater than zero (or nonzero for unsigned arguments), + -1 if negative (unsigned argument is never negative) */ + + int int_arg = 0; unsigned int uint_arg = 0; + /* only defined for length modifier h, or for no length modifiers */ + + long int long_arg = 0; unsigned long int ulong_arg = 0; + /* only defined for length modifier l */ + + void *ptr_arg = NULL; + /* pointer argument value -only defined for p conversion */ + +#ifdef SNPRINTF_LONGLONG_SUPPORT + long long int long_long_arg = 0; + unsigned long long int ulong_long_arg = 0; + /* only defined for length modifier ll */ +#endif + if (fmt_spec == 'p') { + /* HPUX 10: An l, h, ll or L before any other conversion character + * (other than d, i, u, o, x, or X) is ignored. + * Digital Unix: + * not specified, but seems to behave as HPUX does. + * Solaris: If an h, l, or L appears before any other conversion + * specifier (other than d, i, u, o, x, or X), the behavior + * is undefined. (Actually %hp converts only 16-bits of address + * and %llp treats address as 64-bit data which is incompatible + * with (void *) argument on a 32-bit system). + */ +#ifdef SOLARIS_COMPATIBLE +# ifdef SOLARIS_BUG_COMPATIBLE + /* keep length modifiers even if it represents 'll' */ +# else + if (length_modifier == '2') length_modifier = '\0'; +# endif +#else + length_modifier = '\0'; +#endif + ptr_arg = va_arg(ap, void *); + if (ptr_arg != NULL) arg_sign = 1; + } else if (fmt_spec == 'd') { /* signed */ + switch (length_modifier) { + case '\0': + case 'h': + /* It is non-portable to specify a second argument of char or short + * to va_arg, because arguments seen by the called function + * are not char or short. C converts char and short arguments + * to int before passing them to a function. + */ + int_arg = va_arg(ap, int); + if (int_arg > 0) arg_sign = 1; + else if (int_arg < 0) arg_sign = -1; + break; + case 'l': + long_arg = va_arg(ap, long int); + if (long_arg > 0) arg_sign = 1; + else if (long_arg < 0) arg_sign = -1; + break; +#ifdef SNPRINTF_LONGLONG_SUPPORT + case '2': + long_long_arg = va_arg(ap, long long int); + if (long_long_arg > 0) arg_sign = 1; + else if (long_long_arg < 0) arg_sign = -1; + break; +#endif + } + } else { /* unsigned */ + switch (length_modifier) { + case '\0': + case 'h': + uint_arg = va_arg(ap, unsigned int); + if (uint_arg) arg_sign = 1; + break; + case 'l': + ulong_arg = va_arg(ap, unsigned long int); + if (ulong_arg) arg_sign = 1; + break; +#ifdef SNPRINTF_LONGLONG_SUPPORT + case '2': + ulong_long_arg = va_arg(ap, unsigned long long int); + if (ulong_long_arg) arg_sign = 1; + break; +#endif + } + } + str_arg = tmp; str_arg_l = 0; + /* NOTE: + * For d, i, u, o, x, and X conversions, if precision is specified, + * the '0' flag should be ignored. This is so with Solaris 2.6, + * Digital UNIX 4.0, HPUX 10, Linux, FreeBSD, NetBSD; but not with Perl. + */ +#ifndef PERL_COMPATIBLE + if (precision_specified) zero_padding = 0; +#endif + if (fmt_spec == 'd') { + if (force_sign && arg_sign >= 0) + tmp[str_arg_l++] = space_for_positive ? ' ' : '+'; + /* leave negative numbers for sprintf to handle, + to avoid handling tricky cases like (short int)(-32768) */ +#ifdef LINUX_COMPATIBLE + } else if (fmt_spec == 'p' && force_sign && arg_sign > 0) { + tmp[str_arg_l++] = space_for_positive ? ' ' : '+'; +#endif + } else if (alternate_form) { + if (arg_sign != 0 && (fmt_spec == 'x' || fmt_spec == 'X') ) + { tmp[str_arg_l++] = '0'; tmp[str_arg_l++] = fmt_spec; } + /* alternate form should have no effect for p conversion, but ... */ +#ifdef HPUX_COMPATIBLE + else if (fmt_spec == 'p' + /* HPUX 10: for an alternate form of p conversion, + * a nonzero result is prefixed by 0x. */ +#ifndef HPUX_BUG_COMPATIBLE + /* Actually it uses 0x prefix even for a zero value. */ + && arg_sign != 0 +#endif + ) { tmp[str_arg_l++] = '0'; tmp[str_arg_l++] = 'x'; } +#endif + } + zero_padding_insertion_ind = str_arg_l; + if (!precision_specified) precision = 1; /* default precision is 1 */ + if (precision == 0 && arg_sign == 0 +#if defined(HPUX_BUG_COMPATIBLE) || defined(LINUX_COMPATIBLE) + && fmt_spec != 'p' + /* HPUX 10 man page claims: With conversion character p the result of + * converting a zero value with a precision of zero is a null string. + * Actually HP returns all zeroes, and Linux returns "(nil)". */ +#endif + ) { + /* converted to null string */ + /* When zero value is formatted with an explicit precision 0, + the resulting formatted string is empty (d, i, u, o, x, X, p). */ + } else { + char f[5]; int f_l = 0; + f[f_l++] = '%'; /* construct a simple format string for sprintf */ + if (!length_modifier) { } + else if (length_modifier=='2') { f[f_l++] = 'l'; f[f_l++] = 'l'; } + else f[f_l++] = length_modifier; + f[f_l++] = fmt_spec; f[f_l++] = '\0'; + if (fmt_spec == 'p') str_arg_l += sprintf(tmp+str_arg_l, f, ptr_arg); + else if (fmt_spec == 'd') { /* signed */ + switch (length_modifier) { + case '\0': + case 'h': str_arg_l+=sprintf(tmp+str_arg_l, f, int_arg); break; + case 'l': str_arg_l+=sprintf(tmp+str_arg_l, f, long_arg); break; +#ifdef SNPRINTF_LONGLONG_SUPPORT + case '2': str_arg_l+=sprintf(tmp+str_arg_l,f,long_long_arg); break; +#endif + } + } else { /* unsigned */ + switch (length_modifier) { + case '\0': + case 'h': str_arg_l+=sprintf(tmp+str_arg_l, f, uint_arg); break; + case 'l': str_arg_l+=sprintf(tmp+str_arg_l, f, ulong_arg); break; +#ifdef SNPRINTF_LONGLONG_SUPPORT + case '2': str_arg_l+=sprintf(tmp+str_arg_l,f,ulong_long_arg);break; +#endif + } + } + /* include the optional minus sign and possible "0x" + in the region before the zero padding insertion point */ + if (zero_padding_insertion_ind < str_arg_l && + tmp[zero_padding_insertion_ind] == '-') { + zero_padding_insertion_ind++; + } + if (zero_padding_insertion_ind+1 < str_arg_l && + tmp[zero_padding_insertion_ind] == '0' && + (tmp[zero_padding_insertion_ind+1] == 'x' || + tmp[zero_padding_insertion_ind+1] == 'X') ) { + zero_padding_insertion_ind += 2; + } + } + { size_t num_of_digits = str_arg_l - zero_padding_insertion_ind; + if (alternate_form && fmt_spec == 'o' +#ifdef HPUX_COMPATIBLE /* ("%#.o",0) -> "" */ + && (str_arg_l > 0) +#endif +#ifdef DIGITAL_UNIX_BUG_COMPATIBLE /* ("%#o",0) -> "00" */ +#else + /* unless zero is already the first character */ + && !(zero_padding_insertion_ind < str_arg_l + && tmp[zero_padding_insertion_ind] == '0') +#endif + ) { /* assure leading zero for alternate-form octal numbers */ + if (!precision_specified || precision < num_of_digits+1) { + /* precision is increased to force the first character to be zero, + except if a zero value is formatted with an explicit precision + of zero */ + precision = num_of_digits+1; precision_specified = 1; + } + } + /* zero padding to specified precision? */ + if (num_of_digits < precision) + number_of_zeros_to_pad = precision - num_of_digits; + } + /* zero padding to specified minimal field width? */ + if (!justify_left && zero_padding) { + int n = min_field_width - (str_arg_l+number_of_zeros_to_pad); + if (n > 0) number_of_zeros_to_pad += n; + } + break; + } + default: /* unrecognized conversion specifier, keep format string as-is*/ + zero_padding = 0; /* turn zero padding off for non-numeric convers. */ +#ifndef DIGITAL_UNIX_COMPATIBLE + justify_left = 1; min_field_width = 0; /* reset flags */ +#endif +#if defined(PERL_COMPATIBLE) || defined(LINUX_COMPATIBLE) + /* keep the entire format string unchanged */ + str_arg = starting_p; str_arg_l = p - starting_p; + /* well, not exactly so for Linux, which does something inbetween, + * and I don't feel an urge to imitate it: "%+++++hy" -> "%+y" */ +#else + /* discard the unrecognized conversion, just keep * + * the unrecognized conversion character */ + str_arg = p; str_arg_l = 0; +#endif + if (*p) str_arg_l++; /* include invalid conversion specifier unchanged + if not at end-of-string */ + break; + } + if (*p) p++; /* step over the just processed conversion specifier */ + /* insert padding to the left as requested by min_field_width; + this does not include the zero padding in case of numerical conversions*/ + if (!justify_left) { /* left padding with blank or zero */ + int n = min_field_width - (str_arg_l+number_of_zeros_to_pad); + if (n > 0) { + if (str_l < str_m) { + size_t avail = str_m-str_l; + fast_memset(str+str_l, (zero_padding?'0':' '), (n>avail?avail:n)); + } + str_l += n; + } + } + /* zero padding as requested by the precision or by the minimal field width + * for numeric conversions required? */ + if (number_of_zeros_to_pad <= 0) { + /* will not copy first part of numeric right now, * + * force it to be copied later in its entirety */ + zero_padding_insertion_ind = 0; + } else { + /* insert first part of numerics (sign or '0x') before zero padding */ + int n = zero_padding_insertion_ind; + if (n > 0) { + if (str_l < str_m) { + size_t avail = str_m-str_l; + fast_memcpy(str+str_l, str_arg, (n>avail?avail:n)); + } + str_l += n; + } + /* insert zero padding as requested by the precision or min field width */ + n = number_of_zeros_to_pad; + if (n > 0) { + if (str_l < str_m) { + size_t avail = str_m-str_l; + fast_memset(str+str_l, '0', (n>avail?avail:n)); + } + str_l += n; + } + } + /* insert formatted string + * (or as-is conversion specifier for unknown conversions) */ + { int n = str_arg_l - zero_padding_insertion_ind; + if (n > 0) { + if (str_l < str_m) { + size_t avail = str_m-str_l; + fast_memcpy(str+str_l, str_arg+zero_padding_insertion_ind, + (n>avail?avail:n)); + } + str_l += n; + } + } + /* insert right padding */ + if (justify_left) { /* right blank padding to the field width */ + int n = min_field_width - (str_arg_l+number_of_zeros_to_pad); + if (n > 0) { + if (str_l < str_m) { + size_t avail = str_m-str_l; + fast_memset(str+str_l, ' ', (n>avail?avail:n)); + } + str_l += n; + } + } + } + } +#if defined(NEED_SNPRINTF_ONLY) + va_end(ap); +#endif + if (str_m > 0) { /* make sure the string is null-terminated + even at the expense of overwriting the last character + (shouldn't happen, but just in case) */ + str[str_l <= str_m-1 ? str_l : str_m-1] = '\0'; + } + /* Return the number of characters formatted (excluding trailing null + * character), that is, the number of characters that would have been + * written to the buffer if it were large enough. + * + * The value of str_l should be returned, but str_l is of unsigned type + * size_t, and snprintf is int, possibly leading to an undetected + * integer overflow, resulting in a negative return value, which is illegal. + * Both XSH5 and ISO C99 (at least the draft) are silent on this issue. + * Should errno be set to EOVERFLOW and EOF returned in this case??? + */ + return (int) str_l; +} +#endif diff --git a/libtu/snprintf_2.2/snprintf.c b/libtu/snprintf_2.2/snprintf.c new file mode 100644 index 0000000..8720be7 --- /dev/null +++ b/libtu/snprintf_2.2/snprintf.c @@ -0,0 +1,1032 @@ +#include +#define NEED_ASPRINTF +#define NEED_VASPRINTF +/* + * snprintf.c - a portable implementation of snprintf + * + * AUTHOR + * Mark Martinec , April 1999. + * + * Copyright 1999, Mark Martinec. All rights reserved. + * + * TERMS AND CONDITIONS + * This program is free software; you can redistribute it and/or modify + * it under the terms of the "Frontier Artistic License" which comes + * with this Kit. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Frontier Artistic License for more details. + * + * You should have received a copy of the Frontier Artistic License + * with this Kit in the file named LICENSE.txt . + * If not, I'll be glad to provide one. + * + * FEATURES + * - careful adherence to specs regarding flags, field width and precision; + * - good performance for large string handling (large format, large + * argument or large paddings). Performance is similar to system's sprintf + * and in several cases significantly better (make sure you compile with + * optimizations turned on, tell the compiler the code is strict ANSI + * if necessary to give it more freedom for optimizations); + * - return value semantics per ISO/IEC 9899:1999 ("ISO C99"); + * - written in standard ISO/ANSI C - requires an ANSI C compiler. + * + * SUPPORTED CONVERSION SPECIFIERS AND DATA TYPES + * + * This snprintf only supports the following conversion specifiers: + * s, c, d, u, o, x, X, p (and synonyms: i, D, U, O - see below) + * with flags: '-', '+', ' ', '0' and '#'. + * An asterisk is supported for field width as well as precision. + * + * Length modifiers 'h' (short int), 'l' (long int), + * and 'll' (long long int) are supported. + * NOTE: + * If macro SNPRINTF_LONGLONG_SUPPORT is not defined (default) the + * length modifier 'll' is recognized but treated the same as 'l', + * which may cause argument value truncation! Defining + * SNPRINTF_LONGLONG_SUPPORT requires that your system's sprintf also + * handles length modifier 'll'. long long int is a language extension + * which may not be portable. + * + * Conversion of numeric data (conversion specifiers d, u, o, x, X, p) + * with length modifiers (none or h, l, ll) is left to the system routine + * sprintf, but all handling of flags, field width and precision as well as + * c and s conversions is done very carefully by this portable routine. + * If a string precision (truncation) is specified (e.g. %.8s) it is + * guaranteed the string beyond the specified precision will not be referenced. + * + * Length modifiers h, l and ll are ignored for c and s conversions (data + * types wint_t and wchar_t are not supported). + * + * The following common synonyms for conversion characters are supported: + * - i is a synonym for d + * - D is a synonym for ld, explicit length modifiers are ignored + * - U is a synonym for lu, explicit length modifiers are ignored + * - O is a synonym for lo, explicit length modifiers are ignored + * The D, O and U conversion characters are nonstandard, they are supported + * for backward compatibility only, and should not be used for new code. + * + * The following is specifically NOT supported: + * - flag ' (thousands' grouping character) is recognized but ignored + * - numeric conversion specifiers: f, e, E, g, G and synonym F, + * as well as the new a and A conversion specifiers + * - length modifier 'L' (long double) and 'q' (quad - use 'll' instead) + * - wide character/string conversions: lc, ls, and nonstandard + * synonyms C and S + * - writeback of converted string length: conversion character n + * - the n$ specification for direct reference to n-th argument + * - locales + * + * It is permitted for str_m to be zero, and it is permitted to specify NULL + * pointer for resulting string argument if str_m is zero (as per ISO C99). + * + * The return value is the number of characters which would be generated + * for the given input, excluding the trailing null. If this value + * is greater or equal to str_m, not all characters from the result + * have been stored in str, output bytes beyond the (str_m-1) -th character + * are discarded. If str_m is greater than zero it is guaranteed + * the resulting string will be null-terminated. + * + * NOTE that this matches the ISO C99, OpenBSD, and GNU C library 2.1, + * but is different from some older and vendor implementations, + * and is also different from XPG, XSH5, SUSv2 specifications. + * For historical discussion on changes in the semantics and standards + * of snprintf see printf(3) man page in the Linux programmers manual. + * + * Routines asprintf and vasprintf return a pointer (in the ptr argument) + * to a buffer sufficiently large to hold the resulting string. This pointer + * should be passed to free(3) to release the allocated storage when it is + * no longer needed. If sufficient space cannot be allocated, these functions + * will return -1 and set ptr to be a NULL pointer. These two routines are a + * GNU C library extensions (glibc). + * + * Routines asnprintf and vasnprintf are similar to asprintf and vasprintf, + * yet, like snprintf and vsnprintf counterparts, will write at most str_m-1 + * characters into the allocated output string, the last character in the + * allocated buffer then gets the terminating null. If the formatted string + * length (the return value) is greater than or equal to the str_m argument, + * the resulting string was truncated and some of the formatted characters + * were discarded. These routines present a handy way to limit the amount + * of allocated memory to some sane value. + * + * AVAILABILITY + * http://www.ijs.si/software/snprintf/ + * + * REVISION HISTORY + * 1999-04 V0.9 Mark Martinec + * - initial version, some modifications after comparing printf + * man pages for Digital Unix 4.0, Solaris 2.6 and HPUX 10, + * and checking how Perl handles sprintf (differently!); + * 1999-04-09 V1.0 Mark Martinec + * - added main test program, fixed remaining inconsistencies, + * added optional (long long int) support; + * 1999-04-12 V1.1 Mark Martinec + * - support the 'p' conversion (pointer to void); + * - if a string precision is specified + * make sure the string beyond the specified precision + * will not be referenced (e.g. by strlen); + * 1999-04-13 V1.2 Mark Martinec + * - support synonyms %D=%ld, %U=%lu, %O=%lo; + * - speed up the case of long format string with few conversions; + * 1999-06-30 V1.3 Mark Martinec + * - fixed runaway loop (eventually crashing when str_l wraps + * beyond 2^31) while copying format string without + * conversion specifiers to a buffer that is too short + * (thanks to Edwin Young for + * spotting the problem); + * - added macros PORTABLE_SNPRINTF_VERSION_(MAJOR|MINOR) + * to snprintf.h + * 2000-02-14 V2.0 (never released) Mark Martinec + * - relaxed license terms: The Artistic License now applies. + * You may still apply the GNU GENERAL PUBLIC LICENSE + * as was distributed with previous versions, if you prefer; + * - changed REVISION HISTORY dates to use ISO 8601 date format; + * - added vsnprintf (patch also independently proposed by + * Caolan McNamara 2000-05-04, and Keith M Willenson 2000-06-01) + * 2000-06-27 V2.1 Mark Martinec + * - removed POSIX check for str_m<1; value 0 for str_m is + * allowed by ISO C99 (and GNU C library 2.1) - (pointed out + * on 2000-05-04 by Caolan McNamara, caolan@ csn dot ul dot ie). + * Besides relaxed license this change in standards adherence + * is the main reason to bump up the major version number; + * - added nonstandard routines asnprintf, vasnprintf, asprintf, + * vasprintf that dynamically allocate storage for the + * resulting string; these routines are not compiled by default, + * see comments where NEED_V?ASN?PRINTF macros are defined; + * - autoconf contributed by Caolan McNamara + * 2000-10-06 V2.2 Mark Martinec + * - BUG FIX: the %c conversion used a temporary variable + * that was no longer in scope when referenced, + * possibly causing incorrect resulting character; + * - BUG FIX: make precision and minimal field width unsigned + * to handle huge values (2^31 <= n < 2^32) correctly; + * also be more careful in the use of signed/unsigned/size_t + * internal variables - probably more careful than many + * vendor implementations, but there may still be a case + * where huge values of str_m, precision or minimal field + * could cause incorrect behaviour; + * - use separate variables for signed/unsigned arguments, + * and for short/int, long, and long long argument lengths + * to avoid possible incompatibilities on certain + * computer architectures. Also use separate variable + * arg_sign to hold sign of a numeric argument, + * to make code more transparent; + * - some fiddling with zero padding and "0x" to make it + * Linux compatible; + * - systematically use macros fast_memcpy and fast_memset + * instead of case-by-case hand optimization; determine some + * breakeven string lengths for different architectures; + * - terminology change: 'format' -> 'conversion specifier', + * 'C9x' -> 'ISO/IEC 9899:1999 ("ISO C99")', + * 'alternative form' -> 'alternate form', + * 'data type modifier' -> 'length modifier'; + * - several comments rephrased and new ones added; + * - make compiler not complain about 'credits' defined but + * not used; + */ + + +/* Define HAVE_SNPRINTF if your system already has snprintf and vsnprintf. + * + * If HAVE_SNPRINTF is defined this module will not produce code for + * snprintf and vsnprintf, unless PREFER_PORTABLE_SNPRINTF is defined as well, + * causing this portable version of snprintf to be called portable_snprintf + * (and portable_vsnprintf). + */ +/* #define HAVE_SNPRINTF */ + +/* Define PREFER_PORTABLE_SNPRINTF if your system does have snprintf and + * vsnprintf but you would prefer to use the portable routine(s) instead. + * In this case the portable routine is declared as portable_snprintf + * (and portable_vsnprintf) and a macro 'snprintf' (and 'vsnprintf') + * is defined to expand to 'portable_v?snprintf' - see file snprintf.h . + * Defining this macro is only useful if HAVE_SNPRINTF is also defined, + * but does does no harm if defined nevertheless. + */ +/* #define PREFER_PORTABLE_SNPRINTF */ + +/* Define SNPRINTF_LONGLONG_SUPPORT if you want to support + * data type (long long int) and length modifier 'll' (e.g. %lld). + * If undefined, 'll' is recognized but treated as a single 'l'. + * + * If the system's sprintf does not handle 'll' + * the SNPRINTF_LONGLONG_SUPPORT must not be defined! + * + * This is off by default as (long long int) is a language extension. + */ +/* #define SNPRINTF_LONGLONG_SUPPORT */ + +/* Define NEED_SNPRINTF_ONLY if you only need snprintf, and not vsnprintf. + * If NEED_SNPRINTF_ONLY is defined, the snprintf will be defined directly, + * otherwise both snprintf and vsnprintf routines will be defined + * and snprintf will be a simple wrapper around vsnprintf, at the expense + * of an extra procedure call. + */ +/* #define NEED_SNPRINTF_ONLY */ + +/* Define NEED_V?ASN?PRINTF macros if you need library extension + * routines asprintf, vasprintf, asnprintf, vasnprintf respectively, + * and your system library does not provide them. They are all small + * wrapper routines around portable_vsnprintf. Defining any of the four + * NEED_V?ASN?PRINTF macros automatically turns off NEED_SNPRINTF_ONLY + * and turns on PREFER_PORTABLE_SNPRINTF. + * + * Watch for name conflicts with the system library if these routines + * are already present there. + * + * NOTE: vasprintf and vasnprintf routines need va_copy() from stdarg.h, as + * specified by C99, to be able to traverse the same list of arguments twice. + * I don't know of any other standard and portable way of achieving the same. + * With some versions of gcc you may use __va_copy(). You might even get away + * with "ap2 = ap", in this case you must not call va_end(ap2) ! + * #define va_copy(ap2,ap) ap2 = ap + */ +#ifndef va_copy +#define va_copy(ap2,ap) ap2 = ap +#endif + +/* #define NEED_ASPRINTF */ +/* #define NEED_ASNPRINTF */ +/* #define NEED_VASPRINTF */ +/* #define NEED_VASNPRINTF */ + + +/* Define the following macros if desired: + * SOLARIS_COMPATIBLE, SOLARIS_BUG_COMPATIBLE, + * HPUX_COMPATIBLE, HPUX_BUG_COMPATIBLE, LINUX_COMPATIBLE, + * DIGITAL_UNIX_COMPATIBLE, DIGITAL_UNIX_BUG_COMPATIBLE, + * PERL_COMPATIBLE, PERL_BUG_COMPATIBLE, + * + * - For portable applications it is best not to rely on peculiarities + * of a given implementation so it may be best not to define any + * of the macros that select compatibility and to avoid features + * that vary among the systems. + * + * - Selecting compatibility with more than one operating system + * is not strictly forbidden but is not recommended. + * + * - 'x'_BUG_COMPATIBLE implies 'x'_COMPATIBLE . + * + * - 'x'_COMPATIBLE refers to (and enables) a behaviour that is + * documented in a sprintf man page on a given operating system + * and actually adhered to by the system's sprintf (but not on + * most other operating systems). It may also refer to and enable + * a behaviour that is declared 'undefined' or 'implementation specific' + * in the man page but a given implementation behaves predictably + * in a certain way. + * + * - 'x'_BUG_COMPATIBLE refers to (and enables) a behaviour of system's sprintf + * that contradicts the sprintf man page on the same operating system. + * + * - I do not claim that the 'x'_COMPATIBLE and 'x'_BUG_COMPATIBLE + * conditionals take into account all idiosyncrasies of a particular + * implementation, there may be other incompatibilities. + */ + + + +/* ============================================= */ +/* NO USER SERVICABLE PARTS FOLLOWING THIS POINT */ +/* ============================================= */ + +#define PORTABLE_SNPRINTF_VERSION_MAJOR 2 +#define PORTABLE_SNPRINTF_VERSION_MINOR 2 + +#if defined(NEED_ASPRINTF) || defined(NEED_ASNPRINTF) || defined(NEED_VASPRINTF) || defined(NEED_VASNPRINTF) +# if defined(NEED_SNPRINTF_ONLY) +# undef NEED_SNPRINTF_ONLY +# endif +# if !defined(PREFER_PORTABLE_SNPRINTF) +# define PREFER_PORTABLE_SNPRINTF +# endif +#endif + +#if defined(SOLARIS_BUG_COMPATIBLE) && !defined(SOLARIS_COMPATIBLE) +#define SOLARIS_COMPATIBLE +#endif + +#if defined(HPUX_BUG_COMPATIBLE) && !defined(HPUX_COMPATIBLE) +#define HPUX_COMPATIBLE +#endif + +#if defined(DIGITAL_UNIX_BUG_COMPATIBLE) && !defined(DIGITAL_UNIX_COMPATIBLE) +#define DIGITAL_UNIX_COMPATIBLE +#endif + +#if defined(PERL_BUG_COMPATIBLE) && !defined(PERL_COMPATIBLE) +#define PERL_COMPATIBLE +#endif + +#if defined(LINUX_BUG_COMPATIBLE) && !defined(LINUX_COMPATIBLE) +#define LINUX_COMPATIBLE +#endif + +#include +#include +#include +#include +#include +#include +#include + +#ifdef isdigit +#undef isdigit +#endif +#define isdigit(c) ((c) >= '0' && (c) <= '9') + +/* For copying strings longer or equal to 'breakeven_point' + * it is more efficient to call memcpy() than to do it inline. + * The value depends mostly on the processor architecture, + * but also on the compiler and its optimization capabilities. + * The value is not critical, some small value greater than zero + * will be just fine if you don't care to squeeze every drop + * of performance out of the code. + * + * Small values favor memcpy, large values favor inline code. + */ +#if defined(__alpha__) || defined(__alpha) +# define breakeven_point 2 /* AXP (DEC Alpha) - gcc or cc or egcs */ +#endif +#if defined(__i386__) || defined(__i386) +# define breakeven_point 12 /* Intel Pentium/Linux - gcc 2.96 */ +#endif +#if defined(__hppa) +# define breakeven_point 10 /* HP-PA - gcc */ +#endif +#if defined(__sparc__) || defined(__sparc) +# define breakeven_point 33 /* Sun Sparc 5 - gcc 2.8.1 */ +#endif + +/* some other values of possible interest: */ +/* #define breakeven_point 8 */ /* VAX 4000 - vaxc */ +/* #define breakeven_point 19 */ /* VAX 4000 - gcc 2.7.0 */ + +#ifndef breakeven_point +# define breakeven_point 6 /* some reasonable one-size-fits-all value */ +#endif + +#define fast_memcpy(d,s,n) \ + { register size_t nn = (size_t)(n); \ + if (nn >= breakeven_point) memcpy((d), (s), nn); \ + else if (nn > 0) { /* proc call overhead is worth only for large strings*/\ + register char *dd; register const char *ss; \ + for (ss=(s), dd=(d); nn>0; nn--) *dd++ = *ss++; } } + +#define fast_memset(d,c,n) \ + { register size_t nn = (size_t)(n); \ + if (nn >= breakeven_point) memset((d), (int)(c), nn); \ + else if (nn > 0) { /* proc call overhead is worth only for large strings*/\ + register char *dd; register const int cc=(int)(c); \ + for (dd=(d); nn>0; nn--) *dd++ = cc; } } + +/* prototypes */ + +#if defined(NEED_ASPRINTF) +int asprintf (char **ptr, const char *fmt, /*args*/ ...); +#endif +#if defined(NEED_VASPRINTF) +int vasprintf (char **ptr, const char *fmt, va_list ap); +#endif +#if defined(NEED_ASNPRINTF) +int asnprintf (char **ptr, size_t str_m, const char *fmt, /*args*/ ...); +#endif +#if defined(NEED_VASNPRINTF) +int vasnprintf (char **ptr, size_t str_m, const char *fmt, va_list ap); +#endif + +#if defined(HAVE_SNPRINTF) +/* declare our portable snprintf routine under name portable_snprintf */ +/* declare our portable vsnprintf routine under name portable_vsnprintf */ +#else +/* declare our portable routines under names snprintf and vsnprintf */ +#define portable_snprintf snprintf +#if !defined(NEED_SNPRINTF_ONLY) +#define portable_vsnprintf vsnprintf +#endif +#endif + +#if !defined(HAVE_SNPRINTF) || defined(PREFER_PORTABLE_SNPRINTF) +int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...); +#if !defined(NEED_SNPRINTF_ONLY) +int portable_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap); +#endif +#endif + +/* declarations */ + +static char credits[] = "\n\ +@(#)snprintf.c, v2.2: Mark Martinec, \n\ +@(#)snprintf.c, v2.2: Copyright 1999, Mark Martinec. Frontier Artistic License applies.\n\ +@(#)snprintf.c, v2.2: http://www.ijs.si/software/snprintf/\n"; + +#if defined(NEED_ASPRINTF) +int asprintf(char **ptr, const char *fmt, /*args*/ ...) { + va_list ap; + size_t str_m; + int str_l; + + *ptr = NULL; + va_start(ap, fmt); /* measure the required size */ + str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap); + va_end(ap); + assert(str_l >= 0); /* possible integer overflow if str_m > INT_MAX */ + *ptr = (char *) malloc(str_m = (size_t)str_l + 1); + if (*ptr == NULL) { errno = ENOMEM; str_l = -1; } + else { + int str_l2; + va_start(ap, fmt); + str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap); + va_end(ap); + assert(str_l2 == str_l); + } + return str_l; +} +#endif + +#if defined(NEED_VASPRINTF) +int vasprintf(char **ptr, const char *fmt, va_list ap) { + size_t str_m; + int str_l; + + *ptr = NULL; + { va_list ap2; + va_copy(ap2, ap); /* don't consume the original ap, we'll need it again */ + str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap2);/*get required size*/ + va_end(ap2); + } + assert(str_l >= 0); /* possible integer overflow if str_m > INT_MAX */ + *ptr = (char *) malloc(str_m = (size_t)str_l + 1); + if (*ptr == NULL) { errno = ENOMEM; str_l = -1; } + else { + int str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap); + assert(str_l2 == str_l); + } + return str_l; +} +#endif + +#if defined(NEED_ASNPRINTF) +int asnprintf (char **ptr, size_t str_m, const char *fmt, /*args*/ ...) { + va_list ap; + int str_l; + + *ptr = NULL; + va_start(ap, fmt); /* measure the required size */ + str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap); + va_end(ap); + assert(str_l >= 0); /* possible integer overflow if str_m > INT_MAX */ + if ((size_t)str_l + 1 < str_m) str_m = (size_t)str_l + 1; /* truncate */ + /* if str_m is 0, no buffer is allocated, just set *ptr to NULL */ + if (str_m == 0) { /* not interested in resulting string, just return size */ + } else { + *ptr = (char *) malloc(str_m); + if (*ptr == NULL) { errno = ENOMEM; str_l = -1; } + else { + int str_l2; + va_start(ap, fmt); + str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap); + va_end(ap); + assert(str_l2 == str_l); + } + } + return str_l; +} +#endif + +#if defined(NEED_VASNPRINTF) +int vasnprintf (char **ptr, size_t str_m, const char *fmt, va_list ap) { + int str_l; + + *ptr = NULL; + { va_list ap2; + va_copy(ap2, ap); /* don't consume the original ap, we'll need it again */ + str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap2);/*get required size*/ + va_end(ap2); + } + assert(str_l >= 0); /* possible integer overflow if str_m > INT_MAX */ + if ((size_t)str_l + 1 < str_m) str_m = (size_t)str_l + 1; /* truncate */ + /* if str_m is 0, no buffer is allocated, just set *ptr to NULL */ + if (str_m == 0) { /* not interested in resulting string, just return size */ + } else { + *ptr = (char *) malloc(str_m); + if (*ptr == NULL) { errno = ENOMEM; str_l = -1; } + else { + int str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap); + assert(str_l2 == str_l); + } + } + return str_l; +} +#endif + +/* + * If the system does have snprintf and the portable routine is not + * specifically required, this module produces no code for snprintf/vsnprintf. + */ +#if !defined(HAVE_SNPRINTF) || defined(PREFER_PORTABLE_SNPRINTF) + +#if !defined(NEED_SNPRINTF_ONLY) +int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...) { + va_list ap; + int str_l; + + va_start(ap, fmt); + str_l = portable_vsnprintf(str, str_m, fmt, ap); + va_end(ap); + return str_l; +} +#endif + +#if defined(NEED_SNPRINTF_ONLY) +int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...) { +#else +int portable_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap) { +#endif + +#if defined(NEED_SNPRINTF_ONLY) + va_list ap; +#endif + size_t str_l = 0; + const char *p = fmt; + +/* In contrast with POSIX, the ISO C99 now says + * that str can be NULL and str_m can be 0. + * This is more useful than the old: if (str_m < 1) return -1; */ + +#if defined(NEED_SNPRINTF_ONLY) + va_start(ap, fmt); +#endif + if (!p) p = ""; + while (*p) { + if (*p != '%') { + /* if (str_l < str_m) str[str_l++] = *p++; -- this would be sufficient */ + /* but the following code achieves better performance for cases + * where format string is long and contains few conversions */ + const char *q = strchr(p+1,'%'); + size_t n = !q ? strlen(p) : (size_t)(q-p); + if (str_l < str_m) { + size_t avail = str_m-str_l; + fast_memcpy(str+str_l, p, (n>avail?avail:n)); + } + p += n; str_l += n; + } else { + const char *starting_p; + size_t min_field_width = 0, precision = 0; + int zero_padding = 0, precision_specified = 0, justify_left = 0; + int alternate_form = 0, force_sign = 0; + int space_for_positive = 1; /* If both the ' ' and '+' flags appear, + the ' ' flag should be ignored. */ + char length_modifier = '\0'; /* allowed values: \0, h, l, L */ + char tmp[32];/* temporary buffer for simple numeric->string conversion */ + + const char *str_arg; /* string address in case of string argument */ + size_t str_arg_l; /* natural field width of arg without padding + and sign */ + unsigned char uchar_arg; + /* unsigned char argument value - only defined for c conversion. + N.B. standard explicitly states the char argument for + the c conversion is unsigned */ + + size_t number_of_zeros_to_pad = 0; + /* number of zeros to be inserted for numeric conversions + as required by the precision or minimal field width */ + + size_t zero_padding_insertion_ind = 0; + /* index into tmp where zero padding is to be inserted */ + + char fmt_spec = '\0'; + /* current conversion specifier character */ + + str_arg = credits;/* just to make compiler happy (defined but not used)*/ + str_arg = NULL; + starting_p = p; p++; /* skip '%' */ + /* parse flags */ + while (*p == '0' || *p == '-' || *p == '+' || + *p == ' ' || *p == '#' || *p == '\'') { + switch (*p) { + case '0': zero_padding = 1; break; + case '-': justify_left = 1; break; + case '+': force_sign = 1; space_for_positive = 0; break; + case ' ': force_sign = 1; + /* If both the ' ' and '+' flags appear, the ' ' flag should be ignored */ +#ifdef PERL_COMPATIBLE + /* ... but in Perl the last of ' ' and '+' applies */ + space_for_positive = 1; +#endif + break; + case '#': alternate_form = 1; break; + case '\'': break; + } + p++; + } + /* If the '0' and '-' flags both appear, the '0' flag should be ignored. */ + + /* parse field width */ + if (*p == '*') { + int j; + p++; j = va_arg(ap, int); + if (j >= 0) min_field_width = j; + else { min_field_width = -j; justify_left = 1; } + } else if (isdigit((int)(*p))) { + /* size_t could be wider than unsigned int; + make sure we treat argument like common implementations do */ + unsigned int uj = *p++ - '0'; + while (isdigit((int)(*p))) uj = 10*uj + (unsigned int)(*p++ - '0'); + min_field_width = uj; + } + /* parse precision */ + if (*p == '.') { + p++; precision_specified = 1; + if (*p == '*') { + int j = va_arg(ap, int); + p++; + if (j >= 0) precision = j; + else { + precision_specified = 0; precision = 0; + /* NOTE: + * Solaris 2.6 man page claims that in this case the precision + * should be set to 0. Digital Unix 4.0, HPUX 10 and BSD man page + * claim that this case should be treated as unspecified precision, + * which is what we do here. + */ + } + } else if (isdigit((int)(*p))) { + /* size_t could be wider than unsigned int; + make sure we treat argument like common implementations do */ + unsigned int uj = *p++ - '0'; + while (isdigit((int)(*p))) uj = 10*uj + (unsigned int)(*p++ - '0'); + precision = uj; + } + } + /* parse 'h', 'l' and 'll' length modifiers */ + if (*p == 'h' || *p == 'l') { + length_modifier = *p; p++; + if (length_modifier == 'l' && *p == 'l') { /* double l = long long */ +#ifdef SNPRINTF_LONGLONG_SUPPORT + length_modifier = '2'; /* double l encoded as '2' */ +#else + length_modifier = 'l'; /* treat it as a single 'l' */ +#endif + p++; + } + } + fmt_spec = *p; + /* common synonyms: */ + switch (fmt_spec) { + case 'i': fmt_spec = 'd'; break; + case 'D': fmt_spec = 'd'; length_modifier = 'l'; break; + case 'U': fmt_spec = 'u'; length_modifier = 'l'; break; + case 'O': fmt_spec = 'o'; length_modifier = 'l'; break; + default: break; + } + /* get parameter value, do initial processing */ + switch (fmt_spec) { + case '%': /* % behaves similar to 's' regarding flags and field widths */ + case 'c': /* c behaves similar to 's' regarding flags and field widths */ + case 's': + length_modifier = '\0'; /* wint_t and wchar_t not supported */ + /* the result of zero padding flag with non-numeric conversion specifier*/ + /* is undefined. Solaris and HPUX 10 does zero padding in this case, */ + /* Digital Unix and Linux does not. */ +#if !defined(SOLARIS_COMPATIBLE) && !defined(HPUX_COMPATIBLE) + zero_padding = 0; /* turn zero padding off for string conversions */ +#endif + str_arg_l = 1; + switch (fmt_spec) { + case '%': + str_arg = p; break; + case 'c': { + int j = va_arg(ap, int); + uchar_arg = (unsigned char) j; /* standard demands unsigned char */ + str_arg = (const char *) &uchar_arg; + break; + } + case 's': + str_arg = va_arg(ap, const char *); + if (!str_arg) str_arg_l = 0; + /* make sure not to address string beyond the specified precision !!! */ + else if (!precision_specified) str_arg_l = strlen(str_arg); + /* truncate string if necessary as requested by precision */ + else if (precision == 0) str_arg_l = 0; + else { + /* memchr on HP does not like n > 2^31 !!! */ + const char *q = memchr(str_arg, '\0', + precision <= 0x7fffffff ? precision : 0x7fffffff); + str_arg_l = !q ? precision : (size_t)(q-str_arg); + } + break; + default: break; + } + break; + case 'd': case 'u': case 'o': case 'x': case 'X': case 'p': { + /* NOTE: the u, o, x, X and p conversion specifiers imply + the value is unsigned; d implies a signed value */ + + int arg_sign = 0; + /* 0 if numeric argument is zero (or if pointer is NULL for 'p'), + +1 if greater than zero (or nonzero for unsigned arguments), + -1 if negative (unsigned argument is never negative) */ + + int int_arg = 0; unsigned int uint_arg = 0; + /* only defined for length modifier h, or for no length modifiers */ + + long int long_arg = 0; unsigned long int ulong_arg = 0; + /* only defined for length modifier l */ + + void *ptr_arg = NULL; + /* pointer argument value -only defined for p conversion */ + +#ifdef SNPRINTF_LONGLONG_SUPPORT + long long int long_long_arg = 0; + unsigned long long int ulong_long_arg = 0; + /* only defined for length modifier ll */ +#endif + if (fmt_spec == 'p') { + /* HPUX 10: An l, h, ll or L before any other conversion character + * (other than d, i, u, o, x, or X) is ignored. + * Digital Unix: + * not specified, but seems to behave as HPUX does. + * Solaris: If an h, l, or L appears before any other conversion + * specifier (other than d, i, u, o, x, or X), the behavior + * is undefined. (Actually %hp converts only 16-bits of address + * and %llp treats address as 64-bit data which is incompatible + * with (void *) argument on a 32-bit system). + */ +#ifdef SOLARIS_COMPATIBLE +# ifdef SOLARIS_BUG_COMPATIBLE + /* keep length modifiers even if it represents 'll' */ +# else + if (length_modifier == '2') length_modifier = '\0'; +# endif +#else + length_modifier = '\0'; +#endif + ptr_arg = va_arg(ap, void *); + if (ptr_arg != NULL) arg_sign = 1; + } else if (fmt_spec == 'd') { /* signed */ + switch (length_modifier) { + case '\0': + case 'h': + /* It is non-portable to specify a second argument of char or short + * to va_arg, because arguments seen by the called function + * are not char or short. C converts char and short arguments + * to int before passing them to a function. + */ + int_arg = va_arg(ap, int); + if (int_arg > 0) arg_sign = 1; + else if (int_arg < 0) arg_sign = -1; + break; + case 'l': + long_arg = va_arg(ap, long int); + if (long_arg > 0) arg_sign = 1; + else if (long_arg < 0) arg_sign = -1; + break; +#ifdef SNPRINTF_LONGLONG_SUPPORT + case '2': + long_long_arg = va_arg(ap, long long int); + if (long_long_arg > 0) arg_sign = 1; + else if (long_long_arg < 0) arg_sign = -1; + break; +#endif + } + } else { /* unsigned */ + switch (length_modifier) { + case '\0': + case 'h': + uint_arg = va_arg(ap, unsigned int); + if (uint_arg) arg_sign = 1; + break; + case 'l': + ulong_arg = va_arg(ap, unsigned long int); + if (ulong_arg) arg_sign = 1; + break; +#ifdef SNPRINTF_LONGLONG_SUPPORT + case '2': + ulong_long_arg = va_arg(ap, unsigned long long int); + if (ulong_long_arg) arg_sign = 1; + break; +#endif + } + } + str_arg = tmp; str_arg_l = 0; + /* NOTE: + * For d, i, u, o, x, and X conversions, if precision is specified, + * the '0' flag should be ignored. This is so with Solaris 2.6, + * Digital UNIX 4.0, HPUX 10, Linux, FreeBSD, NetBSD; but not with Perl. + */ +#ifndef PERL_COMPATIBLE + if (precision_specified) zero_padding = 0; +#endif + if (fmt_spec == 'd') { + if (force_sign && arg_sign >= 0) + tmp[str_arg_l++] = space_for_positive ? ' ' : '+'; + /* leave negative numbers for sprintf to handle, + to avoid handling tricky cases like (short int)(-32768) */ +#ifdef LINUX_COMPATIBLE + } else if (fmt_spec == 'p' && force_sign && arg_sign > 0) { + tmp[str_arg_l++] = space_for_positive ? ' ' : '+'; +#endif + } else if (alternate_form) { + if (arg_sign != 0 && (fmt_spec == 'x' || fmt_spec == 'X') ) + { tmp[str_arg_l++] = '0'; tmp[str_arg_l++] = fmt_spec; } + /* alternate form should have no effect for p conversion, but ... */ +#ifdef HPUX_COMPATIBLE + else if (fmt_spec == 'p' + /* HPUX 10: for an alternate form of p conversion, + * a nonzero result is prefixed by 0x. */ +#ifndef HPUX_BUG_COMPATIBLE + /* Actually it uses 0x prefix even for a zero value. */ + && arg_sign != 0 +#endif + ) { tmp[str_arg_l++] = '0'; tmp[str_arg_l++] = 'x'; } +#endif + } + zero_padding_insertion_ind = str_arg_l; + if (!precision_specified) precision = 1; /* default precision is 1 */ + if (precision == 0 && arg_sign == 0 +#if defined(HPUX_BUG_COMPATIBLE) || defined(LINUX_COMPATIBLE) + && fmt_spec != 'p' + /* HPUX 10 man page claims: With conversion character p the result of + * converting a zero value with a precision of zero is a null string. + * Actually HP returns all zeroes, and Linux returns "(nil)". */ +#endif + ) { + /* converted to null string */ + /* When zero value is formatted with an explicit precision 0, + the resulting formatted string is empty (d, i, u, o, x, X, p). */ + } else { + char f[5]; int f_l = 0; + f[f_l++] = '%'; /* construct a simple format string for sprintf */ + if (!length_modifier) { } + else if (length_modifier=='2') { f[f_l++] = 'l'; f[f_l++] = 'l'; } + else f[f_l++] = length_modifier; + f[f_l++] = fmt_spec; f[f_l++] = '\0'; + if (fmt_spec == 'p') str_arg_l += sprintf(tmp+str_arg_l, f, ptr_arg); + else if (fmt_spec == 'd') { /* signed */ + switch (length_modifier) { + case '\0': + case 'h': str_arg_l+=sprintf(tmp+str_arg_l, f, int_arg); break; + case 'l': str_arg_l+=sprintf(tmp+str_arg_l, f, long_arg); break; +#ifdef SNPRINTF_LONGLONG_SUPPORT + case '2': str_arg_l+=sprintf(tmp+str_arg_l,f,long_long_arg); break; +#endif + } + } else { /* unsigned */ + switch (length_modifier) { + case '\0': + case 'h': str_arg_l+=sprintf(tmp+str_arg_l, f, uint_arg); break; + case 'l': str_arg_l+=sprintf(tmp+str_arg_l, f, ulong_arg); break; +#ifdef SNPRINTF_LONGLONG_SUPPORT + case '2': str_arg_l+=sprintf(tmp+str_arg_l,f,ulong_long_arg);break; +#endif + } + } + /* include the optional minus sign and possible "0x" + in the region before the zero padding insertion point */ + if (zero_padding_insertion_ind < str_arg_l && + tmp[zero_padding_insertion_ind] == '-') { + zero_padding_insertion_ind++; + } + if (zero_padding_insertion_ind+1 < str_arg_l && + tmp[zero_padding_insertion_ind] == '0' && + (tmp[zero_padding_insertion_ind+1] == 'x' || + tmp[zero_padding_insertion_ind+1] == 'X') ) { + zero_padding_insertion_ind += 2; + } + } + { size_t num_of_digits = str_arg_l - zero_padding_insertion_ind; + if (alternate_form && fmt_spec == 'o' +#ifdef HPUX_COMPATIBLE /* ("%#.o",0) -> "" */ + && (str_arg_l > 0) +#endif +#ifdef DIGITAL_UNIX_BUG_COMPATIBLE /* ("%#o",0) -> "00" */ +#else + /* unless zero is already the first character */ + && !(zero_padding_insertion_ind < str_arg_l + && tmp[zero_padding_insertion_ind] == '0') +#endif + ) { /* assure leading zero for alternate-form octal numbers */ + if (!precision_specified || precision < num_of_digits+1) { + /* precision is increased to force the first character to be zero, + except if a zero value is formatted with an explicit precision + of zero */ + precision = num_of_digits+1; precision_specified = 1; + } + } + /* zero padding to specified precision? */ + if (num_of_digits < precision) + number_of_zeros_to_pad = precision - num_of_digits; + } + /* zero padding to specified minimal field width? */ + if (!justify_left && zero_padding) { + int n = min_field_width - (str_arg_l+number_of_zeros_to_pad); + if (n > 0) number_of_zeros_to_pad += n; + } + break; + } + default: /* unrecognized conversion specifier, keep format string as-is*/ + zero_padding = 0; /* turn zero padding off for non-numeric convers. */ +#ifndef DIGITAL_UNIX_COMPATIBLE + justify_left = 1; min_field_width = 0; /* reset flags */ +#endif +#if defined(PERL_COMPATIBLE) || defined(LINUX_COMPATIBLE) + /* keep the entire format string unchanged */ + str_arg = starting_p; str_arg_l = p - starting_p; + /* well, not exactly so for Linux, which does something inbetween, + * and I don't feel an urge to imitate it: "%+++++hy" -> "%+y" */ +#else + /* discard the unrecognized conversion, just keep * + * the unrecognized conversion character */ + str_arg = p; str_arg_l = 0; +#endif + if (*p) str_arg_l++; /* include invalid conversion specifier unchanged + if not at end-of-string */ + break; + } + if (*p) p++; /* step over the just processed conversion specifier */ + /* insert padding to the left as requested by min_field_width; + this does not include the zero padding in case of numerical conversions*/ + if (!justify_left) { /* left padding with blank or zero */ + int n = min_field_width - (str_arg_l+number_of_zeros_to_pad); + if (n > 0) { + if (str_l < str_m) { + ssize_t avail = str_m-str_l; + fast_memset(str+str_l, (zero_padding?'0':' '), (n>avail?avail:n)); + } + str_l += n; + } + } + /* zero padding as requested by the precision or by the minimal field width + * for numeric conversions required? */ + if (number_of_zeros_to_pad <= 0) { + /* will not copy first part of numeric right now, * + * force it to be copied later in its entirety */ + zero_padding_insertion_ind = 0; + } else { + /* insert first part of numerics (sign or '0x') before zero padding */ + int n = zero_padding_insertion_ind; + if (n > 0) { + if (str_l < str_m) { + ssize_t avail = str_m-str_l; + fast_memcpy(str+str_l, str_arg, (n>avail?avail:n)); + } + str_l += n; + } + /* insert zero padding as requested by the precision or min field width */ + n = number_of_zeros_to_pad; + if (n > 0) { + if (str_l < str_m) { + ssize_t avail = str_m-str_l; + fast_memset(str+str_l, '0', (n>avail?avail:n)); + } + str_l += n; + } + } + /* insert formatted string + * (or as-is conversion specifier for unknown conversions) */ + { int n = str_arg_l - zero_padding_insertion_ind; + if (n > 0) { + if (str_l < str_m) { + ssize_t avail = str_m-str_l; + fast_memcpy(str+str_l, str_arg+zero_padding_insertion_ind, + (n>avail?avail:n)); + } + str_l += n; + } + } + /* insert right padding */ + if (justify_left) { /* right blank padding to the field width */ + int n = min_field_width - (str_arg_l+number_of_zeros_to_pad); + if (n > 0) { + if (str_l < str_m) { + ssize_t avail = str_m-str_l; + fast_memset(str+str_l, ' ', (n>avail?avail:n)); + } + str_l += n; + } + } + } + } +#if defined(NEED_SNPRINTF_ONLY) + va_end(ap); +#endif + if (str_m > 0) { /* make sure the string is null-terminated + even at the expense of overwriting the last character + (shouldn't happen, but just in case) */ + str[str_l <= str_m-1 ? str_l : str_m-1] = '\0'; + } + /* Return the number of characters formatted (excluding trailing null + * character), that is, the number of characters that would have been + * written to the buffer if it were large enough. + * + * The value of str_l should be returned, but str_l is of unsigned type + * size_t, and snprintf is int, possibly leading to an undetected + * integer overflow, resulting in a negative return value, which is illegal. + * Both XSH5 and ISO C99 (at least the draft) are silent on this issue. + * Should errno be set to EOVERFLOW and EOF returned in this case??? + */ + return (int) str_l; +} +#endif diff --git a/libtu/snprintf_2.2/snprintf.h b/libtu/snprintf_2.2/snprintf.h new file mode 100644 index 0000000..70ec841 --- /dev/null +++ b/libtu/snprintf_2.2/snprintf.h @@ -0,0 +1,26 @@ +#ifndef _PORTABLE_SNPRINTF_H_ +#define _PORTABLE_SNPRINTF_H_ + +#define PORTABLE_SNPRINTF_VERSION_MAJOR 2 +#define PORTABLE_SNPRINTF_VERSION_MINOR 2 + +#ifdef HAVE_SNPRINTF +#include +#else +extern int snprintf(char *, size_t, const char *, /*args*/ ...); +extern int vsnprintf(char *, size_t, const char *, va_list); +#endif + +#if defined(HAVE_SNPRINTF) && defined(PREFER_PORTABLE_SNPRINTF) +extern int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...); +extern int portable_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap); +#define snprintf portable_snprintf +#define vsnprintf portable_vsnprintf +#endif + +extern int asprintf (char **ptr, const char *fmt, /*args*/ ...); +extern int vasprintf (char **ptr, const char *fmt, va_list ap); +extern int asnprintf (char **ptr, size_t str_m, const char *fmt, /*args*/ ...); +extern int vasnprintf(char **ptr, size_t str_m, const char *fmt, va_list ap); + +#endif diff --git a/libtu/snprintf_2.2/test.c b/libtu/snprintf_2.2/test.c new file mode 100644 index 0000000..5fddd82 --- /dev/null +++ b/libtu/snprintf_2.2/test.c @@ -0,0 +1,689 @@ +/* + * test.c - test a portable implementation of snprintf + * + * AUTHOR + * Mark Martinec , April 1999. + * + * Copyright 1999, Mark Martinec. All rights reserved. + * + * TERMS AND CONDITIONS + * This program is free software; you can redistribute it and/or modify + * it under the terms of the "Frontier Artistic License" which comes + * with this Kit. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Frontier Artistic License for more details. + * + * You should have received a copy of the Frontier Artistic License + * with this Kit in the file named LICENSE.txt . + * If not, I'll be glad to provide one. + * + * NOTE: This test program is a QUICK and DIRTY tool + * ===== used while testing and benchmarking my portable snprintf. + * Certain data types are not fully supported, certain test + * cases were fabricated during testing by modifying the code + * or running it by specifying test parameters in the command line. + * + * You are on your own if you want to use this test program! + */ + +/* If no command arguments are specified do the exhaustive test. + * This takes a long time. You may want to reduce the fw and fp + * upper limits in the for loops. + * You may also reduce the number of test elements in the array iargs. + */ + +#include +#include +#include +#include +#include + +#include +#include + +#if defined(NEED_ASPRINTF) || defined(NEED_ASNPRINTF) || defined(NEED_VASPRINTF) || defined(NEED_VASNPRINTF) +# if defined(NEED_SNPRINTF_ONLY) +# undef NEED_SNPRINTF_ONLY +# endif +# if !defined(PREFER_PORTABLE_SNPRINTF) +# define PREFER_PORTABLE_SNPRINTF +# endif +#endif + +#ifdef HAVE_SNPRINTF +extern int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...); +#else +extern int snprintf(char *, size_t, const char *, /*args*/ ...); +extern int vsnprintf(char *, size_t, const char *, va_list); +#endif + +#ifndef HAVE_SNPRINTF +#define portable_snprintf snprintf +#define portable_vsnprintf vsnprintf +#endif + +#ifndef CLOCKS_PER_SEC +#define CLOCKS_PER_SEC 100 +#endif + +#define min(a,b) ((a)<(b) ? (a) : (b)) +#define max(a,b) ((a)>(b) ? (a) : (b)) + +extern int asprintf (char **ptr, const char *fmt, /*args*/ ...); +extern int vasprintf (char **ptr, const char *fmt, va_list ap); +extern int asnprintf (char **ptr, size_t str_m, const char *fmt, /*args*/ ...); +extern int vasnprintf(char **ptr, size_t str_m, const char *fmt, va_list ap); + +#ifndef NEED_SNPRINTF_ONLY +int wrap_vsnprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...); +int wrap_vsnprintf(char *str, size_t str_m, const char *fmt, ...) { + va_list ap; + int str_l; + + va_start(ap, fmt); + str_l = vsnprintf(str, str_m, fmt, ap); + va_end(ap); + return str_l; +} +#endif + +#ifdef NEED_VASPRINTF +int wrap_vasprintf(char **ptr, const char *fmt, /*args*/ ...); +int wrap_vasprintf(char **ptr, const char *fmt, ...) { + va_list ap; + int str_l; + + va_start(ap, fmt); + str_l = vasprintf(ptr, fmt, ap); + va_end(ap); + return str_l; +} +#endif + +#ifdef NEED_VASNPRINTF +int wrap_vasnprintf(char **ptr, size_t str_m, const char *fmt, /*args*/ ...); +int wrap_vasnprintf(char **ptr, size_t str_m, const char *fmt, ...) { + va_list ap; + int str_l; + + va_start(ap, fmt); + str_l = vasnprintf(ptr, str_m, fmt, ap); + va_end(ap); + return str_l; +} +#endif + +int main(int argc, char *argv[]) { + char str1[256], str2[256]; +#ifdef HAVE_SNPRINTF + char str3[256]; +#endif + int len1, len2, len3; + int bad = 0; + size_t str_m = 20; /* declared str size */ + + if (0) { + /* benchmarking */ + const int cnt = 100000; + size_t size; + char str[40000]; + time_t t0,t; + int j,len,l1,l2; + char *p; + int breakpoint; + + size = 18000; + + printf("\n\nsize = %d\n", (int)size); + p = malloc(size); assert(p); + memset(p,'h',size); p[size-1] = '\0'; + t0 = clock(); + + printf("\ndetermine breakeven point to see when it is worth\n"); + printf("calling memcpy and when to do inline string copy\n"); + printf("str_l, memcpy, inline\n"); + for (breakpoint=0; breakpoint<=35; breakpoint++) { + register size_t nnn = (size_t)breakpoint; + printf("%5ld", nnn); + for (j=10*cnt; j>0; j--) memcpy(str, p, nnn); + t = clock(); printf(" %1.3f", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t; + for (j=10*cnt; j>0; j--) { + register size_t nn = (size_t)breakpoint; + if (nn > 0) { + register char *dd; register const char *ss; + for (ss=p, dd=str; nn>0; nn--) *dd++ = *ss++; + } + } + t = clock(); printf(" %1.3f\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t; + } + + printf("\nmeasuring time to SKIP a long format with no conversions\n"); + p[0] = '%'; p[1] = 's'; + for (j=cnt; j>0; j--) l1=portable_snprintf(NULL,(size_t)0,p,"1234567890"); + t = clock(); printf("t_port_nul = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t; + for (j=cnt; j>0; j--) l2=portable_snprintf(str,(size_t)8,p,"1234567890"); + t = clock(); printf("t_port = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t; + assert(l1==l2); + p[0] = p[1] = 'h'; + + printf("\nmeasuring time to copy a long format with no conversions\n"); + for (j=cnt; j>0; j--) l1=portable_snprintf(NULL,(size_t)0,p); + t = clock(); printf("t_port_nul = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t; + for (j=cnt; j>0; j--) l2=portable_snprintf(str,sizeof(str),p); + t = clock(); printf("t_port = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t; + assert(l1==l2); + for (j=cnt; j>0; j--) sprintf(str,p); + t = clock(); printf("t_sys = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t; + + printf("\nmeasuring time to copy a long format with one conversion\n"); + p[size-10] = '%'; + for (j=cnt; j>0; j--) l1=portable_snprintf(NULL,(size_t)0,p); + t = clock(); printf("t_port_nul = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t; + for (j=cnt; j>0; j--) l2=portable_snprintf(str,sizeof(str),p); + t = clock(); printf("t_port = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t; + assert(l1==l2); + for (j=cnt; j>0; j--) sprintf(str,p); + t = clock(); printf("t_sys = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t; + + printf("\nmeasuring string argument copy speed\n"); + for (j=cnt; j>0; j--) l1=portable_snprintf(NULL,(size_t)0,"%.18000s",p); + t = clock(); printf("t_port_nul = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t; + for (j=cnt; j>0; j--) l2=portable_snprintf(str,sizeof(str),"%.18000s",p); + t = clock(); printf("t_port = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t; + assert(l1==l2); + for (j=cnt; j>0; j--) sprintf(str,"%.18000s",p); + t = clock(); printf("t_sys = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t; + + printf("\nmeasuring left padding speed\n"); + p[0] = '\0'; + for (j=cnt; j>0; j--) l1=portable_snprintf(NULL,(size_t)0,"%-18000s",p); + t = clock(); printf("t_port_nul = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t; + for (j=cnt; j>0; j--) l2=portable_snprintf(str,sizeof(str),"%-18000s",p); + t = clock(); printf("t_port = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t; + assert(l1==l2); + for (j=cnt; j>0; j--) sprintf(str,"%-18000s",p); + t = clock(); printf("t_sys = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t; + + printf("\nmeasuring right padding speed\n"); + p[0] = '\0'; + for (j=cnt; j>0; j--) l1=portable_snprintf(NULL,(size_t)0,"%18000s",p); + t = clock(); printf("t_port_nul = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t; + for (j=cnt; j>0; j--) l2=portable_snprintf(str,sizeof(str),"%18000s",p); + t = clock(); printf("t_port = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t; + assert(l1==l2); + for (j=cnt; j>0; j--) sprintf(str,"%18000s",p); + t = clock(); printf("t_sys = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t; + + printf("\nmeasuring zero padding speed\n"); + for (j=cnt; j>0; j--) l1=portable_snprintf(NULL,(size_t)0,"%018000d",1); + t = clock(); printf("t_port_nul = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t; + for (j=cnt; j>0; j--) l2=portable_snprintf(str,sizeof(str),"%018000d",1); + t = clock(); printf("t_port = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t; + assert(l1==l2); + for (j=cnt; j>0; j--) sprintf(str,"%018000d",1); + t = clock(); printf("t_sys = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t; + + printf("\nmeasuring system's sprintf to efficiently handle truncated strings\n"); + memset(p,'h',size); p[size-1] = '\0'; + t0 = clock(); + for (j=cnt; j>0; j--) len = strlen(p); + printf("len = %d\n", len); + t = clock(); printf("t_strlen = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t; +/* test if the system sprintf scans the whole string (e.g. by strlen) + * before recognizing this was a bad idea since the format specified + * a truncated string precision, e.g. "%.8s" . + */ + for (j=cnt; j>0; j--) sprintf(str,"%.2s",p); + t = clock(); printf("t_sys = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t; +#ifdef HAVE_SNPRINTF + for (j=cnt; j>0; j--) snprintf(str,sizeof(str),"%.2s",p); + t = clock(); printf("t_sys = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t; +#endif + for (j=cnt; j>0; j--) portable_snprintf(str,sizeof(str),"%.2s",p); + t = clock(); printf("t_port = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t; + + free(p); + return 0; + } + + +/* preliminary halfhearted test */ +{ + const char fmt[] = "Bla%.4s%05iHE%%%-50sTail"; + char *ptr4=0, *ptr5=0, *ptr6=0, *ptr7=0; + int len1f; + char str_full[256]; + + printf("\npreliminary test: snprintf\n"); + len1 = snprintf(str1, str_m, fmt, "abcdef",-12,"str"); + len1f = snprintf(str_full, sizeof(str_full), fmt, "abcdef",-12,"str"); + assert(len1f==len1); + assert(memcmp(str1,str_full,min(len1,str_m-1)) == 0); + assert(str1[str_m-1] == '\0'); + assert(str_full[sizeof(str_full)-1] == '\0'); + +#ifndef NEED_SNPRINTF_ONLY + printf("preliminary test: vsnprintf\n"); + len2 = wrap_vsnprintf(str2, str_m, fmt, "abcdef",-12,"str"); + assert(len2==len1); + assert(memcmp(str1,str2,min(len1+1,str_m)) == 0); + assert(str2[str_m-1] == '\0'); +#endif + +#ifdef NEED_ASPRINTF + printf("preliminary test: asprintf\n"); + len4 = asprintf(&ptr4, fmt, "abcdef",-12,"str"); + assert(ptr4); + assert(len4==len1); + assert(memcmp(str_full,ptr4,min(len4+1,sizeof(str_full))) == 0); + assert(ptr4[len4] == '\0'); +#endif + +#ifdef NEED_ASNPRINTF + printf("preliminary test: asnprintf\n"); + len5 = asnprintf(&ptr5, str_m, fmt, "abcdef",-12,"str"); + assert(ptr5); + assert(len5==len1); + assert(memcmp(str1,ptr5,min(len5+1,str_m)) == 0); + assert(ptr5[len5] == '\0'); +#endif + +#ifdef NEED_VASPRINTF + printf("preliminary test: vasprintf\n"); + len6 = wrap_vasprintf(&ptr6, fmt, "abcdef",-12,"str"); + assert(ptr6); + assert(len6==len1); + assert(memcmp(str_full,ptr6,min(len6+1,sizeof(str_full))) == 0); + assert(ptr6[len6] == '\0'); +#endif + +#ifdef NEED_VASNPRINTF + printf("preliminary test: vasnprintf\n"); + len7 = wrap_vasnprintf(&ptr7, str_m, fmt, "abcdef",-12,"str"); + assert(ptr7); + assert(len7==len1); + assert(memcmp(str1,ptr7,min(len7+1,str_m)) == 0); + assert(ptr7[len7] == '\0'); +#endif + + if (ptr4) free(ptr4); + if (ptr5) free(ptr5); + if (ptr6) free(ptr6); + if (ptr7) free(ptr7); +} + +/* second preliminary halfhearted test */ +{ + printf("\nsecond preliminary test:\n"); + + printf("test 0a\n"); + memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2)); + len1 = snprintf(str1, sizeof(str1), ""); + len2 = sprintf (str2, ""); + assert(len1==len2); + assert(memcmp(str1,str2,(size_t)len1) == 0); + + printf("test 0b\n"); + memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2)); + len1 = snprintf(str1, sizeof(str1), "YK"); + len2 = sprintf (str2, "YK"); + assert(len1==len2); + assert(memcmp(str1,str2,(size_t)len1) == 0); + + printf("test 1\n"); + memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2)); + len1 = snprintf(str1, sizeof(str1), "%+d",0); + len2 = sprintf (str2, "%+d",0); + assert(len1==len2); + assert(memcmp(str1,str2,(size_t)len1) == 0); + + printf("test 2\n"); + memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2)); + len1 = snprintf(str1, sizeof(str1), "%.2147483647s", "13"); + len2 = sprintf (str2, "%.2147483647s", "13"); + printf("len1=%d, len2=%d\n", len1,len2); + assert(len1==len2); + assert(memcmp(str1,str2,(size_t)len1) == 0); + + printf("test 3a\n"); + memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2)); + len1 = snprintf(str1, sizeof(str1), "%.2147483648s", "13"); + len2 = sprintf (str2, "%.2147483648s", "13"); + printf("len1=%d, len2=%d\n", len1,len2); + assert(len1==len2); + assert(memcmp(str1,str2,(size_t)len1) == 0); + + printf("test 3b\n"); + memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2)); + len1 = snprintf(str1, sizeof(str1), "%.2147483649s", "13"); + len2 = sprintf (str2, "%.2147483649s", "13"); + printf("len1=%d, len2=%d\n", len1,len2); + assert(len1==len2); + assert(memcmp(str1,str2,(size_t)len1) == 0); + + printf("test 4\n"); + memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2)); + len1 = snprintf(str1, sizeof(str1), "%-.2147483647s", "13"); + len2 = sprintf (str2, "%-.2147483647s", "13"); + printf("len1=%d, len2=%d\n", len1,len2); + assert(len1==len2); + assert(memcmp(str1,str2,(size_t)len1) == 0); + + printf("test 5\n"); + memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2)); + len1 = snprintf(str1, sizeof(str1), "%-.2147483648s", "13"); + len2 = sprintf (str2, "%-.2147483648s", "13"); + printf("len1=%d, len2=%d\n", len1,len2); + assert(len1==len2); + assert(memcmp(str1,str2,(size_t)len1) == 0); + + printf("test 6\n"); + memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2)); + len1 = snprintf(str1, sizeof(str1), "%.4294967295s", "13"); + len2 = sprintf (str2, "%.4294967295s", "13"); + printf("len1=%d, len2=%d\n", len1,len2); + assert(len1==len2); + assert(memcmp(str1,str2,(size_t)len1) == 0); + + printf("test 7\n"); + memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2)); + len1 = snprintf(str1, sizeof(str1), "%.4294967296s", "13"); + len2 = sprintf (str2, "%.4294967296s", "13"); + printf("len1=%d, len2=%d\n", len1,len2); + assert(len1==len2); + assert(memcmp(str1,str2,(size_t)len1) == 0); + + + printf("test 12\n"); + memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2)); + len1 = snprintf(str1, sizeof(str1), "%.*s", 2147483647,"13"); + len2 = sprintf (str2, "%.*s", 2147483647,"13"); + printf("len1=%d, len2=%d\n", len1,len2); + assert(len1==len2); + assert(memcmp(str1,str2,(size_t)len1) == 0); + + printf("test 13\n"); + memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2)); + len1 = snprintf(str1, sizeof(str1), "%.*s", 2147483648U,"13"); + len2 = sprintf (str2, "%.*s", 2147483648U,"13"); + printf("len1=%d, len2=%d\n", len1,len2); + assert(len1==len2); + assert(memcmp(str1,str2,(size_t)len1) == 0); + + printf("test 14\n"); + memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2)); + len1 = snprintf(str1, sizeof(str1), "%-.*s", 2147483647,"13"); + len2 = sprintf (str2, "%-.*s", 2147483647,"13"); + printf("len1=%d, len2=%d\n", len1,len2); + assert(len1==len2); + assert(memcmp(str1,str2,(size_t)len1) == 0); + + printf("test 15\n"); + memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2)); + len1 = snprintf(str1, sizeof(str1), "%-.*s", 2147483648U,"13"); + len2 = sprintf (str2, "%-.*s", 2147483648U,"13"); + printf("len1=%d, len2=%d\n", len1,len2); + assert(len1==len2); + assert(memcmp(str1,str2,(size_t)len1) == 0); + + printf("test 16\n"); + memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2)); + len1 = snprintf(str1, sizeof(str1), "%.*s", 4294967295U,"13"); + len2 = sprintf (str2, "%.*s", 4294967295U,"13"); + printf("len1=%d, len2=%d\n", len1,len2); + assert(len1==len2); + assert(memcmp(str1,str2,(size_t)len1) == 0); + + printf("test 17\n"); + memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2)); + len1 = snprintf(str1, sizeof(str1), "%.*s", 4294967296U,"13"); +/* len2 = sprintf (str2, "%.*s", 4294967296U,"13"); */ /* core dumps on HPUX */ +/* assert(len1==len2); + * assert(memcmp(str1,str2,(size_t)len1) == 0); + */ + + + printf("test 95\n"); + memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2)); + len1 = snprintf(str1, sizeof(str1), "%c",'A'); + len2 = sprintf (str2, "%c",'A'); + assert(len1==len2); + assert(memcmp(str1,str2,(size_t)len1) == 0); + + printf("test 96\n"); + memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2)); + len1 = snprintf(str1, sizeof(str1), "%10c",'A'); + len2 = sprintf (str2, "%10c",'A'); + assert(len1==len2); + assert(memcmp(str1,str2,(size_t)len1) == 0); + + printf("test 97\n"); + memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2)); + len1 = snprintf(str1, sizeof(str1), "%-10c",'A'); + len2 = sprintf (str2, "%-10c",'A'); + assert(len1==len2); + assert(memcmp(str1,str2,(size_t)len1) == 0); + + printf("test 98\n"); + memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2)); + len1 = snprintf(str1, sizeof(str1), "%.10c",'A'); + len2 = sprintf (str2, "%.10c",'A'); + assert(len1==len2); + assert(memcmp(str1,str2,(size_t)len1) == 0); + + printf("test 99\n"); + memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2)); + len1 = snprintf(str1, sizeof(str1), "%-.10c",'A'); + len2 = sprintf (str2, "%-.10c",'A'); + assert(len1==len2); + assert(memcmp(str1,str2,(size_t)len1) == 0); + + + printf("test 100\n"); + memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2)); + len1 = snprintf(str1, (size_t)8, "blaBhb%shehe%cX","ABCD",'1'); + len2 = sprintf (str2, "blaBhb%shehe%cX","ABCD",'1'); + assert(len1==len2); + assert(memcmp(str1,str2,(size_t)7) == 0); + assert(str1[7] == '\0'); + assert(memcmp(str1+14,str2+16,(size_t)(len1-16)) == 0); + +} + + /* testing for correctness and compatibility */ + if (argc >= 3) { + char *c; int alldigits = 1; + for (c=argv[2]; *c; c++) + if (! (*c == '-' || (*c >= '0' && *c <= '9'))) alldigits = 0; + if (alldigits) { + int j = atoi(argv[2]); + len1 = portable_snprintf(str1, str_m, argv[1], j, 3); + len2 = sprintf(str2, argv[1], j, 3); +#ifdef HAVE_SNPRINTF + len3 = snprintf(str3, str_m, argv[1], j, 3); +#endif + } else { + len1 = portable_snprintf(str1, str_m, argv[1], argv[2], 3); + len2 = sprintf(str2, argv[1], argv[2], 3); +#ifdef HAVE_SNPRINTF + len3 = snprintf(str3, str_m, argv[1], argv[2], 3); +#endif + } + printf("portable: |%s| len = %d\n", str1, len1); + printf("sys sprintf: |%s| len = %d\n", str2, len2); +#ifdef HAVE_SNPRINTF + printf("sys snprintf: |%s| len = %d\n", str3, len3); +#endif + } else { /* exhaustive testing */ + + const char flags[] = "+- 0#"; /* set of test flags (including '\0')*/ + int flags_l = strlen(flags); + + const char fspec[] = "scdpoxXuiy"; /* set of test formats (including '\0') */ + int fspec_l = strlen(fspec); + +#ifdef SNPRINTF_LONGLONG_SUPPORT + const char datatype[] = " hl2"; /* set of datatypes */ +#else + const char datatype[] = " hl"; /* set of datatypes */ +#endif + int datatype_l = strlen(datatype); + + const long int iargs[] = /* set of numeric test arguments */ + { 0,1,9,10,28,99,100,127,128,129,998,1000,32767,32768,32769, + -1,-9,-10,-28,-99,-100,-127,-128,-129, + -998,-1000,-32767,-32768,-32769 }; + int iargs_l = sizeof(iargs)/sizeof(iargs[0]); + const char *sargs[] = /* set of string test arguments */ + { "", "a", "0", "-ab", "abcde", "abcdefghijk mnopqrstuv" }; + int sargs_l = sizeof(sargs)/sizeof(sargs[0]); + + char fmt[256]; + int fmt_l; + int a, fs, fl1, fl2, fl3, fl4, fl5, fw, fp, dt; + + for (fs=0; fs<=fspec_l; fs++) { /* format specifier */ + int strtype = (fspec[fs] == 's' || fspec[fs] == '%'); + int args_l = (strtype ? sargs_l : iargs_l); + + for (fw= -1; fw<=3; fw++) { /* minimal field width */ + + printf("Trying format %%"); + if (fw >= 0) printf("%d", fw); + if (fspec[fs]) putchar(fspec[fs]); + putchar('\n'); + + for (fp= -2; fp<=3; fp++) { /* format field precision */ + + /* data type modifiers */ + for (dt=0; dt < ((strtype||fspec[fs]=='c') ? 1 : datatype_l); dt++) { + + int dataty = datatype[dt]; + + if (fspec[fs] == 'D' || fspec[fs] == 'U' || fspec[fs] == 'O') + dataty = 'l'; + + if (fspec[fs] == 'p' && dataty == '2') continue; + + for (fl1=0; fl1<=flags_l; fl1++) { /* flags */ + for (fl2=0; fl2<=flags_l; fl2++) { + for (fl3=0; fl3<=flags_l; fl3++) { + for (fl4=0; fl4<=flags_l; fl4++) { + for (fl5=0; fl5<=flags_l; fl5++) { + + for (a=0; a= 0) fmt_l += sprintf(fmt+fmt_l, "%d", fw); + if (fp >= -1) { + fmt[fmt_l++] = '.'; + if (fp >= 0) fmt_l += sprintf(fmt+fmt_l, "%d", fp); + } + if (dataty == '2') + { fmt[fmt_l++] = 'l'; fmt[fmt_l++] = 'l'; } + else if (dataty != ' ') + { fmt[fmt_l++] = dataty; } + + if (fspec[fs]) fmt[fmt_l++] = fspec[fs]; + fmt[fmt_l++] = '\0'; + + if (a==0 && fl1==flags_l && fl2==flags_l && fl3==flags_l + && fl4==flags_l && fl5==flags_l) printf("%s\n", fmt); + +#ifdef HAVE_SNPRINTF + memset(str1,'G',sizeof(str1)); + memset(str2,'G',sizeof(str2)); + memset(str3,'G',sizeof(str3)); +#endif + len1 = len2 = len3 = 0; + if (strtype) { + len1 = portable_snprintf(str1, str_m, fmt, sargs[a]); + len2 = sprintf(str2, fmt, sargs[a]); +#ifdef HAVE_SNPRINTF + len3 = snprintf(str3, str_m, fmt, sargs[a]); +#endif + } else if (fspec[fs] == 'p') { + len1 = portable_snprintf(str1, str_m, fmt, (void *)iargs[a]); + len2 = sprintf(str2, fmt, (void *)iargs[a]); +#ifdef HAVE_SNPRINTF + len3 = snprintf(str3, str_m, fmt, (void *)iargs[a]); +#endif + } else { + switch (dataty) { + case '\0': + len1 = portable_snprintf(str1, str_m, fmt, (int) iargs[a]); + len2 = sprintf(str2, fmt, (int) iargs[a]); +#ifdef HAVE_SNPRINTF + len3 = snprintf(str3, str_m, fmt, (int) iargs[a]); +#endif + break; + case 'h': + len1 = portable_snprintf(str1, str_m, fmt, (short int)iargs[a]); + len2 = sprintf(str2, fmt, (short int)iargs[a]); +#ifdef HAVE_SNPRINTF + len3 = snprintf(str3, str_m, fmt, (short int)iargs[a]); +#endif + break; + case 'l': + len1 = portable_snprintf(str1, str_m, fmt, (long int)iargs[a]); + len2 = sprintf(str2, fmt, (long int)iargs[a]); +#ifdef HAVE_SNPRINTF + len3 = snprintf(str3, str_m, fmt, (long int)iargs[a]); +#endif + break; +#ifdef SNPRINTF_LONGLONG_SUPPORT + case '2': + len1 = portable_snprintf(str1, str_m, fmt, (long long int)iargs[a]); + len2 = sprintf(str2, fmt, (long long int)iargs[a]); +#ifdef HAVE_SNPRINTF + len3 = snprintf(str3, str_m, fmt, (long long int)iargs[a]); +#endif + break; +#endif + } + } + + if (0) { +#ifdef HAVE_SNPRINTF + } else if (len1 != len3 || + memcmp(str1,str3,min(len1+20,sizeof(str1))) != 0) { + bad = 1; + if (strtype) printf("\n2: %s, <%s>\n", fmt, sargs[a]); + else printf("\n2: %s, %ld\n", fmt, iargs[a]); + printf("portable: |%s| len = %d\n", str1, len1); + printf("sys sprintf: |%s| len = %d\n", str2, len2); + printf("sys snprintf: |%s| len = %d\n", str3, len3); +#else + } else if (len1 != len2 || + (len1>0 && memcmp(str1,str2,min(len1,str_m)-1) != 0)) { + bad = 1; + if (strtype) printf("\n1: %s, <%s>\n", fmt, sargs[a]); + else printf("\n1: %s, %ld\n", fmt, iargs[a]); + printf("portable: |%s| len = %d\n", str1, len1); + printf("sys sprintf: |%s| len = %d\n", str2, len2); +#endif + } + if (bad) return(1); + } + + } + } + } + } + + } + } + } + } + } + } + return (bad?1:0); +} diff --git a/libtu/stringstore.c b/libtu/stringstore.c new file mode 100644 index 0000000..6f54387 --- /dev/null +++ b/libtu/stringstore.c @@ -0,0 +1,98 @@ +/* + * libtu/stringstore.c + * + * Copyright (c) Tuomo Valkonen 2004. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#include + +#include "misc.h" +#include "output.h" +#include "rb.h" +#include "stringstore.h" + + +static Rb_node stringstore=NULL; + + +const char *stringstore_get(StringId id) +{ + return (const char*)(((Rb_node)id)->k.key); +} + + +StringId stringstore_find(const char *str) +{ + Rb_node node; + int found=0; + + if(stringstore==NULL) + return STRINGID_NONE; + + node=rb_find_key_n(stringstore, str, &found); + + if(!found) + return STRINGID_NONE; + + return (StringId)node; +} + + +StringId stringstore_alloc(const char *str) +{ + Rb_node node=(Rb_node)stringstore_find(str); + char *s; + + if(node!=NULL){ + node->v.ival++; + return node; + } + + if(stringstore==NULL){ + stringstore=make_rb(); + if(stringstore==NULL) + return STRINGID_NONE; + } + + s=scopy(str); + + if(s==NULL) + return STRINGID_NONE; + + node=rb_insert(stringstore, s, NULL); + + if(node==NULL) + return STRINGID_NONE; + + node->v.ival=1; + + return (StringId)node; +} + + +void stringstore_free(StringId id) +{ + Rb_node node=(Rb_node)id; + + if(node==NULL){ + warn("Attempt to free un-allocated string from stringstore."); + return; + } + + if(node->v.ival<=0){ + warn("Stringstore reference count corrupted."); + return; + } + + node->v.ival--; + + if(node->v.ival==0){ + char *s=(char*)(node->k.key); + rb_delete_node(node); + free(s); + } +} + diff --git a/libtu/stringstore.h b/libtu/stringstore.h new file mode 100644 index 0000000..752e573 --- /dev/null +++ b/libtu/stringstore.h @@ -0,0 +1,22 @@ +/* + * libtu/stringstore.h + * + * Copyright (c) Tuomo Valkonen 2004. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#ifndef LIBTU_STRINGSTORE_H +#define LIBTU_STRINGSTORE_H + +typedef void* StringId; + +#define STRINGID_NONE ((StringId)0) + +extern const char *stringstore_get(StringId id); +extern StringId stringstore_find(const char *str); +extern StringId stringstore_alloc(const char *str); +extern void stringstore_free(StringId id); + +#endif /* LIBTU_STRINGSTORE_H */ diff --git a/libtu/tester.c b/libtu/tester.c new file mode 100644 index 0000000..4f27fd6 --- /dev/null +++ b/libtu/tester.c @@ -0,0 +1,54 @@ +/* + * libtu/tester.c + * + * Copyright (c) Tuomo Valkonen 1999-2002. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#include + +#include "misc.h" +#include "tokenizer.h" +#include "util.h" + +int main(int argc, char *argv[]) +{ + Tokenizer*tokz; + Token tok=TOK_INIT; + + libtu_init(argv[0]); + + if(!(tokz=tokz_open_file(stdin, "stdin"))) + return EXIT_FAILURE; + + while(tokz_get_token(tokz, &tok)){ + switch(tok.type){ + case TOK_LONG: + printf("long - %ld\n", TOK_LONG_VAL(&tok)); + break; + case TOK_DOUBLE: + printf("double - %g\n", TOK_DOUBLE_VAL(&tok)); + break; + case TOK_CHAR: + printf("char - '%c'\n", TOK_CHAR_VAL(&tok)); + break; + case TOK_STRING: + printf("string - \"%s\"\n", TOK_STRING_VAL(&tok)); + break; + case TOK_IDENT: + printf("ident - %s\n", TOK_IDENT_VAL(&tok)); + break; + case TOK_COMMENT: + printf("comment - %s\n", TOK_COMMENT_VAL(&tok)); + break; + case TOK_OP: + printf("operator - %03x\n", TOK_OP_VAL(&tok)); + break; + } + } + + return EXIT_SUCCESS; +} + diff --git a/libtu/tester2.c b/libtu/tester2.c new file mode 100644 index 0000000..832c474 --- /dev/null +++ b/libtu/tester2.c @@ -0,0 +1,69 @@ +/* + * libtu/tester2.c + * + * Copyright (c) Tuomo Valkonen 1999-2002. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#include + +#include "misc.h" +#include "tokenizer.h" +#include "parser.h" +#include "util.h" + + +static bool test_fn(Tokenizer *tokz, int n, Token *toks) +{ + printf("test_fn() %d %s\n", n, TOK_IDENT_VAL(toks)); + + return TRUE; +} + + + +static bool sect_fn(Tokenizer *tokz, int n, Token *toks) +{ + printf("sect_fn() %d %s\n", n, TOK_IDENT_VAL(toks+1)); + + return TRUE; +} + + +static bool test2_fn(Tokenizer *tokz, int n, Token *toks) +{ + printf("test2_fn() %d %s %f\n", n, TOK_BOOL_VAL(toks+1) ? "TRUE" : "FALSE", TOK_DOUBLE_VAL(toks+2)); + + return TRUE; +} + +static bool test3_fn(Tokenizer *tokz, int n, Token *toks) +{ + if(n<=2) + printf("test3_fn() %d \"%s\"\n", n, TOK_STRING_VAL(toks+1)); + else + printf("test3_fn() %d \"%s\" %ld\n", n, TOK_STRING_VAL(toks+1), TOK_LONG_VAL(toks+2)); + + return TRUE; +} + + +static ConfOpt opts[]={ + {"test", NULL, test_fn, NULL}, + {"t2", "bd", test2_fn, NULL}, + {"foo", "s?l", test3_fn, NULL}, + {"sect", "s", sect_fn, opts}, + {NULL, NULL, NULL, NULL} +}; + + +int main(int argc, char *argv[]) +{ + libtu_init(argv[0]); + parse_config_file(stdin, opts, TOKZ_ERROR_TOLERANT); + + return EXIT_SUCCESS; +} + diff --git a/libtu/tester3.c b/libtu/tester3.c new file mode 100644 index 0000000..12bda85 --- /dev/null +++ b/libtu/tester3.c @@ -0,0 +1,72 @@ +/* + * libtu/tester3.c + * + * Copyright (c) Tuomo Valkonen 1999-2002. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#include + +#include "util.h" +#include "misc.h" +#include "optparser.h" + + +static const char usage[]= + "Usage: $p [options]\n" + "\n" + "Where options are:\n" + "$o\n"; + + +static OptParserOpt opts[]={ + {'o', "opt", OPT_ARG, "OPTION", "foo bar baz quk asdf jklö äölk dfgh quik aaaa bbbb cccc dddd eeee ffff"}, + {'f', "file", OPT_ARG, "FILE", "asdfsadlfölökjasdflökjasdflkjöasdflkjöas dlöfjkasdfölkjasdfölkjasdfasdflöjasdfkasödjlfkasdlföjasdölfjkölkasjdfasdfölkjasd asdöljfasöldf asdölfköasdlf asfdlök asdföljkadsfölasdfölasdölkfjasdölfasödlflöskflasdföaölsdf"}, + {'v', "view", 0, NULL, "asfasdf"}, + {'z', "zip", 0, NULL, "asdfasdf"}, + {'x', "extract", 0, NULL, "asdfasdf"}, + {0, NULL, 0, NULL, NULL} +}; + +static OptParserCommonInfo tester3_cinfo={ + NULL, + usage, + NULL +}; + + +int main(int argc, char *argv[]) +{ + int opt; + + libtu_init(argv[0]); + + optparser_init(argc, argv, OPTP_NO_DASH, opts, &tester3_cinfo); + + while((opt=optparser_get_opt())){ + switch(opt){ + case 'o': + printf("opt: %s\n", optparser_get_arg()); + break; + case 'f': + printf("file: %s\n", optparser_get_arg()); + break; + case 'v': + printf("view\n"); + break; + case 'z': + printf("zip\n"); + break; + case 'x': + printf("extract\n"); + break; + default: + optparser_print_error(); + return 1; + } + } + return 0; +} + diff --git a/libtu/tokenizer.c b/libtu/tokenizer.c new file mode 100644 index 0000000..cf92942 --- /dev/null +++ b/libtu/tokenizer.c @@ -0,0 +1,958 @@ +/* + * libtu/tokenizer.c + * + * Copyright (c) Tuomo Valkonen 1999-2002. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "tokenizer.h" +#include "misc.h" +#include "output.h" +#include "private.h" + + +static const char *errors[]={ + DUMMY_TR("(no error)"), + DUMMY_TR("Unexpected end of file"), /* E_TOKZ_UNEXPECTED_EOF */ + DUMMY_TR("Unexpected end of line"), /* E_TOKZ_UNEXPECTED_EOL */ + DUMMY_TR("End of line expected"), /* E_TOKZ_EOL_EXPECTED */ + DUMMY_TR("Invalid character"), /* E_TOKZ_INVALID_CHAR*/ + DUMMY_TR("Numeric constant too big"), /* E_TOKZ_TOOBIG */ + DUMMY_TR("Invalid numberic format"), /* E_TOKZ_NUMFMT */ + DUMMY_TR("Junk after numeric constant"), /* E_TOKZ_NUM_JUNK */ + DUMMY_TR("Not an integer"), /* E_TOKZ_NOTINT */ + DUMMY_TR("Numeric constant out of range"), /* E_TOKZ_RANGE */ + DUMMY_TR("Multi-character character constant"), /* E_TOKZ_MULTICHAR */ + DUMMY_TR("Token/statement limit reached"), /* E_TOKZ_TOKEN_LIMIT */ + DUMMY_TR("Unknown option"), /* E_TOKZ_UNKONWN_OPTION */ + DUMMY_TR("Syntax error"), /* E_TOKZ_SYNTAX */ + DUMMY_TR("Invalid argument"), /* E_TOKZ_INVALID_ARGUMENT */ + DUMMY_TR("End of statement expected"), /* E_TOKZ_EOS_EXPECTED */ + DUMMY_TR("Too few arguments"), /* E_TOKZ_TOO_FEW_ARGS */ + DUMMY_TR("Too many arguments"), /* E_TOKZ_TOO_MANY_ARGS */ + DUMMY_TR("Maximum section nestin level exceeded"), /* E_TOK_Z_MAX_NEST */ + DUMMY_TR("Identifier expected"), /* E_TOKZ_IDENTIFIER_EXPECTED */ + DUMMY_TR("Starting brace ('{') expected"), /* E_TOKZ_LBRACE_EXPECTED */ +}; + + +/* */ + +#define STRBLEN 32 + +#define STRING_DECL(X) int err=0; char* X=NULL; char X##_tmp[STRBLEN]; int X##_tmpl=0 +#define STRING_DECL_P(X, P) int err=0; char* X=NULL; char X##_tmp[STRBLEN]=P; int X##_tmpl=sizeof(P)-1 +#define STRING_APPEND(X, C) {if(!_string_append(&X, X##_tmp, &X##_tmpl, c)) err=-ENOMEM;} +#define STRING_FREE(X) if(X!=NULL) free(X) +#define STRING_FINISH(X) {if(err!=0) return err; if(!_string_finish(&X, X##_tmp, X##_tmpl)) err=-ENOMEM;} + + +static bool _string_append(char **p, char *tmp, int *tmplen, char c) +{ + char *tmp2; + + if(*tmplen==STRBLEN-1){ + tmp[STRBLEN-1]='\0'; + if(*p!=NULL){ + tmp2=scat(*p, tmp); + free(*p); + *p=tmp2; + }else{ + *p=scopy(tmp); + } + *tmplen=1; + tmp[0]=c; + return *p!=NULL; + }else{ + tmp[(*tmplen)++]=c; + return TRUE; + } +} + + +static bool _string_finish(char **p, char *tmp, int tmplen) +{ + char *tmp2; + + if(tmplen==0){ + if(*p==NULL) + *p=scopy(""); + }else{ + tmp[tmplen]='\0'; + if(*p!=NULL){ + tmp2=scat(*p, tmp); + free(*p); + *p=tmp2; + }else{ + *p=scopy(tmp); + } + } + return *p!=NULL; +} + + +/* */ + + +#define INC_LINE() tokz->line++ +#define GETCH() _getch(tokz) +#define UNGETCH(C) _ungetch(tokz, C) + +static int _getch(Tokenizer *tokz) +{ + int c; + + if(tokz->ungetc!=-1){ + c=tokz->ungetc; + tokz->ungetc=-1; + }else if (tokz->flags&TOKZ_READ_FROM_BUFFER) { + assert(tokz->buffer.data!=NULL); + if (tokz->buffer.pos==tokz->buffer.len) + c=EOF; + else + c=tokz->buffer.data[tokz->buffer.pos++]; + }else{ + c=getc(tokz->file); + } + + return c; +} + + +static void _ungetch(Tokenizer *tokz, int c) +{ + tokz->ungetc=c; +} + + +/* */ + + +static int scan_line_comment(Token *tok, Tokenizer *tokz) +{ + STRING_DECL_P(s, "#"); + int c; + + c=GETCH(); + + while(c!='\n' && c!=EOF){ + STRING_APPEND(s, c); + c=GETCH(); + } + + UNGETCH(c); + + STRING_FINISH(s); + + TOK_SET_COMMENT(tok, s); + + return 0; +} + + +static int skip_line_comment(Tokenizer *tokz) +{ + int c; + + do{ + c=GETCH(); + }while(c!='\n' && c!=EOF); + + UNGETCH(c); + + return 0; +} + + +/* */ + + +static int scan_c_comment(Token *tok, Tokenizer *tokz) +{ + STRING_DECL_P(s, "/*"); + int c; + int st=0; + + while(1){ + c=GETCH(); + + if(c==EOF){ + STRING_FREE(s); + return E_TOKZ_UNEXPECTED_EOF; + } + + STRING_APPEND(s, c); + + if(c=='\n'){ + INC_LINE(); + }else if(st==0 && c=='*'){ + st=1; + }else if(st==1){ + if(c=='/') + break; + st=0; + } + } + + STRING_FINISH(s); + + TOK_SET_COMMENT(tok, s); + + return 0; +} + + +static int skip_c_comment(Tokenizer *tokz) +{ + int c; + int st=0; + + while(1){ + c=GETCH(); + + if(c==EOF) + return E_TOKZ_UNEXPECTED_EOF; + + if(c=='\n') + INC_LINE(); + else if(st==0 && c=='*') + st=1; + else if(st==1){ + if(c=='/') + break; + st=0; + } + } + + return 0; +} + + +/* */ + + +static int scan_char_escape(Tokenizer *tokz) +{ + static char* special_chars="nrtbae"; + static char* specials="\n\r\t\b\a\033"; + int base, max; + int i ,c; + + c=GETCH(); + + for(i=0;special_chars[i];i++){ + if(special_chars[i]==c) + return specials[c]; + } + + if(c=='x' || c=='X'){ + base=16;max=2;i=0; + }else if(c=='d' || c=='D'){ + base=10;max=3;i=0; + }else if(c=='8' || c=='9'){ + base=10;max=2;i=c-'0'; + }else if('0'<=c && c<='7'){ + base=8;max=2;i=c-'0'; + }else if(c=='\n'){ + UNGETCH(c); + return -2; + }else{ + return c; + } + + + while(--max>=0){ + c=GETCH(); + + if(c==EOF) + return EOF; + + if(c=='\n'){ + UNGETCH(c); + return -2; + } + + if(base==16){ + if(!isxdigit(c)) + break; + + i<<=4; + + if(isdigit(c)) + i+=c-'0'; + else if(i>='a') + i+=0xa+c-'a'; + else + i+=0xa+c-'a'; + + }else if(base==10){ + if(!isdigit(c)) + break; + i*=10; + i+=c-'0'; + }else{ + if(c<'0' || c>'7') + break; + i<<=3; + i+=c-'0'; + } + } + + if(max>=0) + UNGETCH(c); + + return i; +} + + +/* */ + + +static int scan_string(Token *tok, Tokenizer *tokz, bool escapes) +{ + STRING_DECL(s); + int c; + + while(1){ + c=GETCH(); + + if(c=='"') + break; + + if(c=='\n'){ + UNGETCH(c); + STRING_FREE(s); + return E_TOKZ_UNEXPECTED_EOL; + } + + if(c=='\\' && escapes){ + c=scan_char_escape(tokz); + if(c==-2){ + STRING_FREE(s); + return E_TOKZ_UNEXPECTED_EOL; + } + } + + if(c==EOF){ + STRING_FREE(s); + return E_TOKZ_UNEXPECTED_EOF; + } + + STRING_APPEND(s, c); + } + + STRING_FINISH(s); + + TOK_SET_STRING(tok, s); + + return 0; +} + + +/* */ + + +static int scan_char(Token *tok, Tokenizer *tokz) +{ + int c, c2; + + c=GETCH(); + + if(c==EOF) + return E_TOKZ_UNEXPECTED_EOF; + + if(c=='\n') + return E_TOKZ_UNEXPECTED_EOL; + + if(c=='\\'){ + c=scan_char_escape(tokz); + + if(c==EOF) + return E_TOKZ_UNEXPECTED_EOF; + + if(c==-2) + return E_TOKZ_UNEXPECTED_EOL; + } + + c2=GETCH(); + + if(c2!='\'') + return E_TOKZ_MULTICHAR; + + TOK_SET_CHAR(tok, c); + + return 0; +} + + +/* */ + + +#define START_IDENT(X) (isalpha(X) || X=='_' || X=='$') + + +static int scan_identifier(Token *tok, Tokenizer *tokz, int c) +{ + STRING_DECL(s); + + do{ + STRING_APPEND(s, c); + c=GETCH(); + }while(isalnum(c) || c=='_' || c=='$'); + + UNGETCH(c); + + STRING_FINISH(s); + + TOK_SET_IDENT(tok, s); + + return 0; +} + +#define NP_SIMPLE_IMPL +#include "np/numparser2.h" +#include "np/np-conv.h" + + +static int scan_number(Token *tok, Tokenizer *tokz, int c) +{ + NPNum num=NUM_INIT; + int e; + + if((e=parse_number(&num, tokz, c))) + return e; + + if(num.type==NPNUM_INT){ + long l; + if((e=num_to_long(&l, &num, TRUE))) + return e; + + TOK_SET_LONG(tok, l); + }else if(num.type==NPNUM_FLOAT){ + double d; + if((e=num_to_double(&d, &num))) + return e; + + TOK_SET_DOUBLE(tok, d); + }else{ + return E_TOKZ_NUMFMT; + } + + return 0; +} + + +/* */ + + +static uchar op_map[]={ + 0x00, /* ________ 0-7 */ + 0x00, /* ________ 8-15 */ + 0x00, /* ________ 16-23 */ + 0x00, /* ________ 24-31 */ + 0x62, /* _!___%&_ 32-39 */ + 0xff, /* ()*+,-./ 40-47 */ + 0x00, /* ________ 48-55 */ + 0xfc, /* __:;<=>? 56-63 */ + 0x01, /* @_______ 64-71 */ + 0x00, /* ________ 72-79 */ + 0x00, /* ________ 80-87 */ + 0x78, /* ___[_]^_ 88-95 */ + 0x00, /* ________ 96-103 */ + 0x00, /* ________ 104-111 */ + 0x00, /* ________ 112-119 */ + 0x38 /* ___{|}__ 120-127 */ +}; + + +static bool map_isset(uchar *map, uint ch) +{ + if(ch>127) + return FALSE; + + return map[ch>>3]&(1<<(ch&7)); +} + + +static bool is_opch(uint ch) +{ + return map_isset(op_map, ch); +} + + +static int scan_op(Token *tok, Tokenizer *tokz, int c) +{ + int c2; + int op=-1; + + /* Quickly check it is an operator character */ + if(!is_opch(c)) + return E_TOKZ_INVALID_CHAR; + + switch(c){ + case '+': + case '-': + case '*': +/* case '/': Checked elsewhere */ + case '%': + case '^': + case '!': + case '=': + case '<': + case '>': + c2=GETCH(); + if(c2=='='){ + op=c|(c2<<8); + }else if(c2==c && (c2!='%' && c2!='!' && c2!='*')){ + if(c=='<' || c=='>'){ + int c3=GETCH(); + if(c3=='='){ + op=c|(c2<<8)|(c3<<16); + }else{ + UNGETCH(c3); + op=c|(c2<<8); + } + }else{ + op=c|(c2<<8); + } + }else{ + UNGETCH(c2); + op=c; + } + break; + + /* It is already known that it is a operator so these are not needed + case ':': + case '~': + case '?': + case '.': + case ';'; + case '{': + case '}': + case '@': + case '|': + case '&': + */ + default: + op=c; + } + + TOK_SET_OP(tok, op); + + return 0; +} + + +/* */ + + +void tokz_warn(const Tokenizer *tokz, int line, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + + if(tokz!=NULL) + warn_obj_line_v(tokz->name, line, fmt, args); + else + warn(fmt, args); + + va_end(args); +} + + +void tokz_warn_error(const Tokenizer *tokz, int line, int e) +{ + if(e==E_TOKZ_UNEXPECTED_EOF) + line=0; + + if(e<0) + tokz_warn(tokz, line, "%s", strerror(-e)); + else + tokz_warn(tokz, line, "%s", TR(errors[e])); +} + + +bool tokz_get_token(Tokenizer *tokz, Token *tok) +{ + int c, c2, e; + + if (!(tokz->flags&TOKZ_READ_FROM_BUFFER)) + assert(tokz->file!=NULL); + + tok_free(tok); + + if(!TOK_IS_INVALID(&(tokz->ungettok))){ + *tok=tokz->ungettok; + tokz->ungettok.type=TOK_INVALID; + return TRUE; + } + + while(1){ + + e=0; + + do{ + c=GETCH(); + }while(c!='\n' && c!=EOF && isspace(c)); + + tok->line=tokz->line; + + switch(c){ + case EOF: + TOK_SET_OP(tok, OP_EOF); + return TRUE; + + case '\n': + INC_LINE(); + + if(tokz->flags&TOKZ_IGNORE_NEXTLINE) + continue; + + TOK_SET_OP(tok, OP_NEXTLINE); + + return TRUE; + + case '\\': + do{ + c=GETCH(); + if(c==EOF){ + TOK_SET_OP(tok, OP_EOF); + return FALSE; + } + if(!isspace(c) && e==0){ + e=E_TOKZ_EOL_EXPECTED; + tokz_warn_error(tokz, tokz->line, e); + if(!(tokz->flags&TOKZ_ERROR_TOLERANT)) + return FALSE; + } + }while(c!='\n'); + + INC_LINE(); + continue; + + case '#': + if(tokz->flags&TOKZ_READ_COMMENTS){ + e=scan_line_comment(tok, tokz); + break; + }else if((e=skip_line_comment(tokz))){ + break; + } + + continue; + + case '/': + c2=GETCH(); + + if(c2=='='){ + TOK_SET_OP(tok, OP_AS_DIV); + return TRUE; + } + + if(c2!='*'){ + UNGETCH(c2); + TOK_SET_OP(tok, OP_DIV); + return TRUE; + } + + if(tokz->flags&TOKZ_READ_COMMENTS){ + e=scan_c_comment(tok, tokz); + break; + }else if((e=skip_c_comment(tokz))){ + break; + } + + continue; + + case '\"': + e=scan_string(tok, tokz, TRUE); + break; + + case '\'': + e=scan_char(tok, tokz); + break; + + default: + if(('0'<=c && c<='9') || c=='-' || c=='+'){ + e=scan_number(tok, tokz, c); + break; + } + + if(START_IDENT(c)) + e=scan_identifier(tok, tokz, c); + else + e=scan_op(tok, tokz, c); + } + + if(!e) + return TRUE; + + tokz_warn_error(tokz, tokz->line, e); + return FALSE; + } +} + + +void tokz_unget_token(Tokenizer *tokz, Token *tok) +{ + tok_free(&(tokz->ungettok)); + tokz->ungettok=*tok; + tok->type=TOK_INVALID; +} + + +/* + * File open + */ + +static bool do_tokz_pushf(Tokenizer *tokz) +{ + Tokenizer_FInfo *finfo; + + finfo=REALLOC_N(tokz->filestack, Tokenizer_FInfo, + tokz->filestack_n, tokz->filestack_n+1); + + if(finfo==NULL) + return FALSE; + + tokz->filestack=finfo; + finfo=&(finfo[tokz->filestack_n++]); + + finfo->file=tokz->file; + finfo->name=tokz->name; + finfo->line=tokz->line; + finfo->ungetc=tokz->ungetc; + finfo->ungettok=tokz->ungettok; + + return TRUE; +} + + +bool tokz_pushf_file(Tokenizer *tokz, FILE *file, const char *fname) +{ + char *fname_copy=NULL; + + if(file==NULL) + return FALSE; + + if(fname!=NULL){ + fname_copy=scopy(fname); + if(fname_copy==NULL){ + warn_err(); + return FALSE; + } + } + + if(tokz->file!=NULL){ + if(!do_tokz_pushf(tokz)){ + warn_err(); + if(fname_copy!=NULL) + free(fname_copy); + return FALSE; + } + } + + tokz->file=file; + tokz->name=fname_copy; + tokz->line=1; + tokz->ungetc=-1; + tokz->ungettok.type=TOK_INVALID; + + return TRUE; +} + + +bool tokz_pushf(Tokenizer *tokz, const char *fname) +{ + FILE *file; + + file=fopen(fname, "r"); + + if(file==NULL){ + warn_err_obj(fname); + return FALSE; + } + + if(!tokz_pushf_file(tokz, file, fname)){ + fclose(file); + return FALSE; + } + + return TRUE; +} + + + +static Tokenizer *tokz_create() +{ + Tokenizer*tokz; + + tokz=ALLOC(Tokenizer); + + if(tokz==NULL){ + warn_err(); + return NULL; + } + + tokz->file=NULL; + tokz->name=NULL; + tokz->line=1; + tokz->ungetc=-1; + tokz->ungettok.type=TOK_INVALID; + tokz->flags=0; + tokz->optstack=NULL; + tokz->nest_lvl=0; + tokz->filestack_n=0; + tokz->filestack=NULL; + tokz->buffer.data=0; + tokz->buffer.len=0; + tokz->buffer.pos=0; + + return tokz; +} + + +Tokenizer *tokz_open(const char *fname) +{ + Tokenizer *tokz; + + tokz=tokz_create(); + + if(!tokz_pushf(tokz, fname)){ + free(tokz); + return NULL; + } + + return tokz; +} + + +Tokenizer *tokz_open_file(FILE *file, const char *fname) +{ + Tokenizer *tokz; + + tokz=tokz_create(); + + if(!tokz_pushf_file(tokz, file, fname)){ + free(tokz); + return NULL; + } + + return tokz; +} + +Tokenizer *tokz_prepare_buffer(char *buffer, int len) +{ + Tokenizer *tokz; + char old=0; + + tokz=tokz_create(); + if(len>0){ + old=buffer[len-1]; + buffer[len-1]='\0'; + } + + tokz->flags|=TOKZ_READ_FROM_BUFFER; + tokz->buffer.data=scopy(buffer); + tokz->buffer.len=(len>0 ? (uint)len : strlen(tokz->buffer.data)); + tokz->buffer.pos=0; + + if(old>0) + buffer[len-1]=old; + + return tokz; +} + +/* + * File close + */ + +static bool do_tokz_popf(Tokenizer *tokz, bool shrink) +{ + Tokenizer_FInfo *finfo; + + if(tokz->filestack_n<=0) + return FALSE; + + if(tokz->file!=NULL) + fclose(tokz->file); + if(tokz->name!=NULL) + free(tokz->name); + + finfo=&(tokz->filestack[--tokz->filestack_n]); + + tokz->file=finfo->file; + tokz->name=finfo->name; + tokz->line=finfo->line; + tokz->ungetc=finfo->ungetc; + tokz->ungettok=finfo->ungettok; + + if(tokz->filestack_n==0){ + free(tokz->filestack); + tokz->filestack=NULL; + }else if(shrink){ + finfo=REALLOC_N(tokz->filestack, Tokenizer_FInfo, + tokz->filestack_n+1, tokz->filestack_n); + if(finfo==NULL) + warn_err(); + else + tokz->filestack=finfo; + } + + return TRUE; +} + + +bool tokz_popf(Tokenizer *tokz) +{ + return do_tokz_popf(tokz, TRUE); +} + + +void tokz_close(Tokenizer *tokz) +{ + while(tokz->filestack_n>0) + do_tokz_popf(tokz, FALSE); + + if(tokz->file!=NULL) + fclose(tokz->file); + if(tokz->name!=NULL) + free(tokz->name); + tok_free(&(tokz->ungettok)); + + free(tokz); +} + + + +/* */ + + +void tok_free(Token *tok) +{ + if(TOK_IS_STRING(tok) || TOK_IS_IDENT(tok) || TOK_IS_COMMENT(tok)){ + if(TOK_STRING_VAL(tok)!=NULL) + free(TOK_STRING_VAL(tok)); + } + + tok->type=TOK_INVALID; +} + + +void tok_init(Token *tok) +{ + static Token dummy=TOK_INIT; + + memcpy(tok, &dummy, sizeof(*tok)); +} + diff --git a/libtu/tokenizer.h b/libtu/tokenizer.h new file mode 100644 index 0000000..034a4a7 --- /dev/null +++ b/libtu/tokenizer.h @@ -0,0 +1,212 @@ +/* + * libtu/tokenizer.h + * + * Copyright (c) Tuomo Valkonen 1999-2002. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#ifndef LIBTU_TOKENIZER_H +#define LIBTU_TOKENIZER_H + +#include +#include "types.h" + + +#define TOK_SET_BOOL(TOK, VAL) {(TOK)->type=TOK_BOOL; (TOK)->u.bval=VAL;} +#define TOK_SET_LONG(TOK, VAL) {(TOK)->type=TOK_LONG; (TOK)->u.lval=VAL;} +#define TOK_SET_DOUBLE(TOK, VAL) {(TOK)->type=TOK_DOUBLE; (TOK)->u.dval=VAL;} +#define TOK_SET_CHAR(TOK, VAL) {(TOK)->type=TOK_CHAR; (TOK)->u.cval=VAL;} +#define TOK_SET_STRING(TOK, VAL) {(TOK)->type=TOK_STRING; (TOK)->u.sval=VAL;} +#define TOK_SET_IDENT(TOK, VAL) {(TOK)->type=TOK_IDENT; (TOK)->u.sval=VAL;} +#define TOK_SET_COMMENT(TOK, VAL) {(TOK)->type=TOK_COMMENT; (TOK)->u.sval=VAL;} +#define TOK_SET_OP(TOK, VAL) {(TOK)->type=TOK_OP; (TOK)->u.opval=VAL;} + +#define TOK_TYPE(TOK) ((TOK)->type) +#define TOK_BOOL_VAL(TOK) ((TOK)->u.bval) +#define TOK_LONG_VAL(TOK) ((TOK)->u.lval) +#define TOK_DOUBLE_VAL(TOK) ((TOK)->u.dval) +#define TOK_CHAR_VAL(TOK) ((TOK)->u.cval) +#define TOK_STRING_VAL(TOK) ((TOK)->u.sval) +#define TOK_IDENT_VAL(TOK) ((TOK)->u.sval) +#define TOK_COMMENT_VAL(TOK) ((TOK)->u.sval) +#define TOK_OP_VAL(TOK) ((TOK)->u.opval) + +#define TOK_IS_INVALID(TOK) ((TOK)->type==TOK_INVALID) +#define TOK_IS_BOOL(TOK) ((TOK)->type==TOK_BOOL) +#define TOK_IS_LONG(TOK) ((TOK)->type==TOK_LONG) +#define TOK_IS_DOUBLE(TOK) ((TOK)->type==TOK_DOUBLE) +#define TOK_IS_CHAR(TOK) ((TOK)->type==TOK_CHAR) +#define TOK_IS_STRING(TOK) ((TOK)->type==TOK_STRING) +#define TOK_IS_IDENT(TOK) ((TOK)->type==TOK_IDENT) +#define TOK_IS_COMMENT(TOK) ((TOK)->type==TOK_COMMENT) +#define TOK_IS_OP(TOK) ((TOK)->type==TOK_OP) + +#define TOK_OP_IS(TOK, OP) ((TOK)->type==TOK_OP && (TOK)->u.opval==(OP)) + +#define TOK_TAKE_STRING_VAL(TOK) ((TOK)->type=TOK_INVALID, (TOK)->u.sval) +#define TOK_TAKE_IDENT_VAL(TOK) ((TOK)->type=TOK_INVALID, (TOK)->u.sval) +#define TOK_TAKE_COMMENT_VAL(TOK) ((TOK)->type=TOK_INVALID, (TOK)->u.sval) + + +enum{ + TOK_INVALID=0, + TOK_LONG='l', + TOK_DOUBLE='d', + TOK_CHAR='c', + TOK_STRING='s', + TOK_IDENT='i', + TOK_BOOL='b', + TOK_COMMENT='#', + TOK_OP='+' +}; + + +enum{ +#define OP2(X,Y) ((X)|((Y)<<8)) +#define OP3(X,Y,Z) ((X)|((Y)<<8)|((Z)<<16)) + + OP_L_PAR= '(', OP_R_PAR= ')', OP_L_BRK= '[', OP_R_BRK= ']', + OP_L_BRC= '{', OP_R_BRC= '}', OP_COMMA= ',', OP_SCOLON= ';', + + OP_PLUS= '+', OP_MINUS= '-', OP_MUL= '*', OP_DIV= '/', + OP_MOD= '%', OP_POW= '^', OP_OR= '|', OP_AND= '&', + /*OP_NOT= '~',*/ OP_NOT= '!', OP_ASGN= '=', OP_LT= '<', + OP_GT= '>', OP_DOT= '.', OP_COLON= ':', OP_QMARK= '?', + OP_AT= '@', + OP_NEXTLINE='\n',OP_EOF= -1, + + OP_INC= OP2('+','+'), OP_DEC= OP2('-','-'), + OP_LSHIFT= OP2('<','<'), OP_RSHIFT= OP2('>','>'), + OP_AS_INC= OP2('+','='), OP_AS_DEC= OP2('-','='), + OP_AS_MUL= OP2('*','='), OP_AS_DIV= OP2('/','='), + OP_AS_MOD= OP2('%','='), OP_AS_POW= OP2('^','='), + +/* AS_OR= OP2('|','='), AS_AND= OP2('&','='), */ + OP_EQ= OP2('=','='), OP_NE= OP2('!','='), + OP_LE= OP2('<','='), OP_GE= OP2('>','=') + +/* L_AND= OP2('&','&'), L_OR= OP2('|','|'), + L_XOR= OP2('^','^'), */ + +/* AsLShift= OP3('<','<','='), + AsRShift= OP3('>','>','='), */ + +#undef OP2 +#undef OP3 +}; + + +typedef struct{ + int type; + int line; + union{ + bool bval; + long lval; + double dval; + char cval; + char *sval; + int opval; + } u; +} Token; + +#define TOK_INIT {0, 0, {0}} + + +extern void tok_free(Token*tok); +extern void tok_init(Token*tok); + + +/* */ + + +enum{ + TOKZ_IGNORE_NEXTLINE=0x1, + TOKZ_READ_COMMENTS=0x2, + TOKZ_PARSER_INDENT_MODE=0x04, + TOKZ_ERROR_TOLERANT=0x8, + TOKZ_READ_FROM_BUFFER=0x10, + TOKZ_DEFAULT_OPTION=0x20 +}; + + +enum{ + E_TOKZ_UNEXPECTED_EOF=1, + E_TOKZ_UNEXPECTED_EOL, + E_TOKZ_EOL_EXPECTED, + E_TOKZ_INVALID_CHAR, + E_TOKZ_TOOBIG, + E_TOKZ_NUMFMT, + E_TOKZ_NUM_JUNK, + E_TOKZ_NOTINT, + E_TOKZ_RANGE, + E_TOKZ_MULTICHAR, + + E_TOKZ_TOKEN_LIMIT, + E_TOKZ_UNKNOWN_OPTION, + E_TOKZ_SYNTAX, + E_TOKZ_INVALID_ARGUMENT, + E_TOKZ_EOS_EXPECTED, + E_TOKZ_TOO_FEW_ARGS, + E_TOKZ_TOO_MANY_ARGS, + E_TOKZ_MAX_NEST, + E_TOKZ_IDENTIFIER_EXPECTED, + + E_TOKZ_LBRACE_EXPECTED +}; + + +struct _ConfOpt; + +typedef struct _Tokenizer_FInfo{ + FILE *file; + char *name; + int line; + int ungetc; + Token ungettok; +} Tokenizer_FInfo; + +typedef struct _Tokenizer_Buffer{ + char *data; + int len; + int pos; +} Tokenizer_Buffer; + +typedef struct _Tokenizer{ + FILE *file; + char *name; + int line; + int ungetc; + Token ungettok; + + Tokenizer_Buffer buffer; + + int flags; + const struct _ConfOpt **optstack; + int nest_lvl; + void *user_data; + + int filestack_n; + Tokenizer_FInfo *filestack; + + char **includepaths; +} Tokenizer; + + +extern Tokenizer *tokz_open(const char *fname); +extern Tokenizer *tokz_open_file(FILE *file, const char *fname); +extern Tokenizer *tokz_prepare_buffer(char *buffer, int len); +extern void tokz_close(Tokenizer *tokz); +extern bool tokz_get_token(Tokenizer *tokz, Token *tok); +extern void tokz_unget_token(Tokenizer *tokz, Token *tok); +extern void tokz_warn_error(const Tokenizer *tokz, int line, int e); +extern void tokz_warn(const Tokenizer *tokz, int line, const char *fmt, ...); + +extern bool tokz_pushf(Tokenizer *tokz, const char *fname); +extern bool tokz_pushf_file(Tokenizer *tokz, FILE *file, const char *fname); +extern bool tokz_popf(Tokenizer *tokz); + +extern void tokz_set_includepaths(Tokenizer *tokz, char **paths); + +#endif /* LIBTU_TOKENIZER_H */ diff --git a/libtu/types.h b/libtu/types.h new file mode 100644 index 0000000..b2bd6a7 --- /dev/null +++ b/libtu/types.h @@ -0,0 +1,86 @@ +/* + * libtu/types.h + * + * Copyright (c) Tuomo Valkonen 1999-2002. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#ifndef LIBTU_TYPES_H +#define LIBTU_TYPES_H + +#include + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef NULL +#define NULL ((void*)0) +#endif + +#ifndef LIBTU_TYPEDEF_UXXX + + /* All systems seem to define these whichever way they want to + * despite -D_*_SOURCE etc. so there is no easy way to know whether + * they can be typedef'd or not. Unless you want to go through using + * autoconf or similar methods. ==> Just stick to #define. :-( + */ + +#ifndef uchar +#define uchar unsigned char +#endif + +#ifndef ushort +#define ushort unsigned short +#endif + +#ifndef uint +#define uint unsigned int +#endif + +#ifndef ulong +#define ulong unsigned long +#endif + +#else /* LIBTU_TYPEDEF_UXXX */ + +#ifndef uchar +typedef unsigned char uchar; +#endif + +#ifndef ushort +typedef unsigned short ushort; +#endif + +#ifndef uint +typedef unsigned int uint; +#endif + +#ifndef ulong +typedef unsigned long ulong; +#endif + +#endif /* LIBTU_TYPEDEF_UXXX */ + + +#ifndef LIBTU_TYPEDEF_BOOL + +#ifndef bool +#define bool int +#endif + +#else /* LIBTU_TYPEDEF_BOOL */ + +#ifndef bool +typedef int bool; +#endif + +#endif /* LIBTU_TYPEDEF_BOOL */ + +#endif /* LIBTU_TYPES_H */ diff --git a/libtu/util.c b/libtu/util.c new file mode 100644 index 0000000..aafdf13 --- /dev/null +++ b/libtu/util.c @@ -0,0 +1,37 @@ +/* + * libtu/util.c + * + * Copyright (c) Tuomo Valkonen 1999-2002. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#include +#include +#include +#include + +#include "locale.h" +#include "util.h" +#include "misc.h" + + +static const char *progname=NULL; + + +void libtu_init(const char *argv0) +{ + progname=argv0; + +#ifndef CF_NO_LOCALE + textdomain(simple_basename(argv0)); +#endif +} + + +const char *prog_execname() +{ + return progname; +} + diff --git a/libtu/util.h b/libtu/util.h new file mode 100644 index 0000000..c8bddcf --- /dev/null +++ b/libtu/util.h @@ -0,0 +1,23 @@ +/* + * libtu/util.h + * + * Copyright (c) Tuomo Valkonen 1999-2002. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#ifndef LIBTU_UTIL_H +#define LIBTU_UTIL_H + +#include +#include +#include + +#include "types.h" +#include "optparser.h" + +extern void libtu_init(const char *argv0); +extern const char *prog_execname(); + +#endif /* LIBTU_UTIL_H */ diff --git a/man/Makefile b/man/Makefile new file mode 100644 index 0000000..c35c5a9 --- /dev/null +++ b/man/Makefile @@ -0,0 +1,70 @@ +## +## Ion manual page Makefile +## + +# System-specific configuration is in system.mk +TOPDIR=.. +include $(TOPDIR)/build/system-inc.mk + +###################################### + +TRANSLATIONS=fi cs de + +WELCOME_TARGETS=\ + welcome.txt \ + $(foreach tr, $(TRANSLATIONS), welcome.$(tr).txt) + +TARGETS=ion3.1 $(foreach tr, $(TRANSLATIONS), ion3.$(tr).1) \ + pwm3.1 $(foreach tr, $(TRANSLATIONS), pwm3.$(tr).1) \ + $(WELCOME_TARGETS) + +MKMAN=$(LUA) ../build/mkman.lua +NROFF=nroff -man -Tlatin1 +#FILTERCRAP=perl -p -i -e 's/.\10//g' +FILTERCRAP=$(LUA) -e 'io.write((string.gsub(io.read("*a"), ".\8", "")))' + +CONFIGS=../etc/cfg_ioncore.lua \ + ../etc/cfg_tiling.lua \ + ../etc/cfg_query.lua \ + ../etc/cfg_menu.lua + +# TODO: PWM configuration file is undocumented +PWM_CONFIGS=\ + ../etc/cfg_ioncore.lua \ + ../etc/cfg_menu.lua + +###################################### + +include $(TOPDIR)/build/rules.mk + +###################################### + +ion3.1: ion3.in $(CONFIGS) + $(MKMAN) -i $< -o $@ -D ETCDIR $(ETCDIR) -D DOCDIR $(DOCDIR) $(CONFIGS) + +pwm3.1: pwm3.in $(PWM_CONFIGS) + $(MKMAN) -i $< -o $@ -D ETCDIR $(ETCDIR) -D DOCDIR $(DOCDIR) $(PWM_CONFIGS) + +ion3.%.1: ion3.%.in $(CONFIGS) ../po/%.po + $(MKMAN) -po ../po/$*.po -i $< -o $@ -D ETCDIR $(ETCDIR) -D DOCDIR $(DOCDIR) $(CONFIGS) + +pwm3.%.1: pwm3.%.in $(PWM_CONFIGS) ../po/%.po + $(MKMAN) -po ../po/$*.po -i $< -o $@ -D ETCDIR $(ETCDIR) -D DOCDIR $(DOCDIR) $(PWM_CONFIGS) + +welcome%txt: welcome%head ion3%1 + (cat welcome$*head; \ + $(NROFF) ion3$*1 | $(FILTERCRAP)) > $@ + +_install: + $(INSTALLDIR) $(MANDIR)/man1 + $(INSTALL) -m $(DATA_MODE) ion3.1 $(MANDIR)/man1 + $(INSTALL) -m $(DATA_MODE) pwm3.1 $(MANDIR)/man1 + for tr in $(TRANSLATIONS); do \ + $(INSTALLDIR) $(MANDIR)/$$tr/man1 ; \ + $(INSTALL) -m $(DATA_MODE) ion3.$$tr.1 $(MANDIR)/$$tr/man1/ion3.1 ; \ + $(INSTALL) -m $(DATA_MODE) pwm3.$$tr.1 $(MANDIR)/$$tr/man1/pwm3.1 ; \ + done + $(INSTALLDIR) $(SHAREDIR) + for i in $(WELCOME_TARGETS); do \ + $(INSTALL) -m $(DATA_MODE) $$i $(SHAREDIR) ; \ + done diff --git a/man/ion3.cs.in b/man/ion3.cs.in new file mode 100644 index 0000000..b3a8c91 --- /dev/null +++ b/man/ion3.cs.in @@ -0,0 +1,169 @@ +.TH ION 1 +.SH NÁZEV +Ion - správce oken systému X11 +.SH PØEHLED +.B ion3 +.I "[volby]" +.SH "POPIS" + +Ion je dla¾dicový správce oken se zálo¾kami navr¾ený pro klávesnicové u¾ivatele. + +.SH "VOLBY" +.TP +.B \-display poèítaè:displej.obrazovka +X displej, který se má spravovat +.TP +.B \-conffile soubor +Konfiguraèní soubor, který se má pou¾ít +.TP +.B \-searchdir adresáø +Adresáø, ve kterém se budou hledat konfiguraèní soubory a ostatní skripty +.TP +.B \-oneroot +Na X serverech s více obrazovkami (ne-Xinerama) bude spravovat pouze výchozí +obrazovku (koøenové okno). (Tedy ne v¹echny, které byly zadány v parametru +-display nebo v promìnné DISPLAY.) +.TP +.B \-session název_sezení +Nastaví název sezení. Tato volba ovlivní, kam se ulo¾í pracovní plochy +a ostatní soubory. Pokud není nastavená, pou¾ije se ~/.ion3/session_name. +.TP +.B \-xinerama 0|1 +Jestli¾e byl Ion sestaven s podporou Xineramy, mù¾ete touto volbou +povolit/zakázat (1/0) její pou¾ití. Implicitnì je Xinerama povolena. +.TP +.B \-help +Zobrazí nápovìdu k pøíkazovým parametrùm +.TP +.B \-version +Zobrazí verzi +.TP +.B \-about +Zobrazí informace o programu (verze, copyright) + +.SH Základní pojetí + +Tato sekce pøiná¹í pøehled typù objektù, které se objevují na X displeji +spravovaném Ionem. To je nezbytné pro pochopení operací s objekty a pro +pochopení, proè jsou rùzná klávesová pøiøazení dostupná jen u nìkterých +objektù. Podrobnìj¹í popis nutný pro psaní vlastních pøizpùsobení naleznete +na webových stránkách Ionu. + +Nejvy¹¹ími objekty jsou \fBobrazovky\fP, které odpovídají fyzickým +obrazovkám. Obrazovky obsahují \fBpracovní plochy\fP (co¾ je nìco jako +\fBskupiny\fP) a \fBklientská okna\fP v celoobrazovkovém re¾imu. V jeden +okam¾ik mù¾e být na obrazovce zobrazen právì jeden takový objekt. + +Pracovní plochy mohou obsahovat \fBdla¾dice\fP a odpojené/plovoucí +\fBrámy\fP. Dla¾dice obsahují \fBrámy\fP tak, aby bezezbytku vyplnily +(vydlá¾dily) celou obrazovku, a pøípadnì stavový øádek a dok. Podobnì +jako obrazovky obsahují i rámy dal¹í objekty, ale v tomto pøípadì ji¾ +jde vìt¹inou o \fBskupiny\fP klientských oken. Po vìt¹inu èasu bývají +rámy jedinou viditelnou èástí Ionu - volitelnì mohou mít kolem sebe +orámování a pro ka¾dého potomka zobrazují \fBzálo¾ku\fP. + +Dal¹í viditelnou èástí jsou \fBdotazy\fP. Nejde o nic jiného ne¾ obdélníky, +které se objeví ve spodní èásti rámù nebo obrazovek v¾dy, kdy¾ se Ion ptá +na nìjakou informaci (tøeba název okna pro pøipojení, nebo název programu, +který se má spustit). Vìt¹ina dotazù podporuje doplòování klávesou tab. + + +.SH PØIØAZENÍ + +Toto jsou výchozí pøiøazení klávesových zkratek. Pøiøazení modifikátoru +(\fBMod1\fP) závisí na systému. Na PCèkách s XFree86 bude nejspí¹ navázán +na levou klávesu Alt (\fBAlt_L\fP). Na Sunech je obvykle namapován na klávesy +s diamanty (\fBMeta_L\fP, \fBMeta_R\fP). Konkrétní pøiøazení zjistíte +programem \fIxmodmap(1)\fP. + +Øetìzec v hranatých závorkách urèuje modul, jeho¾ konfiguraèní soubor +definuje toto pøiøazení. + +.SS Globální pøiøazení + +BINDINGS:WScreen + +.SS Pøiøazení pro práci s nejvy¹¹ími rámy a obrazovkami a jejich potomky + +BINDINGS:WMPlex.toplevel + +.SS Pøiøazení pro práci s rámy a obrazovkami a jejich potomky + +BINDINGS:WMPlex + +.SS Pøiøazení pro práci se v¹emi rámy (vèetnì doèasných) a jejich potomky + +BINDINGS:WFrame + +.SS Pøiøazení pro práci s nejvy¹¹ími rámy (ne s doèasnými) a jejich potomky + +BINDINGS:WFrame.toplevel + +.SS Pøiøazení pro práci s plovoucími/odpojenými rámy + +BINDINGS:WFloatFrame + +.SS Pøiøazení pro dla¾dicové plochy a rámy [mod_tiling] + +BINDINGS:WTiling +BINDINGS:WFrame-on-WTiling + +.SS Pøiøazení pro kientská okna + +BINDINGS:WClientWin + +BINDINGS:WGroupWS + +.SS Pøiøazení pro pøesun/zmìnu velikosti + +BINDINGS:WMoveresMode + +.SS Pøiøazení pro informaèní zprávy a dotazy [mod_query] + +BINDINGS:WInput + +.SS Pøiøazení pro úpravu dotazù [mod_query] + +Tyto jsou podobné jako v textovém editoru \fIjoe(1)\fP. +Vyjmutí, kopírování a vlo¾ení sice pracují mírnì konvenènìji, ale +klávesy jsou shodné. + +BINDINGS:WEdln + +.SS Pøiøazení pro menu [mod_menu] + +BINDINGS:WMenu + + +.SH SOUBORY A ADRESÁØE +.TP +.B ETCDIR/cfg_ion.lua +Hlavní systémový konfiguraèní soubor +.TP +.B ETCDIR/cfg_*.lua +Ostatní konfiguraèní soubory +.TP +.B ETCDIR/look_*.lua +Soubory nastavující barevné schéma +.TP +.B ~/.ion3/ +U¾ivatelské konfiguraèní soubory +.TP +.B ~/.ion3/cfg_ion.lua +Hlavní u¾ivatelský konfiguraèní soubor (pøepisuje systémové nastavení) + +.SH VIZ TAKÉ +Domovská stránka Ionu \fIhttp://iki.fi/tuomov/ion/\fP +.PP +Dokument "Ion: Configuring and extending with Lua" k nalezení +tamté¾. +.PP +.I DOCDIR/ +.PP +\fIX(7x)\fP, \fIpwm3(1)\fP, \fIjoe(1)\fP + +.SH AUTOR +Ion napsal Tuomo Valkonen . + +.SH PØEKLAD +Do èe¹tiny pøelo¾il Miroslav Kuøe . diff --git a/man/ion3.de.in b/man/ion3.de.in new file mode 100644 index 0000000..3a2106f --- /dev/null +++ b/man/ion3.de.in @@ -0,0 +1,164 @@ +.TH ION 1 +.SH NAME +Ion - ein X11 Fenster Manager +.SH ÜBERSICHT +.B ion3 +.I "[optionen]" +.SH "BESCHREIBUNG" + +Ion ist ein tabellarisch aufgebauter Fenster Manager, entwickelt für +Benutzer welche auf die Tastatur spezialisiert sind. + +.SH "OPTIONEN" +.TP +.B \-display host:display.screen +X Anzeige zum darstellen +.TP +.B \-conffile Konfigurationsdatei +Konfigurationsdatei welche anstelle der Vorgegebenen benutzt werden soll +.TP +.B \-searchdir Verzeichnis +Verzeichnis worin die Konfigurationsdateien und andere Scripte gesucht werden +.TP +.B \-oneroot +Auf X Servern mit mehreren (traditionell non-Xinerama) Screens; handhabt nur den +voreingestellten Screen (Wurzelfenster); nicht alle, welche durch die '-display' +Option oder in der DISPLAY Umgebungsvariable bereitgestellt werden +.TP +.B \-session sitzungs_name +Setzt einen Sitzungsnamen. Diese Option beeinflusst wo der Arbeitsbereich und +andere Sicherungsdateien abgelegt werden (~/.ion3/sitzungs_name wenn die Option +gesetzt wurde). +.TP +.B \-xinerama 0|1 +Wenn Ion mit Xinerama Unterstützung kompiliert wurde, kann diese Option benutzt +werden um sie 'an = 1' oder 'aus = 0' zu stellen. Die Voreinstelllung ist +Xineramas Screeninformation zu benutzen. +.TP +.B \-help +Zeigt die Hilfe für die Kommandozeilenoptionen +.TP +.B \-version +Zeigt die Version +.TP +.B \-about +Zeigt Informationen über den Autor, die Version und das Copyright + +.SH BASIS KONZEPTE + +Diese Kapitel ist ein Überblick über die Objekttypen welche auf der X Anzeige +durch Ion behandelt werden. Die Beziehungen sind auf die Standardeinstellungen +ausgerichtet. Diese Informationen hier sind nötig um die Operationen und +Möglichkeiten der unterschiedlichen Tastenkombinationen zu verstehen. Siehe +dazu auch die weiteren Erklärungen unten. Für detailliertere Erklärungen, +welche zum schreiben eigener Konfigurationsdateien benötigt werden, siehe die +zusätzlichen Dokumentationen auf der Ion Webseite + +Die obersten Objekte welche benutzt werden können sind \fBScreens\fP. +Sie sind mit physikalischen Sceeens zu vergleichen. Screens enthalten +\fBArbeitsbereiche\fP und \fBClient Fenster\fP gepackt in einem Vollbildmodus. +Diese Objekte sind \fBgebündelt\fP, mit dem Hintergrund, dass nur einer auf +einmal angezeigt werden kann. + +Derzeit unterstützt Ion zwei Arten von Arbeitsbereichen; \fBgeteilt\fP in den +traditionellen Ion-Style und den herkömlichen \fBfliessenden\fP Arbeitsbereich +des PWM. Arbeitsbereiche sind keine sichtbaren Objekte sondern eher eine +Sammlung von gleichzeitig erkennbaren \fBFrames\fP. + +Ähnlich wie Screens, bündeln Frames andere Objekte, in diesem Fall sind dies +meist Client Fenster. Die meiste Zeit, sind Frames das einzige von Ion was auf +dem Screen zu sehen ist. Frames können Rahmen haben und sie haben einen +\fBReiter\fP für jedes in ihnen gebündelte Objekt. + +\fBAnfragen\fP sind Textboxen die am unteren Rand eines Frames oder Screens +erscheinen. Sie können nach Texteingaben zum ausführen einer Aktion verlangen. +Diese sind abhängig von den an sie gebundenen Aktionen. Die meisten Anfragen +unterstützen Autovervollständigung. + +.SH BINDUNGEN + +Hier sind die voreingestellten Tasten- und Mauszeigerbindungen beschrieben. +(\fBMod1\fP) hängt vom jeweiligen verwendeten System ab. Auf PC's mit XFree86 +ist es höchstwahrscheinlich an die linke 'ALT-Taste' gebungen (\fBALT_L\fP). +Auf SUN Systemen ist es sicherlich an die 'Diamant' Tasten gebunden +(\fBMETA_L\fP, \fBMETA_R\fP). Benutze \fIxmodmap(1x)\fP um die Belegung +herauszufinden. + +Die Zeichenkette in den eckigen Klammern nach einer Bindungsgruppe (wird unten +gezeigt) deutet auf die Konfigurationsdatei des Modules in welchem die Bindungen +definiert sind. + +.SS Global verfügbare Bindungen + +BINDINGS:WScreen + +.".SS Bindungsoperationen auf Frames und Screens und ihren Kindern + +BINDINGS:WMPlex + +.SS Bindungsoperationen auf Frames und ihren Kindern + +BINDINGS:WFrame + +.SS Bindungen zum Bewegungs-/Veränderungsmodus + +BINDINGS:WMoveresMode + +.SS Bindungen zum teilen der Arbeitsfläche [mod_ionws] + +BINDINGS:WTiling + +.SS Bindungen für den fliessenden Arbeitsbereich und Frames [mod_floatws] + +BINDINGS:WGroupWS + +BINDINGS:WFloatFrame + +.SS Bindungen für Message- und Anfrageboxen [mod_query] + +BINDINGS:WInput + +.SS Bindungen zum Editieren in Anfragen [mod_query] + +Diese Bindungen sind vergleichbar mit denen des \fIjoe(1)\fP Texteditors. +Auschneiden, Kopieren und Einfügen arbeiten nach konventionellen +Gesichtspunkten. Etwas überarbeitet aber die gleichen Tasten. + +BINDINGS:WEdln + +.SS Bindungen für die Menüs [mod_menu] + +BINDINGS:WMenu + + +.SH DATEIEN und VERZEICHNISSE +.TP +.B ETCDIR/cfg_ion.lua +Voreingestellte Hauptkonfigurationsdateien +.TP +.B ETCDIR/cfg_*.lua +Andere Konfigurationsdateien +.TP +.B ETCDIR/look_*.lua +Farbschemata Konfigurationsdateien +.TP +.B ~/.ion3/ +Benutzerkonfigurationsdateien +.TP +.B ~/.ion3/cfg_ion.lua +Voreingestellte Benutzer Hauptkonfigurationsdateien (überschreiben die +systemweiten Konfigurationen) + +.SH SIEHE AUCH +Die Ion Homepage, \fIhttp://iki.fi/tuomov/ion/\fP +.PP +Das Dokument "Ion: Configuring and extending with Lua" kann auf der Ion Webseite +gefunden werden. + +.PP +.I DOCDIR/ +.PP +\fIX(7x)\fP, \fIpwm3(1)\fP, \fIjoe(1)\fP + +.SH AUTOR +Ion wurde von Tuomo Valkonen geschrieben diff --git a/man/ion3.fi.in b/man/ion3.fi.in new file mode 100644 index 0000000..ae82a00 --- /dev/null +++ b/man/ion3.fi.in @@ -0,0 +1,169 @@ +.TH ION 1 +.SH NIMI +Ion - X11 ikkunanhallintaohjelma +.SH SELOSTE +.B ion3 +.I "[valinnat]" +.SH "KUVAUS" + +Ion on näppäimistökäyttäjiä silmälläpitäen suunniteltu laatoitusta ja +välilehtiä hyödyntävä ikkunanhallintaohjelma X11:lle. + +.SH "VALINNAT" +.TP +.B \-display kone:palvelin.näyttö +X palvelin, johon ottaa yhteys. +.TP +.B \-conffile asetustiedosto +Käytettävä asetustiedosto. +.TP +.B \-searchdir dir +Hakupolkuun lisättävä hakemisto, josta löytyy asetus- ja muita tiedostoja. +.TP +.B \-oneroot +Hallitse vain oletusnäyttöä X-palvelimilla, joissa on useampi perinteinen +(ei Xinerama) näyttö/juuri-ikkuna. Tämä näyttö voidaan valita -display +parametrillä tai DISPLAY ympäristömuuttujalla. +.TP +.B \-session session_name +Istunnon nimi. Tämä vaikuttaa talletustiedostojen sijaintiin. +.TP +.B \-xinerama 0|1 +Jos Ion käännettiin Xinerama-tuella, voidaan Xinerama tieto silti jättää +hyödyntämättä asettamalla tämä valinta arvoon 0. +.TP +.B \-help +Näytä komentorivin ohje. +.TP +.B \-version +Näytä Ionin versio. +.TP +.B \-about +Näytä tietoja Ionista (versio, tekijänoikeudet). + +.SH PERUSKÄSITTEET + +Tämä kappale on katsaus erilaisiin niin kutsuttuihin \fBolioihin\fP, +joita ilmenee Ionin hallitsemalla X-näytöllä, ja niiden suhteisiin +tavallisessa kokoonpanossa. Sinun tarvitse ymmärtää nämä suhteet +tietääksesi milloin mitkäkin alempana kuvattavat näppäinsidonnat +ovat käytettävissäsi. Jos kaipaat vielä tarkempaa kuvausta, esimerkiksi +tehdäksesi omat asetustiedostot, katso Ionin seittisivulta saatavaa +yksityiskohtaisempaa dokumentaatiota. + +Ylimmän tason olioita, joilla on merkitystä tässä yhteydessä, +kutsutaan \fBnäytöiksi\fP. Ne vastaavat fyysisiä näyttöjä. Näytöt +sisältävät \fBtyöpöytiä\fP (jotka ovat itse eräänlaisia \fBryhmiä\fP) +ja kokoruudun tilassa olevia \fBasiakasikkunoita\fP. Nämä oliot ovat +\fBlomitettu\fP siten, että vain yksi voi näkyä kerrallaan. + +Työpöydät voivat vuorostaan sisältää \fBlaatoituksia\fP sekä +kelluvia/irroitettuja \fBkehyksiä\fP. Laatoitukset taasen sisältävät +\fBkehyksiä\fP laatoitettuna täyttämään koko näytön, sekä mahdollisesti +tilapalkin tai telakan (dock). Näyttöjen tapaan kehykset lomittavat +muita olioita, mutta tässä tapauksessa lähinnä asiakasikkuna\fBryhmiä\fP. +Kehykset ovat suurimman osan ajasta ainut asia, minkä näet Ionista. Niillä +voi olla reunakoristukset, ja lisäksi kehyksissä on \fBvälilehti\fP +jokaiselle lomitetulle oliolle. + +\fBKyselyt\fP ovat näyttöjen tai kehysten alaosiin imestyviä laatikoita, +jotka odottavat sinulta tekstin syöttöä jonkin siitä riippuvan toiminnon +suorittamiseksi. Useimmat kyselyt tukevat tab-täydennystä. + + +.SH SIDONNAT + +Nämä ovat oletusarvoiset näppäin- ja osoitinsidonnat. Näppäinmuunninta +(\fBMod1\fP) vastaava näppäin riippuu järjestelmästäsi. PC-tietokoneilla, +joissa on XFree86 se on todennäköisesti vasen Alt-näppäin (\fBAlt_L\fP). +Suneilla se on luultavasti sidottu timanttinäppäimiin. Käytä komentoa +\fIxmodmap(1x)\fP selvittääksesi asian tarkemmin. + +Otsikkojen perässä hakasuluissa oleva merkkijono ilmoittaa moduulin, +jonka asetustiedosto määrittelee alla olevat sidonnat. + +.SS Yleisesti saatavilla olevat sidonnat + +BINDINGS:WScreen + +.SS Näytöillä tai ylimmän tason kehyksissä toimivat sidonnat + +BINDINGS:WMPlex.toplevel + +.SS Näytöillä tai (kaikissa) kehyksissä toimivat sidonnat + +BINDINGS:WMPlex + +.SS Kaikissa kehyksissä toimivat sidonnat + +BINDINGS:WFrame + +.SS Ylimmän tason kehyksissä (ei ns. transient-dialogi) toimivat sidonnat + +BINDINGS:WFrame.toplevel + +.SS Kelluvissa/irroitetuissa kehyksissä toimivat sidonnat + +BINDINGS:WFloatFrame + +.SS Laatoituksien ja laatoitettujen kehysten sidonnat [mod_tiling] + +BINDINGS:WTiling +BINDINGS:WFrame-on-WTiling + +.SS Asiakasikkunoiden sidonnat + +BINDINGS:WClientWin + +BINDINGS:WGroupCW + +.SS Siirto- ja koonmuutostilan sidonnat + +BINDINGS:WMoveresMode + +.SS Viestilaatikoiden ja kyselyiden sidonnat [mod_query] + +BINDINGS:WInput + +.SS Tekstinmuokkaussidonnat kyselyihin [mod_query] + +Nämä näppäinsidonnat vastaavat tekstieditorin \fIjoe(1)\fP sidontoja. +Leikkaus ja liimaus toimii kuitenkin tavanomaisemmin, joskin näppäimet +ovat vastaavat. + +BINDINGS:WEdln + +.SS Valikoiden sidonnat [mod_menu] + +BINDINGS:WMenu + + +.SH TIEDOSTOT JA HAKEMISTOT +.TP +.B ETCDIR/cfg_ion.lua +Järjestelmän oletusarvoinen pääasetustiedosto. +.TP +.B ETCDIR/cfg_*.lua +Muita asetustiedostoja. +.TP +.B ETCDIR/look_*.lua +Ulkonäön asetustiedostoja. +.TP +.B ~/.ion3/ +Käyttäjän asetustiedostot. +.TP +.B ~/.ion3/cfg_ion.lua +Käyttäjän oletusarvoinen pääasetustiedosto. + +.SH KATSO MYÖS +Ionin kotisivu, \fIhttp://iki.fi/tuomov/ion/\fP (englanniksi). +.PP +Dokumentti "Ion: Configuring and extending with Lua" (englanniksi) +nähtävissä Ionin kotisivulla. +.PP +.I DOCDIR/ +.PP +\fIX(7x)\fP, \fIpwm3(1)\fP, \fIjoe(1)\fP + +.SH TEKIJÄ +Ionin on kirjoittanut Tuomo Valkonen . diff --git a/man/ion3.in b/man/ion3.in new file mode 100644 index 0000000..5ddbb33 --- /dev/null +++ b/man/ion3.in @@ -0,0 +1,167 @@ +.TH ION 1 +.SH NAME +Ion - an X11 window manager +.SH SYNOPSIS +.B ion3 +.I "[options]" +.SH "DESCRIPTION" + +Ion is a tiling tabbed window manager designed with keyboard users in mind. + +.SH "OPTIONS" +.TP +.B \-display host:display.screen +X display to manage +.TP +.B \-conffile configfile +Configuration file to use instead of default +.TP +.B \-searchdir dir +Directory to search for configuration files and other scripts +.TP +.B \-oneroot +On X servers with multiple (traditional non-Xinerama) screens, manage only +default screen (root window), not all, as given by the -display option or +in the DISPLAY environment variable. +.TP +.B \-session session_name +Set session name. This option affects where workspace and other save files are +put (~/.ion3/session_name if option set). +.TP +.B \-xinerama 0|1 +If Ion was compiled with Xinerama support, this option can be used to +enable/disable (1/0) the use of it. The default is to use Xinerama +screen information. +.TP +.B \-help +Show help on command line options +.TP +.B \-version +Show version +.TP +.B \-about +Show about text (version, copyright) + +.SH BASIC CONCEPTS + +This section is an overview of the types objects that appear on an X +display managed by Ion and their relationships in a standard setup. This +information is necessary to understand the operations and availability +of the different key bindings explained below. For a more detailed +explanation, needed for writing custom bindings configuration files, +see the additional documentation available from the Ion Web page. + +The top-level objects that matter in the case at hand are \fBscreens\fP. +They correspond to physical screens. Screens contain \fBworkspaces\fP +(which are a kind of \fBgroup\fP), and \fBclient windows\fP put into full +screen mode. These objects are \fBmultiplexed\fP in the sense that only +one can be displayed at a time. + +Workspaces themselves may contain \fBtilings\fP and detached/floating +\fBframes\fP. Tilings themselves contain \fBframes\fP tiled to fill the +screen, and possibly a statusbar or dock. Akin to screens, frames multiplex +other objects, but in this case mostly client window \fBgroups\fP. Most of +the time, frames are the only trace of Ion you see on the screen. Frames +may have border decorations, and they have a \fBtab\fP for each multiplexed +object. + +\fBQueries\fP are boxes that appear at the bottoms of frames or screens +to ask you for textual input to execute an action that depends on it. +Most queries support tab-completion. + + +.SH BINDINGS + +These are the default key and pointer bindings. (\fBMod1\fP) depends on your +system. On PC:s with XFree86 it is probably bound to the left Alt key +(\fBAlt_L\fP). On Suns it is usually bound to the diamond keys +(\fBMeta_L\fP, \fBMeta_R\fP). Use \fIxmodmap(1x)\fP to find out. + +The string in square brackets after a binding group heading below indicates +the module that whose configuration file defines these bindings. + +.SS Globally available bindings + +BINDINGS:WScreen + +.SS Bindings operating on top-level frames and screens and their children + +BINDINGS:WMPlex.toplevel + +.SS Bindings operating on frames and screens and their children + +BINDINGS:WMPlex + +.SS Bindings operating on all frames (including transient) and their children + +BINDINGS:WFrame + +.SS Bindings operating on top-level (non-transient) frames and their children + +BINDINGS:WFrame.toplevel + +.SS Bindings for floating/detached frames + +BINDINGS:WFloatFrame + +.SS Bindings for tilings and tiled frames [mod_tiling] + +BINDINGS:WTiling +BINDINGS:WFrame-on-WTiling + +.SS Bindings for client windows + +BINDINGS:WClientWin + +BINDINGS:WGroupCW + +.SS Move/resize mode bindings + +BINDINGS:WMoveresMode + +.SS Bindings for message boxes and queries [mod_query] + +BINDINGS:WInput + +.SS Bindings for editing in queries [mod_query] + +These bindings are similar to those of the \fIjoe(1)\fP text editor. +Cut, copy and paste works in a more conventional manner, though, but +the keys are equivalent. + +BINDINGS:WEdln + +.SS Bindings for menus [mod_menu] + +BINDINGS:WMenu + + +.SH FILES AND DIRECTORIES +.TP +.B ETCDIR/cfg_ion.lua +System default main configuration files +.TP +.B ETCDIR/cfg_*.lua +Other configuration files. +.TP +.B ETCDIR/look_*.lua +Colour scheme configuration files +.TP +.B ~/.ion3/ +User configuration files +.TP +.B ~/.ion3/cfg_ion.lua +User default main configuration file (overrides system default) + +.SH SEE ALSO +The Ion home page, \fIhttp://iki.fi/tuomov/ion/\fP +.PP +The document "Ion: Configuring and extending with Lua" found on the +Ion home page. +.PP +.I DOCDIR/ +.PP +\fIX(7x)\fP, \fIpwm3(1)\fP, \fIjoe(1)\fP + +.SH AUTHOR +Ion was written by Tuomo Valkonen . diff --git a/man/pwm3.cs.in b/man/pwm3.cs.in new file mode 100644 index 0000000..a45f74c --- /dev/null +++ b/man/pwm3.cs.in @@ -0,0 +1,110 @@ +.TH PWM 1 +.SH NÁZEV +PWM - správce oken systému X11 +.SH PØEHLED +.B pwm3 +.I "[volby]" +.SH "POPIS" + +Pùvodní PWM byl první správce oken, který pøinesl zálo¾ky. Aktuální verze +PWM je zalo¾ena na zdrojových kódech Ionu a ve skuteènosti to je jeden a ten +samý program, pouze s jinými konfiguraèními soubory a nìkolika drobnostmi. + +.SH "VOLBY" +.TP +.B \-display poèítaè:displej.obrazovka +X displej, který se má spravovat +.TP +.B \-conffile soubor +Konfiguraèní soubor, který se má pou¾ít +.TP +.B \-searchdir adresáø +Adresáø, ve kterém se budou hledat konfiguraèní soubory a ostatní skripty +.TP +.B \-oneroot +Na X serverech s více obrazovkami (ne-Xinerama) bude spravovat pouze výchozí +obrazovku (koøenové okno). (Tedy ne v¹echny, které byly zadány v parametru +-display nebo v promìnné DISPLAY.) +.TP +.B \-sessionname session_name +Nastaví název sezení. Tato volba ovlivní, kam se ulo¾í pracovní plochy +a ostatní soubory. Pokud není nastavená, pou¾ije se ~/.pwm3/session_name. +.TP +.B \-xinerama 0|1 +Jestli¾e byl Ioncore sestaven s podporou Xineramy, mù¾ete touto volbou +povolit/zakázat (1/0) její pou¾ití. Implicitnì je Xinerama _zakázána_. +.TP +.B \-help +Zobrazí nápovìdu k pøíkazovým parametrùm +.TP +.B \-version +Zobrazí verzi +.TP +.B \-about +Zobrazí informace o programu (verze, copyright) + +.SH PØIØAZENÍ + +Toto jsou implicitní pøiøazení klávesových zkratek. Pøiøazení modifikátoru +(\fBMod1\fP) závisí na systému. Na PCèkách s XFree86 bude nejspí¹ navázán +na levou klávesu Alt (\fBAlt_L\fP). Na Sunech je obvykle namapován na klávesy +s diamanty (\fBMeta_L\fP, \fBMeta_R\fP). Konkrétní pøiøazení zjistíte +programem \fIxmodmap(1x)\fP. + +Øetìzec v hranatých závorkách urèuje modul, jeho¾ konfiguraèní soubor +definuje toto pøiøazení. + +.SS Globální pøiøazení + +BINDINGS:WScreen + +.".SS Pøiøazení pro práci s rámy, obrazovkami a jejich potomky + +BINDINGS:WMPlex + +.SS Pøiøazení pro práci s rámy a jejich potomky + +BINDINGS:WFrame + +.SS Pøiøazení pro pøesun/zmìnu velikosti + +BINDINGS:WMoveresMode + +.SS Pøiøazení pro plovoucí pracovní plochy [mod_floatws] + +BINDINGS:WGroupWS + +BINDINGS:WFloatFrame + +.SS Pøiøazení pro menu [mod_menu] + +BINDINGS:WMenu + + +.SH SOUBORY A ADRESÁØE +.TP +.B ETCDIR/cfg_pwm.lua +Hlavní systémový konfiguraèní soubor +.TP +.B ETCDIR/cfg_*.lua +Ostatní konfiguraèní soubory +.TP +.B ETCDIR/look_*.lua +Soubory nastavující barevné schéma +.TP +.B ~/.pwm3/ +U¾ivatelské konfiguraèní soubory +.TP +.B ~/.pwm3/cfg_pwm.lua +Hlavní u¾ivatelský konfiguraèní soubor (pøepisuje systémové nastavení) + +.SH VIZ TAKÉ + +Více informací naleznete v +\fIion3(1)\fP. + +.SH AUTOR +Programy PWM a Ion napsal Tuomo Valkonen . + +.SH PØEKLAD +Do èe¹tiny pøelo¾il Miroslav Kuøe . diff --git a/man/pwm3.de.in b/man/pwm3.de.in new file mode 100644 index 0000000..fe38739 --- /dev/null +++ b/man/pwm3.de.in @@ -0,0 +1,111 @@ +.TH PWM 1 +.SH NAME +PWM - ein X11 Fenster Manager +.SH ÜBERSICHT +.B pwm3 +.I "[optionen]" +.SH "BESCHREIBUNG" + +Der originale PWM war der erste Fenster Manager, welcher einen tabellenartigen +Aufbau seiner Objekte unterstützte. Diese Version des PWM basiert auf dem +Quellcode von Ion und unterscheidet sich zu diesem nur durch Unterschiede in den +Konfigurationsdateien, den Pfaden und einigen wenigen zusätzliche Optionen. + +.SH "OPTIONEN" +.TP +.B \-display host:display.screen +X Anzeige zum darstellen +.TP +.B \-conffile Konfigurationsdatei +Konfigurationsdatei welche anstelle der Vorgegebenen benutzt werden soll +.TP +.B \-searchdir dir +Verzeichnis worin die Konfigurationsdateien und andere Scripte gesucht werden +.TP +.B \-oneroot +Auf X Servern mit mehreren (traditionell non-Xinerama) Screens; handhabt nur den +voreingestellten Screen (Wurzelfenster); nicht alle, welche durch die '-display' +Option oder in der DISPLAY Umgebungsvariable bereitgestellt werden +.TP +.B \-sessionname sitzungs_name +Setzt einen Sitzungsnamen. Diese Option beeinflusst wo der Arbeitsbereich und +andere Sicherungsdateien abgelegt werden (~/.pwm3/sitzungs_name wenn die Option +gesetzt wurde). +.TP +.B \-xinerama 0|1 +Wenn Ion mit Xinerama Unterstützung kompiliert wurde, kann diese Option benutzt +werden um sie 'an = 1' oder 'aus = 0' zu stellen. Die Voreinstelllung ist +Xineramas Screeninformation 'NICHT' zu benutzen. +.TP +.B \-help +Zeigt die Hilfe für die Kommandozeilenoptionen +.TP +.B \-version +Zeigt die Version +.TP +.B \-about +Zeigt Informationen über den Autor, die Version und das Copyright + +.SH BINDUNGEN +Hier sind die voreingestellten Tasten- und Mauszeigerbindungen beschrieben. +(\fBMod1\fP) hängt vom jeweiligen verwendeten System ab. Auf PC's mit XFree86 +ist es höchstwahrscheinlich an die linke 'ALT-Taste' gebungen (\fBALT_L\fP). +Auf SUN Systemen ist es sicherlich an die 'Diamant' Tasten gebunden +(\fBMETA_L\fP, \fBMETA_R\fP). Benutze \fIxmodmap(1x)\fP um die Belegung +herauszufinden. + +Die Zeichenkette in den eckigen Klammern nach einer Bindungsgruppe (wird unten +gezeigt) deutet auf die Konfigurationsdatei des Modules in welchem die Bindungen +definiert sind. + +.SS Global verfügbare Bindungen + +BINDINGS:WScreen + +.".SS Bindungsoperationen auf Frames und Screens und ihren Kindern + +BINDINGS:WMPlex + +.SS Bindungsoperationen auf Frames und ihren Kindern + +BINDINGS:WFrame + +.SS Bindungen zum Bewegungs-/Veränderungsmodus + +BINDINGS:WMoveresMode + +.SS Bindungen für den fliessenden Arbeitsbereich und Frames [mod_floatws] + +BINDINGS:WGroupWS + +BINDINGS:WFloatFrame + +.SS Bindungen für die Menüs [mod_menu] + +BINDINGS:WMenu + + +.SH DATEIEN und VERZEICHNISSE +.TP +.B ETCDIR/cfg_pwm.lua +Voreingestellte Hauptkonfigurationsdateien +.TP +.B ETCDIR/cfg_*.lua +Andere Konfigurationsdateien +.TP +.B ETCDIR/look_*.lua +Farbschemata Konfigurationsdateien +.TP +.B ~/.pwm3/ +Benutzerkonfigurationsdateien +.TP +.B ~/.pwm3/cfg_pwm.lua +Voreingestellte Benutzer Hauptkonfigurationsdateien (überschreiben die +systemweiten Konfigurationen) + +.SH SIEHE AUCH + +Weitere Informationen siehe \fIion3(1)\fP. + +.SH AUTOR +PWM und Ion wurde von Tuomo Valkonen geschrieben diff --git a/man/pwm3.fi.in b/man/pwm3.fi.in new file mode 100644 index 0000000..6e634c9 --- /dev/null +++ b/man/pwm3.fi.in @@ -0,0 +1,107 @@ +.TH ION 1 +.SH NIMI +PWM - X11 ikkunanhallintaohjelma +.SH SELOSTE +.B pwm3 +.I "[valinnat]" +.SH "KUVAUS" + +Alkuperäinen PWM oli ensimmäinen ohjelma, joka toi välilehdet +ikkunanhallintaohjelman tasolle. Tämä versio PWM:stä taas pohjautuu +Ioniin ja sen mod_floatws moduuliin hieman eri oletusasetuksilla. + +.SH "VALINNAT" +.TP +.B \-display kone:palvelin.näyttö +X palvelin, johon ottaa yhteys. +.TP +.B \-conffile asetustiedosto +Käytettävä asetustiedosto. +.TP +.B \-searchdir dir +Hakupolkuun lisättävä hakemisto, josta löytyy asetus- ja muita tiedostoja. +.TP +.B \-oneroot +Hallitse vain oletusnäyttöä X-palvelimilla, joissa on useampi perinteinen +(ei Xinerama) näyttö/juuri-ikkuna. Tämä näyttö voidaan valita -display +parametrillä tai DISPLAY ympäristömuuttujalla. +.TP +.B \-sessionname session_name +Istunnon nimi. Tämä vaikuttaa talletustiedostojen sijaintiin. +.TP +.B \-xinerama 0|1 +PWM ei oletuksena käytä Xinerama-tietoa Ionista poiketen. Jos se kuitenkin +käännettiin Xinerama-tuella, voidaan tuo tieto ottaa käyttää asettamalla +tämän parametrin arvoksi 1. Tuolloin kaikilla Xinerama-näytöillä on +erilliset työpöydät. +.TP +.B \-help +Näytä komentorivin ohje. +.TP +.B \-version +Näytä Ionin versio. +.TP +.B \-about +Näytä tietoja PWM:sta (versio, tekijänoikeudet). + +.SH SIDONNAT + +Nämä ovat oletusarvoiset näppäin- ja osoitinsidonnat. Näppäinmuunninta +(\fBMod1\fP) vastaava näppäin riippuu järjestelmästäsi. PC-tietokoneilla, +joissa on XFree86 se on todennäköisesti vasen Alt-näppäin (\fBAlt_L\fP). +Suneilla se on luultavasti sidottu timanttinäppäimiin. Käytä komentoa +\fIxmodmap(1x)\fP selvittääksesi asian tarkemmin. + +Otsikkojen perässä hakasuluissa oleva merkkijono ilmoittaa moduulin, +jonka asetustiedosto määrittelee alla olevat sidonnat. + +.SS Yleisesti saatavilla olevat sidonnat + +BINDINGS:WScreen + +.\".SS Kehyksiä ja näyttöjä, sekä niiden lapsia käsittelevät sidonnat + +BINDINGS:WMPlex + +.SS Kehyksiä, sekä niiden lapsia käsittelevät sidonnat + +BINDINGS:WFrame + +.SS Siirto ja koonmuutostilan sidonnat + +BINDINGS:WMoveresMode + +.SS Kelluvien työpöytien ja kehyksien sidonnat [mod_floatws] + +BINDINGS:WGroupWS + +BINDINGS:WFloatFrame + +.SS Valikoiden sidonnat [mod_menu] + +BINDINGS:WMenu + + +.SH TIEDOSTOT JA HAKEMISTOT +.TP +.B ETCDIR/cfg_pwm.lua +Järjestelmän oletusarvoinen pääasetustiedosto. +.TP +.B ETCDIR/cfg_*.lua +Muita asetustiedostoja. +.TP +.B ETCDIR/look_*.lua +Ulkonäön asetustiedostoja. +.TP +.B ~/.ion3/ +Käyttäjän asetustiedostot. +.TP +.B ~/.ion3/cfg_pwm.lua +Käyttäjän oletusarvoinen pääasetustiedosto. + +.SH KATSO MYÖS + +Katso \fIion3(1)\fP. + +.SH TEKIJÄ +PWM:n sekä Ionin on kirjoittanut Tuomo Valkonen . diff --git a/man/pwm3.in b/man/pwm3.in new file mode 100644 index 0000000..50df639 --- /dev/null +++ b/man/pwm3.in @@ -0,0 +1,108 @@ +.TH PWM 1 +.SH NAME +PWM - An X11 window manager +.SH SYNOPSIS +.B pwm3 +.I "[options]" +.SH "DESCRIPTION" + +The original PWM was the first tabbing window manager. This version of +PWM is based on the code of Ion and is actually exactly the same window +manager with only differences in default configuration files, configuration +file lookup paths and some options. + +.SH "OPTIONS" +.TP +.B \-display host:display.screen +X display to manage +.TP +.B \-conffile configfile +Configuration file to use instead of default +.TP +.B \-searchdir dir +Directory to search for configuration files and other scripts +.TP +.B \-oneroot +On X servers with multiple (traditional non-Xinerama) screens, manage only +default screen (root window), not all, as given by the -display option or +in the DISPLAY environment variable. +.TP +.B \-sessionname session_name +Set session name. This option affects where workspace and other save files are +put (~/.pwm3/session_name if option set). +.TP +.B \-xinerama 0|1 +If Ioncore was compiled with Xinerama support, this option can be used to +enable/disable (1/0) the use of it. The default is _not_ to use Xinerama +screen information. +.TP +.B \-help +Show help on command line options +.TP +.B \-version +Show version +.TP +.B \-about +Show about text (version, copyright) + +.SH BINDINGS + +These are the default key and pointer bindings. (\fBMod1\fP) depends on your +system. On PC:s with XFree86 it is probably bound to the left Alt key +(\fBAlt_L\fP). On Suns it is usually bound to the diamond keys +(\fBMeta_L\fP, \fBMeta_R\fP). Use \fIxmodmap(1x)\fP to find out. + +The string in square brackets after a binding group heading below indicates +the module that whose configuration file defines these bindings. + +.SS Globally available bindings + +BINDINGS:WScreen + +.".SS Bindings operating on both frames and screens and their children + +BINDINGS:WMPlex + +.SS Bindings operating on frames and their children + +BINDINGS:WFrame + +.SS Move/resize mode bindings + +BINDINGS:WMoveresMode + +.SS Bindings for floating workspaces and frames [mod_floatws] + +BINDINGS:WGroupWS + +BINDINGS:WFloatFrame + +.SS Bindings for menus [mod_menu] + +BINDINGS:WMenu + + +.SH FILES AND DIRECTORIES +.TP +.B ETCDIR/cfg_pwm.lua +System default main configuration file +.TP +.B ETCDIR/cfg_*.lua +Other configuration files +.TP +.B ETCDIR/look_*.lua +Colour scheme configuration files +.TP +.B ~/.pwm3/ +User configuration files +.TP +.B ~/.pwm3/cfg_pwm.lua +User default main configuration file (overrides system default) + +.SH SEE ALSO + +For more information, see +\fIion3(1)\fP. + +.SH AUTHOR +Both PWM and Ion have been written by Tuomo Valkonen . diff --git a/man/welcome.cs.head b/man/welcome.cs.head new file mode 100644 index 0000000..3ad07d9 --- /dev/null +++ b/man/welcome.cs.head @@ -0,0 +1,26 @@ + +Vítejte v Ionu! + +Pokud jste je¹tì nikdy Ion nepou¾ívali, silnì vám doporuèujeme +prostudovat si pøed pokraèováním jeho manuálovou stránku. Kopii +manuálové stránky naleznete ní¾e, ale mù¾ete se k ní kdykoliv vrátit +napøíklad stiskem klávesy následované klávesou , nebo +v terminálu spu¹tìním pøíkazu 'man ion3'. (Pøedpokládáme, ¾e jste +Ion nainstalovali standardním zpùsobem a ¾e systémové pøíkazy ví, kde +naleznou manuálovou stránku.) + +Jestli¾e jste pøíli¹ nedoèkaví a pøeze v¹echna doporuèení si +manuálovou stránku nepøeètete, mìli byste alespoò vìdìt, ¾e klávesou + by se mìl spustit emulátor terminálu (xterm) a ¾e do hlavního +menu se dostanete klávesou . + +A¾ se budete cítit pøipraveni na psaní vlastních konfiguraèních +souborù (jednou k tomu stejnì dojde), prostudujte si konfiguraèní +manuál dostupný na webových stránkách Ionu. Pøedtím ale doporuèujeme +Ion nìjakou dobu pou¾ívat, abyste pochytili základní koncepty nutné +pro porozumìní konfiguraèních souborù. + + +---- Následuje manuálová stránka ---- + + diff --git a/man/welcome.de.head b/man/welcome.de.head new file mode 100644 index 0000000..519e364 --- /dev/null +++ b/man/welcome.de.head @@ -0,0 +1,22 @@ +Willkommen bei Ion! + +Wenn Sie nie zuvor Ion benutzt haben, empfehlen wir Ihnen unbedingt das +Ion Handbuch zu studieren, bevor Sie weiter fortfahren. Obwohl es +unten aufgeführt ist, können Sie später wieder darauf zurückgreifen, indem +Sie drücken und dann , oder, indem Sie 'man ion3' in einem +Terminal ausführen. All das setzt voraus, dass Sie Ion in einem +Standardspeicherplatz installiert haben, damit die System Kommandos die +Handbuchseite finden können. + +Wenn Sie zu wild darauf sind, Ion auszuprobieren und gegen alle +Empfehlungen das Lesen des Handbuches überspringen wollen, so sollten Sie +zumindestens wissen, dass mit drücken der - Taste ein Terminal +gestartet werden kann, und dass man auf das Hauptmenü mit zugreifen kann. + +Wenn Sie sich bereit fühlen, individuelle Konfigurationsdateien zu +schreiben (was jederzeit möglich ist), so schauen sich bitte in das +Konfigurationshandbuch, welches auf der Ion Webseite zu finden ist. +(Link siehe Ende des Benutzerhandbuches) Ausserdem kann es nuetzlich sein, +sich erst ein wenig mit Ion vertraut zu machen bevor man sich eingehend +damit befasst. Damit Sie die Grundkonzepte erfassen koennen, die notwendig +sind, um die nötigen Konfigurationsdateien zu verstehen. diff --git a/man/welcome.fi.head b/man/welcome.fi.head new file mode 100644 index 0000000..e9a3693 --- /dev/null +++ b/man/welcome.fi.head @@ -0,0 +1,25 @@ + +Tervetuloa käyttämään Ionia! + +Jos et ole käyttänyt Ionia aiemmin, on hyvin suotiteltavaa, että opiskelet +Ionin ohjesivun ennen kuin jatkat tätä pidemmälle. Se on toistettu alla, +mutta voit aukaista sen myös myöhemmin painamalla ja , tai +suorittamalla 'man ion3' komentoriviltä. (Tämä toimii sillä oletuksella, +että olet asentanut Ionin järjestelmässäsi sellaiseen vakiosijaintin, +mistä sen työkalut osaavat hakea ohjesivuja.) + +Jos olet innokas kokeilemaan Ionia nyt heti ja vastoin suosituksia haluat +jättää ohjesivun lukematta tällä istumalla, sinun tulisi kuitenkin tietää +ainakin seuraavat näppäimet: käynnistää pääte-emulaattorin (xterm) +ja näyttää päävalikon. + +Jossain vaiheessa, opeteltuasi Ionin peruskäsitteet ja toiminnot, haluat +varmaan muokata sen asetukset itsellesi sopivammiksi. Tähän löydät apua +(englanniksi) Ionin asetus- ja ohjelmointimanuaalista. Se löytyy ainakin +Ionin seittisivulta, johon on linkki alla olevan ohjesivun lopussa. +Huomautettakoon vielä, että Ionin käytön ja peruskäsitteiden opettelu +on hyvin suositeltvaa ennen asetustiedostojen pariin hyppäämistä. + +---- Ohjesivu seuraa ---- + + diff --git a/man/welcome.head b/man/welcome.head new file mode 100644 index 0000000..6a95c1f --- /dev/null +++ b/man/welcome.head @@ -0,0 +1,26 @@ + +Welcome to Ion! + +If you have never used Ion before, it is highly recommended that you study +the Ion manual page before proceeding further. It is reproduced below, but +you can also access it later on, for example, by pressing and then + or, by running 'man ion3' in a terminal. All this supposing that +you have installed Ion in a standard location so that the system commands +can find the manual page. + +If you are too eager to try out Ion and against all recommendation want to +skip reading the manual page, you should at least know that pressing +should start a terminal emulator (xterm) and that the main menu can be +accessed with . + +When you feel ready to write customized configuration files (you're going +to do that in any case), please see the configuration manual available from +the Ion webpage listed at the end of the user manual below. It may, however, +be beneficial to become well acquainted with Ion before delving into this +so that you have grasped the basic concepts necessary to understand the +binding configuration files. + + +---- Manual page follows ---- + + diff --git a/mod_dock/LICENSE b/mod_dock/LICENSE new file mode 100644 index 0000000..b1e3f5a --- /dev/null +++ b/mod_dock/LICENSE @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/mod_dock/Makefile b/mod_dock/Makefile new file mode 100644 index 0000000..2801471 --- /dev/null +++ b/mod_dock/Makefile @@ -0,0 +1,35 @@ +## +## Ion dock module Makefile +## +## $Header: /home/twp/cvsroot/twp/ion/ion-devel-dock/Makefile,v 1.15 2003/12/21 18:08:43 twp Exp $ +## + +# System specific configuration is in system.mk +TOPDIR=.. +include $(TOPDIR)/build/system-inc.mk + +###################################### + +INCLUDES += $(X11_INCLUDES) $(LIBTU_INCLUDES) $(LIBEXTL_INCLUDES) -I.. +CFLAGS += $(XOPEN_SOURCE) $(C99_SOURCE) + +SOURCES=dock.c +DOCS=LICENSE README.dock + +MAKE_EXPORTS=mod_dock + +MODULE=mod_dock + +###################################### + +include $(TOPDIR)/build/rules.mk + +###################################### + +_install: module_install + +###################################### + +.PHONY: tags +tags: + exuberant-ctags -R . $(TOPDIR) diff --git a/mod_dock/README.dock b/mod_dock/README.dock new file mode 100644 index 0000000..c2d81ba --- /dev/null +++ b/mod_dock/README.dock @@ -0,0 +1,98 @@ +Ion dock module +Copyright (c) Tom Payne 2003 +Copyright (c) Per Olofsson 2003 + +by Tom Payne +based on code by Per Olofsson + +INTRODUCTION + + This module provides a dock for the Ion window manager. + + Features: + - supports most dockapps + - configurable layout + - configurable dockapp order + - supports shaped dockapps + - uses ion's drawing engine + +DEPENDENCIES + + ion3 + +USAGE + + 1. Copy the configuration files cfg_dock.lua and dock-draw.lua to + your ~/.ion3 directory. Edit cfg_dock.lua and dock-draw.lua to + suit your preferences. + + 2. Add load_module("mod_dock") to ~/.ion3/cfg_ion.lua. + + 3. Add include("dock-draw.lua") near the end of ~/.ion3/draw.lua, + before gr_refresh(). + + 4. (Re)start ion3. + +CONFIGURATION + + See the comments in cfg_dock.lua and dock-draw.lua. + +KNOWN BUGS + + Not all dockapps dock. This is because dockapps use a variety of methods + to signal that they are dockapps and the dock doesn't yet + recognise all of them. If your favourite dockapp does not work then + please inform the author (email address at top of file). + + The following dockapps are known to have problems: + + - gkrellm2 can have the wrong shape if it is started after the dock + module is loaded. + + - wmxmms needs a winprop{...}. See the example cfg_dock.lua. Ion + complains: Client window "wmxmms" has broken transient_for hint. + ("Extended WM hints" multi-parent brain damage?). + +IMPLEMENTATION NOTES + + If you would like to help improve the dock then the following + notes might be useful. + + You should use folding in your editor when you edit/view the source + code. The code follows ion's coding standards, but there are many more + folds. VIM and Emacs both support folding. + + Dockapps use a variety of methods to signal to the window manager that + they are a dockapp. Ion automatically detects some (see + manage_clientwin() in ioncore/clientwin.c), and the dock detects + some others (see dock_clientwin_is_dockapp() in dock.c). Adding more + dockapp types will require editing these functions and possibly writing + type-specific management code. + + The dock should really implement region_save_to_file()/region_load() to + maintain state over a restart and session management. However, over + restart a new dock will also be created by cfg_dock.lua, meaning that there + will be multiple docks on the same screen. The solution is probably to + automatically create docks as they are required, with cfg_dock.lua + specifying the default configuration of new docks but not actually + creating any docks. + + As far as I can tell the gkrellm2 problem is a race condition and the + sequence of events is as follows: + 1. gkrellm2 starts and gets added to the dock with is initial geom. + 2. gkrellm2 grows as it loads plugins. + 3. The dock allocates more space to gkrellm2 and requests + gkrellm2's new shape, but gkrellm2 reports its old shape. + 4. Further requests to gkrellm2 return is new (actual) shape, but by + now it's too late... + Any assistance in fixing this bug would be much appreciated. + +AUTHORS + + Tom Payne + Per Olofsson + +LICENSE + + This program is licensed under the terms of the Lesser General Public + License (LGPL) version 2.1. See the file COPYING for details. diff --git a/mod_dock/dock.c b/mod_dock/dock.c new file mode 100644 index 0000000..3b18513 --- /dev/null +++ b/mod_dock/dock.c @@ -0,0 +1,1701 @@ +/* + * Ion dock module + * Copyright (C) 2003 Tom Payne + * Copyright (C) 2003 Per Olofsson + * Copyright (C) 2004-2006 Tuomo Valkonen + * + * by Tom Payne + * based on code by Per Olofsson + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Header: /home/twp/cvsroot/twp/ion/ion-devel-dock/dock.c,v 1.17 2003/12/21 11:59:48 twp Exp $ + * + */ + + +/*{{{ Includes */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "exports.h" + +/*}}}*/ + + +/*{{{ Macros */ + +#ifdef __GNUC__ +#define UNUSED __attribute__ ((unused)) +#else +#define UNUSED +#endif + +/*}}}*/ + + +/*{{{ Variables */ + +#include "../version.h" + +static const char *modname="dock"; +const char mod_dock_ion_api_version[]=ION_API_VERSION; + +static bool shape_extension=FALSE; +static int shape_event_basep=0; +static int shape_error_basep=0; + +static WBindmap *dock_bindmap=NULL; + +/*}}}*/ + + +/*{{{ Classes */ + +INTRSTRUCT(WDockParam); +INTRSTRUCT(WDockApp); +INTRCLASS(WDock); + +DECLSTRUCT(WDockParam){ + const char *key; + const char *desc; + const StringIntMap *map; + int dflt; +}; + +DECLSTRUCT(WDockApp){ + WDockApp *next, *prev; + WRegion *reg; + int pos; + bool draw_border; + bool tile; + WRectangle geom; + WRectangle tile_geom; + WRectangle border_geom; +}; + +DECLCLASS(WDock){ + WWindow win; + WDock *dock_next, *dock_prev; + int pos, grow; + bool is_auto; + GrBrush *brush; + WDockApp *dockapps; + + int min_w, min_h; + int max_w, max_h; + + bool arrange_called; + bool save; +}; + +static WDock *docks=NULL; + +/*}}}*/ + + +/*{{{ Parameter conversion */ + +static void dock_param_extl_table_get(const WDockParam *param, + ExtlTab conftab, int value) +{ + const char *s; + + s=stringintmap_key(param->map, value, NULL); + if(s){ + extl_table_sets_s(conftab, param->key, s); + } + +} + + +static bool dock_param_do_set(const WDockParam *param, char *s, + int *ret) +{ + bool changed=FALSE; + int i=stringintmap_value(param->map, s, -1); + if(i<0){ + warn_obj(modname, "Invalid %s \"%s\"", param->desc, s); + }else{ + if(*ret!=i){ + changed=TRUE; + } + *ret=i; + } + free(s); + + return changed; + +} + + +static bool dock_param_extl_table_set(const WDockParam *param, ExtlTab conftab, + int *ret) +{ + char *s; + + if(extl_table_gets_s(conftab, param->key, &s)) + return dock_param_do_set(param, s, ret); + + return FALSE; + +} + + +static bool dock_param_brush_set(const WDockParam *param, GrBrush *brush, + int *ret) +{ + char *s; + + if(grbrush_get_extra(brush, param->key, 's', &s)) + return dock_param_do_set(param, s, ret); + + return FALSE; + +} + +/*}}}*/ + + +/*{{{ Parameter descriptions */ + +static const WDockParam dock_param_name={ + "name", + "name", + NULL, + 0 +}; + + +#define DOCK_HPOS_MASK 0x000f +#define DOCK_HPOS_LEFT 0x0000 +#define DOCK_HPOS_CENTER 0x0001 +#define DOCK_HPOS_RIGHT 0x0002 +#define DOCK_VPOS_MASK 0x00f0 +#define DOCK_VPOS_TOP 0x0000 +#define DOCK_VPOS_MIDDLE 0x0010 +#define DOCK_VPOS_BOTTOM 0x0020 + + +static StringIntMap dock_pos_map[]={ + {"tl", DOCK_VPOS_TOP|DOCK_HPOS_LEFT}, + {"tc", DOCK_VPOS_TOP|DOCK_HPOS_CENTER}, + {"tr", DOCK_VPOS_TOP|DOCK_HPOS_RIGHT}, + {"ml", DOCK_VPOS_MIDDLE|DOCK_HPOS_LEFT}, + {"mc", DOCK_VPOS_MIDDLE|DOCK_HPOS_CENTER}, + {"mr", DOCK_VPOS_MIDDLE|DOCK_HPOS_RIGHT}, + {"bl", DOCK_VPOS_BOTTOM|DOCK_HPOS_LEFT}, + {"bc", DOCK_VPOS_BOTTOM|DOCK_HPOS_CENTER}, + {"br", DOCK_VPOS_BOTTOM|DOCK_HPOS_RIGHT}, + END_STRINGINTMAP +}; + +static WDockParam dock_param_pos={ + "pos", + "dock position", + dock_pos_map, + DOCK_HPOS_LEFT|DOCK_VPOS_BOTTOM +}; + + +enum WDockGrow{ + DOCK_GROW_UP, + DOCK_GROW_DOWN, + DOCK_GROW_LEFT, + DOCK_GROW_RIGHT +}; + +static StringIntMap dock_grow_map[]={ + {"up", DOCK_GROW_UP}, + {"down", DOCK_GROW_DOWN}, + {"left", DOCK_GROW_LEFT}, + {"right", DOCK_GROW_RIGHT}, + END_STRINGINTMAP +}; + +WDockParam dock_param_grow={ + "grow", + "growth direction", + dock_grow_map, + DOCK_GROW_RIGHT +}; + + +static const WDockParam dock_param_is_auto={ + "is_auto", + "is automatic", + NULL, + TRUE +}; + + +enum WDockOutlineStyle{ + DOCK_OUTLINE_STYLE_NONE, + DOCK_OUTLINE_STYLE_ALL, + DOCK_OUTLINE_STYLE_EACH +}; + +static StringIntMap dock_outline_style_map[]={ + {"none", DOCK_OUTLINE_STYLE_NONE}, + {"all", DOCK_OUTLINE_STYLE_ALL}, + {"each", DOCK_OUTLINE_STYLE_EACH}, + END_STRINGINTMAP +}; + +WDockParam dock_param_outline_style={ + "outline_style", + "outline style", + dock_outline_style_map, + DOCK_OUTLINE_STYLE_ALL +}; + + +static const WDockParam dock_param_tile_width={ + "width", + "tile width", + NULL, + 64 +}; + +static const WDockParam dock_param_tile_height={ + "height", + "tile height", + NULL, + 64 +}; + + +/*}}}*/ + + +/*{{{ Misc. */ + +#define CLIENTWIN_WINPROP_POSITION "dockposition" +#define CLIENTWIN_WINPROP_BORDER "dockborder" + +static WDockApp *dock_find_dockapp(WDock *dock, WRegion *reg) +{ + WDockApp *dockapp; + + for(dockapp=dock->dockapps; dockapp!=NULL; dockapp=dockapp->next){ + if(dockapp->reg==reg){ + return dockapp; + } + } + + return NULL; + +} + + +static void dock_get_outline_style(WDock *dock, int *ret) +{ + + *ret=dock_param_outline_style.dflt; + if(dock->brush!=NULL) + dock_param_brush_set(&dock_param_outline_style, dock->brush, ret); + +} + +/*}}}*/ + + +/*{{{ Size calculation */ + + +static void dock_get_tile_size(WDock *dock, WRectangle *ret) +{ + ExtlTab tile_size_table; + + ret->x=0; + ret->y=0; + ret->w=dock_param_tile_width.dflt; + ret->h=dock_param_tile_height.dflt; + if(dock->brush==NULL) + return; + if(grbrush_get_extra(dock->brush, "tile_size", 't', &tile_size_table)){ + extl_table_gets_i(tile_size_table, dock_param_tile_width.key, &ret->w); + extl_table_gets_i(tile_size_table, dock_param_tile_height.key, &ret->h); + extl_unref_table(tile_size_table); + } + +} + + +static void dock_get_pos_grow(WDock *dock, int *pos, int *grow) +{ + WMPlex *mplex=OBJ_CAST(REGION_PARENT(dock), WMPlex); + WRegion *mplex_stdisp; + WMPlexSTDispInfo din; + + if(mplex!=NULL){ + mplex_get_stdisp(mplex, &mplex_stdisp, &din); + if(mplex_stdisp==(WRegion*)dock){ + /* Ok, we're assigned as a status display for mplex, so + * get parameters from there. + */ + *pos=((din.pos==MPLEX_STDISP_TL || din.pos==MPLEX_STDISP_BL) + ? DOCK_HPOS_LEFT + : DOCK_HPOS_RIGHT) + | ((din.pos==MPLEX_STDISP_TL || din.pos==MPLEX_STDISP_TR) + ? DOCK_VPOS_TOP + : DOCK_VPOS_BOTTOM); + *grow=dock->grow; + return; + } + } + + *grow=dock->grow; + *pos=dock->pos; +} + + + +static void dock_reshape(WDock *dock) +{ + int outline_style; + + if(!shape_extension){ + return; + } + + dock_get_outline_style(dock, &outline_style); + + switch(outline_style){ + case DOCK_OUTLINE_STYLE_NONE: + case DOCK_OUTLINE_STYLE_EACH: + { + WDockApp *dockapp; + + /* Start with an empty set */ + XShapeCombineRectangles(ioncore_g.dpy, ((WWindow*)dock)->win, + ShapeBounding, 0, 0, NULL, 0, ShapeSet, 0); + + /* Union with dockapp shapes */ + for(dockapp=dock->dockapps; dockapp!=NULL; dockapp=dockapp->next){ + WClientWin *cwin=OBJ_CAST(dockapp->reg, WClientWin); + if(outline_style==DOCK_OUTLINE_STYLE_EACH + && dockapp->draw_border){ + /* Union with border shape */ + XRectangle tile_rect; + + tile_rect.x=dockapp->border_geom.x; + tile_rect.y=dockapp->border_geom.y; + tile_rect.width=dockapp->border_geom.w; + tile_rect.height=dockapp->border_geom.h; + XShapeCombineRectangles(ioncore_g.dpy, ((WWindow*)dock)->win, + ShapeBounding, 0, 0, &tile_rect, 1, + ShapeUnion, 0); + }else if(cwin!=NULL){ + /* Union with dockapp shape */ + int count; + int ordering; + + XRectangle *rects=XShapeGetRectangles(ioncore_g.dpy, cwin->win, + ShapeBounding, &count, + &ordering); + if(rects!=NULL){ + WRectangle dockapp_geom=REGION_GEOM(cwin); + XShapeCombineRectangles(ioncore_g.dpy, ((WWindow*)dock)->win, + ShapeBounding, + dockapp_geom.x, dockapp_geom.y, + rects, count, ShapeUnion, ordering); + XFree(rects); + } + } + } + } + break; + + case DOCK_OUTLINE_STYLE_ALL: + { + WRectangle geom; + XRectangle rect; + + geom=REGION_GEOM(dock); + rect.x=0; + rect.y=0; + rect.width=geom.w; + rect.height=geom.h; + XShapeCombineRectangles(ioncore_g.dpy, ((WWindow*)dock)->win, + ShapeBounding, 0, 0, &rect, 1, ShapeSet, 0); + } + break; + } + +} + + +static void dock_arrange_dockapps(WDock *dock, const WRectangle *bd_dockg, + const WDockApp *replace_this, + WDockApp *with_this) +{ + GrBorderWidths dock_bdw, dockapp_bdw; + WDockApp dummy_copy, *dockapp; + int pos, grow, cur_coord=0; + WRectangle dock_geom; + + dock->arrange_called=TRUE; + + dock_get_pos_grow(dock, &pos, &grow); + + /* Determine dock and dockapp border widths */ + memset(&dock_bdw, 0, sizeof(GrBorderWidths)); + memset(&dockapp_bdw, 0, sizeof(GrBorderWidths)); + + if(dock->brush){ + int outline_style; + + dock_get_outline_style(dock, &outline_style); + switch(outline_style){ + case DOCK_OUTLINE_STYLE_NONE: + break; + case DOCK_OUTLINE_STYLE_ALL: + grbrush_get_border_widths(dock->brush, &dock_bdw); + dockapp_bdw.spacing=dock_bdw.spacing; + break; + case DOCK_OUTLINE_STYLE_EACH: + grbrush_get_border_widths(dock->brush, &dockapp_bdw); + break; + } + } + + dock_geom.w=bd_dockg->w-dock_bdw.left-dock_bdw.right; + dock_geom.h=bd_dockg->h-dock_bdw.top-dock_bdw.bottom; + + /* Calculate initial co-ordinate for layout algorithm */ + switch(grow){ + case DOCK_GROW_UP: + cur_coord=dock_bdw.top+dock_geom.h; + break; + case DOCK_GROW_DOWN: + cur_coord=dock_bdw.top; + break; + case DOCK_GROW_LEFT: + cur_coord=dock_bdw.left+dock_geom.w; + break; + case DOCK_GROW_RIGHT: + cur_coord=dock_bdw.left; + break; + } + + /* Arrange dockapps */ + for(dockapp=dock->dockapps; dockapp!=NULL; dockapp=dockapp->next){ + WDockApp *da=dockapp; + + if(replace_this!=NULL){ + if(replace_this==dockapp){ + da=with_this; + }else{ + dummy_copy=*dockapp; + da=&dummy_copy; + } + } + + /* Calculate first co-ordinate */ + switch(grow){ + case DOCK_GROW_UP: + case DOCK_GROW_DOWN: + switch(pos&DOCK_HPOS_MASK){ + case DOCK_HPOS_LEFT: + da->border_geom.x=0; + break; + case DOCK_HPOS_CENTER: + da->border_geom.x=(dock_geom.w-da->border_geom.w)/2; + break; + case DOCK_HPOS_RIGHT: + da->border_geom.x=dock_geom.w-da->border_geom.w; + break; + } + da->border_geom.x+=dock_bdw.left; + break; + case DOCK_GROW_LEFT: + case DOCK_GROW_RIGHT: + switch(pos&DOCK_VPOS_MASK){ + case DOCK_VPOS_TOP: + da->border_geom.y=0; + break; + case DOCK_VPOS_MIDDLE: + da->border_geom.y=(dock_geom.h-da->border_geom.h)/2; + break; + case DOCK_VPOS_BOTTOM: + da->border_geom.y=dock_geom.h-da->border_geom.h; + break; + } + da->border_geom.y+=dock_bdw.top; + break; + } + + /* Calculate second co-ordinate */ + switch(grow){ + case DOCK_GROW_UP: + cur_coord-=da->border_geom.h; + da->border_geom.y=cur_coord; + cur_coord-=dockapp_bdw.spacing; + break; + case DOCK_GROW_DOWN: + da->border_geom.y=cur_coord; + cur_coord+=da->border_geom.h+dockapp_bdw.spacing; + break; + case DOCK_GROW_LEFT: + cur_coord-=da->border_geom.w; + da->border_geom.x=cur_coord; + cur_coord-=dockapp_bdw.spacing; + break; + case DOCK_GROW_RIGHT: + da->border_geom.x=cur_coord; + cur_coord+=da->border_geom.w+dockapp_bdw.spacing; + break; + } + + /* Calculate tile geom */ + da->tile_geom.x=da->border_geom.x+dockapp_bdw.left; + da->tile_geom.y=da->border_geom.y+dockapp_bdw.top; + + /* Calculate dockapp geom */ + if(da->tile){ + da->geom.x=da->tile_geom.x+(da->tile_geom.w-da->geom.w)/2; + da->geom.y=da->tile_geom.y+(da->tile_geom.h-da->geom.h)/2; + }else{ + da->geom.x=da->tile_geom.x; + da->geom.y=da->tile_geom.y; + } + + if(replace_this==NULL) + region_fit(da->reg, &(da->geom), REGION_FIT_BOUNDS); + } +} + + +static void calc_dock_pos(WRectangle *dg, const WRectangle *pg, int pos) +{ + switch(pos&DOCK_HPOS_MASK){ + case DOCK_HPOS_LEFT: + dg->x=pg->x; + break; + case DOCK_HPOS_CENTER: + dg->x=pg->x+(pg->w-dg->w)/2; + break; + case DOCK_HPOS_RIGHT: + dg->x=pg->x+(pg->w-dg->w); + break; + } + + switch(pos&DOCK_VPOS_MASK){ + case DOCK_VPOS_TOP: + dg->y=pg->y; + break; + case DOCK_VPOS_MIDDLE: + dg->y=pg->y+(pg->h-dg->h)/2; + break; + case DOCK_VPOS_BOTTOM: + dg->y=pg->y+(pg->h-dg->h); + break; + } +} + + +static void dock_set_minmax(WDock *dock, int grow, const WRectangle *g) +{ + dock->min_w=g->w; + dock->min_h=g->h; + if(grow==DOCK_GROW_UP || grow==DOCK_GROW_DOWN){ + dock->max_w=g->w; + dock->max_h=INT_MAX; + }else{ + dock->max_w=INT_MAX; + dock->max_h=g->h; + } +} + + +static void dockapp_calc_preferred_size(WDock *dock, int grow, + const WRectangle *tile_size, + WDockApp *da) +{ + int w=da->geom.w, h=da->geom.h; + + if(grow==DOCK_GROW_UP || grow==DOCK_GROW_DOWN){ + da->geom.w=minof(w, tile_size->w); + da->geom.h=h; + }else{ + da->geom.w=w; + da->geom.h=minof(h, tile_size->h); + } + + region_size_hints_correct(da->reg, &(da->geom.w), &(da->geom.h), TRUE); +} + + + +static void dock_managed_rqgeom_(WDock *dock, WRegion *reg, int flags, + const WRectangle *geom, WRectangle *geomret, + bool just_update_minmax) +{ + WDockApp *dockapp=NULL, *thisdockapp=NULL, thisdockapp_copy; + WRectangle parent_geom, dock_geom, border_dock_geom; + GrBorderWidths dock_bdw, dockapp_bdw; + int n_dockapps=0, max_w=1, max_h=1, total_w=0, total_h=0; + int pos, grow; + WRectangle tile_size; + WWindow *par=REGION_PARENT(dock); + + /* dock_resize calls with NULL parameters. */ + assert(reg!=NULL || (geomret==NULL && !(flags®ION_RQGEOM_TRYONLY))); + + dock_get_pos_grow(dock, &pos, &grow); + + /* Determine parent and tile geoms */ + parent_geom.x=0; + parent_geom.y=0; + if(par!=NULL){ + parent_geom.w=REGION_GEOM(par).w; + parent_geom.h=REGION_GEOM(par).h; + }else{ + /* Should not happen in normal operation. */ + parent_geom.w=1; + parent_geom.h=1; + } + + dock_get_tile_size(dock, &tile_size); + + /* Determine dock and dockapp border widths */ + memset(&dock_bdw, 0, sizeof(GrBorderWidths)); + memset(&dockapp_bdw, 0, sizeof(GrBorderWidths)); + + if(dock->brush){ + int outline_style; + + dock_get_outline_style(dock, &outline_style); + switch(outline_style){ + case DOCK_OUTLINE_STYLE_NONE: + break; + case DOCK_OUTLINE_STYLE_ALL: + grbrush_get_border_widths(dock->brush, &dock_bdw); + dockapp_bdw.spacing=dock_bdw.spacing; + break; + case DOCK_OUTLINE_STYLE_EACH: + grbrush_get_border_widths(dock->brush, &dockapp_bdw); + break; + } + } + + /* Calculate widths and heights */ + for(dockapp=dock->dockapps; dockapp!=NULL; dockapp=dockapp->next){ + WDockApp *da=dockapp; + bool update=!(flags®ION_RQGEOM_TRYONLY); + if(dockapp->reg==reg){ + thisdockapp=dockapp; + if(flags®ION_RQGEOM_TRYONLY){ + thisdockapp_copy=*dockapp; + thisdockapp_copy.geom=*geom; + da=&thisdockapp_copy; + update=TRUE; + } + da->geom=*geom; + } + + if(update){ + /* Calculcate preferred size */ + dockapp_calc_preferred_size(dock, grow, &tile_size, da); + + /* Determine whether dockapp should be placed on a tile */ + da->tile=da->geom.w<=tile_size.w && da->geom.h<=tile_size.h; + + /* Calculate width and height */ + if(da->tile){ + da->tile_geom.w=tile_size.w; + da->tile_geom.h=tile_size.h; + }else{ + da->tile_geom.w=da->geom.w; + da->tile_geom.h=da->geom.h; + } + + /* Calculate border width and height */ + da->border_geom.w=dockapp_bdw.left+da->tile_geom.w+dockapp_bdw.right; + da->border_geom.h=dockapp_bdw.top+da->tile_geom.h+dockapp_bdw.right; + } + + /* Calculate maximum and accumulated widths and heights */ + if(da->border_geom.w>max_w) + max_w=da->border_geom.w; + total_w+=da->border_geom.w+(n_dockapps ? dockapp_bdw.spacing : 0); + + if(da->border_geom.h>max_h) + max_h=da->border_geom.h; + total_h+=da->border_geom.h+(n_dockapps ? dockapp_bdw.spacing : 0); + + /* Count dockapps */ + ++n_dockapps; + } + + if(thisdockapp==NULL && reg!=NULL){ + warn("Requesting dockapp not found."); + if(geomret) + *geomret=REGION_GEOM(reg); + return; + } + + /* Calculate width and height of dock */ + if(n_dockapps){ + switch(grow){ + case DOCK_GROW_LEFT: + case DOCK_GROW_RIGHT: + dock_geom.w=total_w; + dock_geom.h=max_h; + break; + case DOCK_GROW_UP: + case DOCK_GROW_DOWN: + default: + dock_geom.w=max_w; + dock_geom.h=total_h; + break; + } + }else{ + dock_geom.w=tile_size.w; + dock_geom.h=tile_size.h; + } + border_dock_geom.w=dock_bdw.left+dock_geom.w+dock_bdw.right; + border_dock_geom.h=dock_bdw.top+dock_geom.h+dock_bdw.bottom; + + calc_dock_pos(&border_dock_geom, &parent_geom, pos); + + /* Fit dock to new geom if required */ + if(!(flags®ION_RQGEOM_TRYONLY)){ + WRQGeomParams rq=RQGEOMPARAMS_INIT; + + dock_set_minmax(dock, grow, &border_dock_geom); + + if(just_update_minmax) + return; + + rq.flags=REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_Y; + rq.geom=border_dock_geom; + + dock->arrange_called=FALSE; + + region_rqgeom((WRegion*)dock, &rq, NULL); + + if(!dock->arrange_called) + dock_arrange_dockapps(dock, ®ION_GEOM(dock), NULL, NULL); + + if(thisdockapp!=NULL && geomret!=NULL) + *geomret=thisdockapp->geom; + }else{ + if(thisdockapp!=NULL && geomret!=NULL){ + dock_arrange_dockapps(dock, ®ION_GEOM(dock), + thisdockapp, &thisdockapp_copy); + *geomret=thisdockapp_copy.geom; + } + } +} + +static void dock_managed_rqgeom(WDock *dock, WRegion *reg, + const WRQGeomParams *rq, + WRectangle *geomret) +{ + dock_managed_rqgeom_(dock, reg, rq->flags, &rq->geom, geomret, FALSE); +} + + +void dock_size_hints(WDock *dock, WSizeHints *hints) +{ + hints->min_set=TRUE; + hints->min_width=dock->min_w; + hints->min_height=dock->min_h; + + hints->max_set=TRUE; + hints->max_width=dock->max_w; + hints->max_height=dock->max_h; +} + + +static bool dock_fitrep(WDock *dock, WWindow *parent, const WFitParams *fp) +{ + if(!window_fitrep(&(dock->win), parent, fp)) + return FALSE; + + dock_arrange_dockapps(dock, &(fp->g), NULL, NULL); + + if(shape_extension) + dock_reshape(dock); + + return TRUE; +} + + +static int dock_orientation(WDock *dock) +{ + return ((dock->grow==DOCK_GROW_LEFT || dock->grow==DOCK_GROW_RIGHT) + ? REGION_ORIENTATION_HORIZONTAL + : REGION_ORIENTATION_VERTICAL); +} + + +/*}}}*/ + + +/*{{{ Drawing */ + + +static void dock_draw(WDock *dock, bool complete) +{ + int outline_style; + WRectangle g; + + if(dock->brush==NULL) + return; + + g.x=0; + g.y=0; + g.w=REGION_GEOM(dock).w; + g.h=REGION_GEOM(dock).h; + + grbrush_begin(dock->brush, &g, (complete ? 0 : GRBRUSH_NO_CLEAR_OK)); + + dock_get_outline_style(dock, &outline_style); + switch(outline_style){ + case DOCK_OUTLINE_STYLE_NONE: + break; + case DOCK_OUTLINE_STYLE_ALL: + { + WRectangle geom=REGION_GEOM(dock); + geom.x=geom.y=0; + grbrush_draw_border(dock->brush, &geom, "dock"); + } + break; + case DOCK_OUTLINE_STYLE_EACH: + { + WDockApp *dockapp; + for(dockapp=dock->dockapps; dockapp!=NULL; + dockapp=dockapp->next){ + grbrush_draw_border(dock->brush, &dockapp->tile_geom, + "dock"); + } + } + break; + } + + grbrush_end(dock->brush); +} + + +/*EXTL_DOC + * Resizes and refreshes \var{dock}. + */ +EXTL_EXPORT_MEMBER +void dock_resize(WDock *dock) +{ + dock_managed_rqgeom_(dock, NULL, 0, NULL, NULL, FALSE); + dock_draw(dock, TRUE); + +} + +static void dock_brush_release(WDock *dock) +{ + + if(dock->brush){ + grbrush_release(dock->brush); + dock->brush=NULL; + } + +} + + +static void dock_brush_get(WDock *dock) +{ + + dock_brush_release(dock); + dock->brush=gr_get_brush(((WWindow*)dock)->win, + region_rootwin_of((WRegion*)dock), + "stdisp-dock"); +} + + +static void dock_updategr(WDock *dock) +{ + dock_brush_get(dock); + dock_resize(dock); +} + +/*}}}*/ + + +/*{{{ Set/get */ + + +static void mplexpos(int pos, int *mpos) +{ + int hp=pos&DOCK_HPOS_MASK, vp=pos&DOCK_VPOS_MASK; + int p; + + p=(vp!=DOCK_VPOS_MIDDLE + ? (vp==DOCK_VPOS_TOP + ? (hp!=DOCK_HPOS_CENTER + ? (hp==DOCK_HPOS_RIGHT + ? MPLEX_STDISP_TR + : MPLEX_STDISP_TL) + : -1) + : (hp!=DOCK_HPOS_CENTER + ? (hp==DOCK_HPOS_RIGHT + ? MPLEX_STDISP_BR + : MPLEX_STDISP_BL) + : -1)) + : -1); + + if(p==-1) + warn("Invalid dock position while as stdisp."); + else + *mpos=p; +} + + +static void dock_do_set(WDock *dock, ExtlTab conftab, bool resize) +{ + char *s; + bool b; + bool growset=FALSE; + bool posset=FALSE; + bool save=FALSE; + + if(extl_table_gets_s(conftab, dock_param_name.key, &s)){ + if(!region_set_name((WRegion*)dock, s)){ + warn_obj(modname, "Can't set name to \"%s\"", s); + } + free(s); + } + + if(extl_table_gets_b(conftab, "save", &save)) + dock->save=save; + + if(dock_param_extl_table_set(&dock_param_pos, conftab, &dock->pos)) + posset=TRUE; + + if(dock_param_extl_table_set(&dock_param_grow, conftab, &dock->grow)) + growset=TRUE; + + if(extl_table_gets_b(conftab, dock_param_is_auto.key, &b)) + dock->is_auto=b; + + if(resize && (growset || posset)){ + WMPlex *par=OBJ_CAST(REGION_PARENT(dock), WMPlex); + WRegion *stdisp=NULL; + WMPlexSTDispInfo din; + + if(par!=NULL){ + mplex_get_stdisp(par, &stdisp, &din); + din.fullsize=FALSE; /* not supported. */ + if(stdisp==(WRegion*)dock){ + if(posset) + mplexpos(dock->pos, &din.pos); + if(growset){ + /* Update min/max first */ + dock_managed_rqgeom_(dock, NULL, 0, NULL, NULL, TRUE); + } + mplex_set_stdisp(par, (WRegion*)dock, &din); + } + } + + dock_resize(dock); + } +} + + +/*EXTL_DOC + * Configure \var{dock}. \var{conftab} is a table of key/value pairs: + * + * \begin{tabularx}{\linewidth}{llX} + * \tabhead{Key & Values & Description} + * \var{name} & string & Name of dock \\ + * \var{pos} & string in $\{t,m,b\}\times\{t,c,b\}$ & Dock position. + * Can only be used in floating mode. \\ + * \var{grow} & up/down/left/right & + * Growth direction where new dockapps are added. Also + * sets orientation for dock when working as WMPlex status + * display (see \fnref{WMPlex.set_stdisp}). \\ + * \var{is_auto} & bool & + * Should \var{dock} automatically manage new dockapps? \\ + * \end{tabularx} + * + * Any parameters not explicitly set in \var{conftab} will be left unchanged. + */ +EXTL_EXPORT_MEMBER +void dock_set(WDock *dock, ExtlTab conftab) +{ + dock_do_set(dock, conftab, TRUE); +} + + +static void dock_do_get(WDock *dock, ExtlTab conftab) +{ + extl_table_sets_s(conftab, dock_param_name.key, + region_name((WRegion*)dock)); + dock_param_extl_table_get(&dock_param_pos, conftab, dock->pos); + dock_param_extl_table_get(&dock_param_grow, conftab, dock->grow); + extl_table_sets_b(conftab, dock_param_is_auto.key, dock->is_auto); + extl_table_sets_b(conftab, "save", dock->save); +} + + +/*EXTL_DOC + * Get \var{dock}'s configuration table. See \fnref{WDock.set} for a + * description of the table. + */ +EXTL_SAFE +EXTL_EXPORT_MEMBER +ExtlTab dock_get(WDock *dock) +{ + ExtlTab conftab; + + conftab=extl_create_table(); + dock_do_get(dock, conftab); + return conftab; +} + + +/*}}}*/ + + +/*{{{ Init/deinit */ + + +static bool dock_init(WDock *dock, WWindow *parent, const WFitParams *fp) +{ + WFitParams fp2=*fp; + + dock->pos=dock_param_pos.dflt; + dock->grow=dock_param_grow.dflt; + dock->is_auto=dock_param_is_auto.dflt; + dock->brush=NULL; + dock->dockapps=NULL; + dock->min_w=1; + dock->min_h=1; + dock->max_w=1; + dock->max_h=1; + dock->arrange_called=FALSE; + dock->save=TRUE; + + + if(!window_init((WWindow*)dock, parent, &fp2)) + return FALSE; + + region_add_bindmap((WRegion*)dock, dock_bindmap); + + ((WRegion*)dock)->flags|=REGION_SKIP_FOCUS; + + window_select_input(&(dock->win), IONCORE_EVENTMASK_CWINMGR); + + dock_brush_get(dock); + + LINK_ITEM(docks, dock, dock_next, dock_prev); + + return TRUE; +} + + +static WDock *create_dock(WWindow *parent, const WFitParams *fp) +{ + CREATEOBJ_IMPL(WDock, dock, (p, parent, fp)); +} + + +static void dock_deinit(WDock *dock) +{ + while(dock->dockapps!=NULL) + destroy_obj((Obj*)dock->dockapps->reg); + + UNLINK_ITEM(docks, dock, dock_next, dock_prev); + + dock_brush_release(dock); + + window_deinit((WWindow*) dock); +} + + +bool dock_may_destroy(WDock *dock) +{ + if(dock->dockapps!=NULL){ + warn_obj(modname, "Dock \"%s\" is still managing other objects " + " -- refusing to close.", region_name((WRegion*)dock)); + return FALSE; + } + + return TRUE; +} + + +EXTL_EXPORT +WDock *mod_dock_create(ExtlTab tab) +{ + char *mode=NULL; + bool floating=FALSE; + int screenid=0; + WScreen *screen=NULL; + WDock *dock=NULL; + WRegion *stdisp=NULL; + WMPlexSTDispInfo din; + + if(extl_table_gets_s(tab, "mode", &mode)){ + if(strcmp(mode, "floating")==0){ + floating=TRUE; + }else if(strcmp(mode, "embedded")!=0){ + warn("Invalid dock mode."); + free(mode); + return NULL; + } + free(mode); + } + + extl_table_gets_i(tab, "screen", &screenid); + screen=ioncore_find_screen_id(screenid); + if(screen==NULL){ + warn("Screen %d does not exist.", screenid); + return NULL; + } + + for(dock=docks; dock; dock=dock->dock_next){ + if(region_screen_of((WRegion*)dock)==screen){ + warn("Screen %d already has a dock. Refusing to create another.", + screenid); + return NULL; + } + } + + if(!floating){ + mplex_get_stdisp((WMPlex*)screen, &stdisp, &din); + if(stdisp!=NULL && !extl_table_is_bool_set(tab, "force")){ + warn("Screen %d already has an stdisp. Refusing to add embedded " + "dock.", screenid); + return NULL; + } + } + + /* Create the dock */ + + if(floating){ + WMPlexAttachParams par; + + par.flags=(MPLEX_ATTACH_UNNUMBERED + |MPLEX_ATTACH_SIZEPOLICY + |MPLEX_ATTACH_GEOM); + + par.szplcy=SIZEPOLICY_FREE; + par.geom.x=0; + par.geom.y=0; + par.geom.w=1; + par.geom.h=1; + + if(extl_table_is_bool_set(tab, "floating_hidden")) + par.flags|=MPLEX_ATTACH_HIDDEN; + + dock=(WDock*)mplex_do_attach_new((WMPlex*)screen, &par, + (WRegionCreateFn*)create_dock, + NULL); + }else{ + WFitParams fp; + + fp.mode=REGION_FIT_BOUNDS|REGION_FIT_WHATEVER; + fp.g.x=0; + fp.g.y=0; + fp.g.w=1; + fp.g.h=1; + + dock=create_dock((WWindow*)screen, &fp); + } + + if(dock==NULL){ + warn("Failed to create dock."); + return NULL; + } + + /* Get parameters */ + dock->save=FALSE; + dock_do_set(dock, tab, FALSE); + + /* Final setup */ + if(floating){ + WRQGeomParams rq=RQGEOMPARAMS_INIT; + const WRectangle *pg=®ION_GEOM(screen); + + /* Just calculate real min/max size */ + dock_managed_rqgeom_(dock, NULL, 0, NULL, NULL, TRUE); + + rq.geom.w=minof(dock->min_w, pg->w); + rq.geom.h=minof(dock->min_h, pg->h); + calc_dock_pos(&rq.geom, pg, dock->pos); + + region_rqgeom((WRegion*)dock, &rq, NULL); + + return dock; + }else{ + mplexpos(dock->pos, &din.pos); + din.fullsize=FALSE; /* not supported */ + if(mplex_set_stdisp((WMPlex*)screen, (WRegion*)dock, &din)) + return dock; + } + + /* Failed to attach. */ + warn("Failed to attach dock to screen."); + destroy_obj((Obj*)dock); + return NULL; +} + + +/*}}}*/ + + +/*{{{ Toggle */ + + +/*EXTL_DOC + * Toggle floating docks on \var{mplex}. + */ +EXTL_EXPORT +void mod_dock_set_floating_shown_on(WMPlex *mplex, const char *how) +{ + int setpar=libtu_setparam_invert(libtu_string_to_setparam(how)); + WDock *dock; + + for(dock=docks; dock; dock=dock->dock_next){ + if(REGION_MANAGER(dock)==(WRegion*)mplex) + mplex_set_hidden(mplex, (WRegion*)dock, setpar); + } +} + + +/*}}}*/ + + +/*{{{ Save/load */ + + +ExtlTab dock_get_configuration(WDock *dock) +{ + ExtlTab tab; + + if(dock->save==FALSE) + return extl_table_none(); + + tab=region_get_base_configuration((WRegion*)dock); + dock_do_get(dock, tab); + + return tab; +} + + +WRegion *dock_load(WWindow *par, const WFitParams *fp, ExtlTab tab) +{ + WDock *dock=create_dock(par, fp); + if(dock!=NULL){ + dock_set(dock, tab); + dock_fitrep(dock, NULL, fp); + } + + return (WRegion*)dock; +} + + +/*}}}*/ + + +/*{{{ Client window management setup */ + + +static bool dock_do_attach_final(WDock *dock, WRegion *reg, void *unused) +{ + WDockApp *dockapp, *before_dockapp; + WRectangle geom; + WFitParams fp; + bool draw_border=TRUE; + int pos=INT_MAX; + + /* Create and initialise a new WDockApp struct */ + dockapp=ALLOC(WDockApp); + + if(dockapp==NULL) + return FALSE; + + if(OBJ_IS(reg, WClientWin)){ + ExtlTab proptab=((WClientWin*)reg)->proptab; + extl_table_gets_b(proptab, CLIENTWIN_WINPROP_BORDER, &draw_border); + extl_table_gets_i(proptab, CLIENTWIN_WINPROP_POSITION, &pos); + } + + dockapp->reg=reg; + dockapp->draw_border=draw_border; + dockapp->pos=pos; + dockapp->tile=FALSE; + + /* Insert the dockapp at the correct relative position */ + before_dockapp=dock->dockapps; + for(before_dockapp=dock->dockapps; + before_dockapp!=NULL && dockapp->pos>=before_dockapp->pos; + before_dockapp=before_dockapp->next){ + } + + if(before_dockapp!=NULL){ + LINK_ITEM_BEFORE(dock->dockapps, before_dockapp, dockapp, next, prev); + }else{ + LINK_ITEM(dock->dockapps, dockapp, next, prev); + } + + region_set_manager(reg, (WRegion*)dock); + + geom=REGION_GEOM(reg); + dock_managed_rqgeom_(dock, reg, + REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_Y, + &geom, NULL, FALSE); + + region_map(reg); + + return TRUE; +} + + + +static WRegion *dock_do_attach(WDock *dock, WRegionAttachData *data) +{ + WFitParams fp; + dock_get_tile_size(dock, &(fp.g)); + fp.g.x=0; + fp.g.y=0; + fp.mode=REGION_FIT_WHATEVER|REGION_FIT_BOUNDS; + + return region_attach_helper((WRegion*)dock, (WWindow*)dock, &fp, + (WRegionDoAttachFn*)dock_do_attach_final, + NULL, data); +} + + +/*EXTL_DOC + * Attach \var{reg} to \var{dock}. + */ +EXTL_EXPORT_MEMBER +bool dock_attach(WDock *dock, WRegion *reg) +{ + WRegionAttachData data; + WFitParams fp; + + data.type=REGION_ATTACH_REPARENT; + data.u.reg=reg; + + return (dock_do_attach(dock, &data)!=NULL); +} + + +static bool dock_handle_drop(WDock *dock, int x, int y, + WRegion *dropped) +{ + return dock_attach(dock, dropped); +} + + +static WRegion *dock_ph_handler(WDock *dock, int flags, WRegionAttachData *data) +{ + return dock_do_attach(dock, data); +} + + +static WPHolder *dock_managed_get_pholder(WDock *dock, WRegion *mgd) +{ + return (WPHolder*)create_basicpholder((WRegion*)dock, + ((WBasicPHolderHandler*) + dock_ph_handler)); +} + + +static WPHolder *dock_prepare_manage(WDock *dock, const WClientWin *cwin, + const WManageParams *param UNUSED, + int redir) +{ + if(redir==MANAGE_REDIR_STRICT_YES) + return NULL; + + return (WPHolder*)create_basicpholder((WRegion*)dock, + ((WBasicPHolderHandler*) + dock_ph_handler)); +} + + +static void dock_managed_remove(WDock *dock, WRegion *reg) +{ + + WDockApp *dockapp=dock_find_dockapp(dock, reg); + + if(dockapp==NULL) + return; + + UNLINK_ITEM(dock->dockapps, dockapp, next, prev); + free(dockapp); + + region_unset_manager(reg, (WRegion*)dock); + + dock_resize(dock); +} + + +static bool dock_clientwin_is_dockapp(WClientWin *cwin, + const WManageParams *param) +{ + bool is_dockapp=FALSE; + + /* First, inspect the WManageParams.dockapp parameter */ + if(param->dockapp){ + is_dockapp=TRUE; + } + + /* Second, inspect the _NET_WM_WINDOW_TYPE property */ + if(!is_dockapp){ + static Atom atom__net_wm_window_type=None; + static Atom atom__net_wm_window_type_dock=None; + Atom actual_type=None; + int actual_format; + unsigned long nitems; + unsigned long bytes_after; + unsigned char *prop; + + if(atom__net_wm_window_type==None){ + atom__net_wm_window_type=XInternAtom(ioncore_g.dpy, + "_NET_WM_WINDOW_TYPE", + False); + } + if(atom__net_wm_window_type_dock==None){ + atom__net_wm_window_type_dock=XInternAtom(ioncore_g.dpy, + "_NET_WM_WINDOW_TYPE_DOCK", + False); + } + if(XGetWindowProperty(ioncore_g.dpy, cwin->win, atom__net_wm_window_type, + 0, sizeof(Atom), False, XA_ATOM, &actual_type, + &actual_format, &nitems, &bytes_after, &prop) + ==Success){ + if(actual_type==XA_ATOM && nitems>=1 + && *(Atom*)prop==atom__net_wm_window_type_dock){ + is_dockapp=TRUE; + } + XFree(prop); + } + } + + /* Third, inspect the WM_CLASS property */ + if(!is_dockapp){ + char **p; + int n; + + p=xwindow_get_text_property(cwin->win, XA_WM_CLASS, &n); + if(p!=NULL){ + if(n>=2 && strcmp(p[1], "DockApp")==0){ + is_dockapp=TRUE; + } + XFreeStringList(p); + } + } + + /* Fourth, inspect the _KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR property */ + if(!is_dockapp){ + static Atom atom__kde_net_wm_system_tray_window_for=None; + Atom actual_type=None; + int actual_format; + unsigned long nitems; + unsigned long bytes_after; + unsigned char *prop; + + if(atom__kde_net_wm_system_tray_window_for==None){ + atom__kde_net_wm_system_tray_window_for=XInternAtom(ioncore_g.dpy, + "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR", + False); + } + if(XGetWindowProperty(ioncore_g.dpy, cwin->win, + atom__kde_net_wm_system_tray_window_for, 0, + sizeof(Atom), False, AnyPropertyType, + &actual_type, &actual_format, &nitems, + &bytes_after, &prop)==Success){ + if(actual_type!=None){ + is_dockapp=TRUE; + } + XFree(prop); + } + } + + return is_dockapp; + +} + + +static WDock *dock_find_suitable_dock(WClientWin *cwin, + const WManageParams *param) +{ + WDock *dock; + + for(dock=docks; dock; dock=dock->dock_next){ + if(!dock->is_auto) + continue; + if(!region_same_rootwin((WRegion*)dock, (WRegion*)cwin)) + continue; + break; + } + + return dock; +} + + +static bool clientwin_do_manage_hook(WClientWin *cwin, const WManageParams *param) +{ + WDock *dock; + + if(!dock_clientwin_is_dockapp(cwin, param)){ + return FALSE; + } + + dock=dock_find_suitable_dock(cwin, param); + if(!dock){ + return FALSE; + } + + return region_manage_clientwin((WRegion*)dock, cwin, param, + MANAGE_REDIR_PREFER_NO); +} + + +/*}}}*/ + + +/*{{{ Module init/deinit */ + + +bool mod_dock_init() +{ + + if(XShapeQueryExtension(ioncore_g.dpy, &shape_event_basep, + &shape_error_basep)){ + shape_extension=TRUE; + }else{ + XMissingExtension(ioncore_g.dpy, "SHAPE"); + } + + if(!ioncore_register_regclass(&CLASSDESCR(WDock), + (WRegionLoadCreateFn*)dock_load)){ + return FALSE; + } + + if(!mod_dock_register_exports()){ + ioncore_unregister_regclass(&CLASSDESCR(WDock)); + return FALSE; + } + + dock_bindmap=ioncore_alloc_bindmap("WDock", NULL); + if(dock_bindmap==NULL){ + warn("Unable to allocate dock bindmap."); + mod_dock_unregister_exports(); + ioncore_unregister_regclass(&CLASSDESCR(WDock)); + } + + extl_read_config("cfg_dock", NULL, TRUE); + + hook_add(clientwin_do_manage_alt, + (WHookDummy*)clientwin_do_manage_hook); + + return TRUE; + +} + + +void mod_dock_deinit() +{ + WDock *dock; + + ioncore_unregister_regclass(&CLASSDESCR(WDock)); + + hook_remove(clientwin_do_manage_alt, + (WHookDummy*)clientwin_do_manage_hook); + + dock=docks; + while(dock!=NULL){ + WDock *next=dock->dock_next; + destroy_obj((Obj*)dock); + dock=next; + } + + mod_dock_unregister_exports(); + + if(dock_bindmap!=NULL){ + ioncore_free_bindmap("WDock", dock_bindmap); + dock_bindmap=NULL; + } +} + + +/*}}}*/ + + +/*{{{ WDock class description and dynfun list */ + + +static DynFunTab dock_dynfuntab[]={ + {window_draw, dock_draw}, + {region_updategr, dock_updategr}, + {region_managed_rqgeom, dock_managed_rqgeom}, + {(DynFun*)region_prepare_manage, (DynFun*)dock_prepare_manage}, + {region_managed_remove, dock_managed_remove}, + {(DynFun*)region_get_configuration, (DynFun*)dock_get_configuration}, + {region_size_hints, dock_size_hints}, + {(DynFun*)region_fitrep, (DynFun*)dock_fitrep}, + {(DynFun*)region_orientation, (DynFun*)dock_orientation}, + {(DynFun*)region_may_destroy, (DynFun*)dock_may_destroy}, + {(DynFun*)region_handle_drop, (DynFun*)dock_handle_drop}, + + {(DynFun*)region_managed_get_pholder, + (DynFun*)dock_managed_get_pholder}, + END_DYNFUNTAB +}; + + +EXTL_EXPORT +IMPLCLASS(WDock, WWindow, dock_deinit, dock_dynfuntab); + + +/*}}}*/ + diff --git a/mod_menu/Makefile b/mod_menu/Makefile new file mode 100644 index 0000000..9a2fac6 --- /dev/null +++ b/mod_menu/Makefile @@ -0,0 +1,27 @@ +## +## Menu module Makefile +## + +# System-specific configuration is in system.mk +TOPDIR=.. +include $(TOPDIR)/build/system-inc.mk + +###################################### + +INCLUDES += $(X11_INCLUDES) $(LIBTU_INCLUDES) $(LIBEXTL_INCLUDES) -I.. +CFLAGS += $(XOPEN_SOURCE) $(C99_SOURCE) + +SOURCES=main.c menu.c mkmenu.c grabmenu.c + +MAKE_EXPORTS=mod_menu + +MODULE=mod_menu +MODULE_STUB=mod_menu.lua + +###################################### + +include $(TOPDIR)/build/rules.mk + +###################################### + +_install: module_install diff --git a/mod_menu/grabmenu.c b/mod_menu/grabmenu.c new file mode 100644 index 0000000..cc94437 --- /dev/null +++ b/mod_menu/grabmenu.c @@ -0,0 +1,89 @@ +/* + * ion/mod_menu/grabmenu.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include "menu.h" +#include "mkmenu.h" + + +static bool grabmenu_handler(WRegion *reg, XEvent *xev) +{ + XKeyEvent *ev=&xev->xkey; + WMenu *menu=(WMenu*)reg; + + if(ev->type==KeyRelease){ + if(ioncore_unmod(ev->state, ev->keycode)==0){ + menu_finish(menu); + return TRUE; + } + return FALSE; + } + + if(reg==NULL) + return FALSE; + + if(menu->gm_state==ev->state && ev->keycode==menu->gm_kcb) + menu_select_next(menu); + + return FALSE; +} + + +/*--lowlevel routine not to be called by the user--*/ +EXTL_EXPORT +WMenu *mod_menu_do_grabmenu(WMPlex *mplex, ExtlFn handler, ExtlTab tab, + ExtlTab param) +{ + WMenuCreateParams fnp; + WMPlexAttachParams par; + WMenu *menu; + XKeyEvent *ev; + + ev=ioncore_current_key_event(); + + if(ev==NULL) + return NULL; + + fnp.handler=handler; + fnp.tab=tab; + fnp.pmenu_mode=FALSE; + fnp.submenu_mode=FALSE; + fnp.big_mode=extl_table_is_bool_set(param, "big"); + fnp.initial=0; + extl_table_gets_i(param, "initial", &(fnp.initial)); + + par.flags=(MPLEX_ATTACH_SWITCHTO| + MPLEX_ATTACH_UNNUMBERED| + MPLEX_ATTACH_SIZEPOLICY); + par.szplcy=SIZEPOLICY_FULL_BOUNDS; + + menu=(WMenu*)mplex_do_attach_new(mplex, &par, + (WRegionCreateFn*)create_menu, + (void*)&fnp); + + if(menu==NULL) + return FALSE; + + menu->gm_kcb=ev->keycode; + menu->gm_state=ev->state; + + ioncore_grab_establish((WRegion*)menu, grabmenu_handler, NULL, 0); + + return menu; +} + diff --git a/mod_menu/main.c b/mod_menu/main.c new file mode 100644 index 0000000..98df15b --- /dev/null +++ b/mod_menu/main.c @@ -0,0 +1,73 @@ +/* + * ion/mod_menu/main.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include + +#include "menu.h" +#include "exports.h" + + +/*{{{ Module information */ + + +#include "../version.h" + +char mod_menu_ion_api_version[]=ION_API_VERSION; + + +/*}}}*/ + + +/*{{{ Bindmaps */ + + +WBindmap *mod_menu_menu_bindmap=NULL; + + +/*}}}*/ + + +/*{{{ Init & deinit */ + + +void mod_menu_deinit() +{ + if(mod_menu_menu_bindmap!=NULL){ + ioncore_free_bindmap("WMenu", mod_menu_menu_bindmap); + mod_menu_menu_bindmap=NULL; + } + + mod_menu_unregister_exports(); +} + + +bool mod_menu_init() +{ + mod_menu_menu_bindmap=ioncore_alloc_bindmap("WMenu", NULL); + + if(mod_menu_menu_bindmap==NULL) + return FALSE; + + if(!mod_menu_register_exports()){ + mod_menu_deinit(); + return FALSE; + } + + /*ioncore_read_config("cfg_menu", NULL, TRUE);*/ + + return TRUE; +} + + +/*}}}*/ + diff --git a/mod_menu/main.h b/mod_menu/main.h new file mode 100644 index 0000000..c5568b8 --- /dev/null +++ b/mod_menu/main.h @@ -0,0 +1,23 @@ +/* + * ion/mod_menu/main.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_MOD_MENU_MAIN_H +#define ION_MOD_MENU_MAIN_H + +#include + +extern bool mod_menu_init(); +extern void mod_menu_deinit(); + +extern WBindmap *mod_menu_menu_bindmap; + + +#endif /* ION_MOD_MENU_MAIN_H */ diff --git a/mod_menu/menu.c b/mod_menu/menu.c new file mode 100644 index 0000000..b8ec9cf --- /dev/null +++ b/mod_menu/menu.c @@ -0,0 +1,1375 @@ +/* + * ion/mod_menu/menu.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "menu.h" +#include "main.h" + + +#define MENU_WIN(MENU) ((MENU)->win.win) + + +/*{{{ Helpers */ + + +static bool extl_table_getis(ExtlTab tab, int i, const char *s, char c, + void *p) +{ + ExtlTab sub; + bool ret; + + if(!extl_table_geti_t(tab, i, &sub)) + return FALSE; + ret=extl_table_get(sub, 's', c, s, p); + extl_unref_table(sub); + return ret; +} + + +/*}}}*/ + + +/*{{{ Drawing routines */ + + +static void get_outer_geom(WMenu *menu, WRectangle *geom) +{ + geom->x=0; + geom->y=0; + geom->w=REGION_GEOM(menu).w; + geom->h=REGION_GEOM(menu).h; +} + + +static void get_inner_geom(WMenu *menu, WRectangle *geom) +{ + GrBorderWidths bdw; + + get_outer_geom(menu, geom); + + if(menu->brush!=NULL){ + grbrush_get_border_widths(menu->brush, &bdw); + geom->x+=bdw.left; + geom->y+=bdw.top; + geom->w-=bdw.left+bdw.right; + geom->h-=bdw.top+bdw.bottom; + geom->w=maxof(0, geom->w); + geom->h=maxof(0, geom->h); + } +} + + +static void menu_draw_entry(WMenu *menu, int i, const WRectangle *igeom, + bool complete) +{ + WRectangle geom; + int a; + + static const char *attrs[]={ + "active-selected-normal", + "active-selected-submenu", + "active-unselected-normal", + "active-unselected-submenu", + "inactive-selected-normal", + "inactive-selected-submenu", + "inactive-unselected-normal", + "inactive-unselected-submenu", + }; + + if(menu->entry_brush==NULL) + return; + + geom=*igeom; + geom.h=menu->entry_h; + geom.y+=(i-menu->first_entry)*(menu->entry_h+menu->entry_spacing); + + a=((REGION_IS_ACTIVE(menu) ? 0 : 4) + |(menu->selected_entry==i ? 0 : 2) + |(menu->entries[i].flags&WMENUENTRY_SUBMENU ? 1 : 0)); + + grbrush_begin(menu->entry_brush, &geom, GRBRUSH_AMEND); + + grbrush_draw_textbox(menu->entry_brush, &geom, menu->entries[i].title, + attrs[a], complete); + + grbrush_end(menu->entry_brush); +} + + +void menu_draw_entries(WMenu *menu, bool complete) +{ + WRectangle igeom; + int i, mx; + + if(menu->entry_brush==NULL) + return; + + get_inner_geom(menu, &igeom); + + mx=menu->first_entry+menu->vis_entries; + mx=(mx < menu->n_entries ? mx : menu->n_entries); + + for(i=menu->first_entry; ibrush==NULL) + return; + + get_outer_geom(menu, &geom); + + grbrush_begin(menu->brush, &geom, + (complete ? 0 : GRBRUSH_NO_CLEAR_OK)); + + grbrush_draw_border(menu->brush, &geom, substyle); + + menu_draw_entries(menu, FALSE); + + grbrush_end(menu->brush); +} + + +/*}}}*/ + + +/*{{{ Resize */ + + +static void menu_calc_size(WMenu *menu, bool maxexact, + int maxw, int maxh, + int *w_ret, int *h_ret) +{ + GrBorderWidths bdw, e_bdw; + char *str; + int i; + int nath, bdh, maxew=menu->max_entry_w; + + grbrush_get_border_widths(menu->brush, &bdw); + grbrush_get_border_widths(menu->entry_brush, &e_bdw); + + if(maxexact || maxew>maxw-(int)bdw.left-(int)bdw.right){ + maxew=maxw-bdw.left-bdw.right; + *w_ret=maxw; + }else{ + *w_ret=maxew+bdw.left+bdw.right; + } + + bdh=bdw.top+bdw.bottom; + + if(menu->n_entries==0){ + *h_ret=(maxexact ? maxh : bdh); + menu->first_entry=0; + menu->vis_entries=0; + }else{ + int vis=(maxh-bdh+e_bdw.spacing)/(e_bdw.spacing+menu->entry_h); + if(vis>menu->n_entries){ + vis=menu->n_entries; + menu->first_entry=0; + }else if(menu->selected_entry>=0){ + if(menu->selected_entryfirst_entry) + menu->first_entry=menu->selected_entry; + else if(menu->selected_entry>=menu->first_entry+vis) + menu->first_entry=menu->selected_entry-vis+1; + } + if(vis<=0) + vis=1; + menu->vis_entries=vis; + if(maxexact) + *h_ret=maxh; + else + *h_ret=vis*menu->entry_h+(vis-1)*e_bdw.spacing+bdh; + } + + /* Calculate new shortened entry names */ + maxew-=e_bdw.left+e_bdw.right; + + for(i=0; in_entries; i++){ + if(menu->entries[i].title){ + free(menu->entries[i].title); + menu->entries[i].title=NULL; + } + if(maxew<=0) + continue; + + if(extl_table_getis(menu->tab, i+1, "name", 's', &str)){ + menu->entries[i].title=grbrush_make_label(menu->entry_brush, + str, maxew); + free(str); + } + } +} + + +void calc_size(WMenu *menu, int *w, int *h) +{ + if(menu->pmenu_mode){ + menu_calc_size(menu, FALSE, INT_MAX, INT_MAX, w, h); + }else{ + menu_calc_size(menu, !(menu->last_fp.mode®ION_FIT_BOUNDS), + menu->last_fp.g.w, menu->last_fp.g.h, w, h); + } +} + + +/* Return offset from bottom-left corner of containing mplex or top-right + * corner of parent menu for the respective corner of menu. + */ +static void get_placement_offs(WMenu *menu, int *xoff, int *yoff) +{ + GrBorderWidths bdw; + + *xoff=0; + *yoff=0; + + if(menu->brush!=NULL){ + grbrush_get_border_widths(menu->brush, &bdw); + *xoff+=bdw.right; + *yoff+=bdw.top; + } + + if(menu->entry_brush!=NULL){ + grbrush_get_border_widths(menu->entry_brush, &bdw); + *xoff+=bdw.right; + *yoff+=bdw.top; + } +} + + +#define MINIMUM_Y_VISIBILITY 20 +#define POINTER_OFFSET 5 + +static void menu_firstfit(WMenu *menu, bool submenu, const WRectangle *refg) +{ + WRectangle geom; + + calc_size(menu, &(geom.w), &(geom.h)); + + if(!(menu->last_fp.mode®ION_FIT_BOUNDS)){ + geom.x=menu->last_fp.g.x; + geom.y=menu->last_fp.g.y; + }else if(menu->pmenu_mode){ + geom.x=refg->x; + geom.y=refg->y; + + if(!submenu){ + const WRectangle *maxg = + ®ION_GEOM(REGION_PARENT((WRegion*)menu)); + + geom.x-=geom.w/2; + geom.y+=POINTER_OFFSET; + + if(geom.y+MINIMUM_Y_VISIBILITY>maxg->y+maxg->h){ + geom.y=maxg->y+maxg->h-MINIMUM_Y_VISIBILITY; + geom.x=refg->x+POINTER_OFFSET; + if(geom.x+geom.w>maxg->x+maxg->w) + geom.x=refg->x-geom.w-POINTER_OFFSET; + }else{ + if(geom.x<0) + geom.x=0; + else if(geom.x+geom.w>maxg->x+maxg->w) + geom.x=maxg->x+maxg->w-geom.w; + } + } + }else{ + const WRectangle *maxg=&(menu->last_fp.g); + if(submenu){ + int l, r, t, b, xoff, yoff; + + get_placement_offs(menu, &xoff, &yoff); + l=refg->x+xoff; + r=refg->x+refg->w+xoff; + t=refg->y-yoff; + b=refg->y+refg->h-yoff; + + geom.x=maxof(l, r-geom.w); + if(geom.x+geom.w>maxg->x+maxg->w) + geom.x=maxg->x; + + geom.y=minof(b-geom.h, t); + if(geom.yy) + geom.y=maxg->y; + }else{ + geom.x=maxg->x; + geom.y=maxg->y+maxg->h-geom.h; + } + } + + window_do_fitrep(&menu->win, NULL, &geom); +} + + +static void menu_do_refit(WMenu *menu, WWindow *par, const WFitParams *oldfp) +{ + WRectangle geom; + + calc_size(menu, &(geom.w), &(geom.h)); + + if(!(menu->last_fp.mode®ION_FIT_BOUNDS)){ + geom.x=menu->last_fp.g.x; + geom.y=menu->last_fp.g.y; + }else if(menu->pmenu_mode){ + geom.x=REGION_GEOM(menu).x; + geom.y=REGION_GEOM(menu).y; + }else{ + const WRectangle *maxg=&(menu->last_fp.g); + int xdiff=REGION_GEOM(menu).x-oldfp->g.x; + int ydiff=(REGION_GEOM(menu).y+REGION_GEOM(menu).h + -(oldfp->g.y+oldfp->g.h)); + geom.x=maxof(0, minof(maxg->x+xdiff, maxg->x+maxg->w-geom.w)); + geom.y=maxof(0, minof(maxg->y+maxg->h+ydiff, maxg->y+maxg->h)-geom.h); + } + + window_do_fitrep(&menu->win, par, &geom); +} + + +bool menu_fitrep(WMenu *menu, WWindow *par, const WFitParams *fp) +{ + WFitParams oldfp; + + if(par!=NULL && !region_same_rootwin((WRegion*)par, (WRegion*)menu)) + return FALSE; + + oldfp=menu->last_fp; + menu->last_fp=*fp; + menu_do_refit(menu, par, &oldfp); + + if(menu->submenu!=NULL && !menu->pmenu_mode) + region_fitrep((WRegion*)(menu->submenu), par, fp); + + return TRUE; +} + + +/*}}}*/ + + +/*{{{ Brush update */ + + +static void calc_entry_dimens(WMenu *menu) +{ + int i, n=extl_table_get_n(menu->tab); + GrFontExtents fnte; + GrBorderWidths bdw; + int maxw=0; + char *str; + +#if 0 + if(extl_table_gets_s(menu->tab, title, &str)){ + maxw=grbrush_get_text_width(title_brush, str, strlen(str)); + free(str); + } +#endif + + for(i=1; i<=n; i++){ + if(extl_table_getis(menu->tab, i, "name", 's', &str)){ + int w=grbrush_get_text_width(menu->entry_brush, + str, strlen(str)); + if(w>maxw) + maxw=w; + free(str); + } + } + + grbrush_get_border_widths(menu->entry_brush, &bdw); + grbrush_get_font_extents(menu->entry_brush, &fnte); + + menu->max_entry_w=maxw+bdw.left+bdw.right; + menu->entry_h=fnte.max_height+bdw.top+bdw.bottom; + menu->entry_spacing=bdw.spacing; +} + + +static bool menu_init_gr(WMenu *menu, WRootWin *rootwin, Window win) +{ + GrBrush *brush, *entry_brush; + char *st; + const char *style=(menu->big_mode + ? "input-menu-big" + : (menu->pmenu_mode + ? "input-menu-pmenu" + : "input-menu-normal")); + + const char *entry_style=(menu->big_mode + ? "tab-menuentry-big" + : (menu->pmenu_mode + ? "tab-menuentry-pmenu" + : "tab-menuentry-normal")); + + brush=gr_get_brush(win, rootwin, style); + + if(brush==NULL) + return FALSE; + + entry_brush=grbrush_get_slave(brush, rootwin, entry_style); + + if(entry_brush==NULL){ + grbrush_release(brush); + return FALSE; + } + + if(menu->entry_brush!=NULL) + grbrush_release(menu->entry_brush); + if(menu->brush!=NULL) + grbrush_release(menu->brush); + + menu->brush=brush; + menu->entry_brush=entry_brush; + + calc_entry_dimens(menu); + + return TRUE; +} + + +void menu_updategr(WMenu *menu) +{ + if(!menu_init_gr(menu, region_rootwin_of((WRegion*)menu), + MENU_WIN(menu))){ + return; + } + + menu_do_refit(menu, NULL, &(menu->last_fp)); + + region_updategr_default((WRegion*)menu); + + window_draw((WWindow*)menu, TRUE); +} + + +static void menu_release_gr(WMenu *menu) +{ + if(menu->entry_brush!=NULL){ + grbrush_release(menu->entry_brush); + menu->entry_brush=NULL; + } + if(menu->brush!=NULL){ + grbrush_release(menu->brush); + menu->brush=NULL; + } +} + + +/*}}}*/ + + +/*{{{ Init/deinit */ + + +static WMenuEntry *preprocess_menu(ExtlTab tab, int *n_entries) +{ + ExtlTab entry, sub; + ExtlFn fn; + WMenuEntry *entries; + int i, n; + + n=extl_table_get_n(tab); + *n_entries=n; + + if(n<=0) + return NULL; + + entries=ALLOC_N(WMenuEntry, n); + + if(entries==NULL) + return NULL; + + /* Initialise entries and check submenus */ + for(i=1; i<=n; i++){ + entries[i-1].title=NULL; + entries[i-1].flags=0; + if(extl_table_getis(tab, i, "submenu_fn", 'f', &fn)){ + entries[i-1].flags|=WMENUENTRY_SUBMENU; + extl_unref_fn(fn); + }else if(extl_table_getis(tab, i, "submenu", 't', &sub)){ + entries[i-1].flags|=WMENUENTRY_SUBMENU; + extl_unref_table(sub); + } + } + + return entries; +} + + + +bool menu_init(WMenu *menu, WWindow *par, const WFitParams *fp, + const WMenuCreateParams *params) +{ + Window win; + int i; + + menu->entries=preprocess_menu(params->tab, &(menu->n_entries)); + + if(menu->entries==NULL){ + warn(TR("Empty menu.")); + return FALSE; + } + + menu->tab=extl_ref_table(params->tab); + menu->handler=extl_ref_fn(params->handler); + menu->pmenu_mode=params->pmenu_mode; + menu->big_mode=params->big_mode; + + menu->last_fp=*fp; + + if(params->pmenu_mode) + menu->selected_entry=-1; + else{ + menu->selected_entry=params->initial-1; + if(menu->selected_entry<0) + menu->selected_entry=0; + if(params->initial > menu->n_entries) + menu->selected_entry=0; + } + + menu->max_entry_w=0; + menu->entry_h=0; + menu->brush=NULL; + menu->entry_brush=NULL; + menu->entry_spacing=0; + menu->vis_entries=menu->n_entries; + menu->first_entry=0; + menu->submenu=NULL; + menu->typeahead=NULL; + + menu->gm_kcb=0; + menu->gm_state=0; + + if(!window_init((WWindow*)menu, par, fp)) + goto fail; + + win=menu->win.win; + + if(!menu_init_gr(menu, region_rootwin_of((WRegion*)par), win)) + goto fail2; + + menu_firstfit(menu, params->submenu_mode, &(params->refg)); + + window_select_input(&(menu->win), IONCORE_EVENTMASK_NORMAL); + + region_add_bindmap((WRegion*)menu, mod_menu_menu_bindmap); + + region_register((WRegion*)menu); + + return TRUE; + +fail2: + window_deinit((WWindow*)menu); +fail: + extl_unref_table(menu->tab); + extl_unref_fn(menu->handler); + free(menu->entries); + return FALSE; +} + + +WMenu *create_menu(WWindow *par, const WFitParams *fp, + const WMenuCreateParams *params) +{ + CREATEOBJ_IMPL(WMenu, menu, (p, par, fp, params)); +} + + + +void menu_deinit(WMenu *menu) +{ + int i; + + menu_typeahead_clear(menu); + + if(menu->submenu!=NULL) + destroy_obj((Obj*)menu->submenu); + + extl_unref_table(menu->tab); + extl_unref_fn(menu->handler); + + for(i=0; in_entries; i++) + free(menu->entries[i].title); + free(menu->entries); + + menu_release_gr(menu); + window_deinit((WWindow*)menu); +} + + +/*}}}*/ + + +/*{{{ Focus */ + + +static void menu_inactivated(WMenu *menu) +{ + window_draw((WWindow*)menu, FALSE); +} + + +static void menu_activated(WMenu *menu) +{ + window_draw((WWindow*)menu, FALSE); +} + + +/*}}}*/ + + +/*{{{ Submenus */ + + +static WMenu *menu_head(WMenu *menu) +{ + WMenu *m=REGION_MANAGER_CHK(menu, WMenu); + return (m==NULL ? menu : menu_head(m)); +} + + +static WMenu *menu_tail(WMenu *menu) +{ + return (menu->submenu==NULL ? menu : menu_tail(menu->submenu)); +} + + +static void menu_managed_remove(WMenu *menu, WRegion *sub) +{ + bool mcf=region_may_control_focus((WRegion*)menu); + + if(sub!=(WRegion*)menu->submenu) + return; + + menu->submenu=NULL; + + region_unset_manager(sub, (WRegion*)menu); + + if(mcf) + region_do_set_focus((WRegion*)menu, FALSE); +} + + +int get_sub_y_off(WMenu *menu, int n) +{ + /* top + sum of above entries and spacings - top_of_sub */ + return (menu->entry_h+menu->entry_spacing)*(n-menu->first_entry); +} + + +static void show_sub(WMenu *menu, int n) +{ + WFitParams fp; + WMenuCreateParams fnp; + WMenu *submenu; + WWindow *par; + + par=REGION_PARENT(menu); + + if(par==NULL) + return; + + fp=menu->last_fp; + + fnp.pmenu_mode=menu->pmenu_mode; + fnp.big_mode=menu->big_mode; + fnp.submenu_mode=TRUE; + + if(menu->pmenu_mode){ + fnp.refg.x=REGION_GEOM(menu).x+REGION_GEOM(menu).w; + fnp.refg.y=REGION_GEOM(menu).y+get_sub_y_off(menu, n); + fnp.refg.w=0; + fnp.refg.h=0; + }else{ + fnp.refg=REGION_GEOM(menu); + } + + fnp.tab=extl_table_none(); + { + ExtlFn fn; + if(extl_table_getis(menu->tab, n+1, "submenu_fn", 'f', &fn)){ + extl_protect(NULL); + extl_call(fn, NULL, "t", &(fnp.tab)); + extl_unprotect(NULL); + extl_unref_fn(fn); + }else{ + extl_table_getis(menu->tab, n+1, "submenu", 't', &(fnp.tab)); + } + if(fnp.tab==extl_table_none()) + return; + } + + fnp.handler=extl_ref_fn(menu->handler); + + fnp.initial=0; + { + ExtlFn fn; + if(extl_table_getis(menu->tab, n+1, "initial", 'f', &fn)){ + extl_protect(NULL); + extl_call(fn, NULL, "i", &(fnp.initial)); + extl_unprotect(NULL); + extl_unref_fn(fn); + }else{ + extl_table_getis(menu->tab, n+1, "initial", 'i', &(fnp.initial)); + } + } + + submenu=create_menu(par, &fp, &fnp); + + if(submenu==NULL) + return; + + menu->submenu=submenu; + region_set_manager((WRegion*)submenu, (WRegion*)menu); + + region_restack((WRegion*)submenu, MENU_WIN(menu), Above); + region_map((WRegion*)submenu); + + if(!menu->pmenu_mode && region_may_control_focus((WRegion*)menu)) + region_do_set_focus((WRegion*)submenu, FALSE); +} + + +static void menu_do_set_focus(WMenu *menu, bool warp) +{ + if(menu->submenu!=NULL) + region_do_set_focus((WRegion*)menu->submenu, warp); + else + window_do_set_focus((WWindow*)menu, warp); +} + + +void menu_restack(WMenu *menu, Window other, int mode) +{ + xwindow_restack(MENU_WIN(menu), other, mode); + if(menu->submenu!=NULL) + region_restack((WRegion*)(menu->submenu), MENU_WIN(menu), Above); +} + + +void menu_stacking(WMenu *menu, Window *bottomret, Window *topret) +{ + *topret=None; + + if(menu->submenu!=NULL) + region_stacking((WRegion*)(menu->submenu), bottomret, topret); + + *bottomret=MENU_WIN(menu); + if(*topret==None) + *topret=MENU_WIN(menu); + +} + + +/*}}}*/ + + +/*{{{ Exports */ + + +static void menu_do_select_nth(WMenu *menu, int n) +{ + int oldn=menu->selected_entry; + bool drawfull=FALSE; + + if(oldn==n) + return; + + if(menu->submenu!=NULL) + destroy_obj((Obj*)menu->submenu); + + assert(menu->submenu==NULL); + + menu->selected_entry=n; + + if(n>=0){ + if(nfirst_entry){ + menu->first_entry=n; + drawfull=TRUE; + }else if(n>=menu->first_entry+menu->vis_entries){ + menu->first_entry=n-menu->vis_entries+1; + drawfull=TRUE; + } + + if(menu->entries[n].flags&WMENUENTRY_SUBMENU && + menu->pmenu_mode){ + show_sub(menu, n); + } + } + + if(drawfull){ + menu_draw_entries(menu, TRUE); + }else{ + /* redraw new and old selected entry */ + WRectangle igeom; + get_inner_geom(menu, &igeom); + + /* !!!BEGIN!!! */ + if(oldn!=-1) + menu_draw_entry(menu, oldn, &igeom, TRUE); + if(n!=-1) + menu_draw_entry(menu, n, &igeom, TRUE); + } +} + + +/*EXTL_DOC + * Select \var{n}:th entry in menu. + */ +EXTL_EXPORT_MEMBER +void menu_select_nth(WMenu *menu, int n) +{ + if(n<0) + n=0; + if(n>=menu->n_entries) + n=menu->n_entries-1; + + menu_typeahead_clear(menu); + menu_do_select_nth(menu, n); +} + + +/*EXTL_DOC + * Select previous entry in menu. + */ +EXTL_EXPORT_MEMBER +void menu_select_prev(WMenu *menu) +{ + menu_select_nth(menu, (menu->selected_entry<=0 + ? menu->n_entries-1 + : menu->selected_entry-1)); +} + + +/*EXTL_DOC + * Select next entry in menu. + */ +EXTL_EXPORT_MEMBER +void menu_select_next(WMenu *menu) +{ + menu_select_nth(menu, (menu->selected_entry+1)%menu->n_entries); +} + + +static void menu_do_finish(WMenu *menu) +{ + ExtlFn handler; + ExtlTab tab; + bool ok; + WMenu *head=menu_head(menu); + + handler=menu->handler; + menu->handler=extl_fn_none(); + + ok=extl_table_geti_t(menu->tab, menu->selected_entry+1, &tab); + + if(region_manager_allows_destroying((WRegion*)head)) + destroy_obj((Obj*)head); + else if(head->submenu!=NULL) + destroy_obj((Obj*)head->submenu); + + if(ok) + extl_call(handler, "t", NULL, tab); + + extl_unref_fn(handler); + extl_unref_table(tab); +} + + +/*EXTL_DOC + * If selected entry is a submenu, display that. + * Otherwise destroy the menu and call handler for selected entry. + */ +EXTL_EXPORT_MEMBER +void menu_finish(WMenu *menu) +{ + menu_typeahead_clear(menu); + + if(!menu->pmenu_mode && menu->selected_entry>=0 && + menu->entries[menu->selected_entry].flags&WMENUENTRY_SUBMENU){ + show_sub(menu, menu->selected_entry); + return; + } + + mainloop_defer_action((Obj*)menu, (WDeferredAction*)menu_do_finish); +} + + + +/*EXTL_DOC + * Close \var{menu} not calling any possible finish handlers. + */ +EXTL_EXPORT_MEMBER +void menu_cancel(WMenu *menu) +{ + if(region_manager_allows_destroying((WRegion*)menu)) + mainloop_defer_destroy((Obj*)menu); +} + + +/*}}}*/ + + +/*{{{ Scroll */ + +static int scroll_time=20; +static int scroll_amount=3; +static WTimer *scroll_timer=NULL; + + +static void reset_scroll_timer() +{ + if(scroll_timer!=NULL){ + destroy_obj((Obj*)scroll_timer); + scroll_timer=NULL; + } +} + + +/*EXTL_DOC + * Set module basic settings. The parameter table may contain the + * following fields: + * + * \begin{tabularx}{\linewidth}{lX} + * \tabhead{Field & Description} + * \var{scroll_amount} & Number of pixels to scroll at a time + * pointer-controlled menus when one extends + * beyond a border of the screen and the pointer + * touches that border. \\ + * \var{scroll_delay} & Time between such scrolling events in + * milliseconds. + * \end{tabularx} + */ +EXTL_EXPORT +void mod_menu_set(ExtlTab tab) +{ + int a, t; + + if(extl_table_gets_i(tab, "scroll_amount", &a)) + scroll_amount=maxof(0, a); + if(extl_table_gets_i(tab, "scroll_delay", &t)) + scroll_time=maxof(0, t); +} + + +/*EXTL_DOC + * Get module basic settings. For details, see \fnref{mod_menu.set}. + */ +EXTL_SAFE +EXTL_EXPORT +ExtlTab mod_menu_get() +{ + ExtlTab tab=extl_create_table(); + extl_table_sets_i(tab, "scroll_amount", scroll_amount); + extl_table_sets_i(tab, "scroll_delay", scroll_time); + return tab; +} + + +enum{ + D_LEFT, + D_RIGHT, + D_DOWN, + D_UP +}; + + +static int calc_diff(const WRectangle *mg, const WRectangle *pg, int d) +{ + switch(d){ + case D_LEFT: + return mg->x+mg->w-pg->w; + case D_UP: + return mg->y+mg->h-pg->h; + case D_RIGHT: + return -mg->x; + case D_DOWN: + return -mg->y; + } + return 0; +} + + +static int scrolld_subs(WMenu *menu, int d) +{ + int diff=0; + WRegion *p=REGION_PARENT_REG(menu); + const WRectangle *pg; + + if(p==NULL) + return 0; + + pg=®ION_GEOM(p); + + while(menu!=NULL){ + diff=maxof(diff, calc_diff(®ION_GEOM(menu), pg, d)); + menu=menu->submenu; + } + + return minof(maxof(0, diff), scroll_amount); +} + + +static void menu_select_entry_at(WMenu *menu, int px, int py); + + +static void do_scroll(WMenu *menu, int xd, int yd) +{ + WRectangle g; + int px=-1, py=-1; + + xwindow_pointer_pos(region_root_of((WRegion*)menu), &px, &py); + + while(menu!=NULL){ + g=REGION_GEOM(menu); + g.x+=xd; + g.y+=yd; + + window_do_fitrep((WWindow*)menu, NULL, &g); + + menu_select_entry_at(menu, px, py); + + menu=menu->submenu; + } +} + + +static void scroll_left(WTimer *timer, WMenu *menu) +{ + if(menu!=NULL){ + do_scroll(menu, -scrolld_subs(menu, D_LEFT), 0); + if(scrolld_subs(menu, D_LEFT)>0){ + timer_set(timer, scroll_time, (WTimerHandler*)scroll_left, + (Obj*)menu); + return; + } + } +} + + +static void scroll_up(WTimer *timer, WMenu *menu) +{ + if(menu!=NULL){ + do_scroll(menu, 0, -scrolld_subs(menu, D_UP)); + if(scrolld_subs(menu, D_UP)>0){ + timer_set(timer, scroll_time, (WTimerHandler*)scroll_up, + (Obj*)menu); + + return; + } + } +} + + +static void scroll_right(WTimer *timer, WMenu *menu) +{ + if(menu!=NULL){ + do_scroll(menu, scrolld_subs(menu, D_RIGHT), 0); + if(scrolld_subs(menu, D_RIGHT)>0){ + timer_set(timer, scroll_time, (WTimerHandler*)scroll_right, + (Obj*)menu); + return; + } + } +} + + +static void scroll_down(WTimer *timer, WMenu *menu) +{ + if(menu!=NULL){ + do_scroll(menu, 0, scrolld_subs(menu, D_DOWN)); + if(scrolld_subs(menu, D_DOWN)>0){ + timer_set(timer, scroll_time, (WTimerHandler*)scroll_down, + (Obj*)menu); + return; + } + } +} + + +static void end_scroll(WMenu *menu) +{ + reset_scroll_timer(); +} + +#define SCROLL_OFFSET 10 + +static void check_scroll(WMenu *menu, int x, int y) +{ + WRegion *parent=REGION_PARENT_REG(menu); + int rx, ry; + WTimerHandler *fn=NULL; + + if(!menu->pmenu_mode) + return; + + if(parent==NULL){ + end_scroll(menu); + return; + } + + region_rootpos(parent, &rx, &ry); + x-=rx; + y-=ry; + + if(x<=SCROLL_OFFSET){ + fn=(WTimerHandler*)scroll_right; + }else if(y<=SCROLL_OFFSET){ + fn=(WTimerHandler*)scroll_down; + }else if(x>=REGION_GEOM(parent).w-SCROLL_OFFSET){ + fn=(WTimerHandler*)scroll_left; + }else if(y>=REGION_GEOM(parent).h-SCROLL_OFFSET){ + fn=(WTimerHandler*)scroll_up; + }else{ + end_scroll(menu); + return; + } + + assert(fn!=NULL); + + if(scroll_timer!=NULL){ + if(scroll_timer->handler==(WTimerHandler*)fn && + timer_is_set(scroll_timer)){ + return; + } + }else{ + scroll_timer=create_timer(); + if(scroll_timer==NULL) + return; + } + + fn(scroll_timer, (Obj*)menu_head(menu)); +} + + +/*}}}*/ + + +/*{{{ Pointer handlers */ + + +int menu_entry_at_root(WMenu *menu, int root_x, int root_y) +{ + int rx, ry, x, y, entry; + WRectangle ig; + region_rootpos((WRegion*)menu, &rx, &ry); + + get_inner_geom(menu, &ig); + + x=root_x-rx-ig.x; + y=root_y-ry-ig.y; + + if(x<0 || x>=ig.w || y<0 || y>=ig.h) + return -1; + + entry=y/(menu->entry_h+menu->entry_spacing); + if(entry<0 || entry>=menu->vis_entries) + return -1; + return entry+menu->first_entry; +} + + +int menu_entry_at_root_tree(WMenu *menu, int root_x, int root_y, + WMenu **realmenu) +{ + int entry=-1; + + menu=menu_tail(menu); + + *realmenu=menu; + + if(!menu->pmenu_mode) + return menu_entry_at_root(menu, root_x, root_y); + + while(menu!=NULL){ + entry=menu_entry_at_root(menu, root_x, root_y); + if(entry>=0){ + *realmenu=menu; + break; + } + menu=REGION_MANAGER_CHK(menu, WMenu); + } + + return entry; +} + + +static void menu_select_entry_at(WMenu *menu, int px, int py) +{ + int entry=menu_entry_at_root_tree(menu, px, py, &menu); + if(entry>=0) + menu_do_select_nth(menu, entry); +} + + +void menu_release(WMenu *menu, XButtonEvent *ev) +{ + int entry=menu_entry_at_root_tree(menu, ev->x_root, ev->y_root, &menu); + end_scroll(menu); + if(entry>=0){ + menu_select_nth(menu, entry); + menu_finish(menu); + }else if(menu->pmenu_mode){ + menu_cancel(menu_head(menu)); + } +} + + +void menu_motion(WMenu *menu, XMotionEvent *ev, int dx, int dy) +{ + menu_select_entry_at(menu, ev->x_root, ev->y_root); + check_scroll(menu, ev->x_root, ev->y_root); +} + + +void menu_button(WMenu *menu, XButtonEvent *ev) +{ + int entry=menu_entry_at_root_tree(menu, ev->x_root, ev->y_root, &menu); + if(entry>=0) + menu_select_nth(menu, entry); +} + + +int menu_press(WMenu *menu, XButtonEvent *ev, WRegion **reg_ret) +{ + menu_button(menu, ev); + menu=menu_head(menu); + ioncore_set_drag_handlers((WRegion*)menu, + NULL, + (WMotionHandler*)menu_motion, + (WButtonHandler*)menu_release, + NULL, + NULL); + return 0; +} + + +/*}}}*/ + + +/*{{{ Typeahead find */ + + +static void menu_insstr(WMenu *menu, const char *buf, size_t n) +{ + size_t oldlen=(menu->typeahead==NULL ? 0 : strlen(menu->typeahead)); + char *newta=(char*)malloc(oldlen+n+1); + char *newta_orig; + int entry; + + if(newta==NULL) + return; + + if(oldlen!=0) + memcpy(newta, menu->typeahead, oldlen); + if(n!=0) + memcpy(newta+oldlen, buf, n); + newta[oldlen+n]='\0'; + newta_orig=newta; + + while(*newta!='\0'){ + bool found=FALSE; + entry=menu->selected_entry; + do{ + if(menu->entries[entry].title!=NULL){ + size_t l=strlen(menu->entries[entry].title); + if(libtu_strcasestr(menu->entries[entry].title, newta)){ + found=TRUE; + break; + } + } + entry=(entry+1)%menu->n_entries; + }while(entry!=menu->selected_entry); + if(found){ + menu_do_select_nth(menu, entry); + break; + } + newta++; + } + + if(newta_orig!=newta){ + if(*newta=='\0'){ + free(newta_orig); + newta=NULL; + }else{ + char *p=scopy(newta); + free(newta_orig); + newta=p; + } + } + if(menu->typeahead!=NULL) + free(menu->typeahead); + menu->typeahead=newta; +} + + +/*EXTL_DOC + * Clear typeahead buffer. + */ +EXTL_EXPORT_MEMBER +void menu_typeahead_clear(WMenu *menu) +{ + if(menu->typeahead!=NULL){ + free(menu->typeahead); + menu->typeahead=NULL; + } +} + + +/*}}}*/ + + +/*{{{ Dynamic function table and class implementation */ + + +static DynFunTab menu_dynfuntab[]={ + {(DynFun*)region_fitrep, (DynFun*)menu_fitrep}, + {region_updategr, menu_updategr}, + {window_draw, menu_draw}, + {(DynFun*)window_press, (DynFun*)menu_press}, + {region_managed_remove, menu_managed_remove}, + {region_do_set_focus, menu_do_set_focus}, + {region_activated, menu_activated}, + {region_inactivated, menu_inactivated}, + {window_insstr, menu_insstr}, + {region_restack, menu_restack}, + {region_stacking, menu_stacking}, + END_DYNFUNTAB +}; + + +EXTL_EXPORT +IMPLCLASS(WMenu, WWindow, menu_deinit, menu_dynfuntab); + + +/*}}}*/ + + diff --git a/mod_menu/menu.h b/mod_menu/menu.h new file mode 100644 index 0000000..a79d36a --- /dev/null +++ b/mod_menu/menu.h @@ -0,0 +1,95 @@ +/* + * ion/mod_menu/menu.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_MOD_MENU_MENU_H +#define ION_MOD_MENU_MENU_H + +#include +#include +#include +#include + +INTRCLASS(WMenu); +INTRSTRUCT(WMenuEntry); + +#define WMENUENTRY_SUBMENU 0x0001 + +DECLSTRUCT(WMenuEntry){ + char *title; + int flags; +}; + +DECLCLASS(WMenu){ + WWindow win; + GrBrush *brush; + GrBrush *entry_brush; + + WFitParams last_fp; + + bool pmenu_mode; + bool big_mode; + int n_entries, selected_entry; + int first_entry, vis_entries; + int max_entry_w, entry_h, entry_spacing; + WMenuEntry *entries; + + WMenu *submenu; + + ExtlTab tab; + ExtlFn handler; + + char *typeahead; + + uint gm_kcb, gm_state; +}; + + +INTRSTRUCT(WMenuCreateParams); + +DECLSTRUCT(WMenuCreateParams){ + ExtlFn handler; + ExtlTab tab; + bool pmenu_mode; + bool submenu_mode; + bool big_mode; + int initial; + WRectangle refg; +}; + + +extern WMenu *create_menu(WWindow *par, const WFitParams *fp, + const WMenuCreateParams *params); +extern bool menu_init(WMenu *menu, WWindow *par, const WFitParams *fp, + const WMenuCreateParams *params); +extern void menu_deinit(WMenu *menu); + +extern bool menu_fitrep(WMenu *menu, WWindow *par, const WFitParams *fp); + +extern void menu_finish(WMenu *menu); +extern void menu_cancel(WMenu *menu); +extern bool menu_rqclose(WMenu *menu); +extern void menu_updategr(WMenu *menu); + +extern int menu_entry_at_root(WMenu *menu, int root_x, int root_y); +extern void menu_release(WMenu *menu, XButtonEvent *ev); +extern void menu_motion(WMenu *menu, XMotionEvent *ev, int dx, int dy); +extern void menu_button(WMenu *menu, XButtonEvent *ev); +extern int menu_press(WMenu *menu, XButtonEvent *ev, WRegion **reg_ret); + +extern void mod_menu_set_scroll_params(int delay, int amount); + +extern void menu_typeahead_clear(WMenu *menu); + +extern void menu_select_nth(WMenu *menu, int n); +extern void menu_select_prev(WMenu *menu); +extern void menu_select_next(WMenu *menu); + +#endif /* ION_MOD_MENU_MENU_H */ diff --git a/mod_menu/mkmenu.c b/mod_menu/mkmenu.c new file mode 100644 index 0000000..5f4fecf --- /dev/null +++ b/mod_menu/mkmenu.c @@ -0,0 +1,127 @@ +/* + * ion/mod_menu/mkmenu.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include "menu.h" +#include "mkmenu.h" + + +/*--lowlevel routine not to be called by the user--EXTL_DOC + * Display a menu inside multiplexer. The \var{handler} parameter + * is a function that gets the selected menu entry as argument and + * should call it with proper parameters. The table \var{tab} is a + * list of menu entries of the form \code{\{name = ???, [ submenu_fn = ??? ]\}}. + * The function \var{submenu_fn} return a similar submenu definition + * when called. + * + * Do not use this function directly. Use \fnref{mod_menu.menu} and + * \fnref{mod_menu.bigmenu}. + */ +EXTL_EXPORT +WMenu *mod_menu_do_menu(WMPlex *mplex, ExtlFn handler, ExtlTab tab, + ExtlTab param) +{ + WMenuCreateParams fnp; + WMPlexAttachParams par; + + fnp.handler=handler; + fnp.tab=tab; + fnp.pmenu_mode=FALSE; + fnp.submenu_mode=FALSE; + fnp.big_mode=extl_table_is_bool_set(param, "big"); + fnp.initial=0; + extl_table_gets_i(param, "initial", &(fnp.initial)); + fnp.refg.x=0; + fnp.refg.y=0; + fnp.refg.w=0; + fnp.refg.h=0; + + par.flags=(MPLEX_ATTACH_SWITCHTO| + MPLEX_ATTACH_MODAL| + MPLEX_ATTACH_UNNUMBERED| + MPLEX_ATTACH_SIZEPOLICY); + par.szplcy=SIZEPOLICY_FULL_BOUNDS; + + return (WMenu*)mplex_do_attach_new(mplex, &par, + (WRegionCreateFn*)create_menu, + (void*)&fnp); +} + + +/*--lowlevel routine not to be called by the user--EXTL_DOC + * Display a pop-up menu inside window \var{where}. This function + * can only be called from a mouse/pointing device button press handler + * and the menu will be placed below the point where the press occured. + * The \var{handler} and \var{tab} parameters are similar to those of + * \fnref{menu_menu}. + * + * Do not use this function directly. Use \fnref{mod_menu.pmenu}. + */ +EXTL_EXPORT +WMenu *mod_menu_do_pmenu(WWindow *where, ExtlFn handler, ExtlTab tab) +{ + WScreen *scr; + WMenuCreateParams fnp; + XEvent *ev=ioncore_current_pointer_event(); + WMenu *menu; + WFitParams fp; + + if(ev==NULL || ev->type!=ButtonPress) + return NULL; + + scr=region_screen_of((WRegion*)where); + + if(scr==NULL) + return NULL; + + fnp.handler=handler; + fnp.tab=tab; + fnp.pmenu_mode=TRUE; + fnp.big_mode=FALSE; + fnp.submenu_mode=FALSE; + fnp.initial=0; + fnp.refg.x=ev->xbutton.x_root-REGION_GEOM(scr).x; + fnp.refg.y=ev->xbutton.y_root-REGION_GEOM(scr).y; + fnp.refg.w=0; + fnp.refg.h=0; + + fp.mode=REGION_FIT_BOUNDS; + fp.g.x=REGION_GEOM(where).x; + fp.g.y=REGION_GEOM(where).y; + fp.g.w=REGION_GEOM(where).w; + fp.g.h=REGION_GEOM(where).h; + + menu=create_menu((WWindow*)scr, &fp, &fnp); + + if(menu==NULL) + return NULL; + + region_restack((WRegion*)menu, None, Above); + + if(!ioncore_set_drag_handlers((WRegion*)menu, + NULL, + (WMotionHandler*)menu_motion, + (WButtonHandler*)menu_release, + NULL, + (GrabKilledHandler*)menu_cancel)){ + destroy_obj((Obj*)menu); + return NULL; + } + + region_map((WRegion*)menu); + + return menu; +} + diff --git a/mod_menu/mkmenu.h b/mod_menu/mkmenu.h new file mode 100644 index 0000000..2efed2f --- /dev/null +++ b/mod_menu/mkmenu.h @@ -0,0 +1,23 @@ +/* + * ion/mod_menu/mkmenu.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_MOD_MENU_MKMENU_H +#define ION_MOD_MENU_MKMENU_H + +#include +#include +#include "menu.h" + +extern WMenu *mod_menu_do_menu(WMPlex *mplex, ExtlFn handler, ExtlTab tab, + ExtlTab param); +extern WMenu *mod_menu_do_pmenu(WWindow *where, ExtlFn handler, ExtlTab tab); + +#endif /* ION_MOD_MENU_MKMENU_H */ diff --git a/mod_menu/mod_menu.lua b/mod_menu/mod_menu.lua new file mode 100644 index 0000000..6375e9f --- /dev/null +++ b/mod_menu/mod_menu.lua @@ -0,0 +1,108 @@ +-- +-- ion/mod_menu/mod_menu.lua -- Menu opening helper routines. +-- +-- Copyright (c) Tuomo Valkonen 2004-2006. +-- +-- Ion is free software; you can redistribute it and/or modify it under +-- the terms of the GNU Lesser General Public License as published by +-- the Free Software Foundation; either version 2.1 of the License, or +-- (at your option) any later version. +-- + +-- This is a slight abuse of the package.loaded variable perhaps, but +-- library-like packages should handle checking if they're loaded instead of +-- confusing the user with require/include differences. +if package.loaded["mod_menu"] then return end + +if not ioncore.load_module("mod_menu") then + return +end + +local mod_menu=_G["mod_menu"] +local menudb=_G["ioncore"] + +assert(mod_menu and menudb) + + +-- Menu commands {{{ + +local function menu_(reg, sub, menu_or_name, fn, check) + if check then + -- Check that no other menus are open in reg. + local l=reg:managed_list() + for i, r in pairs(l) do + if obj_is(r, "WMenu") then + return + end + end + end + + menu=menudb.evalmenu(menu_or_name, {reg, sub}) + + return fn(reg, function(e) e.func(reg, sub) end, menu) +end + + +--DOC +-- Display a menu in the lower-left corner of \var{mplex}. +-- The variable \var{menu_or_name} is either the name of a menu +-- defined with \fnref{mod_menu.defmenu} or directly a table similar +-- to ones passesd to this function. When this function is +-- called from a binding handler, \var{sub} should be set to +-- the second argument of to the binding handler (\var{_sub}) +-- so that the menu handler will get the same parameters as the +-- binding handler. Extra options can be passed in the table +-- \var{param}. The initial entry can be specified as the field +-- \var{initial} as an integer starting from 1. Menus can be made +-- to use a bigger style by setting the field \var{big} to \code{true}. +function mod_menu.menu(mplex, sub, menu_or_name, param) + local function menu_stdmenu(m, s, menu) + return mod_menu.do_menu(m, s, menu, param) + end + return menu_(mplex, sub, menu_or_name, menu_stdmenu, true) +end + +-- Compatibility +function mod_menu.bigmenu(mplex, sub, menu_or_name, initial) + local function menu_bigmenu(m, s, menu) + return mod_menu.do_menu(m, s, menu, {big=true, initial=initial}) + end + return menu_(mplex, sub, menu_or_name, menu_bigmenu, true) +end + +--DOC +-- This function is similar to \fnref{mod_menu.menu}, but input +-- is grabbed and the key used to active the menu can be used to +-- cycle through menu entries. +function mod_menu.grabmenu(mplex, sub, menu_or_name, param) + local function menu_grabmenu(m, s, menu) + return mod_menu.do_grabmenu(m, s, menu, param) + end + return menu_(mplex, sub, menu_or_name, menu_grabmenu, true) +end + +-- Compatibility +function mod_menu.biggrabmenu(mplex, sub, menu_or_name, key, initial) + local function menu_biggrabmenu(m, s, menu) + return mod_menu.do_grabmenu(m, s, menu, true, key, initial or 0) + end + return menu_(mplex, sub, menu_or_name, menu_biggrabmenu, true, initial) +end + +--DOC +-- This function displays a drop-down menu and should only +-- be called from a mouse press handler. The parameters are +-- similar to those of \fnref{mod_menu.menu}. +function mod_menu.pmenu(win, sub, menu_or_name) + return menu_(win, sub, menu_or_name, mod_menu.do_pmenu) +end + +-- }}} + + +-- Mark ourselves loaded. +package.loaded["mod_menu"]=true + + +-- Load configuration file +dopath('cfg_menu', true) diff --git a/mod_mgmtmode/Makefile b/mod_mgmtmode/Makefile new file mode 100644 index 0000000..2d0b769 --- /dev/null +++ b/mod_mgmtmode/Makefile @@ -0,0 +1,26 @@ +## +## Management mode module Makefile +## + +# System-specific configuration is in system.mk +TOPDIR=.. +include $(TOPDIR)/build/system-inc.mk + +###################################### + +INCLUDES += $(X11_INCLUDES) $(LIBTU_INCLUDES) $(LIBEXTL_INCLUDES) -I.. +CFLAGS += $(XOPEN_SOURCE) $(C99_SOURCE) + +SOURCES=main.c mgmtmode.c + +MAKE_EXPORTS=mod_mgmtmode + +MODULE=mod_mgmtmode + +###################################### + +include $(TOPDIR)/build/rules.mk + +###################################### + +_install: module_install diff --git a/mod_mgmtmode/main.c b/mod_mgmtmode/main.c new file mode 100644 index 0000000..fc5ecc6 --- /dev/null +++ b/mod_mgmtmode/main.c @@ -0,0 +1,71 @@ +/* + * ion/mod_mgmtmode/main.c + * + * Copyright (c) Tuomo Valkonen 2004-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include + +#include "exports.h" + +/*{{{ Module information */ + + +#include "../version.h" + +char mod_mgmtmode_ion_api_version[]=ION_API_VERSION; + + +/*}}}*/ + + +/*{{{ Bindmaps */ + + +WBindmap *mod_mgmtmode_bindmap=NULL; + + +/*}}}*/ + + +/*{{{ Init & deinit */ + + +void mod_mgmtmode_deinit() +{ + if(mod_mgmtmode_bindmap!=NULL){ + ioncore_free_bindmap("WMgmtMode", mod_mgmtmode_bindmap); + mod_mgmtmode_bindmap=NULL; + } + + mod_mgmtmode_unregister_exports(); +} + + +bool mod_mgmtmode_init() +{ + mod_mgmtmode_bindmap=ioncore_alloc_bindmap("WMgmtMode", NULL); + + if(mod_mgmtmode_bindmap==NULL) + return FALSE; + + if(!mod_mgmtmode_register_exports()){ + mod_mgmtmode_deinit(); + return FALSE; + } + + extl_read_config("cfg_mgmtmode", NULL, TRUE); + + return TRUE; +} + + +/*}}}*/ + diff --git a/mod_mgmtmode/main.h b/mod_mgmtmode/main.h new file mode 100644 index 0000000..30f61f3 --- /dev/null +++ b/mod_mgmtmode/main.h @@ -0,0 +1,22 @@ +/* + * ion/mod_mgmtmode/main.h + * + * Copyright (c) Tuomo Valkonen 2004-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_MOD_MGMTMODE_MAIN_H +#define ION_MOD_MGMTMODE_MAIN_H + +#include + +extern bool mod_mgmtmode_init(); +extern void mod_mgmtmode_deinit(); + +extern WBindmap *mod_mgmtmode_bindmap; + +#endif /* ION_MOD_MGMTMODE_MAIN_H */ diff --git a/mod_mgmtmode/mgmtmode.c b/mod_mgmtmode/mgmtmode.c new file mode 100644 index 0000000..92a3d27 --- /dev/null +++ b/mod_mgmtmode/mgmtmode.c @@ -0,0 +1,222 @@ +/* + * ion/mod_mgmtmode/mgmtmode.c + * + * Copyright (c) Tuomo Valkonen 2004-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include "mgmtmode.h" +#include "main.h" + + +static WMgmtMode *mgmt_mode=NULL; + + +static void cancel_mgmt(WRegion *reg); + + +/*{{{ WMgmtMode */ + + +static bool mgmtmode_init(WMgmtMode *mode, WRegion *reg) +{ + watch_init(&(mode->selw)); + watch_setup(&(mode->selw), (Obj*)reg, NULL); + return TRUE; +} + + +static WMgmtMode *create_mgmtmode(WRegion *reg) +{ + CREATEOBJ_IMPL(WMgmtMode, mgmtmode, (p, reg)); +} + + +static void mgmtmode_deinit(WMgmtMode *mode) +{ + if(mgmt_mode==mode) + mgmt_mode=NULL; + + watch_reset(&(mode->selw)); +} + + +/*EXTL_DOC + * Select management mode target. + */ +EXTL_EXPORT_MEMBER +void mgmtmode_select(WMgmtMode *mode, WRegion *reg) +{ + watch_setup(&(mode->selw), (Obj*)reg, NULL); +} + + +/*EXTL_DOC + * Return management mode target. + */ +EXTL_SAFE +EXTL_EXPORT_MEMBER +WRegion *mgmtmode_selected(WMgmtMode *mode) +{ + return (WRegion*)(mode->selw.obj); +} + + +/*EXTL_DOC + * End management mode. + */ +EXTL_EXPORT_MEMBER +void mgmtmode_finish(WMgmtMode *mode) +{ + if(mgmt_mode==mode) + cancel_mgmt(NULL); +} + + +EXTL_EXPORT +IMPLCLASS(WMgmtMode, Obj, mgmtmode_deinit, NULL); + + +/*}}}*/ + + +/*{{{ Rubberband */ + + +static void draw_rubberbox(WRootWin *rw, const WRectangle *rect) +{ + XPoint fpts[5]; + + fpts[0].x=rect->x; + fpts[0].y=rect->y; + fpts[1].x=rect->x+rect->w; + fpts[1].y=rect->y; + fpts[2].x=rect->x+rect->w; + fpts[2].y=rect->y+rect->h; + fpts[3].x=rect->x; + fpts[3].y=rect->y+rect->h; + fpts[4].x=rect->x; + fpts[4].y=rect->y; + + XDrawLines(ioncore_g.dpy, WROOTWIN_ROOT(rw), rw->xor_gc, fpts, 5, + CoordModeOrigin); +} + + +static void mgmtmode_draw(WMgmtMode *mode) +{ + WRegion *reg=mgmtmode_selected(mode); + + if(reg!=NULL){ + WRootWin *rw=region_rootwin_of(reg); + WRectangle g=REGION_GEOM(reg); + int rx=0, ry=0; + + region_rootpos(reg, &rx, &ry); + + g.x=rx; + g.y=ry; + + draw_rubberbox(rw, &g); + } +} + + +static void mgmtmode_erase(WMgmtMode *mode) +{ + mgmtmode_draw(mode); +} + + +/*}}}*/ + + +/*{{{ The mode */ + + +static bool mgmt_handler(WRegion *reg, XEvent *xev) +{ + XKeyEvent *ev=&xev->xkey; + WBinding *binding=NULL; + WMgmtMode *mode; + + if(ev->type==KeyRelease) + return FALSE; + + if(reg==NULL) + return FALSE; + + mode=mgmt_mode; + + if(mode==NULL) + return FALSE; + + binding=bindmap_lookup_binding(mod_mgmtmode_bindmap, + BINDING_KEYPRESS, + ev->state, ev->keycode); + + if(!binding) + return FALSE; + + if(binding!=NULL){ + mgmtmode_erase(mode); + extl_call(binding->func, "o", NULL, mode); + if(mgmt_mode!=NULL) + mgmtmode_draw(mgmt_mode); + } + + return (mgmt_mode==NULL); +} + + +static void cancel_mgmt(WRegion *reg) +{ + if(mgmt_mode!=NULL){ + mgmtmode_erase(mgmt_mode); + destroy_obj((Obj*)mgmt_mode); + } + ioncore_grab_remove(mgmt_handler); +} + + +/*EXTL_DOC + * Begin management mode. + */ +EXTL_EXPORT +WMgmtMode *mod_mgmtmode_begin(WRegion *reg) +{ + if(mgmt_mode!=NULL) + return NULL; + + mgmt_mode=create_mgmtmode(reg); + + if(mgmt_mode==NULL) + return NULL; + + ioncore_grab_establish((WRegion*)region_rootwin_of(reg), + mgmt_handler, + (GrabKilledHandler*)cancel_mgmt, 0); + + mgmtmode_draw(mgmt_mode); + + return mgmt_mode; +} + + +/*}}}*/ diff --git a/mod_mgmtmode/mgmtmode.h b/mod_mgmtmode/mgmtmode.h new file mode 100644 index 0000000..1e5ee64 --- /dev/null +++ b/mod_mgmtmode/mgmtmode.h @@ -0,0 +1,33 @@ +/* + * ion/mod_mgmtmode/mgmtmode.h + * + * Copyright (c) Tuomo Valkonen 2004-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_MOD_MGMTMODE_MGMTMODE_H +#define ION_MOD_MGMTMODE_MGMTMODE_H + +#include +#include + +INTRCLASS(WMgmtMode); + +DECLCLASS(WMgmtMode){ + Obj obj; + Watch selw; +}; + + +extern WMgmtMode *mod_mgmtmode_begin(WRegion *reg); + +extern void mgmtmode_select(WMgmtMode *mode, WRegion *reg); +extern WRegion *mgmtmode_selected(WMgmtMode *mode); + +extern void mgmtmode_finish(WMgmtMode *mode); + +#endif /* ION_MOD_MGMTMODE_MGMTMODE_H */ diff --git a/mod_panews/Makefile b/mod_panews/Makefile new file mode 100644 index 0000000..c1b79fb --- /dev/null +++ b/mod_panews/Makefile @@ -0,0 +1,27 @@ +## +## mod_panews workspace module Makefile +## + +# System-specific configuration is in system.mk +TOPDIR=.. +include $(TOPDIR)/build/system-inc.mk + +###################################### + +INCLUDES += $(X11_INCLUDES) $(LIBTU_INCLUDES) $(LIBEXTL_INCLUDES) -I.. +CFLAGS += $(XOPEN_SOURCE) $(C99_SOURCE) + +SOURCES=main.c panews.c placement.c splitext.c unusedwin.c + +MAKE_EXPORTS=mod_panews + +MODULE=mod_panews +MODULE_STUB=mod_panews.lua + +###################################### + +include $(TOPDIR)/build/rules.mk + +###################################### + +_install: module_install diff --git a/mod_panews/main.c b/mod_panews/main.c new file mode 100644 index 0000000..b3f4f10 --- /dev/null +++ b/mod_panews/main.c @@ -0,0 +1,141 @@ +/* + * ion/panews/main.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include "main.h" +#include "panews.h" +#include "placement.h" +#include "exports.h" + + +/*{{{ Module information */ + + +#include "../version.h" + +char mod_panews_ion_api_version[]=ION_API_VERSION; + + +/*}}}*/ + + +/*{{{ Bindmaps */ + + +WBindmap *mod_panews_panews_bindmap=NULL; +WBindmap *mod_panews_frame_bindmap=NULL; +WBindmap *mod_panews_unusedwin_bindmap=NULL; + + +/*}}}*/ + + +/*{{{ Module init & deinit */ + + +void mod_panews_deinit() +{ + mod_panews_unregister_exports(); + ioncore_unregister_regclass(&CLASSDESCR(WPaneWS)); + + if(mod_panews_panews_bindmap!=NULL){ + ioncore_free_bindmap("WPaneWS", mod_panews_panews_bindmap); + mod_panews_panews_bindmap=NULL; + } + + if(mod_panews_unusedwin_bindmap!=NULL){ + ioncore_free_bindmap("WUnusedWin", mod_panews_unusedwin_bindmap); + mod_panews_unusedwin_bindmap=NULL; + } + + if(mod_panews_frame_bindmap!=NULL){ + ioncore_free_bindmap("WFrame-on-WPaneWS", mod_panews_frame_bindmap); + mod_panews_frame_bindmap=NULL; + } + + if(panews_init_layout_alt!=NULL){ + destroy_obj((Obj*)panews_init_layout_alt); + panews_init_layout_alt=NULL; + } + + if(panews_make_placement_alt!=NULL){ + destroy_obj((Obj*)panews_make_placement_alt); + panews_make_placement_alt=NULL; + } +} + + +static bool register_regions() +{ + if(!ioncore_register_regclass(&CLASSDESCR(WPaneWS), + (WRegionLoadCreateFn*)panews_load)){ + return FALSE; + } + + return TRUE; +} + + +#define INIT_HOOK_(NM) \ + NM=mainloop_register_hook(#NM, create_hook()); \ + if(NM==NULL) return FALSE; + + +static bool init_hooks() +{ + INIT_HOOK_(panews_init_layout_alt); + INIT_HOOK_(panews_make_placement_alt); + return TRUE; +} + + + +bool mod_panews_init() +{ + if(!init_hooks()) + goto err; + + mod_panews_panews_bindmap=ioncore_alloc_bindmap("WPaneWS", NULL); + mod_panews_unusedwin_bindmap=ioncore_alloc_bindmap_frame("WUnusedWin"); + mod_panews_frame_bindmap=ioncore_alloc_bindmap_frame("WFrame-on-WPaneWS"); + + if(mod_panews_panews_bindmap==NULL || + mod_panews_unusedwin_bindmap==NULL || + mod_panews_frame_bindmap==NULL){ + goto err; + } + + if(!mod_panews_register_exports()) + goto err; + + if(!register_regions()) + goto err; + + /*ioncore_read_config("cfg_panews", NULL, FALSE);*/ + + return TRUE; + +err: + mod_panews_deinit(); + return FALSE; +} + + +/*}}}*/ + diff --git a/mod_panews/main.h b/mod_panews/main.h new file mode 100644 index 0000000..5dc226c --- /dev/null +++ b/mod_panews/main.h @@ -0,0 +1,25 @@ +/* + * ion/panews/main.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_PANEWS_MAIN_H +#define ION_PANEWS_MAIN_H + +#include +#include + +extern bool mod_panews_init(); +extern void mod_panews_deinit(); + +extern WBindmap *mod_panews_panews_bindmap; +extern WBindmap *mod_panews_unusedwin_bindmap; +extern WBindmap *mod_panews_frame_bindmap; + +#endif /* ION_PANEWS_MAIN_H */ diff --git a/mod_panews/mod_panews.lua b/mod_panews/mod_panews.lua new file mode 100644 index 0000000..65bf32e --- /dev/null +++ b/mod_panews/mod_panews.lua @@ -0,0 +1,562 @@ +-- +-- ion/mod_panews/mod_panews.lua +-- +-- Copyright (c) Tuomo Valkonen 2004-2006. +-- +-- Ion is free software; you can redistribute it and/or modify it under +-- the terms of the GNU Lesser General Public License as published by +-- the Free Software Foundation; either version 2.1 of the License, or +-- (at your option) any later version. +-- + + +-- This is a slight abuse of the package.loaded variable perhaps, but +-- library-like packages should handle checking if they're loaded instead of +-- confusing the user with require/include differences. +if package.loaded["templates"] then return end + +if not ioncore.load_module("mod_panews") then + return +end + +assert(not _G["mod_panews"]); +local mod_panews={} +_G["mod_panews"]=mod_panews + +local private={} +local settings={} + +-- Settings {{{ + +-- Classes: +-- (T)erminal +-- (V)iewer +-- (M)isc +settings.valid_classifications={["V"]=true, ["T"]=true, ["M"]=true,} + +-- Xterm, rxvt, aterm, etc. all have "XTerm" as class part of WM_CLASS +settings.terminal_emulators={["XTerm"]=true,} + +-- Pixel scale factor from 1280x1024/75dpi +settings.scalef=1.0 + +--settings.b_ratio=(1+math.sqrt(5))/2 +--settings.s_ratio=1 +settings.b_ratio=3 +settings.s_ratio=2 + +settings.b_ratio2=7 +settings.s_ratio2=1 + +settings.templates={} + +settings.templates["default"]={ + type="WSplitFloat", + dir="horizontal", + tls=settings.b_ratio, + brs=settings.s_ratio, + tls_brs_incl_handles=true, + tl={ + type="WSplitPane", + contents={ + type="WSplitFloat", + dir="vertical", + tls=settings.b_ratio2, + brs=settings.s_ratio2, + tls_brs_incl_handles=true, + tl={ + type="WSplitPane", + marker="V:single", + }, + br={ + type="WSplitPane", + marker="M:right", + }, + }, + }, + br={ + type="WSplitPane", + marker="T:up", + }, +} + + +settings.templates["alternative1"]={ + type="WSplitFloat", + dir="horizontal", + tls=settings.s_ratio2, + brs=settings.b_ratio2, + tls_brs_incl_handles=true, + tl={ + type="WSplitPane", + marker="M:down", + }, + br={ + type="WSplitFloat", + dir="vertical", + tls=settings.b_ratio, + brs=settings.s_ratio, + tls_brs_incl_handles=true, + tl={ + type="WSplitPane", + marker="V:single", + }, + br={ + type="WSplitPane", + marker="T:right", + }, + }, +} + + +settings.templates["alternative2"]={ + type="WSplitFloat", + dir="vertical", + tls=settings.b_ratio, + brs=settings.s_ratio, + tls_brs_incl_handles=true, + tl={ + type="WSplitFloat", + dir="horizontal", + tls=settings.s_ratio2, + brs=settings.b_ratio2, + tls_brs_incl_handles=true, + tl={ + type="WSplitPane", + marker="M:down", + }, + br={ + type="WSplitPane", + marker="V:single", + }, + }, + br={ + type="WSplitPane", + marker="T:right", + }, +} + + +settings.template=settings.templates["default"] + +settings.shrink_minimum=32 + +--DOC +-- Set some module parameters. Currently \var{s} may contain the following +-- fields: +-- \begin{tabularx}{\linewidth}{lX} +-- \tabhead{Field & Description} +-- \var{template} & layout template for newly created \type{WPaneWS} +-- workspaces. This can be either a table or one of the +-- predefined layouts 'default', 'alternative1', and +-- 'alternative2'. \\ +-- \var{scalef} & Scale factor for classification heuristics to work +-- with different screen resolutions. The default is 1.0 +-- and is designed for 1280x1024 at 75dpi. \\ +-- \var{valid_classifications} & A table with valid window classifications +-- as valid keys. \\ +-- \end{tabularx} +function mod_panews.set(s) + if s.template then + local ok=false + if type(s.template)=="string" then + if settings.templates[s.template] then + settings.template=settings.templates[s.template] + ok=true + end + elseif type(s.template)=="table" then + settings.template=s.template + ok=true + end + if not ok then + ioncore.warn_traced("Invalid template.") + end + end + if s.scalef then + if type(s.scalef)~="number" or s.scalef<=0 then + ioncore.warn_traced('Invalid scale factor') + else + settings.scalef=s.scalef + end + end + + if type(s.valid_classifications)=="table" then + settings.valid_classifications=s.valid_classifications + end +end + + +--DOC +-- Get some module settings. See \fnref{mod_panews.set} for documentation +-- on the contents of the returned table. +function mod_panews.get() + return table.copy(settings, true) +end + + +-- }}} + + +-- Helper code {{{ + +local function sfind(s, p) + local function drop2(a, b, ...) + return unpack(arg) + end + return drop2(string.find(s, p)) +end + + +function private.div_length(w, r1, r2) + local a=math.ceil(w*r1/(r1+r2)) + return a, w-a +end + +function private.split3(d, ls, cs, rs, lo, co, ro) + return { + tls = ls+cs, + brs = rs, + dir = d, + tl = { + tls = ls, + brs = cs, + dir = d, + tl = (lo or {}), + br = (co or {}), + }, + br = (ro or {}), + } +end + +function private.center3(d, ts, cs, lo, co, ro) + local sc=math.min(ts, cs) + local sl=math.floor((ts-sc)/2) + local sr=ts-sc-sl + local r=private.split3(d, sl, sc, sr, lo, co, ro) + return r +end + +function private.split2(d, ts, ls, rs, lo, ro) + if ls and rs then + assert(ls+rs==ts) + elseif not ls then + ls=ts-rs + else + rs=ts-ls + end + assert(rs>=0 and ls>=0) + return { + type="WSplitSplit", + dir=d, + tls=math.floor(ls), + brs=math.ceil(rs), + tl=lo, + br=ro, + } +end + +-- }}} + + +-- Classification {{{ + +function private.classify(ws, reg) + if obj_is(reg, "WClientWin") then + -- Check if there's a winprop override + local wp=ioncore.getwinprop(reg) + if wp and wp.panews_classification then + if settings.valid_classifications[wp.panews_classification] then + return wp.panews_classification + end + end + + -- Handle known terminal emulators. + local id=reg:get_ident() + if settings.terminal_emulators[id.class] then + return "T" + end + end + + -- Try size heuristics. + local cg=reg:geom() + + if cg.w<3/8*(1280*settings.scalef) then + return "M" + end + + if cg.h>4/8*(960*settings.scalef) then + return "V" + else + return "T" + end +end + +-- }}} + + +-- Placement code {{{ + +local max_penalty=5 +local just_some_pixel_count=16 + +function private.fitlevel_(min1, s1, us1, min2, s2, us2) + local p1, p2=4, 0.5 + + if us2>=min2 then + p2=0 + end + + if us1>=math.max(s1/2, min1) then + p1=0 + elseif us1>=min1+just_some_pixel_count then + p1=1 + elseif us1>=min1 then + p1=2 + elseif us1>0 then + p1=3 + end + + return p1+p2 +end + +function private.fitlevel(frame, node, horiz) + local fg=frame:geom() + local fsh=frame:size_hints() + local sg=node:geom() + if not horiz then + return private.fitlevel_(fsh.min_h, fg.h, sg.h, fsh.min_w, fg.w, sg.w) + else + return private.fitlevel_(fsh.min_w, fg.w, sg.w, fsh.min_h, fg.h, sg.h) + end +end + + +function private.use_unused(p, n, d, forcelevel) + local f=private.fitlevel(p.frame, n, (d=="left" or d=="right")) + if f>forcelevel then + return false, f + end + + if d=="single" then + p.res_node=n + p.res_config={reg=p.frame} + return true, f + end + + -- TODO: Check fit + local sg=n:geom() + local fg=p.frame:geom() + + if d=="up" or d=="down" then + p.res_node=n + local fh=math.min(fg.h, sg.h) + if d=="up" then + p.res_config=private.split2("vertical", sg.h, nil, fh, + {}, {reg=p.frame}) + else + p.res_config=private.split2("vertical", sg.h, fh, nil, + {reg=p.frame}, {}) + end + return true, f + elseif d=="left" or d=="right" then + p.res_node=n + local fw=math.min(fg.w, sg.w) + if d=="left" then + p.res_config=private.split2("horizontal", sg.w, nil, fw, + {}, {reg=p.frame}) + else + p.res_config=private.split2("horizontal", sg.w, fw, nil, + {reg=p.frame}, {}) + end + return true, f + end + + return false, f +end + + +function private.scan_pane(p, node, d) + local function do_scan_active(n, uf) + local t=obj_typename(n) + if t=="WSplitRegion" then + local f=private.fitlevel(p.frame, n, (d=="left" or d=="right")) + if f + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "panews.h" +#include "placement.h" +#include "main.h" +#include "splitext.h" + + +/*{{{ Create/destroy */ + + +bool panews_managed_add(WPaneWS *ws, WRegion *reg) +{ + if(OBJ_IS(reg, WFrame)) + region_add_bindmap(reg, mod_panews_frame_bindmap); + + return tiling_managed_add_default(&(ws->tiling), reg); +} + + +static WRegion *create_frame_panews(WWindow *parent, const WFitParams *fp) +{ + return (WRegion*)create_frame(parent, fp, "frame-tiled-panews"); +} + + +static bool mrsh_init_layout_extl(ExtlFn fn, WPaneWSInitParams *p) +{ + ExtlTab t=extl_create_table(); + bool ret=FALSE; + + extl_table_sets_o(t, "ws", (Obj*)p->ws); + + extl_protect(NULL); + ret=extl_call(fn, "t", "b", t, &ret); + extl_unprotect(NULL); + + if(ret) + ret=extl_table_gets_t(t, "layout", &(p->layout)); + + extl_unref_table(t); + return ret; +} + + +static bool panews_init_layout(WPaneWS *ws) +{ + WPaneWSInitParams p; + + p.ws=ws; + p.layout=extl_table_none(); + + hook_call_p(panews_init_layout_alt, &p, + (WHookMarshallExtl*)mrsh_init_layout_extl); + + if(p.layout!=extl_table_none()){ + ws->tiling.split_tree=tiling_load_node(&(ws->tiling), + ®ION_GEOM(ws), + p.layout); + extl_unref_table(p.layout); + } + + if(ws->tiling.split_tree==NULL) + ws->tiling.split_tree=(WSplit*)create_splitunused(®ION_GEOM(ws), ws); + + if(ws->tiling.split_tree!=NULL) + ws->tiling.split_tree->ws_if_root=&(ws->tiling); + + return (ws->tiling.split_tree!=NULL); +} + + +bool panews_init(WPaneWS *ws, WWindow *parent, const WFitParams *fp, + bool ilo) +{ + if(!tiling_init(&(ws->tiling), parent, fp, + create_frame_panews, FALSE)) + return FALSE; + + region_add_bindmap((WRegion*)ws, mod_panews_panews_bindmap); + + assert(ws->tiling.split_tree==NULL); + + if(ilo){ + if(!panews_init_layout(ws)){ + panews_deinit(ws); + return FALSE; + } + } + + return TRUE; +} + + +WPaneWS *create_panews(WWindow *parent, const WFitParams *fp, bool cu) +{ + CREATEOBJ_IMPL(WPaneWS, panews, (p, parent, fp, cu)); +} + + +WPaneWS *create_panews_simple(WWindow *parent, const WFitParams *fp) +{ + return create_panews(parent, fp, TRUE); +} + + +void panews_deinit(WPaneWS *ws) +{ + tiling_deinit(&(ws->tiling)); +} + + +static WSplitRegion *get_node_check(WPaneWS *ws, WRegion *reg) +{ + WSplitRegion *node; + + if(reg==NULL) + return NULL; + + node=splittree_node_of(reg); + + if(node==NULL || REGION_MANAGER(reg)!=(WRegion*)ws) + return NULL; + + return node; +} + + +static void panews_do_managed_remove(WPaneWS *ws, WRegion *reg) +{ + tiling_do_managed_remove(&(ws->tiling), reg); + if(OBJ_IS(reg, WFrame)) + region_remove_bindmap(reg, mod_panews_frame_bindmap); +} + + +static bool plainregionfilter(WSplit *node) +{ + return (strcmp(OBJ_TYPESTR(node), "WSplitRegion")==0); +} + + + +void panews_managed_remove(WPaneWS *ws, WRegion *reg) +{ + bool ds=OBJ_IS_BEING_DESTROYED(ws); + bool act=REGION_IS_ACTIVE(reg); + bool mcf=region_may_control_focus((WRegion*)ws); + WSplitRegion *node=get_node_check(ws, reg); + WRegion *other=NULL; + + other=tiling_do_get_nextto(&(ws->tiling), reg, SPLIT_ANY, PRIMN_ANY, FALSE); + + panews_do_managed_remove(ws, reg); + + if(node==(WSplitRegion*)(ws->tiling.stdispnode)) + ws->tiling.stdispnode=NULL; + + if(node==NULL) + return; + + splittree_remove((WSplit*)node, !ds); + + if(!ds){ + if(other==NULL){ + if(ws->tiling.split_tree==NULL){ + warn(TR("Unable to re-initialise workspace. Destroying.")); + mainloop_defer_destroy((Obj*)ws); + }else if(act && mcf){ + /* We don't want to give the stdisp focus, even if one exists. + * Or do we? + */ + tiling_fallback_focus(&ws->tiling, FALSE); + } + }else if(act && mcf){ + region_warp(other); + } + } +} + + +/*}}}*/ + + +/*{{{ Misc. */ + + +bool panews_managed_prepare_focus(WPaneWS *ws, WRegion *reg, + int flags, WPrepareFocusResult *res) +{ + if(flags®ION_GOTO_ENTERWINDOW){ + WSplitRegion *other, *node=get_node_check(ws, reg); + if(node!=NULL && OBJ_IS(node, WSplitUnused)){ + /* An unused region - do not focus unless there are no + * normal regions in its pane. + */ + other=split_tree_find_region_in_pane_of((WSplit*)node); + if(other!=NULL){ + tiling_managed_prepare_focus(&(ws->tiling), other->reg, + flags&~REGION_GOTO_ENTERWINDOW, + res); + return FALSE; + } + } + } + + return tiling_managed_prepare_focus(&(ws->tiling), reg, flags, res); +} + + +static bool filter_no_stdisp_unused(WSplit *split) +{ + return (OBJ_IS(split, WSplitRegion) + && !OBJ_IS(split, WSplitST) + && !OBJ_IS(split, WSplitUnused)); +} + + +bool panews_managed_may_destroy(WPaneWS *ws, WRegion *reg) +{ + if(region_manager_allows_destroying((WRegion*)ws)) + return TRUE; + + if(tiling_do_get_nextto(&(ws->tiling), reg, + SPLIT_ANY, PRIMN_ANY, FALSE)==NULL){ + return FALSE; + } + + return TRUE; +} + + +bool panews_may_destroy(WPaneWS *ws) +{ + if(split_current_todir(ws->tiling.split_tree, SPLIT_ANY, PRIMN_ANY, + filter_no_stdisp_unused)!=NULL){ + warn(TR("Refusing to close non-empty workspace.")); + return FALSE; + } + + return TRUE; +} + + +/* +static WRegion *panews_rqclose_propagate(WPaneWS *ws, WRegion *sub) +{ + WSplitRegion *node=NULL; + WRegion *reg=NULL; + + if(sub==NULL){ + if(ws->tiling.split_tree!=NULL){ + node=(WSplitRegion*)split_current_todir(ws->tiling.split_tree, + SPLIT_ANY, PRIMN_ANY, + filter_no_stdisp_unused); + } + if(node==NULL){ + mainloop_defer_destroy((Obj*)ws); + return (WRegion*)ws; + } + if(node->reg==NULL) + return NULL; + sub=node->reg; + }else{ + node=get_node_check(ws, sub); + if(node==NULL) + return NULL; + } + + + return (region_rqclose(sub) ? sub : NULL); +} +*/ + + +/*}}}*/ + + +/*{{{ Save */ + + +ExtlTab panews_get_configuration(WPaneWS *ws) +{ + return tiling_get_configuration(&(ws->tiling)); +} + + +/*}}}*/ + + +/*{{{ Load */ + + +static WSplit *load_splitunused(WPaneWS *ws, const WRectangle *geom, + ExtlTab tab) +{ + return (WSplit*)create_splitunused(geom, (WPaneWS*)ws); +} + + +static WSplit *load_splitpane(WPaneWS *ws, const WRectangle *geom, ExtlTab tab) +{ + ExtlTab t; + WSplitPane *pane; + WSplit *cnt; + + pane=create_splitpane(geom, NULL); + if(pane==NULL) + return NULL; + + if(extl_table_gets_t(tab, "contents", &t)){ + cnt=tiling_load_node(&(ws->tiling), geom, t); + extl_unref_table(t); + }else{ + cnt=load_splitunused(ws, geom, extl_table_none()); + } + + if(cnt==NULL){ + destroy_obj((Obj*)pane); + return NULL; + } + + pane->contents=cnt; + cnt->parent=&(pane->isplit); + + assert(pane->marker==NULL); + extl_table_gets_s(tab, "marker", &(pane->marker)); + + return (WSplit*)pane; +} + + +static WSplit *panews_load_node(WPaneWS *ws, const WRectangle *geom, + ExtlTab tab) +{ + char *s=NULL; + + if(!extl_table_gets_s(tab, "type", &s)){ + WRegion *reg=NULL; + /* Shortcuts for templates.lua */ + if(extl_table_gets_o(tab, "reg", (Obj**)®)){ + if(OBJ_IS(reg, WRegion)) + return load_splitregion_doit(&(ws->tiling), geom, tab); + }else{ + return load_splitunused(ws, geom, tab); + } + }else{ + if(strcmp(s, "WSplitPane")==0) + return load_splitpane(ws, geom, tab); + else if(strcmp(s, "WSplitUnused")==0) + return load_splitunused(ws, geom, tab); + } + + return tiling_load_node_default(&(ws->tiling), geom, tab); +} + + +WRegion *panews_load(WWindow *par, const WFitParams *fp, ExtlTab tab) +{ + WPaneWS *ws; + ExtlTab treetab; + + ws=create_panews(par, fp, FALSE); + + if(ws==NULL) + return NULL; + + if(extl_table_gets_t(tab, "split_tree", &treetab)){ + ws->tiling.split_tree=tiling_load_node(&(ws->tiling), ®ION_GEOM(ws), + treetab); + extl_unref_table(treetab); + } + + if(ws->tiling.split_tree==NULL){ + if(!panews_init_layout(ws)){ + destroy_obj((Obj*)ws); + return NULL; + } + } + + ws->tiling.split_tree->ws_if_root=ws; + split_restack(ws->tiling.split_tree, ws->tiling.dummywin, Above); + + return (WRegion*)ws; +} + + +/*}}}*/ + + +/*{{{ Dynamic function table and class implementation */ + + +static DynFunTab panews_dynfuntab[]={ + {region_managed_remove, + panews_managed_remove}, + + {(DynFun*)region_prepare_manage, + (DynFun*)panews_prepare_manage}, + + {(DynFun*)region_get_configuration, + (DynFun*)panews_get_configuration}, + + {(DynFun*)region_may_destroy, + (DynFun*)panews_may_destroy}, + + {(DynFun*)region_managed_may_destroy, + (DynFun*)panews_managed_may_destroy}, + + {(DynFun*)tiling_managed_add, + (DynFun*)panews_managed_add}, + + {(DynFun*)tiling_load_node, + (DynFun*)panews_load_node}, + + {(DynFun*)tiling_do_get_nextto, + (DynFun*)panews_do_get_nextto}, + + {(DynFun*)tiling_do_get_farthest, + (DynFun*)panews_do_get_farthest}, + + {(DynFun*)region_managed_prepare_focus, + (DynFun*)panews_managed_prepare_focus}, + + /* + {(DynFun*)region_rqclose_propagate, + (DynFun*)panews_rqclose_propagate},*/ + + END_DYNFUNTAB +}; + + +EXTL_EXPORT +IMPLCLASS(WPaneWS, WTiling, panews_deinit, panews_dynfuntab); + + +/*}}}*/ + diff --git a/mod_panews/panews.h b/mod_panews/panews.h new file mode 100644 index 0000000..1402523 --- /dev/null +++ b/mod_panews/panews.h @@ -0,0 +1,47 @@ +/* + * ion/panews/panews.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_PANEWS_PANEWS_H +#define ION_PANEWS_PANEWS_H + +#include +#include +#include +#include +#include + +#include + + +INTRCLASS(WPaneWS); +DECLCLASS(WPaneWS){ + WTiling tiling; +}; + + +extern void panews_deinit(WPaneWS *ws); +extern bool panews_init(WPaneWS *ws, WWindow *parent, const WFitParams *fp, + bool cu); +extern WPaneWS *create_panews(WWindow *parent, const WFitParams *fp, bool cu); +extern WPaneWS *create_panews_simple(WWindow *parent, const WFitParams *fp); +extern WRegion *panews_load(WWindow *par, const WFitParams *fp, ExtlTab tab); + +/* Dynfun implementations */ + +extern bool panews_managed_may_destroy(WPaneWS *ws, WRegion *reg); +extern void panews_managed_remove(WPaneWS *ws, WRegion *reg); +extern bool panews_managed_add(WPaneWS *ws, WRegion *reg); +extern WRegion *panews_managed_control_focus(WPaneWS *ws, WRegion *reg); + +extern ExtlTab panews_get_configuration(WPaneWS *ws); +extern WRegion *panews_load(WWindow *par, const WFitParams *fp, ExtlTab tab); + +#endif /* ION_PANEWS_PANEWS_H */ diff --git a/mod_panews/placement.c b/mod_panews/placement.c new file mode 100644 index 0000000..73e2690 --- /dev/null +++ b/mod_panews/placement.c @@ -0,0 +1,320 @@ +/* + * ion/mod_panews/placement.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "placement.h" +#include "panews.h" +#include "splitext.h" +#include "unusedwin.h" + + +WHook *panews_init_layout_alt=NULL; +WHook *panews_make_placement_alt=NULL; + + +/*{{{ create_frame_for */ + + +static WFrame *create_frame_for(WPaneWS *ws, WRegion *reg) +{ + WWindow *par=REGION_PARENT(ws); + WFitParams fp; + WRectangle mg; + WFrame *frame; + + if(par==NULL) + return NULL; + + fp.g=REGION_GEOM(ws); + fp.mode=REGION_FIT_BOUNDS; + + frame=(WFrame*)ws->tiling.create_frame_fn(par, &fp); + + if(frame==NULL) + return NULL; + + frame->flags|=FRAME_DEST_EMPTY; + + mplex_managed_geom((WMPlex*)frame, &mg); + + fp.g.w=REGION_GEOM(reg).w+(REGION_GEOM(frame).w-mg.w); + fp.g.h=REGION_GEOM(reg).h+(REGION_GEOM(frame).h-mg.h); + fp.mode=REGION_FIT_EXACT; + + region_fitrep((WRegion*)frame, NULL, &fp); + + return (WFrame*)frame; +} + + +/*}}}*/ + + +/*{{{ Placement scan */ + + +static bool mrsh_layout_extl(ExtlFn fn, WPaneWSPlacementParams *p) +{ + ExtlTab t=extl_create_table(); + bool ret=FALSE; + + extl_table_sets_o(t, "ws", (Obj*)p->ws); + extl_table_sets_o(t, "frame", (Obj*)p->frame); + extl_table_sets_o(t, "reg", (Obj*)p->reg); + extl_table_sets_o(t, "specifier", (Obj*)p->specifier); + + extl_protect(NULL); + extl_call(fn, "t", "b", t, &ret); + extl_unprotect(NULL); + + if(ret){ + ret=FALSE; + + extl_table_gets_i(t, "res_w", &(p->res_w)); + extl_table_gets_i(t, "res_h", &(p->res_h)); + + if(extl_table_gets_o(t, "res_node", (Obj**)&(p->res_node))){ + if(OBJ_IS(p->res_node, WSplitUnused)){ + if(!extl_table_gets_t(t, "res_config", &(p->res_config))){ + warn(TR("Malfunctioning placement hook; condition #%d."), 1); + goto err; + } + }else if(!OBJ_IS(p->res_node, WSplitRegion)){ + warn(TR("Malfunctioning placement hook; condition #%d."), 2); + goto err; + } + } + } + + extl_unref_table(t); + + return ret; + +err: + p->res_node=NULL; + extl_unref_table(t); + return FALSE; +} + + +static bool plainregionfilter(WSplit *node) +{ + return (strcmp(OBJ_TYPESTR(node), "WSplitRegion")==0); +} + + +static bool fallback_filter(WSplit *node) +{ + return (OBJ_IS(node, WSplitUnused) || plainregionfilter(node)); +} + + +static bool fallback_layout(WPaneWSPlacementParams *p) +{ + if(p->ws->tiling.split_tree==NULL) + return FALSE; + + if(p->specifier!=NULL){ + p->res_node=(WSplit*)p->specifier; + }else{ + p->res_node=split_current_todir(p->ws->tiling.split_tree, SPLIT_ANY, + PRIMN_ANY, fallback_filter); + } + + if(p->res_node!=NULL && OBJ_IS(p->res_node, WSplitUnused)){ + p->res_config=extl_create_table(); + if(p->res_config==extl_table_none() || p->frame==NULL) + return FALSE; + extl_table_sets_o(p->res_config, "reg", (Obj*)(p->frame)); + } + + return (p->res_node!=NULL); +} + + +/*}}}*/ + + +/*{{{ Split/replace unused code */ + + +static bool do_replace(WPaneWS *ws, WFrame *frame, WRegion *reg, + WPaneWSPlacementParams *rs) +{ + WSplit *u=rs->res_node; + WSplit *node=tiling_load_node(&(ws->tiling), &(u->geom), rs->res_config); + + assert(OBJ_IS(u, WSplitUnused)); + + if(node==NULL){ + warn(TR("Malfunctioning placement hook; condition #%d."), 3); + return FALSE; + } + + if(REGION_MANAGER(frame)!=(WRegion*)ws){ + warn(TR("Malfunctioning placement hook; condition #%d."), 4); + destroy_obj((Obj*)node); + return FALSE; + } + + if(u->parent!=NULL) + splitinner_replace(u->parent, u, node); + else + splittree_changeroot((WSplit*)u, node); + + u->parent=NULL; + mainloop_defer_destroy((Obj*)u); + + if(ws->tiling.stdispnode!=NULL) + split_regularise_stdisp(ws->tiling.stdispnode); + + if(ws->tiling.split_tree!=NULL) + split_restack(ws->tiling.split_tree, ws->tiling.dummywin, Above); + + return TRUE; +} + +/*}}}*/ + + +/*{{{ The main dynfun */ + + +static bool current_unused(WPaneWS *ws) +{ + return OBJ_IS(tiling_current(&ws->tiling), WUnusedWin); +} + + +static WRegion *panews_get_target(WPaneWS *ws, WSplitUnused *specifier, + WRegion *reg) +{ + WRegion *target=NULL; + WFrame *frame=create_frame_for(ws, reg); + WSplit **tree=&(ws->tiling.split_tree); + WPaneWSPlacementParams rs; + + assert(ws->tiling.split_tree!=NULL); + + rs.ws=ws; + rs.frame=frame; + rs.reg=reg; + rs.specifier=specifier; + rs.res_node=NULL; + rs.res_config=extl_table_none(); + rs.res_w=-1; + rs.res_h=-1; + + if(frame!=NULL){ + split_update_bounds(*tree, TRUE); + + assert(panews_make_placement_alt!=NULL); + + hook_call_p(panews_make_placement_alt, &rs, + (WHookMarshallExtl*)mrsh_layout_extl); + } + + if(rs.res_node==NULL && specifier==NULL) + fallback_layout(&rs); + + if(rs.res_node!=NULL){ + /* Resize */ + if(rs.res_w>0 || rs.res_h>0){ + WRectangle grq=rs.res_node->geom; + int gflags=REGION_RQGEOM_WEAK_ALL; + + if(rs.res_w>0){ + grq.w=rs.res_w; + gflags&=~REGION_RQGEOM_WEAK_W; + } + + if(rs.res_h>0){ + grq.h=rs.res_h; + gflags&=~REGION_RQGEOM_WEAK_H; + } + + splittree_rqgeom(rs.res_node, gflags, &grq, NULL); + } + + if(OBJ_IS(rs.res_node, WSplitUnused)){ + if(frame!=NULL){ + if(do_replace(ws, frame, reg, &rs)) + target=(WRegion*)frame; + } + }else{ + assert(OBJ_IS(rs.res_node, WSplitRegion)); + target=((WSplitRegion*)rs.res_node)->reg; + } + + extl_unref_table(rs.res_config); + } + + if(frame!=NULL && target!=(WRegion*)frame) + destroy_obj((Obj*)frame); + + if(target!=NULL && current_unused(ws)) + region_goto(target); + + return target; +} + + +WPHolder *panews_prepare_manage(WPaneWS *ws, const WClientWin *cwin, + const WManageParams *param, int redir) +{ + WRegion *target=panews_get_target(ws, NULL, (WRegion*)cwin); + WPHolder *ph; + + if(target!=NULL){ + ph=region_prepare_manage(target, cwin, param, MANAGE_REDIR_PREFER_YES); + if(ph!=NULL) + return ph; + } + + warn(TR("Ooops... could not find a region to attach client window to " + "on workspace %s."), region_name((WRegion*)ws)); + return NULL; +} + + +bool panews_handle_unused_drop(WPaneWS *ws, WSplitUnused *specifier, + WRegion *reg) +{ + WRegion *target=panews_get_target(ws, specifier, reg); + + if(target==NULL || !OBJ_IS(target, WMPlex)) + return FALSE; + + return (mplex_attach_simple((WMPlex*)target, reg, + MPLEX_ATTACH_SWITCHTO)!=NULL); +} + + +/*}}}*/ + diff --git a/mod_panews/placement.h b/mod_panews/placement.h new file mode 100644 index 0000000..23dd9fe --- /dev/null +++ b/mod_panews/placement.h @@ -0,0 +1,55 @@ +/* + * ion/mod_panews/placement.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_MOD_PANEWS_PLACEMENT_H +#define ION_MOD_PANEWS_PLACEMENT_H + +#include +#include +#include +#include +#include +#include +#include "panews.h" +#include "splitext.h" + + +typedef struct{ + WPaneWS *ws; + ExtlTab layout; +} WPaneWSInitParams; + + +typedef struct{ + WPaneWS *ws; + WFrame *frame; + WRegion *reg; + WSplitUnused *specifier; + + WSplit *res_node; + ExtlTab res_config; + int res_w, res_h; +} WPaneWSPlacementParams; + + +/* Handlers of this hook should accept WPaneWSInitParams as parameter. */ +extern WHook *panews_init_layout_alt; +/* Handlers of this hook should accept WPaneWSPlacementParams as parameter. */ +extern WHook *panews_make_placement_alt; + + +extern WPHolder *panews_prepare_manage(WPaneWS *ws, const WClientWin *cwin, + const WManageParams *param, + int redir); +extern bool panews_handle_unused_drop(WPaneWS *ws, WSplitUnused *specifier, + WRegion *reg); + +#endif /* ION_MOD_PANEWS_PLACEMENT_H */ diff --git a/mod_panews/splitext.c b/mod_panews/splitext.c new file mode 100644 index 0000000..ad392c5 --- /dev/null +++ b/mod_panews/splitext.c @@ -0,0 +1,617 @@ +/* + * ion/panews/splitext.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "splitext.h" +#include "unusedwin.h" + + +#define GEOM(X) (((WSplit*)(X))->geom) + + +/*{{{ Init/deinit */ + + +bool splitunused_init(WSplitUnused *split, const WRectangle *geom, + WPaneWS *ws) +{ + WWindow *par=REGION_PARENT(ws); + WUnusedWin *uwin; + WFitParams fp; + + assert(par!=NULL); + + fp.g=*geom; + fp.mode=REGION_FIT_EXACT; + + uwin=create_unusedwin(par, &fp); + + if(uwin==NULL) + return FALSE; + + if(!splitregion_init(&(split->regnode), geom, (WRegion*)uwin)){ + destroy_obj((Obj*)uwin); + return FALSE; + } + + if(!tiling_managed_add(&ws->tiling, (WRegion*)uwin)){ + split->regnode.reg=NULL; + destroy_obj((Obj*)uwin); + return FALSE; + } + + return TRUE; +} + + +WSplitUnused *create_splitunused(const WRectangle *geom, WPaneWS *ws) +{ + CREATEOBJ_IMPL(WSplitUnused, splitunused, (p, geom, ws)); +} + + +bool splitpane_init(WSplitPane *pane, const WRectangle *geom, WSplit *cnt) +{ + pane->contents=cnt; + pane->marker=NULL; + + if(!splitinner_init(&(pane->isplit), geom)) + return FALSE; + + return TRUE; +} + + +WSplitPane *create_splitpane(const WRectangle *geom, WSplit *cnt) +{ + CREATEOBJ_IMPL(WSplitPane, splitpane, (p, geom, cnt)); +} + + +void splitunused_deinit(WSplitUnused *split) +{ + if(split->regnode.reg!=NULL){ + destroy_obj((Obj*)split->regnode.reg); + split->regnode.reg=NULL; + } + + splitregion_deinit(&(split->regnode)); +} + + +void splitpane_deinit(WSplitPane *split) +{ + if(split->contents!=NULL){ + WSplit *tmp=split->contents; + split->contents=NULL; + tmp->parent=NULL; + destroy_obj((Obj*)tmp); + } + splitinner_deinit(&(split->isplit)); +} + + +/*}}}*/ + + +/*{{{ X window handling */ + + +static void splitpane_stacking(WSplitPane *pane, + Window *bottomret, Window *topret) +{ + + *bottomret=None; + *topret=None; + + if(pane->contents!=NULL) + split_stacking(pane->contents, bottomret, topret); +} + + +static void splitpane_restack(WSplitPane *pane, Window other, int mode) +{ + if(pane->contents!=None) + split_restack(pane->contents, other, mode); +} + + +static void stack_restack_reg(WRegion *reg, Window *other, int *mode) +{ + Window b=None, t=None; + + if(reg!=NULL){ + region_restack(reg, *other, *mode); + region_stacking(reg, &b, &t); + if(t!=None){ + *other=t; + *mode=Above; + } + } +} + + +static void stack_restack_split(WSplit *split, Window *other, int *mode) +{ + Window b=None, t=None; + + if(split!=NULL){ + split_restack(split, *other, *mode); + split_stacking(split, &b, &t); + if(t!=None){ + *other=t; + *mode=Above; + } + } +} + + +static void splitpane_reparent(WSplitPane *pane, WWindow *target) +{ + if(pane->contents!=NULL) + split_reparent(pane->contents, target); +} + + +static void reparentreg(WRegion *reg, WWindow *target) +{ + WRectangle g=REGION_GEOM(reg); + region_reparent(reg, target, &g, REGION_FIT_EXACT); +} + + +/*}}}*/ + + +/*{{{ Geometry */ + + +static void set_unused_bounds(WSplit *node) +{ + node->min_w=0; + node->min_h=0; + node->max_w=INT_MAX; + node->max_h=INT_MAX; + node->unused_w=node->geom.w; + node->unused_h=node->geom.h; +} + + +static void copy_bounds(WSplit *dst, const WSplit *src) +{ + dst->min_w=src->min_w; + dst->min_h=src->min_h; + dst->max_w=src->max_w; + dst->max_h=src->max_h; + dst->unused_w=src->unused_w; + dst->unused_h=src->unused_h; +} + + +static void splitunused_update_bounds(WSplitUnused *node, bool recursive) +{ + set_unused_bounds((WSplit*)node); +} + + +static void splitpane_update_bounds(WSplitPane *node, bool recursive) +{ + if(node->contents!=NULL){ + if(recursive) + split_update_bounds(node->contents, recursive); + copy_bounds((WSplit*)node, node->contents); + }else{ + set_unused_bounds((WSplit*)node); + } +} + + +static int infadd(int x, int y) +{ + return ((x==INT_MAX || y==INT_MAX) ? INT_MAX : (x+y)); +} + + +static void splitpane_do_resize(WSplitPane *pane, const WRectangle *ng, + int hprimn, int vprimn, bool transpose) +{ + if(transpose && pane->marker!=NULL){ + char *growdir=strchr(pane->marker, ':'); + if(growdir!=NULL){ + const char *newdir=NULL; + growdir++; + + if(strcmp(growdir, "right")==0) + newdir="down"; + else if(strcmp(growdir, "left")==0) + newdir="up"; + if(strcmp(growdir, "down")==0) + newdir="right"; + else if(strcmp(growdir, "up")==0) + newdir="left"; + + if(newdir!=NULL){ + char *newmarker=NULL; + *growdir='\0'; + libtu_asprintf(&newmarker, "%s:%s", pane->marker, newdir); + if(newmarker==NULL){ + *growdir=':'; + }else{ + free(pane->marker); + pane->marker=newmarker; + } + } + } + + } + + ((WSplit*)pane)->geom=*ng; + + if(pane->contents!=NULL) + split_do_resize(pane->contents, ng, hprimn, vprimn, transpose); +} + + +static void splitpane_do_rqsize(WSplitPane *pane, WSplit *node, + RootwardAmount *ha, RootwardAmount *va, + WRectangle *rg, bool tryonly) +{ + WSplitInner *par=((WSplit*)pane)->parent; + + if(par!=NULL){ + splitinner_do_rqsize(par, (WSplit*)pane, ha, va, rg, tryonly); + if(!tryonly) + ((WSplit*)pane)->geom=*rg; + }else{ + *rg=GEOM(pane); + } +} + + +/*}}}*/ + + +/*{{{ Tree manipulation */ + + +static void splitpane_replace(WSplitPane *pane, WSplit *child, WSplit *what) +{ + assert(child==pane->contents && what!=NULL); + + child->parent=NULL; + pane->contents=what; + what->parent=(WSplitInner*)pane; + what->ws_if_root=NULL; /* May not be needed */ +} + + +static WPaneWS *find_ws(WSplit *split) +{ + if(split->parent!=NULL) + return find_ws((WSplit*)split->parent); + + if(split->ws_if_root!=NULL) + return OBJ_CAST(split->ws_if_root, WPaneWS); + + return NULL; +} + + +static void splitpane_remove(WSplitPane *pane, WSplit *child, + bool reclaim_space) +{ + WSplitInner *parent=((WSplit*)pane)->parent; + WSplitUnused *un; + WPaneWS *ws=find_ws((WSplit*)pane); + + assert(child==pane->contents); + + pane->contents=NULL; + child->parent=NULL; + + if(ws!=NULL + && !OBJ_IS_BEING_DESTROYED(ws) + && !OBJ_IS_BEING_DESTROYED(pane)){ + pane->contents=(WSplit*)create_splitunused(&GEOM(pane), ws); + if(pane->contents!=NULL){ + pane->contents->parent=(WSplitInner*)pane; + return; + } + } + + if(parent!=NULL) + splitinner_remove(parent, (WSplit*)pane, reclaim_space); + else + splittree_changeroot((WSplit*)pane, NULL); + + destroy_obj((Obj*)pane); +} + + +/*}}}*/ + + +/*{{{ Tree traversal */ + + +static bool filter_any(WSplit *split) +{ + return OBJ_IS(split, WSplitRegion); +} + + +static bool filter_no_unused(WSplit *split) +{ + return (OBJ_IS(split, WSplitRegion) + && !OBJ_IS(split, WSplitUnused)); +} + + +static bool filter_no_stdisp(WSplit *split) +{ + return (OBJ_IS(split, WSplitRegion) + && !OBJ_IS(split, WSplitST)); +} + + +static bool filter_no_stdisp_unused(WSplit *split) +{ + return (OBJ_IS(split, WSplitRegion) + && !OBJ_IS(split, WSplitST) + && !OBJ_IS(split, WSplitUnused)); + +} + + +static WSplit *splitpane_current_todir(WSplitPane *pane, int dir, int primn, + WSplitFilter *filter) +{ + WSplit *ret=NULL; + + if(pane->contents==NULL) + return NULL; + + /* Try non-unused first */ + if(filter==filter_no_stdisp){ + ret=split_current_todir(pane->contents, dir, primn, + filter_no_stdisp_unused); + }else if(filter==filter_any){ + ret=split_current_todir(pane->contents, dir, primn, + filter_no_unused); + } + + if(ret==NULL) + ret=split_current_todir(pane->contents, dir, primn, filter); + + return ret; +} + + +static void splitpane_forall(WSplitPane *pane, WSplitFn *fn) +{ + if(pane->contents!=NULL) + fn(pane->contents); +} + + +static WSplit *splitpane_current(WSplitPane *pane) +{ + return pane->contents; +} + + +static WSplitRegion *get_node_check(WPaneWS *ws, WRegion *reg) +{ + WSplitRegion *node; + + if(reg==NULL) + return NULL; + + node=splittree_node_of(reg); + + if(node==NULL || REGION_MANAGER(reg)!=(WRegion*)ws) + return NULL; + + return node; +} + + +static WSplitRegion *do_get_nextto(WSplit *node, int dir, int primn, + bool any, bool paneonly) +{ + WSplitFilter *filter=(any ? filter_no_unused : filter_no_stdisp_unused); + WSplit *nextto=NULL; + + while(node->parent!=NULL){ + if(OBJ_IS(node, WSplitPane)){ + if(paneonly) + break; + filter=(any ? filter_any : filter_no_stdisp); + } + nextto=splitinner_nextto(node->parent, node, dir, primn, filter); + if(nextto!=NULL) + break; + node=(WSplit*)(node->parent); + } + + if(OBJ_IS(nextto, WSplitRegion)) + return (WSplitRegion*)nextto; + return NULL; +} + + +WRegion *panews_do_get_nextto(WPaneWS *ws, WRegion *reg, + int dir, int primn, bool any) +{ + WSplitRegion *node=get_node_check(ws, reg), *nextto=NULL; + + if(node==NULL) + return NULL; + + nextto=do_get_nextto((WSplit*)node, dir, primn, TRUE, FALSE); + + if(nextto!=NULL) + return nextto->reg; + + return NULL; +} + +WRegion *panews_do_get_farthest(WPaneWS *ws, + int dir, int primn, bool any) +{ + WSplitFilter *filter=(any ? filter_any : filter_no_stdisp); + WSplit *node=NULL; + if(ws->tiling.split_tree!=NULL) + node=split_current_todir(ws->tiling.split_tree, dir, primn, filter); + if(node!=NULL && OBJ_IS(node, WSplitRegion)) + return ((WSplitRegion*)node)->reg; + return NULL; +} + + +WSplitRegion *split_tree_find_region_in_pane_of(WSplit *node) +{ + return do_get_nextto(node, SPLIT_ANY, PRIMN_ANY, FALSE, TRUE); +} + + +/*}}}*/ + + +/*{{{ Markers and other exports */ + + +/*EXTL_DOC + * Get marker. + */ +EXTL_SAFE +EXTL_EXPORT_MEMBER +const char *splitpane_marker(WSplitPane *pane) +{ + return pane->marker; +} + + +/*EXTL_DOC + * Set marker. + */ +EXTL_EXPORT_MEMBER +bool splitpane_set_marker(WSplitPane *pane, const char *s) +{ + char *s2=NULL; + + if(s!=NULL){ + s2=scopy(s); + if(s2==NULL) + return FALSE; + } + + if(pane->marker==NULL) + free(pane->marker); + + pane->marker=s2; + + return TRUE; +} + + +/*EXTL_DOC + * Get root of contained sub-split tree. + */ +EXTL_SAFE +EXTL_EXPORT_MEMBER +WSplit *splitpane_contents(WSplitPane *pane) +{ + return pane->contents; +} + + +/*}}}*/ + + +/*{{{ Save support */ + + +static bool splitunused_get_config(WSplitUnused *node, ExtlTab *ret) +{ + *ret=split_base_config((WSplit*)node); + return TRUE; +} + + +static bool splitpane_get_config(WSplitPane *pane, ExtlTab *ret) +{ + *ret=split_base_config((WSplit*)pane); + + if(pane->contents!=NULL){ + ExtlTab t; + if(!split_get_config(pane->contents, &t)){ + extl_unref_table(*ret); + return FALSE; + } + extl_table_sets_t(*ret, "contents", t); + extl_unref_table(t); + } + + extl_table_sets_s(*ret, "marker", pane->marker); + + return TRUE; +} + + +/*}}}*/ + + +/*{{{ The classes */ + + +static DynFunTab splitunused_dynfuntab[]={ + {split_update_bounds, splitunused_update_bounds}, + {(DynFun*)split_get_config, (DynFun*)splitunused_get_config}, + END_DYNFUNTAB, +}; + + +static DynFunTab splitpane_dynfuntab[]={ + {split_update_bounds, splitpane_update_bounds}, + {split_do_resize, splitpane_do_resize}, + {splitinner_do_rqsize, splitpane_do_rqsize}, + {splitinner_replace, splitpane_replace}, + {splitinner_remove, splitpane_remove}, + {(DynFun*)split_current_todir, (DynFun*)splitpane_current_todir}, + {(DynFun*)splitinner_current, (DynFun*)splitpane_current}, + {(DynFun*)split_get_config, (DynFun*)splitpane_get_config}, + {splitinner_forall, splitpane_forall}, + {split_stacking, splitpane_stacking}, + {split_restack, splitpane_restack}, + {split_reparent, splitpane_reparent}, + END_DYNFUNTAB, +}; + + +EXTL_EXPORT +IMPLCLASS(WSplitUnused, WSplitRegion, splitunused_deinit, splitunused_dynfuntab); + +EXTL_EXPORT +IMPLCLASS(WSplitPane, WSplitInner, splitpane_deinit, splitpane_dynfuntab); + + +/*}}}*/ + diff --git a/mod_panews/splitext.h b/mod_panews/splitext.h new file mode 100644 index 0000000..0bc73e3 --- /dev/null +++ b/mod_panews/splitext.h @@ -0,0 +1,55 @@ +/* + * ion/panews/splitext.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_PANEWS_SPLITEXT_H +#define ION_PANEWS_SPLITEXT_H + +#include +#include + +INTRCLASS(WSplitUnused); +INTRCLASS(WSplitPane); + +#include "panews.h" + +DECLCLASS(WSplitUnused){ + WSplitRegion regnode; +}; + +DECLCLASS(WSplitPane){ + WSplitInner isplit; + WSplit *contents; + char *marker; +}; + +extern bool splitunused_init(WSplitUnused *split, const WRectangle *geom, + WPaneWS *ws); +extern bool splitpane_init(WSplitPane *split, const WRectangle *geom, + WSplit *cnt); + +extern WSplitUnused *create_splitunused(const WRectangle *geom, + WPaneWS *ws); +extern WSplitPane *create_splitpane(const WRectangle *geom, WSplit *cnt); + +extern void splitunused_deinit(WSplitUnused *split); +extern void splitpane_deinit(WSplitPane *split); + +extern const char *splitpane_get_marker(WSplitPane *pane); +extern bool splitpane_set_marker(WSplitPane *pane, const char *s); + +extern WRegion *panews_do_get_nextto(WPaneWS *ws, WRegion *reg, + int dir, int primn, bool any); +extern WRegion *panews_do_get_farthest(WPaneWS *ws, + int dir, int primn, bool any); + +extern WSplitRegion *split_tree_find_region_in_pane_of(WSplit *node); + +#endif /* ION_PANEWS_SPLITEXT_H */ diff --git a/mod_panews/unusedwin.c b/mod_panews/unusedwin.c new file mode 100644 index 0000000..7e030f0 --- /dev/null +++ b/mod_panews/unusedwin.c @@ -0,0 +1,211 @@ +/* + * ion/panews/unusedwin.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "unusedwin.h" +#include "splitext.h" +#include "placement.h" +#include "main.h" + + +/*{{{ Init/deinit */ + + +static void unusedwin_getbrush(WUnusedWin *uwin) +{ + GrBrush *brush=gr_get_brush(uwin->wwin.win, + region_rootwin_of((WRegion*)uwin), + "frame-tiled-panews-unused"); + + if(brush!=NULL){ + if(uwin->brush!=NULL) + grbrush_release(uwin->brush); + + uwin->brush=brush; + + grbrush_enable_transparency(brush, GR_TRANSPARENCY_YES); + } +} + + +bool unusedwin_init(WUnusedWin *uwin, WWindow *parent, const WFitParams *fp) +{ + uwin->brush=NULL; + + if(!window_init(&(uwin->wwin), parent, fp)) + return FALSE; + + unusedwin_getbrush(uwin); + + region_add_bindmap((WRegion*)uwin, mod_panews_unusedwin_bindmap); + + window_select_input(&(uwin->wwin), IONCORE_EVENTMASK_NORMAL); + + ((WRegion*)uwin)->flags|=REGION_PLEASE_WARP; + + return TRUE; +} + + +WUnusedWin *create_unusedwin(WWindow *parent, const WFitParams *fp) +{ + CREATEOBJ_IMPL(WUnusedWin, unusedwin, (p, parent, fp)); +} + + +void unusedwin_deinit(WUnusedWin *uwin) +{ + if(uwin->brush!=NULL){ + grbrush_release(uwin->brush); + uwin->brush=NULL; + } + + window_deinit(&(uwin->wwin)); +} + + +/*}}}*/ + + +/*{{{ unusedwin_press */ + + +static void unusedwin_border_inner_geom(const WUnusedWin *uwin, + WRectangle *geom) +{ + GrBorderWidths bdw; + + geom->x=0; + geom->y=0; + geom->w=REGION_GEOM(uwin).w; + geom->h=REGION_GEOM(uwin).h; + + if(uwin->brush!=NULL){ + grbrush_get_border_widths(uwin->brush, &bdw); + + geom->x+=bdw.left; + geom->y+=bdw.top; + geom->w-=bdw.left+bdw.right; + geom->h-=bdw.top+bdw.bottom; + } + + geom->w=maxof(geom->w, 0); + geom->h=maxof(geom->h, 0); +} + + +static int unusedwin_press(WUnusedWin *uwin, XButtonEvent *ev, + WRegion **reg_ret) +{ + WRectangle g; + + *reg_ret=NULL; + + window_p_resize_prepare(&uwin->wwin, ev); + + /* Check border */ + + unusedwin_border_inner_geom(uwin, &g); + + if(rectangle_contains(&g, ev->x, ev->y)) + return FRAME_AREA_CLIENT; + + return FRAME_AREA_BORDER; +} + + +/*}}}*/ + + +/*{{{ unusedwin_handle_drop */ + + +static bool unusedwin_handle_drop(WUnusedWin *uwin, int x, int y, + WRegion *dropped) +{ + WSplitUnused *us=OBJ_CAST(splittree_node_of((WRegion*)uwin), + WSplitUnused); + WPaneWS *ws=REGION_MANAGER_CHK(uwin, WPaneWS); + + if(us==NULL || ws==NULL) + return FALSE; + + return panews_handle_unused_drop(ws, us, dropped); +} + + +/*}}}*/ + + +/*{{{ Drawing */ + + +static void unusedwin_updategr(WUnusedWin *uwin) +{ + unusedwin_getbrush(uwin); + region_updategr_default((WRegion*)uwin); +} + + +static void unusedwin_draw(WUnusedWin *uwin, bool complete) +{ + WRectangle g; + const char *substyle=(REGION_IS_ACTIVE(uwin) + ? "active" + : "inactive"); + + if(uwin->brush==NULL) + return; + + g.x=0; + g.y=0; + g.w=REGION_GEOM(uwin).w; + g.h=REGION_GEOM(uwin).h; + + grbrush_begin(uwin->brush, &g, (complete ? 0 : GRBRUSH_NO_CLEAR_OK)); + + grbrush_draw_border(uwin->brush, &g, substyle); + + grbrush_end(uwin->brush); +} + + +/*}}}*/ + + +/*{{{ The class */ + + +static DynFunTab unusedwin_dynfuntab[]={ + {region_updategr, unusedwin_updategr}, + {window_draw, unusedwin_draw}, + {(DynFun*)window_press, (DynFun*)unusedwin_press}, + {(DynFun*)region_handle_drop, (DynFun*)unusedwin_handle_drop}, + END_DYNFUNTAB, +}; + + +IMPLCLASS(WUnusedWin, WWindow, unusedwin_deinit, unusedwin_dynfuntab); + + +/*}}}*/ + diff --git a/mod_panews/unusedwin.h b/mod_panews/unusedwin.h new file mode 100644 index 0000000..bcdc5ba --- /dev/null +++ b/mod_panews/unusedwin.h @@ -0,0 +1,29 @@ +/* + * ion/panews/unusedwin.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_PANEWS_UNUSEDWIN_H +#define ION_PANEWS_UNUSEDWIN_H + +#include +#include + +INTRCLASS(WUnusedWin); + +DECLCLASS(WUnusedWin){ + WWindow wwin; + GrBrush *brush; +}; + +extern bool unusedwin_init(WUnusedWin *pwin, + WWindow *parent, const WFitParams *fp); +extern WUnusedWin *create_unusedwin(WWindow *parent, const WFitParams *fp); + +#endif /* ION_PANEWS_UNUSEDWIN_H */ diff --git a/mod_query/Makefile b/mod_query/Makefile new file mode 100644 index 0000000..8008dea --- /dev/null +++ b/mod_query/Makefile @@ -0,0 +1,29 @@ +## +## Query module Makefile +## + +# System-specific configuration is in system.mk +TOPDIR=.. +include $(TOPDIR)/build/system-inc.mk + +###################################### + +INCLUDES += $(X11_INCLUDES) $(LIBTU_INCLUDES) $(LIBEXTL_INCLUDES) -I.. +CFLAGS += $(XOPEN_SOURCE) $(C99_SOURCE) + +SOURCES=complete.c edln.c input.c listing.c main.c wedln.c \ + wedln-wrappers.c wmessage.c query.c fwarn.c history.c + +MAKE_EXPORTS=mod_query + +MODULE=mod_query +MODULE_STUB=mod_query.lua +LUA_SOURCES=mod_query_chdir.lua + +###################################### + +include $(TOPDIR)/build/rules.mk + +###################################### + +_install: module_install lc_install diff --git a/mod_query/complete.c b/mod_query/complete.c new file mode 100644 index 0000000..0bc2df3 --- /dev/null +++ b/mod_query/complete.c @@ -0,0 +1,206 @@ +/* + * ion/mod_query/complete.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include + +#include +#include +#include "complete.h" +#include "edln.h" +#include "wedln.h" + + +/*{{{ Completion list processing */ + + +static int str_common_part_l(const char *p1, const char *p2) +{ + int i=0; + + while(1){ + if(*p1=='\0' || *p1!=*p2) + break; + p1++; p2++; i++; + } + + return i; +} + + +/* Get length of part common to all completions + * and remove duplicates + */ +static int get_common_part_rmdup(char **completions, int *ncomp) +{ + int i, j, c=INT_MAX, c2; + + for(i=0, j=1; j<*ncomp; j++){ + c2=str_common_part_l(completions[i], completions[j]); + if(c2palloced>=1); + + edln->p[0]='\0'; + edln->psize=0; + edln->point=0; + edln->mark=-1; + edln->histent=-1; +} + + +static void edln_do_set_completion(Edln *edln, const char *comp, int len, + const char *beg, const char *end) +{ + edln_reset(edln); + + if(beg!=NULL) + edln_insstr_n(edln, beg, strlen(beg), FALSE, TRUE); + + if(len>0) + edln_insstr_n(edln, comp, len, FALSE, TRUE); + + if(end!=NULL) + edln_insstr_n(edln, end, strlen(end), FALSE, FALSE); + + if(edln->ui_update!=NULL){ + edln->ui_update(edln->uiptr, 0, + EDLN_UPDATE_MOVED|EDLN_UPDATE_CHANGED| + EDLN_UPDATE_NEW); + } + +} + + +void edln_set_completion(Edln *edln, const char *comp, + const char *beg, const char *end) +{ + edln_do_set_completion(edln, comp, strlen(comp), beg, end); +} + + +int edln_do_completions(Edln *edln, char **completions, int ncomp, + const char *beg, const char *end, bool setcommon, + bool nosort) +{ + int len; + int i; + + if(ncomp==0){ + return 0; + }else if(ncomp==1){ + len=strlen(completions[0]); + }else{ + if(!nosort) + qsort(completions, ncomp, sizeof(char**), compare); + len=get_common_part_rmdup(completions, &ncomp); + } + + if(setcommon) + edln_do_set_completion(edln, completions[0], len, beg, end); + + return ncomp; +} + + +/*}}}*/ + + +/*{{{ WComplProxy */ + + +bool complproxy_init(WComplProxy *proxy, WEdln *wedln, int id, int cycle) +{ + watch_init(&(proxy->wedln_watch)); + if(!watch_setup(&(proxy->wedln_watch), (Obj*)wedln, NULL)) + return FALSE; + + proxy->id=id; + proxy->cycle=cycle; + + return TRUE; +} + + +WComplProxy *create_complproxy(WEdln *wedln, int id, int cycle) +{ + CREATEOBJ_IMPL(WComplProxy, complproxy, (p, wedln, id, cycle)); +} + + +void complproxy_deinit(WComplProxy *proxy) +{ + watch_reset(&(proxy->wedln_watch)); +} + + +/*EXTL_DOC + * Set completion list of the \type{WEdln} that \var{proxy} refers to to + * \var{compls}, if it is still waiting for this completion run. The + * numerical indexes of \var{compls} list the found completions. If the + * entry \var{common_beg} (\var{common_end}) exists, it gives an extra + * common prefix (suffix) of all found completions. + */ +EXTL_EXPORT_MEMBER +bool complproxy_set_completions(WComplProxy *proxy, ExtlTab compls) +{ + WEdln *wedln=(WEdln*)proxy->wedln_watch.obj; + + if(wedln!=NULL){ + if(wedln->compl_waiting_id==proxy->id){ + wedln_set_completions(wedln, compls, proxy->cycle); + wedln->compl_current_id=proxy->id; + return TRUE; + } + } + + return FALSE; +} + + +EXTL_EXPORT +IMPLCLASS(WComplProxy, Obj, complproxy_deinit, NULL); + + +/*}}}*/ + diff --git a/mod_query/complete.h b/mod_query/complete.h new file mode 100644 index 0000000..f7aa910 --- /dev/null +++ b/mod_query/complete.h @@ -0,0 +1,42 @@ +/* + * ion/mod_query/complete.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_MOD_QUERY_COMPLETE_H +#define ION_MOD_QUERY_COMPLETE_H + +#include +#include +#include +#include "edln.h" +#include "wedln.h" + +INTRCLASS(WComplProxy); + +DECLCLASS(WComplProxy){ + Obj o; + Watch wedln_watch; + int id; + int cycle; +}; + + +extern WComplProxy *create_complproxy(WEdln *wedln, int id, int cycle); + +extern bool complproxy_set_completions(WComplProxy *proxy, ExtlTab compls); + + +extern int edln_do_completions(Edln *edln, char **completions, int ncomp, + const char *beg, const char *end, + bool setcommon, bool nosort); +extern void edln_set_completion(Edln *edln, const char *comp, + const char *beg, const char *end); + +#endif /* ION_MOD_QUERY_COMPLETE_H */ diff --git a/mod_query/edln.c b/mod_query/edln.c new file mode 100644 index 0000000..99283f4 --- /dev/null +++ b/mod_query/edln.c @@ -0,0 +1,779 @@ +/* + * ion/mod_query/edln.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include + +#include +#include +#include +#include + +#include "edln.h" +#include "wedln.h" +#include "history.h" + +#define EDLN_ALLOCUNIT 16 + +#define UPDATE(X) \ + edln->ui_update(edln->uiptr, X, 0) + +#define UPDATE_MOVED(X) \ + edln->ui_update(edln->uiptr, X, EDLN_UPDATE_MOVED) + +#define UPDATE_CHANGED(X) \ + edln->ui_update(edln->uiptr, X, \ + EDLN_UPDATE_MOVED|EDLN_UPDATE_CHANGED) + +#define UPDATE_CHANGED_NOMOVE(X) \ + edln->ui_update(edln->uiptr, X, \ + EDLN_UPDATE_CHANGED) + +#define UPDATE_NEW() \ + edln->ui_update(edln->uiptr, 0, \ + EDLN_UPDATE_NEW|EDLN_UPDATE_MOVED|EDLN_UPDATE_CHANGED) + +#define CHAR wchar_t +#define ISALNUM iswalnum +#define CHAR_AT(P, N) str_wchar_at(P, N) + + +/*{{{ Alloc */ + + +static bool edln_pspc(Edln *edln, int n) +{ + char *np; + int pa; + + if(edln->pallocedpsize+1+n){ + pa=edln->palloced+n; + pa|=(EDLN_ALLOCUNIT-1); + np=ALLOC_N(char, pa); + + if(np==NULL) + return FALSE; + + memmove(np, edln->p, edln->point*sizeof(char)); + memmove(np+edln->point+n, edln->p+edln->point, + (edln->psize-edln->point+1)*sizeof(char)); + free(edln->p); + edln->p=np; + edln->palloced=pa; + }else{ + memmove(edln->p+edln->point+n, edln->p+edln->point, + (edln->psize-edln->point+1)*sizeof(char)); + } + + if(edln->mark>edln->point) + edln->mark+=n; + + edln->psize+=n; + + edln->modified=1; + return TRUE; +} + + +static bool edln_rspc(Edln *edln, int n) +{ + char *np; + int pa; + + if(n+edln->point>=edln->psize) + n=edln->psize-edln->point; + + if(n==0) + return TRUE; + + if((edln->psize+1-n)<(edln->palloced&~(EDLN_ALLOCUNIT-1))){ + pa=edln->palloced&~(EDLN_ALLOCUNIT-1); + np=ALLOC_N(char, pa); + + if(np==NULL) + goto norm; + + memmove(np, edln->p, edln->point*sizeof(char)); + memmove(np+edln->point, edln->p+edln->point+n, + (edln->psize-edln->point+1-n)*sizeof(char)); + free(edln->p); + edln->p=np; + edln->palloced=pa; + }else{ + norm: + memmove(edln->p+edln->point, edln->p+edln->point+n, + (edln->psize-edln->point+1-n)*sizeof(char)); + } + edln->psize-=n; + + if(edln->mark>edln->point) + edln->mark-=n; + + edln->modified=1; + return TRUE; +} + + +static void edln_clearstr(Edln *edln) +{ + if(edln->p!=NULL){ + free(edln->p); + edln->p=NULL; + } + edln->palloced=0; + edln->psize=0; +} + + +static bool edln_initstr(Edln *edln, const char *p) +{ + int l=strlen(p), al; + + al=(l+1)|(EDLN_ALLOCUNIT-1); + + edln->p=ALLOC_N(char, al); + + if(edln->p==NULL) + return FALSE; + + edln->palloced=al; + edln->psize=l; + strcpy(edln->p, p); + + return TRUE; +} + + +static bool edln_setstr(Edln *edln, const char *p) +{ + edln_clearstr(edln); + return edln_initstr(edln, p); +} + + +/*}}}*/ + + +/*{{{ Insert */ + + +bool edln_insstr(Edln *edln, const char *str) +{ + int l; + + if(str==NULL) + return FALSE; + + l=strlen(str); + + return edln_insstr_n(edln, str, l, TRUE, TRUE); +} + + +bool edln_insstr_n(Edln *edln, const char *str, int l, + bool update, bool movepoint) +{ + if(!edln_pspc(edln, l)) + return FALSE; + + memmove(&(edln->p[edln->point]), str, l); + if(movepoint){ + edln->point+=l; + if(update) + UPDATE_CHANGED(edln->point-l); + }else{ + if(update) + UPDATE_CHANGED_NOMOVE(edln->point-l); + } + + return TRUE; +} + + +/*}}}*/ + + +/*{{{ Transpose */ + + +bool edln_transpose_chars(Edln *edln) +{ + int off1, off2, pos; + char *buf; + + if((edln->point==0) || (edln->psize<2)) + return FALSE; + + pos=edln->point; + if(edln->point==edln->psize) + pos=pos-str_prevoff(edln->p, edln->point); + + off1=str_nextoff(edln->p, pos); + off2=str_prevoff(edln->p, pos); + + buf=ALLOC_N(char, off2); + if(buf==NULL) + return FALSE; + memmove(buf, &(edln->p[pos-off2]), off2); + memmove(&(edln->p[pos-off2]), &(edln->p[pos]), off1); + memmove(&(edln->p[pos-off2+off1]), buf, off2); + FREE(buf); + + if(edln->point!=edln->psize) + edln->point+=off1; + + UPDATE_CHANGED(0); + return TRUE; +} + + +bool edln_transpose_words(Edln *edln) +{ + int m1, m2, m3, m4, off1, off2, oldp; + char *buf; + + if((edln->point==edln->psize) || (edln->psize<3)) + return FALSE; + + oldp=edln->point; + edln_bskip_word(edln); + m1=edln->point; + edln_skip_word(edln); + m2=edln->point; + edln_skip_word(edln); + if(edln->point==m2) + goto noact; + m4=edln->point; + edln_bskip_word(edln); + if(edln->point==m1) + goto noact; + m3=edln->point; + + off1=m4-m3; + off2=m3-m2; + + buf=ALLOC_N(char, m4-m1); + if(buf==NULL) + goto noact; + memmove(buf, &(edln->p[m3]), off1); + memmove(buf+off1, &(edln->p[m2]), off2); + memmove(buf+off1+off2, &(edln->p[m1]), m2-m1); + memmove(&(edln->p[m1]), buf, m4-m1); + FREE(buf); + + edln->point=m4; + UPDATE_CHANGED(0); + return TRUE; + +noact: + edln->point=oldp; + UPDATE_MOVED(edln->point); + return FALSE; +} + + +/*}}}*/ + + +/*{{{ Movement */ + + +static int do_edln_back(Edln *edln) +{ + int l=str_prevoff(edln->p, edln->point); + edln->point-=l; + return l; +} + + +void edln_back(Edln *edln) +{ + int p=edln->point; + do_edln_back(edln); + /*if(edln->point!=p)*/ + UPDATE_MOVED(edln->point); +} + + +static int do_edln_forward(Edln *edln) +{ + int l=str_nextoff(edln->p, edln->point); + edln->point+=l; + return l; +} + + +void edln_forward(Edln *edln) +{ + int p=edln->point; + do_edln_forward(edln); + /*if(edln->point!=p)*/ + UPDATE_MOVED(p); +} + + +void edln_bol(Edln *edln) +{ + if(edln->point!=0){ + edln->point=0; + UPDATE_MOVED(0); + } +} + + +void edln_eol(Edln *edln) +{ + int o=edln->point; + + if(edln->point!=edln->psize){ + edln->point=edln->psize; + UPDATE_MOVED(o); + } +} + + +void edln_bskip_word(Edln *edln) +{ + int p, n; + CHAR c; + + while(edln->point>0){ + n=do_edln_back(edln); + c=CHAR_AT(edln->p+edln->point, n); + if(ISALNUM(c)) + goto fnd; + } + UPDATE_MOVED(edln->point); + return; + +fnd: + while(edln->point>0){ + p=edln->point; + n=do_edln_back(edln); + c=CHAR_AT(edln->p+edln->point, n); + + if(!ISALNUM(c)){ + edln->point=p; + break; + } + } + UPDATE_MOVED(edln->point); +} + + +void edln_skip_word(Edln *edln) +{ + int oldp=edln->point; + CHAR c; + + while(edln->pointpsize){ + c=CHAR_AT(edln->p+edln->point, edln->psize-edln->point); + if(ISALNUM(c)) + goto fnd; + if(do_edln_forward(edln)==0) + break; + } + UPDATE_MOVED(oldp); + return; + +fnd: + while(edln->pointpsize){ + c=CHAR_AT(edln->p+edln->point, edln->psize-edln->point); + if(!ISALNUM(c)) + break; + if(do_edln_forward(edln)==0) + break; + } + UPDATE_MOVED(oldp); +} + + +void edln_set_point(Edln *edln, int point) +{ + int o=edln->point; + + if(point<0) + point=0; + else if(point>edln->psize) + point=edln->psize; + + edln->point=point; + + if(opsize-edln->point; + size_t l; + + if(left<=0) + return; + + l=str_nextoff(edln->p, edln->point); + + if(l>0) + edln_rspc(edln, l); + + UPDATE_CHANGED_NOMOVE(edln->point); +} + + +void edln_backspace(Edln *edln) +{ + int n; + if(edln->point==0) + return; + n=do_edln_back(edln); + if(n!=0){ + edln_rspc(edln, n); + UPDATE_CHANGED(edln->point); + } +} + +void edln_kill_to_eol(Edln *edln) +{ + edln_rspc(edln, edln->psize-edln->point); + UPDATE_CHANGED_NOMOVE(edln->point); +} + + +void edln_kill_to_bol(Edln *edln) +{ + int p=edln->point; + + edln_bol(edln); + edln_rspc(edln, p); + edln->point=0; + UPDATE_CHANGED(0); +} + + +void edln_kill_line(Edln *edln) +{ + edln_bol(edln); + edln_kill_to_eol(edln); + UPDATE_CHANGED(0); +} + + +void edln_kill_word(Edln *edln) +{ + int oldp=edln->point; + int l; + edln_skip_word(edln); + + if(edln->point==oldp) + return; + + l=edln->point-oldp; + edln->point=oldp; + edln_rspc(edln, l); + + UPDATE_CHANGED_NOMOVE(oldp); +} + + +void edln_bkill_word(Edln *edln) +{ + int oldp=edln->point; + + edln_bskip_word(edln); + + if(edln->point==oldp) + return; + + edln_rspc(edln, oldp-edln->point); + UPDATE_CHANGED(edln->point); +} + + +/*}}}*/ + + +/*{{{ Selection */ + + +static void do_set_mark(Edln *edln, int nm) +{ + int m=edln->mark; + edln->mark=nm; + if(m!=-1) + UPDATE(m < edln->point ? m : edln->point); +} + + +void edln_set_mark(Edln *edln) +{ + do_set_mark(edln, edln->point); +} + + +void edln_clear_mark(Edln *edln) +{ + do_set_mark(edln, -1); +} + + +static void edln_do_copy(Edln *edln, bool del) +{ + int beg, end; + + if(edln->mark<0 || edln->point==edln->mark) + return; + + if(edln->pointmark){ + beg=edln->point; + end=edln->mark; + }else{ + beg=edln->mark; + end=edln->point; + } + + ioncore_set_selection_n(edln->p+beg, end-beg); + + if(del){ + edln->point=beg; + edln_rspc(edln, end-beg); + } + edln->mark=-1; + + UPDATE(beg); +} + + +void edln_cut(Edln *edln) +{ + edln_do_copy(edln, TRUE); +} + + +void edln_copy(Edln *edln) +{ + edln_do_copy(edln, FALSE); +} + + +/*}}}*/ + + +/*{{{ History */ + + +bool edln_set_context(Edln *edln, const char *str) +{ + char *s=scat(str, ":"), *cp; + + if(s==NULL) + return FALSE; + + cp=strchr(s, ':'); + while(cp!=NULL && *(cp+1)!='\0'){ + *cp='_'; + cp=strchr(cp, ':'); + } + + if(edln->context!=NULL) + free(edln->context); + edln->context=s; + + return TRUE; +} + + +static void edln_do_set_hist(Edln *edln, int e, bool match) +{ + const char *str=mod_query_history_get(e), *s2; + if(str!=NULL){ + if(edln->histent<0){ + edln->tmp_p=edln->p; + edln->tmp_palloced=edln->palloced; + edln->p=NULL; + } + + /* Skip context label */ + s2=strchr(str, ':'); + if(s2!=NULL) + str=s2+1; + + edln->histent=e; + edln_setstr(edln, str); + edln->point=(match + ? minof(edln->point, edln->psize) + : edln->psize); + edln->mark=-1; + edln->modified=FALSE; + UPDATE_NEW(); + } +} + + +static char *history_search_str(Edln *edln) +{ + char *sstr; + char tmp=edln->p[edln->point]; + edln->p[edln->point]='\0'; + sstr=scat(edln->context ? edln->context : "*:", edln->p); + edln->p[edln->point]=tmp; + return sstr; +} + + +static int search(Edln *edln, int from, bool bwd, bool match) +{ + int e; + + if(match && edln->point>0){ + char *tmpstr=history_search_str(edln); + if(tmpstr==NULL) + return edln->histent; + e=mod_query_history_search(tmpstr, from, bwd); + free(tmpstr); + }else{ + e=mod_query_history_search(edln->context, from, bwd); + } + + return e; +} + + +void edln_history_prev(Edln *edln, bool match) +{ + int e=search(edln, edln->histent+1, FALSE, match); + if(e>=0) + edln_do_set_hist(edln, e, match); +} + + +void edln_history_next(Edln *edln, bool match) +{ + int e=edln->histent; + + if(edln->histent<0) + return; + + e=search(edln, edln->histent-1, TRUE, match); + + if(e>=0){ + edln_do_set_hist(edln, e, match); + }else{ + edln->histent=-1; + if(edln->p!=NULL) + free(edln->p); + edln->p=edln->tmp_p; + edln->palloced=edln->tmp_palloced; + edln->tmp_p=NULL; + edln->psize=(edln->p==NULL ? 0 : strlen(edln->p)); + edln->point=edln->psize; + edln->mark=-1; + edln->modified=TRUE; + UPDATE_NEW(); + } +} + + +uint edln_history_matches(Edln *edln, char ***h_ret) +{ + char *tmpstr=history_search_str(edln); + uint ret; + + if(tmpstr==NULL){ + *h_ret=NULL; + return 0; + } + + ret=mod_query_history_complete(tmpstr, h_ret); + + free(tmpstr); + + return ret; +} + + +/*}}}*/ + + +/*{{{ Init/deinit */ + + +bool edln_init(Edln *edln, const char *p) +{ + if(p==NULL) + p=""; + + if(!edln_initstr(edln, p)) + return FALSE; + + edln->point=edln->psize; + edln->mark=-1; + edln->histent=-1; + edln->modified=FALSE; + edln->tmp_p=NULL; + edln->context=NULL; + + return TRUE; +} + + +void edln_deinit(Edln *edln) +{ + if(edln->p!=NULL){ + free(edln->p); + edln->p=NULL; + } + if(edln->tmp_p!=NULL){ + free(edln->tmp_p); + edln->tmp_p=NULL; + } + if(edln->context!=NULL){ + free(edln->context); + edln->context=NULL; + } +} + + +static const char *ctx(Edln *edln) +{ + if(edln->context!=NULL) + return edln->context; + else + return "default:"; +} + + +char* edln_finish(Edln *edln) +{ + char *p=edln->p, *hist; + + if(p!=NULL){ + libtu_asprintf(&hist, "%s%s", ctx(edln), p); + if(hist!=NULL) + mod_query_history_push_(hist); + } + + edln->p=NULL; + edln->psize=edln->palloced=0; + + /*stripws(p);*/ + return str_stripws(p); +} + +/*}}}*/ + diff --git a/mod_query/edln.h b/mod_query/edln.h new file mode 100644 index 0000000..9d95de5 --- /dev/null +++ b/mod_query/edln.h @@ -0,0 +1,75 @@ +/* + * ion/mod_query/edln.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_MOD_QUERY_EDLN_H +#define ION_MOD_QUERY_EDLN_H + +#include +#include + +INTRSTRUCT(Edln); + +typedef void EdlnUpdateHandler(void*, int from, int mode); + +#define EDLN_UPDATE_MOVED 0x01 +#define EDLN_UPDATE_CHANGED 0x02 +#define EDLN_UPDATE_NEW 0x04 + +DECLSTRUCT(Edln){ + char *p; + char *tmp_p; + int point; + int mark; + int psize; + int palloced; + int tmp_palloced; + int modified; + int histent; + void *uiptr; + char *context; + + EdlnUpdateHandler *ui_update; +}; + + +bool edln_insstr(Edln *edln, const char *str); +bool edln_insstr_n(Edln *edln, const char *str, int len, + bool update, bool movepoint); +bool edln_transpose_chars(Edln *edln); +bool edln_transpose_words(Edln *edln); +void edln_back(Edln *edln); +void edln_forward(Edln *edln); +void edln_bol(Edln *edln); +void edln_eol(Edln *edln); +void edln_bskip_word(Edln *edln); +void edln_skip_word(Edln *edln); +void edln_set_point(Edln *edln, int point); +void edln_delete(Edln *edln); +void edln_backspace(Edln *edln); +void edln_kill_to_eol(Edln *edln); +void edln_kill_to_bol(Edln *edln); +void edln_kill_line(Edln *edln); +void edln_kill_word(Edln *edln); +void edln_bkill_word(Edln *edln); +void edln_set_mark(Edln *edln); +void edln_clear_mark(Edln *edln); +void edln_cut(Edln *edln); +void edln_copy(Edln *edln); +void edln_history_prev(Edln *edln, bool match); +void edln_history_next(Edln *edln, bool match); +uint edln_history_matches(Edln *edln, char ***h_ret); +bool edln_set_context(Edln *edln, const char *str); + +bool edln_init(Edln *edln, const char *dflt); +void edln_deinit(Edln *edln); +char* edln_finish(Edln *edln); + +#endif /* ION_MOD_QUERY_EDLN_H */ diff --git a/mod_query/fwarn.c b/mod_query/fwarn.c new file mode 100644 index 0000000..7280f85 --- /dev/null +++ b/mod_query/fwarn.c @@ -0,0 +1,67 @@ +/* + * ion/mod_query/query.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + + +#include +#include +#include +#include +#include +#include "wmessage.h" +#include "fwarn.h" + + +/*EXTL_DOC + * Display an error message box in the multiplexer \var{mplex}. + */ +EXTL_EXPORT +WMessage *mod_query_warn(WMPlex *mplex, const char *p) +{ + char *p2; + WMessage *wmsg; + + if(p==NULL) + return NULL; + + p2=scat(TR("Error:\n"), p); + + if(p2==NULL) + return NULL; + + wmsg=mod_query_message(mplex, p2); + + free(p2); + + return wmsg; +} + +/*EXTL_DOC + * Display a message in the \var{mplex}. + */ +EXTL_EXPORT +WMessage *mod_query_message(WMPlex *mplex, const char *p) +{ + WMPlexAttachParams par; + + if(p==NULL) + return NULL; + + par.flags=(MPLEX_ATTACH_SWITCHTO| + MPLEX_ATTACH_MODAL| + MPLEX_ATTACH_UNNUMBERED| + MPLEX_ATTACH_SIZEPOLICY); + par.szplcy=SIZEPOLICY_FULL_BOUNDS; + + return (WMessage*)mplex_do_attach_new(mplex, &par, + (WRegionCreateFn*)create_wmsg, + (void*)p); +} + diff --git a/mod_query/fwarn.h b/mod_query/fwarn.h new file mode 100644 index 0000000..6dfee37 --- /dev/null +++ b/mod_query/fwarn.h @@ -0,0 +1,21 @@ +/* + * ion/mod_query/fwarn.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_MOD_QUERY_FWARN_H +#define ION_MOD_QUERY_FWARN_H + +#include +#include "wmessage.h" + +extern WMessage *mod_query_message(WMPlex *mplex, const char *p); +extern WMessage *mod_query_fwarn(WMPlex *mplex, const char *p); + +#endif /* ION_MOD_QUERY_FWARN_H */ diff --git a/mod_query/history.c b/mod_query/history.c new file mode 100644 index 0000000..05b6aa5 --- /dev/null +++ b/mod_query/history.c @@ -0,0 +1,207 @@ +/* + * ion/mod_query/history.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include + +#include +#include + +#include "history.h" + + +#define HISTORY_SIZE 1024 + + +static int hist_head=HISTORY_SIZE; +static int hist_count=0; +static char *hist[HISTORY_SIZE]; + + +int get_index(int i) +{ + if(i<0 || i>=hist_count) + return -1; + return (hist_head+i)%HISTORY_SIZE; +} + + +/*EXTL_DOC + * Push an entry into line editor history. + */ +EXTL_EXPORT +bool mod_query_history_push(const char *str) +{ + char *s=scopy(str); + + if(s==NULL) + return FALSE; + + mod_query_history_push_(s); + + return TRUE; +} + + +void mod_query_history_push_(char *str) +{ + int ndx=mod_query_history_search(str, 0, FALSE); + + if(ndx==0){ + return; /* First entry already */ + }else if(ndx>0){ + int i, j; + i=get_index(ndx); + free(hist[i]); + while(++ndx +#include + +extern const char *mod_query_history_get(int n); +extern bool mod_query_history_push(const char *s); +extern void mod_query_history_push_(char *s); +extern void mod_query_history_clear(); +extern int mod_query_history_search(const char *s, int from, bool bwd); +extern uint mod_query_history_complete(const char *s, char ***h_ret); +extern ExtlTab mod_query_history_table(); + +#endif /* ION_MOD_QUERY_HISTORY_H */ diff --git a/mod_query/input.c b/mod_query/input.c new file mode 100644 index 0000000..ee19026 --- /dev/null +++ b/mod_query/input.c @@ -0,0 +1,206 @@ +/* + * ion/mod_query/input.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "inputp.h" + + +/*{{{ Dynfuns */ + + +/*EXTL_DOC + * Scroll input \var{input} text contents up. + */ +EXTL_EXPORT_MEMBER +void input_scrollup(WInput *input) +{ + CALL_DYN(input_scrollup, input, (input)); +} + + +/*EXTL_DOC + * Scroll input \var{input} text contents down. + */ +EXTL_EXPORT_MEMBER +void input_scrolldown(WInput *input) +{ + CALL_DYN(input_scrolldown, input, (input)); +} + + +void input_calc_size(WInput *input, WRectangle *geom) +{ + *geom=input->last_fp.g; + { + CALL_DYN(input_calc_size, input, (input, geom)); + } +} + + +const char *input_style(WInput *input) +{ + const char *ret="input"; + CALL_DYN_RET(ret, const char*, input_style, input, (input)); + return ret; +} + + +/*}}}*/ + + +/*{{{ Resize and draw config update */ + + +static void input_do_refit(WInput *input, WWindow *par) +{ + WRectangle g; + input_calc_size(input, &g); + window_do_fitrep(&input->win, par, &g); +} + + +void input_refit(WInput *input) +{ + input_do_refit(input, NULL); +} + + +void input_fitrep(WInput *input, WWindow *par, const WFitParams *fp) +{ + input->last_fp=*fp; + input_do_refit(input, par); +} + + +void input_updategr(WInput *input) +{ + GrBrush *brush; + + brush=gr_get_brush(input->win.win, + region_rootwin_of((WRegion*)input), + input_style(input)); + + if(brush==NULL) + return; + + if(input->brush!=NULL) + grbrush_release(input->brush); + input->brush=brush; + input_refit(input); + + region_updategr_default((WRegion*)input); + + window_draw((WWindow*)input, TRUE); +} + + +/*}}}*/ + + +/*{{{ Init/deinit */ + + +bool input_init(WInput *input, WWindow *par, const WFitParams *fp) +{ + Window win; + + input->last_fp=*fp; + + if(!window_init((WWindow*)input, par, fp)) + return FALSE; + + win=input->win.win; + + input->brush=gr_get_brush(win, region_rootwin_of((WRegion*)par), + input_style(input)); + + if(input->brush==NULL) + goto fail; + + input_refit(input); + window_select_input(&(input->win), IONCORE_EVENTMASK_NORMAL); + + region_add_bindmap((WRegion*)input, mod_query_input_bindmap); + + region_register((WRegion*)input); + + return TRUE; + +fail: + window_deinit((WWindow*)input); + return FALSE; +} + + +void input_deinit(WInput *input) +{ + if(input->brush!=NULL) + grbrush_release(input->brush); + + window_deinit((WWindow*)input); +} + + +/*EXTL_DOC + * Close input not calling any possible finish handlers. + */ +EXTL_EXPORT_MEMBER +void input_cancel(WInput *input) +{ + if(region_manager_allows_destroying((WRegion*)input)) + mainloop_defer_destroy((Obj*)input); +} + + +/*}}}*/ + + +/*{{{ Focus */ + + +static void input_inactivated(WInput *input) +{ + window_draw((WWindow*)input, FALSE); +} + + +static void input_activated(WInput *input) +{ + window_draw((WWindow*)input, FALSE); +} + + +/*}}}*/ + + +/*{{{ Dynamic function table and class implementation */ + + +static DynFunTab input_dynfuntab[]={ + {(DynFun*)region_fitrep, (DynFun*)input_fitrep}, + {region_updategr, input_updategr}, + {region_activated, input_activated}, + {region_inactivated, input_inactivated}, + END_DYNFUNTAB +}; + + +EXTL_EXPORT +IMPLCLASS(WInput, WWindow, input_deinit, input_dynfuntab); + + +/*}}}*/ diff --git a/mod_query/input.h b/mod_query/input.h new file mode 100644 index 0000000..21abf71 --- /dev/null +++ b/mod_query/input.h @@ -0,0 +1,42 @@ +/* + * ion/mod_query/input.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_MOD_QUERY_INPUT_H +#define ION_MOD_QUERY_INPUT_H + +#include +#include +#include +#include + +INTRCLASS(WInput); + +DECLCLASS(WInput){ + WWindow win; + WFitParams last_fp; + GrBrush *brush; +}; + +extern bool input_init(WInput *input, WWindow *par, const WFitParams *fp); +extern void input_deinit(WInput *input); + +extern void input_fitrep(WInput *input, WWindow *par, const WFitParams *fp); +extern void input_refit(WInput *input); +extern void input_cancel(WInput *input); +extern bool input_rqclose(WInput *input); +extern void input_updategr(WInput *input); + +DYNFUN void input_scrollup(WInput *input); +DYNFUN void input_scrolldown(WInput *input); +DYNFUN void input_calc_size(WInput *input, WRectangle *geom); +DYNFUN const char *input_style(WInput *input); + +#endif /* ION_MOD_QUERY_INPUT_H */ diff --git a/mod_query/inputp.h b/mod_query/inputp.h new file mode 100644 index 0000000..1da92b5 --- /dev/null +++ b/mod_query/inputp.h @@ -0,0 +1,28 @@ +/* + * ion/mod_query/inputp.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_MOD_QUERY_INPUTP_H +#define ION_MOD_QUERY_INPUTP_H + +#include +#include +#include +#include +#include "input.h" + +typedef void WInputCalcSizeFn(WInput*, WRectangle*); +typedef void WInputScrollFn(WInput*); +typedef void WInputDrawFn(WInput*, bool complete); + +extern WBindmap *mod_query_input_bindmap; +extern WBindmap *mod_query_wedln_bindmap; + +#endif /* ION_MOD_QUERY_INPUTP_H */ diff --git a/mod_query/listing.c b/mod_query/listing.c new file mode 100644 index 0000000..9ceb41d --- /dev/null +++ b/mod_query/listing.c @@ -0,0 +1,529 @@ +/* + * ion/mod_query/listing.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include + +#include +#include +#include +#include +#include "listing.h" + + +#define COL_SPACING 16 +#define CONT_INDENT "xx" +#define CONT_INDENT_LEN 2 +#define ITEMROWS(L, R) ((L)->iteminfos==NULL ? 1 : (L)->iteminfos[R].n_parts) + + +static int strings_maxw(GrBrush *brush, char **strs, int nstrs) +{ + int maxw=0, w, i; + + for(i=0; imaxw) + maxw=w; + } + + return maxw; +} + + +static int getbeg(GrBrush *brush, int maxw, char *str, int l, int *wret) +{ + int n=0, nprev=0, w; + GrFontExtents fnte; + + if(maxw<=0){ + *wret=0; + return 0; + } + + grbrush_get_font_extents(brush, &fnte); + + if(fnte.max_width!=0){ + /* Do an initial skip. */ + int n2=maxw/fnte.max_width; + + n=0; + while(n2>0){ + n+=str_nextoff(str, n); + n2--; + } + } + + w=grbrush_get_text_width(brush, str, n); + nprev=n; + *wret=w; + + while(w<=maxw){ + *wret=w; + nprev=n; + n+=str_nextoff(str, n); + if(n==nprev) + break; + w=grbrush_get_text_width(brush, str, n); + } + + return nprev; +} + + +static void reset_iteminfo(WListingItemInfo *iinf) +{ + iinf->n_parts=1; + if(iinf->part_lens!=NULL){ + free(iinf->part_lens); + iinf->part_lens=NULL; + } +} + + +static void string_do_calc_parts(GrBrush *brush, int maxw, char *str, int l, + WListingItemInfo *iinf, + int wrapw, int ciw) +{ + int i=iinf->n_parts, l2=l, w; + int rmaxw=maxw-(i==0 ? 0 : ciw); + + iinf->n_parts++; + + w=grbrush_get_text_width(brush, str, l); + + if(w>rmaxw){ + l2=getbeg(brush, rmaxw-wrapw, str, l, &w); + if(l2<=0) + l2=1; + } + + if(l2part_lens, iinf->n_parts*sizeof(int)); + if(p==NULL){ + reset_iteminfo(iinf); + }else{ + iinf->part_lens=p; + } + } + + if(iinf->part_lens!=NULL) + iinf->part_lens[i]=l2; +} + + +static void string_calc_parts(GrBrush *brush, int maxw, char *str, + WListingItemInfo *iinf) +{ + int wrapw=grbrush_get_text_width(brush, "\\", 1); + int ciw=grbrush_get_text_width(brush, CONT_INDENT, CONT_INDENT_LEN); + int i, s; + + iinf->n_parts=0; + iinf->len=strlen(str); + + if(maxw<=0) + reset_iteminfo(iinf); + else + string_do_calc_parts(brush, maxw, str, iinf->len, iinf, wrapw, ciw); +} + + +static void draw_multirow(GrBrush *brush, int x, int y, int h, + char *str, WListingItemInfo *iinf, + int maxw, int ciw, int wrapw, const char *style) +{ + int i, l; + + if(iinf==NULL){ + grbrush_draw_string(brush, x, y, str, strlen(str), TRUE, style); + return; + } + + assert(iinf->n_parts>=1); + if(iinf->part_lens==NULL){ + assert(iinf->n_parts==1); + l=iinf->len; + }else{ + l=iinf->part_lens[0]; + } + + grbrush_draw_string(brush, x, y, str, l, TRUE, style); + + for(i=1; in_parts; i++){ + grbrush_draw_string(brush, x+maxw-wrapw, y, "\\", 1, TRUE, style); + + y+=h; + str+=l; + if(i==1){ + x+=ciw; + maxw-=ciw; + } + l=iinf->part_lens[i]; + + grbrush_draw_string(brush, x, y, str, l, TRUE, style); + } +} + + +static int col_fit(int w, int itemw, int spacing) +{ + int ncol=1; + int tmp=w-itemw; + itemw+=spacing; + + if(tmp>0) + ncol+=tmp/itemw; + + return ncol; +} + +static bool one_row_up(WListing *l, int *ip, int *rp) +{ + int i=*ip, r=*rp; + int ir=ITEMROWS(l, i); + + if(r>0){ + (*rp)--; + return TRUE; + } + + if(i==0) + return FALSE; + + (*ip)--; + *rp=ITEMROWS(l, i-1)-1; + return TRUE; +} + + +static bool one_row_down(WListing *l, int *ip, int *rp) +{ + int i=*ip, r=*rp; + int ir=ITEMROWS(l, i); + + if(rnitemcol-1) + return FALSE; + + (*ip)++; + *rp=0; + return TRUE; +} + + +void setup_listing(WListing *l, char **strs, int nstrs, bool onecol) +{ + if(l->strs!=NULL) + deinit_listing(l); + + l->iteminfos=ALLOC_N(WListingItemInfo, nstrs); + l->strs=strs; + l->nstrs=nstrs; + l->onecol=onecol; + l->selected_str=-1; +} + + +void fit_listing(GrBrush *brush, const WRectangle *geom, WListing *l) +{ + int ncol, nrow=0, visrow=INT_MAX; + int i, maxw, w, h; + GrFontExtents fnte; + GrBorderWidths bdw; + + grbrush_get_font_extents(brush, &fnte); + grbrush_get_border_widths(brush, &bdw); + + w=geom->w-bdw.left-bdw.right; + h=geom->h-bdw.top-bdw.bottom; + + maxw=strings_maxw(brush, l->strs, l->nstrs); + l->itemw=maxw+COL_SPACING; + l->itemh=fnte.max_height; + + if(l->onecol) + ncol=1; + else + ncol=col_fit(w, l->itemw-COL_SPACING, COL_SPACING); + + if(l->iteminfos!=NULL){ + for(i=0; instrs; i++){ + if(ncol!=1){ + reset_iteminfo(&(l->iteminfos[i])); + l->iteminfos[i].len=strlen(l->strs[i]); + }else{ + string_calc_parts(brush, w, l->strs[i], &(l->iteminfos[i])); + } + nrow+=l->iteminfos[i].n_parts; + } + }else{ + nrow=l->nstrs; + } + + if(ncol>1){ + nrow=l->nstrs/ncol+(l->nstrs%ncol ? 1 : 0); + l->nitemcol=nrow; + }else{ + l->nitemcol=l->nstrs; + } + + if(l->itemh>0) + visrow=h/l->itemh; + + if(visrow>nrow) + visrow=nrow; + + l->ncol=ncol; + l->nrow=nrow; + l->visrow=visrow; + l->toth=visrow*l->itemh; + +#if 0 + l->firstitem=l->nitemcol-1; + l->firstoff=ITEMROWS(l, l->nitemcol-1)-1; + for(i=1; ifirstitem), &(l->firstoff)); +#else + l->firstitem=0; + l->firstoff=0; +#endif + +} + + +void deinit_listing(WListing *l) +{ + int i; + + if(l->strs==NULL) + return; + + while(l->nstrs--){ + free(l->strs[l->nstrs]); + if(l->iteminfos!=NULL) + reset_iteminfo(&(l->iteminfos[l->nstrs])); + } + + free(l->strs); + l->strs=NULL; + + if(l->iteminfos!=NULL){ + free(l->iteminfos); + l->iteminfos=NULL; + } +} + + +void init_listing(WListing *l) +{ + l->nstrs=0; + l->strs=NULL; + l->iteminfos=NULL; + l->nstrs=0; + l->selected_str=-1; + l->onecol=TRUE; + l->itemw=0; + l->itemh=0; + l->ncol=0; + l->nrow=0; + l->nitemcol=0; + l->visrow=0; + l->toth=0; +} + + +static void do_draw_listing(GrBrush *brush, const WRectangle *geom, + WListing *l, const char *style, + const char *selstyle) +{ + int wrapw=grbrush_get_text_width(brush, "\\", 1); + int ciw=grbrush_get_text_width(brush, CONT_INDENT, CONT_INDENT_LEN); + int r, c, i, x, y; + GrFontExtents fnte; + + if(l->nitemcol==0 || l->visrow==0) + return; + + grbrush_get_font_extents(brush, &fnte); + + x=0; + c=0; + while(1){ + y=geom->y+fnte.baseline; + i=l->firstitem+c*l->nitemcol; + r=-l->firstoff; + y+=r*l->itemh; + while(rvisrow){ + if(i>=l->nstrs) + return; + + draw_multirow(brush, geom->x+x, y, l->itemh, l->strs[i], + (l->iteminfos!=NULL ? &(l->iteminfos[i]) : NULL), + geom->w-x, ciw, wrapw, + (i==l->selected_str ? selstyle : style)); + + y+=l->itemh*ITEMROWS(l, i); + r+=ITEMROWS(l, i); + i++; + } + x+=l->itemw; + c++; + } +} + + +void draw_listing(GrBrush *brush, const WRectangle *geom, + WListing *l, bool complete, const char *style, + const char *selstyle) +{ + WRectangle geom2; + GrBorderWidths bdw; + + grbrush_begin(brush, geom, GRBRUSH_AMEND|GRBRUSH_NEED_CLIP); + + if(complete) + grbrush_clear_area(brush, geom); + + grbrush_draw_border(brush, geom, style); + + grbrush_get_border_widths(brush, &bdw); + + geom2.x=geom->x+bdw.left; + geom2.y=geom->y+bdw.top; + geom2.w=geom->w-bdw.left-bdw.right; + geom2.h=geom->h-bdw.top-bdw.bottom; + + do_draw_listing(brush, &geom2, l, style, selstyle); + + grbrush_end(brush); +} + + +static bool do_scrollup_listing(WListing *l, int n) +{ + int i=l->firstitem; + int r=l->firstoff; + bool ret=FALSE; + + while(n>0){ + if(!one_row_up(l, &i, &r)) + break; + ret=TRUE; + n--; + } + + l->firstitem=i; + l->firstoff=r; + + return ret; +} + + +static bool do_scrolldown_listing(WListing *l, int n) +{ + int i=l->firstitem; + int r=l->firstoff; + int br=r, bi=i; + int bc=l->visrow; + bool ret=FALSE; + + while(--bc>0) + one_row_down(l, &bi, &br); + + while(n>0){ + if(!one_row_down(l, &bi, &br)) + break; + one_row_down(l, &i, &r); + ret=TRUE; + n--; + } + + l->firstitem=i; + l->firstoff=r; + + return ret; +} + + +bool scrollup_listing(WListing *l) +{ + return do_scrollup_listing(l, l->visrow); +} + + +bool scrolldown_listing(WListing *l) +{ + return do_scrolldown_listing(l, l->visrow); +} + + +static int listing_first_row_of_item(WListing *l, int i) +{ + int fci=i%l->nitemcol, j; + int r=0; + + for(j=0; jfirstitem)+l->firstoff; +} + + +bool listing_select(WListing *l, int i) +{ + int irow, frow, lrow; + bool complredraw=FALSE; + + if(i<0){ + l->selected_str=-1; + return FALSE; + } + + assert(instrs); + + l->selected_str=i; + + /* Adjust visible area */ + + irow=listing_first_row_of_item(l, i); + frow=listing_first_visible_row(l); + + while(irowfirstitem), &(l->firstoff)); + frow--; + complredraw=TRUE; + } + + irow+=ITEMROWS(l, i)-1; + lrow=frow+l->visrow-1; + + while(irow>lrow){ + one_row_down(l, &(l->firstitem), &(l->firstoff)); + lrow++; + complredraw=TRUE; + } + + return complredraw; +} + diff --git a/mod_query/listing.h b/mod_query/listing.h new file mode 100644 index 0000000..457ec08 --- /dev/null +++ b/mod_query/listing.h @@ -0,0 +1,50 @@ +/* + * ion/mod_query/listing.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_MOD_QUERY_LISTING_H +#define ION_MOD_QUERY_LISTING_H + +#include +#include +#include + +INTRSTRUCT(WListing); +INTRSTRUCT(WListingItemInfo); + +DECLSTRUCT(WListingItemInfo){ + int len; + int n_parts; + int *part_lens; +}; + +DECLSTRUCT(WListing){ + char **strs; + WListingItemInfo *iteminfos; + int nstrs; + int selected_str; + int ncol, nrow, nitemcol, visrow; + int firstitem, firstoff; + int itemw, itemh, toth; + bool onecol; +}; + +extern void init_listing(WListing *l); +extern void setup_listing(WListing *l, char **strs, int nstrs, bool onecol); +extern void deinit_listing(WListing *l); +extern void fit_listing(GrBrush *brush, const WRectangle *geom, WListing *l); +extern void draw_listing(GrBrush *brush, const WRectangle *geom, WListing *l, + bool complete, const char *style, + const char *selstyle); +extern bool scrollup_listing(WListing *l); +extern bool scrolldown_listing(WListing *l); +extern bool listing_select(WListing *l, int i); + +#endif /* ION_MOD_QUERY_LISTING_H */ diff --git a/mod_query/main.c b/mod_query/main.c new file mode 100644 index 0000000..9eb7323 --- /dev/null +++ b/mod_query/main.c @@ -0,0 +1,191 @@ +/* + * ion/mod_query/main.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "query.h" +#include "edln.h" +#include "wedln.h" +#include "input.h" +#include "complete.h" +#include "history.h" +#include "exports.h" +#include "main.h" + + +/*{{{ Module information */ + + +#include "../version.h" + +char mod_query_ion_api_version[]=ION_API_VERSION; + + +/*}}}*/ + + +/*{{{ Bindmaps */ + + +WBindmap *mod_query_input_bindmap=NULL; +WBindmap *mod_query_wedln_bindmap=NULL; + + +/*}}}*/ + + +/*{{{ Set & get */ + + +ModQueryConfig mod_query_config={ + 250, + TRUE +}; + + +/*EXTL_DOC + * Set module configuration. The following are supported: + * + * \begin{tabularx}{\linewidth}{lX} + * \tabhead{Field & Description} + * \var{autoshowcompl} & (boolean) Is auto-show-completions enabled? + * (default: true). \\ + * \var{autoshowcompl_delay} & (integer) auto-show-completions delay + * in milliseconds (default: 250). \\ + * \end{tabularx} + */ +EXTL_EXPORT +void mod_query_set(ExtlTab tab) +{ + ModQueryConfig *c=&mod_query_config; + + extl_table_gets_b(tab, "autoshowcompl", &c->autoshowcompl); + + if(extl_table_gets_i(tab, "autoshowcompl_delay", + &c->autoshowcompl_delay)){ + c->autoshowcompl_delay=maxof(c->autoshowcompl_delay, 0); + } +} + +/*EXTL_DOC + * Get module configuration. For more information see + * \fnref{mod_query.set}. + */ +EXTL_EXPORT +ExtlTab mod_query_get() +{ + ModQueryConfig *c=&mod_query_config; + ExtlTab tab=extl_create_table(); + + extl_table_sets_b(tab, "autoshowcompl", c->autoshowcompl); + extl_table_sets_i(tab, "autoshowcompl_delay", c->autoshowcompl_delay); + + return tab; +} + + +/*}}}*/ + + +/*{{{ Init & deinit */ + + +static void load_history() +{ + ExtlTab tab; + int i, n; + + if(!extl_read_savefile("saved_queryhist", &tab)) + return; + + n=extl_table_get_n(tab); + + for(i=n; i>=1; i--){ + char *s=NULL; + if(extl_table_geti_s(tab, i, &s)){ + mod_query_history_push(s); + free(s); + } + } + + extl_unref_table(tab); +} + + +static void save_history() +{ + ExtlTab tab=mod_query_history_table(); + + extl_write_savefile("saved_queryhist", tab); + + extl_unref_table(tab); +} + + +static bool loaded_ok=FALSE; + +void mod_query_deinit() +{ + mod_query_unregister_exports(); + + if(mod_query_input_bindmap!=NULL){ + ioncore_free_bindmap("WInput", mod_query_input_bindmap); + mod_query_input_bindmap=NULL; + } + + if(mod_query_wedln_bindmap!=NULL){ + ioncore_free_bindmap("WEdln", mod_query_wedln_bindmap); + mod_query_wedln_bindmap=NULL; + } + + hook_remove(ioncore_snapshot_hook, save_history); +} + + +bool mod_query_init() +{ + if(!mod_query_register_exports()) + goto err; + + mod_query_input_bindmap=ioncore_alloc_bindmap("WInput", NULL); + mod_query_wedln_bindmap=ioncore_alloc_bindmap("WEdln", NULL); + + if(mod_query_wedln_bindmap==NULL || + mod_query_input_bindmap==NULL){ + goto err; + } + + /*ioncore_read_config("cfg_query", NULL, TRUE);*/ + + load_history(); + + loaded_ok=TRUE; + + hook_add(ioncore_snapshot_hook, save_history); + + return TRUE; + +err: + mod_query_deinit(); + return FALSE; +} + + +/*}}}*/ + diff --git a/mod_query/main.h b/mod_query/main.h new file mode 100644 index 0000000..2d57b13 --- /dev/null +++ b/mod_query/main.h @@ -0,0 +1,28 @@ +/* + * ion/mod_query/main.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_MOD_QUERY_MAIN_H +#define ION_MOD_QUERY_MAIN_H + +extern bool mod_query_init(); +extern void mod_query_deinit(); + +INTRSTRUCT(ModQueryConfig); + +DECLSTRUCT(ModQueryConfig){ + int autoshowcompl_delay; + bool autoshowcompl; +}; + + +extern ModQueryConfig mod_query_config; + +#endif /* ION_MOD_QUERY_MAIN_H */ diff --git a/mod_query/mod_query.lua b/mod_query/mod_query.lua new file mode 100644 index 0000000..0f51738 --- /dev/null +++ b/mod_query/mod_query.lua @@ -0,0 +1,1217 @@ +-- +-- ion/query/mod_query.lua -- Some common queries for Ion +-- +-- Copyright (c) Tuomo Valkonen 2004-2006. +-- +-- Ion is free software; you can redistribute it and/or modify it under +-- the terms of the GNU Lesser General Public License as published by +-- the Free Software Foundation; either version 2.1 of the License, or +-- (at your option) any later version. +-- + + +-- This is a slight abuse of the package.loaded variable perhaps, but +-- library-like packages should handle checking if they're loaded instead of +-- confusing the user with require/include differences. +if package.loaded["mod_query"] then return end + +if not ioncore.load_module("mod_query") then + return +end + +local mod_query=_G["mod_query"] + +assert(mod_query) + + +local DIE_TIMEOUT_ERRORCODE=10 -- 10 seconds +local DIE_TIMEOUT_NO_ERRORCODE=2 -- 2 seconds + + +-- Generic helper functions {{{ + + +function mod_query.make_completor(completefn) + local function completor(cp, str, point) + cp:set_completions(completefn(str, point)) + end + return completor +end + + +--DOC +-- Low-level query routine. \var{mplex} is the \type{WMPlex} to display +-- the query in, \var{prompt} the prompt string, and \var{initvalue} +-- the initial contents of the query box. \var{handler} is a function +-- that receives (\var{mplex}, result string) as parameter when the +-- query has been succesfully completed, \var{completor} the completor +-- routine which receives a (\var{cp}, \var{str}, \var{point}) as parameters. +-- The parameter \var{str} is the string to be completed and \var{point} +-- cursor's location within it. Completions should be eventually, +-- possibly asynchronously, set with \fnref{WComplProxy.set_completions} +-- on \var{cp}. +function mod_query.query(mplex, prompt, initvalue, handler, completor, + context) + local function handle_it(str) + handler(mplex, str) + end + local function cycle(wedln) + wedln:complete('next', 'normal') + end + + -- Check that no other queries are open in the mplex. + local l=mplex:managed_list() + for i, r in pairs(l) do + if obj_is(r, "WEdln") then + return + end + end + local wedln=mod_query.do_query(mplex, prompt, initvalue, + handle_it, completor, cycle) + if context then + wedln:set_context(context) + end + + return wedln +end + + +--DOC +-- This function query will display a query with prompt \var{prompt} in +-- \var{mplex} and if the user answers affirmately, call \var{handler} +-- with \var{mplex} as parameter. +function mod_query.query_yesno(mplex, prompt, handler) + local function handler_yesno(mplex, str) + if str=="y" or str=="Y" or str=="yes" then + handler(mplex) + end + end + return mod_query.query(mplex, prompt, nil, handler_yesno, nil, + "yesno") +end + + +local errdata={} + +local function maybe_finish(pid) + local t=errdata[pid] + + if t and t.closed and t.dietime then + errdata[pid]=nil + local tmd=os.difftime(t.dietime, t.starttime) + --if tmd=2 then + pipes[rcv]=nil + return + end + pst.maybe_stalled=0 + + totallen=totallen+string.len(str) + if totallen>ioncore.RESULT_DATA_LIMIT then + error(TR("Too much result data")) + end + + + data=string.gsub(data..str, "([^\n]*)\n", + function(s) + reshnd(results, s) + lines=lines+1 + return "" + end) + + if lines>mod_query.COLLECT_THRESHOLD then + collectgarbage() + lines=0 + end + + str=coroutine.yield() + end + + if not results.common_beg then + results.common_beg=beg + end + + (fn or WComplProxy.set_completions)(cp, results) + + pipes[rcv]=nil + results={} + + collectgarbage() + end + + local found_clean=false + + for k, v in pairs(pipes) do + if v.cp==cp then + if v.maybe_stalled<2 then + v.maybe_stalled=v.maybe_stalled+1 + found_clean=true + end + end + end + + if not found_clean then + pipes[rcv]=pst + ioncore.popen_bgread(cmd, coroutine.wrap(rcv)) + end +end + + +-- }}} + + +-- Simple queries for internal actions {{{ + + +function mod_query.call_warn(mplex, fn) + local err = collect_errors(fn) + if err then + mod_query.warn(mplex, err) + end + return err +end + + +function mod_query.complete_name(str, list) + local entries={} + local l=string.len(str) + for i, reg in pairs(list) do + local nm=reg:name() + if nm and string.sub(nm, 1, l)==str then + table.insert(entries, nm) + end + end + if #entries==0 then + for i, reg in pairs(list) do + local nm=reg:name() + if nm and string.find(nm, str, 1, true) then + table.insert(entries, nm) + end + end + end + return entries +end + +function mod_query.complete_clientwin(str) + return mod_query.complete_name(str, ioncore.clientwin_list()) +end + +function mod_query.complete_workspace(str) + return mod_query.complete_name(str, ioncore.region_list("WGroupWS")) +end + +function mod_query.complete_region(str) + return mod_query.complete_name(str, ioncore.region_list()) +end + + +function mod_query.gotoclient_handler(frame, str) + local cwin=ioncore.lookup_clientwin(str) + + if cwin==nil then + mod_query.warn(frame, TR("Could not find client window %s.", str)) + else + cwin:goto() + end +end + +function mod_query.attachclient_handler(frame, str) + local cwin=ioncore.lookup_clientwin(str) + + if not cwin then + mod_query.warn(frame, TR("Could not find client window %s.", str)) + return + end + + local reg=cwin:manager() + local attach + + if not obj_is(reg, "WGroupCW") then + reg = cwin + attach = function() + frame:attach_new { + type = "WGroupCW", + switchto = true, + managed = {{ reg = cwin, bottom = true }} + } + end + else + attach = function() + frame:attach(reg, { switchto = true }) + end + end + + if frame:rootwin_of()~=reg:rootwin_of() then + mod_query.warn(frame, TR("Cannot attach: different root windows.")) + elseif reg:manager()==frame then + reg:goto() + else + mod_query.call_warn(frame, attach) + end +end + + +function mod_query.workspace_handler(mplex, name) + local ws=ioncore.lookup_region(name, "WGroupWS") + if ws then + ws:goto() + return + end + + local scr=mplex:screen_of() + + local function mkws() + if not ioncore.create_ws(scr, {name=name}) then + error(TR("Unknown error")) + end + end + + mod_query.call_warn(mplex, mkws) +end + + +--DOC +-- This query asks for the name of a client window and attaches +-- it to the frame the query was opened in. It uses the completion +-- function \fnref{ioncore.complete_clientwin}. +function mod_query.query_gotoclient(mplex) + mod_query.query(mplex, TR("Go to window:"), nil, + mod_query.gotoclient_handler, + mod_query.make_completor(mod_query.complete_clientwin), + "windowname") +end + +--DOC +-- This query asks for the name of a client window and switches +-- focus to the one entered. It uses the completion function +-- \fnref{ioncore.complete_clientwin}. +function mod_query.query_attachclient(mplex) + mod_query.query(mplex, TR("Attach window:"), nil, + mod_query.attachclient_handler, + mod_query.make_completor(mod_query.complete_clientwin), + "windowname") +end + + +--DOC +-- This query asks for the name of a workspace. If a workspace +-- (an object inheriting \type{WGroupWS}) with such a name exists, +-- it will be switched to. Otherwise a new workspace with the +-- entered name will be created and the user will be queried for +-- the type of the workspace. +function mod_query.query_workspace(mplex) + mod_query.query(mplex, TR("Go to or create workspace:"), nil, + mod_query.workspace_handler, + mod_query.make_completor(mod_query.complete_workspace), + "workspacename") +end + + +--DOC +-- This query asks whether the user wants to exit Ion (no session manager) +-- or close the session (running under a session manager that supports such +-- requests). If the answer is 'y', 'Y' or 'yes', so will happen. +function mod_query.query_shutdown(mplex) + mod_query.query_yesno(mplex, TR("Exit Ion/Shutdown session (y/n)?"), + ioncore.shutdown) +end + + +--DOC +-- This query asks whether the user wants restart Ioncore. +-- If the answer is 'y', 'Y' or 'yes', so will happen. +function mod_query.query_restart(mplex) + mod_query.query_yesno(mplex, TR("Restart Ion (y/n)?"), ioncore.restart) +end + + +--DOC +-- This function asks for a name new for the frame where the query +-- was created. +function mod_query.query_renameframe(frame) + mod_query.query(frame, TR("Frame name:"), frame:name(), + function(frame, str) frame:set_name(str) end, + nil, "framename") +end + + +--DOC +-- This function asks for a name new for the workspace on which the +-- query resides. +function mod_query.query_renameworkspace(mplex) + local ws=ioncore.find_manager(mplex, "WGroupWS") + mod_query.query(mplex, TR("Workspace name:"), ws:name(), + function(mplex, str) ws:set_name(str) end, + nil, "framename") +end + + +-- }}} + + +-- Run/view/edit {{{ + + +--DOC +-- Asks for a file to be edited. This script uses +-- \command{run-mailcap --mode=edit} by default, but you may provide an +-- alternative script to use. The default prompt is "Edit file:" (translated). +function mod_query.query_editfile(mplex, script, prompt) + mod_query.query_execfile(mplex, + prompt or TR("Edit file:"), + script or "run-mailcap --action=edit") +end + + +--DOC +-- Asks for a file to be viewed. This script uses +-- \command{run-mailcap --action=view} by default, but you may provide an +-- alternative script to use. The default prompt is "View file:" (translated). +function mod_query.query_runfile(mplex, script, prompt) + mod_query.query_execfile(mplex, + prompt or TR("View file:"), + script or "run-mailcap --action=view") + +end + + +local function isspace(s) + return string.find(s, "^%s*$")~=nil +end + + +local function break_cmdline(str, no_ws) + local st, en, beg, rest, ch, rem + local res={""} + + local function ins(str) + local n=#res + if string.find(res[n], "^%s+$") then + table.insert(res, str) + else + res[n]=res[n]..str + end + end + + local function ins_space(str) + local n=#res + if no_ws then + if res[n]~="" then + table.insert(res, "") + end + else + if isspace(res[n]) then + res[n]=res[n]..str + else + table.insert(res, str) + end + end + end + + -- Handle terminal startup syntax + st, en, beg, ch, rest=string.find(str, "^(%s*)(:+)(.*)") + if beg then + if string.len(beg)>0 then + ins_space(beg) + end + ins(ch) + ins_space("") + str=rest + end + + while str~="" do + st, en, beg, rest, ch=string.find(str, "^(.-)(([%s'\"\\|])(.*))") + if not beg then + ins(str) + break + end + + ins(beg) + str=rest + + local sp=false + + if ch=="\\" then + st, en, beg, rest=string.find(str, "^(\\.)(.*)") + elseif ch=='"' then + st, en, beg, rest=string.find(str, "^(\".-[^\\]\")(.*)") + + if not beg then + st, en, beg, rest=string.find(str, "^(\"\")(.*)") + end + elseif ch=="'" then + st, en, beg, rest=string.find(str, "^('.-')(.*)") + else + if ch=='|' then + ins_space('') + ins(ch) + else -- ch==' ' + ins_space(ch) + end + st, en, beg, rest=string.find(str, "^.(%s*)(.*)") + assert(beg and rest) + ins_space(beg) + sp=true + str=rest + end + + if not sp then + if not beg then + beg=str + rest="" + end + ins(beg) + str=rest + end + end + + return res +end + + +local function unquote(str) + str=string.gsub(str, "^['\"]", "") + str=string.gsub(str, "([^\\])['\"]", "%1") + str=string.gsub(str, "\\(.)", "%1") + return str +end + + +local function quote(str) + return string.gsub(str, "([%(%)\"'\\%*%?%[%]%| ])", "\\%1") +end + + +local function find_point(strs, point) + for i, s in ipairs(strs) do + point=point-string.len(s) + if point<=1 then + return i + end + end + return #strs +end + + +function mod_query.exec_completor(wedln, str, point) + local parts=break_cmdline(str) + local complidx=find_point(parts, point+1) + + local s_compl, s_beg, s_end="", "", "" + + if complidx==1 and string.find(parts[1], "^:+$") then + complidx=complidx+1 + end + + if string.find(parts[complidx], "[^%s]") then + s_compl=unquote(parts[complidx]) + end + + for i=1, complidx-1 do + s_beg=s_beg..parts[i] + end + + for i=complidx+1, #parts do + s_end=s_end..parts[i] + end + + local wp=" " + if complidx==1 or (complidx==2 and isspace(parts[1])) then + wp=" -wp " + elseif string.find(parts[1], "^:+$") then + if complidx==2 then + wp=" -wp " + elseif string.find(parts[2], "^%s*$") then + if complidx==3 then + wp=" -wp " + end + end + end + + local function set_fn(cp, res) + res=table.map(quote, res) + res.common_beg=s_beg..(res.common_beg or "") + res.common_end=(res.common_end or "")..s_end + cp:set_completions(res) + end + + local function filter_fn(res, s) + if not res.common_beg then + if s=="./" then + res.common_beg="" + else + res.common_beg=s + end + else + table.insert(res, s) + end + end + + local ic=ioncore.lookup_script("ion-completefile") + if ic then + mod_query.popen_completions(wedln, + ic..wp..string.shell_safe(s_compl), + set_fn, filter_fn) + end +end + + +local cmd_overrides={} + + +--DOC +-- Define a command override for the \fnrefx{mod_query}{query_exec} query. +function mod_query.defcmd(cmd, fn) + cmd_overrides[cmd]=fn +end + + +function mod_query.exec_handler(mplex, cmdline) + local parts=break_cmdline(cmdline, true) + local cmd=table.remove(parts, 1) + + if cmd_overrides[cmd] then + cmd_overrides[cmd](mplex, table.map(unquote, parts)) + elseif cmd~="" then + mod_query.exec_on_merr(mplex, cmdline) + end +end + + +--DOC +-- This function asks for a command to execute with \file{/bin/sh}. +-- If the command is prefixed with a colon (':'), the command will +-- be run in an XTerm (or other terminal emulator) using the script +-- \file{ion-runinxterm}. Two colons ('::') will ask you to press +-- enter after the command has finished. +function mod_query.query_exec(mplex) + mod_query.query(mplex, TR("Run:"), nil, mod_query.exec_handler, + mod_query.exec_completor, + "run") +end + + +-- }}} + + +-- SSH {{{ + + +mod_query.known_hosts={} + + +function mod_query.get_known_hosts(mplex) + mod_query.known_hosts={} + local f + local h=os.getenv("HOME") + if h then + f=io.open(h.."/.ssh/known_hosts") + end + if not f then + warn(TR("Failed to open ~/.ssh/known_hosts")) + return + end + for l in f:lines() do + local st, en, hostname=string.find(l, "^([^%s,]+)") + if hostname then + table.insert(mod_query.known_hosts, hostname) + end + end + f:close() +end + + +mod_query.hostnicks={} + +function mod_query.get_hostnicks(mplex) + mod_query.hostnicks={} + local f + local substr, pat, patterns + local h=os.getenv("HOME") + + if h then + f=io.open(h.."/.ssh/config") + end + if not f then + warn(TR("Failed to open ~/.ssh/config")) + return + end + + for l in f:lines() do + _, _, substr=string.find(l, "^%s*[hH][oO][sS][tT](.*)") + if substr then + _, _, pat=string.find(substr, "^%s*[=%s]%s*(%S.*)") + if pat then + patterns=pat + elseif string.find(substr, "^[nN][aA][mM][eE]") + and patterns then + for s in string.gfind(patterns, "%S+") do + if not string.find(s, "[*?]") then + table.insert(mod_query.hostnicks, s) + end + end + end + end + end + f:close() +end + + +function mod_query.complete_ssh(str) + local st, en, user, at, host=string.find(str, "^([^@]*)(@?)(.*)$") + + if string.len(at)==0 and string.len(host)==0 then + host = user; user = "" + end + + if at=="@" then + user = user .. at + end + + local res = {} + + if string.len(host)==0 then + if string.len(user)==0 then + return mod_query.ssh_completions + end + + for _, v in ipairs(mod_query.ssh_completions) do + table.insert(res, user .. v) + end + return res + end + + for _, v in ipairs(mod_query.ssh_completions) do + local s, e=string.find(v, host, 1, true) + if s==1 and e>=1 then + table.insert(res, user .. v) + end + end + + return res +end + +mod_query.ssh_completions={} + +--DOC +-- This query asks for a host to connect to with SSH. +-- Hosts to tab-complete are read from \file{\~{}/.ssh/known\_hosts}. +function mod_query.query_ssh(mplex, ssh) + mod_query.get_known_hosts(mplex) + mod_query.get_hostnicks(mplex) + + for _, v in ipairs(mod_query.known_hosts) do + table.insert(mod_query.ssh_completions, v) + end + for _, v in ipairs(mod_query.hostnicks) do + table.insert(mod_query.ssh_completions, v) + end + + ssh=(ssh or ":ssh") + + local function handle_exec(mplex, str) + if not (str and string.find(str, "[^%s]")) then + return + end + + mod_query.exec_on_merr(mplex, ssh.." "..string.shell_safe(str)) + end + + return mod_query.query(mplex, TR("SSH to:"), nil, handle_exec, + mod_query.make_completor(mod_query.complete_ssh), + "ssh") +end + +-- }}} + + +-- Man pages {{{{ + + +function mod_query.man_completor(wedln, str) + local mc=ioncore.lookup_script("ion-completeman") + if mc then + mod_query.popen_completions(wedln, (mc.." -complete " + ..string.shell_safe(str))) + end +end + + +--DOC +-- This query asks for a manual page to display. By default it runs the +-- \command{man} command in an \command{xterm} using \command{ion-runinxterm}, +-- but it is possible to pass another program as the \var{prog} argument. +function mod_query.query_man(mplex, prog) + local dflt=ioncore.progname() + mod_query.query_execwith(mplex, TR("Manual page (%s):", dflt), + dflt, prog or ":man", + mod_query.man_completor, "man", + true --[[ no quoting ]]) +end + + +-- }}} + + +-- Lua code execution {{{ + + +function mod_query.create_run_env(mplex) + local origenv=getfenv() + local meta={__index=origenv, __newindex=origenv} + local env={ + _=mplex, + _sub=mplex:current(), + print=my_print + } + setmetatable(env, meta) + return env +end + +function mod_query.do_handle_lua(mplex, env, code) + local print_res + local function collect_print(...) + local tmp="" + local l=#arg + for i=1,l do + tmp=tmp..tostring(arg[i])..(i==l and "\n" or "\t") + end + print_res=(print_res and print_res..tmp or tmp) + end + + local f, err=loadstring(code) + if not f then + mod_query.warn(mplex, err) + return + end + + env.print=collect_print + setfenv(f, env) + + err=collect_errors(f) + if err then + mod_query.warn(mplex, err) + elseif print_res then + mod_query.message(mplex, print_res) + end +end + +local function getindex(t) + local mt=getmetatable(t) + if mt then return mt.__index end + return nil +end + +function mod_query.do_complete_lua(env, str) + -- Get the variable to complete, including containing tables. + -- This will also match string concatenations and such because + -- Lua's regexps don't support optional subexpressions, but we + -- handle them in the next step. + local comptab=env + local metas=true + local _, _, tocomp=string.find(str, "([%w_.:]*)$") + + -- Descend into tables + if tocomp and string.len(tocomp)>=1 then + for t in string.gfind(tocomp, "([^.:]*)[.:]") do + metas=false + if string.len(t)==0 then + comptab=env; + elseif comptab then + if type(comptab[t])=="table" then + comptab=comptab[t] + elseif type(comptab[t])=="userdata" then + comptab=getindex(comptab[t]) + metas=true + else + comptab=nil + end + end + end + end + + if not comptab then return {} end + + local compl={} + + -- Get the actual variable to complete without containing tables + _, _, compl.common_beg, tocomp=string.find(str, "(.-)([%w_]*)$") + + local l=string.len(tocomp) + + local tab=comptab + local seen={} + while true do + if type(tab) == "table" then + for k in pairs(tab) do + if type(k)=="string" then + if string.sub(k, 1, l)==tocomp then + table.insert(compl, k) + end + end + end + end + + -- We only want to display full list of functions for objects, not + -- the tables representing the classes. + --if not metas then break end + + seen[tab]=true + tab=getindex(tab) + if not tab or seen[tab] then break end + end + + -- If there was only one completion and it is a string or function, + -- concatenate it with "." or "(", respectively. + if #compl==1 then + if type(comptab[compl[1]])=="table" then + compl[1]=compl[1] .. "." + elseif type(comptab[compl[1]])=="function" then + compl[1]=compl[1] .. "(" + end + end + + return compl +end + + +--DOC +-- This query asks for Lua code to execute. It sets the variable '\var{\_}' +-- in the local environment of the string to point to the mplex where the +-- query was created. It also sets the table \var{arg} in the local +-- environment to \code{\{_, _:current()\}}. +function mod_query.query_lua(mplex) + local env=mod_query.create_run_env(mplex) + + local function complete(cp, code) + cp:set_completions(mod_query.do_complete_lua(env, code)) + end + + local function handler(mplex, code) + return mod_query.do_handle_lua(mplex, env, code) + end + + mod_query.query(mplex, TR("Lua code:"), nil, handler, complete, "lua") +end + +-- }}} + + +-- Menu query {{{ + +--DOC +-- This query can be used to create a query of a defined menu. +function mod_query.query_menu(mplex, themenu, prompt) + local _sub=mplex:current() + local menu=ioncore.evalmenu(themenu, {mplex, _sub}) + local menuname=(type(themenu)=="string" and themenu or "?") + + if not menu then + mod_query.warn(mplex, TR("Unknown menu %s.", tostring(themenu))) + return + end + + if not prompt then + prompt=menuname..":" + else + prompt=TR(prompt) + end + + local function xform_name(n, is_submenu) + return (string.lower(string.gsub(n, "[-%s]+", "-")) + ..(is_submenu and "/" or "")) + end + + local function xform_menu(t, m, p) + for _, v in ipairs(m) do + if v.name then + local is_submenu=v.submenu_fn + local n=p..xform_name(v.name, is_submenu) + while t[n] do + n=n.."'" + end + t[n]=v + if is_submenu and not v.noautoexpand then + local sm=v.submenu_fn() + if sm then + xform_menu(t, sm, n) + else + ioncore.warn_traced(TR("Missing submenu ") + ..(v.name or "")) + end + end + end + end + return t + end + + local ntab=xform_menu({}, menu, "") + + local function complete(str) + local results={} + for s, e in pairs(ntab) do + if string.find(s, str, 1, true) then + table.insert(results, s) + end + end + return results + end + + local function handle(mplex, str) + local e=ntab[str] + if e then + if e.func then + local err=collect_errors(function() + e.func(mplex, _sub) + end) + if err then + mod_query.warn(mplex, err) + end + elseif e.submenu_fn then + mod_query.query_menu(mplex, e.submenu_fn(), + TR("%s:", e.name)) + end + else + mod_query.warn(mplex, TR("No entry '%s'", str)) + end + end + + mod_query.query(mplex, prompt, nil, handle, + mod_query.make_completor(complete), "menu."..menuname) +end + +-- }}} + + +-- Miscellaneous {{{ + + +--DOC +-- Display an "About Ion" message in \var{mplex}. +function mod_query.show_about_ion(mplex) + mod_query.message(mplex, ioncore.aboutmsg()) +end + + +--DOC +-- Show information about a region tree +function mod_query.show_tree(mplex, reg, max_depth) + local function indent(s) + local i=" " + return i..string.gsub(s, "\n", "\n"..i) + end + + local function get_info(reg, indent, d) + if not reg then + return (indent .. "No region") + end + + local function n(s) return (s or "") end + + local s=string.format("%s%s \"%s\"", indent, obj_typename(reg), + n(reg:name())) + indent = indent .. " " + if obj_is(reg, "WClientWin") then + local i=reg:get_ident() + s=s .. TR("\n%sClass: %s\n%sRole: %s\n%sInstance: %s\n%sXID: 0x%x", + indent, n(i.class), + indent, n(i.role), + indent, n(i.instance), + indent, reg:xid()) + end + + if (not max_depth or max_depth > d) and reg.managed_list then + local mgd=reg:managed_list() + if #mgd > 0 then + s=s .. "\n" .. indent .. "---" + for k, v in pairs(mgd) do + s=s .. "\n" .. get_info(v, indent, d+1) + end + end + end + + return s + end + + mod_query.message(mplex, get_info(reg, "", 0)) +end + +-- }}} + +-- Load extras +dopath('mod_query_chdir') + +-- Mark ourselves loaded. +package.loaded["mod_query"]=true + + +-- Load configuration file +dopath('cfg_query', true) diff --git a/mod_query/mod_query_chdir.lua b/mod_query/mod_query_chdir.lua new file mode 100644 index 0000000..3b408d2 --- /dev/null +++ b/mod_query/mod_query_chdir.lua @@ -0,0 +1,26 @@ +-- +-- ion/query/mod_query_chdir.lua +-- +-- Copyright (c) Tuomo Valkonen 2004-2006. +-- +-- Ion is free software; you can redistribute it and/or modify it under +-- the terms of the GNU Lesser General Public License as published by +-- the Free Software Foundation; either version 2.1 of the License, or +-- (at your option) any later version. +-- + +local function ws_chdir(mplex, params) + ws=assert(ioncore.find_manager(mplex, "WGroupWS")) + local ok, err=ioncore.chdir_for(ws, params[1] or "") + if not ok then + mod_query.warn(mplex, err) + end +end + +local function ws_showdir(mplex, params) + local dir=assert(ioncore.get_dir_for(mplex) or os.getenv("PWD")) + mod_query.message(mplex, dir) +end + +mod_query.defcmd("cd", ws_chdir) +mod_query.defcmd("pwd", ws_showdir) diff --git a/mod_query/query.c b/mod_query/query.c new file mode 100644 index 0000000..b56c087 --- /dev/null +++ b/mod_query/query.c @@ -0,0 +1,94 @@ +/* + * ion/mod_query/query.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include + +#include +#include +#include +#include +#include +#include "query.h" +#include "wedln.h" + + +static void create_cycle_binding(WEdln *wedln, XKeyEvent *ev, ExtlFn cycle) +{ + WBindmap *bindmap=create_bindmap(); + WBinding b; + + if(bindmap==NULL) + return; + + b.ksb=XKeycodeToKeysym(ioncore_g.dpy, ev->keycode, 0); + b.kcb=ev->keycode; + b.state=ev->state; + b.act=BINDING_KEYPRESS; + b.area=0; + b.wait=FALSE; + b.submap=NULL; + b.func=extl_ref_fn(cycle); + + if(!bindmap_add_binding(bindmap, &b)){ + extl_unref_fn(b.func); + bindmap_destroy(bindmap); + return; + } + + if(!region_add_bindmap((WRegion*)wedln, bindmap)){ + bindmap_destroy(bindmap); + return; + } + + wedln->cycle_bindmap=bindmap; +} + + +/*--lowlevel routine not to be called by the user--EXTL_DOC + * Show a query window in \var{mplex} with prompt \var{prompt}, initial + * contents \var{dflt}. The function \var{handler} is called with + * the entered string as the sole argument when \fnref{WEdln.finish} + * is called. The function \var{completor} is called with the created + * \type{WEdln} is first argument and the string to complete is the + * second argument when \fnref{WEdln.complete} is called. + */ +EXTL_EXPORT +WEdln *mod_query_do_query(WMPlex *mplex, const char *prompt, const char *dflt, + ExtlFn handler, ExtlFn completor, ExtlFn cycle) +{ + WRectangle geom; + WEdlnCreateParams fnp; + WMPlexAttachParams par; + XKeyEvent *ev=ioncore_current_key_event(); + WEdln *wedln; + + fnp.prompt=prompt; + fnp.dflt=dflt; + fnp.handler=handler; + fnp.completor=completor; + + par.flags=(MPLEX_ATTACH_SWITCHTO| + MPLEX_ATTACH_MODAL| + MPLEX_ATTACH_UNNUMBERED| + MPLEX_ATTACH_SIZEPOLICY); + par.szplcy=SIZEPOLICY_FULL_BOUNDS; + + wedln=(WEdln*)mplex_do_attach_new(mplex, &par, + (WRegionCreateFn*)create_wedln, + (void*)&fnp); + + if(wedln!=NULL && ev!=NULL && cycle!=extl_fn_none()) + create_cycle_binding(wedln, ev, cycle); + + + return wedln; +} + diff --git a/mod_query/query.h b/mod_query/query.h new file mode 100644 index 0000000..9387a17 --- /dev/null +++ b/mod_query/query.h @@ -0,0 +1,24 @@ +/* + * ion/mod_query/query.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_MOD_QUERY_QUERY_H +#define ION_MOD_QUERY_QUERY_H + +#include +#include +#include +#include "wedln.h" + +extern WEdln *mod_query_do_query(WMPlex *mplex, const char *prompt, + const char *dflt, ExtlFn handler, + ExtlFn completor, ExtlFn cycle); + +#endif /* ION_MOD_QUERY_QUERY_H */ diff --git a/mod_query/wedln-wrappers.c b/mod_query/wedln-wrappers.c new file mode 100644 index 0000000..3043d05 --- /dev/null +++ b/mod_query/wedln-wrappers.c @@ -0,0 +1,278 @@ +/* + * ion/mod_query/wedln-wrappers.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include "wedln.h" +#include "edln.h" +#include "complete.h" + +/*EXTL_DOC + * Move backward one character. + */ +EXTL_EXPORT_MEMBER +void wedln_back(WEdln *wedln) +{ + edln_back(&(wedln->edln)); +} + +/*EXTL_DOC + * Move forward one character. + */ +EXTL_EXPORT_MEMBER +void wedln_forward(WEdln *wedln) +{ + edln_forward(&(wedln->edln)); +} + +/*EXTL_DOC + * Transpose characters. + */ +EXTL_EXPORT_MEMBER +void wedln_transpose_chars(WEdln *wedln) +{ + edln_transpose_chars(&(wedln->edln)); +} + +/*EXTL_DOC + * Transpose words. + */ +EXTL_EXPORT_MEMBER +void wedln_transpose_words(WEdln *wedln) +{ + edln_transpose_words(&(wedln->edln)); +} + +/*EXTL_DOC + * Go to the beginning of line. + */ +EXTL_EXPORT_MEMBER +void wedln_bol(WEdln *wedln) +{ + edln_bol(&(wedln->edln)); +} + +/*EXTL_DOC + * Go to the end of line. + */ +EXTL_EXPORT_MEMBER +void wedln_eol(WEdln *wedln) +{ + edln_eol(&(wedln->edln)); +} + +/*EXTL_DOC + * Go to to end of current sequence of whitespace followed by alphanumeric + * characters.. + */ +EXTL_EXPORT_MEMBER +void wedln_skip_word(WEdln *wedln) +{ + edln_skip_word(&(wedln->edln)); +} + +/*EXTL_DOC + * Go to to beginning of current sequence of alphanumeric characters + * followed by whitespace. + */ +EXTL_EXPORT_MEMBER +void wedln_bskip_word(WEdln *wedln) +{ + edln_bskip_word(&(wedln->edln)); +} + +/*EXTL_DOC + * Delete current character. + */ +EXTL_EXPORT_MEMBER +void wedln_delete(WEdln *wedln) +{ + edln_delete(&(wedln->edln)); +} + +/*EXTL_DOC + * Delete previous character. + */ +EXTL_EXPORT_MEMBER +void wedln_backspace(WEdln *wedln) +{ + edln_backspace(&(wedln->edln)); +} + +/*EXTL_DOC + * Delete all characters from current to end of line. + */ +EXTL_EXPORT_MEMBER +void wedln_kill_to_eol(WEdln *wedln) +{ + edln_kill_to_eol(&(wedln->edln)); +} + +/*EXTL_DOC + * Delete all characters from previous to beginning of line. + */ +EXTL_EXPORT_MEMBER +void wedln_kill_to_bol(WEdln *wedln) +{ + edln_kill_to_bol(&(wedln->edln)); +} + +/*EXTL_DOC + * Delete the whole line. + */ +EXTL_EXPORT_MEMBER +void wedln_kill_line(WEdln *wedln) +{ + edln_kill_line(&(wedln->edln)); +} + +/*EXTL_DOC + * Starting from the current point, delete possible whitespace and + * following alphanumeric characters until next non-alphanumeric character. + */ +EXTL_EXPORT_MEMBER +void wedln_kill_word(WEdln *wedln) +{ + edln_kill_word(&(wedln->edln)); +} + +/*EXTL_DOC + * Starting from the previous characters, delete possible whitespace and + * preceding alphanumeric characters until previous non-alphanumeric character. + */ +EXTL_EXPORT_MEMBER +void wedln_bkill_word(WEdln *wedln) +{ + edln_bkill_word(&(wedln->edln)); +} + +/*EXTL_DOC + * Set \emph{mark} to current cursor position. + */ +EXTL_EXPORT_MEMBER +void wedln_set_mark(WEdln *wedln) +{ + edln_set_mark(&(wedln->edln)); +} + +/*EXTL_DOC + * Clear \emph{mark}. + */ +EXTL_EXPORT_MEMBER +void wedln_clear_mark(WEdln *wedln) +{ + edln_clear_mark(&(wedln->edln)); +} + +/*EXTL_DOC + * Copy text between \emph{mark} and current cursor position to clipboard + * and then delete that sequence. + */ +EXTL_EXPORT_MEMBER +void wedln_cut(WEdln *wedln) +{ + edln_cut(&(wedln->edln)); +} + +/*EXTL_DOC + * Copy text between \emph{mark} and current cursor position to clipboard. + */ +EXTL_EXPORT_MEMBER +void wedln_copy(WEdln *wedln) +{ + edln_copy(&(wedln->edln)); +} + +/*EXTL_DOC + * Replace line editor contents with next entry in history if one exists. + * If \var{match} is \code{true}, the initial part of the history entry + * must match the current line from beginning to point. + */ +EXTL_EXPORT_MEMBER +void wedln_history_next(WEdln *wedln, bool match) +{ + edln_history_next(&(wedln->edln), match); +} + +/*EXTL_DOC + * Replace line editor contents with previous in history if one exists. + * If \var{match} is \code{true}, the initial part of the history entry + * must match the current line from beginning to point. + */ +EXTL_EXPORT_MEMBER +void wedln_history_prev(WEdln *wedln, bool match) +{ + edln_history_prev(&(wedln->edln), match); +} + + +/*EXTL_DOC + * Input \var{str} in wedln at current editing point. + */ +EXTL_EXPORT_AS(WEdln, insstr) +void wedln_insstr_exported(WEdln *wedln, const char *str) +{ + edln_insstr(&(wedln->edln), str); +} + + +/*EXTL_DOC + * Get line editor contents. + */ +EXTL_SAFE +EXTL_EXPORT_MEMBER +const char *wedln_contents(WEdln *wedln) +{ + return wedln->edln.p; +} + +/*EXTL_DOC + * Get current editing point. + * Beginning of the edited line is point 0. + */ +EXTL_SAFE +EXTL_EXPORT_MEMBER +int wedln_point(WEdln *wedln) +{ + return wedln->edln.point; +} + +/*EXTL_DOC + * Get current mark (start of selection) for \var{wedln}. + * Return value of -1 indicates that there is no mark, and + * 0 is the beginning of the line. + */ +EXTL_SAFE +EXTL_EXPORT_MEMBER +int wedln_mark(WEdln *wedln) +{ + return wedln->edln.mark; +} + + +/*EXTL_DOC + * Set history context for \var{wedln}. + */ +EXTL_EXPORT_MEMBER +void wedln_set_context(WEdln *wedln, const char *context) +{ + edln_set_context(&(wedln->edln), context); +} + + +/*EXTL_DOC + * Get history context for \var{wedln}. + */ +EXTL_SAFE +EXTL_EXPORT_MEMBER +const char *wedln_context(WEdln *wedln) +{ + return wedln->edln.context; +} + diff --git a/mod_query/wedln.c b/mod_query/wedln.c new file mode 100644 index 0000000..094f22c --- /dev/null +++ b/mod_query/wedln.c @@ -0,0 +1,1072 @@ +/* + * ion/mod_query/wedln.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include "edln.h" +#include "wedln.h" +#include "inputp.h" +#include "complete.h" +#include "main.h" + + +#define WEDLN_BRUSH(X) ((X)->input.brush) + + +/*{{{ Drawing primitives */ + + +static int calc_text_y(WEdln *wedln, const WRectangle *geom) +{ + GrFontExtents fnte; + + grbrush_get_font_extents(WEDLN_BRUSH(wedln), &fnte); + + return geom->y+geom->h/2-fnte.max_height/2+fnte.baseline; +} + + +static int wedln_draw_strsect(WEdln *wedln, const WRectangle *geom, + int x, int y, const char *str, int len, + const char *attr) +{ + if(len==0) + return 0; + + grbrush_draw_string(WEDLN_BRUSH(wedln), x, y, str, len, TRUE, attr); + + return grbrush_get_text_width(WEDLN_BRUSH(wedln), str, len); +} + +#if 0 +static void dispu(const char* s, int l) +{ + while(l>0){ + int c=(unsigned char)*s; + fprintf(stderr, "%d[%c]", c, *s); + s++; + l--; + } + fprintf(stderr, "\n"); +} +#endif + +#define DSTRSECT(LEN, INV) \ + if(LEN>0){tx+=wedln_draw_strsect(wedln, geom, geom->x+tx, ty, str, LEN, INV); \ + str+=LEN; len-=LEN;} + + +static void wedln_do_draw_str_box(WEdln *wedln, const WRectangle *geom, + const char *str, int cursor, + int mark, int tx) +{ + int len=strlen(str), ll=0, ty=0; + const char *normalstyle=(REGION_IS_ACTIVE(wedln) + ? "active-normal" : "inactive-normal"); + const char *selectionstyle=(REGION_IS_ACTIVE(wedln) + ? "active-selection" : "inactive-selection"); + const char *cursorstyle=(REGION_IS_ACTIVE(wedln) + ? "active-cursor" : "inactive-cursor"); + + /*if(txw){ + WRectangle g=*geom; + g.x+=tx; + g.w-=tx; + grbrush_clear_area(WEDLN_BRUSH(wedln), &g); + }*/ + + ty=calc_text_y(wedln, geom); + + if(mark<=cursor){ + if(mark>=0){ + DSTRSECT(mark, normalstyle); + DSTRSECT(cursor-mark, selectionstyle); + }else{ + DSTRSECT(cursor, normalstyle); + } + if(len==0){ + tx+=wedln_draw_strsect(wedln, geom, geom->x+tx, ty, + " ", 1, cursorstyle); + }else{ + ll=str_nextoff(str, 0); + DSTRSECT(ll, cursorstyle); + } + }else{ + DSTRSECT(cursor, normalstyle); + ll=str_nextoff(str, 0); + DSTRSECT(ll, cursorstyle); + DSTRSECT(mark-cursor-ll, selectionstyle); + } + DSTRSECT(len, normalstyle); + + if(txw){ + WRectangle g=*geom; + g.x+=tx; + g.w-=tx; + grbrush_clear_area(WEDLN_BRUSH(wedln), &g); + } +} + + +static void wedln_draw_str_box(WEdln *wedln, const WRectangle *geom, + int vstart, const char *str, + int dstart, int point, int mark) +{ + int tx=0; + + /* Some fonts and Xmb/utf8 routines don't work well with dstart!=0. */ + dstart=0; + + if(mark>=0){ + mark-=vstart+dstart; + if(mark<0) + mark=0; + } + + point-=vstart+dstart; + + if(dstart!=0) + tx=grbrush_get_text_width(WEDLN_BRUSH(wedln), str+vstart, dstart); + + grbrush_begin(WEDLN_BRUSH(wedln), geom, GRBRUSH_AMEND|GRBRUSH_NEED_CLIP); + + wedln_do_draw_str_box(wedln, geom, str+vstart+dstart, point, mark, tx); + + grbrush_end(WEDLN_BRUSH(wedln)); +} + + +static bool wedln_update_cursor(WEdln *wedln, int iw) +{ + int cx, l; + int vstart=wedln->vstart; + int point=wedln->edln.point; + int len=wedln->edln.psize; + int mark=wedln->edln.mark; + const char *str=wedln->edln.p; + bool ret; + + if(pointvstart) + wedln->vstart=point; + + if(wedln->vstart==point) + return FALSE; + + while(vstartvstart!=vstart); + wedln->vstart=vstart; + + return ret; +} + + +/*}}}*/ + + +/*{{{ Size/location calc */ + + +static int get_textarea_height(WEdln *wedln, bool with_spacing) +{ + GrBorderWidths bdw; + GrFontExtents fnte; + + grbrush_get_border_widths(WEDLN_BRUSH(wedln), &bdw); + grbrush_get_font_extents(WEDLN_BRUSH(wedln), &fnte); + + return (fnte.max_height+bdw.top+bdw.bottom+ + (with_spacing ? bdw.spacing : 0)); +} + + +enum{G_NORESET, G_MAX, G_CURRENT}; + + +static void get_geom(WEdln *wedln, int mode, WRectangle *geom) +{ + if(mode==G_MAX) + *geom=wedln->input.last_fp.g; + else if(mode==G_CURRENT) + *geom=REGION_GEOM(wedln); +} + + +static void get_completions_geom(WEdln *wedln, int mode, WRectangle *geom) +{ + get_geom(wedln, mode, geom); + geom->x=0; + geom->y=0; + + geom->h-=get_textarea_height(wedln, TRUE); + if(geom->h<0) + geom->h=0; +} + + +static void get_outer_geom(WEdln *wedln, int mode, WRectangle *geom) +{ + int th; + + get_geom(wedln, mode, geom); + geom->x=0; + geom->y=0; + + th=get_textarea_height(wedln, FALSE); + + geom->y+=geom->h-th; + geom->h=th; +} + + +static void get_inner_geom(WEdln *wedln, int mode, WRectangle *geom) +{ + GrBorderWidths bdw; + + grbrush_get_border_widths(WEDLN_BRUSH(wedln), &bdw); + + get_outer_geom(wedln, mode, geom); + + geom->x+=bdw.left; + geom->w-=bdw.left+bdw.right; + geom->y+=bdw.top; + geom->h-=bdw.top+bdw.bottom; + geom->w=maxof(0, geom->w); + geom->h=maxof(0, geom->h); +} + + +static void get_textarea_geom(WEdln *wedln, int mode, WRectangle *geom) +{ + get_inner_geom(wedln, mode, geom); + geom->x+=wedln->prompt_w; + geom->w=maxof(0, geom->w - wedln->prompt_w - wedln->info_w); +} + + +static void wedln_calc_size(WEdln *wedln, WRectangle *geom) +{ + int h, th; + GrBorderWidths bdw; + WRectangle max_geom=*geom, tageom; + + if(WEDLN_BRUSH(wedln)==NULL) + return; + + if(wedln->prompt!=NULL){ + wedln->prompt_w=grbrush_get_text_width(WEDLN_BRUSH(wedln), + wedln->prompt, + wedln->prompt_len); + } + + if(wedln->info!=NULL){ + wedln->info_w=grbrush_get_text_width(WEDLN_BRUSH(wedln), + wedln->info, + wedln->info_len); + } + + th=get_textarea_height(wedln, wedln->compl_list.strs!=NULL); + + if(wedln->compl_list.strs==NULL){ + if(max_geom.hinput.last_fp.mode®ION_FIT_BOUNDS)) + geom->h=max_geom.h; + else + geom->h=th; + }else{ + WRectangle g; + + get_completions_geom(wedln, G_MAX, &g); + + fit_listing(WEDLN_BRUSH(wedln), &g, &(wedln->compl_list)); + + grbrush_get_border_widths(WEDLN_BRUSH(wedln), &bdw); + + h=wedln->compl_list.toth; + th+=bdw.top+bdw.bottom; + + if(h+th>max_geom.h || !(wedln->input.last_fp.mode®ION_FIT_BOUNDS)) + h=max_geom.h-th; + geom->h=h+th; + } + + geom->w=max_geom.w; + geom->y=max_geom.y+max_geom.h-geom->h; + geom->x=max_geom.x; + + tageom=*geom; + get_textarea_geom(wedln, G_NORESET, &tageom); + wedln_update_cursor(wedln, tageom.w); +} + + +/*}}}*/ + + +/*{{{ Draw */ + + +void wedln_draw_completions(WEdln *wedln, bool complete) +{ + WRectangle geom; + + if(wedln->compl_list.strs!=NULL && WEDLN_BRUSH(wedln)!=NULL){ + const char *style=(REGION_IS_ACTIVE(wedln) + ? "active" + : "inactive"); + const char *selstyle=(REGION_IS_ACTIVE(wedln) + ? "active-selection" + : "inactive-selection"); + + get_completions_geom(wedln, G_CURRENT, &geom); + + draw_listing(WEDLN_BRUSH(wedln), &geom, &(wedln->compl_list), + complete, style, selstyle); + } +} + + +void wedln_draw_textarea(WEdln *wedln) +{ + WRectangle geom; + int ty; + const char *style=(REGION_IS_ACTIVE(wedln) ? "active" : "inactive"); + + if(WEDLN_BRUSH(wedln)==NULL) + return; + + get_outer_geom(wedln, G_CURRENT, &geom); + + /*grbrush_begin(WEDLN_BRUSH(wedln), &geom, GRBRUSH_AMEND);*/ + + grbrush_draw_border(WEDLN_BRUSH(wedln), &geom, style); + + get_inner_geom(wedln, G_CURRENT, &geom); + + ty=calc_text_y(wedln, &geom); + + if(wedln->prompt!=NULL){ + const char *promptstyle=(REGION_IS_ACTIVE(wedln) + ? "active-prompt" + : "inactive-prompt"); + grbrush_draw_string(WEDLN_BRUSH(wedln), geom.x, ty, + wedln->prompt, wedln->prompt_len, TRUE, + promptstyle); + } + + if(wedln->info!=NULL){ + int x=geom.x+geom.w-wedln->info_w; + const char *promptstyle=(REGION_IS_ACTIVE(wedln) + ? "active-prompt-info" + : "inactive-prompt-info"); + grbrush_draw_string(WEDLN_BRUSH(wedln), x, ty, + wedln->info, wedln->info_len, TRUE, + promptstyle); + } + + get_textarea_geom(wedln, G_CURRENT, &geom); + + wedln_draw_str_box(wedln, &geom, wedln->vstart, wedln->edln.p, 0, + wedln->edln.point, wedln->edln.mark); + + /*grbrush_end(WEDLN_BRUSH(wedln));*/ +} + + +void wedln_draw(WEdln *wedln, bool complete) +{ + WRectangle g; + int f=(complete ? 0 : GRBRUSH_NO_CLEAR_OK); + + if(WEDLN_BRUSH(wedln)==NULL) + return; + + get_geom(wedln, G_CURRENT, &g); + + grbrush_begin(WEDLN_BRUSH(wedln), &g, f); + + wedln_draw_completions(wedln, FALSE); + wedln_draw_textarea(wedln); + + grbrush_end(WEDLN_BRUSH(wedln)); +} + + +/*}}} */ + + +/*{{{ Info area */ + + +static void wedln_set_info(WEdln *wedln, const char *info) +{ + WRectangle tageom; + char *p; + + if(wedln->info!=NULL){ + free(wedln->info); + wedln->info=NULL; + wedln->info_w=0; + wedln->info_len=0; + } + + if(info!=NULL){ + wedln->info=scat3(" [", info, "]"); + if(wedln->info!=NULL){ + wedln->info_len=strlen(wedln->info); + if(WEDLN_BRUSH(wedln)!=NULL){ + wedln->info_w=grbrush_get_text_width(WEDLN_BRUSH(wedln), + wedln->info, + wedln->info_len); + } + } + } + + get_textarea_geom(wedln, G_CURRENT, &tageom); + wedln_update_cursor(wedln, tageom.w); + + wedln_draw_textarea(wedln); +} + + +/*}}}*/ + + +/*{{{ Completions */ + + +static void wedln_show_completions(WEdln *wedln, char **strs, int nstrs, + int selected) +{ + int w=REGION_GEOM(wedln).w; + int h=REGION_GEOM(wedln).h; + + if(WEDLN_BRUSH(wedln)==NULL) + return; + + setup_listing(&(wedln->compl_list), strs, nstrs, FALSE); + wedln->compl_list.selected_str=selected; + + input_refit((WInput*)wedln); + if(w==REGION_GEOM(wedln).w && h==REGION_GEOM(wedln).h) + wedln_draw_completions(wedln, TRUE); +} + + +void wedln_hide_completions(WEdln *wedln) +{ + if(wedln->compl_list.strs!=NULL){ + deinit_listing(&(wedln->compl_list)); + input_refit((WInput*)wedln); + } +} + + +void wedln_scrollup_completions(WEdln *wedln) +{ + if(wedln->compl_list.strs==NULL) + return; + if(scrollup_listing(&(wedln->compl_list))) + wedln_draw_completions(wedln, TRUE); +} + + +void wedln_scrolldown_completions(WEdln *wedln) +{ + if(wedln->compl_list.strs==NULL) + return; + if(scrolldown_listing(&(wedln->compl_list))) + wedln_draw_completions(wedln, TRUE); +} + + +static int update_nocompl=0; + + +static void free_completions(char **ptr, int i) +{ + while(i>0){ + i--; + free(ptr[i]); + } + free(ptr); +} + + +bool wedln_do_set_completions(WEdln *wedln, char **ptr, int n, + char *beg, char *end, int cycle, + bool nosort) +{ + int sel=-1; + + if(wedln->compl_beg!=NULL) + free(wedln->compl_beg); + + if(wedln->compl_end!=NULL) + free(wedln->compl_end); + + wedln->compl_beg=beg; + wedln->compl_end=end; + wedln->compl_current_id=-1; + + n=edln_do_completions(&(wedln->edln), ptr, n, beg, end, + !mod_query_config.autoshowcompl, nosort); + + if(mod_query_config.autoshowcompl && n>0 && cycle!=0){ + update_nocompl++; + sel=(cycle>0 ? 0 : n-1); + edln_set_completion(&(wedln->edln), ptr[sel], beg, end); + update_nocompl--; + } + + if(n>1 || (mod_query_config.autoshowcompl && n>0)){ + wedln_show_completions(wedln, ptr, n, sel); + return TRUE; + } + + free_completions(ptr, n); + + return FALSE; +} + + +void wedln_set_completions(WEdln *wedln, ExtlTab completions, int cycle) +{ + int n=0, i=0; + char **ptr=NULL, *beg=NULL, *end=NULL, *p=NULL; + + n=extl_table_get_n(completions); + + if(n==0){ + wedln_hide_completions(wedln); + return; + } + + ptr=ALLOC_N(char*, n); + if(ptr==NULL) + goto allocfail; + + for(i=0; i0){ + i--; + /* edln_do_completions may NULL things */ + if(ptr[i]!=NULL) + free(ptr[i]); + } + free(ptr); +} + + +static void wedln_do_select_completion(WEdln *wedln, int n) +{ + bool complredraw=listing_select(&(wedln->compl_list), n); + wedln_draw_completions(wedln, complredraw); + + update_nocompl++; + edln_set_completion(&(wedln->edln), wedln->compl_list.strs[n], + wedln->compl_beg, wedln->compl_end); + update_nocompl--; +} + + + +static ExtlExportedFn *sc_safe_fns[]={ + (ExtlExportedFn*)&complproxy_set_completions, + NULL +}; + + +static ExtlSafelist sc_safelist=EXTL_SAFELIST_INIT(sc_safe_fns); + + +static int wedln_alloc_compl_id(WEdln *wedln) +{ + int id=wedln->compl_waiting_id+1; + wedln->compl_waiting_id=maxof(0, wedln->compl_waiting_id+1); + return id; +} + +static bool wedln_do_call_completor(WEdln *wedln, int id, int cycle) +{ + if(wedln->compl_history_mode){ + uint n; + char **h=NULL; + + wedln->compl_waiting_id=id; + + n=edln_history_matches(&wedln->edln, &h); + + if(n==0){ + wedln_hide_completions(wedln); + return FALSE; + } + + if(wedln_do_set_completions(wedln, h, n, NULL, NULL, cycle, TRUE)){ + wedln->compl_current_id=id; + return TRUE; + } + + return FALSE; + }else{ + const char *p=wedln->edln.p; + int point=wedln->edln.point; + WComplProxy *proxy=create_complproxy(wedln, id, cycle); + + if(proxy==NULL) + return FALSE; + + /* Set Lua-side to own the proxy: it gets freed by Lua's GC */ + ((Obj*)proxy)->flags|=OBJ_EXTL_OWNED; + + if(p==NULL){ + p=""; + point=0; + } + + extl_protect(&sc_safelist); + extl_call(wedln->completor, "osi", NULL, proxy, p, point); + extl_unprotect(&sc_safelist); + + return TRUE; + } +} + + +static void timed_complete(WTimer *tmr, Obj *obj) +{ + WEdln *wedln=(WEdln*)obj; + + if(wedln!=NULL){ + int id=wedln->compl_timed_id; + wedln->compl_timed_id=-1; + if(id==wedln->compl_waiting_id && id>=0) + wedln_do_call_completor((WEdln*)obj, id, FALSE); + } +} + + +/*EXTL_DOC + * Select next completion. + */ +EXTL_EXPORT_MEMBER +bool wedln_next_completion(WEdln *wedln) +{ + int n=-1; + + if(wedln->compl_current_id!=wedln->compl_waiting_id) + return FALSE; + + if(wedln->compl_list.nstrs<=0) + return FALSE; + + if(wedln->compl_list.selected_str<0 || + wedln->compl_list.selected_str+1>=wedln->compl_list.nstrs){ + n=0; + }else{ + n=wedln->compl_list.selected_str+1; + } + + if(n!=wedln->compl_list.selected_str) + wedln_do_select_completion(wedln, n); + + return TRUE; +} + + +/*EXTL_DOC + * Select previous completion. + */ +EXTL_EXPORT_MEMBER +bool wedln_prev_completion(WEdln *wedln) +{ + int n=-1; + + if(wedln->compl_current_id!=wedln->compl_waiting_id) + return FALSE; + + if(wedln->compl_list.nstrs<=0) + return FALSE; + + if(wedln->compl_list.selected_str<=0){ + n=wedln->compl_list.nstrs-1; + }else{ + n=wedln->compl_list.selected_str-1; + } + + if(n!=wedln->compl_list.selected_str) + wedln_do_select_completion(wedln, n); + + return TRUE; +} + + +/*EXTL_DOC + * Call completion handler with the text between the beginning of line and + * current cursor position, or select next/previous completion from list if in + * auto-show-completions mode and \var{cycle} is set to ``next'' or ``prev'', + * respectively. The \var{mode} may be ``history'' or ``normal''. If it is + * not set, the previous mode is used. Normally next entry is not cycled to + * despite the setting of \var{cycle} if mode switch occurs. To override + * this, use ``next-always'' and ``prev-always'' for \var{cycle}. + */ +EXTL_EXPORT_MEMBER +void wedln_complete(WEdln *wedln, const char *cycle, const char *mode) +{ + bool valid=TRUE; + int cyclei=0; + + if(mode!=NULL){ + if(strcmp(mode, "history")==0){ + valid=wedln->compl_history_mode; + wedln->compl_history_mode=TRUE; + }else if(strcmp(mode, "normal")==0){ + valid=!wedln->compl_history_mode; + wedln->compl_history_mode=FALSE; + } + if(!valid){ + wedln_set_info(wedln, + (wedln->compl_history_mode + ? TR("history") + : NULL)); + } + } + + if(cycle!=NULL){ + if((valid && strcmp(cycle, "next")==0) || + strcmp(cycle, "next-always")==0){ + cyclei=1; + }else if((valid && strcmp(cycle, "prev")==0) || + strcmp(cycle, "prev-always")==0){ + cyclei=-1; + } + } + + if(valid && cyclei!=0 && mod_query_config.autoshowcompl && + wedln->compl_list.nstrs>0){ + if(cyclei>0) + wedln_next_completion(wedln); + else + wedln_prev_completion(wedln); + }else{ + int oldid=wedln->compl_waiting_id; + + if(!wedln_do_call_completor(wedln, wedln_alloc_compl_id(wedln), + cyclei)){ + wedln->compl_waiting_id=oldid; + } + } +} + + +/*EXTL_DOC + * Get history completion mode. + */ +EXTL_EXPORT_MEMBER +bool wedln_is_histcompl(WEdln *wedln) +{ + return wedln->compl_history_mode; +} + + +/*}}}*/ + + +/*{{{ Update handler */ + + +static void wedln_update_handler(WEdln *wedln, int from, int flags) +{ + WRectangle geom; + + if(WEDLN_BRUSH(wedln)==NULL) + return; + + get_textarea_geom(wedln, G_CURRENT, &geom); + + if(flags&EDLN_UPDATE_NEW) + wedln->vstart=0; + + if(flags&EDLN_UPDATE_MOVED){ + if(wedln_update_cursor(wedln, geom.w)) + from=wedln->vstart; + } + + from=maxof(0, from-wedln->vstart); + + wedln_draw_str_box(wedln, &geom, wedln->vstart, wedln->edln.p, from, + wedln->edln.point, wedln->edln.mark); + + if(update_nocompl==0 && + mod_query_config.autoshowcompl && + flags&EDLN_UPDATE_CHANGED){ + wedln->compl_current_id=-1; /* invalidate */ + if(wedln->autoshowcompl_timer==NULL) + wedln->autoshowcompl_timer=create_timer(); + if(wedln->autoshowcompl_timer!=NULL){ + wedln->compl_timed_id=wedln_alloc_compl_id(wedln); + timer_set(wedln->autoshowcompl_timer, + mod_query_config.autoshowcompl_delay, + timed_complete, (Obj*)wedln); + } + } +} + + +/*}}}*/ + + +/*{{{ Init, deinit and config update */ + + +static bool wedln_init_prompt(WEdln *wedln, const char *prompt) +{ + char *p; + + if(prompt!=NULL){ + p=scat(prompt, " "); + + if(p==NULL) + return FALSE; + + wedln->prompt=p; + wedln->prompt_len=strlen(p); + }else{ + wedln->prompt=NULL; + wedln->prompt_len=0; + } + wedln->prompt_w=0; + + return TRUE; +} + + +static bool wedln_init(WEdln *wedln, WWindow *par, const WFitParams *fp, + WEdlnCreateParams *params) +{ + wedln->vstart=0; + + if(!wedln_init_prompt(wedln, params->prompt)) + return FALSE; + + if(!edln_init(&(wedln->edln), params->dflt)){ + free(wedln->prompt); + return FALSE; + } + + wedln->handler=extl_fn_none(); + wedln->completor=extl_fn_none(); + + wedln->edln.uiptr=wedln; + wedln->edln.ui_update=(EdlnUpdateHandler*)wedln_update_handler; + + wedln->autoshowcompl_timer=NULL; + + init_listing(&(wedln->compl_list)); + + wedln->compl_waiting_id=-1; + wedln->compl_current_id=-1; + wedln->compl_timed_id=-1; + wedln->compl_beg=NULL; + wedln->compl_end=NULL; + wedln->compl_tab=FALSE; + wedln->compl_history_mode=FALSE; + + wedln->info=NULL; + wedln->info_len=0; + wedln->info_w=0; + + wedln->cycle_bindmap=NULL; + + if(!input_init((WInput*)wedln, par, fp)){ + edln_deinit(&(wedln->edln)); + free(wedln->prompt); + return FALSE; + } + + window_create_xic(&wedln->input.win); + + wedln->handler=extl_ref_fn(params->handler); + wedln->completor=extl_ref_fn(params->completor); + + region_add_bindmap((WRegion*)wedln, mod_query_wedln_bindmap); + + return TRUE; +} + + +WEdln *create_wedln(WWindow *par, const WFitParams *fp, + WEdlnCreateParams *params) +{ + CREATEOBJ_IMPL(WEdln, wedln, (p, par, fp, params)); +} + + +static void wedln_deinit(WEdln *wedln) +{ + if(wedln->prompt!=NULL) + free(wedln->prompt); + + if(wedln->info!=NULL) + free(wedln->info); + + if(wedln->compl_beg!=NULL) + free(wedln->compl_beg); + + if(wedln->compl_end!=NULL) + free(wedln->compl_end); + + if(wedln->compl_list.strs!=NULL) + deinit_listing(&(wedln->compl_list)); + + if(wedln->autoshowcompl_timer!=NULL) + destroy_obj((Obj*)wedln->autoshowcompl_timer); + + if(wedln->cycle_bindmap!=NULL) + bindmap_destroy(wedln->cycle_bindmap); + + extl_unref_fn(wedln->completor); + extl_unref_fn(wedln->handler); + + edln_deinit(&(wedln->edln)); + input_deinit((WInput*)wedln); +} + + +static void wedln_do_finish(WEdln *wedln) +{ + ExtlFn handler; + char *p; + + handler=wedln->handler; + wedln->handler=extl_fn_none(); + p=edln_finish(&(wedln->edln)); + + if(region_manager_allows_destroying((WRegion*)wedln)) + destroy_obj((Obj*)wedln); + + if(p!=NULL) + extl_call(handler, "s", NULL, p); + + free(p); + extl_unref_fn(handler); +} + + +/*EXTL_DOC + * Close \var{wedln} and call any handlers. + */ +EXTL_EXPORT_MEMBER +void wedln_finish(WEdln *wedln) +{ + mainloop_defer_action((Obj*)wedln, (WDeferredAction*)wedln_do_finish); +} + + +/*}}}*/ + + +/*{{{ The rest */ + + +/*EXTL_DOC + * Request selection from application holding such. + * + * Note that this function is asynchronous; the selection will not + * actually be inserted before Ion receives it. This will be no + * earlier than Ion return to its main loop. + */ +EXTL_EXPORT_MEMBER +void wedln_paste(WEdln *wedln) +{ + ioncore_request_selection_for(wedln->input.win.win); +} + + +void wedln_insstr(WEdln *wedln, const char *buf, size_t n) +{ + edln_insstr_n(&(wedln->edln), buf, n, TRUE, TRUE); +} + + +static const char *wedln_style(WEdln *wedln) +{ + return "input-edln"; +} + + +/*}}}*/ + + +/*{{{ Dynamic function table and class implementation */ + + +static DynFunTab wedln_dynfuntab[]={ + {window_draw, wedln_draw}, + {input_calc_size, wedln_calc_size}, + {input_scrollup, wedln_scrollup_completions}, + {input_scrolldown, wedln_scrolldown_completions}, + {window_insstr, wedln_insstr}, + {(DynFun*)input_style, (DynFun*)wedln_style}, + END_DYNFUNTAB +}; + + +EXTL_EXPORT +IMPLCLASS(WEdln, WInput, wedln_deinit, wedln_dynfuntab); + + +/*}}}*/ diff --git a/mod_query/wedln.h b/mod_query/wedln.h new file mode 100644 index 0000000..40c0529 --- /dev/null +++ b/mod_query/wedln.h @@ -0,0 +1,81 @@ +/* + * ion/mod_query/wedln.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_MOD_QUERY_WEDLN_H +#define ION_MOD_QUERY_WEDLN_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include "listing.h" +#include "input.h" +#include "edln.h" + +INTRCLASS(WEdln); +INTRSTRUCT(WEdlnCreateParams); + +DECLSTRUCT(WEdlnCreateParams){ + const char *prompt; + const char *dflt; + ExtlFn handler; + ExtlFn completor; +}; + + +DECLCLASS(WEdln){ + WInput input; + + Edln edln; + + char *prompt; + int prompt_len; + int prompt_w; + + char *info; + int info_len; + int info_w; + + int vstart; + + ExtlFn handler; + ExtlFn completor; + + WTimer *autoshowcompl_timer; + + WListing compl_list; + char *compl_beg; + char *compl_end; + int compl_waiting_id; + int compl_current_id; + int compl_timed_id; + uint compl_tab:1; + uint compl_history_mode:1; + + WBindmap *cycle_bindmap; +}; + +extern WEdln *create_wedln(WWindow *par, const WFitParams *fp, + WEdlnCreateParams *p); +extern void wedln_finish(WEdln *wedln); +extern void wedln_paste(WEdln *wedln); +extern void wedln_draw(WEdln *wedln, bool complete); +extern void wedln_set_completions(WEdln *wedln, ExtlTab completions, + bool autoshow_select_first); +extern void wedln_hide_completions(WEdln *wedln); +extern bool wedln_set_histcompl(WEdln *wedln, int sp); +extern bool wedln_get_histcompl(WEdln *wedln); + +#endif /* ION_MOD_QUERY_WEDLN_H */ diff --git a/mod_query/wmessage.c b/mod_query/wmessage.c new file mode 100644 index 0000000..aa6002d --- /dev/null +++ b/mod_query/wmessage.c @@ -0,0 +1,231 @@ +/* + * ion/mod_query/wmessage.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include + +#include +#include +#include +#include +#include +#include "wmessage.h" +#include "inputp.h" + + +#define WMSG_BRUSH(WMSG) ((WMSG)->input.brush) +#define WMSG_WIN(WMSG) ((WMSG)->input.win.win) + + +/*{{{ Sizecalc */ + + +static void get_geom(WMessage *wmsg, bool max, WRectangle *geom) +{ + if(max){ + geom->w=wmsg->input.last_fp.g.w; + geom->h=wmsg->input.last_fp.g.h; + }else{ + geom->w=REGION_GEOM(wmsg).w; + geom->h=REGION_GEOM(wmsg).h; + } + geom->x=0; + geom->y=0; +} + + +static void wmsg_calc_size(WMessage *wmsg, WRectangle *geom) +{ + WRectangle max_geom=*geom; + GrBorderWidths bdw; + int h=16; + + if(WMSG_BRUSH(wmsg)!=NULL){ + WRectangle g; + g.w=max_geom.w; + g.h=max_geom.h; + g.x=0; + g.y=0; + + fit_listing(WMSG_BRUSH(wmsg), &g, &(wmsg->listing)); + + grbrush_get_border_widths(WMSG_BRUSH(wmsg), &bdw); + + h=bdw.top+bdw.bottom+wmsg->listing.toth; + } + + if(h>max_geom.h || !(wmsg->input.last_fp.mode®ION_FIT_BOUNDS)) + h=max_geom.h; + + geom->h=h; + geom->w=max_geom.w; + geom->y=max_geom.y+max_geom.h-geom->h; + geom->x=max_geom.x; +} + + +/*}}}*/ + + +/*{{{ Draw */ + + +static void wmsg_draw(WMessage *wmsg, bool complete) +{ + const char *style=(REGION_IS_ACTIVE(wmsg) ? "active" : "inactive"); + WRectangle geom; + + if(WMSG_BRUSH(wmsg)==NULL) + return; + + get_geom(wmsg, FALSE, &geom); + + grbrush_begin(WMSG_BRUSH(wmsg), &geom, + (complete ? 0 : GRBRUSH_NO_CLEAR_OK)); + + draw_listing(WMSG_BRUSH(wmsg), &geom, &(wmsg->listing), + FALSE, style, style); + + grbrush_end(WMSG_BRUSH(wmsg)); +} + + +/*}}}*/ + + +/*{{{ Scroll */ + + +static void wmsg_scrollup(WMessage *wmsg) +{ + if(scrollup_listing(&(wmsg->listing))) + wmsg_draw(wmsg, TRUE); +} + + +static void wmsg_scrolldown(WMessage *wmsg) +{ + if(scrolldown_listing(&(wmsg->listing))) + wmsg_draw(wmsg, TRUE); +} + + +/*}}}*/ + + +/*{{{ Init, deinit draw config update */ + + +static bool wmsg_init(WMessage *wmsg, WWindow *par, const WFitParams *fp, + const char *msg) +{ + char **ptr; + int k, n=0; + char *cmsg; + const char *p; + size_t l; + + p=msg; + while(1){ + n=n+1; + p=strchr(p, '\n'); + if(p==NULL || *(p+1)=='\0') + break; + p=p+1; + } + + if(n==0) + return FALSE; + + ptr=ALLOC_N(char*, n); + + if(ptr==NULL) + return FALSE; + + for(k=0; k0){ + k--; + free(ptr[k]); + } + free(ptr); + return FALSE; + } + strncpy(cmsg, p, l); + cmsg[l]='\0'; + ptr[k]=cmsg; + k++; + if(p[l]=='\0') + break; + p=p+l+1; + } + + init_listing(&(wmsg->listing)); + setup_listing(&(wmsg->listing), ptr, k, TRUE); + + if(!input_init((WInput*)wmsg, par, fp)){ + deinit_listing(&(wmsg->listing)); + return FALSE; + } + + return TRUE; +} + + +WMessage *create_wmsg(WWindow *par, const WFitParams *fp, const char *msg) +{ + CREATEOBJ_IMPL(WMessage, wmsg, (p, par, fp, msg)); +} + + +static void wmsg_deinit(WMessage *wmsg) +{ + if(wmsg->listing.strs!=NULL) + deinit_listing(&(wmsg->listing)); + + input_deinit((WInput*)wmsg); +} + + +static const char *wmsg_style(WMessage *wmsg) +{ + return "input-message"; +} + + +/*}}}*/ + + +/*{{{ Dynamic function table and class implementation */ + + +static DynFunTab wmsg_dynfuntab[]={ + {window_draw, wmsg_draw}, + {input_calc_size, wmsg_calc_size}, + {input_scrollup, wmsg_scrollup}, + {input_scrolldown, wmsg_scrolldown}, + {(DynFun*)input_style, + (DynFun*)wmsg_style}, + END_DYNFUNTAB +}; + + +EXTL_EXPORT +IMPLCLASS(WMessage, WInput, wmsg_deinit, wmsg_dynfuntab); + + +/*}}}*/ diff --git a/mod_query/wmessage.h b/mod_query/wmessage.h new file mode 100644 index 0000000..bc05b52 --- /dev/null +++ b/mod_query/wmessage.h @@ -0,0 +1,32 @@ +/* + * ion/mod_query/wmessage.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_MOD_QUERY_WMESSAGE_H +#define ION_MOD_QUERY_WMESSAGE_H + +#include +#include +#include +#include +#include "listing.h" +#include "input.h" + +INTRCLASS(WMessage); + +DECLCLASS(WMessage){ + WInput input; + WListing listing; +}; + +extern WMessage *create_wmsg(WWindow *par, const WFitParams *fp, + const char *msg); + +#endif /* ION_MOD_QUERY_WMESSAGE_H */ diff --git a/mod_sm/Makefile b/mod_sm/Makefile new file mode 100644 index 0000000..c33f819 --- /dev/null +++ b/mod_sm/Makefile @@ -0,0 +1,27 @@ +## +## Session management support module Makefile +## + +# System-specific configuration is in system.mk +TOPDIR=.. +include $(TOPDIR)/build/system-inc.mk + +###################################### + +INCLUDES += $(X11_INCLUDES) $(LIBTU_INCLUDES) $(LIBEXTL_INCLUDES) -I.. +CFLAGS += $(XOPEN_SOURCE) $(C99_SOURCE) +LIBS += $(X11_LIBS) -lSM -lICE + +SOURCES=sm.c sm_matchwin.c sm_session.c + +MAKE_EXPORTS=mod_sm + +MODULE=mod_sm + +###################################### + +include $(TOPDIR)/build/rules.mk + +###################################### + +_install: module_install diff --git a/mod_sm/sm.c b/mod_sm/sm.c new file mode 100644 index 0000000..6971f7d --- /dev/null +++ b/mod_sm/sm.c @@ -0,0 +1,158 @@ +/* + * ion/mod_sm/sm.c + * + * Copyright (c) Tuomo Valkonen 2004-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sm_matchwin.h" +#include "sm_session.h" +#include "exports.h" + + +/*{{{ Module information */ + + +#include "../version.h" + +char mod_sm_ion_api_version[]=ION_API_VERSION; + + +/*}}}*/ + + +/*{{{ Manage callback */ + + +static bool sm_do_manage(WClientWin *cwin, const WManageParams *param) +{ + int transient_mode=TRANSIENT_MODE_OFF; + WPHolder *ph; + bool ret; + + if(param->tfor!=NULL) + return FALSE; + + ph=mod_sm_match_cwin_to_saved(cwin); + if(ph==NULL) + return FALSE; + + ret=pholder_attach(ph, 0, (WRegion*)cwin); + + destroy_obj((Obj*)ph); + + return ret; +} + + +/*}}}*/ + + +/*{{{ Init/deinit */ + + +static void mod_sm_set_sessiondir() +{ + const char *smdir=NULL, *id=NULL; + char *tmp; + bool ok=FALSE; + + smdir=getenv("SM_SAVE_DIR"); + id=getenv("GNOME_DESKTOP_SESSION_ID"); + + /* Running under SM, try to use a directory specific + * to the session. + */ + if(smdir!=NULL){ + tmp=scat(smdir, "/ion3"); /* TODO: pwm<=>ion! */ + }else if(id!=NULL){ + tmp=scat("gnome-session-", id); + if(tmp!=NULL){ + char *p=tmp; + while(1){ + p=strpbrk(p, "/ :?*"); + if(p==NULL) + break; + *p='-'; + p++; + } + } + }else{ + tmp=scopy("default-session-sm"); + } + + if(tmp!=NULL){ + ok=extl_set_sessiondir(tmp); + free(tmp); + } + + if(!ok) + warn(TR("Failed to set session directory.")); +} + + + +void mod_sm_deinit() +{ + ioncore_set_smhook(NULL); + + hook_remove(clientwin_do_manage_alt, (WHookDummy*)sm_do_manage); + + ioncore_set_sm_callbacks(NULL, NULL); + + mod_sm_unregister_exports(); + + mod_sm_close(); +} + + +int mod_sm_init() +{ + if(ioncore_g.sm_client_id!=NULL) + mod_sm_set_ion_id(ioncore_g.sm_client_id); + + if(!mod_sm_init_session()) + goto err; + + if(extl_sessiondir()==NULL) + mod_sm_set_sessiondir(); + + if(!mod_sm_register_exports()) + goto err; + + ioncore_set_sm_callbacks(mod_sm_add_match, mod_sm_get_configuration); + + hook_add(clientwin_do_manage_alt, (WHookDummy*)sm_do_manage); + + ioncore_set_smhook(mod_sm_smhook); + + return TRUE; + +err: + mod_sm_deinit(); + return FALSE; +} + + +/*}}}*/ + diff --git a/mod_sm/sm_matchwin.c b/mod_sm/sm_matchwin.c new file mode 100644 index 0000000..d64f671 --- /dev/null +++ b/mod_sm/sm_matchwin.c @@ -0,0 +1,309 @@ +/* + * ion/mod_sm/sm_mathcwin.c + * + * Copyright (c) Tuomo Valkonen 2004-2006. + * + * Based on the code of the 'sm' module for Ion1 by an unknown contributor. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "sm_matchwin.h" + +#define TIME_OUT 60000 + +static WWinMatch *match_list=NULL; +static WTimer *purge_timer=NULL; + + +char *mod_sm_get_window_role(Window window) +{ + Atom atom; + XTextProperty tp; + + atom=XInternAtom(ioncore_g.dpy, "WM_WINDOW_ROLE", False); + + if(XGetTextProperty(ioncore_g.dpy, window, &tp, atom)) + { + if(tp.encoding == XA_STRING && tp.format == 8 && tp.nitems != 0) + return((char *)tp.value); + } + + return NULL; +} + +Window mod_sm_get_client_leader(Window window) +{ + Window client_leader = 0; + Atom atom; + Atom actual_type; + int actual_format; + unsigned long nitems; + unsigned long bytes_after; + unsigned char *prop = NULL; + + atom=XInternAtom(ioncore_g.dpy, "WM_CLIENT_LEADER", False); + + if(XGetWindowProperty(ioncore_g.dpy, window, atom, + 0L, 1L, False, AnyPropertyType, &actual_type, + &actual_format, &nitems, &bytes_after, + &prop) == Success) + { + if(actual_type == XA_WINDOW && actual_format == 32 + && nitems == 1 && bytes_after == 0) + client_leader=*((Window *)prop); + XFree(prop); + } + return client_leader; +} + +char *mod_sm_get_client_id(Window window) +{ + char *client_id = NULL; + Window client_leader; + XTextProperty tp; + Atom atom; + + if((client_leader=mod_sm_get_client_leader(window))!=0){ + atom=XInternAtom(ioncore_g.dpy, "SM_CLIENT_ID", False); + if (XGetTextProperty (ioncore_g.dpy, client_leader, &tp, atom)) + if (tp.encoding == XA_STRING && tp.format == 8 && tp.nitems != 0) + client_id = (char *) tp.value; + } + + return client_id; +} + +char *mod_sm_get_window_cmd(Window window) +{ + char **cmd_argv, *command=NULL; + int id, i, len=0, cmd_argc=0; + + if(XGetCommand(ioncore_g.dpy, window, &cmd_argv, &cmd_argc) && (cmd_argc > 0)) + ; + else if((id=mod_sm_get_client_leader(window))) + XGetCommand(ioncore_g.dpy, id, &cmd_argv, &cmd_argc); + + if(cmd_argc > 0){ + for(i=0; i < cmd_argc; i++) + len+=strlen(cmd_argv[i])+1; + command=ALLOC_N(char, len+1); + strcpy(command, cmd_argv[0]); + for(i=1; i < cmd_argc; i++){ + strcat(command, " "); + strcat(command, cmd_argv[i]); + } + XFreeStringList(cmd_argv); + } + + return command; +} + +static void free_win_match(WWinMatch *match) +{ + UNLINK_ITEM(match_list, match, next, prev); + + if(match->pholder!=NULL) + destroy_obj((Obj*)match->pholder); + + if(match->client_id) + free(match->client_id); + if(match->window_role) + free(match->window_role); + if(match->wclass) + free(match->wclass); + if(match->wm_name) + free(match->wm_name); + if(match->wm_cmd) + free(match->wm_cmd); + free(match); +} + +static void mod_sm_purge_matches(WTimer *timer) +{ + assert(timer==purge_timer); + purge_timer=NULL; + destroy_obj((Obj*)timer); + +#ifdef DEBUG + warn("purging remaining matches\n"); +#endif + while(match_list) + free_win_match(match_list); +} + +void mod_sm_start_purge_timer() +{ + if(purge_timer==NULL) + purge_timer=create_timer(); + if(purge_timer!=NULL){ + timer_set(purge_timer, TIME_OUT, + (WTimerHandler*)mod_sm_purge_matches, NULL); + } +} + +void mod_sm_register_win_match(WWinMatch *match) +{ + LINK_ITEM(match_list, match, next, prev); +} + +bool mod_sm_have_match_list() +{ + if(match_list!=NULL) + return TRUE; + else + return FALSE; +} + +#define xstreq(a,b) (a && b && (strcmp(a,b)==0)) + +/* Tries to match a window against a list of loaded matches */ + +static WWinMatch *match_cwin(WClientWin *cwin) +{ + WWinMatch *match=match_list; + int win_match; + XClassHint clss; + char *client_id=mod_sm_get_client_id(cwin->win); + char *window_role=mod_sm_get_window_role(cwin->win); + char *wm_cmd=mod_sm_get_window_cmd(cwin->win); + char **wm_name=NULL; + int n; + + wm_name=xwindow_get_text_property(cwin->win, XA_WM_NAME, &n); + if(n<=0) + assert(wm_name==NULL); + + XGetClassHint(ioncore_g.dpy, cwin->win, &clss); + + for(; match!=NULL; match=match->next){ + + win_match=0; + + if(client_id || match->client_id){ + if(xstreq(match->client_id, client_id)){ + win_match+=2; + if(xstreq(match->window_role, window_role)) + win_match++; + } + } + if(win_match<3){ + if(xstreq(match->wclass, clss.res_class) && xstreq(match->winstance, clss.res_name)){ + win_match++; + if(win_match<3){ + if(xstreq(match->wm_cmd, wm_cmd)) + win_match++; + if(wm_name!=NULL && *wm_name!=NULL && + xstreq(match->wm_name, *wm_name)){ + win_match++; + } + } + } + } + if(win_match>2) + break; + } + XFree(client_id); + XFree(window_role); + XFreeStringList(wm_name); + free(wm_cmd); + return match; +} + +/* Returns frame_id of a match. Called from add_clientwin_alt in sm.c */ + +WPHolder *mod_sm_match_cwin_to_saved(WClientWin *cwin) +{ + WWinMatch *match=NULL; + WPHolder *ph=NULL; + + if((match=match_cwin(cwin))){ + ph=match->pholder; + match->pholder=NULL; + free_win_match(match); + } + + return ph; +} + + +bool mod_sm_add_match(WPHolder *ph, ExtlTab tab) +{ + WWinMatch *m=NULL; + + m=ALLOC(WWinMatch); + if(m==NULL) + return FALSE; + + m->client_id=NULL; + m->window_role=NULL; + m->winstance=NULL; + m->wclass=NULL; + m->wm_name=NULL; + m->wm_cmd=NULL; + + extl_table_gets_s(tab, "mod_sm_client_id", &(m->client_id)); + extl_table_gets_s(tab, "mod_sm_window_role", &(m->window_role)); + extl_table_gets_s(tab, "mod_sm_wclass", &(m->wclass)); + extl_table_gets_s(tab, "mod_sm_winstance", &(m->winstance)); + extl_table_gets_s(tab, "mod_sm_wm_name", &(m->wm_name)); + extl_table_gets_s(tab, "mod_sm_wm_cmd", &(m->wm_cmd)); + + m->pholder=ph; + + mod_sm_register_win_match(m); + + return TRUE; +} + + +void mod_sm_get_configuration(WClientWin *cwin, ExtlTab tab) +{ + XClassHint clss; + char *client_id=NULL, *window_role=NULL, *wm_cmd=NULL, **wm_name; + int n=0; + + if((client_id=mod_sm_get_client_id(cwin->win))){ + extl_table_sets_s(tab, "mod_sm_client_id", client_id); + XFree(client_id); + } + + + if((window_role=mod_sm_get_window_role(cwin->win))){ + extl_table_sets_s(tab, "mod_sm_window_role", window_role); + XFree(window_role); + } + + if(XGetClassHint(ioncore_g.dpy, cwin->win, &clss) != 0){ + extl_table_sets_s(tab, "mod_sm_wclass", clss.res_class); + extl_table_sets_s(tab, "mod_sm_winstance", clss.res_name); + } + + wm_name=xwindow_get_text_property(cwin->win, XA_WM_NAME, &n); + + if(n>0 && wm_name!=NULL){ + extl_table_sets_s(tab, "mod_sm_wm_name", *wm_name); + XFreeStringList(wm_name); + + } + + if((wm_cmd=mod_sm_get_window_cmd(cwin->win))){ + extl_table_sets_s(tab, "mod_sm_wm_cmd", wm_cmd); + free(wm_cmd); + } +} diff --git a/mod_sm/sm_matchwin.h b/mod_sm/sm_matchwin.h new file mode 100644 index 0000000..2dd45f3 --- /dev/null +++ b/mod_sm/sm_matchwin.h @@ -0,0 +1,47 @@ +/* + * ion/mod_sm/sm_mathcwin.c + * + * Copyright (c) Tuomo Valkonen 2004-2006. + * + * Based on the code of the 'sm' module for Ion1 by an unknown contributor. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_MOD_SM_MATCHWIN_H +#define ION_MOD_SM_MATCHWIN_H + +#include +#include +#include + +INTRSTRUCT(WWinMatch); + +DECLSTRUCT(WWinMatch){ + WPHolder *pholder; + + char *client_id; + char *window_role; + char *wclass; + char *winstance; + char *wm_name; + char *wm_cmd; + + WWinMatch *next, *prev; +}; + +extern WPHolder *mod_sm_match_cwin_to_saved(WClientWin *cwin); +extern void mod_sm_register_win_match(WWinMatch *match); +extern char *mod_sm_get_window_cmd(Window window); +extern char *mod_sm_get_client_id(Window window); +extern char *mod_sm_get_window_role(Window window); +extern bool mod_sm_have_match_list(); +extern void mod_sm_start_purge_timer(); + +extern bool mod_sm_add_match(WPHolder *ph, ExtlTab tab); +extern void mod_sm_get_configuration(WClientWin *cwin, ExtlTab tab); + +#endif /* ION_MOD_SM_MATCHWIN_H */ diff --git a/mod_sm/sm_session.c b/mod_sm/sm_session.c new file mode 100644 index 0000000..7472e8b --- /dev/null +++ b/mod_sm/sm_session.c @@ -0,0 +1,413 @@ +/* + * ion/mod_sm/sm_session.c + * + * Copyright (c) Tuomo Valkonen 2004-2006. + * + * Based on the code of the 'sm' module for Ion1 by an unknown contributor. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include "sm_session.h" + + +static IceConn ice_sm_conn=NULL; +static SmcConn sm_conn=NULL; +static int sm_fd=-1; + +static char *sm_client_id=NULL; +static char restart_hint=SmRestartImmediately; + +static Bool sent_save_done=FALSE; + +/* Function to be called when sm tells client save is complete */ +static void (*save_complete_fn)(); + +void mod_sm_set_ion_id(const char *client_id) +{ + if(sm_client_id) + free(sm_client_id); + + if(client_id==NULL) + sm_client_id=NULL; + else + sm_client_id=scopy(client_id); +} + +char *mod_sm_get_ion_id() +{ + return sm_client_id; +} + +/* Called when there's data to be read. + IcePcocessMessages determines message protocol, + unpacks the message and sends it to the client via + registered callbacks. */ + +static void sm_process_messages(int fd, void *data) +{ + Bool ret; + + if(IceProcessMessages(ice_sm_conn, NULL, &ret)==IceProcessMessagesIOError){ + mod_sm_close(); + } +} + +/* Callback triggered when an Ice connection is + opened or closed. */ + +static void sm_ice_watch_fd(IceConn conn, + IcePointer client_data, + Bool opening, + IcePointer *watch_data) +{ + if(opening){ + if(sm_fd!=-1){ /* shouldn't happen */ + warn(TR("Too many ICE connections.")); + } + else{ + sm_fd=IceConnectionNumber(conn); + cloexec_braindamage_fix(sm_fd); + mainloop_register_input_fd(sm_fd, NULL, &sm_process_messages); + } + } + else{ + if (IceConnectionNumber(conn)==sm_fd){ + mainloop_unregister_input_fd(sm_fd); + sm_fd=-1; + } + } +} + +/* Store restart information and stuff in the session manager */ + +static void sm_set_some_properties() +{ + SmPropValue program_val, userid_val; + SmProp program_prop, userid_prop, clone_prop; + SmProp *props[3]; + + props[0]=&program_prop; + props[1]=&userid_prop; + props[2]=&clone_prop; + + program_val.value=ioncore_g.argv[0]; + program_val.length=strlen(program_val.value); + program_prop.name=SmProgram; + program_prop.type=SmARRAY8; + program_prop.num_vals=1; + program_prop.vals=&program_val; + + userid_val.value=getenv("USER"); + userid_val.length=strlen(userid_val.value); + userid_prop.name=SmUserID; + userid_prop.type=SmARRAY8; + userid_prop.num_vals=1; + userid_prop.vals=&userid_val; + + clone_prop.name=SmCloneCommand; + clone_prop.type=SmLISTofARRAY8; + clone_prop.num_vals=1; + clone_prop.vals=&program_val; + + SmcSetProperties(sm_conn, + sizeof(props)/sizeof(props[0]), + (SmProp **)&props); +} + +static void sm_set_other_properties() +{ + char *restore="-session"; + char *clientid="-smclientid"; + char *rmprog="/bin/rm"; + char *rmarg="-rf"; + int nvals=0, i; + const char *sdir=NULL, *cid=NULL; + + SmPropValue discard_val[3]; + SmProp discard_prop={ SmDiscardCommand, SmLISTofARRAY8, 3, NULL }; + SmPropValue restart_hint_val, *restart_val=NULL; + SmProp restart_hint_prop={ SmRestartStyleHint, SmCARD8, 1, NULL}; + SmProp restart_prop={ SmRestartCommand, SmLISTofARRAY8, 0, NULL}; + + SmProp *props[2]; + + discard_prop.vals=discard_val; + restart_hint_prop.vals=&restart_hint_val; + + props[0]=&restart_prop; + props[1]=&restart_hint_prop; + /*props[2]=&discard_prop;*/ + + sdir=extl_sessiondir(); + cid=mod_sm_get_ion_id(); + + if(sdir==NULL || cid==NULL) + return; + + restart_hint_val.value=&restart_hint; + restart_hint_val.length=1; + + restart_val=(SmPropValue *)malloc((ioncore_g.argc+4)*sizeof(SmPropValue)); + for(i=0; i=0){ + mainloop_unregister_input_fd(sm_fd); + close(sm_fd); + sm_fd=-1; + } + + if(sm_client_id!=NULL){ + free(sm_client_id); + sm_client_id=NULL; + } +} + + +static void sm_exit() +{ + sm_die(sm_conn, NULL); +} + + +static void sm_restart() +{ + ioncore_do_restart(); +} + + +void mod_sm_smhook(int what) +{ + save_complete_fn=NULL; + + /* pending check? */ + + switch(what){ + case IONCORE_SM_RESIGN: + restart_hint=SmRestartIfRunning; + sm_set_properties(); + /*SmcRequestSaveYourself(sm_conn, SmSaveBoth, False, + SmInteractStyleAny, False, False); + save_complete_fn=&sm_exit;*/ + ioncore_do_exit(); + break; + case IONCORE_SM_SHUTDOWN: + restart_hint=SmRestartIfRunning; + SmcRequestSaveYourself(sm_conn, SmSaveBoth, True, + SmInteractStyleAny, False, True); + break; + case IONCORE_SM_RESTART: + restart_hint=SmRestartImmediately; + SmcRequestSaveYourself(sm_conn, SmSaveBoth, False, + SmInteractStyleAny, False, False); + save_complete_fn=&sm_exit; + break; + case IONCORE_SM_RESTART_OTHER: + restart_hint=SmRestartIfRunning; + SmcRequestSaveYourself(sm_conn, SmSaveBoth, False, + SmInteractStyleAny, False, False); + save_complete_fn=&sm_restart; + break; + case IONCORE_SM_SNAPSHOT: + restart_hint=SmRestartImmediately; + SmcRequestSaveYourself(sm_conn, SmSaveBoth, False, + SmInteractStyleAny, False, True); + break; + } +} + + diff --git a/mod_sm/sm_session.h b/mod_sm/sm_session.h new file mode 100644 index 0000000..306bf99 --- /dev/null +++ b/mod_sm/sm_session.h @@ -0,0 +1,23 @@ +/* + * ion/mod_sm/sm_session.h + * + * Copyright (c) Tuomo Valkonen 2004-2006. + * + * Based on the code of the 'sm' module for Ion1 by an unknown contributor. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_MOD_SM_SESSION_H +#define ION_MOD_SM_SESSION_H + +extern bool mod_sm_init_session(); +extern void mod_sm_set_ion_id(const char *client_id); +extern char *mod_sm_get_ion_id(); +extern void mod_sm_close(); +extern void mod_sm_smhook(int what); + +#endif /* ION_MOD_SM_BINDING_H */ diff --git a/mod_sp/Makefile b/mod_sp/Makefile new file mode 100644 index 0000000..e965d92 --- /dev/null +++ b/mod_sp/Makefile @@ -0,0 +1,27 @@ +## +## Scratchpad module Makefile +## + +# System-specific configuration is in system.mk +TOPDIR=.. +include $(TOPDIR)/build/system-inc.mk + +###################################### + +INCLUDES += $(X11_INCLUDES) $(LIBTU_INCLUDES) $(LIBEXTL_INCLUDES) -I.. +CFLAGS += $(XOPEN_SOURCE) $(C99_SOURCE) + +SOURCES=main.c +ETC=cfg_sp.lua + +MAKE_EXPORTS=mod_sp + +MODULE=mod_sp + +###################################### + +include $(TOPDIR)/build/rules.mk + +###################################### + +_install: module_install etc_install diff --git a/mod_sp/cfg_sp.lua b/mod_sp/cfg_sp.lua new file mode 100644 index 0000000..7b2c2ac --- /dev/null +++ b/mod_sp/cfg_sp.lua @@ -0,0 +1,19 @@ +-- +-- Ion mod_sp configuration file +-- + +defbindings("WScreen", { + bdoc("Toggle scratchpad."), + kpress(META.."space", "mod_sp.set_shown_on(_, 'toggle')"), + + -- A more ideal key for toggling the scratchpad would be the key left of + -- the key for numeral 1. Unfortunately the symbols mapped to this key + -- vary by the keyboard layout, and to be fully portable to different + -- architechtures and fancy keyboards, we can't rely on keycodes either. + -- However, on standard Finnish/Swedish (and other Nordic) keyboard + -- layouts the following should work: + --kpress(META.."section", "mod_sp.set_shown_on(_, 'toggle')"), + -- and on UK and US layouts this should work: + --kpress(META.."grave", "mod_sp.set_shown_on(_, 'toggle')"), +}) + diff --git a/mod_sp/main.c b/mod_sp/main.c new file mode 100644 index 0000000..b827ae2 --- /dev/null +++ b/mod_sp/main.c @@ -0,0 +1,206 @@ +/* + * ion/mod_sp/main.c + * + * Copyright (c) Tuomo Valkonen 2004-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "main.h" +#include "exports.h" + + +/*{{{ Module information */ + +#include "../version.h" + +char mod_sp_ion_api_version[]=ION_API_VERSION; + + +/*}}}*/ + + +/*{{{ Bindmaps, config, etc. */ + + +#define SP_NAME "*scratchpad*" + + +/*}}}*/ + + +/*{{{ Exports */ + + +static WRegion *create_frame_scratchpad(WWindow *parent, const WFitParams *fp, + void *unused) +{ + return (WRegion*)create_frame(parent, fp, FRAME_MODE_UNKNOWN); +} + + +static WFrame *create(WMPlex *mplex, int flags) +{ + WFrame *sp; + WMPlexAttachParams par; + int sw=REGION_GEOM(mplex).w, sh=REGION_GEOM(mplex).h; + + par.flags=(flags + |MPLEX_ATTACH_UNNUMBERED + |MPLEX_ATTACH_MODAL + |MPLEX_ATTACH_SIZEPOLICY + |MPLEX_ATTACH_GEOM); + par.szplcy=SIZEPOLICY_FREE_GLUE; + + par.geom.w=minof(sw, CF_SCRATCHPAD_DEFAULT_W); + par.geom.h=minof(sh, CF_SCRATCHPAD_DEFAULT_H); + par.geom.x=(sw-par.geom.w)/2; + par.geom.y=(sh-par.geom.h)/2; + + sp=(WFrame*)mplex_do_attach_new((WMPlex*)mplex, &par, + create_frame_scratchpad, + NULL); + + + if(sp==NULL){ + warn(TR("Unable to create scratchpad.")); + } + + region_set_name((WRegion*)sp, SP_NAME); + + return sp; +} + + +static bool is_scratchpad(WRegion *reg) +{ + char *nm=reg->ni.name; + int inst_off=reg->ni.inst_off; + + if(nm==NULL) + return FALSE; + + if(inst_off<0) + return (strcmp(nm, SP_NAME)==0); + else + return (strncmp(nm, SP_NAME, inst_off)==0); +} + + +/*EXTL_DOC + * Change displayed status of some scratchpad on \var{mplex} if one is + * found. The parameter \var{how} is one of (set/unset/toggle). + */ +EXTL_EXPORT +bool mod_sp_set_shown_on(WMPlex *mplex, const char *how) +{ + int setpar=libtu_setparam_invert(libtu_string_to_setparam(how)); + WMPlexIterTmp tmp; + WRegion *reg; + bool found=FALSE; + + FOR_ALL_MANAGED_BY_MPLEX(mplex, reg, tmp){ + if(is_scratchpad(reg)){ + mplex_set_hidden(mplex, reg, setpar); + found=TRUE; + } + } + + if(!found){ + int sp=libtu_string_to_setparam(how); + if(sp==SETPARAM_SET || sp==SETPARAM_TOGGLE) + found=(create(mplex, 0)!=NULL); + } + + return found; +} + + +/*EXTL_DOC + * Toggle displayed status of \var{sp}. + * The parameter \var{how} is one of (set/unset/toggle). + */ +EXTL_EXPORT +bool mod_sp_set_shown(WFrame *sp, const char *how) +{ + if(sp!=NULL){ + int setpar=libtu_setparam_invert(libtu_string_to_setparam(how)); + WMPlex *mplex=OBJ_CAST(REGION_MANAGER(sp), WMPlex); + if(mplex!=NULL) + return mplex_set_hidden(mplex, (WRegion*)sp, setpar); + } + + return FALSE; +} + + +/*}}}*/ + + +/*{{{ Init & deinit */ + + +void mod_sp_deinit() +{ + mod_sp_unregister_exports(); +} + + +static void check_and_create() +{ + WMPlexIterTmp tmp; + WScreen *scr; + WRegion *reg; + + /* No longer needed, free the memory the list uses. */ + hook_remove(ioncore_post_layout_setup_hook, check_and_create); + + FOR_ALL_SCREENS(scr){ + FOR_ALL_MANAGED_BY_MPLEX((WMPlex*)scr, reg, tmp){ + if(is_scratchpad(reg)) + return; + } + + create(&scr->mplex, MPLEX_ATTACH_HIDDEN); + } +} + + +bool mod_sp_init() +{ + if(!mod_sp_register_exports()) + return FALSE; + + extl_read_config("cfg_sp", NULL, FALSE); + + if(ioncore_g.opmode==IONCORE_OPMODE_INIT){ + hook_add(ioncore_post_layout_setup_hook, check_and_create); + }else{ + check_and_create(); + } + + return TRUE; +} + + +/*}}}*/ + diff --git a/mod_sp/main.h b/mod_sp/main.h new file mode 100644 index 0000000..f290b35 --- /dev/null +++ b/mod_sp/main.h @@ -0,0 +1,25 @@ +/* + * ion/mod_sp/main.h + * + * Copyright (c) Tuomo Valkonen 2004-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_MOD_SP_MAIN_H +#define ION_MOD_SP_MAIN_H + +#include + +extern bool mod_sp_init(); +extern void mod_sp_deinit(); + +extern WBindmap *mod_sp_scratchpad_bindmap; + +#define CF_SCRATCHPAD_DEFAULT_W 640 +#define CF_SCRATCHPAD_DEFAULT_H 480 + +#endif /* ION_MOD_SP_MAIN_H */ diff --git a/mod_statusbar/Makefile b/mod_statusbar/Makefile new file mode 100644 index 0000000..0a13f4d --- /dev/null +++ b/mod_statusbar/Makefile @@ -0,0 +1,34 @@ +## +## Ion mod_statusbar Makefile +## + +# System-specific configuration is in system.mk +TOPDIR=.. +include $(TOPDIR)/build/system-inc.mk + +###################################### + +INCLUDES += $(X11_INCLUDES) $(LIBTU_INCLUDES) $(LIBEXTL_INCLUDES) -I.. +CFLAGS += $(XOPEN_SOURCE) $(C99_SOURCE) + +SOURCES = main.c statusbar.c draw.c + +SUBDIRS = ion-statusd +INSTALL_SUBDIRS = $(SUBDIRS) + +MAKE_EXPORTS = mod_statusbar + +MODULE = mod_statusbar +MODULE_STUB = mod_statusbar.lua + +###################################### + +include $(TOPDIR)/build/rules.mk + +###################################### + +_install: module_install symlink_install + +symlink_install: + rm -f $(LCDIR)/ext_statusbar.lc + ln -s mod_statusbar.lc $(LCDIR)/ext_statusbar.lc diff --git a/mod_statusbar/draw.c b/mod_statusbar/draw.c new file mode 100644 index 0000000..29e9f02 --- /dev/null +++ b/mod_statusbar/draw.c @@ -0,0 +1,183 @@ +/* + * ion/mod_statusbar/draw.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include + +#include +#include +#include "statusbar.h" +#include "draw.h" + + +static void calc_elems_x(WRectangle *g, WSBElem *elems, int nelems) +{ + int x=g->x; + + while(nelems>0){ + elems->x=x; + if(elems->type==WSBELEM_STRETCH) + x+=elems->text_w+elems->stretch; + else + x+=elems->text_w; + + nelems--; + elems++; + } +} + + +static void calc_elems_x_ra(WRectangle *g, WSBElem *elems, int nelems) +{ + int x=g->x+g->w; + + elems+=nelems-1; + + while(nelems>0){ + if(elems->type==WSBELEM_STRETCH) + x-=elems->text_w+elems->stretch; + else + x-=elems->text_w; + elems->x=x; + + elems--; + nelems--; + } +} + + +void statusbar_calculate_xs(WStatusBar *sb) +{ + WRectangle g; + GrBorderWidths bdw; + WMPlex *mgr=NULL; + bool right_align=FALSE; + int minx, maxx; + int nleft=0, nright=0; + + if(sb->brush==NULL || sb->elems==NULL) + return; + + grbrush_get_border_widths(sb->brush, &bdw); + + g.x=0; + g.y=0; + g.w=REGION_GEOM(sb).w; + g.h=REGION_GEOM(sb).h; + + mgr=OBJ_CAST(REGION_PARENT(sb), WMPlex); + if(mgr!=NULL){ + WRegion *std=NULL; + WMPlexSTDispInfo din; + din.pos=MPLEX_STDISP_TL; + mplex_get_stdisp(mgr, &std, &din); + if(std==(WRegion*)sb) + right_align=(din.pos==MPLEX_STDISP_TR || din.pos==MPLEX_STDISP_BR); + } + + g.x+=bdw.left; + g.w-=bdw.left+bdw.right; + g.y+=bdw.top; + g.h-=bdw.top+bdw.bottom; + + if(sb->filleridx>=0){ + nleft=sb->filleridx; + nright=sb->nelems-(sb->filleridx+1); + }else if(!right_align){ + nleft=sb->nelems; + nright=0; + }else{ + nleft=0; + nright=sb->nelems; + } + + if(nleft>0) + calc_elems_x(&g, sb->elems, nleft); + + if(nright>0) + calc_elems_x_ra(&g, sb->elems+sb->nelems-nright, nright); +} + + + +static void draw_elems(GrBrush *brush, WRectangle *g, int ty, + WSBElem *elems, int nelems, bool needfill, + const char *dfltattr, bool complete) +{ + int prevx=g->x; + int maxx=g->x+g->w; + + while(nelems>0){ + if(prevxx){ + g->x=prevx; + g->w=elems->x-prevx; + grbrush_clear_area(brush, g); + } + + if(elems->type==WSBELEM_TEXT || elems->type==WSBELEM_METER){ + const char *s=(elems->text!=NULL + ? elems->text + : STATUSBAR_NX_STR); + grbrush_draw_string(brush, elems->x, ty, s, strlen(s), needfill, + elems->attr ? elems->attr : dfltattr); + prevx=elems->x+elems->text_w; + } + elems++; + nelems--; + } + + if(prevxx=prevx; + g->w=maxx-prevx; + grbrush_clear_area(brush, g); + } +} + + +void statusbar_draw(WStatusBar *sb, bool complete) +{ + WRectangle g; + GrBorderWidths bdw; + GrFontExtents fnte; + Window win=sb->wwin.win; + int ty; + + if(sb->brush==NULL) + return; + + grbrush_get_border_widths(sb->brush, &bdw); + grbrush_get_font_extents(sb->brush, &fnte); + + g.x=0; + g.y=0; + g.w=REGION_GEOM(sb).w; + g.h=REGION_GEOM(sb).h; + + grbrush_begin(sb->brush, &g, (complete ? 0 : GRBRUSH_NO_CLEAR_OK)); + + grbrush_draw_border(sb->brush, &g, NULL); + + if(sb->elems==NULL) + return; + + g.x+=bdw.left; + g.w-=bdw.left+bdw.right; + g.y+=bdw.top; + g.h-=bdw.top+bdw.bottom; + + ty=(g.y+fnte.baseline+(g.h-fnte.max_height)/2); + + draw_elems(sb->brush, &g, ty, sb->elems, sb->nelems, + TRUE, NULL, complete); + + grbrush_end(sb->brush); +} + + diff --git a/mod_statusbar/draw.h b/mod_statusbar/draw.h new file mode 100644 index 0000000..c30a86f --- /dev/null +++ b/mod_statusbar/draw.h @@ -0,0 +1,21 @@ +/* + * ion/mod_statusbar/draw.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_MOD_STATUSBAR_DRAW_H +#define ION_MOD_STATUSBAR_DRAW_H + +#include +#include "statusbar.h" + +extern void statusbar_draw(WStatusBar *sb, bool complete); +extern void statusbar_calculate_xs(WStatusBar *sb); + +#endif /* ION_MOD_STATUSBAR_DRAW_H */ diff --git a/mod_statusbar/ion-statusd/Makefile b/mod_statusbar/ion-statusd/Makefile new file mode 100644 index 0000000..1ed1ac9 --- /dev/null +++ b/mod_statusbar/ion-statusd/Makefile @@ -0,0 +1,40 @@ +## +## Ion-statusd Makefile +## + +# System-specific configuration is in system.mk +TOPDIR=../.. +include $(TOPDIR)/build/system-inc.mk + +###################################### + +INCLUDES += $(LIBMAINLOOP_INCLUDES) $(LIBEXTL_INCLUDES) $(LIBTU_INCLUDES) +LIBS += $(LIBMAINLOOP_LIBS) $(LIBEXTL_LIBS) $(LIBTU_LIBS) $(LUA_LIBS) $(DL_LIBS) -lm +CFLAGS += $(XOPEN_SOURCE) $(C99_SOURCE) + +DEFINES += -DETCDIR=\"$(ETCDIR)\" -DSHAREDIR=\"$(SHAREDIR)\" \ + -DEXTRABINDIR=\"$(EXTRABINDIR)\" -DMODULEDIR=\"$(MODULEDIR)\" \ + -DLCDIR=\"$(LCDIR)\" -DLOCALEDIR=\"$(LOCALEDIR)\" + +SOURCES = ion-statusd.c exec.c extlrx.c + +TARGETS = ion-statusd + +LUA_SOURCES = statusd_date.lua statusd_mail.lua statusd_load.lua + +MAKE_EXPORTS = statusd + +include $(TOPDIR)/libmainloop/rx.mk + +###################################### + +include $(TOPDIR)/build/rules.mk + +###################################### + +ion-statusd: $(OBJS) $(EXT_OBJS) + $(CC) $(LINKOPTS) $(OBJS) $(EXT_OBJS) $(LDFLAGS) -o $@ + +_install: lc_install + $(INSTALLDIR) $(EXTRABINDIR) + $(INSTALL) -s -m $(BIN_MODE) ion-statusd $(EXTRABINDIR) diff --git a/mod_statusbar/ion-statusd/exec.c b/mod_statusbar/ion-statusd/exec.c new file mode 100644 index 0000000..a42c0dc --- /dev/null +++ b/mod_statusbar/ion-statusd/exec.c @@ -0,0 +1,39 @@ +/* + * ion/mod_statusbar/ion-statusd/exec.c + * + * Copyright (c) Tuomo Valkonen 2005-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + + +#include +#include + + +/*EXTL_DOC + * Run \var{cmd} in the background. + */ +EXTL_SAFE +EXTL_EXPORT +int statusd_exec(const char *cmd) +{ + return mainloop_spawn(cmd); +} + + +/*EXTL_DOC + * Run \var{cmd} with a read pipe connected to its stdout. + * When data is received through the pipe, \var{h} is called + * with that data. + */ +EXTL_SAFE +EXTL_EXPORT +int statusd_popen_bgread(const char *cmd, ExtlFn h, ExtlFn errh) +{ + return mainloop_popen_bgread(cmd, NULL, NULL, h, errh); +} + diff --git a/mod_statusbar/ion-statusd/extlrx.c b/mod_statusbar/ion-statusd/extlrx.c new file mode 100644 index 0000000..9b6ac58 --- /dev/null +++ b/mod_statusbar/ion-statusd/extlrx.c @@ -0,0 +1,42 @@ +/* + * ion/mod_statusbar/ion-statusd/extlrx.c + * + * Copyright (c) Tuomo Valkonen 2004-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include + + +/*{{{ libtu */ + + +/*EXTL_DOC + * Issue a warning. How the message is displayed depends on the current + * warning handler. + */ +EXTL_EXPORT +void statusd_warn(const char *str) +{ + warn("%s", str); +} + + +EXTL_EXPORT +const char *statusd_gettext(const char *s) +{ + if(s==NULL) + return NULL; + else + return TR(s); +} + + +/*}}}*/ + diff --git a/mod_statusbar/ion-statusd/ion-statusd.c b/mod_statusbar/ion-statusd/ion-statusd.c new file mode 100644 index 0000000..0e0cdb0 --- /dev/null +++ b/mod_statusbar/ion-statusd/ion-statusd.c @@ -0,0 +1,291 @@ +/* + * ion/mod_statusbar/ion-statusd/ion-statusd.c + * + * Copyright (c) Tuomo Valkonen 2004-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef CF_NO_LOCALE +#include +#endif + +#include "../../version.h" + + +static OptParserOpt ion_opts[]={ + /*{OPT_ID('d'), "display", OPT_ARG, "host:dpy.scr", + DUMMY_TR("X display to use")},*/ + + {'c', "conffile", OPT_ARG, "config_file", + DUMMY_TR("Configuration file")}, + + {'s', "searchdir", OPT_ARG, "dir", + DUMMY_TR("Add directory to search path")}, + + /*{OPT_ID('s'), "session", OPT_ARG, "session_name", + DUMMY_TR("Name of session (affects savefiles)")},*/ + + {'h', "help", 0, NULL, + DUMMY_TR("Show this help")}, + + {'V', "version", 0, NULL, + DUMMY_TR("Show program version")}, + + {OPT_ID('a'), "about", 0, NULL, + DUMMY_TR("Show about text")}, + + {'q', "quiet", 0, NULL, + DUMMY_TR("Quiet mode")}, + + {'m', "meter", OPT_ARG, "meter_module", + DUMMY_TR("Load a meter module")}, + + END_OPTPARSEROPTS +}; + + +static const char statusd_copy[]= + "Ion-statusd " ION_VERSION ", copyright (c) Tuomo Valkonen 2004-2005."; + + +static const char statusd_license[]=DUMMY_TR( + "This program is free software; you can redistribute it and/or\n" + "modify it under the terms of the GNU Lesser General Public\n" + "License as published by the Free Software Foundation; either\n" + "version 2.1 of the License, or (at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n" + "Lesser General Public License for more details.\n"); + + +/* new_informs=TRUE because we should always print period when + * initialisation is done. + */ +static bool new_informs=TRUE; +static ExtlTab configtab; + +static void help() +{ + int i; + printf(TR("Usage: %s [options]\n\n"), prog_execname()); + for(i=0; ion_opts[i].descr!=NULL; i++) + ion_opts[i].descr=TR(ion_opts[i].descr); + optparser_printhelp(OPTP_MIDLONG, ion_opts); + printf("\n"); +} + + +static void flush_informs() +{ + if(new_informs){ + printf(".\n"); + fflush(stdout); + new_informs=FALSE; + } +} + + +static void mainloop() +{ + sigset_t trapset; + + sigemptyset(&trapset); + sigaddset(&trapset, SIGALRM); + sigaddset(&trapset, SIGCHLD); + mainloop_trap_signals(&trapset); + + while(1){ + int kill_sig=mainloop_check_signals(); + if(kill_sig!=0 && kill_sig!=SIGUSR1){ + if(kill_sig==SIGTERM) + exit(EXIT_FAILURE); + else + kill(getpid(), kill_sig); + } + + mainloop_execute_deferred(); + + flush_informs(); + + mainloop_select(); + } +} + + +extern bool statusd_register_exports(); +extern void statusd_unregister_exports(); + + +static void stdout_closed(int fd, void *data) +{ + exit(EXIT_SUCCESS); +} + + +int main(int argc, char*argv[]) +{ + const char *mod=NULL; + char *mod2=NULL; + int loaded=0; + int opt; + bool quiet=FALSE; + +#ifndef CF_NO_LOCALE + if(setlocale(LC_ALL, "")==NULL) + warn("setlocale() call failed."); +#endif + + configtab=extl_table_none(); + + libtu_init(argv[0]); + extl_init(); + + if(!statusd_register_exports()) + return EXIT_FAILURE; + + extl_add_searchdir(EXTRABINDIR); + extl_add_searchdir(MODULEDIR); + extl_add_searchdir(ETCDIR); + extl_add_searchdir(SHAREDIR); + extl_add_searchdir(LCDIR); + extl_set_userdirs("ion3"); + + optparser_init(argc, argv, OPTP_MIDLONG, ion_opts); + + extl_read_config("ioncore_luaext", NULL, TRUE); + + while((opt=optparser_get_opt())){ + switch(opt){ + /*case OPT_ID('d'): + display=optparser_get_arg(); + break;*/ + case 's': + extl_add_searchdir(optparser_get_arg()); + break; + /*case OPT_ID('s'): + extl_set_sessiondir(optparser_get_arg()); + break;*/ + case 'h': + help(); + return EXIT_SUCCESS; + case 'V': + printf("%s\n", ION_VERSION); + return EXIT_SUCCESS; + case OPT_ID('a'): + printf("%s\n\n%s", statusd_copy, TR(statusd_license)); + return EXIT_SUCCESS; + case 'c': + { + ExtlTab t; + const char *f=optparser_get_arg(); + if(extl_read_savefile(f, &t)){ + extl_unref_table(configtab); + configtab=t; + }else{ + warn(TR("Unable to load configuration file %s"), f); + } + } + break; + case 'q': + quiet=TRUE; + break; + case 'm': + mod=optparser_get_arg(); + if(strchr(mod, '/')==NULL && strchr(mod, '.')==NULL){ + mod2=scat("statusd_", mod); + if(mod2==NULL) + return EXIT_FAILURE; + mod=mod2; + } + if(extl_read_config(mod, NULL, !quiet)) + loaded++; + if(mod2!=NULL) + free(mod2); + break; + default: + warn(TR("Invalid command line.")); + help(); + return EXIT_FAILURE; + } + } + + if(loaded==0 && !quiet){ + warn(TR("No meters loaded.")); + return EXIT_FAILURE; + } + + mainloop(); + + return EXIT_SUCCESS; +} + + +/*EXTL_DOC + * Inform that meter \var{name} has value \var{value}. + */ +EXTL_EXPORT +void statusd_inform(const char *name, const char *value) +{ + printf("%s: %s\n", name, value); + new_informs=TRUE; +} + + +/*EXTL_DOC + * Get configuration table for module \var{name} + */ +EXTL_EXPORT +ExtlTab statusd_get_config(const char *name) +{ + if(name==NULL){ + return extl_ref_table(configtab); + }else{ + ExtlTab t; + if(extl_table_gets_t(configtab, name, &t)) + return t; + else + return extl_create_table(); + } +} + + +/*EXTL_DOC + * Get last file modification time. + */ +EXTL_EXPORT +double statusd_last_modified(const char *fname) +{ + struct stat st; + + if(fname==NULL) + return (double)(-1); + + if(stat(fname, &st)!=0){ + /*warn_err_obj(fname);*/ + return (double)(-1); + } + + return (double)(st.st_mtime>st.st_ctime ? st.st_mtime : st.st_ctime); +} + diff --git a/mod_statusbar/ion-statusd/statusd_date.lua b/mod_statusbar/ion-statusd/statusd_date.lua new file mode 100644 index 0000000..ea36258 --- /dev/null +++ b/mod_statusbar/ion-statusd/statusd_date.lua @@ -0,0 +1,41 @@ +-- +-- ion/mod_statusbar/ion-statusd/statusd_date.lua +-- +-- Copyright (c) Tuomo Valkonen 2004-2006. +-- +-- Ion is free software; you can redistribute it and/or modify it under +-- the terms of the GNU Lesser General Public License as published by +-- the Free Software Foundation; either version 2.1 of the License, or +-- (at your option) any later version. +-- + + +local timer + +local defaults={ + date_format='%a %Y-%m-%d %H:%M', + formats={}, +} + +local settings=table.join(statusd.get_config('date'), defaults) + +local function update() + local tm=os.time() + statusd.inform('date', os.date(settings.date_format, tm)) + for k, f in pairs(settings.formats) do + statusd.inform('date_'..k, os.date(f, tm)) + end + return tm +end + +local function timer_handler(tmr) + local tm=update() + + local t=os.date('*t', tm) + local d=(60-t.sec)*1000 + + timer:set(d, timer_handler) +end + +timer=statusd.create_timer() +timer_handler(timer) diff --git a/mod_statusbar/ion-statusd/statusd_load.lua b/mod_statusbar/ion-statusd/statusd_load.lua new file mode 100644 index 0000000..cd62178 --- /dev/null +++ b/mod_statusbar/ion-statusd/statusd_load.lua @@ -0,0 +1,99 @@ +-- +-- ion/mod_statusbar/ion-statusd/statusd_load.lua +-- +-- Copyright (c) Tuomo Valkonen 2004-2006. +-- +-- Ion is free software; you can redistribute it and/or modify it under +-- the terms of the GNU Lesser General Public License as published by +-- the Free Software Foundation; either version 2.1 of the License, or +-- (at your option) any later version. +-- + +-- +-- We should really use getloadavg(3) instead and move the meter to +-- Ion side to get properly up-to-date loads. But until such an export +-- is made, and we use potentially blocking files and external programs, +-- this meter must be in ion-statusd. +-- + +local defaults={ + update_interval=10*1000, + load_hint=1, + important_threshold=1.5, + critical_threshold=4.0 +} + +local settings=table.join(statusd.get_config("load"), defaults) + +local loadpat='^(%d+%.%d+).*(%d+%.%d+).*(%d+%.%d+)' + +local function get_load_proc() + local f=io.open('/proc/loadavg', 'r') + if not f then + return "" + end + local s=f:read('*l') + f:close() + local st, en, load=string.find(s, '^(%d+%.%d+ %d+%.%d+ %d+%.%d+)') + + return string.gsub((load or ""), " ", ", ") +end + +local function get_load_uptime() + local f=io.popen('uptime', 'r') + if not f then + return "??" + end + local s=f:read('*l') + f:close() + local st, en, load=string.find(s, 'load averages?:%s*(.*)') + return (load or "") +end + +local function detect_load_fn() + if get_load_proc()~="" then + return get_load_proc + else + return get_load_uptime + end +end + +local get_load, load_timer + +local function get_hint(l) + local v=tonumber(l) + local i="normal" + if v then + if v>settings.critical_threshold then + i="critical" + elseif v>settings.important_threshold then + i="important" + end + end + return i +end + +local l1min, l5min, l15min=2+1, 2+2, 2+3 + +local function update_load() + local l = get_load() + local lds={string.find(l, loadpat)} + statusd.inform("load", l) + statusd.inform("load_hint", get_hint(lds[settings.load_hint+2])) + statusd.inform("load_1min", lds[l1min]) + statusd.inform("load_1min_hint", get_hint(lds[l1min])) + statusd.inform("load_5min", lds[l5min]) + statusd.inform("load_5min_hint", get_hint(lds[l5min])) + statusd.inform("load_15min", lds[l15min]) + statusd.inform("load_15min_hint", get_hint(lds[l15min])) + load_timer:set(settings.update_interval, update_load) +end + +-- Init +--statusd.inform("load_template", "0.00, 0.00, 0.00"); + +get_load=detect_load_fn() +load_timer=statusd.create_timer() +update_load() + + diff --git a/mod_statusbar/ion-statusd/statusd_mail.lua b/mod_statusbar/ion-statusd/statusd_mail.lua new file mode 100644 index 0000000..71cfea9 --- /dev/null +++ b/mod_statusbar/ion-statusd/statusd_mail.lua @@ -0,0 +1,156 @@ +-- +-- ion/mod_statusbar/ion-statusd/statusd_mail.lua +-- +-- Copyright (c) Tuomo Valkonen 2004-2006. +-- +-- Ion is free software; you can redistribute it and/or modify it under +-- the terms of the GNU Lesser General Public License as published by +-- the Free Software Foundation; either version 2.1 of the License, or +-- (at your option) any later version. +-- + +-- The keyword for this monitor +local mon = "mail" + +local defaults={ + update_interval=10*1000, + retry_interval=60*10*1000, + mbox = os.getenv("MAIL"), + files = {} +} + +local settings=table.join(statusd.get_config(mon), defaults) + +local function TR(s, ...) + return string.format(statusd.gettext(s), unpack(arg)) +end + +local function check_spool() + if not settings.mbox then + statusd.warn(TR("MAIL environment variable not set ".. + "and no spool given.")) + end +end + +if not settings.files["spool"] then + check_spool() + settings.files["spool"] = settings.mbox +elseif not(settings.files["spool"] == mbox) then + statusd.warn(TR("%s.mbox does not match %s.files['spool']; using %s.mbox", + mon, mon, mon)) + check_spool() + settings.files["spool"] = settings.mbox +end + +local function calcmail(fname) + local f, err=io.open(fname, 'r') + local total, read, old=0, 0, 0 + local had_blank=true + local in_headers=false + local had_status=false + + if not f then + statusd.warn(err) + return + end + + for l in f:lines() do + if had_blank and string.find(l, '^From ') then + total=total+1 + had_status=false + in_headers=true + had_blank=false + else + had_blank=false + if l=="" then + if in_headers then + in_headers=false + end + had_blank=true + elseif in_headers and not had_status then + local st, en, stat=string.find(l, '^Status:(.*)') + if stat then + had_status=true + if string.find(l, 'R') then + read=read+1 + end + if string.find(l, 'O') then + old=old+1 + end + end + end + end + end + + f:close() + + return total, total-read, total-old +end + + +local mail_timer +local mail_timestamps = {} +function init_timestamps () + for key, val in pairs(settings.files) do + mail_timestamps[key]=-2.0 + end +end +init_timestamps() + +local function update_mail() + local failed + for key, mbox in pairs(settings.files) do + if not mbox then + error(TR(key.." not set")) + end + + local old_tm=mail_timestamps[key] + mail_timestamps[key]=statusd.last_modified(mbox) + + if mail_timestamps[key]>old_tm then + local mail_total, mail_unread, mail_new=calcmail(mbox) + if failed == nil then + failed = not mail_total + else + failed = failed and (not mail_total) + end + + if key == "spool" then + meter=mon + else + meter=mon.."_"..key + end + if mail_total then + statusd.inform(meter.."_new", tostring(mail_new)) + statusd.inform(meter.."_unread", tostring(mail_unread)) + statusd.inform(meter.."_total", tostring(mail_total)) + + if mail_new>0 then + statusd.inform(meter.."_new_hint", "important") + else + statusd.inform(meter.."_new_hint", "normal") + end + + if mail_unread>0 then + statusd.inform(meter.."_unread_hint", "important") + else + statusd.inform(meter.."_unread_hint", "normal") + end + end + end + end + + if failed then + statusd.warn(TR("Disabling mail monitor for %d seconds.", + settings.retry_interval/1000)) + init_timestamps() + mail_timer:set(settings.retry_interval, update_mail) + return + end + + mail_timer:set(settings.update_interval, update_mail) +end + +mail_timer=statusd.create_timer() +update_mail() + diff --git a/mod_statusbar/main.c b/mod_statusbar/main.c new file mode 100644 index 0000000..9ec4f20 --- /dev/null +++ b/mod_statusbar/main.c @@ -0,0 +1,312 @@ +/* + * ion/mod_statusbar/main.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "statusbar.h" +#include "exports.h" + + +#define CF_STATUSD_TIMEOUT_SEC 5 + + +/*{{{ Module information */ + + +#include "../version.h" + +char mod_statusbar_ion_api_version[]=ION_API_VERSION; + + +/*}}}*/ + + +/*{{{ Bindmaps */ + + +WBindmap *mod_statusbar_statusbar_bindmap=NULL; + + +/*}}}*/ + + +/*{{{ Statusd launch helper */ + + +#define BL 1024 + + +static bool process_pipe(int fd, ExtlFn fn, + bool *doneseen, bool *eagain) +{ + char buf[BL]; + int n; + bool fnret; + + *eagain=FALSE; + + n=read(fd, buf, BL-1); + + if(n<0){ + if(errno==EAGAIN || errno==EINTR){ + *eagain=(errno==EAGAIN); + return TRUE; + } + warn_err_obj(TR("reading a pipe")); + return FALSE; + }else if(n>0){ + buf[n]='\0'; + *doneseen=FALSE; + return extl_call(fn, "s", "b", &buf, doneseen); + } + + return FALSE; +} + + +#define USEC 1000000 + + +static bool wait_statusd_init(int outfd, int errfd, ExtlFn dh, ExtlFn eh) +{ + fd_set rfds; + struct timeval tv, endtime, now; + int nfds=maxof(outfd, errfd); + int retval; + bool dummy, doneseen, eagain=FALSE; + const char *timeout_msg=TR("ion-statusd launch timeout."); + + if(gettimeofday(&endtime, NULL)!=0){ + warn_err(); + return FALSE; + } + + now=endtime; + endtime.tv_sec+=CF_STATUSD_TIMEOUT_SEC; + + while(1){ + FD_ZERO(&rfds); + + /* Calculate remaining time */ + if(now.tv_sec>endtime.tv_sec){ + goto timeout; + }else if(now.tv_sec==endtime.tv_sec){ + if(now.tv_usec>=endtime.tv_usec) + goto timeout; + tv.tv_sec=0; + tv.tv_usec=endtime.tv_usec-now.tv_usec; + }else{ + tv.tv_usec=USEC+endtime.tv_usec-now.tv_usec; + tv.tv_sec=-1+endtime.tv_sec-now.tv_sec; + /* Kernel lameness tuner: */ + tv.tv_sec+=tv.tv_usec/USEC; + tv.tv_usec%=USEC; + } + + FD_SET(outfd, &rfds); + FD_SET(errfd, &rfds); + + retval=select(nfds+1, &rfds, NULL, NULL, &tv); + if(retval>0){ + if(FD_ISSET(errfd, &rfds)){ + if(!process_pipe(errfd, eh, &dummy, &eagain)) + return FALSE; + } + if(FD_ISSET(outfd, &rfds)){ + if(!process_pipe(outfd, dh, &doneseen, &eagain)) + return FALSE; + if(doneseen){ + /* Read rest of errors. */ + bool ok; + do{ + ok=process_pipe(errfd, eh, &dummy, &eagain); + }while(ok && !eagain); + return TRUE; + } + } + }else if(retval==0){ + goto timeout; + } + + if(gettimeofday(&now, NULL)!=0){ + warn_err(); + return FALSE; + } + } + + return TRUE; + +timeout: + warn(TR("ion-statusd timed out.")); + return FALSE; +} + + +EXTL_EXPORT +int mod_statusbar__launch_statusd(const char *cmd, + ExtlFn initdatahandler, + ExtlFn initerrhandler, + ExtlFn datahandler, + ExtlFn errhandler) +{ + pid_t pid; + int outfd=-1, errfd=-1; + + if(cmd==NULL) + return -1; + + pid=mainloop_do_spawn(cmd, NULL, NULL, + NULL, &outfd, &errfd); + + if(pid<0) + return -1; + + if(!wait_statusd_init(outfd, errfd, initdatahandler, initerrhandler)) + goto err; + + if(!mainloop_register_input_fd_extlfn(outfd, datahandler)) + goto err; + + if(!mainloop_register_input_fd_extlfn(errfd, errhandler)) + goto err2; + + return pid; + +err2: + mainloop_unregister_input_fd(outfd); +err: + close(outfd); + close(errfd); + return -1; +} + + +/*}}}*/ + + +/*{{{ Systray */ + + +static bool is_systray(WClientWin *cwin) +{ + static Atom atom__kde_net_wm_system_tray_window_for=None; + Atom actual_type=None; + int actual_format; + unsigned long nitems; + unsigned long bytes_after; + unsigned char *prop; + char *dummy; + bool is=FALSE; + + if(extl_table_gets_s(cwin->proptab, "statusbar", &dummy)){ + free(dummy); + return TRUE; + } + + if(atom__kde_net_wm_system_tray_window_for==None){ + atom__kde_net_wm_system_tray_window_for=XInternAtom(ioncore_g.dpy, + "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR", + False); + } + if(XGetWindowProperty(ioncore_g.dpy, cwin->win, + atom__kde_net_wm_system_tray_window_for, 0, + sizeof(Atom), False, AnyPropertyType, + &actual_type, &actual_format, &nitems, + &bytes_after, &prop)==Success){ + if(actual_type!=None){ + is=TRUE; + } + XFree(prop); + } + + return is; +} + + +static bool clientwin_do_manage_hook(WClientWin *cwin, const WManageParams *param) +{ + WStatusBar *sb=NULL; + + if(!is_systray(cwin)) + return FALSE; + + sb=mod_statusbar_find_suitable(cwin, param); + if(sb==NULL) + return FALSE; + + return region_manage_clientwin((WRegion*)sb, cwin, param, + MANAGE_REDIR_PREFER_NO); +} + + +/*}}}*/ + + +/*{{{ Init & deinit */ + + +void mod_statusbar_deinit() +{ + hook_remove(clientwin_do_manage_alt, + (WHookDummy*)clientwin_do_manage_hook); + + if(mod_statusbar_statusbar_bindmap!=NULL){ + ioncore_free_bindmap("WStatusBar", mod_statusbar_statusbar_bindmap); + mod_statusbar_statusbar_bindmap=NULL; + } + + ioncore_unregister_regclass(&CLASSDESCR(WStatusBar)); + + mod_statusbar_unregister_exports(); +} + + +bool mod_statusbar_init() +{ + mod_statusbar_statusbar_bindmap=ioncore_alloc_bindmap("WStatusBar", NULL); + + if(mod_statusbar_statusbar_bindmap==NULL) + return FALSE; + + if(!ioncore_register_regclass(&CLASSDESCR(WStatusBar), + (WRegionLoadCreateFn*) statusbar_load)){ + mod_statusbar_deinit(); + return FALSE; + } + + if(!mod_statusbar_register_exports()){ + mod_statusbar_deinit(); + return FALSE; + } + + hook_add(clientwin_do_manage_alt, + (WHookDummy*)clientwin_do_manage_hook); + + /*ioncore_read_config("cfg_statusbar", NULL, TRUE);*/ + + return TRUE; +} + + +/*}}}*/ + diff --git a/mod_statusbar/main.h b/mod_statusbar/main.h new file mode 100644 index 0000000..b8ec8d2 --- /dev/null +++ b/mod_statusbar/main.h @@ -0,0 +1,23 @@ +/* + * ion/mod_statusbar/main.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_MOD_STATUSBAR_MAIN_H +#define ION_MOD_STATUSBAR_MAIN_H + +#include + +extern bool mod_statusbar_init(); +extern void mod_statusbar_deinit(); + +extern WBindmap *mod_statusbar_statusbar_bindmap; + + +#endif /* ION_MOD_STATUSBAR_MAIN_H */ diff --git a/mod_statusbar/mod_statusbar.lua b/mod_statusbar/mod_statusbar.lua new file mode 100644 index 0000000..aeef9cb --- /dev/null +++ b/mod_statusbar/mod_statusbar.lua @@ -0,0 +1,340 @@ +-- +-- ion/mod_statusbar/mod_statusbar.lua +-- +-- Copyright (c) Tuomo Valkonen 2004-2006. +-- +-- Ion is free software; you can redistribute it and/or modify it under +-- the terms of the GNU Lesser General Public License as published by +-- the Free Software Foundation; either version 2.1 of the License, or +-- (at your option) any later version. +-- + +-- This is a slight abuse of the package.loaded variable perhaps, but +-- library-like packages should handle checking if they're loaded instead of +-- confusing the user with require/include differences. +if package.loaded["mod_statusbar"] then return end + +if not ioncore.load_module("mod_statusbar") then + return +end + +local mod_statusbar=_G["mod_statusbar"] +assert(mod_statusbar) + + +-- Meter list {{{ + +local meters={} + +--DOC +-- Inform of a value. +function mod_statusbar.inform(name, value) + meters[name]=value +end + +-- }}} + + +-- Template processing {{{ + +local function process_template(template, meter_f, text_f, stretch_f) + local st, en, b, c, r, p, tmp + + while template~="" do + -- Find '%something' + st, en, b, r=string.find(template, '^(.-)%%(.*)') + + if not b then + -- Not found + text_f(template) + break + else + if b~="" then + -- Add preciding text as normal text element + text_f(b) + end + template=r + + -- Check for '% ' and '%%' + st, en, c, r=string.find(template, '^([ %%])(.*)') + + if c then + if c==' ' then + stretch_f(c) + else + text_f(c) + end + template=r + else + -- Extract [alignment][zero padding] + local pat='([<|>]?)(0*[0-9]*)([a-zA-Z0-9_]+)' + -- First within {...} + st, en, c, p, b, r=string.find(template, '^{'..pat..'}(.*)') + if not st then + -- And then without + st, en, c, p, b, r=string.find(template, '^'..pat..'(.*)') + end + if b then + meter_f(b, c, tonumber(p)) + template=r + end + end + end + end +end + + + +function mod_statusbar.template_to_table(template) + local res={} + local m=meters --set_date(stng, meters) + local aligns={["<"]=0, ["|"]=1, [">"]=2} + + process_template(template, + -- meter + function(s, c, p) + if s=="filler" then + table.insert(res, {type=4}) + elseif (string.find(s, "^systray$") or + string.find(s, "^systray_")) then + table.insert(res, { + type=5, + meter=s, + align=aligns[c], + }) + else + table.insert(res, { + type=2, + meter=s, + align=aligns[c], + tmpl=meters[s.."_template"], + zeropad=p, + }) + end + end, + -- text + function(t) + table.insert(res, { + type=1, + text=t, + }) + end, + -- stretch + function(t) + table.insert(res, { + type=3, + text=t, + }) + end) + return res +end + + +mod_statusbar._set_template_parser(mod_statusbar.template_to_table) + + +-- }}} + +-- Update {{{ + +--DOC +-- Update statusbar contents. To be called after series +-- of \fnref{mod_statusbar.inform} calls. +function mod_statusbar.update(update_templates) + for _, sb in pairs(mod_statusbar.statusbars()) do + if update_templates then + local t=sb:get_template_table() + for _, v in pairs(t) do + if v.meter then + v.tmpl=meters[v.meter.."_template"] + end + end + sb:set_template_table(t) + end + sb:update(meters) + end +end + +-- }}} + + +-- ion-statusd support {{{ + +local statusd_pid=0 + +function mod_statusbar.rcv_statusd(str) + local data="" + local updatenw=false + local updated=false + + local function doline(i) + if i=="." then + mod_statusbar.update(updatenw) + updated=true + else + local _, _, m, v=string.find(i, "^([^:]+):%s*(.*)") + if m and v then + mod_statusbar.inform(m, v) + updatenw=updatenw or string.find(m, "_template") + end + end + return "" + end + + while str do + updated=false + data=string.gsub(data..str, "([^\n]*)\n", doline) + str=coroutine.yield(updated) + end + + ioncore.warn(TR("ion-statusd quit.")) + statusd_pid=0 + meters={} + mod_statusbar.update(updatenw) +end + + +local function get_modules() + local mods={} + local specials={["filler"]=true, ["systray"]=true} + + for _, sb in pairs(mod_statusbar.statusbars()) do + for _, item in pairs(sb:get_template_table()) do + if item.type==2 and not specials[item.meter] then + local _, _, m=string.find(item.meter, "^([^_]*)"); + if m and m~="" then + mods[m]=true + end + end + end + end + + return mods +end + + +function mod_statusbar.cfg_statusd(cfg) + if date_format_backcompat_kludge then + if not cfg.date then + cfg=table.copy(cfg, false) + cfg.date={date_format=date_format_backcompat_kludge} + elseif not cfg.date.date_format then + cfg=table.copy(cfg, true) + cfg.date.date_format=date_format_backcompat_kludge + end + end + + --TODO: don't construct file name twice. + ioncore.write_savefile("cfg_statusd", cfg) + return ioncore.get_savefile("cfg_statusd") +end + + +function mod_statusbar.rcv_statusd_err(str) + if str then + io.stderr:write(str) + end +end + + +--DOC +-- Load modules and launch ion-statusd with configuration table \var{cfg}. +function mod_statusbar.launch_statusd(cfg) + if statusd_pid>0 then + return + end + + local mods=get_modules() + + -- Load modules + for m in pairs(mods) do + if dopath("statusbar_"..m, true) then + mods[m]=nil + end + end + + -- Lookup ion-statusd + local statusd=ioncore.lookup_script("ion-statusd") + if not statusd then + ioncore.warn(TR("Could not find %s", script)) + return + end + + local statusd_errors + local function initrcverr(str) + statusd_errors=(statusd_errors or "")..str + end + + local cfg=mod_statusbar.cfg_statusd(cfg or {}) + local params="" + table.foreach(mods, function(k) params=params.." -m "..k end) + local cmd=statusd.." -q -c "..cfg..params + + local rcv=coroutine.wrap(mod_statusbar.rcv_statusd) + local rcverr=mod_statusbar.rcv_statusd_err + + statusd_pid=mod_statusbar._launch_statusd(cmd, + rcv, initrcverr, + rcv, rcverr) + + if statusd_errors then + warn(TR("Errors starting ion-statusd:\n")..statusd_errors) + end + + if statusd_pid<=0 then + warn(TR("Failed to start ion-statusd.")) + end +end + +--}}} + + +-- Initialisation and default settings {{{ + +--DOC +-- Create a statusbar. +function mod_statusbar.create(param) + local scr=ioncore.find_screen_id(param.screen or 0) + if not scr then + error(TR("Screen not found.")) + end + + if not param.force then + local stdisp=scr:get_stdisp() + if stdisp and stdisp.reg then + error(TR("Screen already has an stdisp. Refusing to create a ".. + "statusbar.")) + end + end + + local sb=scr:set_stdisp({ + type="WStatusBar", + pos=(param.pos or "bl"), + fullsize=param.fullsize, + name="*statusbar*", + template=param.template, + template_table=param.template_table, + systray=param.systray, + }) + + if not sb then + error(TR("Failed to create statusbar.")) + end + + return sb +end + +-- }}} + + +-- Mark ourselves loaded. +package.loaded["mod_statusbar"]=true + + +-- Load user configuration file +dopath('cfg_statusbar', true) + +-- Launch statusd if the user didn't launch it. +if statusd_pid<=0 then + mod_statusbar.launch_statusd() +end diff --git a/mod_statusbar/statusbar.c b/mod_statusbar/statusbar.c new file mode 100644 index 0000000..1568b9f --- /dev/null +++ b/mod_statusbar/statusbar.c @@ -0,0 +1,1092 @@ +/* + * ion/mod_statusbar/statusbar.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "statusbar.h" +#include "main.h" +#include "draw.h" + + +static void statusbar_set_elems(WStatusBar *sb, ExtlTab t); +static void statusbar_free_elems(WStatusBar *sb); +static void statusbar_update_natural_size(WStatusBar *p); +static void statusbar_arrange_systray(WStatusBar *p); +static int statusbar_systray_x(WStatusBar *p); +static void statusbar_rearrange(WStatusBar *sb, bool rs); +static void do_calc_systray_w(WStatusBar *p, WSBElem *el); +static void statusbar_calc_systray_w(WStatusBar *p); + + +static WStatusBar *statusbars=NULL; + + +/*{{{ Init/deinit */ + + +bool statusbar_init(WStatusBar *p, WWindow *parent, const WFitParams *fp) +{ + if(!window_init(&(p->wwin), parent, fp)) + return FALSE; + + p->brush=NULL; + p->elems=NULL; + p->nelems=0; + p->natural_w=1; + p->natural_h=1; + p->filleridx=-1; + p->sb_next=NULL; + p->sb_prev=NULL; + p->traywins=NULL; + p->systray_enabled=TRUE; + + statusbar_updategr(p); + + if(p->brush==NULL){ + window_deinit(&(p->wwin)); + return FALSE; + } + + window_select_input(&(p->wwin), IONCORE_EVENTMASK_NORMAL); + + region_register((WRegion*)p); + + region_add_bindmap((WRegion*)p, mod_statusbar_statusbar_bindmap); + + ((WRegion*)p)->flags|=REGION_SKIP_FOCUS; + + LINK_ITEM(statusbars, p, sb_next, sb_prev); + + return TRUE; +} + + + +WStatusBar *create_statusbar(WWindow *parent, const WFitParams *fp) +{ + CREATEOBJ_IMPL(WStatusBar, statusbar, (p, parent, fp)); +} + + +void statusbar_deinit(WStatusBar *p) +{ + UNLINK_ITEM(statusbars, p, sb_next, sb_prev); + + statusbar_free_elems(p); + + if(p->brush!=NULL){ + grbrush_release(p->brush); + p->brush=NULL; + } + + window_deinit(&(p->wwin)); +} + + +/*}}}*/ + + +/*{{{ Content stuff */ + +static void init_sbelem(WSBElem *el) +{ + el->type=WSBELEM_NONE; + el->meter=NULL; + el->text_w=0; + el->text=NULL; + el->max_w=0; + el->tmpl=NULL; + el->attr=NULL; + el->stretch=0; + el->align=WSBELEM_ALIGN_CENTER; + el->zeropad=0; + el->x=0; + el->traywins=NULL; +} + + +static WSBElem *get_sbelems(ExtlTab t, int *nret, int *filleridxret) +{ + int i, n=extl_table_get_n(t); + WSBElem *el; + int systrayidx=-1; + + *nret=0; + *filleridxret=-1; + + if(n<=0) + return NULL; + + el=ALLOC_N(WSBElem, n); + + if(el==NULL) + return NULL; + + for(i=0; ielems=get_sbelems(t, &(sb->nelems), &(sb->filleridx)); +} + + +static void statusbar_free_elems(WStatusBar *sb) +{ + if(sb->elems!=NULL){ + free_sbelems(sb->elems, sb->nelems); + sb->elems=NULL; + sb->nelems=0; + sb->filleridx=-1; + } +} + + +/*}}}*/ + + + +/*{{{ Size stuff */ + + +static void statusbar_resize(WStatusBar *p) +{ + WRQGeomParams rq=RQGEOMPARAMS_INIT; + + rq.flags=REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_Y; + + rq.geom.w=p->natural_w; + rq.geom.h=p->natural_h; + rq.geom.x=REGION_GEOM(p).x; + rq.geom.y=REGION_GEOM(p).y; + + if(rectangle_compare(&rq.geom, ®ION_GEOM(p))!=RECTANGLE_SAME) + region_rqgeom((WRegion*)p, &rq, NULL); +} + + +static void calc_elem_w(WStatusBar *p, WSBElem *el, GrBrush *brush) +{ + const char *str; + + if(el->type==WSBELEM_SYSTRAY){ + do_calc_systray_w(p, el); + return; + } + + if(brush==NULL){ + el->text_w=0; + return; + } + + if(el->type==WSBELEM_METER){ + str=(el->text!=NULL ? el->text : STATUSBAR_NX_STR); + el->text_w=grbrush_get_text_width(brush, str, strlen(str)); + str=el->tmpl; + el->max_w=maxof((str!=NULL + ? grbrush_get_text_width(brush, str, strlen(str)) + : 0), + el->text_w); + }else{ + str=el->text; + el->text_w=(str!=NULL + ? grbrush_get_text_width(brush, str, strlen(str)) + : 0); + el->max_w=el->text_w; + } +} + + +static void statusbar_calc_widths(WStatusBar *sb) +{ + int i; + + for(i=0; inelems; i++) + calc_elem_w(sb, &(sb->elems[i]), sb->brush); +} + + +static void statusbar_do_update_natural_size(WStatusBar *p) +{ + GrBorderWidths bdw; + GrFontExtents fnte; + WRegion *reg; + PtrListIterTmp tmp; + int totw=0, stmh=0; + int i; + + if(p->brush==NULL){ + bdw.left=0; bdw.right=0; + bdw.top=0; bdw.bottom=0; + fnte.max_height=4; + }else{ + grbrush_get_border_widths(p->brush, &bdw); + grbrush_get_font_extents(p->brush, &fnte); + } + + for(i=0; inelems; i++) + totw+=p->elems[i].max_w; + + FOR_ALL_ON_PTRLIST(WRegion*, reg, p->traywins, tmp){ + stmh=maxof(stmh, REGION_GEOM(reg).h); + } + + p->natural_w=bdw.left+totw+bdw.right; + p->natural_h=maxof(stmh, fnte.max_height)+bdw.top+bdw.bottom; +} + + +void statusbar_size_hints(WStatusBar *p, WSizeHints *h) +{ + h->min_set=TRUE; + h->min_width=p->natural_w; + h->min_height=p->natural_h; + + h->max_set=TRUE; + h->max_width=p->natural_w; + h->max_height=p->natural_h; +} + + +/*}}}*/ + + +/*{{{ Systray */ + + +static WSBElem *statusbar_associate_systray(WStatusBar *sb, WRegion *reg) +{ + WClientWin *cwin=OBJ_CAST(reg, WClientWin); + WSBElem *el=NULL, *fbel=NULL; + char *name=NULL; + int i; + + if(cwin!=NULL) + extl_table_gets_s(cwin->proptab, "statusbar", &name); + + for(i=0; inelems; i++){ + if(sb->elems[i].type!=WSBELEM_SYSTRAY) + continue; + if(sb->elems[i].meter==NULL){ + fbel=&sb->elems[i]; + continue; + } + if(name!=NULL && strcmp(sb->elems[i].meter, name)==0){ + el=&sb->elems[i]; + break; + } + if(strcmp(sb->elems[i].meter, "systray")==0) + fbel=&sb->elems[i]; + } + + if(name!=NULL) + free(name); + + if(el==NULL) + el=fbel; + + if(el==NULL) + return NULL; + + ptrlist_insert_last(&el->traywins, (Obj*)reg); + + return el; +} + + +static WSBElem *statusbar_unassociate_systray(WStatusBar *sb, WRegion *reg) +{ + int i; + + for(i=0; inelems; i++){ + if(ptrlist_remove(&(sb->elems[i].traywins), (Obj*)reg)) + return &sb->elems[i]; + } + + return NULL; +} + + + +static void do_calc_systray_w(WStatusBar *p, WSBElem *el) +{ + WRegion *reg; + PtrListIterTmp tmp; + int padding=0; + int w=-padding; + + FOR_ALL_ON_PTRLIST(WRegion*, reg, el->traywins, tmp){ + w=w+REGION_GEOM(reg).w+padding; + } + + el->text_w=maxof(0, w); + el->max_w=el->text_w; /* for now */ +} + + +static void statusbar_calc_systray_w(WStatusBar *p) +{ + int i; + + for(i=0; inelems; i++){ + if(p->elems[i].type==WSBELEM_SYSTRAY) + do_calc_systray_w(p, &p->elems[i]); + } +} + + +static void statusbar_arrange_systray(WStatusBar *p) +{ + WRegion *reg; + PtrListIterTmp tmp; + GrBorderWidths bdw; + int padding=0, ymiddle; + int i, x; + + if(p->brush!=NULL){ + grbrush_get_border_widths(p->brush, &bdw); + }else{ + bdw.top=0; + bdw.bottom=0; + } + + ymiddle=bdw.top+(REGION_GEOM(p).h-bdw.top-bdw.bottom)/2; + + for(i=0; inelems; i++){ + WSBElem *el=&p->elems[i]; + if(el->type!=WSBELEM_SYSTRAY) + continue; + x=el->x; + FOR_ALL_ON_PTRLIST(WRegion*, reg, el->traywins, tmp){ + WRectangle g=REGION_GEOM(reg); + g.x=x; + g.y=ymiddle-g.h/2; + region_fit(reg, &g, REGION_FIT_EXACT); + x=x+g.w+padding; + } + } +} + + +static void systray_adjust_size(WRegion *reg, WRectangle *g) +{ + g->h=CF_STATUSBAR_SYSTRAY_HEIGHT; + + region_size_hints_correct(reg, &g->w, &g->h, TRUE); +} + + + +static WRegion *statusbar_do_attach_final(WStatusBar *sb, + WRegion *reg, + void *unused) +{ + WFitParams fp; + WSBElem *el; + + if(!ptrlist_insert_last(&sb->traywins, (Obj*)reg)) + return NULL; + + el=statusbar_associate_systray(sb, reg); + if(el==NULL){ + ptrlist_remove(&sb->traywins, (Obj*)reg); + return NULL; + } + + fp.g=REGION_GEOM(reg); + fp.mode=REGION_FIT_EXACT; + systray_adjust_size(reg, &fp.g); + + region_fitrep(reg, NULL, &fp); + + do_calc_systray_w(sb, el); + + region_set_manager(reg, (WRegion*)sb); + + statusbar_rearrange(sb, TRUE); + + if(REGION_IS_MAPPED(sb)) + region_map(reg); + + return reg; +} + + +static WRegion *statusbar_do_attach(WStatusBar *sb, WRegionAttachData *data) +{ + WFitParams fp; + + fp.g.x=0; + fp.g.y=0; + fp.g.h=CF_STATUSBAR_SYSTRAY_HEIGHT; + fp.g.w=CF_STATUSBAR_SYSTRAY_HEIGHT; + fp.mode=REGION_FIT_WHATEVER|REGION_FIT_BOUNDS; + + return region_attach_helper((WRegion*)sb, (WWindow*)sb, &fp, + (WRegionDoAttachFn*)statusbar_do_attach_final, + NULL, data); +} + + +static WRegion *statusbar_attach_ph(WStatusBar *sb, int flags, + WRegionAttachData *data) +{ + return statusbar_do_attach(sb, data); +} + + +static WPHolder *statusbar_prepare_manage(WStatusBar *sb, + const WClientWin *cwin, + const WManageParams *param, + int redir) +{ + if(redir==MANAGE_REDIR_STRICT_YES) + return NULL; + + return (WPHolder*)create_basicpholder((WRegion*)sb, + ((WBasicPHolderHandler*) + statusbar_attach_ph)); +} + + +static void statusbar_managed_remove(WStatusBar *sb, WRegion *reg) +{ + WSBElem *el; + + ptrlist_remove(&sb->traywins, (Obj*)reg); + + el=statusbar_unassociate_systray(sb, reg); + + region_unset_manager(reg, (WRegion*)sb); + + if(el!=NULL && ioncore_g.opmode!=IONCORE_OPMODE_DEINIT){ + do_calc_systray_w(sb, el); + statusbar_rearrange(sb, TRUE); + } +} + + +static void statusbar_managed_rqgeom(WStatusBar *sb, WRegion *reg, + const WRQGeomParams *rq, + WRectangle *geomret) +{ + WRectangle g; + + g.x=REGION_GEOM(reg).x; + g.y=REGION_GEOM(reg).y; + g.w=rq->geom.w; + g.h=rq->geom.h; + + systray_adjust_size(reg, &g); + + if(rq->flags®ION_RQGEOM_TRYONLY){ + if(geomret!=NULL) + *geomret=g; + return; + } + + region_fit(reg, &g, REGION_FIT_EXACT); + + statusbar_calc_systray_w(sb); + statusbar_rearrange(sb, TRUE); + + if(geomret!=NULL) + *geomret=REGION_GEOM(reg); + +} + + +void statusbar_map(WStatusBar *sb) +{ + WRegion *reg; + PtrListIterTmp tmp; + + window_map((WWindow*)sb); + + FOR_ALL_ON_PTRLIST(WRegion*, reg, sb->traywins, tmp) + region_map(reg); +} + + +void statusbar_unmap(WStatusBar *sb) +{ + WRegion *reg; + PtrListIterTmp tmp; + + window_unmap((WWindow*)sb); + + FOR_ALL_ON_PTRLIST(WRegion*, reg, sb->traywins, tmp) + region_unmap(reg); +} + + +bool statusbar_fitrep(WStatusBar *sb, WWindow *par, const WFitParams *fp) +{ + bool wchg=(REGION_GEOM(sb).w!=fp->g.w); + bool hchg=(REGION_GEOM(sb).h!=fp->g.h); + + window_do_fitrep(&(sb->wwin), par, &(fp->g)); + + if(wchg || hchg){ + statusbar_calculate_xs(sb); + statusbar_arrange_systray(sb); + statusbar_draw(sb, TRUE); + } + + return TRUE; +} + + +WPHolder *statusbar_prepare_manage_transient(WStatusBar *sb, + const WClientWin *cwin, + const WManageParams *param, + int unused) +{ + WRegion *mgr=REGION_MANAGER(sb); + + if(mgr==NULL) + mgr=(WRegion*)region_screen_of((WRegion*)sb); + + if(mgr!=NULL) + return region_prepare_manage(mgr, cwin, param, + MANAGE_REDIR_PREFER_NO); + else + return NULL; +} + + + +/*}}}*/ + + +/*{{{ Exports */ + + +static ExtlFn parse_template_fn; +static bool parse_template_fn_set=FALSE; + + +EXTL_EXPORT +void mod_statusbar__set_template_parser(ExtlFn fn) +{ + if(parse_template_fn_set) + extl_unref_fn(parse_template_fn); + parse_template_fn=extl_ref_fn(fn); + parse_template_fn_set=TRUE; +} + + +/*EXTL_DOC + * Set statusbar template. + */ +EXTL_EXPORT_MEMBER +void statusbar_set_template(WStatusBar *sb, const char *tmpl) +{ + ExtlTab t=extl_table_none(); + bool ok=FALSE; + + if(parse_template_fn_set){ + extl_protect(NULL); + ok=extl_call(parse_template_fn, "s", "t", tmpl, &t); + extl_unprotect(NULL); + } + + if(ok) + statusbar_set_template_table(sb, t); +} + + +/*EXTL_DOC + * Set statusbar template as table. + */ +EXTL_EXPORT_MEMBER +void statusbar_set_template_table(WStatusBar *sb, ExtlTab t) +{ + WRegion *reg; + PtrListIterTmp tmp; + + statusbar_set_elems(sb, t); + + FOR_ALL_ON_PTRLIST(WRegion*, reg, sb->traywins, tmp){ + statusbar_associate_systray(sb, reg); + } + + statusbar_calc_widths(sb); + statusbar_rearrange(sb, FALSE); +} + + +/*EXTL_DOC + * Get statusbar template as table. + */ +EXTL_EXPORT_MEMBER +ExtlTab statusbar_get_template_table(WStatusBar *sb) +{ + int count = sb->nelems; + int i; + + ExtlTab t = extl_create_table(); + + for(i=0; ielems[i].type); + extl_table_sets_s(tt, "text", sb->elems[i].text); + extl_table_sets_s(tt, "meter", sb->elems[i].meter); + extl_table_sets_s(tt, "tmpl", sb->elems[i].tmpl); + extl_table_sets_i(tt, "align", sb->elems[i].align); + extl_table_sets_i(tt, "zeropad", sb->elems[i].zeropad); + + extl_table_seti_t(t, (i+1), tt); + extl_unref_table(tt); + } + + return t; +} + + +static void reset_stretch(WStatusBar *sb) +{ + int i; + + for(i=0; inelems; i++) + sb->elems[i].stretch=0; +} + + +static void positive_stretch(WStatusBar *sb) +{ + int i; + + for(i=0; inelems; i++) + sb->elems[i].stretch=maxof(0, sb->elems[i].stretch); +} + + +static void spread_stretch(WStatusBar *sb) +{ + int i, j, k; + int diff; + WSBElem *el, *lel, *rel; + const char *str; + + for(i=0; inelems; i++){ + el=&(sb->elems[i]); + + if(el->type!=WSBELEM_METER && el->type!=WSBELEM_SYSTRAY) + continue; + + diff=el->max_w-el->text_w; + + lel=NULL; + rel=NULL; + + if(el->align!=WSBELEM_ALIGN_RIGHT){ + for(j=i+1; jnelems; j++){ + if(sb->elems[j].type==WSBELEM_STRETCH){ + rel=&(sb->elems[j]); + break; + } + } + } + + if(el->align!=WSBELEM_ALIGN_LEFT){ + for(k=i-1; k>=0; k--){ + if(sb->elems[k].type==WSBELEM_STRETCH){ + lel=&(sb->elems[k]); + break; + } + } + } + + if(rel!=NULL && lel!=NULL){ + int l=diff/2; + int r=diff-l; + lel->stretch+=l; + rel->stretch+=r; + }else if(lel!=NULL){ + lel->stretch+=diff; + }else if(rel!=NULL){ + rel->stretch+=diff; + } + } +} + + +static void statusbar_rearrange(WStatusBar *sb, bool rs) +{ + if(rs){ + int onw=sb->natural_w; + int onh=sb->natural_h; + + statusbar_do_update_natural_size(sb); + + if( (sb->natural_h>onh && REGION_GEOM(sb).h>=onh) + || (sb->natural_hnatural_w>onw && REGION_GEOM(sb).w>=onw) + || (sb->natural_wbrush==NULL) + return; + + for(i=0; inelems; i++){ + el=&(sb->elems[i]); + + if(el->type!=WSBELEM_METER) + continue; + + if(el->text!=NULL){ + free(el->text); + el->text=NULL; + } + + if(el->attr!=NULL){ + free(el->attr); + el->attr=NULL; + } + + if(el->meter!=NULL){ + const char *str; + char *attrnm; + + extl_table_gets_s(t, el->meter, &(el->text)); + + if(el->text==NULL){ + str=STATUSBAR_NX_STR; + }else{ + /* Zero-pad */ + int l=strlen(el->text); + int ml=str_len(el->text); + int diff=el->zeropad-ml; + if(diff>0){ + char *tmp=ALLOC_N(char, l+diff+1); + if(tmp!=NULL){ + memset(tmp, '0', diff); + memcpy(tmp+diff, el->text, l+1); + free(el->text); + el->text=tmp; + } + } + str=el->text; + } + + if(el->tmpl!=NULL && el->text!=NULL){ + char *tmp=grbrush_make_label(sb->brush, el->text, el->max_w); + if(tmp!=NULL){ + free(el->text); + el->text=tmp; + str=tmp; + } + } + + el->text_w=grbrush_get_text_width(sb->brush, str, strlen(str)); + + if(el->text_w>el->max_w && el->tmpl==NULL){ + el->max_w=el->text_w; + grow=TRUE; + } + + attrnm=scat(el->meter, "_hint"); + if(attrnm!=NULL){ + extl_table_gets_s(t, attrnm, &(el->attr)); + free(attrnm); + } + } + } + + statusbar_rearrange(sb, grow); + + window_draw((WWindow*)sb, FALSE); +} + + +/*}}}*/ + + +/*{{{ Updategr */ + + +void statusbar_updategr(WStatusBar *p) +{ + GrBrush *nbrush; + + nbrush=gr_get_brush(p->wwin.win, region_rootwin_of((WRegion*)p), + "stdisp-statusbar"); + if(nbrush==NULL) + return; + + if(p->brush!=NULL) + grbrush_release(p->brush); + + p->brush=nbrush; + + statusbar_calc_widths(p); + statusbar_rearrange(p, TRUE); + + window_draw(&(p->wwin), TRUE); +} + + +/*}}}*/ + + +/*{{{ Misc */ + + +int statusbar_orientation(WStatusBar *sb) +{ + return REGION_ORIENTATION_HORIZONTAL; +} + + +/*EXTL_DOC + * Returns a list of all statusbars. + */ +EXTL_EXPORT +ExtlTab mod_statusbar_statusbars() +{ + ExtlTab t=extl_create_table(); + WStatusBar *sb; + int i=1; + + for(sb=statusbars; sb!=NULL; sb=sb->sb_next){ + extl_table_seti_o(t, i, (Obj*)sb); + i++; + } + + return t; +} + + +WStatusBar *mod_statusbar_find_suitable(WClientWin *cwin, + const WManageParams *param) +{ + WStatusBar *sb; + + for(sb=statusbars; sb!=NULL; sb=sb->sb_next){ + /*if(!sb->is_auto) + continue;*/ + if(!sb->systray_enabled) + continue; + if(!region_same_rootwin((WRegion*)sb, (WRegion*)cwin)) + continue; + break; + } + + return sb; +} + + +bool statusbar_set_systray(WStatusBar *sb, int sp) +{ + bool set=sb->systray_enabled; + bool nset=libtu_do_setparam(sp, set); + + sb->systray_enabled=nset; + + return nset; +} + + +/*EXTL_DOC + * Enable or disable use of \var{sb} as systray. + * The parameter \var{how} can be one of (set/unset/toggle). + * Resulting state is returned. + */ +EXTL_EXPORT_AS(WStatusBar, set_systray) +bool statusbar_set_systray_extl(WStatusBar *sb, const char *how) +{ + return statusbar_set_systray(sb, libtu_string_to_setparam(how)); +} + + +/*EXTL_DOC + * Is \var{sb} used as a systray? + */ +EXTL_EXPORT_MEMBER +bool statusbar_is_systray_extl(WStatusBar *sb) +{ + return sb->systray_enabled; +} + + +/*}}}*/ + + +/*{{{ Load */ + + +WRegion *statusbar_load(WWindow *par, const WFitParams *fp, ExtlTab tab) +{ + WStatusBar *sb=create_statusbar(par, fp); + + if(sb!=NULL){ + char *tmpl=NULL; + ExtlTab t=extl_table_none(); + if(extl_table_gets_s(tab, "template", &tmpl)){ + statusbar_set_template(sb, tmpl); + free(tmpl); + }else if(extl_table_gets_t(tab, "template_table", &t)){ + statusbar_set_template_table(sb, t); + extl_unref_table(t); + }else{ + const char *tmpl=TR("[ %date || load: %load ] %filler%systray"); + statusbar_set_template(sb, tmpl); + } + + extl_table_gets_b(tab, "systray", &sb->systray_enabled); + } + + return (WRegion*)sb; +} + + +/*}}}*/ + + +/*{{{ Dynamic function table and class implementation */ + + +static DynFunTab statusbar_dynfuntab[]={ + {window_draw, statusbar_draw}, + {region_updategr, statusbar_updategr}, + {region_size_hints, statusbar_size_hints}, + {(DynFun*)region_orientation, (DynFun*)statusbar_orientation}, + + {region_managed_rqgeom, statusbar_managed_rqgeom}, + {(DynFun*)region_prepare_manage, (DynFun*)statusbar_prepare_manage}, + {region_managed_remove, statusbar_managed_remove}, + + {(DynFun*)region_prepare_manage_transient, + (DynFun*)statusbar_prepare_manage_transient}, + + {region_map, statusbar_map}, + {region_unmap, statusbar_unmap}, + + {(DynFun*)region_fitrep, + (DynFun*)statusbar_fitrep}, + + END_DYNFUNTAB +}; + + +EXTL_EXPORT +IMPLCLASS(WStatusBar, WWindow, statusbar_deinit, statusbar_dynfuntab); + + +/*}}}*/ + diff --git a/mod_statusbar/statusbar.h b/mod_statusbar/statusbar.h new file mode 100644 index 0000000..73999ad --- /dev/null +++ b/mod_statusbar/statusbar.h @@ -0,0 +1,94 @@ +/* + * ion/mod_statusbar/statusbar.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_MOD_STATUSBAR_STATUSBAR_H +#define ION_MOD_STATUSBAR_STATUSBAR_H + +#include +#include +#include +#include +#include +#include + + +#define STATUSBAR_NX_STR "?" + + +typedef enum{ + WSBELEM_ALIGN_LEFT=0, + WSBELEM_ALIGN_CENTER=1, + WSBELEM_ALIGN_RIGHT=2 +} WSBElemAlign; + + +typedef enum{ + WSBELEM_NONE=0, + WSBELEM_TEXT=1, + WSBELEM_METER=2, + WSBELEM_STRETCH=3, + WSBELEM_FILLER=4, + WSBELEM_SYSTRAY=5 +} WSBElemType; + + +INTRSTRUCT(WSBElem); + +DECLSTRUCT(WSBElem){ + int type; + int align; + int stretch; + int text_w; + char *text; + char *meter; + int max_w; + char *tmpl; + char *attr; + int zeropad; + int x; + PtrList *traywins; +}; + +INTRCLASS(WStatusBar); + +DECLCLASS(WStatusBar){ + WWindow wwin; + GrBrush *brush; + WSBElem *elems; + int nelems; + int natural_w, natural_h; + int filleridx; + WStatusBar *sb_next, *sb_prev; + PtrList *traywins; + bool systray_enabled; +}; + +extern bool statusbar_init(WStatusBar *p, WWindow *parent, + const WFitParams *fp); +extern WStatusBar *create_statusbar(WWindow *parent, const WFitParams *fp); +extern void statusbar_deinit(WStatusBar *p); + +extern WRegion *statusbar_load(WWindow *par, const WFitParams *fp, + ExtlTab tab); + +extern void statusbar_set_natural_w(WStatusBar *p, const char *str); +extern void statusbar_size_hints(WStatusBar *p, WSizeHints *h); +extern void statusbar_updategr(WStatusBar *p); +extern void statusbar_set_contents(WStatusBar *sb, ExtlTab t); + +extern void statusbar_set_template(WStatusBar *sb, const char *tmpl); +extern void statusbar_set_template_table(WStatusBar *sb, ExtlTab t); +extern ExtlTab statusbar_get_template_table(WStatusBar *sb); + +extern WStatusBar *mod_statusbar_find_suitable(WClientWin *cwin, + const WManageParams *param); + +#endif /* ION_MOD_STATUSBAR_STATUSBAR_H */ diff --git a/mod_tiling/Makefile b/mod_tiling/Makefile new file mode 100644 index 0000000..a921270 --- /dev/null +++ b/mod_tiling/Makefile @@ -0,0 +1,30 @@ +## +## Ion workspace module Makefile +## + +# System-specific configuration is in system.mk +TOPDIR=.. +include $(TOPDIR)/build/system-inc.mk + +###################################### + +INCLUDES += $(X11_INCLUDES) $(LIBTU_INCLUDES) $(LIBEXTL_INCLUDES) -I.. +CFLAGS += $(XOPEN_SOURCE) $(C99_SOURCE) + +SOURCES=main.c tiling.c placement.c split.c split-stdisp.c \ + splitfloat.c panehandle.c ops.c + +MAKE_EXPORTS=mod_tiling + +MODULE=mod_tiling + +###################################### + +include $(TOPDIR)/build/rules.mk + +###################################### + +_install: module_install + + + diff --git a/mod_tiling/main.c b/mod_tiling/main.c new file mode 100644 index 0000000..5901c1c --- /dev/null +++ b/mod_tiling/main.c @@ -0,0 +1,157 @@ +/* + * ion/mod_tiling/main.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include "main.h" +#include "tiling.h" +#include "placement.h" +#include "exports.h" + + +/*{{{ Module information */ + + +#include "../version.h" + +char mod_tiling_ion_api_version[]=ION_API_VERSION; + + +/*}}}*/ + + +/*{{{ Bindmaps and configuration variables */ + + +WBindmap *mod_tiling_tiling_bindmap=NULL; + +int mod_tiling_raise_delay=CF_RAISE_DELAY; + + +/*}}}*/ + + +/*{{{ Configuration */ + + +/*EXTL_DOC + * Set parameters. Currently only \var{raise_delay} (in milliseconds) + * is supported. + */ +EXTL_EXPORT +void mod_tiling_set(ExtlTab tab) +{ + int d; + if(extl_table_gets_i(tab, "raise_delay", &d)){ + if(d>=0) + mod_tiling_raise_delay=d; + } +} + + +/*EXTL_DOC + * Get parameters. For details see \fnref{mod_tiling.set}. + */ +EXTL_SAFE +EXTL_EXPORT +ExtlTab mod_tiling_get() +{ + ExtlTab tab=extl_create_table(); + + extl_table_sets_i(tab, "raise_delay", mod_tiling_raise_delay); + + return tab; +} + + + +/*}}}*/ + + + +/*{{{ Module init & deinit */ + + +void mod_tiling_deinit() +{ + mod_tiling_unregister_exports(); + ioncore_unregister_regclass(&CLASSDESCR(WTiling)); + + if(mod_tiling_tiling_bindmap!=NULL){ + ioncore_free_bindmap("WTiling", mod_tiling_tiling_bindmap); + mod_tiling_tiling_bindmap=NULL; + } + + if(tiling_placement_alt!=NULL){ + destroy_obj((Obj*)tiling_placement_alt); + tiling_placement_alt=NULL; + } +} + + +static bool register_regions() +{ + if(!ioncore_register_regclass(&CLASSDESCR(WTiling), + (WRegionLoadCreateFn*)tiling_load)){ + return FALSE; + } + + return TRUE; +} + + +#define INIT_HOOK_(NM) \ + NM=mainloop_register_hook(#NM, create_hook()); \ + if(NM==NULL) return FALSE; + + +static bool init_hooks() +{ + INIT_HOOK_(tiling_placement_alt); + return TRUE; +} + + +bool mod_tiling_init() +{ + if(!init_hooks()) + goto err; + + mod_tiling_tiling_bindmap=ioncore_alloc_bindmap("WTiling", NULL); + + if(mod_tiling_tiling_bindmap==NULL) + goto err; + + if(!mod_tiling_register_exports()) + goto err; + + if(!register_regions()) + goto err; + + extl_read_config("cfg_tiling", NULL, TRUE); + + return TRUE; + +err: + mod_tiling_deinit(); + return FALSE; +} + + +/*}}}*/ + diff --git a/mod_tiling/main.h b/mod_tiling/main.h new file mode 100644 index 0000000..35d9ddf --- /dev/null +++ b/mod_tiling/main.h @@ -0,0 +1,26 @@ +/* + * ion/mod_tiling/main.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_MOD_TILING_MAIN_H +#define ION_MOD_TILING_MAIN_H + +#include +#include + +extern bool mod_tiling_init(); +extern void mod_tiling_deinit(); + +extern WBindmap *mod_tiling_tiling_bindmap; +extern WBindmap *mod_tiling_frame_bindmap; + +extern int mod_tiling_raise_delay; + +#endif /* ION_MOD_TILING_MAIN_H */ diff --git a/mod_tiling/ops.c b/mod_tiling/ops.c new file mode 100644 index 0000000..c86a80a --- /dev/null +++ b/mod_tiling/ops.c @@ -0,0 +1,190 @@ +/* + * ion/mod_tiling/ops.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include "tiling.h" + + +static WGroup *find_group(WRegion *reg, bool *detach_framed) +{ + WRegion *mgr=REGION_MANAGER(reg); + bool was_grouped=FALSE; + + if(OBJ_IS(mgr, WMPlex)){ + WMPlex *mplex=(WMPlex*)mgr; + *detach_framed=TRUE; + mgr=REGION_MANAGER(mgr); + if(OBJ_IS(mgr, WGroup)){ + assert(mplex->mgd!=NULL); + if(mplex->mgd->reg==reg && mplex->mgd->mgr_next==NULL){ + /* Nothing to detach */ + return NULL; + } + } + }else{ + was_grouped=OBJ_IS(mgr, WGroup); + *detach_framed=FALSE; + } + + while(mgr!=NULL){ + mgr=REGION_MANAGER(mgr); + if(OBJ_IS(mgr, WGroup)) + break; + } + + if(mgr==NULL && was_grouped) + warn(TR("Already detached.")); + + return (WGroup*)mgr; +} + + +static void get_relative_geom(WRectangle *g, WRegion *reg, WRegion *mgr) +{ + WWindow *rel=REGION_PARENT(mgr), *w; + + *g=REGION_GEOM(reg); + + for(w=REGION_PARENT(reg); + w!=rel && (WRegion*)w!=mgr; + w=REGION_PARENT(w)){ + + g->x+=REGION_GEOM(w).x; + g->y+=REGION_GEOM(w).y; + } +} + + +/*EXTL_DOC + * Detach \var{reg}, i.e. make it managed by its nearest ancestor + * \type{WGroup}, framed if \var{reg} is not itself \type{WFrame}. + */ +EXTL_EXPORT +bool mod_tiling_detach(WRegion *reg) +{ + bool detach_framed=!OBJ_IS(reg, WFrame); + WGroupAttachParams ap=GROUPATTACHPARAMS_INIT; + WRegionAttachData data; + WGroup *grp; + + grp=find_group(reg, &detach_framed); + + if(grp==NULL) + return FALSE; + + ap.switchto_set=TRUE; + ap.switchto=region_may_control_focus(reg); + + ap.geom_set=TRUE; + get_relative_geom(&ap.geom, reg, (WRegion*)grp); + + /* TODO: Retain (root-relative) geometry of reg for framed + * detach instead of making a frame of this size? + */ + + data.type=REGION_ATTACH_REPARENT; + data.u.reg=reg; + + if(detach_framed){ + WFramedParam fp=FRAMEDPARAM_INIT; + + return (region_attach_framed((WRegion*)grp, &fp, + (WRegionAttachFn*)group_do_attach, + &ap, &data)!=NULL); + }else{ + return (group_do_attach(grp, &ap, &data)!=NULL); + } +} + + +static WRegion *mkbottom_fn(WWindow *parent, const WFitParams *fp, + void *param) +{ + WRegion *reg=(WRegion*)param; + WTiling *tiling; + WSplitRegion *node=NULL; + + if(!region_fitrep(reg, parent, fp)) + return NULL; + + tiling=create_tiling(parent, fp, NULL, FALSE); + + if(tiling==NULL) + return NULL; + + node=create_splitregion(®ION_GEOM(tiling), reg); + if(node!=NULL){ + tiling->split_tree=(WSplit*)node; + tiling->split_tree->ws_if_root=tiling; + + region_detach_manager(reg); + + if(tiling_managed_add(tiling, reg)) + return (WRegion*)tiling; + + #warning "TODO: reattach?" + + destroy_obj((Obj*)tiling->split_tree); + tiling->split_tree=NULL; + } + + destroy_obj((Obj*)tiling); + return NULL; +} + + +/*EXTL_DOC + * Create a new \type{WTiling} 'bottom' for the group of \var{reg}, + * consisting of \var{reg}. + */ +EXTL_EXPORT +bool mod_tiling_mkbottom(WRegion *reg) +{ + WGroup *grp=REGION_MANAGER_CHK(reg, WGroup); + WGroupAttachParams ap=GROUPATTACHPARAMS_INIT; + WRegionAttachData data; + WRegion *tiling; + + if(grp==NULL){ + warn(TR("Not member of a group")); + return FALSE; + } + + if(grp->bottom!=NULL){ + warn(TR("Manager group already has bottom")); + return FALSE; + } + + ap.level_set=TRUE; + ap.level=STACKING_LEVEL_BOTTOM; + + ap.szplcy_set=TRUE; + ap.szplcy=SIZEPOLICY_FULL_EXACT; + + ap.switchto_set=TRUE; + ap.switchto=region_may_control_focus(reg); + + ap.bottom=TRUE; + + data.type=REGION_ATTACH_NEW; + data.u.n.fn=mkbottom_fn; + data.u.n.param=reg; + + /* kele... poisto samalla kuin attach */ + return (group_do_attach(grp, &ap, &data)!=NULL); +} diff --git a/mod_tiling/panehandle.c b/mod_tiling/panehandle.c new file mode 100644 index 0000000..586385d --- /dev/null +++ b/mod_tiling/panehandle.c @@ -0,0 +1,139 @@ +/* + * ion/panews/panehandle.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include "panehandle.h" +#include "main.h" + + +/*{{{ Init/deinit */ + + +static void panehandle_getbrush(WPaneHandle *pwin) +{ + GrBrush *brush=gr_get_brush(pwin->wwin.win, + region_rootwin_of((WRegion*)pwin), + "pane"); + + if(brush!=NULL){ + if(pwin->brush!=NULL) + grbrush_release(pwin->brush); + + pwin->brush=brush; + + grbrush_get_border_widths(brush, &(pwin->bdw)); + grbrush_enable_transparency(brush, GR_TRANSPARENCY_YES); + } +} + + +bool panehandle_init(WPaneHandle *pwin, WWindow *parent, const WFitParams *fp) +{ + pwin->brush=NULL; + pwin->bline=GR_BORDERLINE_NONE; + pwin->splitfloat=NULL; + + if(!window_init(&(pwin->wwin), parent, fp)) + return FALSE; + + ((WRegion*)pwin)->flags|=REGION_SKIP_FOCUS; + + panehandle_getbrush(pwin); + + if(pwin->brush==NULL){ + GrBorderWidths bdw=GR_BORDER_WIDTHS_INIT; + memcpy(&(pwin->bdw), &bdw, sizeof(bdw)); + } + + window_select_input(&(pwin->wwin), IONCORE_EVENTMASK_NORMAL); + + return TRUE; +} + + +WPaneHandle *create_panehandle(WWindow *parent, const WFitParams *fp) +{ + CREATEOBJ_IMPL(WPaneHandle, panehandle, (p, parent, fp)); +} + + +void panehandle_deinit(WPaneHandle *pwin) +{ + assert(pwin->splitfloat==NULL); + + if(pwin->brush!=NULL){ + grbrush_release(pwin->brush); + pwin->brush=NULL; + } + + window_deinit(&(pwin->wwin)); +} + + +/*}}}*/ + + +/*{{{ Drawing */ + + +static void panehandle_updategr(WPaneHandle *pwin) +{ + panehandle_getbrush(pwin); + region_updategr_default((WRegion*)pwin); +} + + +static void panehandle_draw(WPaneHandle *pwin, bool complete) +{ + WRectangle g; + + if(pwin->brush==NULL) + return; + + g.x=0; + g.y=0; + g.w=REGION_GEOM(pwin).w; + g.h=REGION_GEOM(pwin).h; + + grbrush_begin(pwin->brush, &g, (complete ? 0 : GRBRUSH_NO_CLEAR_OK)); + + grbrush_draw_borderline(pwin->brush, &g, NULL, pwin->bline); + + grbrush_end(pwin->brush); +} + + +/*}}}*/ + + +/*{{{ The class */ + + +static DynFunTab panehandle_dynfuntab[]={ + {region_updategr, panehandle_updategr}, + {window_draw, panehandle_draw}, + END_DYNFUNTAB, +}; + + +IMPLCLASS(WPaneHandle, WWindow, panehandle_deinit, panehandle_dynfuntab); + + +/*}}}*/ + diff --git a/mod_tiling/panehandle.h b/mod_tiling/panehandle.h new file mode 100644 index 0000000..c4ad184 --- /dev/null +++ b/mod_tiling/panehandle.h @@ -0,0 +1,34 @@ +/* + * ion/panews/panehandle.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_PANEWS_PANEHANDLE_H +#define ION_PANEWS_PANEHANDLE_H + +#include +#include + +INTRCLASS(WPaneHandle); + +#include "splitfloat.h" + +DECLCLASS(WPaneHandle){ + WWindow wwin; + GrBrush *brush; + GrBorderLine bline; + GrBorderWidths bdw; + WSplitFloat *splitfloat; +}; + +extern bool panehandle_init(WPaneHandle *pwin, + WWindow *parent, const WFitParams *fp); +extern WPaneHandle *create_panehandle(WWindow *parent, const WFitParams *fp); + +#endif /* ION_PANEWS_PANEHANDLE_H */ diff --git a/mod_tiling/placement.c b/mod_tiling/placement.c new file mode 100644 index 0000000..47cfa76 --- /dev/null +++ b/mod_tiling/placement.c @@ -0,0 +1,113 @@ +/* + * ion/mod_tiling/placement.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "placement.h" +#include "tiling.h" + + +WHook *tiling_placement_alt=NULL; + + +static WRegion *find_suitable_target(WTiling *ws) +{ + WRegion *r=tiling_current(ws); + + if(r==NULL){ + FOR_ALL_MANAGED_BY_TILING_UNSAFE(r, ws) + break; + } + + return r; +} + + +static bool placement_mrsh_extl(ExtlFn fn, WTilingPlacementParams *param) +{ + ExtlTab t, mp; + bool ret=FALSE; + + t=extl_create_table(); + + mp=manageparams_to_table(param->mp); + + extl_table_sets_o(t, "tiling", (Obj*)param->ws); + extl_table_sets_o(t, "reg", (Obj*)param->reg); + extl_table_sets_t(t, "mp", mp); + + extl_unref_table(mp); + + extl_protect(NULL); + ret=extl_call(fn, "t", "b", t, &ret); + extl_unprotect(NULL); + + if(ret){ + Obj *tmp=NULL; + + extl_table_gets_o(t, "res_frame", &tmp); + + param->res_frame=OBJ_CAST(tmp, WFrame); + ret=(param->res_frame!=NULL); + } + + extl_unref_table(t); + + return ret; +} + + +WPHolder *tiling_prepare_manage(WTiling *ws, const WClientWin *cwin, + const WManageParams *mp, int redir) +{ + WRegion *target=NULL; + WTilingPlacementParams param; + WPHolder *ph; + bool ret; + + if(redir==MANAGE_REDIR_STRICT_NO) + return NULL; + + param.ws=ws; + param.reg=(WRegion*)cwin; + param.mp=mp; + param.res_frame=NULL; + + ret=hook_call_alt_p(tiling_placement_alt, ¶m, + (WHookMarshallExtl*)placement_mrsh_extl); + + if(ret && param.res_frame!=NULL && + REGION_MANAGER(param.res_frame)==(WRegion*)ws){ + + target=(WRegion*)param.res_frame; + + ph=region_prepare_manage(target, cwin, mp, redir); + if(ph!=NULL) + return ph; + } + + target=find_suitable_target(ws); + + if(target==NULL){ + warn(TR("Ooops... could not find a region to attach client window " + "to on workspace %s."), region_name((WRegion*)ws)); + return NULL; + } + + return region_prepare_manage(target, cwin, mp, redir); +} + diff --git a/mod_tiling/placement.h b/mod_tiling/placement.h new file mode 100644 index 0000000..9094b62 --- /dev/null +++ b/mod_tiling/placement.h @@ -0,0 +1,38 @@ +/* + * ion/mod_tiling/placement.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_MOD_TILING_PLACEMENT_H +#define ION_MOD_TILING_PLACEMENT_H + +#include +#include +#include +#include +#include "tiling.h" + + +typedef struct{ + WTiling *ws; + WRegion *reg; + const WManageParams *mp; + + WFrame *res_frame; +} WTilingPlacementParams; + +/* Handlers of this hook should take (WClientWin*, WTiling*, WManageParams*) + * as parameter. + */ +extern WHook *tiling_placement_alt; + +extern WPHolder *tiling_prepare_manage(WTiling *ws, const WClientWin *cwin, + const WManageParams *param, int redir); + +#endif /* ION_MOD_TILING_PLACEMENT_H */ diff --git a/mod_tiling/split-stdisp.c b/mod_tiling/split-stdisp.c new file mode 100644 index 0000000..d1c55f2 --- /dev/null +++ b/mod_tiling/split-stdisp.c @@ -0,0 +1,747 @@ +/* + * ion/mod_tiling/split-stdisp.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include "split.h" +#include "split-stdisp.h" +#include "tiling.h" + + +/*{{{ Helper routines */ + + +#define STDISP_IS_HORIZONTAL(STDISP) \ + ((STDISP)->orientation==REGION_ORIENTATION_HORIZONTAL) + +#define STDISP_IS_VERTICAL(STDISP) \ + ((STDISP)->orientation==REGION_ORIENTATION_VERTICAL) + +#define STDISP_GROWS_L_TO_R(STDISP) (STDISP_IS_HORIZONTAL(STDISP) && \ + ((STDISP)->corner==MPLEX_STDISP_TL || \ + (STDISP)->corner==MPLEX_STDISP_BL)) + +#define STDISP_GROWS_R_TO_L(STDISP) (STDISP_IS_HORIZONTAL(STDISP) && \ + ((STDISP)->corner==MPLEX_STDISP_TR || \ + (STDISP)->corner==MPLEX_STDISP_BR)) + +#define STDISP_GROWS_T_TO_B(STDISP) (STDISP_IS_VERTICAL(STDISP) && \ + ((STDISP)->corner==MPLEX_STDISP_TL || \ + (STDISP)->corner==MPLEX_STDISP_TR)) + +#define STDISP_GROWS_B_TO_T(STDISP) (STDISP_IS_VERTICAL(STDISP) && \ + ((STDISP)->corner==MPLEX_STDISP_BL || \ + (STDISP)->corner==MPLEX_STDISP_BR)) + +#define GEOM(S) (((WSplit*)S)->geom) + +#define IMPLIES(X, Y) (!(X) || (Y)) + + +static int other_dir(int dir) +{ + return (dir==SPLIT_VERTICAL ? SPLIT_HORIZONTAL : SPLIT_VERTICAL); +} + + +static void swap(int *x, int *y) +{ + int z=*x; + *x=*y; + *y=z; +} + + +static void swapptr(WSplit **x, WSplit **y) +{ + void *z=*x; + *x=*y; + *y=z; +} + + +static void swapgeom(WRectangle *g, WRectangle *h) +{ + WRectangle tmp=*g; + *g=*h; + *h=tmp; +} + + +int stdisp_recommended_w(WSplitST *stdisp) +{ + if(stdisp->regnode.reg==NULL) + return CF_STDISP_MIN_SZ; + + if(stdisp->fullsize && stdisp->orientation==REGION_ORIENTATION_HORIZONTAL){ + WTiling *ws=REGION_MANAGER_CHK(stdisp->regnode.reg, WTiling); + assert(ws!=NULL); + return REGION_GEOM(ws).w; + } + + return maxof(CF_STDISP_MIN_SZ, region_min_w(stdisp->regnode.reg)); +} + + +int stdisp_recommended_h(WSplitST *stdisp) +{ + if(stdisp->regnode.reg==NULL) + return CF_STDISP_MIN_SZ; + + if(stdisp->fullsize && stdisp->orientation==REGION_ORIENTATION_VERTICAL){ + WTiling *ws=REGION_MANAGER_CHK(stdisp->regnode.reg, WTiling); + assert(ws!=NULL); + return REGION_GEOM(ws).h; + } + + return maxof(CF_STDISP_MIN_SZ, region_min_h(stdisp->regnode.reg)); +} + + +static bool stdisp_dir_ok(WSplitSplit *p, WSplitST *stdisp) +{ + assert(p->tl==(WSplit*)stdisp || p->br==(WSplit*)stdisp); + + return (IMPLIES(STDISP_IS_HORIZONTAL(stdisp), p->dir==SPLIT_VERTICAL) && + IMPLIES(STDISP_IS_VERTICAL(stdisp), p->dir==SPLIT_HORIZONTAL)); +} + + +/*}}}*/ + + +/*{{{ New rotation and flipping primitives */ + + +static void replace(WSplitSplit *a, WSplitSplit *p) +{ + if(((WSplit*)a)->parent!=NULL) + splitinner_replace(((WSplit*)a)->parent, (WSplit*)a, (WSplit*)p); + else + splittree_changeroot((WSplit*)a, (WSplit*)p); +} + + +/* Yes, it is overparametrised */ +static void rotate_right(WSplitSplit *a, WSplitSplit *p, WSplit *y) +{ + assert(a->tl==(WSplit*)p && p->tl==y); + + /* Right rotation: + * a p + * / \ / \ + * p x => y a + * / \ / \ + * y ? ? x + */ + + a->tl=p->br; + a->tl->parent=(WSplitInner*)a; + replace(a, p); + p->br=(WSplit*)a; + ((WSplit*)a)->parent=(WSplitInner*)p; +} + + +static void rot_rs_rotate_right(WSplitSplit *a, WSplitSplit *p, WSplit *y) +{ + WRectangle xg, yg, pg, ag, qg; + WSplit *x=a->br, *q=p->br; + + assert(a->dir==other_dir(p->dir)); + + qg=GEOM(q); + xg=GEOM(x); + yg=GEOM(y); + pg=GEOM(p); + ag=GEOM(a); + + if(a->dir==SPLIT_HORIZONTAL){ + /* yyxx yyyy + * ??xx => ??xx + * ??xx ??xx + */ + yg.w=ag.w; + pg.w=ag.w; + xg.h=qg.h; + ag.h=qg.h; + xg.y=qg.y; + ag.y=qg.y; + }else{ + /* y?? y?? + * y?? y?? + * xxx => yxx + * xxx yxx + */ + yg.h=ag.h; + pg.h=ag.h; + xg.w=qg.w; + ag.w=qg.w; + xg.x=qg.x; + ag.x=qg.x; + } + + rotate_right(a, p, y); + + GEOM(p)=pg; + GEOM(a)=ag; + + split_do_resize(x, &xg, PRIMN_TL, PRIMN_TL, FALSE); + split_do_resize(y, &yg, PRIMN_BR, PRIMN_BR, FALSE); +} + + + +static void rotate_left(WSplitSplit *a, WSplitSplit *p, WSplit *y) +{ + assert(a->br==(WSplit*)p && p->br==y); + + /* Left rotation: + * a p + * / \ / \ + * x p => a y + * / \ / \ + * ? y x ? + */ + + a->br=p->tl; + a->br->parent=(WSplitInner*)a; + replace(a, p); + p->tl=(WSplit*)a; + ((WSplit*)a)->parent=(WSplitInner*)p; +} + + +static void rot_rs_rotate_left(WSplitSplit *a, WSplitSplit *p, WSplit *y) +{ + WRectangle xg, yg, pg, ag, qg; + WSplit *x=a->tl, *q=p->tl; + + assert(a->dir==other_dir(p->dir)); + + qg=GEOM(q); + xg=GEOM(x); + yg=GEOM(y); + pg=GEOM(p); + ag=GEOM(a); + + if(a->dir==SPLIT_HORIZONTAL){ + /* xx?? xx?? + * xx?? => xx?? + * xxyy yyyy + */ + yg.w=ag.w; + yg.x=ag.x; + pg.w=ag.w; + pg.x=ag.x; + xg.h=qg.h; + ag.h=qg.h; + }else{ + /* xxx xxy + * xxx xxy + * ??y => ??y + * ??y ??y + */ + yg.h=ag.h; + yg.y=ag.y; + pg.h=ag.h; + pg.y=ag.y; + xg.w=qg.w; + ag.w=qg.w; + } + + rotate_left(a, p, y); + + GEOM(p)=pg; + GEOM(a)=ag; + + split_do_resize(x, &xg, PRIMN_BR, PRIMN_BR, FALSE); + split_do_resize(y, &yg, PRIMN_TL, PRIMN_TL, FALSE); +} + + + +static void flip_right(WSplitSplit *a, WSplitSplit *p) +{ + WSplit *tmp; + + assert(a->tl==(WSplit*)p); + + /* Right flip: + * a p + * / \ / \ + * p x => a y + * / \ / \ + * ? y ? x + */ + + a->tl=p->tl; + a->tl->parent=(WSplitInner*)a; + replace(a, p); + p->tl=(WSplit*)a; + ((WSplit*)a)->parent=(WSplitInner*)p; +} + + +static void rot_rs_flip_right(WSplitSplit *a, WSplitSplit *p) +{ + WRectangle xg, yg, pg, ag, qg; + WSplit *x=a->br, *q=p->tl, *y=p->br; + + assert(a->dir==other_dir(p->dir)); + + qg=GEOM(q); + xg=GEOM(x); + yg=GEOM(y); + pg=GEOM(p); + ag=GEOM(a); + + if(a->dir==SPLIT_HORIZONTAL){ + /* ??xx ??xx + * ??xx => ??xx + * yyxx yyyy + */ + yg.w=ag.w; + pg.w=ag.w; + ag.h=qg.h; + xg.h=qg.h; + }else{ + /* ??y ??y + * ??y ??y + * xxx => xxy + * xxx xxy + */ + yg.h=ag.h; + pg.h=ag.h; + ag.w=qg.w; + xg.w=qg.w; + } + + flip_right(a, p); + + GEOM(p)=pg; + GEOM(a)=ag; + + split_do_resize(x, &xg, PRIMN_BR, PRIMN_BR, FALSE); + split_do_resize(y, &yg, PRIMN_BR, PRIMN_BR, FALSE); +} + + +static void flip_left(WSplitSplit *a, WSplitSplit *p) +{ + WSplit *tmp; + + assert(a->br==(WSplit*)p); + + /* Left flip: + * a p + * / \ / \ + * x p => y a + * / \ / \ + * y ? x ? + */ + + a->br=p->br; + a->br->parent=(WSplitInner*)a; + replace(a, p); + p->br=(WSplit*)a; + ((WSplit*)a)->parent=(WSplitInner*)p; +} + + +static void rot_rs_flip_left(WSplitSplit *a, WSplitSplit *p) +{ + WRectangle xg, yg, pg, ag, qg; + WSplit *x=a->tl, *q=p->br, *y=p->tl; + + assert(a->dir==other_dir(p->dir)); + + qg=GEOM(q); + xg=GEOM(x); + yg=GEOM(y); + pg=GEOM(p); + ag=GEOM(a); + + if(a->dir==SPLIT_HORIZONTAL){ + /* xxyy yyyy + * xx?? => xx?? + * xx?? xx?? + */ + yg.x=ag.x; + yg.w=ag.w; + pg.x=ag.x; + pg.w=ag.w; + xg.h=qg.h; + xg.y=qg.y; + ag.h=qg.h; + ag.y=qg.y; + }else{ + /* xxx yxx + * xxx yxx + * y?? => y?? + * y?? y?? + */ + yg.y=ag.y; + yg.h=ag.h; + pg.y=ag.y; + pg.h=ag.h; + xg.w=qg.w; + xg.x=qg.x; + ag.w=qg.w; + ag.x=qg.x; + } + + flip_left(a, p); + + GEOM(p)=pg; + GEOM(a)=ag; + + split_do_resize(x, &xg, PRIMN_TL, PRIMN_TL, FALSE); + split_do_resize(y, &yg, PRIMN_TL, PRIMN_TL, FALSE); +} + + +static void rot_para_right(WSplitSplit *a, WSplitSplit *p, + WSplit *y) +{ + rotate_right(a, p, y); + if(a->dir==SPLIT_VERTICAL){ + GEOM(p).y=GEOM(a).y; + GEOM(p).h=GEOM(a).h; + GEOM(a).y=GEOM(a->tl).y; + GEOM(a).h=GEOM(a->br).y+GEOM(a->br).h-GEOM(a).y; + }else{ + GEOM(p).x=GEOM(a).x; + GEOM(p).w=GEOM(a).w; + GEOM(a).x=GEOM(a->tl).x; + GEOM(a).w=GEOM(a->br).x+GEOM(a->br).w-GEOM(a).x; + } +} + + +static void rot_para_left(WSplitSplit *a, WSplitSplit *p, + WSplit *y) +{ + rotate_left(a, p, y); + if(a->dir==SPLIT_VERTICAL){ + GEOM(p).y=GEOM(a).y; + GEOM(p).h=GEOM(a).h; + GEOM(a).h=GEOM(a->br).y+GEOM(a->br).h-GEOM(a).y; + }else{ + GEOM(p).x=GEOM(a).x; + GEOM(p).w=GEOM(a).w; + GEOM(a).w=GEOM(a->br).x+GEOM(a->br).w-GEOM(a).x; + } +} + + +/*}}}*/ + + +/*{{{ Sink */ + + +static bool do_try_sink_stdisp_orth(WSplitSplit *p, WSplitST *stdisp, + WSplitSplit *other, bool force) +{ + bool doit=force; + + assert(p->dir==other_dir(other->dir)); + assert(stdisp_dir_ok(p, stdisp)); + + if(STDISP_GROWS_T_TO_B(stdisp) || STDISP_GROWS_L_TO_R(stdisp)){ + if(STDISP_GROWS_L_TO_R(stdisp)){ + assert(other->dir==SPLIT_HORIZONTAL); + if(other->tl->geom.w>=stdisp_recommended_w(stdisp)) + doit=TRUE; + }else{ /* STDISP_GROWS_T_TO_B */ + assert(other->dir==SPLIT_VERTICAL); + if(other->tl->geom.h>=stdisp_recommended_h(stdisp)) + doit=TRUE; + } + + if(doit){ + if(p->br==(WSplit*)stdisp) + rot_rs_flip_right(p, other); + else /* p->tl==stdisp */ + rot_rs_rotate_left(p, other, other->br); + } + }else{ /* STDISP_GROWS_B_TO_T or STDISP_GROW_R_TO_L */ + if(STDISP_GROWS_R_TO_L(stdisp)){ + assert(other->dir==SPLIT_HORIZONTAL); + if(other->br->geom.w>=stdisp_recommended_w(stdisp)) + doit=TRUE; + }else{ /* STDISP_GROWS_B_TO_T */ + assert(other->dir==SPLIT_VERTICAL); + if(other->br->geom.h>=stdisp_recommended_h(stdisp)) + doit=TRUE; + } + + if(doit){ + if(p->tl==(WSplit*)stdisp) + rot_rs_flip_left(p, other); + else /* p->br==stdisp */ + rot_rs_rotate_right(p, other, other->tl); + } + } + + return doit; +} + + +static bool do_try_sink_stdisp_para(WSplitSplit *p, WSplitST *stdisp, + WSplitSplit *other, bool force) +{ + if(!force){ + if(STDISP_IS_HORIZONTAL(stdisp)){ + if(stdisp_recommended_w(stdisp)>=GEOM(p).w) + return FALSE; + }else{ + if(stdisp_recommended_h(stdisp)>=GEOM(p).h) + return FALSE; + } + } + + if(p->tl==(WSplit*)stdisp) + rot_para_left(p, other, other->br); + else + rot_para_right(p, other, other->tl); + + return TRUE; +} + + +bool split_try_sink_stdisp(WSplitSplit *node, bool iterate, bool force) +{ + bool didsomething=FALSE; + bool more=TRUE; + + /*assert(OBJ_IS_EXACTLY(node, WSplitSplit));*/ + + while(more){ + WSplit *tl=node->tl; + WSplit *br=node->br; + WSplitSplit *other=NULL; + WSplitST *st; + + if(OBJ_IS(tl, WSplitST)){ + st=(WSplitST*)tl; + other=OBJ_CAST(br, WSplitSplit); + }else if(OBJ_IS(br, WSplitST)){ + st=(WSplitST*)br; + other=OBJ_CAST(tl, WSplitSplit); + }else{ + break; + } + + if(other==NULL) + break; + + if(!stdisp_dir_ok(node, st)) + break; + + if(other->dir==other_dir(node->dir)){ + if(!do_try_sink_stdisp_orth(node, st, other, force)) + break; + }else /*if(br->dir==node->dir)*/{ + if(!do_try_sink_stdisp_para(node, st, other, force)) + break; + } + didsomething=TRUE; + more=iterate; + } + + return didsomething; +} + + +/*}}}*/ + + +/*{{{ Unsink */ + + +static bool do_try_unsink_stdisp_orth(WSplitSplit *a, WSplitSplit *p, + WSplitST *stdisp, bool force) +{ + bool doit=force; + + assert(p->dir==other_dir(a->dir)); + assert(stdisp_dir_ok(p, stdisp)); + + if(STDISP_GROWS_T_TO_B(stdisp) || STDISP_GROWS_L_TO_R(stdisp)){ + if(STDISP_GROWS_L_TO_R(stdisp)){ + assert(a->dir==SPLIT_HORIZONTAL); + if(GEOM(stdisp).wdir==SPLIT_VERTICAL); + if(GEOM(stdisp).htl){ + if((WSplit*)stdisp==p->br) + rot_rs_flip_right(a, p); + else /*stdisp==p->tl*/ + rot_rs_rotate_right(a, p, (WSplit*)stdisp); + }else{ /*p==a->br*/ +#if 0 + /* abnormal cases. */ + warn(TR("Status display in bad split configuration.")); + return FALSE; +#else + if((WSplit*)stdisp==p->br) + rot_rs_rotate_left(a, p, (WSplit*)stdisp); + else /*stdisp==p->tl*/ + rot_rs_flip_left(a, p); +#endif + } + } + }else{ /*STDISP_GROWS_B_TO_T || STDISP_GROWS_R_TO_L*/ + if(STDISP_GROWS_R_TO_L(stdisp)){ + assert(a->dir==SPLIT_HORIZONTAL); + if(GEOM(stdisp).wdir==SPLIT_VERTICAL); + if(GEOM(stdisp).htl){ +#if 0 + /* abnormal cases. */ + warn(TR("Status display in bad split configuration.")); + return FALSE; +#else + if((WSplit*)stdisp==p->br) + rot_rs_flip_right(a, p); + else /*stdisp==p->tl*/ + rot_rs_rotate_right(a, p, (WSplit*)stdisp); +#endif + }else{ /*p==a->br*/ + if((WSplit*)stdisp==p->br) + rot_rs_rotate_left(a, p, (WSplit*)stdisp); + else /*stdisp==p->tl*/ + rot_rs_flip_left(a, p); + } + } + } + + return doit; +} + + +static bool do_try_unsink_stdisp_para(WSplitSplit *a, WSplitSplit *p, + WSplitST *stdisp, bool force) +{ + if(!force){ + if(STDISP_IS_HORIZONTAL(stdisp)){ + if(stdisp_recommended_w(stdisp)<=GEOM(p).w) + return FALSE; + }else{ + if(stdisp_recommended_h(stdisp)<=GEOM(p).h) + return FALSE; + } + } + + + if(a->tl==(WSplit*)p && p->tl==(WSplit*)stdisp){ + rot_para_right(a, p, (WSplit*)stdisp); + }else if(a->br==(WSplit*)p && p->br==(WSplit*)stdisp){ + rot_para_left(a, p, (WSplit*)stdisp); + }else{ + warn(TR("Status display badly located in split tree.")); + return FALSE; + } + + return TRUE; +} + + +bool split_try_unsink_stdisp(WSplitSplit *node, bool iterate, bool force) +{ + bool didsomething=FALSE; + bool more=TRUE; + + /*assert(OBJ_IS_EXACTLY(node, WSplitSplit));*/ + + while(more){ + WSplitSplit *p=OBJ_CAST(((WSplit*)node)->parent, WSplitSplit); + WSplit *tl=node->tl; + WSplit *br=node->br; + WSplitST *st; + + if(p==NULL) + break; + + if(OBJ_IS(tl, WSplitST)) + st=(WSplitST*)tl; + else if(OBJ_IS(br, WSplitST)) + st=(WSplitST*)br; + else + break; + + if(!stdisp_dir_ok(node, st)) + break; + + if(p->dir==other_dir(node->dir)){ + if(!do_try_unsink_stdisp_orth(p, node, st, force)) + break; + }else /*if(p->dir==node->dir)*/{ + if(!do_try_unsink_stdisp_para(p, node, st, force)) + break; + } + + didsomething=TRUE; + more=iterate; + } + + return didsomething; +} + + +/*}}}*/ + + +/*{{{ Sink or unsink */ + + +bool split_regularise_stdisp(WSplitST *stdisp) +{ + WSplitSplit *node=OBJ_CAST(((WSplit*)stdisp)->parent, WSplitSplit); + + if(node==NULL) + return FALSE; + + if(STDISP_IS_HORIZONTAL(stdisp)){ + if(GEOM(stdisp).wstdisp_recommended_w(stdisp)) + return split_try_sink_stdisp(node, TRUE, FALSE); + }else{ + if(GEOM(stdisp).hstdisp_recommended_h(stdisp)) + return split_try_sink_stdisp(node, TRUE, FALSE); + } + + return FALSE; +} + + +/*}}}*/ + diff --git a/mod_tiling/split-stdisp.h b/mod_tiling/split-stdisp.h new file mode 100644 index 0000000..5d41fe1 --- /dev/null +++ b/mod_tiling/split-stdisp.h @@ -0,0 +1,24 @@ +/* + * ion/mod_tiling/split-stdisp.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_MOD_TILING_SPLIT_STDISP_H +#define ION_MOD_TILING_SPLIT_STDISP_H + +#include "split.h" + +extern bool split_try_sink_stdisp(WSplitSplit *node, bool iterate, bool force); +extern bool split_try_unsink_stdisp(WSplitSplit *node, bool iterate, bool force); +extern bool split_regularise_stdisp(WSplitST *stdisp); + +extern int stdisp_recommended_w(WSplitST *stdisp); +extern int stdisp_recommended_h(WSplitST *stdisp); + +#endif /* ION_MOD_TILING_SPLIT_STDISP_H */ diff --git a/mod_tiling/split.c b/mod_tiling/split.c new file mode 100644 index 0000000..54864d7 --- /dev/null +++ b/mod_tiling/split.c @@ -0,0 +1,2004 @@ +/* + * ion/mod_tiling/split.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "tiling.h" +#include "split.h" +#include "split-stdisp.h" + + +static Rb_node split_of_map=NULL; + + +/*{{{ Geometry helper functions */ + + +int split_size(WSplit *split, int dir) +{ + return (dir==SPLIT_HORIZONTAL ? split->geom.w : split->geom.h); +} + +int split_other_size(WSplit *split, int dir) +{ + return (dir==SPLIT_VERTICAL ? split->geom.w : split->geom.h); +} + +int split_pos(WSplit *split, int dir) +{ + return (dir==SPLIT_HORIZONTAL ? split->geom.x : split->geom.y); +} + +int split_other_pos(WSplit *split, int dir) +{ + return (dir==SPLIT_VERTICAL ? split->geom.x : split->geom.y); +} + + +static int reg_calcresize(WRegion *reg, int dir, int nsize) +{ + int tmp; + + if(dir==SPLIT_HORIZONTAL) + tmp=region_min_w(reg); + else + tmp=region_min_h(reg); + + return (nsizemax) + *what=max; +} + + +/*}}}*/ + + +/*{{{ Functions to get and set a region's containing node */ + + +#define node_of_reg splittree_node_of + +WSplitRegion *splittree_node_of(WRegion *reg) +{ + Rb_node node=NULL; + int found=0; + + /*assert(REGION_MANAGER_CHK(reg, WTiling)!=NULL);*/ + + if(split_of_map!=NULL){ + node=rb_find_pkey_n(split_of_map, reg, &found); + if(found) + return (WSplitRegion*)(node->v.val); + } + + return NULL; +} + + +#define set_node_of_reg splittree_set_node_of + + +bool splittree_set_node_of(WRegion *reg, WSplitRegion *split) +{ + Rb_node node=NULL; + int found; + + /*assert(REGION_MANAGER_CHK(reg, WTiling)!=NULL);*/ + + if(split_of_map==NULL){ + if(split==NULL) + return TRUE; + split_of_map=make_rb(); + if(split_of_map==NULL) + return FALSE; + } + + node=rb_find_pkey_n(split_of_map, reg, &found); + if(found) + rb_delete_node(node); + + return (rb_insertp(split_of_map, reg, split)!=NULL); +} + + +/*}}}*/ + + +/*{{{ Primn */ + + +WPrimn primn_invert(WPrimn primn) +{ + return (primn==PRIMN_TL + ? PRIMN_BR + : (primn==PRIMN_BR + ? PRIMN_TL + : primn)); +} + + +WPrimn primn_none2any(WPrimn primn) +{ + return (primn==PRIMN_NONE ? PRIMN_ANY : primn); +} + + +/*}}}*/ + + +/*{{{ Create */ + + +bool split_init(WSplit *split, const WRectangle *geom) +{ + split->parent=NULL; + split->ws_if_root=NULL; + split->geom=*geom; + split->min_w=0; + split->min_h=0; + split->max_w=INT_MAX; + split->max_h=INT_MAX; + split->unused_w=-1; + split->unused_h=-1; + return TRUE; +} + +bool splitinner_init(WSplitInner *split, const WRectangle *geom) +{ + return split_init(&(split->split), geom); +} + + +bool splitsplit_init(WSplitSplit *split, const WRectangle *geom, int dir) +{ + splitinner_init(&(split->isplit), geom); + split->dir=dir; + split->tl=NULL; + split->br=NULL; + split->current=SPLIT_CURRENT_TL; + return TRUE; +} + + +bool splitregion_init(WSplitRegion *split, const WRectangle *geom, + WRegion *reg) +{ + split_init(&(split->split), geom); + split->reg=reg; + if(reg!=NULL) + set_node_of_reg(reg, split); + return TRUE; +} + + +bool splitst_init(WSplitST *split, const WRectangle *geom, WRegion *reg) +{ + splitregion_init(&(split->regnode), geom, reg); + split->orientation=REGION_ORIENTATION_HORIZONTAL; + split->corner=MPLEX_STDISP_BL; + return TRUE; +} + + +WSplitSplit *create_splitsplit(const WRectangle *geom, int dir) +{ + CREATEOBJ_IMPL(WSplitSplit, splitsplit, (p, geom, dir)); +} + + +WSplitRegion *create_splitregion(const WRectangle *geom, WRegion *reg) +{ + CREATEOBJ_IMPL(WSplitRegion, splitregion, (p, geom, reg)); +} + + +WSplitST *create_splitst(const WRectangle *geom, WRegion *reg) +{ + CREATEOBJ_IMPL(WSplitST, splitst, (p, geom, reg)); +} + + +/*}}}*/ + + +/*{{{ Deinit */ + + +void split_deinit(WSplit *split) +{ + assert(split->parent==NULL); +} + + +void splitinner_deinit(WSplitInner *split) +{ + split_deinit(&(split->split)); +} + + +void splitsplit_deinit(WSplitSplit *split) +{ + if(split->tl!=NULL){ + split->tl->parent=NULL; + destroy_obj((Obj*)(split->tl)); + } + if(split->br!=NULL){ + split->br->parent=NULL; + destroy_obj((Obj*)(split->br)); + } + + splitinner_deinit(&(split->isplit)); +} + + +void splitregion_deinit(WSplitRegion *split) +{ + if(split->reg!=NULL){ + set_node_of_reg(split->reg, NULL); + split->reg=NULL; + } + + split_deinit(&(split->split)); +} + + +void splitst_deinit(WSplitST *split) +{ + splitregion_deinit(&(split->regnode)); +} + + +/*}}}*/ + + +/*{{{ Size bounds management */ + + +static void splitregion_update_bounds(WSplitRegion *node, bool recursive) +{ + WSizeHints hints; + WSplit *snode=(WSplit*)node; + + assert(node->reg!=NULL); + + region_size_hints(node->reg, &hints); + + snode->min_w=maxof(1, hints.min_set ? hints.min_width : 1); + snode->max_w=INT_MAX; + snode->unused_w=-1; + + snode->min_h=maxof(1, hints.min_set ? hints.min_height : 1); + snode->max_h=INT_MAX; + snode->unused_h=-1; +} + + +static void splitst_update_bounds(WSplitST *node, bool rec) +{ + WSplit *snode=(WSplit*)node; + + if(node->regnode.reg==NULL){ + snode->min_w=CF_STDISP_MIN_SZ; + snode->min_h=CF_STDISP_MIN_SZ; + snode->max_w=CF_STDISP_MIN_SZ; + snode->max_h=CF_STDISP_MIN_SZ; + }else{ + WSizeHints hints; + region_size_hints(node->regnode.reg, &hints); + snode->min_w=maxof(1, hints.min_set ? hints.min_width : 1); + snode->max_w=maxof(snode->min_w, hints.min_width); + snode->min_h=maxof(1, hints.min_set ? hints.min_height : 1); + snode->max_h=maxof(snode->min_h, hints.min_height); + } + + snode->unused_w=-1; + snode->unused_h=-1; + + if(node->orientation==REGION_ORIENTATION_HORIZONTAL){ + snode->min_w=CF_STDISP_MIN_SZ; + snode->max_w=INT_MAX; + }else{ + snode->min_h=CF_STDISP_MIN_SZ; + snode->max_h=INT_MAX; + } +} + + +static void splitsplit_update_bounds(WSplitSplit *split, bool recursive) +{ + WSplit *tl, *br; + WSplit *node=(WSplit*)split; + + assert(split->tl!=NULL && split->br!=NULL); + + tl=split->tl; + br=split->br; + + if(recursive){ + split_update_bounds(tl, TRUE); + split_update_bounds(br, TRUE); + } + + if(split->dir==SPLIT_HORIZONTAL){ + node->max_w=infadd(tl->max_w, br->max_w); + node->min_w=infadd(tl->min_w, br->min_w); + node->unused_w=unusedadd(tl->unused_w, br->unused_w); + node->min_h=maxof(tl->min_h, br->min_h); + node->max_h=maxof(minof(tl->max_h, br->max_h), node->min_h); + node->unused_h=minof(tl->unused_h, br->unused_h); + }else{ + node->max_h=infadd(tl->max_h, br->max_h); + node->min_h=infadd(tl->min_h, br->min_h); + node->unused_h=unusedadd(tl->unused_h, br->unused_h); + node->min_w=maxof(tl->min_w, br->min_w); + node->max_w=maxof(minof(tl->max_w, br->max_w), node->min_w); + node->unused_w=minof(tl->unused_w, br->unused_w); + } +} + + +void split_update_bounds(WSplit *node, bool recursive) +{ + CALL_DYN(split_update_bounds, node, (node, recursive)); +} + + +void splitsplit_update_geom_from_children(WSplitSplit *node) +{ + WSplit *split=(WSplit*)node; + + if(node->dir==SPLIT_VERTICAL){ + ((WSplit*)node)->geom.h=node->tl->geom.h+node->br->geom.h; + ((WSplit*)node)->geom.y=node->tl->geom.y; + }else if(node->dir==SPLIT_HORIZONTAL){ + ((WSplit*)node)->geom.w=node->tl->geom.w+node->br->geom.w; + ((WSplit*)node)->geom.x=node->tl->geom.x; + } +} + + +/*}}}*/ + + +/*{{{ Status display handling helper functions. */ + + +static WSplitST *saw_stdisp=NULL; + + +void splittree_begin_resize() +{ + saw_stdisp=NULL; +} + + +void splittree_end_resize() +{ + if(saw_stdisp!=NULL){ + split_regularise_stdisp(saw_stdisp); + saw_stdisp=NULL; + } +} + + +static void splittree_scan_stdisp_rootward_(WSplitInner *node_) +{ + WSplitSplit *node=OBJ_CAST(node_, WSplitSplit); + + if(node!=NULL){ + if(OBJ_IS(node->tl, WSplitST)){ + saw_stdisp=(WSplitST*)(node->tl); + return; + }else if(OBJ_IS(node->br, WSplitST)){ + saw_stdisp=(WSplitST*)(node->br); + return; + } + } + + if(node_->split.parent!=NULL) + splittree_scan_stdisp_rootward_(node_->split.parent); +} + + +void splittree_scan_stdisp_rootward(WSplit *node) +{ + if(node->parent!=NULL) + splittree_scan_stdisp_rootward_(node->parent); +} + + +static WSplitSplit *splittree_scan_stdisp_parent(WSplit *node_, bool set_saw) +{ + WSplitSplit *r, *node=OBJ_CAST(node_, WSplitSplit); + + if(node==NULL) + return NULL; + + if(OBJ_IS(node->tl, WSplitST)){ + if(set_saw) + saw_stdisp=(WSplitST*)node->tl; + return node; + } + + if(OBJ_IS(node->br, WSplitST)){ + if(set_saw) + saw_stdisp=(WSplitST*)node->br; + return node; + } + + r=splittree_scan_stdisp_parent(node->tl, set_saw); + if(r==NULL) + r=splittree_scan_stdisp_parent(node->br, set_saw); + return r; +} + + +static bool stdisp_immediate_child(WSplitSplit *node) +{ + return (node!=NULL && (OBJ_IS(node->tl, WSplitST) || + OBJ_IS(node->br, WSplitST))); +} + + +static WSplit *move_stdisp_out_of_way(WSplit *node) +{ + WSplitSplit *stdispp; + + if(!OBJ_IS(node, WSplitSplit)) + return node; + + stdispp=splittree_scan_stdisp_parent(node, TRUE); + + if(stdispp==NULL) + return node; + + while(stdispp->tl!=node && stdispp->br!=node){ + if(!split_try_unsink_stdisp(stdispp, FALSE, TRUE)){ + warn(TR("Unable to move the status display out of way.")); + return NULL; + } + } + + return (WSplit*)stdispp; +} + + +/*}}}*/ + + +/*{{{ Low-level resize code; from root to leaf */ + + +static void split_do_resize_default(WSplit *node, const WRectangle *ng, + WPrimn hprimn, WPrimn vprimn, + bool transpose) +{ + node->geom=*ng; +} + + +static void splitregion_do_resize(WSplitRegion *node, const WRectangle *ng, + WPrimn hprimn, WPrimn vprimn, + bool transpose) +{ + assert(node->reg!=NULL); + region_fit(node->reg, ng, REGION_FIT_EXACT); + split_update_bounds(&(node->split), FALSE); + node->split.geom=*ng; +} + + +static void splitst_do_resize(WSplitST *node, const WRectangle *ng, + WPrimn hprimn, WPrimn vprimn, + bool transpose) +{ + saw_stdisp=node; + + if(node->regnode.reg==NULL){ + ((WSplit*)node)->geom=*ng; + }else{ + splitregion_do_resize(&(node->regnode), ng, hprimn, vprimn, + transpose); + } +} + + +static int other_dir(int dir) +{ + return (dir==SPLIT_VERTICAL ? SPLIT_HORIZONTAL : SPLIT_VERTICAL); +} + + +static void adjust_sizes(int *tls_, int *brs_, int nsize, int sz, + int tlmin, int brmin, int tlmax, int brmax, + int primn) +{ + int tls=*tls_; + int brs=*brs_; + + if(primn==PRIMN_TL){ + tls=tls+nsize-sz; + bound(&tls, tlmin, tlmax); + brs=nsize-tls; + bound(&brs, brmin, brmax); + tls=nsize-brs; + bound(&tls, tlmin, tlmax); + }else if(primn==PRIMN_BR){ + brs=brs+nsize-sz; + bound(&brs, brmin, brmax); + tls=nsize-brs; + bound(&tls, tlmin, tlmax); + brs=nsize-tls; + bound(&brs, brmin, brmax); + }else{ /* && PRIMN_ANY */ + tls=tls*nsize/sz; + bound(&tls, tlmin, tlmax); + brs=nsize-tls; + bound(&brs, brmin, brmax); + tls=nsize-brs; + bound(&tls, tlmin, tlmax); + } + + *tls_=tls; + *brs_=brs; +} + + +static void get_minmaxunused(WSplit *node, int dir, + int *min, int *max, int *unused) +{ + if(dir==SPLIT_VERTICAL){ + *min=node->min_h; + *max=maxof(*min, node->max_h); + *unused=minof(node->unused_h, node->geom.h); + }else{ + *min=node->min_w; + *max=maxof(*min, node->max_w); + *unused=minof(node->unused_w, node->geom.w); + } +} + + +void splitsplit_do_resize(WSplitSplit *node, const WRectangle *ng, + WPrimn hprimn, WPrimn vprimn, bool transpose) +{ + assert(ng->w>=0 && ng->h>=0); + assert(node->tl!=NULL && node->br!=NULL); + assert(!transpose || (hprimn==PRIMN_ANY && vprimn==PRIMN_ANY)); + + { + WSplit *tl=node->tl, *br=node->br; + int tls=split_size((WSplit*)tl, node->dir); + int brs=split_size((WSplit*)br, node->dir); + int sz=tls+brs; + /* Status display can not be transposed. */ + int dir=((transpose && !stdisp_immediate_child(node)) + ? other_dir(node->dir) + : node->dir); + int nsize=(dir==SPLIT_VERTICAL ? ng->h : ng->w); + int primn=(dir==SPLIT_VERTICAL ? vprimn : hprimn); + int tlmin, tlmax, tlunused, tlused; + int brmin, brmax, brunused, brused; + WRectangle tlg=*ng, brg=*ng; + + get_minmaxunused(tl, dir, &tlmin, &tlmax, &tlunused); + get_minmaxunused(br, dir, &brmin, &brmax, &brunused); + + tlused=maxof(0, tls-maxof(0, tlunused)); + brused=maxof(0, brs-maxof(0, brunused)); + /* tlmin, brmin >= 1 => (tls>=tlmin, brs>=brmin => sz>0) */ + + if(sz>2){ + if(primn==PRIMN_ANY && (tlunused>=0 || brunused>=0)){ + if(nsize<=tlused+brused){ + /* Need to shrink a tangible node */ + adjust_sizes(&tls, &brs, nsize, sz, + tlmin, brmin, tlused, brused, primn); + }else{ + /* Just expand or shrink unused space */ + adjust_sizes(&tls, &brs, nsize, sz, + tlused, brused, + (tlunused<0 ? tlused : tlmax), + (brunused<0 ? brused : brmax), primn); + } + + }else{ + adjust_sizes(&tls, &brs, nsize, sz, + tlmin, brmin, tlmax, brmax, primn); + } + } + + if(tls+brs!=nsize){ + /* Bad fit; just size proportionally. */ + if(sz<=2){ + tls=nsize/2; + brs=nsize-tls; + }else{ + tls=split_size(tl, node->dir)*nsize/sz; + brs=nsize-tls; + } + } + + if(dir==SPLIT_VERTICAL){ + tlg.h=tls; + brg.y+=tls; + brg.h=brs; + }else{ + tlg.w=tls; + brg.x+=tls; + brg.w=brs; + } + + split_do_resize(tl, &tlg, hprimn, vprimn, transpose); + split_do_resize(br, &brg, hprimn, vprimn, transpose); + + node->dir=dir; + ((WSplit*)node)->geom=*ng; + split_update_bounds((WSplit*)node, FALSE); + } +} + + +void split_do_resize(WSplit *node, const WRectangle *ng, + WPrimn hprimn, WPrimn vprimn, bool transpose) +{ + CALL_DYN(split_do_resize, node, (node, ng, hprimn, vprimn, transpose)); +} + + +void split_resize(WSplit *node, const WRectangle *ng, + WPrimn hprimn, WPrimn vprimn) +{ + split_update_bounds(node, TRUE); + splittree_begin_resize(); + split_do_resize(node, ng, hprimn, vprimn, FALSE); + splittree_end_resize(); +} + + +/*}}}*/ + + +/*{{{ Low-level resize code; request towards root */ + + +static void flexibility(WSplit *node, int dir, int *shrink, int *stretch) +{ + if(dir==SPLIT_VERTICAL){ + *shrink=maxof(0, node->geom.h-node->min_h); + if(OBJ_IS(node, WSplitST)) + *stretch=maxof(0, node->max_h-node->geom.h); + else + *stretch=INT_MAX; + }else{ + *shrink=maxof(0, node->geom.w-node->min_w); + if(OBJ_IS(node, WSplitST)) + *stretch=maxof(0, node->max_w-node->geom.w); + else + *stretch=INT_MAX; + } +} + + +static void calc_amount(int *amount, int rs, WSplit *other, int dir) +{ + int shrink, stretch; + + flexibility(other, dir, &shrink, &stretch); + + if(rs>0) + *amount=minof(rs, shrink); + else if(rs<0) + *amount=-minof(-rs, stretch); + else + *amount=0; +} + + + +static void splitsplit_do_rqsize(WSplitSplit *p, WSplit *node, + RootwardAmount *ha, RootwardAmount *va, + WRectangle *rg, bool tryonly) +{ + WPrimn hprimn=PRIMN_ANY, vprimn=PRIMN_ANY; + WRectangle og, pg, ng; + RootwardAmount *ca; + WSplit *other; + WPrimn thisnode; + int amount; + + assert(!ha->any || ha->tl==0); + assert(!va->any || va->tl==0); + assert(p->tl==node || p->br==node); + + if(p->tl==node){ + other=p->br; + thisnode=PRIMN_TL; + }else{ + other=p->tl; + thisnode=PRIMN_BR; + } + + ca=(p->dir==SPLIT_VERTICAL ? va : ha); + + if(thisnode==PRIMN_TL || ca->any){ + calc_amount(&amount, ca->br, other, p->dir); + ca->br-=amount; + }else/*if(thisnode==PRIMN_BR)*/{ + calc_amount(&amount, ca->tl, other, p->dir); + ca->tl-=amount; + } + + if(((WSplit*)p)->parent==NULL /*|| + (ha->tl==0 && ha->br==0 && va->tl==0 && va->br==0)*/){ + if(((WSplit*)p)->ws_if_root!=NULL) + pg=REGION_GEOM((WTiling*)(((WSplit*)p)->ws_if_root)); + else + pg=((WSplit*)p)->geom; + }else{ + splitinner_do_rqsize(((WSplit*)p)->parent, (WSplit*)p, ha, va, + &pg, tryonly); + } + + assert(pg.w>=0 && pg.h>=0); + + og=pg; + ng=pg; + + if(p->dir==SPLIT_VERTICAL){ + ng.h=maxof(0, node->geom.h+amount); + og.h=maxof(0, other->geom.h-amount); + adjust_sizes(&(ng.h), &(og.h), pg.h, ng.h+og.h, + node->min_h, other->min_h, node->max_h, other->max_h, + PRIMN_TL /* node is passed as tl param */); + if(thisnode==PRIMN_TL) + og.y=pg.y+pg.h-og.h; + else + ng.y=pg.y+pg.h-ng.h; + vprimn=thisnode; + }else{ + ng.w=maxof(0, node->geom.w+amount); + og.w=maxof(0, other->geom.w-amount); + adjust_sizes(&(ng.w), &(og.w), pg.w, ng.w+og.w, + node->min_w, other->min_w, node->max_w, other->max_w, + PRIMN_TL /* node is passed as tl param */); + if(thisnode==PRIMN_TL) + og.x=pg.x+pg.w-og.w; + else + ng.x=pg.x+pg.w-ng.w; + hprimn=thisnode; + } + + if(!tryonly){ + /* Entä jos 'other' on stdisp? */ + split_do_resize(other, &og, hprimn, vprimn, FALSE); + + ((WSplit*)p)->geom=pg; + } + + *rg=ng; +} + + +void splitinner_do_rqsize(WSplitInner *p, WSplit *node, + RootwardAmount *ha, RootwardAmount *va, + WRectangle *rg, bool tryonly) +{ + CALL_DYN(splitinner_do_rqsize, p, (p, node, ha, va, rg, tryonly)); +} + + +static void initra(RootwardAmount *ra, int p, int s, int op, int os, + bool any) +{ + ra->any=any; + ra->tl=op-p; + ra->br=(p+s)-(op+os); + if(any){ + ra->br+=ra->tl; + ra->tl=0; + } +} + + +void split_do_rqgeom_(WSplit *node, const WRectangle *ng, + bool hany, bool vany, WRectangle *rg, + bool tryonly) +{ + RootwardAmount ha, va; + + if(node->parent==NULL){ + if(node->ws_if_root!=NULL) + *rg=REGION_GEOM((WTiling*)(node->ws_if_root)); + else + *rg=*ng; + }else{ + initra(&ha, ng->x, ng->w, node->geom.x, node->geom.w, hany); + initra(&va, ng->y, ng->h, node->geom.y, node->geom.h, vany); + + splitinner_do_rqsize(node->parent, node, &ha, &va, rg, tryonly); + } +} + + +/*}}}*/ + + +/*{{{ Resize interface */ + + +static void bnd(int *pos, int *sz, int opos, int osz, int minsz, int maxsz) +{ + int ud=abs(*pos-opos); + int dd=abs((*pos+*sz)-(opos+osz)); + int szrq=*sz; + + if(ud+dd!=0){ + bound(sz, minsz, maxsz); + *pos+=(szrq-*sz)*ud/(ud+dd); + } +} + + +WSplit *split_find_root(WSplit *split) +{ + if(split->parent==NULL) + return split; + return split_find_root((WSplit*)split->parent); +} + + +void splittree_rqgeom(WSplit *sub, int flags, const WRectangle *geom_, + WRectangle *geomret) +{ + bool hany=flags®ION_RQGEOM_WEAK_X; + bool vany=flags®ION_RQGEOM_WEAK_Y; + bool tryonly=flags®ION_RQGEOM_TRYONLY; + WRectangle geom=*geom_; + WRectangle retg; + WSplit *root=split_find_root(sub); + + if(geomret==NULL) + geomret=&retg; + + split_update_bounds(root, TRUE); + + if(OBJ_IS(sub, WSplitST)){ + WSplitST *sub_as_stdisp=(WSplitST*)sub; + + if(flags®ION_RQGEOM_TRYONLY){ + warn(TR("REGION_RQGEOM_TRYONLY unsupported for status display.")); + *geomret=sub->geom; + return; + } + split_regularise_stdisp(sub_as_stdisp); + geom=sub->geom; + if(sub_as_stdisp->orientation==REGION_ORIENTATION_HORIZONTAL){ + if(geom_->h==geom.h) + return; + geom.h=geom_->h; + }else{ + if(geom_->w==geom.w) + return; + geom.w=geom_->w; + } + split_update_bounds(root, TRUE); + } + + /* Handle internal size bounds */ + bnd(&(geom.x), &(geom.w), sub->geom.x, sub->geom.w, + sub->min_w, sub->max_w); + bnd(&(geom.y), &(geom.h), sub->geom.y, sub->geom.h, + sub->min_h, sub->max_h); + + /* Check if we should resize to both tl and br */ + + if(hany){ + geom.w+=sub->geom.x-geom.x; + geom.x=sub->geom.x; + } + + if(vany){ + geom.h+=sub->geom.y-geom.y; + geom.y=sub->geom.y; + } + + splittree_begin_resize(); + + split_do_rqgeom_(sub, &geom, hany, vany, geomret, tryonly); + + if(!tryonly){ + split_do_resize(sub, geomret, hany, vany, FALSE); + splittree_end_resize(); + *geomret=sub->geom; + }else{ + saw_stdisp=NULL; + } +} + + +/*EXTL_DOC + * Attempt to resize and/or move the split tree starting at \var{node}. + * Behaviour and the \var{g} parameter are as for \fnref{WRegion.rqgeom} + * operating on \var{node} (if it were a \type{WRegion}). + */ +EXTL_EXPORT_MEMBER +ExtlTab split_rqgeom(WSplit *node, ExtlTab g) +{ + WRectangle geom, ogeom; + int flags=REGION_RQGEOM_WEAK_ALL; + + geom=node->geom; + ogeom=geom; + + if(extl_table_gets_i(g, "x", &(geom.x))) + flags&=~REGION_RQGEOM_WEAK_X; + if(extl_table_gets_i(g, "y", &(geom.y))) + flags&=~REGION_RQGEOM_WEAK_Y; + if(extl_table_gets_i(g, "w", &(geom.w))) + flags&=~REGION_RQGEOM_WEAK_W; + if(extl_table_gets_i(g, "h", &(geom.h))) + flags&=~REGION_RQGEOM_WEAK_H; + + geom.w=maxof(1, geom.w); + geom.h=maxof(1, geom.h); + + splittree_rqgeom(node, flags, &geom, &ogeom); + + return extl_table_from_rectangle(&ogeom); + +err: + warn(TR("Invalid node.")); + return extl_table_none(); +} + + +/*}}}*/ + + +/*{{{ Split */ + + +void splittree_changeroot(WSplit *root, WSplit *node) +{ + WTiling *ws=(WTiling*)(root->ws_if_root); + assert(ws!=NULL); + assert(ws->split_tree==root); + root->ws_if_root=NULL; + ws->split_tree=node; + if(node!=NULL){ + node->ws_if_root=ws; + node->parent=NULL; + } +} + + +static void splitsplit_replace(WSplitSplit *split, WSplit *child, + WSplit *what) +{ + assert(split->tl==child || split->br==child); + + if(split->tl==child) + split->tl=what; + else + split->br=what; + + child->parent=NULL; + + what->parent=(WSplitInner*)split; + what->ws_if_root=NULL; /* May not be needed. */ +} + + +void splitinner_replace(WSplitInner *split, WSplit *child, WSplit *what) +{ + CALL_DYN(splitinner_replace, split, (split, child, what)); +} + + +WSplitRegion *splittree_split(WSplit *node, int dir, WPrimn primn, + int minsize, WRegionSimpleCreateFn *fn, + WWindow *parent) +{ + int objmin, objmax; + int s, sn, so, pos; + WSplitSplit *nsplit; + WSplitRegion *nnode; + WSplitInner *psplit; + WRegion *nreg; + WFitParams fp; + WRectangle ng, rg; + + assert(node!=NULL && parent!=NULL); + + if(OBJ_IS(node, WSplitST)){ + warn(TR("Splitting the status display is not allowed.")); + return NULL; + } + + splittree_begin_resize(); + + if(!move_stdisp_out_of_way(node)) + return NULL; + + if(primn!=PRIMN_TL && primn!=PRIMN_BR) + primn=PRIMN_BR; + if(dir!=SPLIT_HORIZONTAL && dir!=SPLIT_VERTICAL) + dir=SPLIT_VERTICAL; + + split_update_bounds(split_find_root(node), TRUE); + objmin=(dir==SPLIT_VERTICAL ? node->min_h : node->min_w); + + s=split_size(node, dir); + sn=maxof(minsize, s/2); + so=maxof(objmin, s-sn); + + if(sn+so!=s){ + int rs; + ng=node->geom; + if(dir==SPLIT_VERTICAL) + ng.h=sn+so; + else + ng.w=sn+so; + split_do_rqgeom_(node, &ng, TRUE, TRUE, &rg, TRUE); + rs=(dir==SPLIT_VERTICAL ? rg.h : rg.w); + if(rsrs/2){ + sn=minsize; + so=rs-sn; + }else{ + so=maxof(rs/2, objmin); + sn=rs-so; + } + }else{ + rg=node->geom; + splittree_scan_stdisp_rootward(node); + } + + /* Create split and new window + */ + fp.mode=REGION_FIT_EXACT; + fp.g=rg; + + nsplit=create_splitsplit(&(fp.g), dir); + + if(nsplit==NULL) + return NULL; + + if(dir==SPLIT_VERTICAL){ + if(primn==PRIMN_BR) + fp.g.y+=so; + fp.g.h=sn; + }else{ + if(primn==PRIMN_BR) + fp.g.x+=so; + fp.g.w=sn; + } + + nreg=fn(parent, &fp); + + if(nreg==NULL){ + destroy_obj((Obj*)nsplit); + return NULL; + } + + nnode=create_splitregion(&(fp.g), nreg); + if(nnode==NULL){ + destroy_obj((Obj*)nreg); + destroy_obj((Obj*)nsplit); + return NULL; + } + + /* Now that everything's ok, resize and move original node. + */ + ng=rg; + if(dir==SPLIT_VERTICAL){ + ng.h=so; + if(primn==PRIMN_TL) + ng.y+=sn; + }else{ + ng.w=so; + if(primn==PRIMN_TL) + ng.x+=sn; + } + + split_do_resize(node, &ng, + (dir==SPLIT_HORIZONTAL ? primn : PRIMN_ANY), + (dir==SPLIT_VERTICAL ? primn : PRIMN_ANY), + FALSE); + + /* Set up split structure + */ + psplit=node->parent; + + if(psplit!=NULL) + splitinner_replace(psplit, node, (WSplit*)nsplit); + else + splittree_changeroot(node, (WSplit*)nsplit); + + node->parent=(WSplitInner*)nsplit; + ((WSplit*)nnode)->parent=(WSplitInner*)nsplit; + + if(primn==PRIMN_BR){ + nsplit->tl=node; + nsplit->br=(WSplit*)nnode; + nsplit->current=SPLIT_CURRENT_TL; + }else{ + nsplit->tl=(WSplit*)nnode; + nsplit->br=node; + nsplit->current=SPLIT_CURRENT_BR; + } + + splittree_end_resize(); + + return nnode; +} + + +/*}}}*/ + + +/*{{{ Remove */ + + +static void splitsplit_remove(WSplitSplit *node, WSplit *child, + bool reclaim_space) +{ + static int nstdisp=0; + WSplitInner *parent; + WSplit *other; + + assert(node->tl==child || node->br==child); + + if(node->tl==child) + other=node->br; + else + other=node->tl; + + assert(other!=NULL); + + if(nstdisp==0 && reclaim_space && OBJ_IS(other, WSplitST)){ + /* Try to move stdisp out of the way. */ + split_try_unsink_stdisp(node, FALSE, TRUE); + assert(child->parent!=NULL); + nstdisp++; + splitinner_remove(child->parent, child, reclaim_space); + nstdisp--; + return; + } + + parent=((WSplit*)node)->parent; + + if(parent!=NULL) + splitinner_replace(parent, (WSplit*)node, other); + else + splittree_changeroot((WSplit*)node, other); + + if(reclaim_space) + split_resize(other, &(((WSplit*)node)->geom), PRIMN_ANY, PRIMN_ANY); + + child->parent=NULL; + + node->tl=NULL; + node->br=NULL; + ((WSplit*)node)->parent=NULL; + destroy_obj((Obj*)node); +} + + +void splitinner_remove(WSplitInner *node, WSplit *child, bool reclaim_space) +{ + CALL_DYN(splitinner_remove, node, (node, child, reclaim_space)); +} + + +void splittree_remove(WSplit *node, bool reclaim_space) +{ + if(node->parent!=NULL) + splitinner_remove(node->parent, node, reclaim_space); + else if(node->ws_if_root!=NULL) + splittree_changeroot(node, NULL); + + destroy_obj((Obj*)node); +} + + +/*}}}*/ + + +/*{{{ Tree traversal */ + + +static bool defaultfilter(WSplit *node) +{ + return (OBJ_IS(node, WSplitRegion) && + ((WSplitRegion*)node)->reg!=NULL); +} + + +static WSplit *split_current_todir_default(WSplit *node, + WPrimn hprimn, WPrimn vprimn, + WSplitFilter *filter) +{ + if(filter==NULL) + filter=defaultfilter; + + return (filter(node) ? node : NULL); +} + + +static WSplit *splitsplit_current_todir(WSplitSplit *node, + WPrimn hprimn, WPrimn vprimn, + WSplitFilter *filter) +{ + WPrimn primn=(node->dir==SPLIT_HORIZONTAL ? hprimn : vprimn); + WSplit *first, *second, *ret; + + if(primn==PRIMN_TL || + (primn==PRIMN_ANY && node->current==SPLIT_CURRENT_TL)){ + first=node->tl; + second=node->br; + }else if(primn==PRIMN_BR || + (primn==PRIMN_ANY && node->current==SPLIT_CURRENT_BR)){ + first=node->br; + second=node->tl; + }else{ + return NULL; + } + + ret=split_current_todir(first, hprimn, vprimn, filter); + if(ret==NULL) + ret=split_current_todir(second, hprimn, vprimn, filter); + if(ret==NULL && filter!=NULL){ + if(filter((WSplit*)node)) + ret=(WSplit*)node; + } + + return ret; +} + + +WSplit *split_current_todir(WSplit *node, WPrimn hprimn, WPrimn vprimn, + WSplitFilter *filter) +{ + WSplit *ret=NULL; + CALL_DYN_RET(ret, WSplit*, split_current_todir, node, + (node, hprimn, vprimn, filter)); + return ret; +} + + +WSplit *splitsplit_nextto(WSplitSplit *node, WSplit *child, + WPrimn hprimn, WPrimn vprimn, + WSplitFilter *filter) +{ + WPrimn primn=(node->dir==SPLIT_HORIZONTAL ? hprimn : vprimn); + WSplit *split=NULL, *nnode=NULL; + + if(node->tl==child && (primn==PRIMN_BR || primn==PRIMN_ANY)){ + split=node->br; + primn=PRIMN_TL; + }else if(node->br==child && (primn==PRIMN_TL || primn==PRIMN_ANY)){ + split=node->tl; + primn=PRIMN_BR; + } + + if(split!=NULL){ + if(node->dir==SPLIT_HORIZONTAL){ + hprimn=primn; + vprimn=primn_none2any(vprimn); + }else{ + vprimn=primn; + hprimn=primn_none2any(vprimn); + } + + nnode=split_current_todir(split, hprimn, vprimn, filter); + } + + return nnode; +} + + +WSplit *splitinner_nextto(WSplitInner *node, WSplit *child, + WPrimn hprimn, WPrimn vprimn, + WSplitFilter *filter) +{ + WSplit *ret=NULL; + CALL_DYN_RET(ret, WSplit*, splitinner_nextto, node, + (node, child, hprimn, vprimn, filter)); + return ret; +} + + +WSplit *split_nextto(WSplit *node, WPrimn hprimn, WPrimn vprimn, + WSplitFilter *filter) +{ + while(node->parent!=NULL){ + WSplit *ret=splitinner_nextto(node->parent, node, + hprimn, vprimn, filter); + if(ret!=NULL) + return ret; + node=(WSplit*)node->parent; + } + return NULL; +} + + +void splitinner_mark_current_default(WSplitInner *split, WSplit *child) +{ + if(((WSplit*)split)->parent!=NULL) + splitinner_mark_current(((WSplit*)split)->parent, (WSplit*)split); +} + + +void splitsplit_mark_current(WSplitSplit *split, WSplit *child) +{ + assert(child==split->tl || child==split->br); + + split->current=(split->tl==child ? SPLIT_CURRENT_TL : SPLIT_CURRENT_BR); + + splitinner_mark_current_default(&(split->isplit), child); +} + + +void splitinner_mark_current(WSplitInner *split, WSplit *child) +{ + CALL_DYN(splitinner_mark_current, split, (split, child)); +} + + +static void splitsplit_forall(WSplitSplit *node, WSplitFn *fn) +{ + fn(node->tl); + fn(node->br); +} + + +void splitinner_forall(WSplitInner *node, WSplitFn *fn) +{ + CALL_DYN(splitinner_forall, node, (node, fn)); +} + + +static WSplit *splitsplit_current(WSplitSplit *split) +{ + return (split->current==SPLIT_CURRENT_TL ? split->tl : split->br); +} + + +/*EXTL_DOC + * Returns the most previously active child node of \var{split}. + */ +EXTL_SAFE +EXTL_EXPORT_MEMBER +WSplit *splitinner_current(WSplitInner *node) +{ + WSplit *ret=NULL; + CALL_DYN_RET(ret, WSplit*, splitinner_current, node, (node)); + return ret; +} + + +/*}}}*/ + + +/*{{{ X window handling */ + + +static void splitregion_stacking(WSplitRegion *split, + Window *bottomret, Window *topret) +{ + *bottomret=None; + *topret=None; + if(split->reg!=NULL) + region_stacking(split->reg, bottomret, topret); +} + + +void splitsplit_stacking(WSplitSplit *split, + Window *bottomret, Window *topret) +{ + Window tlb=None, tlt=None; + Window brb=None, brt=None; + + split_stacking(split->tl, &tlb, &tlt); + split_stacking(split->br, &brb, &brt); + + /* To make sure that this condition holds is left to the workspace + * code to do after a split tree has been loaded or modified. + */ + if(split->current==SPLIT_CURRENT_TL){ + *topret=(tlt!=None ? tlt : brt); + *bottomret=(brb!=None ? brb : tlb); + }else{ + *topret=(brt!=None ? brt : tlt); + *bottomret=(tlb!=None ? tlb : brb); + } +} + +void split_stacking(WSplit *split, Window *bottomret, Window *topret) +{ + *bottomret=None; + *topret=None; + { + CALL_DYN(split_stacking, split, (split, bottomret, topret)); + } +} + + +static void splitregion_restack(WSplitRegion *split, Window other, int mode) +{ + if(split->reg!=NULL) + region_restack(split->reg, other, mode); +} + +void splitsplit_restack(WSplitSplit *split, Window other, int mode) +{ + Window bottom=None, top=None; + WSplit *first, *second; + + if(split->current==SPLIT_CURRENT_TL){ + first=split->br; + second=split->tl; + }else{ + first=split->tl; + second=split->br; + } + + split_restack(first, other, mode); + split_stacking(first, &bottom, &top); + if(top!=None){ + other=top; + mode=Above; + } + split_restack(second, other, mode); +} + +void split_restack(WSplit *split, Window other, int mode) +{ + CALL_DYN(split_restack, split, (split, other, mode)); +} + + +static void splitregion_map(WSplitRegion *split) +{ + if(split->reg!=NULL) + region_map(split->reg); +} + +static void splitinner_map(WSplitInner *split) +{ + splitinner_forall(split, split_map); +} + +void split_map(WSplit *split) +{ + CALL_DYN(split_map, split, (split)); +} + + +static void splitregion_unmap(WSplitRegion *split) +{ + if(split->reg!=NULL) + region_unmap(split->reg); +} + +static void splitinner_unmap(WSplitInner *split) +{ + splitinner_forall(split, split_unmap); +} + +void split_unmap(WSplit *split) +{ + CALL_DYN(split_unmap, split, (split)); +} + + +static void splitregion_reparent(WSplitRegion *split, WWindow *wwin) +{ + if(split->reg!=NULL){ + WRectangle g=split->split.geom; + region_reparent(split->reg, wwin, &g, REGION_FIT_EXACT); + } +} + + +static void splitsplit_reparent(WSplitSplit *split, WWindow *wwin) +{ + if(split->current==SPLIT_CURRENT_TL){ + split_reparent(split->br, wwin); + split_reparent(split->tl, wwin); + }else{ + split_reparent(split->tl, wwin); + split_reparent(split->br, wwin); + } +} + + +void split_reparent(WSplit *split, WWindow *wwin) +{ + CALL_DYN(split_reparent, split, (split, wwin)); +} + + +/*}}}*/ + + +/*{{{ Transpose, flip, rotate */ + + +void splitsplit_flip_default(WSplitSplit *split) +{ + WRectangle tlng, brng; + WRectangle *sg=&((WSplit*)split)->geom; + WSplit *tmp; + + assert(split->tl!=NULL && split->br!=NULL); + + split_update_bounds((WSplit*)split, TRUE); + + tlng=split->tl->geom; + brng=split->br->geom; + + if(split->dir==SPLIT_HORIZONTAL){ + brng.x=sg->x; + tlng.x=sg->x+sg->w-tlng.w; + }else{ + brng.y=sg->y; + tlng.y=sg->y+sg->h-tlng.h; + } + + tmp=split->tl; + split->tl=split->br; + split->br=tmp; + split->current=(split->current==SPLIT_CURRENT_TL + ? SPLIT_CURRENT_BR + : SPLIT_CURRENT_TL); + + split_do_resize(split->tl, &brng, PRIMN_ANY, PRIMN_ANY, FALSE); + split_do_resize(split->br, &tlng, PRIMN_ANY, PRIMN_ANY, FALSE); +} + + +static void splitsplit_flip_(WSplitSplit *split) +{ + CALL_DYN(splitsplit_flip, split, (split)); +} + + +/*EXTL_DOC + * Flip contents of \var{node}. + */ +EXTL_EXPORT_MEMBER +void splitsplit_flip(WSplitSplit *split) +{ + splittree_begin_resize(); + + if(!move_stdisp_out_of_way((WSplit*)split)) + return; + + splitsplit_flip_(split); + + splittree_end_resize(); +} + +typedef enum{ + FLIP_VERTICAL, + FLIP_HORIZONTAL, + FLIP_NONE, + FLIP_ANY +} FlipDir; + + +static FlipDir flipdir=FLIP_VERTICAL; + + +static void do_flip(WSplit *split) +{ + WSplitSplit *ss=OBJ_CAST(split, WSplitSplit); + + if(ss!=NULL){ + if((flipdir==FLIP_ANY + || (ss->dir==SPLIT_VERTICAL && flipdir==FLIP_VERTICAL) + || (ss->dir==SPLIT_HORIZONTAL && flipdir==FLIP_HORIZONTAL)) + && !OBJ_IS(ss->tl, WSplitST) + && !OBJ_IS(ss->br, WSplitST)){ + splitsplit_flip_(ss); + } + } + + if(OBJ_IS(ss, WSplitInner)) + splitinner_forall((WSplitInner*)ss, do_flip); +} + + +static void splittree_flip_dir(WSplit *splittree, FlipDir dir) +{ + /* todo stdisp outta way */ + if(OBJ_IS(splittree, WSplitInner)){ + flipdir=dir; + splitinner_forall((WSplitInner*)splittree, do_flip); + } +} + + +static bool split_fliptrans_to(WSplit *node, const WRectangle *geom, + bool trans, FlipDir flip) +{ + WRectangle rg; + WSplit *node2; + + splittree_begin_resize(); + + /* split_do_resize can do things right if 'node' has stdisp as child, + * but otherwise transpose will put the stdisp in a bad split + * configuration if it is contained within 'node', so we must + * first move it and its fixed parent split below node. For correct + * geometry calculation we move it immediately below node, and + * resize stdisp's fixed parent node instead. + */ + node2=move_stdisp_out_of_way(node); + + if(node2==NULL) + return FALSE; + + split_update_bounds(node2, TRUE); + + split_do_rqgeom_(node2, geom, PRIMN_ANY, PRIMN_ANY, &rg, FALSE); + + split_do_resize(node2, &rg, PRIMN_ANY, PRIMN_ANY, trans); + + if(flip!=FLIP_NONE) + splittree_flip_dir(node2, flip); + + splittree_end_resize(); + + return TRUE; +} + + +bool split_transpose_to(WSplit *node, const WRectangle *geom) +{ + return split_fliptrans_to(node, geom, TRUE, FLIP_ANY); +} + + +/*EXTL_DOC + * Transpose contents of \var{node}. + */ +EXTL_EXPORT_MEMBER +void split_transpose(WSplit *node) +{ + WRectangle g=node->geom; + + split_transpose_to(node, &g); +} + + +bool split_rotate_to(WSplit *node, const WRectangle *geom, int rotation) +{ + FlipDir flip=FLIP_NONE; + bool trans=FALSE; + + if(rotation==SCREEN_ROTATION_90){ + flip=FLIP_HORIZONTAL; + trans=TRUE; + }else if(rotation==SCREEN_ROTATION_180){ + flip=FLIP_ANY; + }else if(rotation==SCREEN_ROTATION_270){ + flip=FLIP_VERTICAL; + trans=TRUE; + } + + return split_fliptrans_to(node, geom, trans, flip); +} + +/*}}}*/ + + +/*{{{ Exports */ + + +/*EXTL_DOC + * Return parent split for \var{split}. + */ +EXTL_SAFE +EXTL_EXPORT_MEMBER +WSplitInner *split_parent(WSplit *split) +{ + return split->parent; +} + + +/*EXTL_DOC + * Returns the area of workspace used by the regions under \var{split}. + */ +EXTL_SAFE +EXTL_EXPORT_MEMBER +ExtlTab split_geom(WSplit *split) +{ + return extl_table_from_rectangle(&(split->geom)); +} + + +/*EXTL_DOC + * Returns the top or left child node of \var{split} depending + * on the direction of the split. + */ +EXTL_SAFE +EXTL_EXPORT_MEMBER +WSplit *splitsplit_tl(WSplitSplit *split) +{ + return split->tl; +} + + +/*EXTL_DOC + * Returns the bottom or right child node of \var{split} depending + * on the direction of the split. + */ +EXTL_SAFE +EXTL_EXPORT_MEMBER +WSplit *splitsplit_br(WSplitSplit *split) +{ + return split->br; +} + +/*EXTL_DOC + * Returns the direction of \var{split}; either ''vertical'' or + * ''horizontal''. + */ +EXTL_SAFE +EXTL_EXPORT_MEMBER +const char *splitsplit_dir(WSplitSplit *split) +{ + return (split->dir==SPLIT_VERTICAL ? "vertical" : "horizontal"); +} + + +/*EXTL_DOC + * Returns the region contained in \var{node}. + */ +EXTL_SAFE +EXTL_EXPORT_MEMBER +WRegion *splitregion_reg(WSplitRegion *node) +{ + return node->reg; +} + + +/*}}}*/ + + +/*{{{ Save support */ + + +ExtlTab split_base_config(WSplit *node) +{ + ExtlTab t=extl_create_table(); + extl_table_sets_s(t, "type", OBJ_TYPESTR(node)); + return t; +} + + +static bool splitregion_get_config(WSplitRegion *node, ExtlTab *ret) +{ + ExtlTab rt, t; + + if(node->reg==NULL) + return FALSE; + + if(!region_supports_save(node->reg)){ + warn(TR("Unable to get configuration for %s."), + region_name(node->reg)); + return FALSE; + } + + rt=region_get_configuration(node->reg); + t=split_base_config(&(node->split)); + extl_table_sets_t(t, "regparams", rt); + extl_unref_table(rt); + *ret=t; + + return TRUE; +} + + +static bool splitst_get_config(WSplitST *node, ExtlTab *ret) +{ + *ret=split_base_config((WSplit*)node); + return TRUE; +} + + +static bool splitsplit_get_config(WSplitSplit *node, ExtlTab *ret) +{ + ExtlTab tab, tltab, brtab; + int tls, brs; + + if(!split_get_config(node->tl, &tltab)) + return split_get_config(node->br, ret); + + if(!split_get_config(node->br, &brtab)){ + *ret=tltab; + return TRUE; + } + + tab=split_base_config((WSplit*)node); + + tls=split_size(node->tl, node->dir); + brs=split_size(node->br, node->dir); + + extl_table_sets_s(tab, "dir", (node->dir==SPLIT_VERTICAL + ? "vertical" : "horizontal")); + + extl_table_sets_i(tab, "tls", tls); + extl_table_sets_t(tab, "tl", tltab); + extl_unref_table(tltab); + + extl_table_sets_i(tab, "brs", brs); + extl_table_sets_t(tab, "br", brtab); + extl_unref_table(brtab); + + *ret=tab; + + return TRUE; +} + + +bool split_get_config(WSplit *node, ExtlTab *tabret) +{ + bool ret=FALSE; + CALL_DYN_RET(ret, bool, split_get_config, node, (node, tabret)); + return ret; +} + + +/*}}}*/ + + +/*{{{ The classes */ + + +static DynFunTab split_dynfuntab[]={ + {split_do_resize, split_do_resize_default}, + {(DynFun*)split_current_todir, (DynFun*)split_current_todir_default}, + END_DYNFUNTAB, +}; + +static DynFunTab splitinner_dynfuntab[]={ + {splitinner_mark_current, splitinner_mark_current_default}, + {split_map, splitinner_map}, + {split_unmap, splitinner_unmap}, + END_DYNFUNTAB, +}; + +static DynFunTab splitsplit_dynfuntab[]={ + {split_update_bounds, splitsplit_update_bounds}, + {split_do_resize, splitsplit_do_resize}, + {splitinner_do_rqsize, splitsplit_do_rqsize}, + {splitinner_replace, splitsplit_replace}, + {splitinner_remove, splitsplit_remove}, + {(DynFun*)split_current_todir, (DynFun*)splitsplit_current_todir}, + {(DynFun*)splitinner_current, (DynFun*)splitsplit_current}, + {(DynFun*)splitinner_nextto, (DynFun*)splitsplit_nextto}, + {splitinner_mark_current, splitsplit_mark_current}, + {(DynFun*)split_get_config, (DynFun*)splitsplit_get_config}, + {splitinner_forall, splitsplit_forall}, + {split_restack, splitsplit_restack}, + {split_stacking, splitsplit_stacking}, + {split_reparent, splitsplit_reparent}, + {splitsplit_flip, splitsplit_flip_default}, + END_DYNFUNTAB, +}; + +static DynFunTab splitregion_dynfuntab[]={ + {split_update_bounds, splitregion_update_bounds}, + {split_do_resize, splitregion_do_resize}, + {(DynFun*)split_get_config, (DynFun*)splitregion_get_config}, + {split_map, splitregion_map}, + {split_unmap, splitregion_unmap}, + {split_restack, splitregion_restack}, + {split_stacking, splitregion_stacking}, + {split_reparent, splitregion_reparent}, + END_DYNFUNTAB, +}; + +static DynFunTab splitst_dynfuntab[]={ + {split_update_bounds, splitst_update_bounds}, + {split_do_resize, splitst_do_resize}, + {(DynFun*)split_get_config, (DynFun*)splitst_get_config}, + END_DYNFUNTAB, +}; + + +EXTL_EXPORT +IMPLCLASS(WSplit, Obj, split_deinit, split_dynfuntab); + +EXTL_EXPORT +IMPLCLASS(WSplitInner, WSplit, splitinner_deinit, splitinner_dynfuntab); + +EXTL_EXPORT +IMPLCLASS(WSplitSplit, WSplitInner, splitsplit_deinit, splitsplit_dynfuntab); + +EXTL_EXPORT +IMPLCLASS(WSplitRegion, WSplit, splitregion_deinit, splitregion_dynfuntab); + +EXTL_EXPORT +IMPLCLASS(WSplitST, WSplitRegion, splitst_deinit, splitst_dynfuntab); + + +/*}}}*/ + diff --git a/mod_tiling/split.h b/mod_tiling/split.h new file mode 100644 index 0000000..fa74122 --- /dev/null +++ b/mod_tiling/split.h @@ -0,0 +1,221 @@ +/* + * ion/mod_tiling/split.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_MOD_TILING_SPLIT_H +#define ION_MOD_TILING_SPLIT_H + +#include +#include +#include +#include +#include +#include + + +INTRCLASS(WSplit); +INTRCLASS(WSplitInner); +INTRCLASS(WSplitSplit); +INTRCLASS(WSplitRegion); +INTRCLASS(WSplitST); + + +typedef enum{ + SPLIT_HORIZONTAL, + SPLIT_VERTICAL +} WSplitDir; + + +typedef enum{ + PRIMN_ANY, + PRIMN_TL, + PRIMN_BR, + PRIMN_NONE +} WPrimn; + + +typedef enum { + SPLIT_CURRENT_TL, + SPLIT_CURRENT_BR +} WSplitCurrent; + + +DECLCLASS(WSplit){ + Obj obj; + WRectangle geom; + WSplitInner *parent; + void *ws_if_root; + + int min_w, min_h; + int max_w, max_h; + int unused_w, unused_h; +}; + + +DECLCLASS(WSplitInner){ + WSplit split; +}; + + +DECLCLASS(WSplitSplit){ + WSplitInner isplit; + int dir; + WSplit *tl, *br; + int current; +}; + + +DECLCLASS(WSplitRegion){ + WSplit split; + WRegion *reg; +}; + + +DECLCLASS(WSplitST){ + WSplitRegion regnode; + int orientation; + int corner; + bool fullsize; +}; + + +typedef struct{ + int tl, br; + bool any; +} RootwardAmount; + + +typedef bool WSplitFilter(WSplit *split); +typedef void WSplitFn(WSplit *split); + + +/* Misc. */ + +extern int split_size(WSplit *split, int dir); +extern int split_pos(WSplit *split, int dir); +extern int split_other_size(WSplit *split, int dir); +extern int split_other_pos(WSplit *split, int dir); + +extern WSplitRegion *splittree_node_of(WRegion *reg); +extern bool splittree_set_node_of(WRegion *reg, WSplitRegion *split); + +extern WPrimn primn_invert(WPrimn primn); +extern WPrimn primn_none2any(WPrimn primn); + +/* Init/deinit */ + +extern bool split_init(WSplit *split, const WRectangle *geom); +extern bool splitinner_init(WSplitInner *split, const WRectangle *geom); +extern bool splitsplit_init(WSplitSplit *split, const WRectangle *geom, + int dir); +extern bool splitregion_init(WSplitRegion *split,const WRectangle *geom, + WRegion *reg); +extern bool splitst_init(WSplitST *split, const WRectangle *geom, + WRegion *reg); + + +extern WSplitSplit *create_splitsplit(const WRectangle *geom, int dir); +extern WSplitRegion *create_splitregion(const WRectangle *geom, WRegion *reg); +extern WSplitST *create_splitst(const WRectangle *geom, WRegion *reg); + + +extern void split_deinit(WSplit *split); +extern void splitsplit_deinit(WSplitSplit *split); +extern void splitinner_deinit(WSplitInner *split); +extern void splitregion_deinit(WSplitRegion *split); +extern void splitst_deinit(WSplitST *split); + +/* Geometry */ + +DYNFUN void split_update_bounds(WSplit *node, bool recursive); +extern void splitsplit_update_geom_from_children(WSplitSplit *node); +DYNFUN void split_do_resize(WSplit *node, const WRectangle *ng, + WPrimn hprimn, WPrimn vprimn, bool transpose); +extern void splitsplit_do_resize(WSplitSplit *node, const WRectangle *ng, + WPrimn hprimn, WPrimn vprimn, bool transpose); +extern void split_resize(WSplit *node, const WRectangle *ng, + WPrimn hprimn, WPrimn vprimn); +DYNFUN void splitinner_do_rqsize(WSplitInner *p, WSplit *node, + RootwardAmount *ha, RootwardAmount *va, + WRectangle *rg, bool tryonly); +extern ExtlTab split_rqgeom(WSplit *node, ExtlTab g); + +/* Split */ + +extern void splittree_rqgeom(WSplit *node, int flags, + const WRectangle *geom, WRectangle *geomret); + + +DYNFUN void splitinner_replace(WSplitInner *node, WSplit *child, WSplit *what); +extern WSplitRegion *splittree_split(WSplit *node, int dir, + WPrimn primn, int minsize, + WRegionSimpleCreateFn *fn, + WWindow *parent); + +extern void splittree_changeroot(WSplit *root, WSplit *node); + +/* Remove */ + +DYNFUN void splitinner_remove(WSplitInner *node, WSplit *child, + bool reclaim_space); +extern void splittree_remove(WSplit *node, bool reclaim_space); + +/* Tree traversal */ + +extern WSplit *split_find_root(WSplit *split); +DYNFUN WSplit *split_current_todir(WSplit *node, + WPrimn hprimn, WPrimn vprimn, + WSplitFilter *filter); +DYNFUN WSplit *splitinner_nextto(WSplitInner *node, WSplit *child, + WPrimn hprimn, WPrimn vprimn, + WSplitFilter *filter); +DYNFUN WSplit *splitinner_current(WSplitInner *node); +DYNFUN void splitinner_mark_current(WSplitInner *split, WSplit *child); +DYNFUN void splitinner_forall(WSplitInner *node, WSplitFn *fn); +extern WSplit *split_nextto(WSplit *node, + WPrimn hprimn, WPrimn vprimn, + WSplitFilter *filter); + +/* X window handling */ + +void split_restack(WSplit *split, Window win, int mode); +void split_stacking(WSplit *split, Window *bottom_ret, Window *top_ret); +void split_map(WSplit *split); +void split_unmap(WSplit *split); +void split_reparent(WSplit *split, WWindow *wwin); + +/* Transpose, flip, rotate */ + +extern void split_transpose(WSplit *split); +extern bool split_transpose_to(WSplit *split, const WRectangle *geom); + +extern void splitsplit_flip_default(WSplitSplit *split); +DYNFUN void splitsplit_flip(WSplitSplit *split); + +extern bool split_rotate_to(WSplit *node, const WRectangle *geom, + int rotation); + +/* Save support */ + +extern bool split_get_config(WSplit *node, ExtlTab *ret); +extern ExtlTab split_base_config(WSplit *node); + +/* Internal. */ + +extern void splittree_begin_resize(); +extern void splittree_end_resize(); +extern void splittree_scan_stdisp_rootward(WSplit *node); + +extern void split_do_rqgeom_(WSplit *node, const WRectangle *ng, + bool hany, bool vany, WRectangle *rg, + bool tryonly); + + +#endif /* ION_MOD_TILING_SPLIT_H */ diff --git a/mod_tiling/splitfloat.c b/mod_tiling/splitfloat.c new file mode 100644 index 0000000..6ab6b31 --- /dev/null +++ b/mod_tiling/splitfloat.c @@ -0,0 +1,1009 @@ +/* + * ion/panews/splitext.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tiling.h" +#include "split.h" +#include "splitfloat.h" +#include "panehandle.h" + + +#define GEOM(X) (((WSplit*)(X))->geom) + + +/*{{{ Init/deinit */ + + +static void splitfloat_set_borderlines(WSplitFloat *split) +{ + int dir=split->ssplit.dir; + + split->tlpwin->bline=(dir==SPLIT_HORIZONTAL + ? GR_BORDERLINE_RIGHT + : GR_BORDERLINE_BOTTOM); + + split->brpwin->bline=(dir==SPLIT_HORIZONTAL + ? GR_BORDERLINE_LEFT + : GR_BORDERLINE_TOP); +} + + +bool splitfloat_init(WSplitFloat *split, const WRectangle *geom, + WTiling *ws, int dir) +{ + WFitParams fp; + WWindow *par=REGION_PARENT(ws); + + assert(par!=NULL); + + fp.g=*geom; + fp.mode=REGION_FIT_EXACT; + split->tlpwin=create_panehandle(par, &fp); + if(split->tlpwin==NULL) + return FALSE; + + fp.g=*geom; + fp.mode=REGION_FIT_EXACT; + split->brpwin=create_panehandle(par, &fp); + if(split->brpwin==NULL){ + destroy_obj((Obj*)split->tlpwin); + return FALSE; + } + + if(!splitsplit_init(&(split->ssplit), geom, dir)){ + destroy_obj((Obj*)split->brpwin); + destroy_obj((Obj*)split->tlpwin); + return FALSE; + } + + split->tlpwin->splitfloat=split; + split->brpwin->splitfloat=split; + + splitfloat_set_borderlines(split); + + if(REGION_IS_MAPPED(ws)){ + region_map((WRegion*)(split->tlpwin)); + region_map((WRegion*)(split->brpwin)); + } + + return TRUE; +} + + +WSplitFloat *create_splitfloat(const WRectangle *geom, WTiling *ws, int dir) +{ + CREATEOBJ_IMPL(WSplitFloat, splitfloat, (p, geom, ws, dir)); +} + + +void splitfloat_deinit(WSplitFloat *split) +{ + if(split->tlpwin!=NULL){ + WPaneHandle *tmp=split->tlpwin; + split->tlpwin=NULL; + tmp->splitfloat=NULL; + destroy_obj((Obj*)tmp); + } + + if(split->brpwin!=NULL){ + WPaneHandle *tmp=split->brpwin; + split->brpwin=NULL; + tmp->splitfloat=NULL; + destroy_obj((Obj*)tmp); + } + + splitsplit_deinit(&(split->ssplit)); +} + + +/*}}}*/ + + +/*{{{ X window handling */ + + +static void stack_stacking_reg(WRegion *reg, + Window *bottomret, Window *topret) +{ + Window b=None, t=None; + + if(reg!=NULL){ + region_stacking(reg, &b, &t); + if(*bottomret==None) + *bottomret=b; + if(t!=None) + *topret=t; + } +} + + +static void stack_stacking_split(WSplit *split, + Window *bottomret, Window *topret) +{ + Window b=None, t=None; + + if(split!=NULL){ + split_stacking(split, &b, &t); + if(*bottomret==None) + *bottomret=b; + if(t!=None) + *topret=t; + } +} + + +static void splitfloat_stacking(WSplitFloat *split, + Window *bottomret, Window *topret) +{ + *bottomret=None; + *topret=None; + + if(split->ssplit.current!=SPLIT_CURRENT_TL){ + stack_stacking_reg((WRegion*)split->tlpwin, bottomret, topret); + stack_stacking_split(split->ssplit.tl, bottomret, topret); + stack_stacking_reg((WRegion*)split->brpwin, bottomret, topret); + stack_stacking_split(split->ssplit.br, bottomret, topret); + }else{ + stack_stacking_reg((WRegion*)split->brpwin, bottomret, topret); + stack_stacking_split(split->ssplit.br, bottomret, topret); + stack_stacking_reg((WRegion*)split->tlpwin, bottomret, topret); + stack_stacking_split(split->ssplit.tl, bottomret, topret); + } +} + + +static void stack_restack_reg(WRegion *reg, Window *other, int *mode) +{ + Window b=None, t=None; + + if(reg!=NULL){ + region_restack(reg, *other, *mode); + region_stacking(reg, &b, &t); + if(t!=None){ + *other=t; + *mode=Above; + } + } +} + + +static void stack_restack_split(WSplit *split, Window *other, int *mode) +{ + Window b=None, t=None; + + if(split!=NULL){ + split_restack(split, *other, *mode); + split_stacking(split, &b, &t); + if(t!=None){ + *other=t; + *mode=Above; + } + } +} + + + +static void splitfloat_restack(WSplitFloat *split, Window other, int mode) +{ + if(split->ssplit.current!=SPLIT_CURRENT_TL){ + stack_restack_reg((WRegion*)split->tlpwin, &other, &mode); + stack_restack_split(split->ssplit.tl, &other, &mode); + stack_restack_reg((WRegion*)split->brpwin, &other, &mode); + stack_restack_split(split->ssplit.br, &other, &mode); + }else{ + stack_restack_reg((WRegion*)split->brpwin, &other, &mode); + stack_restack_split(split->ssplit.br, &other, &mode); + stack_restack_reg((WRegion*)split->tlpwin, &other, &mode); + stack_restack_split(split->ssplit.tl, &other, &mode); + } +} + + +static void splitfloat_map(WSplitFloat *split) +{ + region_map((WRegion*)(split->tlpwin)); + region_map((WRegion*)(split->brpwin)); + splitinner_forall((WSplitInner*)split, split_map); +} + + +static void splitfloat_unmap(WSplitFloat *split) +{ + region_unmap((WRegion*)(split->tlpwin)); + region_unmap((WRegion*)(split->brpwin)); + splitinner_forall((WSplitInner*)split, split_unmap); +} + + +static void reparentreg(WRegion *reg, WWindow *target) +{ + WRectangle g=REGION_GEOM(reg); + region_reparent(reg, target, &g, REGION_FIT_EXACT); +} + + +static void splitfloat_reparent(WSplitFloat *split, WWindow *target) +{ + if(split->ssplit.current!=SPLIT_CURRENT_TL){ + reparentreg((WRegion*)split->tlpwin, target); + split_reparent(split->ssplit.tl, target); + reparentreg((WRegion*)split->brpwin, target); + split_reparent(split->ssplit.br, target); + }else{ + reparentreg((WRegion*)split->brpwin, target); + split_reparent(split->ssplit.br, target); + reparentreg((WRegion*)split->tlpwin, target); + split_reparent(split->ssplit.tl, target); + } +} + + +/*}}}*/ + + +/*{{{ Geometry */ + + +#define TL_BORDER(SF) ((SF)->ssplit.dir==SPLIT_VERTICAL \ + ? (SF)->tlpwin->bdw.bottom \ + : (SF)->tlpwin->bdw.right) + +#define BR_BORDER(SF) ((SF)->ssplit.dir==SPLIT_VERTICAL \ + ? (SF)->brpwin->bdw.top \ + : (SF)->brpwin->bdw.left) + + +void splitfloat_tl_pwin_to_cnt(WSplitFloat *split, WRectangle *g) +{ + if(split->ssplit.dir==SPLIT_HORIZONTAL) + g->w=maxof(1, g->w-split->tlpwin->bdw.right); + else + g->h=maxof(1, g->h-split->tlpwin->bdw.bottom); +} + + +void splitfloat_br_pwin_to_cnt(WSplitFloat *split, WRectangle *g) +{ + if(split->ssplit.dir==SPLIT_HORIZONTAL){ + int delta=split->tlpwin->bdw.left; + g->w=maxof(1, g->w-delta); + g->x+=delta; + }else{ + int delta=split->tlpwin->bdw.top; + g->h=maxof(1, g->h-delta); + g->y+=delta; + } +} + + +void splitfloat_tl_cnt_to_pwin(WSplitFloat *split, WRectangle *g) +{ + if(split->ssplit.dir==SPLIT_HORIZONTAL) + g->w=maxof(1, g->w+split->tlpwin->bdw.right); + else + g->h=maxof(1, g->h+split->tlpwin->bdw.bottom); +} + + +void splitfloat_br_cnt_to_pwin(WSplitFloat *split, WRectangle *g) +{ + if(split->ssplit.dir==SPLIT_HORIZONTAL){ + int delta=split->tlpwin->bdw.left; + g->w=maxof(1, g->w+delta); + g->x-=delta; + }else{ + int delta=split->tlpwin->bdw.top; + g->h=maxof(1, g->h+delta); + g->y-=delta; + } +} + + +static int infadd(int x, int y) +{ + return ((x==INT_MAX || y==INT_MAX) ? INT_MAX : (x+y)); +} + + +static int splitfloat_get_handle(WSplitFloat *split, int dir, + WSplit *other) +{ + assert(other==split->ssplit.tl || other==split->ssplit.br); + + if(dir!=split->ssplit.dir) + return 0; + + if(dir==SPLIT_VERTICAL){ + if(other==split->ssplit.tl) + return split->tlpwin->bdw.right; + else if(other==split->ssplit.br) + return split->tlpwin->bdw.left; + }else{ + if(other==split->ssplit.tl) + return split->tlpwin->bdw.bottom; + else if(other==split->ssplit.br) + return split->tlpwin->bdw.top; + } + + return 0; +} + + +static int splitfloat_get_max(WSplitFloat *split, int dir, WSplit *other) +{ + return infadd((dir==SPLIT_VERTICAL ? other->max_h : other->max_w), + splitfloat_get_handle(split, dir, other)); +} + + +static int splitfloat_get_min(WSplitFloat *split, int dir, WSplit *other) +{ + return ((dir==SPLIT_VERTICAL ? other->min_h : other->min_w) + +splitfloat_get_handle(split, dir, other)); +} + + +static void splitfloat_update_bounds(WSplitFloat *split, bool recursive) +{ + WSplit *tl=split->ssplit.tl, *br=split->ssplit.br; + WSplit *node=(WSplit*)split; + int tl_max_w, br_max_w, tl_max_h, br_max_h; + int tl_min_w, br_min_w, tl_min_h, br_min_h; + + if(recursive){ + split_update_bounds(tl, recursive); + split_update_bounds(br, recursive); + } + + tl_max_w=splitfloat_get_max(split, SPLIT_HORIZONTAL, tl); + br_max_w=splitfloat_get_max(split, SPLIT_HORIZONTAL, br); + tl_max_h=splitfloat_get_max(split, SPLIT_VERTICAL, tl); + br_max_h=splitfloat_get_max(split, SPLIT_VERTICAL, br); + tl_min_w=splitfloat_get_min(split, SPLIT_HORIZONTAL, tl); + br_min_w=splitfloat_get_min(split, SPLIT_HORIZONTAL, br); + tl_min_h=splitfloat_get_min(split, SPLIT_VERTICAL, tl); + br_min_h=splitfloat_get_min(split, SPLIT_VERTICAL, br); + + if(split->ssplit.dir==SPLIT_HORIZONTAL){ + node->max_w=infadd(tl_max_w, br_max_w); + node->min_w=minof(tl_min_w, br_min_w); + node->unused_w=0; + node->min_h=maxof(tl_min_h, br_min_h); + node->max_h=maxof(minof(tl_max_h, br_max_h), node->min_h); + node->unused_h=minof(tl->unused_h, br->unused_h); + }else{ + node->max_h=infadd(tl_max_h, br_max_h); + node->min_h=minof(tl_min_h, br_min_h); + node->unused_h=0; + node->min_w=maxof(tl_min_w, br_min_w); + node->max_w=maxof(minof(tl_max_w, br_max_w), node->min_w); + node->unused_w=minof(tl->unused_w, br->unused_w); + } +} + + +void splitfloat_update_handles(WSplitFloat *split, const WRectangle *tlg_, + const WRectangle *brg_) +{ + WRectangle tlg=*tlg_, brg=*brg_; + + if(split->ssplit.dir==SPLIT_HORIZONTAL){ + tlg.w=split->tlpwin->bdw.right; + tlg.x=tlg_->x+tlg_->w-tlg.w; + brg.w=split->brpwin->bdw.left; + }else{ + tlg.h=split->tlpwin->bdw.bottom; + tlg.y=tlg_->y+tlg_->h-tlg.h; + brg.h=split->brpwin->bdw.top; + } + + region_fit((WRegion*)split->tlpwin, &tlg, REGION_FIT_EXACT); + region_fit((WRegion*)split->brpwin, &brg, REGION_FIT_EXACT); +} + + +static void bound(int *what, int min, int max) +{ + if(*whatmax) + *what=max; +} + + +static void adjust_sizes(int *tls_, int *brs_, int nsize, + int tlmin, int brmin, int tlmax, int brmax, + int primn) +{ + int tls=maxof(0, *tls_); + int brs=maxof(0, *brs_); + nsize=maxof(1, nsize); + + if(primn==PRIMN_TL){ + tls=maxof(1, nsize-brs); + bound(&tls, tlmin, tlmax); + brs=nsize-tls; + bound(&brs, brmin, brmax); + tls=nsize-brs; + bound(&tls, tlmin, tlmax); + }else if(primn==PRIMN_BR){ + brs=maxof(1, nsize-tls); + bound(&brs, brmin, brmax); + tls=nsize-brs; + bound(&tls, tlmin, tlmax); + brs=nsize-tls; + bound(&brs, brmin, brmax); + }else{ /* && PRIMN_ANY */ + tls=tls*nsize/maxof(2, tls+brs); + bound(&tls, tlmin, tlmax); + brs=nsize-tls; + bound(&brs, brmin, brmax); + tls=nsize-brs; + bound(&tls, tlmin, tlmax); + } + + *tls_=tls; + *brs_=brs; +} + + +static void adjust_size(int *sz, int dir, WSplitFloat *f, WSplit *s) +{ + int mi=splitfloat_get_min(f, dir, s); + int ma=splitfloat_get_max(f, dir, s); + *sz=maxof(mi, minof(*sz, ma)); +} + + +static void splitfloat_do_resize(WSplitFloat *split, const WRectangle *ng, + int hprimn, int vprimn, bool transpose) +{ + WRectangle tlg=GEOM(split->ssplit.tl); + WRectangle brg=GEOM(split->ssplit.br); + WRectangle ntlg=*ng, nbrg=*ng; + WRectangle *og=&((WSplit*)split)->geom; + int dir=split->ssplit.dir; + bool adjust=TRUE; + + splitfloat_tl_cnt_to_pwin(split, &tlg); + splitfloat_br_cnt_to_pwin(split, &brg); + + if(transpose){ + if(dir==SPLIT_VERTICAL){ + dir=SPLIT_HORIZONTAL; + split->tlpwin->bline=GR_BORDERLINE_RIGHT; + split->brpwin->bline=GR_BORDERLINE_LEFT; + }else{ + dir=SPLIT_VERTICAL; + split->tlpwin->bline=GR_BORDERLINE_BOTTOM; + split->brpwin->bline=GR_BORDERLINE_TOP; + } + split->ssplit.dir=dir; + } + + if(dir==SPLIT_VERTICAL){ + if(ng->h<=tlg.h+brg.h){ + if(transpose){ + ntlg.h=minof(tlg.w, ng->h*2/3); + nbrg.h=minof(brg.w, ng->h*2/3); + adjust_size(&ntlg.h, dir, split, split->ssplit.tl); + adjust_size(&nbrg.h, dir, split, split->ssplit.br); + adjust=(ng->h>ntlg.h+nbrg.h); + }else{ + ntlg.h=minof(ng->h, tlg.h); + nbrg.h=minof(ng->h, brg.h); + adjust=FALSE; + } + }else{ + ntlg.h=tlg.h; + nbrg.h=brg.h; + } + + if(adjust){ + adjust_sizes(&ntlg.h, &nbrg.h, ng->h, + splitfloat_get_min(split, dir, split->ssplit.tl), + splitfloat_get_min(split, dir, split->ssplit.br), + splitfloat_get_max(split, dir, split->ssplit.tl), + splitfloat_get_max(split, dir, split->ssplit.br), + vprimn); + } + + nbrg.y=ng->y+ng->h-nbrg.h; + }else{ + if(ng->w<=tlg.w+brg.w){ + if(transpose){ + ntlg.w=minof(tlg.h, ng->w*2/3); + nbrg.w=minof(brg.h, ng->w*2/3); + adjust_size(&ntlg.w, dir, split, split->ssplit.tl); + adjust_size(&nbrg.w, dir, split, split->ssplit.br); + adjust=(ng->w>ntlg.w+nbrg.w); + }else{ + ntlg.w=minof(ng->w, tlg.w); + nbrg.w=minof(ng->w, brg.w); + adjust=FALSE; + } + }else{ + ntlg.w=tlg.w; + nbrg.w=brg.w; + } + + if(adjust){ + adjust_sizes(&ntlg.w, &nbrg.w, ng->w, + splitfloat_get_min(split, dir, split->ssplit.tl), + splitfloat_get_min(split, dir, split->ssplit.br), + splitfloat_get_max(split, dir, split->ssplit.tl), + splitfloat_get_max(split, dir, split->ssplit.br), + hprimn); + } + + nbrg.x=ng->x+ng->w-nbrg.w; + } + + GEOM(split)=*ng; + + splitfloat_update_handles(split, &ntlg, &nbrg); + + splitfloat_tl_pwin_to_cnt(split, &ntlg); + split_do_resize(split->ssplit.tl, &ntlg, hprimn, vprimn, transpose); + splitfloat_br_pwin_to_cnt(split, &nbrg); + split_do_resize(split->ssplit.br, &nbrg, hprimn, vprimn, transpose); +} + + +static void calc_amount(int *amount, int *oamount, + int rs, WSplitSplit *p, int omax, + const WRectangle *ng, const WRectangle *og) +{ + *oamount=0; + + if(rs>=0){ + if(p->dir==SPLIT_VERTICAL) + *amount=maxof(0, minof(rs, GEOM(p).h-ng->h)); + else + *amount=maxof(0, minof(rs, GEOM(p).w-ng->w)); + }else{ + if(p->dir==SPLIT_VERTICAL){ + int overlap=maxof(0, og->h-(GEOM(p).h-ng->h)); + *amount=-minof(-rs, overlap); + *oamount=maxof(0, minof(*amount-rs, omax-og->h)); + *amount-=*oamount; + }else{ + int overlap=maxof(0, og->w-(GEOM(p).w-ng->w)); + *amount=-minof(-rs, overlap); + *oamount=maxof(0, minof(*amount-rs, omax-og->w)); + *amount-=*oamount; + } + } +} + + +static void splitfloat_do_rqsize(WSplitFloat *split, WSplit *node, + RootwardAmount *ha, RootwardAmount *va, + WRectangle *rg, bool tryonly) +{ + int hprimn=PRIMN_ANY, vprimn=PRIMN_ANY; + WRectangle pg, og, ng, nog, nng; + RootwardAmount *ca; + WSplit *other; + int amount=0, oamount=0, omax; + int thisnode; + WSplitSplit *p=&(split->ssplit); + + assert(!ha->any || ha->tl==0); + assert(!va->any || va->tl==0); + assert(p->tl==node || p->br==node); + + if(p->tl==node){ + other=p->br; + thisnode=PRIMN_TL; + }else{ + other=p->tl; + thisnode=PRIMN_BR; + } + + ng=GEOM(node); + og=GEOM(other); + + if(thisnode==PRIMN_TL){ + splitfloat_tl_cnt_to_pwin(split, &ng); + splitfloat_br_cnt_to_pwin(split, &og); + }else{ + splitfloat_br_cnt_to_pwin(split, &ng); + splitfloat_tl_cnt_to_pwin(split, &og); + } + + ca=(p->dir==SPLIT_VERTICAL ? va : ha); + + omax=splitfloat_get_max(split, p->dir, other); + + if(thisnode==PRIMN_TL || ca->any){ + calc_amount(&amount, &oamount, ca->br, p, omax, &ng, &og); + ca->br-=amount; + }else/*if(thisnode==PRIMN_BR)*/{ + calc_amount(&amount, &oamount, ca->tl, p, omax, &ng, &og); + ca->tl-=amount; + } + + if(((WSplit*)p)->parent==NULL /*|| + (ha->tl==0 && ha->br==0 && va->tl==0 && va->br==0)*/){ + pg=((WSplit*)p)->geom; + }else{ + splitinner_do_rqsize(((WSplit*)p)->parent, (WSplit*)p, ha, va, + &pg, tryonly); + } + + assert(pg.w>=0 && pg.h>=0); + + nog=pg; + nng=pg; + + if(p->dir==SPLIT_VERTICAL){ + nog.h=minof(pg.h, maxof(0, og.h+oamount)); + nng.h=minof(pg.h, maxof(0, ng.h+amount+pg.h-GEOM(p).h)); + if(thisnode==PRIMN_TL) + nog.y=pg.y+pg.h-nog.h; + else + nng.y=pg.y+pg.h-nng.h; + vprimn=thisnode; + }else{ + nog.w=minof(pg.w, maxof(0, og.w+oamount)); + nng.w=minof(pg.w, maxof(0, ng.w+amount+pg.w-GEOM(p).w)); + if(thisnode==PRIMN_TL) + nog.x=pg.x+pg.w-nog.w; + else + nng.x=pg.x+pg.w-nng.w; + hprimn=thisnode; + } + + if(!tryonly){ + GEOM(p)=pg; + + if(thisnode==PRIMN_TL){ + splitfloat_update_handles(split, &nng, &nog); + splitfloat_br_pwin_to_cnt(split, &nog); + }else{ + splitfloat_update_handles(split, &nog, &nng); + splitfloat_tl_pwin_to_cnt(split, &nog); + } + + /* Entä jos 'other' on stdisp? */ + split_do_resize(other, &nog, hprimn, vprimn, FALSE); + } + + *rg=nng; + if(thisnode==PRIMN_TL) + splitfloat_tl_pwin_to_cnt(split, rg); + else + splitfloat_br_pwin_to_cnt(split, rg); +} + + +void splitfloat_flip(WSplitFloat *split) +{ + WRectangle tlg, brg; + + splitsplit_flip_default(&split->ssplit); + + tlg=split->ssplit.tl->geom; + brg=split->ssplit.br->geom; + + splitfloat_tl_cnt_to_pwin(split, &tlg); + splitfloat_br_cnt_to_pwin(split, &brg); + splitfloat_update_handles(split, &tlg, &brg); +} + + +/*}}}*/ + + +/*{{{ Loading code */ + + +#define MINS 8 + +static void adjust_tls_brs(int *tls, int *brs, int total) +{ + if(*tls<=0) + *tls=MINS; + if(*brs<=0) + *brs=MINS; + + if(*tls+*brsw); + tlg->w=tls; + brg->w=brs; + brg->x=geom->x+geom->w-brs; + }else{ + adjust_tls_brs(&tls, &brs, geom->h); + tlg->h=tls; + brg->h=brs; + brg->y=geom->y+geom->h-brs; + } +} + + +WSplit *load_splitfloat(WTiling *ws, const WRectangle *geom, ExtlTab tab) +{ + WSplit *tl=NULL, *br=NULL; + WSplitFloat *split; + char *dir_str; + int dir, brs, tls; + ExtlTab subtab; + WRectangle tlg, brg; + int set=0; + + set+=(extl_table_gets_i(tab, "tls", &tls)==TRUE); + set+=(extl_table_gets_i(tab, "brs", &brs)==TRUE); + set+=(extl_table_gets_s(tab, "dir", &dir_str)==TRUE); + + if(set!=3) + return NULL; + + if(strcmp(dir_str, "vertical")==0){ + dir=SPLIT_VERTICAL; + }else if(strcmp(dir_str, "horizontal")==0){ + dir=SPLIT_HORIZONTAL; + }else{ + warn(TR("Invalid direction.")); + free(dir_str); + return NULL; + } + free(dir_str); + + split=create_splitfloat(geom, ws, dir); + if(split==NULL) + return NULL; + + if(!extl_table_is_bool_set(tab, "tls_brs_incl_handles")){ + if(split->ssplit.dir==SPLIT_HORIZONTAL){ + tls+=split->tlpwin->bdw.right; + brs+=split->brpwin->bdw.left; + }else{ + tls+=split->tlpwin->bdw.bottom; + brs+=split->brpwin->bdw.top; + } + } + + calc_tlg_brg(geom, tls, brs, dir, &tlg, &brg); + + splitfloat_update_handles(split, &tlg, &brg); + + if(extl_table_gets_t(tab, "tl", &subtab)){ + WRectangle g=tlg; + splitfloat_tl_pwin_to_cnt(split, &g); + tl=tiling_load_node(ws, &g, subtab); + extl_unref_table(subtab); + } + + if(extl_table_gets_t(tab, "br", &subtab)){ + WRectangle g; + if(tl==NULL){ + g=*geom; + }else{ + g=brg; + splitfloat_br_pwin_to_cnt(split, &g); + } + br=tiling_load_node(ws, &g, subtab); + extl_unref_table(subtab); + } + + if(tl==NULL || br==NULL){ + destroy_obj((Obj*)split); + if(tl!=NULL){ + split_do_resize(tl, geom, PRIMN_ANY, PRIMN_ANY, FALSE); + return tl; + } + if(br!=NULL){ + split_do_resize(br, geom, PRIMN_ANY, PRIMN_ANY, FALSE); + return br; + } + return NULL; + } + + tl->parent=(WSplitInner*)split; + br->parent=(WSplitInner*)split; + + split->ssplit.tl=tl; + split->ssplit.br=br; + + return (WSplit*)split; +} + + +/*}}}*/ + + +/*{{{ Split */ + + +WSplitRegion *splittree_split_floating(WSplit *node, int dir, int primn, + int nmins, WRegionSimpleCreateFn *fn, + WTiling *ws) +{ + WSplitFloat *sf; + int omins, mins; + int sn, so, s, rs; + int bn, bo; + WRectangle gn, go, gnc, goc; + WFitParams fp; + WRegion *nreg; + WSplitRegion *nnode; + WSplitInner *psplit; + + if(primn!=PRIMN_TL && primn!=PRIMN_BR) + primn=PRIMN_BR; + + split_update_bounds(split_find_root(node), TRUE); + + sf=create_splitfloat(&node->geom, ws, dir); + + if(sf==NULL) + return NULL; + + omins=(dir==SPLIT_VERTICAL ? node->min_h : node->min_w); + s=split_size(node, dir); + + if(primn==PRIMN_BR){ + bn=BR_BORDER(sf); + bo=TL_BORDER(sf); + }else{ + bn=TL_BORDER(sf); + bo=BR_BORDER(sf); + } + + mins=maxof(omins+bo, nmins+bn); + + /* Potentially resize old node. */ + + splittree_begin_resize(); + + if(mins>s){ + WRectangle ng=node->geom, rg; + if(dir==SPLIT_VERTICAL) + ng.h=mins; + else + ng.w=mins; + + split_do_rqgeom_(node, &ng, TRUE, TRUE, &rg, TRUE); + rs=(dir==SPLIT_VERTICAL ? rg.h : rg.w); + if(rsgeom=node->geom; + + if(primn==PRIMN_TL){ + calc_tlg_brg(&(node->geom), sn, so, dir, &gn, &go); + splitfloat_update_handles(sf, &gn, &go); + gnc=gn; splitfloat_tl_pwin_to_cnt(sf, &gnc); + goc=go; splitfloat_br_pwin_to_cnt(sf, &goc); + }else{ + calc_tlg_brg(&(node->geom), so, sn, dir, &go, &gn); + splitfloat_update_handles(sf, &go, &gn); + goc=go; splitfloat_tl_pwin_to_cnt(sf, &goc); + gnc=gn; splitfloat_br_pwin_to_cnt(sf, &gnc); + } + + /* Create the region. */ + + fp.mode=REGION_FIT_EXACT; + fp.g=gnc; + + nreg=fn(REGION_PARENT(ws), &fp); + + if(nreg==NULL){ + destroy_obj((Obj*)sf); + return NULL; + } + + nnode=create_splitregion(&(fp.g), nreg); + if(nnode==NULL){ + destroy_obj((Obj*)nreg); + destroy_obj((Obj*)sf); + return NULL; + } + + /* Now that everything's ok, resize and move original node. */ + + split_do_resize(node, &goc, + (dir==SPLIT_HORIZONTAL ? primn : PRIMN_ANY), + (dir==SPLIT_VERTICAL ? primn : PRIMN_ANY), + FALSE); + + /* Set up split structure. */ + + psplit=node->parent; + + if(psplit!=NULL) + splitinner_replace(psplit, node, (WSplit*)sf); + else + splittree_changeroot(node, (WSplit*)sf); + + node->parent=(WSplitInner*)sf; + ((WSplit*)nnode)->parent=(WSplitInner*)sf; + + if(primn==PRIMN_BR){ + sf->ssplit.tl=node; + sf->ssplit.br=(WSplit*)nnode; + }else{ + sf->ssplit.tl=(WSplit*)nnode; + sf->ssplit.br=node; + } + + /*splittree_end_resize();*/ + + return nnode; +} + + +/*}}}*/ + + +/*{{{ The class */ + + +static DynFunTab splitfloat_dynfuntab[]={ + {split_update_bounds, splitfloat_update_bounds}, + {split_do_resize, splitfloat_do_resize}, + {splitinner_do_rqsize, splitfloat_do_rqsize}, + {split_stacking, splitfloat_stacking}, + {split_restack, splitfloat_restack}, + {split_reparent, splitfloat_reparent}, + {split_map, splitfloat_map}, + {split_unmap, splitfloat_unmap}, + {splitsplit_flip, splitfloat_flip}, + END_DYNFUNTAB, +}; + + +EXTL_EXPORT +IMPLCLASS(WSplitFloat, WSplitSplit, splitfloat_deinit, splitfloat_dynfuntab); + + +/*}}}*/ + diff --git a/mod_tiling/splitfloat.h b/mod_tiling/splitfloat.h new file mode 100644 index 0000000..9bb428e --- /dev/null +++ b/mod_tiling/splitfloat.h @@ -0,0 +1,56 @@ +/* + * ion/panews/splitfloat.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_PANEWS_SPLITFLOAT_H +#define ION_PANEWS_SPLITFLOAT_H + +#include +#include +#include "split.h" +#include "tiling.h" + +INTRCLASS(WSplitFloat); + +#include "panehandle.h" + +DECLCLASS(WSplitFloat){ + WSplitSplit ssplit; + WPaneHandle *tlpwin, *brpwin; +}; + + +extern bool splitfloat_init(WSplitFloat *split, const WRectangle *geom, + WTiling *ws, int dir); + +extern WSplitFloat *create_splitfloat(const WRectangle *geom, + WTiling *ws, int dir); + +extern void splitfloat_deinit(WSplitFloat *split); + +extern void splitfloat_update_handles(WSplitFloat *split, + const WRectangle *tlg, + const WRectangle *brg); +extern void splitfloat_tl_pwin_to_cnt(WSplitFloat *split, WRectangle *g); +extern void splitfloat_br_pwin_to_cnt(WSplitFloat *split, WRectangle *g); +extern void splitfloat_tl_cnt_to_pwin(WSplitFloat *split, WRectangle *g); +extern void splitfloat_br_cnt_to_pwin(WSplitFloat *split, WRectangle *g); + +extern void splitfloat_flip(WSplitFloat *split); + +extern WSplit *load_splitfloat(WTiling *ws, const WRectangle *geom, + ExtlTab tab); + +extern WSplitRegion *splittree_split_floating(WSplit *node, int dir, + int primn, int nmins, + WRegionSimpleCreateFn *fn, + WTiling *ws); + +#endif /* ION_PANEWS_SPLITFLOAT_H */ diff --git a/mod_tiling/tiling.c b/mod_tiling/tiling.c new file mode 100644 index 0000000..2ab56b0 --- /dev/null +++ b/mod_tiling/tiling.c @@ -0,0 +1,1764 @@ +/* + * ion/mod_tiling/tiling.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "placement.h" +#include "tiling.h" +#include "split.h" +#include "splitfloat.h" +#include "split-stdisp.h" +#include "main.h" + + + +static WTilingIterTmp tiling_iter_default_tmp; + + +/*{{{ Some helper routines */ + + +#define STDISP_OF(WS) \ + ((WS)->stdispnode!=NULL ? (WS)->stdispnode->regnode.reg : NULL) + + +static WSplitRegion *get_node_check(WTiling *ws, WRegion *reg) +{ + WSplitRegion *node; + + if(reg==NULL) + return NULL; + + node=splittree_node_of(reg); + + if(node==NULL || REGION_MANAGER(reg)!=(WRegion*)ws) + return NULL; + + return node; +} + + +static bool check_node(WTiling *ws, WSplit *split) +{ + if(split->parent) + return check_node(ws, (WSplit*)split->parent); + + if((split->ws_if_root!=(void*)ws)){ + warn(TR("Split not on workspace.")); + return FALSE; + } + return TRUE; +} + + +/*}}}*/ + + +/*{{{ Dynfun implementations */ + + +static void reparent_mgd(WRegion *sub, WWindow *par) +{ + WFitParams subfp; + subfp.g=REGION_GEOM(sub); + subfp.mode=REGION_FIT_EXACT; + if(!region_fitrep(sub, par, &subfp)){ + warn(TR("Error reparenting %s."), region_name(sub)); + region_detach_manager(sub); + } +} + + +bool tiling_fitrep(WTiling *ws, WWindow *par, const WFitParams *fp) +{ + WTilingIterTmp tmp; + bool ok=FALSE; + + if(par!=NULL){ + if(!region_same_rootwin((WRegion*)ws, (WRegion*)par)) + return FALSE; + + region_unset_parent((WRegion*)ws); + + XReparentWindow(ioncore_g.dpy, ws->dummywin, + par->win, fp->g.x, fp->g.y); + + region_set_parent((WRegion*)ws, par); + + if(ws->split_tree!=NULL) + split_reparent(ws->split_tree, par); + } + + REGION_GEOM(ws)=fp->g; + + if(ws->split_tree!=NULL){ + bool done=FALSE; + if(fp->mode®ION_FIT_ROTATE) + ok=split_rotate_to(ws->split_tree, &(fp->g), fp->rotation); + if(!ok) + split_resize(ws->split_tree, &(fp->g), PRIMN_ANY, PRIMN_ANY); + } + + return TRUE; +} + + +void tiling_managed_rqgeom(WTiling *ws, WRegion *mgd, + const WRQGeomParams *rq, + WRectangle *geomret) +{ + WSplitRegion *node=get_node_check(ws, mgd); + if(node!=NULL && ws->split_tree!=NULL) + splittree_rqgeom((WSplit*)node, rq->flags, &rq->geom, geomret); +} + + +void tiling_map(WTiling *ws) +{ + REGION_MARK_MAPPED(ws); + XMapWindow(ioncore_g.dpy, ws->dummywin); + + if(ws->split_tree!=NULL) + split_map(ws->split_tree); +} + + +void tiling_unmap(WTiling *ws) +{ + REGION_MARK_UNMAPPED(ws); + XUnmapWindow(ioncore_g.dpy, ws->dummywin); + + if(ws->split_tree!=NULL) + split_unmap(ws->split_tree); +} + + +void tiling_fallback_focus(WTiling *ws, bool warp) +{ + region_finalise_focusing((WRegion*)ws, ws->dummywin, warp); +} + + +void tiling_do_set_focus(WTiling *ws, bool warp) +{ + WRegion *sub=tiling_current(ws); + + if(sub==NULL){ + tiling_fallback_focus(ws, warp); + return; + } + + region_do_set_focus(sub, warp); +} + + +static WTimer *restack_timer=NULL; + + +static void restack_handler(WTimer *tmr, Obj *obj) +{ + if(obj!=NULL){ + WTiling *ws=(WTiling*)obj; + split_restack(ws->split_tree, ws->dummywin, Above); + } +} + + +bool tiling_managed_prepare_focus(WTiling *ws, WRegion *reg, + int flags, WPrepareFocusResult *res) +{ + WSplitRegion *node; + + if(!region_prepare_focus((WRegion*)ws, flags, res)) + return FALSE; + + node=get_node_check(ws, reg); + + if(node!=NULL && node->split.parent!=NULL) + splitinner_mark_current(node->split.parent, &(node->split)); + + /* WSplitSplit uses activity based stacking as required on WAutoWS, + * so we must restack here. + */ + if(ws->split_tree!=NULL){ + int rd=mod_tiling_raise_delay; + bool use_timer=rd>0 && flags®ION_GOTO_ENTERWINDOW; + + if(use_timer){ + if(restack_timer!=NULL){ + Obj *obj=restack_timer->objwatch.obj; + if(obj!=(Obj*)ws){ + timer_reset(restack_timer); + restack_handler(restack_timer, obj); + } + }else{ + restack_timer=create_timer(); + } + } + + if(use_timer && restack_timer!=NULL){ + timer_set(restack_timer, rd, restack_handler, (Obj*)ws); + }else{ + split_restack(ws->split_tree, ws->dummywin, Above); + } + } + + res->reg=reg; + res->flags=flags; + return TRUE; +} + + + +void tiling_restack(WTiling *ws, Window other, int mode) +{ + xwindow_restack(ws->dummywin, other, mode); + if(ws->split_tree!=NULL) + split_restack(ws->split_tree, ws->dummywin, Above); +} + + +void tiling_stacking(WTiling *ws, Window *bottomret, Window *topret) +{ + Window sbottom=None, stop=None; + + if(ws->split_tree!=None) + split_stacking(ws->split_tree, &sbottom, &stop); + + *bottomret=ws->dummywin; + *topret=(stop!=None ? stop : ws->dummywin); +} + + +Window tiling_xwindow(const WTiling *ws) +{ + return ws->dummywin; +} + + +/* +WRegion *tiling_rqclose_propagate(WTiling *ws, WRegion *maybe_sub) +{ + return (region_rqclose((WRegion*)ws, FALSE) ? (WRegion*)ws : NULL); +} +*/ + +WPHolder *tiling_prepare_manage_transient(WTiling *ws, + const WClientWin *transient, + const WManageParams *param, + int unused) +{ + /* Transient manager searches should not cross tilings unless + * explicitly floated. + */ + if(extl_table_is_bool_set(transient->proptab, "float")){ + return region_prepare_manage_transient_default((WRegion*)ws, + transient, + param, + unused); + }else{ + return NULL; + } +} + + +/*}}}*/ + + +/*{{{ Status display support code */ + + +static bool regnodefilter(WSplit *split) +{ + return OBJ_IS(split, WSplitRegion); +} + + +void tiling_unmanage_stdisp(WTiling *ws, bool permanent, bool nofocus) +{ + WSplitRegion *tofocus=NULL; + bool setfocus=FALSE; + WRegion *od; + + if(ws->stdispnode==NULL) + return; + + od=ws->stdispnode->regnode.reg; + + if(od!=NULL){ + if(!nofocus && REGION_IS_ACTIVE(od) && + region_may_control_focus((WRegion*)ws)){ + setfocus=TRUE; + tofocus=(WSplitRegion*)split_nextto((WSplit*)(ws->stdispnode), + PRIMN_ANY, PRIMN_ANY, + regnodefilter); + } + /* Reset node_of info here so tiling_managed_remove will not + * remove the node. + */ + splittree_set_node_of(od, NULL); + tiling_do_managed_remove(ws, od); + } + + if(permanent){ + WSplit *node=(WSplit*)ws->stdispnode; + ws->stdispnode=NULL; + splittree_remove(node, TRUE); + } + + if(setfocus){ + if(tofocus!=NULL) + region_set_focus(tofocus->reg); + else + tiling_fallback_focus(ws, FALSE); + } +} + + +static void tiling_create_stdispnode(WTiling *ws, WRegion *stdisp, + int corner, int orientation, + bool fullsize) +{ + int flags=REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_Y; + WRectangle *wg=®ION_GEOM(ws), dg; + WSplitST *stdispnode; + WSplitSplit *split; + + assert(ws->split_tree!=NULL); + + if(orientation==REGION_ORIENTATION_HORIZONTAL){ + dg.x=wg->x; + dg.w=wg->w; + dg.h=0; + dg.y=((corner==MPLEX_STDISP_BL || corner==MPLEX_STDISP_BR) + ? wg->y+wg->h + : 0); + }else{ + dg.y=wg->y; + dg.h=wg->h; + dg.w=0; + dg.x=((corner==MPLEX_STDISP_TR || corner==MPLEX_STDISP_BR) + ? wg->x+wg->w + : 0); + } + + stdispnode=create_splitst(&dg, stdisp); + + if(stdispnode==NULL){ + warn(TR("Unable to create a node for status display.")); + return; + } + + stdispnode->corner=corner; + stdispnode->orientation=orientation; + stdispnode->fullsize=fullsize; + + split=create_splitsplit(wg, (orientation==REGION_ORIENTATION_HORIZONTAL + ? SPLIT_VERTICAL + : SPLIT_HORIZONTAL)); + + if(split==NULL){ + warn(TR("Unable to create new split for status display.")); + stdispnode->regnode.reg=NULL; + destroy_obj((Obj*)stdispnode); + return; + } + + /* Set up new split tree */ + ((WSplit*)stdispnode)->parent=(WSplitInner*)split; + ws->split_tree->parent=(WSplitInner*)split; + ws->split_tree->ws_if_root=NULL; + + if((orientation==REGION_ORIENTATION_HORIZONTAL && + (corner==MPLEX_STDISP_BL || corner==MPLEX_STDISP_BR)) || + (orientation==REGION_ORIENTATION_VERTICAL && + (corner==MPLEX_STDISP_TR || corner==MPLEX_STDISP_BR))){ + split->tl=ws->split_tree; + split->br=(WSplit*)stdispnode; + split->current=SPLIT_CURRENT_TL; + }else{ + split->tl=(WSplit*)stdispnode; + split->br=ws->split_tree; + split->current=SPLIT_CURRENT_BR; + } + + ws->split_tree=(WSplit*)split; + ((WSplit*)split)->ws_if_root=ws; + ws->stdispnode=stdispnode; +} + + +void tiling_manage_stdisp(WTiling *ws, WRegion *stdisp, + const WMPlexSTDispInfo *di) +{ + bool mcf=region_may_control_focus((WRegion*)ws); + int flags=REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_Y; + int orientation=region_orientation(stdisp); + bool act=FALSE; + WRectangle dg, *stdg; + + if(orientation!=REGION_ORIENTATION_VERTICAL /*&& + orientation!=REGION_ORIENTATION_HORIZONTAL*/){ + orientation=REGION_ORIENTATION_HORIZONTAL; + } + + if(ws->stdispnode==NULL || ws->stdispnode->regnode.reg!=stdisp) + region_detach_manager(stdisp); + + /* Remove old stdisp if corner and orientation don't match. + */ + if(ws->stdispnode!=NULL && (di->pos!=ws->stdispnode->corner || + orientation!=ws->stdispnode->orientation)){ + tiling_unmanage_stdisp(ws, TRUE, TRUE); + } + + if(ws->stdispnode==NULL){ + tiling_create_stdispnode(ws, stdisp, di->pos, orientation, + di->fullsize); + if(ws->stdispnode==NULL) + return; + }else{ + WRegion *od=ws->stdispnode->regnode.reg; + if(od!=NULL){ + act=REGION_IS_ACTIVE(od); + splittree_set_node_of(od, NULL); + tiling_managed_remove(ws, od); + assert(ws->stdispnode->regnode.reg==NULL); + } + + ws->stdispnode->fullsize=di->fullsize; + ws->stdispnode->regnode.reg=stdisp; + splittree_set_node_of(stdisp, &(ws->stdispnode->regnode)); + } + + if(!tiling_managed_add(ws, stdisp)){ + tiling_unmanage_stdisp(ws, TRUE, TRUE); + return; + } + + dg=((WSplit*)(ws->stdispnode))->geom; + + dg.h=stdisp_recommended_h(ws->stdispnode); + dg.w=stdisp_recommended_w(ws->stdispnode); + + splittree_rqgeom((WSplit*)(ws->stdispnode), flags, &dg, FALSE); + + stdg=&(((WSplit*)ws->stdispnode)->geom); + + if(stdisp->geom.x!=stdg->x || stdisp->geom.y!=stdg->y || + stdisp->geom.w!=stdg->w || stdisp->geom.h!=stdg->h){ + region_fit(stdisp, stdg, REGION_FIT_EXACT); + } + + /* Restack to ensure the split tree is stacked in the expected order. */ + if(ws->split_tree!=NULL) + split_restack(ws->split_tree, ws->dummywin, Above); + + if(mcf && act) + region_set_focus(stdisp); +} + + +/*}}}*/ + + +/*{{{ Create/destroy */ + + +bool tiling_managed_add_default(WTiling *ws, WRegion *reg) +{ + Window bottom=None, top=None; + WFrame *frame; + + if(STDISP_OF(ws)!=reg){ + if(!ptrlist_insert_last(&(ws->managed_list), reg)) + return FALSE; + } + + region_set_manager(reg, (WRegion*)ws); + + frame=OBJ_CAST(reg, WFrame); + if(frame!=NULL){ + WFrameMode mode=frame_mode(frame); + if(mode!=FRAME_MODE_TILED && mode!=FRAME_MODE_TILED_ALT) + frame_set_mode(frame, FRAME_MODE_TILED); + } + + if(REGION_IS_MAPPED(ws)) + region_map(reg); + + if(region_may_control_focus((WRegion*)ws)){ + WRegion *curr=tiling_current(ws); + if(curr==NULL || !REGION_IS_ACTIVE(curr)) + region_warp(reg); + } + + return TRUE; +} + + +bool tiling_managed_add(WTiling *ws, WRegion *reg) +{ + bool ret=FALSE; + CALL_DYN_RET(ret, bool, tiling_managed_add, ws, (ws, reg)); + return ret; +} + + +static WRegion *create_initial_frame(WTiling *ws, WWindow *parent, + const WFitParams *fp) +{ + WRegion *reg=ws->create_frame_fn(parent, fp); + + if(reg==NULL) + return NULL; + + ws->split_tree=(WSplit*)create_splitregion(&(fp->g), reg); + if(ws->split_tree==NULL){ + destroy_obj((Obj*)reg); + return NULL; + } + ws->split_tree->ws_if_root=ws; + + if(!tiling_managed_add(ws, reg)){ + destroy_obj((Obj*)reg); + destroy_obj((Obj*)ws->split_tree); + return NULL; + } + + return reg; +} + + +static WRegion *create_frame_tiling(WWindow *parent, const WFitParams *fp) +{ + return (WRegion*)create_frame(parent, fp, FRAME_MODE_TILED); +} + + +bool tiling_init(WTiling *ws, WWindow *parent, const WFitParams *fp, + WRegionSimpleCreateFn *create_frame_fn, bool ci) +{ + ws->split_tree=NULL; + ws->create_frame_fn=(create_frame_fn + ? create_frame_fn + : create_frame_tiling); + ws->stdispnode=NULL; + ws->managed_list=NULL; + + ws->dummywin=XCreateWindow(ioncore_g.dpy, parent->win, + fp->g.x, fp->g.y, 1, 1, 0, + CopyFromParent, InputOnly, + CopyFromParent, 0, NULL); + if(ws->dummywin==None) + return FALSE; + + region_init(&(ws->reg), parent, fp); + + ws->reg.flags|=(REGION_GRAB_ON_PARENT| + REGION_PLEASE_WARP); + + if(ci){ + if(create_initial_frame(ws, parent, fp)==NULL){ + XDestroyWindow(ioncore_g.dpy, ws->dummywin); + return FALSE; + } + } + + XSelectInput(ioncore_g.dpy, ws->dummywin, + FocusChangeMask|KeyPressMask|KeyReleaseMask| + ButtonPressMask|ButtonReleaseMask); + XSaveContext(ioncore_g.dpy, ws->dummywin, ioncore_g.win_context, + (XPointer)ws); + + region_register(&(ws->reg)); + region_add_bindmap((WRegion*)ws, mod_tiling_tiling_bindmap); + + return TRUE; +} + + +WTiling *create_tiling(WWindow *parent, const WFitParams *fp, + WRegionSimpleCreateFn *create_frame_fn, bool ci) +{ + CREATEOBJ_IMPL(WTiling, tiling, (p, parent, fp, create_frame_fn, ci)); +} + + +WTiling *create_tiling_simple(WWindow *parent, const WFitParams *fp) +{ + return create_tiling(parent, fp, NULL, TRUE); +} + + +void tiling_deinit(WTiling *ws) +{ + WRegion *reg; + WTilingIterTmp tmp; + WMPlex *remanage_mplex=NULL; + + tiling_unmanage_stdisp(ws, FALSE, TRUE); + + FOR_ALL_MANAGED_BY_TILING(reg, ws, tmp){ + destroy_obj((Obj*)reg); + } + + FOR_ALL_MANAGED_BY_TILING(reg, ws, tmp){ + assert(FALSE); + } + + if(ws->split_tree!=NULL) + destroy_obj((Obj*)(ws->split_tree)); + + XDeleteContext(ioncore_g.dpy, ws->dummywin, ioncore_g.win_context); + XDestroyWindow(ioncore_g.dpy, ws->dummywin); + ws->dummywin=None; + + region_deinit(&(ws->reg)); +} + + +bool tiling_managed_may_destroy(WTiling *ws, WRegion *reg) +{ + WTilingIterTmp tmp; + WRegion *mgd; + + FOR_ALL_MANAGED_BY_TILING(mgd, ws, tmp){ + if(mgd!=STDISP_OF(ws) && mgd!=reg){ + return TRUE; + } + } + + return region_manager_allows_destroying((WRegion*)ws); +} + + +bool tiling_may_destroy(WTiling *ws, WRegion *reg) +{ + WTilingIterTmp tmp; + WRegion *mgd; + + FOR_ALL_MANAGED_BY_TILING(mgd, ws, tmp){ + if(mgd!=STDISP_OF(ws)){ + warn(TR("Workspace not empty - refusing to destroy.")); + return FALSE; + } + } + + return TRUE; +} + + +bool tiling_rescue_clientwins(WTiling *ws, WPHolder *ph) +{ + WTilingIterTmp tmp; + + ptrlist_iter_init(&tmp, ws->managed_list); + + return region_rescue_some_clientwins((WRegion*)ws, ph, + (WRegionIterator*)ptrlist_iter, + &tmp); +} + + +void tiling_do_managed_remove(WTiling *ws, WRegion *reg) +{ + if(STDISP_OF(ws)==reg){ + ws->stdispnode->regnode.reg=NULL; + }else{ + ptrlist_remove(&(ws->managed_list), reg); + } + + region_unset_manager(reg, (WRegion*)ws); +} + + +static bool nostdispfilter(WSplit *node) +{ + return (OBJ_IS(node, WSplitRegion) && !OBJ_IS(node, WSplitST)); +} + + +void tiling_managed_remove(WTiling *ws, WRegion *reg) +{ + bool ds=OBJ_IS_BEING_DESTROYED(ws); + bool act=REGION_IS_ACTIVE(reg); + bool mcf=region_may_control_focus((WRegion*)ws); + WSplitRegion *node=get_node_check(ws, reg); + WRegion *other; + + other=tiling_do_navi_next(ws, reg, REGION_NAVI_ANY, TRUE, FALSE); + + tiling_do_managed_remove(ws, reg); + + if(node==(WSplitRegion*)(ws->stdispnode)) + ws->stdispnode=NULL; + + if(node==NULL) + return; + + splittree_remove((WSplit*)node, (!ds && other!=NULL)); + + if(!ds){ + if(other==NULL) + mainloop_defer_destroy((Obj*)ws); + else if(act && mcf) + region_warp(other); + } +} + + +static bool mplexfilter(WSplit *node) +{ + WSplitRegion *regnode=OBJ_CAST(node, WSplitRegion); + + return (regnode!=NULL && regnode->reg!=NULL && + OBJ_IS(regnode->reg, WMPlex)); +} + + +static WPHolder *find_ph_result=NULL; +static WRegion *find_ph_param=NULL; + + +static bool find_ph(WSplit *split) +{ + WSplitRegion *sr=OBJ_CAST(split, WSplitRegion); + + assert(find_ph_result==NULL); + + if(sr==NULL || sr->reg==NULL) + return FALSE; + + find_ph_result=region_get_rescue_pholder_for(sr->reg, find_ph_param); + + return (find_ph_result!=NULL); +} + + +WPHolder *tiling_get_rescue_pholder_for(WTiling *ws, WRegion *mgd) +{ + WSplit *node=(WSplit*)get_node_check(ws, mgd); + WPHolder *ph; + + find_ph_result=NULL; + find_ph_param=mgd; + + if(node==NULL){ + if(ws->split_tree!=NULL){ + split_current_todir(ws->split_tree, PRIMN_ANY, PRIMN_ANY, + find_ph); + } + }else{ + while(node!=NULL){ + split_nextto(node, PRIMN_ANY, PRIMN_ANY, find_ph); + if(find_ph_result!=NULL) + break; + node=(WSplit*)node->parent; + } + } + + ph=find_ph_result; + find_ph_result=NULL; + find_ph_param=NULL; + + return ph; +} + + +/*}}}*/ + + +/*{{{ Navigation */ + + +static void navi_to_primn(WRegionNavi nh, WPrimn *hprimn, WPrimn *vprimn, + WPrimn choice) +{ + /* choice should be PRIMN_ANY or PRIMN_NONE */ + + switch(nh){ + case REGION_NAVI_BEG: + *vprimn=PRIMN_TL; + *hprimn=PRIMN_TL; + break; + + case REGION_NAVI_END: + *vprimn=PRIMN_BR; + *hprimn=PRIMN_BR; + break; + + case REGION_NAVI_LEFT: + *hprimn=PRIMN_TL; + *vprimn=choice; + break; + + case REGION_NAVI_RIGHT: + *hprimn=PRIMN_BR; + *vprimn=choice; + break; + + case REGION_NAVI_TOP: + *hprimn=choice; + *vprimn=PRIMN_TL; + break; + + case REGION_NAVI_BOTTOM: + *hprimn=choice; + *vprimn=PRIMN_BR; + break; + + default: + case REGION_NAVI_ANY: + *hprimn=PRIMN_ANY; + *vprimn=PRIMN_ANY; + break; + } +} + + +static WRegion *node_reg(WSplit *node) +{ + WSplitRegion *rnode=OBJ_CAST(node, WSplitRegion); + return (rnode!=NULL ? rnode->reg : NULL); +} + + +WRegion *tiling_do_navi_next(WTiling *ws, WRegion *reg, + WRegionNavi nh, bool nowrap, + bool any) +{ + WSplitFilter *filter=(any ? NULL : nostdispfilter); + WPrimn hprimn, vprimn; + WRegion *nxt=NULL; + + navi_to_primn(nh, &hprimn, &vprimn, PRIMN_NONE); + + if(reg==NULL) + reg=tiling_current(ws); + + if(reg!=NULL){ + WSplitRegion *node=get_node_check(ws, reg); + if(node!=NULL){ + nxt=node_reg(split_nextto((WSplit*)node, hprimn, vprimn, + filter)); + } + } + + if(nxt==NULL && !nowrap){ + nxt=node_reg(split_current_todir(ws->split_tree, + primn_none2any(primn_invert(hprimn)), + primn_none2any(primn_invert(vprimn)), + filter)); + } + + return nxt; +} + + +WRegion *tiling_do_navi_first(WTiling *ws, WRegionNavi nh, bool any) +{ + WSplitFilter *filter=(any ? NULL : nostdispfilter); + WPrimn hprimn, vprimn; + + navi_to_primn(nh, &hprimn, &vprimn, PRIMN_ANY); + + return node_reg(split_current_todir(ws->split_tree, + hprimn, vprimn, filter)); +} + + +WRegion *tiling_navi_next(WTiling *ws, WRegion *reg, + WRegionNavi nh, WRegionNaviData *data) +{ + WRegion *nxt=tiling_do_navi_next(ws, reg, nh, TRUE, FALSE); + + return region_navi_cont(&ws->reg, nxt, data); +} + + +WRegion *tiling_navi_first(WTiling *ws, WRegionNavi nh, + WRegionNaviData *data) +{ + WRegion *reg=tiling_do_navi_first(ws, nh, FALSE); + + return region_navi_cont(&ws->reg, reg, data); +} + + +/*}}}*/ + + +/*{{{ Split/unsplit */ + + +static bool get_split_dir_primn(const char *str, int *dir, int *primn) +{ + WPrimn hprimn, vprimn; + WRegionNavi nh; + + if(!ioncore_string_to_navi(str, &nh)) + return FALSE; + + navi_to_primn(nh, &hprimn, &vprimn, PRIMN_NONE); + + if(hprimn==PRIMN_NONE){ + *dir=SPLIT_VERTICAL; + *primn=vprimn; + }else if(vprimn==PRIMN_NONE){ + *dir=SPLIT_HORIZONTAL; + *primn=hprimn; + }else{ + warn(TR("Invalid direction")); + return FALSE; + } + + return TRUE; +} + + +static bool get_split_dir_primn_float(const char *str, int *dir, int *primn, + bool *floating) +{ + if(strncmp(str, "floating:", 9)==0){ + *floating=TRUE; + return get_split_dir_primn(str+9, dir, primn); + }else{ + *floating=FALSE; + return get_split_dir_primn(str, dir, primn); + } +} + + +#define SPLIT_MINS 16 /* totally arbitrary */ + + +static WFrame *tiling_do_split(WTiling *ws, WSplit *node, + const char *dirstr, int minw, int minh) +{ + int dir, primn, mins; + bool floating=FALSE; + WFrame *newframe; + WSplitRegion *nnode; + + if(node==NULL || ws->split_tree==NULL){ + warn(TR("Invalid node.")); + return NULL; + } + + if(!get_split_dir_primn_float(dirstr, &dir, &primn, &floating)) + return NULL; + + mins=(dir==SPLIT_VERTICAL ? minh : minw); + + if(!floating){ + nnode=splittree_split(node, dir, primn, mins, + ws->create_frame_fn, + REGION_PARENT(ws)); + }else{ + nnode=splittree_split_floating(node, dir, primn, mins, + ws->create_frame_fn, ws); + } + + if(nnode==NULL){ + warn(TR("Unable to split.")); + return NULL; + } + + /* We must restack here to ensure the split tree is stacked in the + * expected order. + */ + if(ws->split_tree!=NULL) + split_restack(ws->split_tree, ws->dummywin, Above); + + newframe=OBJ_CAST(nnode->reg, WFrame); + assert(newframe!=NULL); + + if(!tiling_managed_add(ws, nnode->reg)){ + nnode->reg=NULL; + destroy_obj((Obj*)nnode); + destroy_obj((Obj*)newframe); + return NULL; + } + + /* Restack */ + if(ws->split_tree!=NULL) + split_restack(ws->split_tree, ws->dummywin, Above); + + return newframe; +} + + +/*EXTL_DOC + * Create a new frame on \var{ws} above/below/left of/right of + * \var{node} as indicated by \var{dirstr}. If \var{dirstr} is + * prefixed with ''floating:'' a floating split is created. + */ +EXTL_EXPORT_MEMBER +WFrame *tiling_split(WTiling *ws, WSplit *node, const char *dirstr) +{ + if(!check_node(ws, node)) + return NULL; + + return tiling_do_split(ws, node, dirstr, + SPLIT_MINS, SPLIT_MINS); +} + + +/*EXTL_DOC + * Same as \fnref{WTiling.split} at the root of the split tree. + */ +EXTL_EXPORT_MEMBER +WFrame *tiling_split_top(WTiling *ws, const char *dirstr) +{ + return tiling_do_split(ws, ws->split_tree, dirstr, + SPLIT_MINS, SPLIT_MINS); +} + + +/*EXTL_DOC + * Split \var{frame} creating a new frame to direction \var{dirstr} + * (one of ''left'', ''right'', ''top'' or ''bottom'') of \var{frame}. + * If \var{attach_current} is set, the region currently displayed in + * \var{frame}, if any, is moved to thenew frame. + * If \var{dirstr} is prefixed with ''floating:'' a floating split is + * created. + */ +EXTL_EXPORT_MEMBER +WFrame *tiling_split_at(WTiling *ws, WFrame *frame, const char *dirstr, + bool attach_current) +{ + WRegion *curr; + WSplitRegion *node; + WFrame *newframe; + + if(frame==NULL) + return NULL; + + node=get_node_check(ws, (WRegion*)frame); + + newframe=tiling_do_split(ws, (WSplit*)node, dirstr, + region_min_w((WRegion*)frame), + region_min_h((WRegion*)frame)); + + if(newframe==NULL) + return NULL; + + curr=mplex_mx_current(&(frame->mplex)); + + if(attach_current && curr!=NULL) + mplex_attach_simple(&(newframe->mplex), curr, MPLEX_ATTACH_SWITCHTO); + + if(region_may_control_focus((WRegion*)frame)) + region_goto((WRegion*)newframe); + + return newframe; +} + + +/*EXTL_DOC + * Try to relocate regions managed by \var{frame} to another frame + * and, if possible, destroy the frame. + */ +EXTL_EXPORT_MEMBER +bool tiling_unsplit_at(WTiling *ws, WFrame *frame) +{ + if(frame==NULL){ + warn(TR("Nil frame.")); + return FALSE; + } + + if(REGION_MANAGER(frame)!=(WRegion*)ws){ + warn(TR("The frame is not managed by the workspace.")); + return FALSE; + } + + return region_rqclose((WRegion*)frame, TRUE); +} + + +/*}}}*/ + + +/*{{{ Navigation etc. exports */ + + +WRegion *tiling_current(WTiling *ws) +{ + WSplitRegion *node=NULL; + if(ws->split_tree!=NULL){ + node=(WSplitRegion*)split_current_todir(ws->split_tree, + PRIMN_ANY, PRIMN_ANY, NULL); + } + return (node ? node->reg : NULL); +} + + +/*EXTL_DOC + * Returns a list of regions managed by the workspace (frames, mostly). + */ +EXTL_SAFE +EXTL_EXPORT_MEMBER +ExtlTab tiling_managed_list(WTiling *ws) +{ + PtrListIterTmp tmp; + + ptrlist_iter_init(&tmp, ws->managed_list); + + return extl_obj_iterable_to_table((ObjIterator*)ptrlist_iter, &tmp); +} + + +/*EXTL_DOC + * Returns the root of the split tree. + */ +EXTL_SAFE +EXTL_EXPORT_MEMBER +WSplit *tiling_split_tree(WTiling *ws) +{ + return ws->split_tree; +} + + +/*EXTL_DOC + * Return the most previously active region next to \var{reg} in + * direction \var{dirstr} (left/right/up/down). The region \var{reg} + * must be managed by \var{ws}. If \var{any} is not set, the status display + * is not considered. + */ +EXTL_SAFE +EXTL_EXPORT_MEMBER +WRegion *tiling_nextto(WTiling *ws, WRegion *reg, const char *dirstr, + bool any) +{ + WRegionNavi nh; + + if(!ioncore_string_to_navi(dirstr, &nh)) + return NULL; + + return tiling_do_navi_next(ws, reg, nh, FALSE, any); +} + + +/*EXTL_DOC + * Return the most previously active region on \var{ws} with no + * other regions next to it in direction \var{dirstr} + * (left/right/up/down). If \var{any} is not set, the status + * display is not considered. + */ +EXTL_SAFE +EXTL_EXPORT_MEMBER +WRegion *tiling_farthest(WTiling *ws, const char *dirstr, bool any) +{ + WRegionNavi nh; + + if(!ioncore_string_to_navi(dirstr, &nh)) + return NULL; + + return tiling_do_navi_first(ws, nh, any); +} + + +/*EXTL_DOC + * For region \var{reg} managed by \var{ws} return the \type{WSplit} + * a leaf of which \var{reg} is. + */ +EXTL_SAFE +EXTL_EXPORT_MEMBER +WSplitRegion *tiling_node_of(WTiling *ws, WRegion *reg) +{ + if(reg==NULL){ + warn(TR("Nil parameter.")); + return NULL; + } + + if(REGION_MANAGER(reg)!=(WRegion*)ws){ + warn(TR("Manager doesn't match.")); + return NULL; + } + + return splittree_node_of(reg); +} + + +/*}}}*/ + + +/*{{{ Flip and transpose */ + + +static WSplitSplit *get_at_split(WTiling *ws, WRegion *reg) +{ + WSplit *node; + WSplitSplit *split; + + if(reg==NULL){ + split=OBJ_CAST(ws->split_tree, WSplitSplit); + if(split==NULL) + return NULL; + else if(split->br==(WSplit*)ws->stdispnode) + return OBJ_CAST(split->tl, WSplitSplit); + else if(split->tl==(WSplit*)ws->stdispnode) + return OBJ_CAST(split->br, WSplitSplit); + else + return split; + } + + node=(WSplit*)get_node_check(ws, reg); + + if(node==NULL) + return NULL; + + if(node==(WSplit*)ws->stdispnode){ + warn(TR("The status display is not a valid parameter for " + "this routine.")); + return NULL; + } + + split=OBJ_CAST(node->parent, WSplitSplit); + + if(split!=NULL && (split->tl==(WSplit*)ws->stdispnode || + split->br==(WSplit*)ws->stdispnode)){ + split=OBJ_CAST(((WSplit*)split)->parent, WSplitSplit); + } + + return split; +} + + +/*EXTL_DOC + * Flip \var{ws} at \var{reg} or root if nil. + */ +EXTL_EXPORT_MEMBER +bool iowns_flip_at(WTiling *ws, WRegion *reg) +{ + WSplitSplit *split=get_at_split(ws, reg); + + if(split==NULL){ + return FALSE; + }else{ + splitsplit_flip(split); + return TRUE; + } +} + + +/*EXTL_DOC + * Transpose \var{ws} at \var{reg} or root if nil. + */ +EXTL_EXPORT_MEMBER +bool iowns_transpose_at(WTiling *ws, WRegion *reg) +{ + WSplitSplit *split=get_at_split(ws, reg); + + if(split==NULL){ + return FALSE; + }else{ + split_transpose((WSplit*)split); + return TRUE; + } +} + + +/*}}}*/ + + +/*{{{ Floating toggle */ + + +static void replace(WSplitSplit *split, WSplitSplit *nsplit) +{ + WSplitInner *psplit=split->isplit.split.parent; + + nsplit->tl=split->tl; + split->tl=NULL; + nsplit->tl->parent=(WSplitInner*)nsplit; + + nsplit->br=split->br; + split->br=NULL; + nsplit->br->parent=(WSplitInner*)nsplit; + + if(psplit!=NULL){ + splitinner_replace((WSplitInner*)psplit, (WSplit*)split, + (WSplit*)nsplit); + }else{ + splittree_changeroot((WSplit*)split, (WSplit*)nsplit); + } +} + + +WSplitSplit *tiling_set_floating(WTiling *ws, WSplitSplit *split, int sp) +{ + bool set=OBJ_IS(split, WSplitFloat); + bool nset=libtu_do_setparam(sp, set); + const WRectangle *g=&((WSplit*)split)->geom; + WSplitSplit *ns; + + if(!XOR(nset, set)) + return split; + + if(nset){ + ns=(WSplitSplit*)create_splitfloat(g, ws, split->dir); + }else{ + if(OBJ_IS(split->tl, WSplitST) || OBJ_IS(split->br, WSplitST)){ + warn(TR("Refusing to float split directly containing the " + "status display.")); + return NULL; + } + ns=create_splitsplit(g, split->dir); + } + + if(ns!=NULL){ + replace(split, ns); + split_resize((WSplit*)ns, g, PRIMN_ANY, PRIMN_ANY); + mainloop_defer_destroy((Obj*)split); + } + + return ns; +} + + +/*EXTL_DOC + * Toggle floating of a split's sides at \var{split} as indicated by the + * parameter \var{how} (set/unset/toggle). A split of the appropriate is + * returned, if there was a change. + */ +EXTL_EXPORT_AS(WTiling, set_floating) +WSplitSplit *tiling_set_floating_extl(WTiling *ws, WSplitSplit *split, + const char *how) +{ + if(!check_node(ws, (WSplit*)split)) + return NULL; + return tiling_set_floating(ws, split, libtu_string_to_setparam(how)); +} + + +/*EXTL_DOC + * Toggle floating of the sides of a split containin \var{reg} as indicated + * by the parameters \var{how} (set/unset/toggle) and \var{dirstr} + * (left/right/up/down/any). The new status is returned (and \code{false} + * also on error). + */ +EXTL_EXPORT_AS(WTiling, set_floating_at) +bool tiling_set_floating_at_extl(WTiling *ws, WRegion *reg, const char *how, + const char *dirstr) +{ + WPrimn hprimn=PRIMN_ANY, vprimn=PRIMN_ANY; + WSplitSplit *split, *nsplit; + WSplit *node; + + node=(WSplit*)get_node_check(ws, reg); + if(node==NULL) + return FALSE; + + + if(dirstr!=NULL){ + WRegionNavi nh; + + if(!ioncore_string_to_navi(dirstr, &nh)) + return FALSE; + + navi_to_primn(nh, &hprimn, &vprimn, PRIMN_NONE); + } + + while(TRUE){ + split=OBJ_CAST(node->parent, WSplitSplit); + if(split==NULL){ + warn(TR("No suitable split here.")); + return FALSE; + } + + if(!OBJ_IS(split->tl, WSplitST) && !OBJ_IS(split->br, WSplitST)){ + WPrimn tmp=(split->dir==SPLIT_VERTICAL ? vprimn : hprimn); + if(tmp==PRIMN_ANY + || (node==split->tl && tmp==PRIMN_BR) + || (node==split->br && tmp==PRIMN_TL)){ + break; + } + } + + node=(WSplit*)split; + } + + nsplit=tiling_set_floating(ws, split, libtu_string_to_setparam(how)); + + return OBJ_IS((Obj*)(nsplit==NULL ? split : nsplit), WSplitFloat); +} + + +/*}}}*/ + + +/*{{{ Save */ + + +ExtlTab tiling_get_configuration(WTiling *ws) +{ + ExtlTab tab, split_tree=extl_table_none(); + + tab=region_get_base_configuration((WRegion*)ws); + + if(ws->split_tree!=NULL){ + if(!split_get_config(ws->split_tree, &split_tree)) + warn(TR("Could not get split tree.")); + } + + extl_table_sets_t(tab, "split_tree", split_tree); + extl_unref_table(split_tree); + + return tab; +} + + +/*}}}*/ + + +/*{{{ Load */ + + +WSplit *load_splitst(WTiling *ws, const WRectangle *geom, ExtlTab tab) +{ + WSplitST *st; + + if(ws->stdispnode!=NULL){ + warn(TR("Workspace already has a status display node.")); + return NULL; + } + + st=create_splitst(geom, NULL); + ws->stdispnode=st; + return (WSplit*)st; +} + + +static bool do_attach(WTiling *ws, WRegion *reg, void *p) +{ + WSplitRegion *node=create_splitregion(®ION_GEOM(reg), reg); + + if(node==NULL) + return FALSE; + + if(!tiling_managed_add(ws, reg)){ + node->reg=NULL; + destroy_obj((Obj*)node); + return FALSE; + } + + *(WSplitRegion**)p=node; + + return TRUE; +} + + +WSplit *load_splitregion(WTiling *ws, const WRectangle *geom, ExtlTab tab) +{ + WWindow *par=REGION_PARENT(ws); + WRegionAttachData data; + WSplit *node=NULL; + WFitParams fp; + ExtlTab rt; + + if(!extl_table_gets_t(tab, "regparams", &rt)){ + warn(TR("Missing region parameters.")); + return NULL; + } + + data.type=REGION_ATTACH_LOAD; + data.u.tab=rt; + + assert(par!=NULL); + fp.g=*geom; + fp.mode=REGION_FIT_EXACT; + + region_attach_helper((WRegion*)ws, par, &fp, + (WRegionDoAttachFn*)do_attach, &node, &data); + + extl_unref_table(rt); + + return node; +} + + +#define MINS 1 + +WSplit *load_splitsplit(WTiling *ws, const WRectangle *geom, ExtlTab tab) +{ + WSplit *tl=NULL, *br=NULL; + WSplitSplit *split; + char *dir_str; + int dir, brs, tls; + ExtlTab subtab; + WRectangle geom2; + int set=0; + + set+=(extl_table_gets_i(tab, "tls", &tls)==TRUE); + set+=(extl_table_gets_i(tab, "brs", &brs)==TRUE); + set+=(extl_table_gets_s(tab, "dir", &dir_str)==TRUE); + + if(set!=3) + return NULL; + + if(strcmp(dir_str, "vertical")==0){ + dir=SPLIT_VERTICAL; + }else if(strcmp(dir_str, "horizontal")==0){ + dir=SPLIT_HORIZONTAL; + }else{ + warn(TR("Invalid direction.")); + free(dir_str); + return NULL; + } + free(dir_str); + + split=create_splitsplit(geom, dir); + if(split==NULL) + return NULL; + + tls=maxof(tls, MINS); + brs=maxof(brs, MINS); + + geom2=*geom; + if(dir==SPLIT_HORIZONTAL){ + tls=maxof(0, geom->w)*tls/(tls+brs); + geom2.w=tls; + }else{ + tls=maxof(0, geom->h)*tls/(tls+brs); + geom2.h=tls; + } + + if(extl_table_gets_t(tab, "tl", &subtab)){ + tl=tiling_load_node(ws, &geom2, subtab); + extl_unref_table(subtab); + } + + geom2=*geom; + if(dir==SPLIT_HORIZONTAL){ + geom2.w-=tls; + geom2.x+=tls; + }else{ + geom2.h-=tls; + geom2.y+=tls; + } + + if(extl_table_gets_t(tab, "br", &subtab)){ + br=tiling_load_node(ws, &geom2, subtab); + extl_unref_table(subtab); + } + + if(tl==NULL || br==NULL){ + /* PRIMN_TL/BR instead of ANY because of stdisp. */ + destroy_obj((Obj*)split); + if(tl!=NULL){ + split_do_resize(tl, geom, PRIMN_BR, PRIMN_BR, FALSE); + return tl; + } + if(br!=NULL){ + split_do_resize(br, geom, PRIMN_TL, PRIMN_TL, FALSE); + return br; + } + return NULL; + } + + tl->parent=(WSplitInner*)split; + br->parent=(WSplitInner*)split; + + /*split->tmpsize=tls;*/ + split->tl=tl; + split->br=br; + + return (WSplit*)split; +} + + +WSplit *tiling_load_node_default(WTiling *ws, const WRectangle *geom, + ExtlTab tab) +{ + char *typestr=NULL; + WSplit *node=NULL; + + extl_table_gets_s(tab, "type", &typestr); + + if(typestr==NULL){ + warn(TR("No split type given.")); + return NULL; + } + + if(strcmp(typestr, "WSplitRegion")==0) + node=load_splitregion(ws, geom, tab); + else if(strcmp(typestr, "WSplitSplit")==0) + node=load_splitsplit(ws, geom, tab); + else if(strcmp(typestr, "WSplitFloat")==0) + node=load_splitfloat(ws, geom, tab); + else if(strcmp(typestr, "WSplitST")==0) + node=NULL;/*load_splitst(ws, geom, tab);*/ + else + warn(TR("Unknown split type.")); + + free(typestr); + + return node; +} + + +WSplit *tiling_load_node(WTiling *ws, const WRectangle *geom, ExtlTab tab) +{ + WSplit *ret=NULL; + CALL_DYN_RET(ret, WSplit*, tiling_load_node, ws, (ws, geom, tab)); + return ret; +} + + + +WRegion *tiling_load(WWindow *par, const WFitParams *fp, ExtlTab tab) +{ + WTiling *ws; + ExtlTab treetab; + bool ci=TRUE; + + if(extl_table_gets_t(tab, "split_tree", &treetab)) + ci=FALSE; + + ws=create_tiling(par, fp, NULL, ci); + + if(ws==NULL){ + if(!ci) + extl_unref_table(treetab); + return NULL; + } + + if(!ci){ + ws->split_tree=tiling_load_node(ws, ®ION_GEOM(ws), treetab); + extl_unref_table(treetab); + } + + if(ws->split_tree==NULL){ + warn(TR("The workspace is empty.")); + destroy_obj((Obj*)ws); + return NULL; + } + + ws->split_tree->ws_if_root=ws; + split_restack(ws->split_tree, ws->dummywin, Above); + + return (WRegion*)ws; +} + + +/*}}}*/ + + +/*{{{ Dynamic function table and class implementation */ + + +static DynFunTab tiling_dynfuntab[]={ + {region_map, + tiling_map}, + + {region_unmap, + tiling_unmap}, + + {region_do_set_focus, + tiling_do_set_focus}, + + {(DynFun*)region_fitrep, + (DynFun*)tiling_fitrep}, + + {region_managed_rqgeom, + tiling_managed_rqgeom}, + + {region_managed_remove, + tiling_managed_remove}, + + {(DynFun*)region_managed_prepare_focus, + (DynFun*)tiling_managed_prepare_focus}, + + {(DynFun*)region_prepare_manage, + (DynFun*)tiling_prepare_manage}, + + {(DynFun*)region_rescue_clientwins, + (DynFun*)tiling_rescue_clientwins}, + + {(DynFun*)region_get_rescue_pholder_for, + (DynFun*)tiling_get_rescue_pholder_for}, + + {(DynFun*)region_get_configuration, + (DynFun*)tiling_get_configuration}, + + {(DynFun*)region_managed_may_destroy, + (DynFun*)tiling_managed_may_destroy}, + + {(DynFun*)region_may_destroy, + (DynFun*)tiling_may_destroy}, + + {(DynFun*)region_current, + (DynFun*)tiling_current}, + + {(DynFun*)tiling_managed_add, + (DynFun*)tiling_managed_add_default}, + + {region_manage_stdisp, + tiling_manage_stdisp}, + + {region_unmanage_stdisp, + tiling_unmanage_stdisp}, + + {(DynFun*)tiling_load_node, + (DynFun*)tiling_load_node_default}, + + {region_restack, + tiling_restack}, + + {region_stacking, + tiling_stacking}, + + {(DynFun*)region_navi_first, + (DynFun*)tiling_navi_first}, + + {(DynFun*)region_navi_next, + (DynFun*)tiling_navi_next}, + + {(DynFun*)region_xwindow, + (DynFun*)tiling_xwindow}, + + {(DynFun*)region_prepare_manage_transient, + (DynFun*)tiling_prepare_manage_transient}, + + END_DYNFUNTAB +}; + + +EXTL_EXPORT +IMPLCLASS(WTiling, WRegion, tiling_deinit, tiling_dynfuntab); + + +/*}}}*/ + diff --git a/mod_tiling/tiling.h b/mod_tiling/tiling.h new file mode 100644 index 0000000..5b7a8e7 --- /dev/null +++ b/mod_tiling/tiling.h @@ -0,0 +1,127 @@ +/* + * ion/mod_tiling/tiling.h + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#ifndef ION_MOD_TILING_TILING_H +#define ION_MOD_TILING_TILING_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include "split.h" + + +INTRCLASS(WTiling); +DECLCLASS(WTiling){ + WRegion reg; + WSplit *split_tree; + WSplitST *stdispnode; + PtrList *managed_list; + WRegionSimpleCreateFn *create_frame_fn; + Window dummywin; +}; + + +extern bool tiling_init(WTiling *ws, WWindow *parent, const WFitParams *fp, + WRegionSimpleCreateFn *create_frame_fn, bool ci); +extern WTiling *create_tiling(WWindow *parent, const WFitParams *fp, + WRegionSimpleCreateFn *create_frame_fn, bool ci); +extern WTiling *create_tiling_simple(WWindow *parent, const WFitParams *fp); +extern void tiling_deinit(WTiling *ws); + +extern ExtlTab tiling_resize_tree(WTiling *ws, WSplit *node, ExtlTab g); + +extern WRegion *tiling_current(WTiling *ws); +extern WRegion *tiling_nextto(WTiling *ws, WRegion *reg, const char *str, bool any); +extern WRegion *tiling_farthest(WTiling *ws, const char *str, bool any); +extern WRegion *tiling_region_at(WTiling *ws, int x, int y); + +extern WFrame *tiling_split_top(WTiling *ws, const char *dirstr); +extern WFrame *tiling_split_at(WTiling *ws, WFrame *frame, + const char *dirstr, bool attach_current); +extern bool tiling_unsplit_at(WTiling *ws, WFrame *frame); + +extern WSplitSplit *tiling_set_floating(WTiling *ws, WSplitSplit *split, + int sp); + +extern WSplit *tiling_split_tree(WTiling *ws); +extern WSplit *tiling_split_of(WTiling *ws, WRegion *reg); + +extern void tiling_do_managed_remove(WTiling *ws, WRegion *reg); + +DYNFUN bool tiling_managed_add(WTiling *ws, WRegion *reg); +extern bool tiling_managed_add_default(WTiling *ws, WRegion *reg); + +extern WRegion *tiling_do_navi_next(WTiling *ws, WRegion *reg, + WRegionNavi nh, bool nowrap, + bool any); +extern WRegion *tiling_do_navi_first(WTiling *ws, WRegionNavi nh, + bool any); +extern WRegion *tiling_navi_next(WTiling *ws, WRegion *reg, + WRegionNavi nh, WRegionNaviData *data); +extern WRegion *tiling_navi_first(WTiling *ws, WRegionNavi nh, + WRegionNaviData *data); + +/* Inherited dynfun implementations */ + +extern bool tiling_fitrep(WTiling *ws, WWindow *par, const WFitParams *fp); +extern void tiling_map(WTiling *ws); +extern void tiling_unmap(WTiling *ws); +extern ExtlTab tiling_get_configuration(WTiling *ws); +extern void tiling_managed_rqgeom(WTiling *ws, WRegion *reg, + const WRQGeomParams *rq, + WRectangle *geomret); +extern void tiling_managed_remove(WTiling *ws, WRegion *reg); +extern void tiling_managed_activated(WTiling *ws, WRegion *reg); +extern bool tiling_rescue_clientwins(WTiling *ws, WPHolder *ph); +extern WPHolder *tiling_get_rescue_pholder_for(WTiling *ws, WRegion *mgd); +extern void tiling_do_set_focus(WTiling *ws, bool warp); +extern bool tiling_managed_prepare_focus(WTiling *ws, WRegion *reg, + int flags, WPrepareFocusResult *res); +extern bool tiling_managed_may_destroy(WTiling *ws, WRegion *reg); +extern void tiling_manage_stdisp(WTiling *ws, WRegion *stdisp, + const WMPlexSTDispInfo *di); +extern void tiling_unmanage_stdisp(WTiling *ws, bool permanent, bool nofocus); + +extern void tiling_fallback_focus(WTiling *ws, bool warp); + +/* Loading */ + +/* Stupid C can't handle recursive 'WSplitLoadFn *fn' here, so we have + * to use the void pointer. + */ +typedef WSplit *WSplitLoadFn(WTiling *ws, const WRectangle *geom, ExtlTab tab); + +extern WRegion *tiling_load(WWindow *par, const WFitParams *fp, ExtlTab tab); + +DYNFUN WSplit *tiling_load_node(WTiling *ws, const WRectangle *geom, ExtlTab tab); +extern WSplit *tiling_load_node_default(WTiling *ws, const WRectangle *geom, ExtlTab tab); + +extern WSplit *load_splitregion(WTiling *ws, const WRectangle *geom, ExtlTab tab); +extern WSplit *load_splitsplit(WTiling *ws, const WRectangle *geom, ExtlTab tab); +extern WSplit *load_splitst(WTiling *ws, const WRectangle *geom, ExtlTab tab); + +/* Iteration */ + +typedef PtrListIterTmp WTilingIterTmp; + +#define FOR_ALL_MANAGED_BY_TILING(VAR, WS, TMP) \ + FOR_ALL_ON_PTRLIST(WRegion*, VAR, (WS)->managed_list, TMP) + +#define FOR_ALL_MANAGED_BY_TILING_UNSAFE(VAR, WS) \ + FOR_ALL_ON_PTRLIST_UNSAFE(WRegion*, VAR, (WS)->managed_list) + + +#endif /* ION_MOD_TILING_TILING_H */ diff --git a/modulelist.mk b/modulelist.mk new file mode 100644 index 0000000..4339987 --- /dev/null +++ b/modulelist.mk @@ -0,0 +1,11 @@ +## +## List of modules to build +## + +MODULE_LIST = mod_tiling mod_query mod_menu \ + mod_dock mod_sp mod_sm de \ + mod_mgmtmode mod_statusbar + +# Modules to -dlpreload into pwm if statically linking. + +PWM_MODULE_LIST := $(MODULE_LIST) diff --git a/po/Makefile b/po/Makefile new file mode 100644 index 0000000..2339738 --- /dev/null +++ b/po/Makefile @@ -0,0 +1,96 @@ +## +## Ion po Makefile +## + +# System-specific configuration is in system.mk +TOPDIR=.. +include $(TOPDIR)/build/system-inc.mk + +XGETTEXT = xgettext +#MSGFMT = msgfmt -c +MSGFMT = msgfmt +MSGMERGE = msgmerge +MSGCAT = msgcat +LXGETTEXT = lua-xgettext + +TRANSLATIONS := fi cs de ru +MO_FILES := $(patsubst %,%.mo, $(TRANSLATIONS)) +PO_FILES := $(patsubst %,%.po, $(TRANSLATIONS)) + +POTDIRS=../ioncore \ + ../mod_tiling \ + ../mod_query \ + ../mod_menu \ + ../mod_sm \ + ../mod_sp \ + ../mod_statusbar \ + ../mod_mgmtmode \ + ../de \ + ../ion \ + ../pwm \ + ../etc \ + $(LIBEXTL_DIR) + +EXTRA_POTFILES_LUA=../build/mkman.lua + +POTFILE=ion3.pot + +TARGETS = $(MO_FILES) + +ifndef LOCALEDIR +LOCALEDIR := $(PREFIX)/share/locale +endif + +###################################### + +include $(TOPDIR)/build/rules.mk + +###################################### + +potfiles: potdirs_potfiles + (for i in $(POTDIRS); do cat $$i/potfiles_c|sed "s:\w\+:$$i/&:"; done) > potfiles_c + (for i in $(POTDIRS); do cat $$i/potfiles_lua|sed "s:\w\+:$$i/&:"; done; \ + for i in $(EXTRA_POTFILES_LUA); do echo $$i; done) > potfiles_lua + +potdirs_potfiles: + for i in $(POTDIRS); do make -C $$i _potfiles; done + +pot: $(POTFILE) + +$(POTFILE)_c: potfiles + $(XGETTEXT) -kTR -kDUMMY_TR -o $@ -f potfiles_c + +$(POTFILE)_lua: potfiles + $(LXGETTEXT) -k TR -k bdoc -k submenu -k menuentry -o $@ \ + `cat potfiles_lua` + +$(POTFILE): $(POTFILE)_c $(POTFILE)_lua + # + # GNU gettext sucks bigtime, and refuses to work on POT + # files without encoding set. Therefore we'll just have to + # use plain old cat and hope that there aren't dupes. + # + #msgcat -o $@ $+ + # + cat $+ > $@ + +%.mo: %.po + $(MSGFMT) -o $@ $< + +_install: + for i in $(TRANSLATIONS); do \ + $(INSTALLDIR) $(LOCALEDIR)/$$i/LC_MESSAGES ; \ + $(INSTALL) -m $(DATA_MODE) $$i.mo $(LOCALEDIR)/$$i/LC_MESSAGES/ion3.mo ; \ + done + +update_fi: pot + $(MSGMERGE) -U fi.po $(POTFILE) + +update_cs: pot + $(MSGMERGE) -U cs.po $(POTFILE) + +update_ru: pot + $(MSGMERGE) -U ru.po $(POTFILE) + +update_de: pot + $(MSGMERGE) -U de.po $(POTFILE) diff --git a/po/README b/po/README new file mode 100644 index 0000000..ad9e729 --- /dev/null +++ b/po/README @@ -0,0 +1,14 @@ + +To generate the `ion3.pot` file needed for new translations, you +will need `lua-xgettext`, which you can download by executing + + darcs get http://modeemi.fi/~tuomov/repos/lua-xgettext/ + +Build and install it as instructed in its README. Then, `make pot` +in this directory, copy `ion3.pot` over as `$LANG.po`, and start +translating. To update/merge new strings to an existing translation, +run `make update_$LANG`, after you have first copy-pasted this +Makefile target, when it does not already exist. + +Translations for the manual page and welcome message live in +`../man/`. diff --git a/po/cs.po b/po/cs.po new file mode 100644 index 0000000..41235cf --- /dev/null +++ b/po/cs.po @@ -0,0 +1,1716 @@ +# +# Czech language translations for Ion3. +# +# Copyright (c) Miroslav Kure 2004,2005,2006. +# +# This file is distributed under the same license as the Ion3 package. +# +msgid "" +msgstr "" +"Project-Id-Version: Ion3\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2006-11-12 12:07+0100\n" +"PO-Revision-Date: 2006-11-12 12:16+0100\n" +"Last-Translator: Miroslav Kure \n" +"Language-Team: none\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=ISO-8859-2\n" +"Content-Transfer-Encoding: 8bit\n" + +#: ../ioncore/conf-bindings.c:96 +msgid "Insane key combination." +msgstr "©ílená klávesová kombinace." + +#: ../ioncore/conf-bindings.c:100 +msgid "Could not convert keysym to keycode." +msgstr "Nemohu pøevést keysym na keycode." + +#: ../ioncore/conf-bindings.c:111 +#, c-format +msgid "Unknown button \"%s\"." +msgstr "Neznámé tlaèítko \"%s\"." + +#: ../ioncore/conf-bindings.c:116 +msgid "Insane button combination." +msgstr "©ílená kombinace tlaèítek." + +#: ../ioncore/conf-bindings.c:123 ../ioncore/conf-bindings.c:130 +msgid "Insane modifier combination." +msgstr "©ílená kombinace modifikátorù." + +#: ../ioncore/conf-bindings.c:168 +#, c-format +msgid "Can not wait on modifiers when no modifiers set in \"%s\"." +msgstr "Nemohu èekat na modifikátory, kdy¾ ¾ádné nebyly nastaveny v \"%s\"." + +#: ../ioncore/conf-bindings.c:186 +#, c-format +msgid "Unable to add binding %s." +msgstr "Nemohu pøidat vazbu %s." + +#: ../ioncore/conf-bindings.c:191 +#, c-format +msgid "Unable to remove binding %s." +msgstr "Nemohu odstranit vazbu %s." + +#: ../ioncore/conf-bindings.c:230 +#, c-format +msgid "Unable to add submap for binding %s." +msgstr "Nemohu pøidat **** pro vazbu %s." + +# +#: ../ioncore/conf-bindings.c:260 +msgid "Binding type not set." +msgstr "Typ vazby není nastaven." + +#: ../ioncore/conf-bindings.c:270 +#, c-format +msgid "Unknown binding type \"%s\"." +msgstr "Neznámý typ vazby \"%s\"." + +#: ../ioncore/conf-bindings.c:292 +#, c-format +msgid "Unknown area \"%s\" for binding %s." +msgstr "Neznámá oblast \"%s\" pro vazbu %s." + +#: ../ioncore/conf-bindings.c:333 +#, c-format +msgid "Unable to get bindmap entry %d." +msgstr "Nemohu získat záznam o vazbì %d." + +#: ../ioncore/conf-bindings.c:375 +msgid "Unable to convert keysym to string." +msgstr "Nemohu pøevést keysym na øetìzec." + +#: ../ioncore/conf-bindings.c:389 +msgid "Unable to convert button to string." +msgstr "Nemohu pøevést tlaèítko na øetìzec." + +# +#: ../ioncore/event.c:113 +msgid "Time request from X server failed." +msgstr "Èasový po¾adavek od X serveru selhal." + +# +#: ../ioncore/exec.c:177 +msgid "Not saving state: running under session manager." +msgstr "Neukládám stav: bì¾ím pod správcem sezení." + +#: ../ioncore/strings.c:107 ../ioncore/strings.c:143 ../ioncore/strings.c:176 +msgid "Invalid multibyte string." +msgstr "Neplatný vícebajtový øetìzec." + +#: ../ioncore/strings.c:267 +#, c-format +msgid "Error compiling regular expression: %s" +msgstr "Chyba pøi kompilaci regulárního výrazu: %s" + +#: ../ioncore/modules.c:158 +msgid "Invalid module name." +msgstr "Neplatné jméno modulu." + +# +#: ../ioncore/modules.c:170 +msgid "The module is already loaded." +msgstr "Modul je ji¾ zaveden." + +#: ../ioncore/modules.c:185 +msgid "" +"Module version information not found or version mismatch. Refusing to use." +msgstr "" +"Nemohu najít informace o verzi modulu, nebo verze nesouhlasí. Odmítám modul " +"pou¾ít." + +#: ../ioncore/modules.c:196 +#, c-format +msgid "Unable to initialise module %s." +msgstr "Nemohu inicializovat modul %s." + +#: ../ioncore/modules.c:220 ../libextl/readconfig.c:388 +#, c-format +msgid "Unable to find '%s' on search path." +msgstr "V prohledávané cestì nemohu najít \"%s\"." + +#: ../ioncore/modules.c:291 +msgid "Unknown module." +msgstr "Neznámý modul." + +#: ../ioncore/modules.c:299 +msgid "Unable to initialise module." +msgstr "Nemohu inicializovat modul." + +#: ../ioncore/modules.c:344 +msgid "No module to load given." +msgstr "Nebyl zadán modul pro zavedení." + +#: ../ioncore/property.c:355 ../ioncore/property.c:364 +msgid "Invalid arguments." +msgstr "Neplatné argumenty." + +#: ../ioncore/screen.c:298 +msgid "act: " +msgstr "act: " + +#: ../ioncore/screen.c:481 +msgid "Only workspace may not be destroyed." +msgstr "Poslední pracovní plocha nemù¾e být zru¹ena." + +#: ../ioncore/screen.c:489 +msgid "Screens may not be destroyed." +msgstr "Obrazovky nesmí být znièeny." + +#: ../ioncore/screen.c:525 +msgid "Invalid offset." +msgstr "Neplatné odsazení" + +#: ../ioncore/screen.c:568 +#, c-format +msgid "Unable to create a workspace on screen %d." +msgstr "Nemohu vytvoøit pracovní plochu na obrazovce %d." + +#: ../ioncore/sizehint.c:151 +msgid "Invalid client-supplied width/height increment." +msgstr "Klient zadal neplatný pøírùstek ¹íøky/vý¹ky." + +#: ../ioncore/sizehint.c:159 +msgid "Invalid client-supplied aspect-ratio." +msgstr "Klient zadal neplatný pomìr stran." + +#: ../ioncore/ioncore.c:74 +msgid "" +"This program is free software; you can redistribute it and/or\n" +"modify it under the terms of the GNU Lesser General Public\n" +"License as published by the Free Software Foundation; either\n" +"version 2.1 of the License, or (at your option) any later version.\n" +"\n" +"This program is distributed in the hope that it will be useful,\n" +"but WITHOUT ANY WARRANTY; without even the implied warranty of\n" +"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n" +"Lesser General Public License for more details.\n" +msgstr "" +"Tento program je svobodný software; mù¾ete jej ¹íøit a/nebo upravovat\n" +"podle podmínek licence GNU Lesser General Public Licence verze 2.1 nebo\n" +"(podle va¹eho uvá¾ení) novìj¹í, jak ji publikuje Free Software Foundation.\n" +"\n" +"Tento program je distribuovaný v nadìji, ¾e bude u¾iteèný, ale BEZ\n" +"JAKÉKOLIV ZÁRUKY. Pro více podrobností se podívejte do licence GNU\n" +"Lesser General Public License.\n" + +#: ../ioncore/ioncore.c:155 +msgid "No encoding given in LC_CTYPE." +msgstr "V LC_CTYPE není zadáno kódování." + +#: ../ioncore/ioncore.c:437 +#, c-format +msgid "Could not connect to X display '%s'" +msgstr "Nemohu se pøipojit na X displej \"%s\"" + +#: ../ioncore/ioncore.c:489 +msgid "Could not find a screen to manage." +msgstr "Nemohu najít obrazovku, kterou bych mohl spravovat." + +#: ../ioncore/xic.c:38 +msgid "Failed to open input method." +msgstr "Selhalo otevøení vstupní metody." + +#: ../ioncore/xic.c:43 +msgid "Input method doesn't support any style." +msgstr "Vstupní metoda nepodporuje ¾ádný styl." + +#: ../ioncore/xic.c:58 +msgid "input method doesn't support my preedit type." +msgstr "Vstupní metoda nepodporuje mùj pøedpøipravený typ." + +#: ../ioncore/xic.c:86 +msgid "Failed to create input context." +msgstr "Selhalo vytvoøení vstupního kontextu." + +#: ../ioncore/clientwin.c:411 +#, c-format +msgid "The transient_for hint for \"%s\" points to itself." +msgstr "Nápovìda transient_for pro \"%s\" ukazuje sama na sebe." + +#: ../ioncore/clientwin.c:415 +#, c-format +msgid "" +"Client window \"%s\" has broken transient_for hint. (\"Extended WM hints\" " +"multi-parent brain damage?)" +msgstr "" +"Klientské okno \"%s\" má poru¹enou nápovìdu transient_for. (Schizofrenie " +"více rodièù \"Extended WM hints\"?)" + +#: ../ioncore/clientwin.c:420 +#, c-format +msgid "The transient_for window for \"%s\" is not on the same screen." +msgstr "Okno transient_for pro \"%s\" není na stejné obrazovce." + +#: ../ioncore/clientwin.c:440 ../ioncore/clientwin.c:527 +#: ../ioncore/clientwin.c:1300 +#, c-format +msgid "Window %#x disappeared." +msgstr "Okno %#x zmizelo." + +#: ../ioncore/clientwin.c:547 +msgid "Unable to find a matching root window!" +msgstr "Nemohu najít odpovídající koøenové okno!" + +#: ../ioncore/clientwin.c:582 +#, c-format +msgid "Unable to manage client window %#x." +msgstr "Nemohu spravovat klientské okno %#x." + +#: ../ioncore/clientwin.c:633 +msgid "Changes is WM_TRANSIENT_FOR property are unsupported." +msgstr "Zmìny vlastnosti WM_TRANSIENT_FOR nejsou podporovány." + +# +#: ../ioncore/clientwin.c:809 +msgid "Client does not support the WM_DELETE protocol." +msgstr "Klient nepodporuje protokol WM_DELETE." + +#: ../ioncore/clientwin.c:1306 +msgid "Saved client window does not want to be managed." +msgstr "Ulo¾ené klientské okno nechce být spravováno." + +# +#: ../ioncore/colormap.c:96 +msgid "Unable to store colourmap watch info." +msgstr "Nemohu ulo¾it informace o barevné mapì." + +#: ../ioncore/region.c:46 +msgid "Creating region with negative width or height!" +msgstr "Vytváøím oblast se zápornou ¹íøkou nebo vý¹kou!" + +#: ../ioncore/region.c:94 +#, c-format +msgid "Destroying object \"%s\" with client windows as children." +msgstr "Nièím objekt \"%s\" spoleènì s klientskými okny jako dìtmi." + +#: ../ioncore/region.c:395 +msgid "Failed to rescue some client windows - not closing." +msgstr "Záchrana nìkterých klientských oken selhala - nezavírám." + +#: ../ioncore/attach.c:71 ../ioncore/pholder.c:46 +msgid "Unable to reparent." +msgstr "Nemohu zmìnit rodièe." + +#: ../ioncore/attach.c:157 ../ioncore/frame-pointer.c:282 +#, c-format +msgid "Attempt to make region %s manage its ancestor %s." +msgstr "Pokus, aby oblast %s spravovala svého pøedka %s." + +#: ../ioncore/manage.c:77 +msgid "Unable to find a screen for a new client window." +msgstr "Nemohu najít obrazovku pro nové klientské okno." + +#: ../ioncore/rootwin.c:223 +#, c-format +msgid "Unable to redirect root window events for screen %d." +msgstr "Nemohu pøesmìrovat události koøenového okna na obrazovce %d." + +#: ../ioncore/rootwin.c:319 ../ioncore/rootwin.c:344 +msgid "Xinerama sanity check failed; overlapping screens detected." +msgstr "Kontrola Xineramy selhala: byly nalezeny pøekrývající se obrazovky." + +#: ../ioncore/rootwin.c:326 ../ioncore/rootwin.c:351 +msgid "Xinerama sanity check failed; zero size detected." +msgstr "Kontrola Xineramy selhala: byla nalezena nulová velikost." + +#: ../ioncore/rootwin.c:379 ../ioncore/rootwin.c:397 +msgid "Don't know how to get Xinerama information for multiple X root windows." +msgstr "Nevím, jak zpracovat informace z Xineramy pro více koøenových oken." + +#: ../ioncore/rootwin.c:393 +msgid "Error retrieving Xinerama information." +msgstr "Chyba pøi získávání informaci z Xineramy." + +#: ../ioncore/rootwin.c:432 ../ioncore/rootwin.c:451 +#, c-format +msgid "Unable to setup Xinerama screen %d." +msgstr "Nemohu nastavit obrazovku Xineramy %d." + +#: ../ioncore/rootwin.c:464 +#, c-format +msgid "Unable to setup X screen %d." +msgstr "Nemohu nastavit X obrazovku %d." + +#: ../ioncore/names.c:90 +#, c-format +msgid "Corrupt instance number %s." +msgstr "Poru¹ená instance èíslo %s." + +#: ../ioncore/saveload.c:125 +#, c-format +msgid "Unknown class \"%s\", cannot create region." +msgstr "Neznámá tøída \"%s\", nemohu vytvoøit oblast." + +#: ../ioncore/saveload.c:205 +#, c-format +msgid "" +"There were errors loading layout. Backing up current layout savefile as\n" +"%s.\n" +"If you are _not_ running under a session manager and wish to restore your\n" +"old layout, copy this backup file over the layout savefile found in the\n" +"same directory while Ion is not running and after having fixed your other\n" +"configuration files that are causing this problem. (Maybe a missing\n" +"module?)" +msgstr "" +"Pøi nahrávání rozlo¾ení se vyskytly chyby. Zálohuji aktuální soubor\n" +"s rozlo¾ením jako %s.\n" +"Pokud _nepou¾íváte_ správce sezení a chcete obnovit své staré rozlo¾ení,\n" +"ukonèete Ion a zkopírujte tento zálo¾ní soubor pøes soubor s rozlo¾ením.\n" +"Pøedtím nezapomeòte opravit konfiguraèní soubory, které zpùsobily tyto\n" +"problémy. (Mo¾ná chybìjící modul?)" + +#: ../ioncore/saveload.c:248 +msgid "Unable to get file for layout backup." +msgstr "Nemohu získat soubor pro zálohu rozlo¾ení." + +#: ../ioncore/saveload.c:252 +#, c-format +msgid "Backup file %s already exists." +msgstr "Zálo¾ní soubor %s ji¾ existuje." + +#: ../ioncore/saveload.c:258 +msgid "Failed backup." +msgstr "Selhala záloha." + +# +#: ../ioncore/saveload.c:263 +msgid "Unable to initialise layout on any screen." +msgstr "Na ¾ádné obrazovce nemohu inicializovat rozlo¾ení." + +#: ../ioncore/saveload.c:283 +#, c-format +msgid "Unable to get configuration for screen %d." +msgstr "Nemohu získat nastavení pro obrazovku %d." + +#: ../ioncore/saveload.c:296 +msgid "Unable to save layout." +msgstr "Nemohu ulo¾it rozlo¾ení." + +#: ../ioncore/conf.c:143 +msgid "User directory can not be set." +msgstr "U¾ivatelský adresáø nemù¾e být nastaven." + +#: ../ioncore/conf.c:217 +msgid "Some bindmaps were empty, loading ioncore_efbb." +msgstr "Nìkteré tabulky kláves jsou prázdné, nahrávám ioncore_efbb." + +#: ../ioncore/fullscreen.c:98 +msgid "Failed to enter full screen mode." +msgstr "Selhal pøechod do celoobrazovkového re¾imu." + +#: ../ioncore/fullscreen.c:120 +msgid "" +"Failed to return from full screen mode; remaining manager or parent from " +"previous location refused to manage us." +msgstr "" +"Návrat z celoobrazovkového re¾imu se nezdaøil; zbývající správce nebo rodiè " +"z pøedchozího umístìní nás nechce spravovat." + +#: ../ioncore/mplex.c:172 +msgid "Refusing to destroy - not empty." +msgstr "Odmítám zru¹it - není prázdné." + +# +#: ../ioncore/mplex.c:1678 +msgid "Invalid position setting." +msgstr "Neplatné nastavení pozice." + +# +#: ../ioncore/mplex.c:1718 +msgid "Invalid action setting." +msgstr "Neplatné nastavení akce." + +#: ../ioncore/gr.c:119 +#, c-format +msgid "Drawing engine %s is not registered!" +msgstr "Vykreslovací jednotka %s není registrována!" + +#: ../ioncore/gr.c:138 +#, c-format +msgid "Unable to find brush for style '%s'." +msgstr "Nemohu najít ¹tìtec pro styl \"%s\"." + +#: ../ioncore/gr.c:408 +msgid "No drawing engines loaded, trying \"de\"." +msgstr "Nebyly nahrány ¾ádné vykreslovací jednotky, zkou¹ím \"%s\"." + +#: ../ioncore/group.c:184 ../mod_tiling/tiling.c:96 +#, c-format +msgid "Error reparenting %s." +msgstr "Chyba zmìny rodièe %s." + +#: ../ioncore/group.c:481 ../mod_tiling/tiling.c:668 +msgid "Workspace not empty - refusing to destroy." +msgstr "Pracovní plocha není prázdná - odmítám zru¹it." + +#: ../ioncore/group.c:680 +msgid "'bottom' already set." +msgstr "'bottom' je ji¾ nastaven." + +#: ../ioncore/navi.c:45 +msgid "Invalid parameter." +msgstr "Neplatný parametr" + +#: ../ioncore/navi.c:72 +msgid "Invalid direction parameter." +msgstr "Neplatný parametr smìru." + +#: ../ioncore/group-ws.c:67 +#, c-format +msgid "Unknown placement method \"%s\"." +msgstr "Neznámý zpùsob umístìní \"%s\"." + +# +#: ../mod_tiling/tiling.c:77 +msgid "Split not on workspace." +msgstr "Rozdìlení není na pracovní plo¹e." + +#: ../mod_tiling/tiling.c:373 +msgid "Unable to create a node for status display." +msgstr "Nemohu vytvoøit uzel pro stavový øádek." + +#: ../mod_tiling/tiling.c:386 +msgid "Unable to create new split for status display." +msgstr "Nemohu vytvoøit nové rozdìlení pro stavový øádek." + +#: ../mod_tiling/tiling.c:937 +msgid "Invalid direction" +msgstr "Neplatný smìr" + +# +#: ../mod_tiling/tiling.c:970 ../mod_tiling/split.c:1018 +msgid "Invalid node." +msgstr "Neplatný uzel." + +# +#: ../mod_tiling/tiling.c:989 +msgid "Unable to split." +msgstr "Nemohu rozdìlit." + +#: ../mod_tiling/tiling.c:1092 +msgid "Nil frame." +msgstr "Prázdný rám." + +#: ../mod_tiling/tiling.c:1097 +msgid "The frame is not managed by the workspace." +msgstr "Rám není spravován pracovní plochou." + +#: ../mod_tiling/tiling.c:1196 +msgid "Nil parameter." +msgstr "Prázdný parametr." + +#: ../mod_tiling/tiling.c:1201 +msgid "Manager doesn't match." +msgstr "Správce se neshoduje." + +#: ../mod_tiling/tiling.c:1238 +msgid "The status display is not a valid parameter for this routine." +msgstr "Stavový displej není v této rutinì platným parametrem." + +#: ../mod_tiling/tiling.c:1329 +msgid "Refusing to float split directly containing the status display." +msgstr "Odmítám rozdìlit pøímo prvek obsahující stavový displej." + +#: ../mod_tiling/tiling.c:1392 +msgid "No suitable split here." +msgstr "®ádné vhodné rozdìlení." + +# +#: ../mod_tiling/tiling.c:1428 +msgid "Could not get split tree." +msgstr "Nemohu získat strom rozdìlení" + +#: ../mod_tiling/tiling.c:1449 +msgid "Workspace already has a status display node." +msgstr "Pracovní plocha ji¾ obsahuje stavový displej." + +# +#: ../mod_tiling/tiling.c:1487 +msgid "Missing region parameters." +msgstr "Chybìjící parametry oblasti." + +#: ../mod_tiling/tiling.c:1531 ../mod_tiling/splitfloat.c:780 +msgid "Invalid direction." +msgstr "Neplatný smìr." + +# +#: ../mod_tiling/tiling.c:1606 +msgid "No split type given." +msgstr "Nebyl zadán typ rozdìlení." + +#: ../mod_tiling/tiling.c:1619 +msgid "Unknown split type." +msgstr "Neznámý typ rozdìlení." + +# +# +#: ../mod_tiling/tiling.c:1659 +msgid "The workspace is empty." +msgstr "Pracovní plocha je prázdná." + +#: ../mod_tiling/placement.c:106 +#, c-format +msgid "" +"Ooops... could not find a region to attach client window to on workspace %s." +msgstr "" +"Ooops... na pracovní plo¹e %s nemohu najít oblast, ke které se má pøipojit " +"klientské okno." + +# +#: ../mod_tiling/split.c:524 +msgid "Unable to move the status display out of way." +msgstr "Nemohu pøesunout stavový øádek tak, aby nezavazel." + +#: ../mod_tiling/split.c:937 +msgid "REGION_RQGEOM_TRYONLY unsupported for status display." +msgstr "REGION_RQGEOM_TRYONLY není stavovým displejem podporován." + +# +#: ../mod_tiling/split.c:1082 +msgid "Splitting the status display is not allowed." +msgstr "Rozdìlení stavového displeje není povoleno." + +#: ../mod_tiling/split.c:1113 ../mod_tiling/splitfloat.c:903 +msgid "Unable to split: not enough free space." +msgstr "Nelze rozdìlit: nedostatek volného místa." + +#: ../mod_tiling/split.c:1864 +#, c-format +msgid "Unable to get configuration for %s." +msgstr "Nemohu získat nastavení pro %s." + +#: ../mod_tiling/split-stdisp.c:602 ../mod_tiling/split-stdisp.c:627 +msgid "Status display in bad split configuration." +msgstr "Stavový øádek se nachází v chybné konfiguraci rozdìlení." + +#: ../mod_tiling/split-stdisp.c:667 +msgid "Status display badly located in split tree." +msgstr "Stavový øádek je ve stromu rozdìlení umístìn na ¹patném místì." + +#: ../mod_tiling/ops.c:28 +msgid "Already detached" +msgstr "Ji¾ je odpojen" + +#: ../mod_tiling/ops.c:142 +msgid "Not member of a group" +msgstr "Není èlenem skupiny" + +#: ../mod_tiling/ops.c:147 +msgid "Manager group already has bottom" +msgstr "Øídící skupina ji¾ je na dnì" + +#: ../mod_query/wedln.c:777 +msgid "history" +msgstr "historie" + +#: ../mod_query/fwarn.c:34 +msgid "Error:\n" +msgstr "Chyba:\n" + +# +#: ../mod_menu/menu.c:537 +msgid "Empty menu." +msgstr "Prázdné menu." + +# +#: ../mod_sm/sm.c:110 +msgid "Failed to set session directory." +msgstr "Selhalo nastavení adresáøe s relací." + +#: ../mod_sm/sm_session.c:86 +msgid "Too many ICE connections." +msgstr "Pøíli¹ mnoho ICE spojení." + +#: ../mod_sm/sm_session.c:228 +msgid "Failed to save session state" +msgstr "Selhalo ulo¾ení stavu sezení." + +#: ../mod_sm/sm_session.c:247 +msgid "Failed to request save-yourself-phase2 from session manager." +msgstr "Selhalo vy¾ádání save-yourself-phase2 od správce sezení." + +# +#: ../mod_sm/sm_session.c:296 +msgid "SESSION_MANAGER environment variable not set." +msgstr "Promìnná SESSION_MANAGER není nastavena." + +#: ../mod_sm/sm_session.c:301 +msgid "Session Manager: IceAddConnectionWatch failed." +msgstr "Správce sezení: IceAddConnectionWatch selhalo." + +# +#: ../mod_sm/sm_session.c:326 +msgid "Unable to connect to the session manager." +msgstr "Nemohu se pøipojit ke správci sezení." + +#: ../mod_sp/main.c:85 +msgid "Unable to create scratchpad." +msgstr "Nemohu vytvoøit poznámkový blok." + +#: ../mod_statusbar/main.c:75 +msgid "reading a pipe" +msgstr "ètu rouru" + +#: ../mod_statusbar/main.c:97 +msgid "ion-statusd launch timeout." +msgstr "Èas spou¹tìní ion-statusd vypr¹el." + +#: ../mod_statusbar/main.c:160 +msgid "ion-statusd timed out." +msgstr "Èas ion-statusd vypr¹el." + +#: ../mod_statusbar/statusbar.c:1046 +#, c-format +msgid "[ %date || load: %load ] %filler%systray" +msgstr "[ %date || vytí¾ení: %load ] %filler%systray" + +#: ../de/init.c:42 +#, c-format +msgid "Border attribute %s sanity check failed." +msgstr "Selhala kontrola atributu okraje %s." + +#: ../de/init.c:65 +#, c-format +msgid "Unknown border style \"%s\"." +msgstr "Neznámý styl okraje \"%s\"." + +#: ../de/init.c:97 +#, c-format +msgid "Unable to allocate colour \"%s\"." +msgstr "Nemohu alokovat barvu \"%s\"." + +#: ../de/init.c:160 +#, c-format +msgid "Corrupt substyle table %d." +msgstr "Poru¹ená tabulka stylù %d." + +#: ../de/init.c:193 +#, c-format +msgid "Unknown text alignment \"%s\"." +msgstr "Neznámý textový prvek \"%s\"." + +#: ../de/init.c:263 +#, c-format +msgid "'based_on' for %s points back to the style itself." +msgstr "Èást 'based_on' stylu %s ukazuje sama na sebe." + +#: ../de/init.c:266 +#, c-format +msgid "Unknown base style \"%s\"." +msgstr "Neznámý základní styl \"%s\"." + +#: ../de/font.c:47 +#, c-format +msgid "" +"Fontset for font pattern '%s' implements context dependent drawing, which is " +"unsupported. Expect clutter." +msgstr "" +"Sada fontù pro vzor '%s' implementuje kontextovì závislé kreslení, co¾ není " +"podporováno. Oèekávejte nesmysly." + +#: ../de/font.c:59 +#, c-format +msgid "Could not load font \"%s\", trying \"%s\"" +msgstr "Nemohu nahrát font \"%s\", zkou¹ím \"%s\"." + +#: ../de/font.c:63 +msgid "Failed to load fallback font." +msgstr "Nahrání zálo¾ního fontu selhalo." + +#: ../de/style.c:315 +#, c-format +msgid "Style %s still in use [%d] but the module is being unloaded!" +msgstr "Styl %s se stále pou¾ívá [%d], ale modul se ji¾ ru¹í z pamìti!" + +#: ../ion/ion.c:42 ../pwm/pwm.c:42 +msgid "X display to use" +msgstr "X displej, který se má pou¾ít" + +#: ../ion/ion.c:45 ../pwm/pwm.c:45 +msgid "Configuration file" +msgstr "Konfiguraèní soubor" + +#: ../ion/ion.c:48 ../pwm/pwm.c:48 +msgid "Add directory to search path" +msgstr "Pøidá adresáø do prohledávané cesty" + +#: ../ion/ion.c:51 ../pwm/pwm.c:51 +msgid "Manage default root window/non-Xinerama screen only" +msgstr "Spravuje pouze implicitní koøenové okno/obrazovku bez Xineramy" + +#: ../ion/ion.c:55 +msgid "Use Xinerama screen information (default: 1/yes)" +msgstr "Pou¾ít Xineramu (implicitnì: 1/ano)" + +#: ../ion/ion.c:58 ../pwm/pwm.c:58 +msgid "Ignored: not compiled with Xinerama support" +msgstr "Ignorováno: program je sestaven bez podpory Xineramy" + +#: ../ion/ion.c:62 ../pwm/pwm.c:62 +msgid "Name of session (affects savefiles)" +msgstr "Jméno sezení (ovlivní místo ulo¾ení)" + +#: ../ion/ion.c:65 ../pwm/pwm.c:65 +msgid "Session manager client ID" +msgstr "Klientské ID správce sezení" + +#: ../ion/ion.c:68 ../pwm/pwm.c:68 +msgid "Do not create startup error log and display it with xmessage." +msgstr "" +"Nevytváøet záznam o chybách pøi startu a nezobrazovat jej pomocí xmessage." + +#: ../ion/ion.c:72 ../pwm/pwm.c:72 +msgid "Show this help" +msgstr "Zobrazí tuto nápovìdu" + +#: ../ion/ion.c:75 ../pwm/pwm.c:75 +msgid "Show program version" +msgstr "Zobrazí verzi programu" + +#: ../ion/ion.c:78 ../pwm/pwm.c:78 +msgid "Show about text" +msgstr "Zobrazí nìco o programu" + +#: ../ion/ion.c:93 +msgid "Could not get user configuration file directory." +msgstr "Nemohu získat u¾ivatelùv adresáø s nastavením." + +#: ../ion/ion.c:107 +#, c-format +msgid "%s/welcome.txt" +msgstr "%s/welcome.cs.txt" + +#: ../ion/ion.c:140 ../pwm/pwm.c:87 +#, c-format +msgid "" +"Usage: %s [options]\n" +"\n" +msgstr "" +"Pou¾ití: %s [volby]\n" +"\n" + +#: ../ion/ion.c:200 ../pwm/pwm.c:150 +msgid "Invalid parameter to -xinerama." +msgstr "Neplatný parametr pro -xinerama." + +# +#: ../ion/ion.c:219 ../pwm/pwm.c:169 +msgid "Invalid command line." +msgstr "Neplatná pøíkazová øádka." + +#: ../ion/ion.c:241 +msgid "Ion startup error log:\n" +msgstr "Záznam chyb pøi startu Ionu:\n" + +#: ../ion/ion.c:252 ../pwm/pwm.c:202 +msgid "Refusing to start due to encountered errors." +msgstr "Odmítám se spustit kvùli zaznamenaným chybám." + +#: ../pwm/pwm.c:55 +msgid "Use Xinerama screen information (default: 0/no)" +msgstr "Pou¾ít Xineramu (implicitnì: 0/ne) " + +#: ../pwm/pwm.c:191 +msgid "PWM startup error log:\n" +msgstr "Záznam chyb pøi startu PWM:\n" + +#: ../libextl/readconfig.c:86 +msgid "$HOME not set" +msgstr "Promìnná $HOME není nastavená" + +#: ../libextl/readconfig.c:113 +msgid "User directory not set. Unable to set session directory." +msgstr "U¾ivatelský adresáø není nastaven. Nemohu nastavit adresáø pro sezení." + +#: ../libextl/readconfig.c:254 +#, c-format +msgid "Falling back to %s." +msgstr "Nouzovì pou¾ívám %s." + +#: ../libextl/readconfig.c:474 +#, c-format +msgid "Unable to create session directory \"%s\"." +msgstr "Nemohu vytvoøit adresáø pro sezení \"%s\"." + +#: ../libextl/luaextl.c:117 +msgid "Lua stack full." +msgstr "Lua zásobník je plný." + +# +#: ../libextl/luaextl.c:143 +msgid "Unknown Lua error." +msgstr "Neznámá Lua chyba." + +#: ../libextl/luaextl.c:490 +msgid "Stack trace:" +msgstr "Výpis zásobníku:" + +#: ../libextl/luaextl.c:497 +#, c-format +msgid "" +"\n" +"(Unable to get debug info for level %d)" +msgstr "" +"\n" +"(Nemohu získat ladicí informace pro úroveò %d)" + +#: ../libextl/luaextl.c:515 +msgid "" +"\n" +" [Skipping unnamed C functions.]" +msgstr "" +"\n" +" [Pøeskakuji nepojmenované C funkce.]" + +#: ../libextl/luaextl.c:566 +msgid "Internal error." +msgstr "Interní chyba." + +#: ../libextl/luaextl.c:585 +msgid "Unable to initialize Lua." +msgstr "Nemohu inicializovat Lua." + +#: ../libextl/luaextl.c:1336 +msgid "" +"Too many return values. Use a C compiler that has va_copy to support more." +msgstr "" +"Pøíli¹ mnoho návratových hodnot. Pou¾ijte kompilátor C, který obsahuje " +"va_copy." + +#: ../libextl/luaextl.c:1356 +msgid "Returned dead object." +msgstr "Vrácen mrtvý objekt." + +#: ../libextl/luaextl.c:1359 +#, c-format +msgid "Invalid return value (expected '%c', got lua type \"%s\")." +msgstr "Neplatná návratová hodnota (oèekávána %c, obdr¾en lua typ \"%s\")." + +#: ../libextl/luaextl.c:1395 ../libextl/luaextl.c:1750 +msgid "Stack full." +msgstr "Zásobník je plný." + +#: ../libextl/luaextl.c:1761 +#, c-format +msgid "Argument %d to %s is a dead object." +msgstr "Argument %d pro %s je mrtvý objekt." + +#: ../libextl/luaextl.c:1764 +#, c-format +msgid "" +"Argument %d to %s is of invalid type. (Argument template is '%s', got lua " +"type %s)." +msgstr "" +"Argument %d pro %s má neplatný typ. (©ablona argumentu je '%s', dostal jsem " +"lua typ %s)." + +#: ../libextl/luaextl.c:1827 +msgid "L1 call handler upvalues corrupt." +msgstr "Obsluha volání L1 je poru¹ená." + +#: ../libextl/luaextl.c:1832 +msgid "Called function has been unregistered." +msgstr "Volaná funkce byla byla odregistrována." + +#: ../libextl/luaextl.c:1843 +#, c-format +msgid "Attempt to call an unsafe function \"%s\" in restricted mode." +msgstr "Pokus o volání nebezpeèné funkce \"%s\" v omezeném re¾imu." + +#: ../libextl/luaextl.c:1956 +#, c-format +msgid "" +"Function '%s' has more parameters than the level 1 call handler can handle" +msgstr "Funkce '%s' má víc parametrù, ne¾ lze zpracovat obsluhou v 1. úrovni" + +# +#: ../libextl/luaextl.c:2373 +msgid "Maximal serialisation depth reached." +msgstr "Dosa¾ena maximální hloubka serializace." + +#: ../libextl/luaextl.c:2394 +#, c-format +msgid "Unable to serialise type %s." +msgstr "Nemohu serializovat typ %s." + +#: ../libextl/luaextl.c:2425 +msgid "-- This file has been generated by Ion. Do not edit.\n" +msgstr "-- Tento soubor byl vytvoøen Ionem. Neupravujte jej.\n" + +#: ../libextl/misc.c:17 +#, c-format +msgid "" +"Type checking failed in level 2 call handler for parameter %d (got %s, " +"expected %s)." +msgstr "" +"Kontrola typu selhala ve druhé úrovni obsluhy volání pro parametr %d " +"(obdr¾el %s, oèekával %s)." + +msgid "Grow-D" +msgstr "Rùst dolù" + +msgid "Grow-U" +msgstr "Rùst nahoru" + +msgid "Grow-R" +msgstr "Rùst vpravo" + +msgid "Grow-L" +msgstr "Rùst vlevo" + +msgid "Pos-BR" +msgstr "Pozice PD" + +msgid "Pos-BL" +msgstr "Pozice LD" + +msgid "Pos-TR" +msgstr "Pozice PH" + +msgid "Pos-TL" +msgstr "Pozice LH" + +msgid "Toggle floating dock." +msgstr "Zapne plovoucí dok." + +msgid "Clear the menu's typeahead find buffer." +msgstr "Vyma¾e buffer pro dopøedné hledání v menu." + +msgid "Select next/previous menu entry." +msgstr "V menu vybere dal¹í/pøedchozí polo¾ku." + +# +# +msgid "Activate current menu entry." +msgstr "Aktivuje vybranou polo¾ku v menu." + +msgid "Close the menu." +msgstr "Zavøe menu." + +msgid "New tiling" +msgstr "Vytvoøit dla¾dice" + +msgid "Detach" +msgstr "Odpojit" + +msgid "Transpose" +msgstr "Transponovat" + +msgid "Flip" +msgstr "Obrátit" + +msgid "Split horizontally" +msgstr "Rozdìlit horizontálnì" + +msgid "Split vertically" +msgstr "Rozdìlit vertikálnì" + +msgid "At root" +msgstr "Na koøenu" + +msgid "Below" +msgstr "Dole" + +msgid "Above" +msgstr "Nahoøe" + +msgid "At right" +msgstr "Vpravo" + +msgid "At left" +msgstr "Vlevo" + +msgid "Float split" +msgstr "Plovoucí okraj" + +msgid "Destroy frame" +msgstr "Znièit rám" + +msgid "Tile frame, if no tiling exists on the workspace" +msgstr "" +"Zmìní rám na dla¾dice (pokud zatím dla¾dice na pracovní plo¹e neexistují)." + +msgid "Detach window from tiled frame" +msgstr "Odpojí okno od dla¾dicového rámu" + +msgid "Destroy current frame." +msgstr "Znièí aktuální rám." + +msgid "Split current frame horizontally." +msgstr "Rozdìlí rám horizontálnì." + +msgid "Go to frame above/below/right/left of current frame." +msgstr "Pøejde do rámu nad/pod/vpravo/vlevo od aktuálního rámu." + +msgid "Split current frame vertically." +msgstr "Rozdìlí rám vertikálnì." + +msgid "Failed to create statusbar." +msgstr "Selhalo vytvoøení stavového øádku." + +msgid "Screen already has an stdisp. Refusing to create a statusbar." +msgstr "Obrazovka ji¾ obsahuje stdisp. Odmítám vytvoøit stavový øádek." + +msgid "Screen not found." +msgstr "Obrazovka nebyla nalezna." + +msgid "Failed to start ion-statusd." +msgstr "Selhalo spu¹tìní ion-statusd." + +msgid "Errors starting ion-statusd:\n" +msgstr "Chyba pøi startu ion-statusd:\n" + +msgid "Could not find %s" +msgstr "Nemohu najít %s" + +msgid "ion-statusd quit." +msgstr "ion-statusd konèí." + +msgid "Not a directory." +msgstr "Není adresáøem." + +msgid "Invalid command" +msgstr "Neplatný pøíkaz" + +msgid "Error in command string: " +msgstr "Chyba v pøíkazu: " + +msgid "Error compiling guard: %s" +msgstr "Chyba pøi sestavení strá¾ce: %s" + +msgid "Invalid guard %s." +msgstr "Neplatná ochrana %s." + +msgid "Main menu:" +msgstr "Hlavní menu:" + +msgid "Context menu:" +msgstr "Kontextové menu:" + +msgid "Floating frame" +msgstr "Plovoucí rám" + +msgid "Tiled frame" +msgstr "Dla¾dicový rám" + +msgid "Tiling" +msgstr "Dla¾dice" + +# +msgid "Workspace" +msgstr "Pracovní plocha" + +msgid "Screen" +msgstr "Obrazovka" + +msgid "Frame" +msgstr "Rám" + +msgid "Recursive table - unable to deepcopy" +msgstr "Rekurzivní tabulka - nemohu provést hlubokou kopii" + +msgid "" +"Making the following minimal emergency mappings:\n" +" F2 -> xterm\n" +" F11 -> restart\n" +" F12 -> exit\n" +" Mod1+C -> close\n" +" Mod1+K P/N -> WFrame.switch_next/switch_prev\n" +msgstr "" +"Vytváøím nouzové pøiøazení kláves:\n" +" F2 -> xterm\n" +" F11 -> restart\n" +" F12 -> exit\n" +" Mod1+C -> close\n" +" Mod1+K P/N -> WFrame.switch_next/switch_prev\n" + +msgid "Unable to append to non-table menu" +msgstr "Nelze pøidat do netabulkového menu" + +msgid "Cannot save selection." +msgstr "Nemohu ulo¾it výbìr." + +msgid "Save look selection in %s?" +msgstr "Ulo¾it nastavení vzhledu do %s?" + +msgid "Too much result data" +msgstr "Pøíli¹ mnoho výsledkù" + +msgid "Could not find client window %s." +msgstr "Nemohu najít klientské okno %s." + +msgid "Cannot attach: different root windows." +msgstr "Nemohu pøipojit: rùzná koøenová okna." + +msgid "Unknown error" +msgstr "Neznámá chyba" + +msgid "Go to window:" +msgstr "Jít do okna:" + +msgid "Attach window:" +msgstr "Pøipojit okno:" + +msgid "Go to or create workspace:" +msgstr "Vytvoøit nebo pøejít na pracovní plochu:" + +msgid "Exit Ion/Shutdown session (y/n)?" +msgstr "Ukonèit Ion/ukonèit sezení (y/n)?" + +msgid "Restart Ion (y/n)?" +msgstr "Restartovat Ion (y/n)?" + +msgid "Frame name:" +msgstr "Název rámu:" + +msgid "Workspace name:" +msgstr "Název pracovní plochy:" + +msgid "Edit file:" +msgstr "Upravit soubor:" + +msgid "View file:" +msgstr "Prohlédnout soubor:" + +msgid "Run:" +msgstr "Spustit:" + +msgid "Failed to open ~/.ssh/known_hosts" +msgstr "Selhalo otevøení ~/.ssh/known_hosts" + +msgid "Failed to open ~/.ssh/config" +msgstr "Selhalo otevøení ~/.ssh/config" + +msgid "SSH to:" +msgstr "SSH na:" + +msgid "Manual page (%s):" +msgstr "Manuálová stránka (%s):" + +msgid "Lua code:" +msgstr "Lua kód:" + +msgid "Unknown menu %s." +msgstr "Neznámé menu %s." + +msgid "Missing submenu " +msgstr "Chybìjící podmenu" + +msgid "%s:" +msgstr "%s:" + +msgid "No entry '%s'" +msgstr "®ádný záznam '%s'" + +msgid "" +"\n" +"%sClass: %s\n" +"%sRole: %s\n" +"%sInstance: %s\n" +"%sXID: 0x%x" +msgstr "" +"\n" +"%sTøída: %s\n" +"%sRole: %s\n" +"%sInstance: %s\n" +"%sXID: 0x%x" + +msgid "Toggle scratchpad." +msgstr "Zapne poznámkový blok." + +msgid "Programs" +msgstr "Programy" + +msgid "Lock screen" +msgstr "Zamknout obrazovku" + +msgid "Help" +msgstr "Nápovìda" + +# +msgid "Workspaces" +msgstr "Pracovní plochy" + +msgid "New" +msgstr "Nový" + +msgid "Styles" +msgstr "Styly" + +msgid "Session" +msgstr "Sezení" + +msgid "Close" +msgstr "Zavøít" + +msgid "List" +msgstr "Seznam" + +# +msgid "" +"Switch to n:th object (workspace, full screen client window) within current " +"screen." +msgstr "" +"Pøepne se na n-tý objekt (pracovní plochu, celoobrazovkové okno) na aktuální " +"obrazovce." + +# +msgid "Switch to next/previous object within current screen." +msgstr "Pøepne se na dal¹í/pøedchozí objekt v aktuální obrazovce." + +msgid "Go to first region demanding attention or previously active one." +msgstr "" +"Pøejde do prvního regionu vy¾adujícího pozornost nebo do pøedchozího " +"aktivního." + +# +msgid "Clear all tags." +msgstr "Vyèistí v¹echny znaèky." + +# +msgid "Go to n:th screen on multihead setup." +msgstr "U víceobrazovkového nastavení pøejde na n-tou obrazovku." + +# +msgid "Go to next/previous screen on multihead setup." +msgstr "Ve víceobrazovkovém nastavení pøejde na dal¹í/pøedchozí obrazovku." + +# +# +msgid "Create a new workspace of chosen default type." +msgstr "Vytvoøí novou pracovní plochu vybraného typu." + +msgid "Display the main menu." +msgstr "Zobrazí hlavní menu." + +msgid "Display the window list menu." +msgstr "Zobrazí menu se seznamem oken." + +msgid "Forward-circulate focus." +msgstr "Cykluje vpøed." + +msgid "Backward-circulate focus." +msgstr "Cykluje vzad." + +msgid "Raise focused object, if possible." +msgstr "Pokud je to mo¾né, pøesune zamìøený objekt nahoru." + +# +msgid "" +"Nudge the client window. This might help with some programs' resizing " +"problems." +msgstr "" +"Postrèí klientské okno. To mù¾e pomoci nìkterým programùm, které mají " +"problémy se zmìnou velikosti." + +# +msgid "Kill client owning the client window." +msgstr "Ukonèí klienta vlastnícího klientské okno." + +# +msgid "" +"Send next key press to the client window. Some programs may not allow this " +"by default." +msgstr "" +"Po¹le následující stisk klávesy klientskému oknu. Nìkteré programy to " +"implicitnì nepovolují." + +msgid "Toggle client window group full-screen mode" +msgstr "Pøepne skupinu klientského okna do celoobrazovkového re¾imu." + +msgid "Close current object." +msgstr "Zavøe aktuální objekt." + +msgid "Query for manual page to be displayed." +msgstr "Zeptá se na manuálovou stánku, kterou má zobrazit." + +msgid "Show the Ion manual page." +msgstr "Zobrazí manuálovou stránku Ionu." + +# +msgid "Run a terminal emulator." +msgstr "Spustí emulátor terminálu." + +msgid "Query for command line to execute." +msgstr "Zeptá se na pøíkaz, který má spustit." + +msgid "Query for Lua code to execute." +msgstr "Zeptá se na lua kód, který má vykonat." + +msgid "Query for host to connect to with SSH." +msgstr "Zeptá se na jméno poèítaèe, ke kterému se má pøipojit pomocí SSH." + +msgid "Query for file to edit." +msgstr "Zeptá se na soubor, který chcete upravit." + +msgid "Query for file to view." +msgstr "Zeptá se na soubor, který chcete zobrazit." + +msgid "Query for workspace to go to or create a new one." +msgstr "Zeptá se na pracovní plochu, na kterou má pøejít, nebo ji vytvoøit." + +# +msgid "Query for a client window to go to." +msgstr "Dotá¾e se na klientské okno, do nìho¾ má pøejít." + +# +msgid "Query for a client window to attach." +msgstr "Dotá¾e se na klientské okno, které má pøipojit." + +msgid "Display context menu." +msgstr "Zobrazí kontextové menu." + +msgid "Maximize the frame horizontally/vertically." +msgstr "Maximalizuje rám horizontálnì/vertikálnì." + +msgid "Begin move/resize mode." +msgstr "Zahájí re¾im pøesunu/zmìny velikosti." + +msgid "Switch the frame to display the object indicated by the tab." +msgstr "Pøepne rám, aby zobrazoval objekt urèený zálo¾kou." + +msgid "Resize the frame." +msgstr "Zmìní velikost rámu." + +msgid "Move the frame." +msgstr "Pøesune rám." + +msgid "Move objects between frames by dragging and dropping the tab." +msgstr "Pøesune objekty mezi rámy pomocí ta¾ení za zálo¾ku." + +msgid "Tag current object within the frame." +msgstr "Oznaèí aktuální objekt v rámu." + +msgid "Switch to n:th object within the frame." +msgstr "Pøepne se do n-tého objektu v rámu." + +msgid "Switch to next/previous object within the frame." +msgstr "Pøepne se na dal¹í/pøedchozí objekt v rámu." + +msgid "Move current object within the frame left/right." +msgstr "Pøesune objekt v rámu vlevo/vpravo." + +msgid "Attach tagged objects to this frame." +msgstr "Pøipojí oznaèené objekty k tomuto rámu." + +msgid "Toggle shade mode" +msgstr "Pøepne stínový re¾im" + +# +msgid "Raise the frame." +msgstr "Pøesune rám do popøedí." + +msgid "Lower the frame." +msgstr "Pøesune rám do pozadí" + +msgid "Cancel the resize mode." +msgstr "Zru¹í re¾im zmìny velikosti." + +msgid "End the resize mode." +msgstr "Ukonèí re¾im zmìny velikosti." + +# +msgid "Grow in specified direction." +msgstr "Roste v zadaném smìru." + +msgid "Shrink in specified direction." +msgstr "Zmen¹uje v zadaném smìru." + +# +msgid "Move in specified direction." +msgstr "Posune se v zadaném smìru." + +msgid "About Ion" +msgstr "O Ionu" + +msgid "XTerm" +msgstr "XTerm" + +msgid "W3M" +msgstr "W3M" + +msgid "Rxvt" +msgstr "Rxvt" + +msgid "Opera" +msgstr "Opera" + +msgid "Links" +msgstr "Links" + +msgid "Konqueror" +msgstr "Konqueror" + +msgid "Dillo" +msgstr "Dillo" + +# +msgid "Run..." +msgstr "Spustit..." + +msgid "Save" +msgstr "Ulo¾it" + +msgid "Restart" +msgstr "Restartovat" + +msgid "Restart TWM" +msgstr "Restartovat TWM" + +msgid "Exit" +msgstr "Ukonèit" + +msgid "Kill" +msgstr "Ukonèit" + +msgid "Toggle tag" +msgstr "(Od)znaèit" + +msgid "Attach tagged" +msgstr "Pøipojit oznaèené" + +msgid "Clear tags" +msgstr "Vyèistit znaèky" + +msgid "Window info" +msgstr "Informace o oknì" + +# +msgid "New workspace" +msgstr "Nová pracovní plocha" + +msgid "New empty workspace" +msgstr "Nová prázdná pracovní plocha" + +# +msgid "Close workspace" +msgstr "Zavøít pracovní plochu" + +msgid "Move one character forward/backward." +msgstr "Posune se o znak vpøed/vzad." + +msgid "Go to end/beginning." +msgstr "Skoèí na konec/zaèátek." + +msgid "Skip one word forward/backward." +msgstr "Pøeskoèí slovo dopøedu/dozadu." + +msgid "Delete next character." +msgstr "Sma¾e následující znak." + +msgid "Delete previous character." +msgstr "Sma¾e pøedchozí znak." + +msgid "Delete one word forward/backward." +msgstr "Sma¾e slovo pøed/za kurzorem." + +msgid "Delete to end of line." +msgstr "Sma¾e text do konce øádky." + +msgid "Delete the whole line." +msgstr "Sma¾e celý øádek." + +msgid "Transpose characters." +msgstr "Transponuje znaky." + +msgid "Select next/previous (matching) history entry." +msgstr "Vybere dal¹í/pøedchozí (odpovídající) polo¾ku historie." + +msgid "Paste from the clipboard." +msgstr "Vlo¾í ze schránky." + +# +msgid "Set mark/begin selection." +msgstr "Nastaví znaèku/zaèátek výbìru." + +# +msgid "Cut selection." +msgstr "Vyjme výbìr." + +# +msgid "Copy selection." +msgstr "Zkopíruje výbìr." + +# +msgid "Clear mark/cancel selection." +msgstr "Sma¾e znaèku/zru¹í výbìr." + +msgid "Try to complete the entered text or cycle through completions." +msgstr "Zkusí doplnit zadaný text nebo bude cyklovat mezi mo¾nostmi." + +msgid "Complete from history" +msgstr "Doplní z historie." + +msgid "Close the query and execute bound action." +msgstr "Zavøe dotaz a spustí svázanou akci." + +msgid "Close the query/message box, not executing bound actions." +msgstr "Zavøe okno s dotazem/zprávou, ani¾ by se spustily svázané akce.." + +msgid "Scroll the message or completions up/down." +msgstr "Roluje zprávu nebo seznam dokonèení nahoru/dolù." + +msgid "press" +msgstr "stisk" + +msgid "click" +msgstr "kliknutí" + +msgid "drag" +msgstr "ta¾ení" + +msgid "double click" +msgstr "dvojité kliknutí" + +msgid "%s %s" +msgstr "%s %s" + +msgid "%s %s at %s" +msgstr "%s %s na %s" + +#~ msgid "Unable to re-initialise workspace. Destroying." +#~ msgstr "Nemohu znovu inicializovat pracovní plochu. Nièím." + +#~ msgid "Refusing to close non-empty workspace." +#~ msgstr "Odmítám zavøít neprázdnou pracovní plochu." + +#~ msgid "Malfunctioning placement hook; condition #%d." +#~ msgstr "Nefunkèní volání pro umístìní; podmínka #%d." + +#~ msgid "Resize the area." +#~ msgstr "Zmìní velikost oblasti." + +#~ msgid "Refresh list" +#~ msgstr "Obnovit seznam" + +#~ msgid "Could not find a complete workspace class. Please load some modules." +#~ msgstr "Nemohu najít úplnou tøídu pracovní plochy. Zaveïte nìjaké moduly." + +#~ msgid "Failed to rescue some client windows." +#~ msgstr "Záchrana nìkterých klientských oken selhala." + +#~ msgid "Same manager." +#~ msgstr "Stejný mana¾er." + +# +#~ msgid "Invalid split type parameter." +#~ msgstr "Neplatný parametr typu rozdìlení." + +# +#~ msgid "Failure to create a new frame." +#~ msgstr "Chyba pøi vytváøení nového rámu." + +# +#~ msgid "Region not managed by the workspace." +#~ msgstr "Oblast není spravována pracovní plochou." + +#~ msgid "No geometry specified." +#~ msgstr "Nebyla zadána geometrie." + +#~ msgid "none" +#~ msgstr "¾ádná" + +# +#~ msgid "mail" +#~ msgstr "po¹ta" + +#~ msgid "" +#~ "\n" +#~ "Transients:\n" +#~ msgstr "" +#~ "\n" +#~ "Doèasná okna:\n" + +#~ msgid "Workspace type (%s):" +#~ msgstr "Typ pracovní plochy (%s):" + +#~ msgid "Go to previous active object." +#~ msgstr "Pøejde na pøedchozí aktivní objekt." + +# +# +#~ msgid "Toggle fullscreen mode of current client window." +#~ msgstr "Zapne celoobrazovkový re¾im u aktuálního klientského okna." + +#~ msgid "WStatusBar expected." +#~ msgstr "Oèekáván WStatusBar." + +#~ msgid "Backwards-circulate focus and raise the newly focused frame." +#~ msgstr "Zpìtnì cykluje mezi rámy a pøesune je nahoru." + +#~ msgid "(Un)stick" +#~ msgstr "(Pøi/Od)lepit" + +# +#~ msgid "Query for a client window to attach to active frame." +#~ msgstr "Zeptá se na klientské okno, které se má pøipojit k aktivnímu rámu." + +#~ msgid "Raise/lower active frame." +#~ msgstr "Pøesune aktivní rám do popøedí/pozadí." + +#~ msgid "Unable to create workspace: no screen." +#~ msgstr "Nemohu vytvoøit pracovní plochu: obrazovka neexistuje." + +#~ msgid "load" +#~ msgstr "vytí¾ení" + +#~ msgid "Mozilla Firefox" +#~ msgstr "Mozilla Firefox" + +#~ msgid "Circulate focus and raise the newly focused frame." +#~ msgstr "Cykluje mezi rámy a pøesune je nahoru." + +#~ msgid "Restart PWM" +#~ msgstr "Restartovat PWM" + +#~ msgid "(Un)tag" +#~ msgstr "(Od)znaèit" + +# +#~ msgid "Could not find a root window." +#~ msgstr "Nemohu najít koøenové okno." + +#~ msgid "Caught fatal signal %d. Dying without deinit." +#~ msgstr "Zachycen signál %d. Umírám bez ulo¾ení." + +#~ msgid "Caught signal %d. Dying." +#~ msgstr "Zachycen signál %d. Umírám." + +# +#~ msgid "Object destroyed while deferred actions are still pending." +#~ msgstr "Objekt byl zru¹en, zatímco pozdr¾eé akce èekají na vyøízení." + +#~ msgid "Unable to rescue \"%s\"." +#~ msgstr "Nemohu zachránit \"%s\"." + +#~ msgid "Frame is not empty." +#~ msgstr "Rám není prázdný." + +# +#~ msgid "No function given." +#~ msgstr "Nebyla zadána ¾ádná funkce." + +# +#~ msgid "Frame not managed by the workspace." +#~ msgstr "Rám není spravován pracovní plochou." + +#~ msgid "Failed to rescue managed objects." +#~ msgstr "Záchrana spravovaných objektù selhala." + +#~ msgid "Split" +#~ msgstr "Rozdìlit" + +#~ msgid "Failed to create a timer for statusbar." +#~ msgstr "Selhalo vytvoøení èasovaèe pro stavový pruh." + +#~ msgid "Vertically at root" +#~ msgstr "Vertikálnì na koøenu" + +#~ msgid "Flip&transpose" +#~ msgstr "Obrátit&transponovat" + +#~ msgid "Horizontally at root" +#~ msgstr "Horizontálnì na koøenu" diff --git a/po/de.po b/po/de.po new file mode 100644 index 0000000..a4f05ef --- /dev/null +++ b/po/de.po @@ -0,0 +1,1667 @@ +# +# German language translations for Ion3. +# +# Copyright (c) Schott Robert 2005. +# +# This file is distributed under the same license as the Ion3 package. +# + +msgid "" +msgstr "" +"Project-Id-Version: Ion3\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2004-10-11 18:33+0300\n" +"PO-Revision-Date: 2005-02-13 23:50+0300\n" +"Last-Translator: Schott Robert \n" +"Language-Team: none\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=iso-8859-1\n" +"Content-Transfer-Encoding: 8bit\n" + +#: ../ioncore/conf-bindings.c:96 +msgid "Insane key combination." +msgstr "Unsinnige Tastenkombination." + +#: ../ioncore/conf-bindings.c:100 +msgid "Could not convert keysym to keycode." +msgstr "Konnte 'keysym' nicht in 'keycode' umwandeln." + +#: ../ioncore/conf-bindings.c:111 +#, c-format +msgid "Unknown button \"%s\"." +msgstr "Unbekannte Taste \"%s\"." + +#: ../ioncore/conf-bindings.c:116 +msgid "Insane button combination." +msgstr "Unsinnige Tastenkombination." + +#: ../ioncore/conf-bindings.c:123 ../ioncore/conf-bindings.c:130 +msgid "Insane modifier combination." +msgstr "Unsinnige Modifikationskombination." + +#: ../ioncore/conf-bindings.c:168 +#, c-format +msgid "Can not wait on modifiers when no modifiers set in \"%s\"." +msgstr "" +"Kann nicht auf Modifikator warten, wenn kein Modifikator in \"%s\" gesetzt ist." + +#: ../ioncore/conf-bindings.c:186 +#, c-format +msgid "Unable to add binding %s." +msgstr "Die Bindung %s konnte nicht eingestellt werden." + +#: ../ioncore/conf-bindings.c:191 +#, c-format +msgid "Unable to remove binding %s." +msgstr "Die Bindung %s konnte nicht entfernt werden." + +#: ../ioncore/conf-bindings.c:230 +#, c-format +msgid "Unable to add submap for binding %s." +msgstr "Die 'submap' für die Bindung %s konnte nicht eingestellt werden." + +# +#: ../ioncore/conf-bindings.c:260 +msgid "Binding type not set." +msgstr "Bindungsart nicht gesetzt." + +#: ../ioncore/conf-bindings.c:270 +#, c-format +msgid "Unknown binding type \"%s\"." +msgstr "Unbekannte Bindungsart \"%s\"." + +#: ../ioncore/conf-bindings.c:291 +#, c-format +msgid "Unknown area \"%s\" for binding %s." +msgstr "Unbekannter Bereich \"%s.\" für die Bindung %s." + +#: ../ioncore/conf-bindings.c:332 +#, c-format +msgid "Unable to get bindmap entry %d." +msgstr "Der 'bindmap' Eintrag %d wurde nicht gefunden." + +#: ../ioncore/conf-bindings.c:374 +msgid "Unable to convert keysym to string." +msgstr "Konnte keysym nicht in Zeichenkette umwandeln." + +#: ../ioncore/conf-bindings.c:388 +msgid "Unable to convert button to string." +msgstr "Konnte Taste nicht in Zeichenkette umwandeln." + +# +#: ../ioncore/event.c:78 +msgid "Time request from X server failed." +msgstr "Zeitanfrage des X Servers fehlgeschlagen." + +# +#: ../ioncore/exec.c:101 +msgid "Could not find a root window." +msgstr "Konnte kein Wurzelfenster finden." + +#: ../ioncore/exec.c:134 +msgid "reading a pipe" +msgstr "Weiterleitung wird ausgelesen" + +# +#: ../ioncore/exec.c:296 +msgid "Not saving state: running under session manager." +msgstr "Nicht gespeicherter Zustand: läuft im Sitzungsmanager." + +#: ../ioncore/strings.c:107 ../ioncore/strings.c:143 +msgid "Invalid multibyte string." +msgstr "Unzulässige multibyte Zeichenkette." + +#: ../ioncore/strings.c:234 +#, c-format +msgid "Error compiling regular expression: %s" +msgstr "Fehler beim erstellen der regulären Ersetzung: %s" + +#: ../ioncore/modules.c:158 +msgid "Invalid module name." +msgstr "Unzulässiger Modulname." + +# +#: ../ioncore/modules.c:170 +msgid "The module is already loaded." +msgstr "Das Modul wurde bereits geladen." + +#: ../ioncore/modules.c:185 +msgid "" +"Module version information not found or version mismatch. Refusing to use." +msgstr "" +"Information zur Modulversion wurde nicht gefunden oder die Versionen stimmen nicht überein." +"Benutzung wurde abgelehnt." + +#: ../ioncore/modules.c:196 +#, c-format +msgid "Unable to initialise module %s." +msgstr "Module %s konnte nicht initialisiert werden." + +#: ../ioncore/modules.c:220 ../../libextl/readconfig.c:368 +#, c-format +msgid "Unable to find '%s' on search path." +msgstr "Im Suchpfad konnte '%s' nicht gefunden werden." + +#: ../ioncore/modules.c:291 +msgid "Unknown module." +msgstr "Unbekanntes Modul." + +#: ../ioncore/modules.c:299 +msgid "Unable to initialise module." +msgstr "Module konnte nicht initialisiert werden." + +#: ../ioncore/modules.c:344 +msgid "No module to load given." +msgstr "Kein Modul zum laden vorhanden." + +#: ../ioncore/screen.c:301 +msgid "act: " +msgstr "act: " + +#: ../ioncore/screen.c:490 +msgid "Invalid offset." +msgstr "Ungültiges offset." + +#: ../ioncore/screen.c:535 +msgid "Could not find a complete workspace class. Please load some modules." +msgstr "" +"Konnte keine komplette Workspace-Klasse finden." +"Laden Sie bitte einige Module." + +#: ../ioncore/screen.c:545 +#, c-format +msgid "Unable to create a workspace on screen %d." +msgstr "Konnte keinen Arbeitsbereich auf dem Schirm %d erzeugen." + +#: ../ioncore/signal.c:291 +#, c-format +msgid "Caught fatal signal %d. Dying without deinit." +msgstr "" +"Abbruch Signal %d aufgefangen. Beendet ohne deinit." + +#: ../ioncore/signal.c:300 +#, c-format +msgid "Caught signal %d. Dying." +msgstr "Signal %d wurde aufgefangen. Beendet." + +#: ../ioncore/sizehint.c:150 +msgid "Invalid client-supplied width/height increment." +msgstr "Client lieferte ungültige Breiten/Höhen - Schrittweite." + +#: ../ioncore/sizehint.c:158 +msgid "Invalid client-supplied aspect-ratio." +msgstr "Client lieferte ungültiges Seitenverhältnis." + +#: ../ioncore/ioncore.c:66 +msgid "" +"This program is free software; you can redistribute it and/or\n" +"modify it under the terms of the GNU Lesser General Public\n" +"License as published by the Free Software Foundation; either\n" +"version 2.1 of the License, or (at your option) any later version.\n" +"\n" +"This program is distributed in the hope that it will be useful,\n" +"but WITHOUT ANY WARRANTY; without even the implied warranty of\n" +"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n" +"Lesser General Public License for more details.\n" +msgstr "" +"Dieses Programm ist freie Software; Sie können es weiter verteilen und/oder\n" +"modifizieren solange es den Bedingungen der \"GNU Lesser General Public\"\n" +"Lizenz entspricht. Veröffentlicht durch die \"Free Software Foundation\"; jede\n" +"Lizenz der Version 2.1, oder (nach eigener Wahl) jede spätere Version.\n" +"\n" +"Dieses Programm wird mit der Hoffnung das es nützlich ist verteilt, \n" +"aber OHNE JEGLICHE GARANTIE; ohne jede Garantie von MARKTFÄHIGKEIT oder\n" +"EIGNUNG ZU EINEM BESTIMMTEN ZWECK. Siehe die \"GNU Lesser General Public\"\n" +"Lizenz für detailliertere Angaben.\n" + +#: ../ioncore/ioncore.c:385 +#, c-format +msgid "Could not connect to X display '%s'" +msgstr "Konnte keine Verbindung zur X Anzeige '%s' herstellen." + +#: ../ioncore/ioncore.c:438 +msgid "Could not find a screen to manage." +msgstr "Konnte keinen Bildschirm für diese Aktion finden." + +#: ../ioncore/xic.c:43 +msgid "Failed to open input method." +msgstr "Öffnen der Eingabemethode ist fehlgeschlagen." + +#: ../ioncore/xic.c:48 +msgid "Input method doesn't support any style." +msgstr "Die Eingabemethode unterstützt keine Art von Styles" + +#: ../ioncore/xic.c:63 +msgid "input method doesn't support my preedit type." +msgstr "Die Eingabemethode unterstützt nicht meine Art von 'preedit'." + +#: ../ioncore/xic.c:91 +msgid "Failed to create input context." +msgstr "Erstellen des Eingabezusammenhangs ist fehlgeschlagen. " + +#: ../ioncore/clientwin.c:376 +#, c-format +msgid "The transient_for hint for \"%s\" points to itself." +msgstr "Der 'transient_for' Tip für \"%s\" zeigt auf sich selbst." + +#: ../ioncore/clientwin.c:380 +#, c-format +msgid "" +"Client window \"%s\" has broken transient_for hint. (\"Extended WM hints\" " +"multi-parent brain damage?)" +msgstr "" +"Client Fenster \"%s\" hat einen fehlerhaften 'transient_for'-Tip " +"(\"Erweiterte WM Tips\" *** Gehirnschaden?)" + +#: ../ioncore/clientwin.c:385 +#, c-format +msgid "The transient_for window for \"%s\" is not on the same screen." +msgstr "Das 'transient_for'-Fenster für \"%s\" ist nicht auf dem gleichen Schirm." + +#: ../ioncore/clientwin.c:405 ../ioncore/clientwin.c:492 +#: ../ioncore/clientwin.c:1554 +#, c-format +msgid "Window %#x disappeared." +msgstr "Fenster %#x verschwunden." + +#: ../ioncore/clientwin.c:512 +msgid "Unable to find a matching root window!" +msgstr "Unmöglich ein passendes Wurzelfenster zu finden." + +#: ../ioncore/clientwin.c:547 +#, c-format +msgid "Unable to manage client window %#x." +msgstr "Unmöglich die Client Fenster %#x zu bearbeiten." + +#: ../ioncore/clientwin.c:597 +msgid "Changes is WM_TRANSIENT_FOR property are unsupported." +msgstr "Änderung der 'ist WM_TRANSIENT_FOR' Eigenschaft wird nicht unterstützt." + +# +#: ../ioncore/clientwin.c:874 +msgid "Client does not support the WM_DELETE protocol." +msgstr "Client unterstützt nicht das WM_DELETE Protocol." + +#: ../ioncore/clientwin.c:1103 ../mod_ionws/ionws.c:76 +#: ../mod_floatws/floatws.c:103 +#, c-format +msgid "Error reparenting %s." +msgstr "Fehler wieder herstellen %s." + +#: ../ioncore/clientwin.c:1560 +msgid "Saved client window does not want to be managed." +msgstr "Gespeichertes Client Fenster braucht nicht behandelt werden." + +# +#: ../ioncore/colormap.c:96 +msgid "Unable to store colourmap watch info." +msgstr "Unmöglich die 'colourmap' Überwachungsinformation zu speichern." + +#: ../ioncore/region.c:36 +msgid "Creating region with negative width or height!" +msgstr "Gebiet mit negativer Breite oder Höhe erzeugt!" + +#: ../ioncore/region.c:84 +#, c-format +msgid "Destroying object \"%s\" with client windows as children." +msgstr "Zerstörtes Objekt \"%s\", mit Client Fenstern." + +#: ../ioncore/attach.c:80 +msgid "Unable to reparent." +msgstr "Unmöglich wieder herzustellen." + +#: ../ioncore/attach.c:112 ../ioncore/frame-pointer.c:295 +#, c-format +msgid "Attempt to make region %s manage its ancestor %s." +msgstr "Versuche Bereich %s zu erstellen und seine Vorläufer %s zu behandeln." + +# +#: ../ioncore/defer.c:93 +msgid "Object destroyed while deferred actions are still pending." +msgstr "Objekt zerstört während zurückgestellte Aktionen noch bevorstehen." + +#: ../ioncore/manage.c:90 +msgid "Unable to find a screen for a new client window." +msgstr "Konnte keinen Screen für ein neues Client Fenster finden." + +#: ../ioncore/manage.c:187 +#, c-format +msgid "Unable to rescue \"%s\"." +msgstr "Konnte \"%s\" nicht retten." + +#: ../ioncore/rootwin.c:222 +#, c-format +msgid "Unable to redirect root window events for screen %d." +msgstr "Die Ereignisse für das Wurzelfenster des Screens %d können nicht umgeleitet werden." + +#: ../ioncore/rootwin.c:316 +msgid "Xinerama sanity check failed; overlapping screens detected." +msgstr "Xinerama Zustandsüberprüfung fehlgeschlagen; überlappende Screens gefunden." + +#: ../ioncore/rootwin.c:342 +msgid "Don't know how to get Xinerama information for multiple X root windows." +msgstr "Weiß nicht wie die Xinerama Information für multiple X Wurzelfenster zu bekommen ist." + +#: ../ioncore/rootwin.c:378 +#, c-format +msgid "Unable to setup Xinerama screen %d." +msgstr "Konnte Xinerama Screen %d nicht einrichten." + +#: ../ioncore/rootwin.c:389 +#, c-format +msgid "Unable to setup X screen %d." +msgstr "Konnte X Screen %d nicht einrichten." + +#: ../ioncore/names.c:90 +#, c-format +msgid "Corrupt instance number %s." +msgstr "Unbrauchbare Instanznummer %s." + +#: ../ioncore/saveload.c:55 +#, c-format +msgid "Unknown class \"%s\", cannot create region." +msgstr "Unbekannte Klasse \"%s\", Bereich konnte nicht erzeugt werden." + +#: ../ioncore/saveload.c:124 +#, c-format +msgid "" +"There were errors loading layout. Backing up current layout savefile as\n" +"%s.\n" +"If you are _not_ running under a session manager and wish to restore your\n" +"old layout, copy this backup file over the layout savefile found in the\n" +"same directory while Ion is not running and after having fixed your other\n" +"configuration files that are causing this problem. (Maybe a missing\n" +"module?)\n" +msgstr "" +"Fehler beim Laden des Layouts. Sichere jetzige Layoutsicherungsdatei als\n" +"%s.\n" +"Wenn Sie KEINEN Sitzungsmanager gestartet haben, können sie ihr altes Layout wieder herstellen.\n" +"Kopieren sie die Sicherungsdatei über die aktuelle Layoutsicherungsdatei welche im selben Verzeichnis\n" +"zu finden ist.\n" +"Ion kann neu gestartet werden, nachdem alle anderen Konfigurationsdateien überprüft wurden,\n" +"welche zu dem Problem beführt haben. (Eventuell fehlende Module?)\n" + +#: ../ioncore/saveload.c:167 +msgid "Unable to get file for layout backup." +msgstr "Konnte keine Datei für die Layoutsicherung finden." + +#: ../ioncore/saveload.c:171 +#, c-format +msgid "Backup file %s already exists." +msgstr "Sicherungsdatei %s ist bereits vorhanden." + +#: ../ioncore/saveload.c:177 +msgid "Failed backup." +msgstr "Sicherung fehlgeschlagen." + +# +#: ../ioncore/saveload.c:182 +msgid "Unable to initialise layout on any screen." +msgstr "Konnte für keinen Screen ein Layout initialisieren." + +#: ../ioncore/saveload.c:202 +#, c-format +msgid "Unable to get configuration for screen %d." +msgstr "Konnte für Screen %d keine Konfiguration finden." + +#: ../ioncore/saveload.c:215 +msgid "Unable to save layout." +msgstr "Konnte Layout nicht sichern." + +#: ../ioncore/frame.c:122 +msgid "Frame is not empty." +msgstr "Frame ist nicht leer." + +#: ../ioncore/conf.c:134 +msgid "User directory can not be set." +msgstr "Benutzerverzeichnis konnte nicht gesetzt werden." + +#: ../ioncore/conf.c:197 +msgid "Some bindmaps were empty, loading ioncore-efbb." +msgstr "Einige 'bindmaps' sind leer, lade 'ioncore-efbb'." + +#: ../ioncore/fullscreen.c:100 +msgid "Failed to enter full screen mode." +msgstr "Vollbildmode fehlgeschlagen." + +#: ../ioncore/fullscreen.c:158 +msgid "" +"WClientWin failed to return from full screen mode; remaining manager or " +"parent from previous location refused to manage us." +msgstr "" +"'WClientWin' konnte nicht aus dem Vollbildmode zurückkehren;" +"Der vorhandene Manager oder der Elternteil der vorherigen Position verhinterten das." + +# +#: ../ioncore/mplex.c:1247 +msgid "Invalid position setting." +msgstr "Ungültige Positionseinstellung." + +# +#: ../ioncore/mplex.c:1280 +msgid "Invalid action setting." +msgstr "Ungültige Aktionseinstellung." + +#: ../ioncore/gr.c:119 +#, c-format +msgid "Drawing engine %s is not registered!" +msgstr "Zeichenfunktion %s ist nicht registriert." + +#: ../ioncore/gr.c:138 +#, c-format +msgid "Unable to find brush for style '%s'." +msgstr "Konnte Pinsel für Style '%s' nicht finden." + +#: ../ioncore/gr.c:409 +msgid "No drawing engines loaded, trying \"de\"." +msgstr "Keine Zeichenfunktionen geladen, versuche \"de\"." + +# +#: ../ioncore/hooks.c:219 +msgid "No function given." +msgstr "Keine Funktion gegeben." + +#: ../mod_ionws/ionws.c:311 +msgid "Unable to create a node for status display." +msgstr "Konnte keinen Knoten für die Statusanzeige erstellen." + +#: ../mod_ionws/ionws.c:323 +msgid "Unable to create new split for status display." +msgstr "Konnte keine neu Teilung für die Statusanzeige erzeugen." + +# +#: ../mod_ionws/ionws.c:742 +msgid "Frame not managed by the workspace." +msgstr "Frame wird nicht durch den Arbeitsbereich behandelt." + +# +#: ../mod_ionws/ionws.c:747 +msgid "Invalid direction parameter." +msgstr "Unzulässiger Richtungsparameter." + +# +#: ../mod_ionws/ionws.c:765 +msgid "Unable to split." +msgstr "Unmöglich zu Teilen." + +#: ../mod_ionws/ionws.c:803 +msgid "Nil frame." +msgstr "'NIL'(leeres) Frame" + +#: ../mod_ionws/ionws.c:807 +msgid "The frame is not managed by the workspace." +msgstr "Das Frame wird nicht durch den Arbeitsbereich behandelt." + +#: ../mod_ionws/ionws.c:812 +msgid "Frame may not be destroyed." +msgstr "Frame darf nicht zerstört werden." + +#: ../mod_ionws/ionws.c:817 +msgid "Failed to rescue managed objects." +msgstr "Rettung der 'managed objects' fehlgeschlagen." + +#: ../mod_ionws/ionws.c:1028 +msgid "Nil parameter." +msgstr " 'NIL' (leer) Parameter" + +#: ../mod_ionws/ionws.c:1033 +msgid "Manager doesn't match." +msgstr "Manager passt nicht." + +# +#: ../mod_ionws/ionws.c:1055 +msgid "Could not get split tree." +msgstr "Kann Teilungsbaum nicht finden." + +#: ../mod_ionws/ionws.c:1076 +msgid "Workspace already has a status display node." +msgstr "Arbeitsbereich besitzt schon eine Knoten für die Statusanzeige." + +# +#: ../mod_ionws/ionws.c:1126 +msgid "Missing region parameters." +msgstr "Fehlende Bereichsparameter." + +#: ../mod_ionws/ionws.c:1162 ../mod_ionws/splitfloat.c:783 +msgid "Invalid direction." +msgstr "Ungültige Richtung." + +# +#: ../mod_ionws/ionws.c:1236 +msgid "No split type given." +msgstr "Kein Teilungstyp gegeben." + +#: ../mod_ionws/ionws.c:1249 +msgid "Unknown split type." +msgstr "Unbekannter Teilungstype." + +# +# +#: ../mod_ionws/ionws.c:1289 +msgid "The workspace is empty." +msgstr "Der Arbeitsbereich ist leer." + +#: ../mod_ionws/placement.c:97 ../mod_panews/placement.c:299 +#, c-format +msgid "" +"Ooops... could not find a region to attach client window to on workspace %s." +msgstr "Ooops ... konnte keinen Bereich finden um das Client Fenster an den Arbeitsbereich %s einzufügen." + +#: ../mod_ionws/split.c:906 +msgid "REGION_RQGEOM_TRYONLY unsupported for status display." +msgstr "'REGION_RQGEOM_TRYONLY' wird nicht unterstützt für die Statusanzeige." + +# +#: ../mod_ionws/split.c:979 +msgid "Invalid node." +msgstr "Ungültiger Knoten." + +# +#: ../mod_ionws/split.c:1044 +msgid "Splitting the status display is not allowed." +msgstr "Teilung der Statusanzeige ist nicht erlaubt." + +#: ../mod_ionws/split.c:1072 ../mod_ionws/splitfloat.c:896 +msgid "Unable to split: not enough free space." +msgstr "Teilung nicht möglich: nicht genügend freier Speicher." + +# +#: ../mod_ionws/split.c:1558 +msgid "Unable to move the status display out of way of transpose." +msgstr "Es ist unmöglich die Statusanzeige so zu verschieben." + +#: ../mod_ionws/split.c:1722 +#, c-format +msgid "Unable to get configuration for %s." +msgstr "Konnte keine Konfiguration für \"%s\" finden." + +#: ../mod_ionws/split-stdisp.c:591 ../mod_ionws/split-stdisp.c:616 +msgid "Status display in bad split configuration." +msgstr "Statusanzeige hat eine schlechte Teilungskonfiguration." + +#: ../mod_ionws/split-stdisp.c:657 +msgid "Status display badly located in split tree." +msgstr "Statusanzeige liegt schlecht im Teilungsbaum." + +#: ../mod_floatws/floatws.c:351 ../mod_panews/panews.c:259 +msgid "Workspace may not be destroyed." +msgstr "Arbeitsbereich kann nicht zerstört werden." + +#: ../mod_floatws/floatws.c:358 +msgid "Failed to rescue some client windows!" +msgstr "Rettung einiger Client Fenster fehlgeschlagen." + +#: ../mod_floatws/floatws.c:370 ../mod_panews/panews.c:254 +msgid "Refusing to close non-empty workspace." +msgstr "Schließen von 'nicht-leerem' Arbeitsbereich abgelehnt." + +# +#: ../mod_floatws/floatws.c:462 +msgid "Failure to create a new frame." +msgstr "Konnte kein neues Frame erzeugen." + +# +#: ../mod_floatws/floatws.c:1037 ../mod_floatws/floatws.c:1115 +msgid "Region not managed by the workspace." +msgstr "Bereich wird nicht behandelt durch diesen Arbeitsbereich." + +#: ../mod_floatws/floatws.c:1236 +msgid "No geometry specified." +msgstr "Keine Geometrie angegeben." + +#: ../mod_floatws/placement.c:125 +#, c-format +msgid "Unknown placement method \"%s\"." +msgstr "Unbekannte Platzierungsmethode \"%s\"." + +#: ../mod_panews/panews.c:193 +msgid "Unable to re-initialise workspace. Destroying." +msgstr "Konnte den Arbeitsbereich nicht neu initialisieren. Zerstört." + +#: ../mod_panews/placement.c:104 ../mod_panews/placement.c:108 +#: ../mod_panews/placement.c:175 ../mod_panews/placement.c:180 +#, c-format +msgid "Malfunctioning placement hook; condition #%d." +msgstr "Fehlfunktion der Platzierung; Zustand #%d." + +#: ../mod_query/fwarn.c:34 +msgid "Error:\n" +msgstr "Fehler:\n" + +# +#: ../mod_menu/menu.c:490 +msgid "Empty menu." +msgstr "Leeres Menü." + +# +#: ../mod_sm/sm.c:110 +msgid "Failed to set session directory." +msgstr "Konnte Sitzungsverzeichnis nicht setzen." + +#: ../mod_sm/sm_session.c:85 +msgid "Too many ICE connections." +msgstr "Zu viele 'ICE' Verbindungen." + +#: ../mod_sm/sm_session.c:227 +msgid "Failed to save session state" +msgstr "Konnte Sitzungszustand nicht sichern." + +#: ../mod_sm/sm_session.c:246 +msgid "Failed to request save-yourself-phase2 from session manager." +msgstr "Konnte die 'save-yourself-phase2' Anfrage des Sitzungsmanagers nicht gehandeln." + +# +#: ../mod_sm/sm_session.c:295 +msgid "SESSION_MANAGER environment variable not set." +msgstr "'SESSION_MANAGER' Umgebungsvariable nicht gesetzt." + +#: ../mod_sm/sm_session.c:300 +msgid "Session Manager: IceAddConnectionWatch failed." +msgstr "Sitzungsmanager: 'IceAddConnectionWatch' fehlgeschlagen." + +# +#: ../mod_sm/sm_session.c:325 +msgid "Unable to connect to the session manager." +msgstr "Konnte keine Verbindung zu Sitzungsmanager aufbauen." + +#: ../mod_sp/main.c:137 +#, c-format +msgid "Unable to create scratchpad for screen %d." +msgstr "Konnte 'Scratchpad' (Schmirblock) für Screen %d nicht erstellen." + +#: ../de/init.c:41 +#, c-format +msgid "Border attribute %s sanity check failed." +msgstr "Überprüfung der Rahmeneigenschaft %s fehlgeschlagen." + +#: ../de/init.c:64 +#, c-format +msgid "Unknown border style \"%s\"." +msgstr "Unbekannter Rahmenstyle \"%s\"." + +#: ../de/init.c:96 +#, c-format +msgid "Unable to allocate colour \"%s\"." +msgstr "Konnte Farbe \"%s\" nicht zuweisen." + +#: ../de/init.c:160 +#, c-format +msgid "Corrupt substyle table %d." +msgstr "Zerstörte 'substyle' Tabelle %d" + +#: ../de/init.c:193 +#, c-format +msgid "Unknown text alignment \"%s\"." +msgstr "Unbekannte Textausrichtung \"%s\"." + +#: ../de/init.c:263 +#, c-format +msgid "'based_on' for %s points back to the style itself." +msgstr " 'based_on' für %s zeigt zurück auf den selben Style. " + +#: ../de/init.c:266 +#, c-format +msgid "Unknown base style \"%s\"." +msgstr "Unbekannter Basisstyle \"%s\"." + +#: ../de/font.c:47 +#, c-format +msgid "" +"Fontset for font pattern '%s' implements context dependent drawing, which is " +"unsupported. Expect clutter." +msgstr "" +"Schriftart für Schriftmuster '%s' enthält fallspezifische Abhängigkeiten zum Zeichnen" +"Dies wird nicht unterstützt. Erwartet 'clutter'." + +#: ../de/font.c:58 +#, c-format +msgid "Could not load font \"%s\", trying \"%s\"" +msgstr "Konnte Schrift \"%s\" nicht laden, versuche \"%s\"." + +#: ../de/style.c:315 +#, c-format +msgid "Style %s still in use [%d] but the module is being unloaded!" +msgstr "Style %s wird derzeit benutzt [%d] aber das Modul ist nicht mehr geladen." + +#: ../ion/ion.c:41 ../pwm/pwm.c:41 +msgid "X display to use" +msgstr "Benutze X Anzeige" + +#: ../ion/ion.c:44 ../pwm/pwm.c:44 +msgid "Configuration file" +msgstr "Konfigurationsdatei" + +#: ../ion/ion.c:47 ../pwm/pwm.c:47 +msgid "Add directory to search path" +msgstr "Addiere Verzeichnis zum Suchpfad " + +#: ../ion/ion.c:50 ../pwm/pwm.c:50 +msgid "Manage default root window/non-Xinerama screen only" +msgstr "Behandle nur das vorgegebene Wurzelfenster/nicht-Xinerama." +#: ../ion/ion.c:54 +msgid "Use Xinerama screen information (default: 1/yes)" +msgstr "Benutze Xinerama Screen Information (Vorgabe: 1/ja)" + +#: ../ion/ion.c:57 ../pwm/pwm.c:57 +msgid "Ignored: not compiled with Xinerama support" +msgstr "Ignoriert: keine Xinerama Unterstützung kompiliert." + +#: ../ion/ion.c:61 ../pwm/pwm.c:61 +msgid "Name of session (affects savefiles)" +msgstr "Name der Sitzung (betrifft Sicherungsdateien)" + +#: ../ion/ion.c:64 ../pwm/pwm.c:64 +msgid "Session manager client ID" +msgstr "Sitzungsmanager Client ID" + +#: ../ion/ion.c:67 ../pwm/pwm.c:67 +msgid "Do not create startup error log and display it with xmessage." +msgstr "Erstelle keine Fehlerdateien während des Starts und zeige sie mit 'xmessage'. " + +#: ../ion/ion.c:71 ../pwm/pwm.c:71 +msgid "Show this help" +msgstr "Zeige diese Hilfe" + +#: ../ion/ion.c:74 ../pwm/pwm.c:74 +msgid "Show program version" +msgstr "Zeige Programmversion" + +#: ../ion/ion.c:77 ../pwm/pwm.c:77 +msgid "Show about text" +msgstr "Zeige 'About' Text" + +#: ../ion/ion.c:92 +msgid "Could not get user configuration file directory." +msgstr "Konnte das Verzeichnis mit den Benutzerkonfigurationsdateien nicht finden." + +#: ../ion/ion.c:106 +#, c-format +msgid "%s/welcome.txt" +msgstr "%s/welcome.de.txt" + +#: ../ion/ion.c:139 ../pwm/pwm.c:86 +#, c-format +msgid "" +"Usage: %s [options]\n" +"\n" +msgstr "" +"Anwendung: %s [Optionen]\n" +"\n" + +#: ../ion/ion.c:199 ../pwm/pwm.c:149 +msgid "Invalid parameter to -xinerama." +msgstr "Ungültiger Parameter für '-xinerama'." + +# +#: ../ion/ion.c:218 ../pwm/pwm.c:168 +msgid "Invalid command line." +msgstr "Ungültige Kommandozeile." + +#: ../ion/ion.c:239 +msgid "Ion startup error log:\n" +msgstr "Ion-Start Fehlerausgabe:\n" + +#: ../ion/ion.c:249 ../pwm/pwm.c:199 +msgid "Refusing to start due to encountered errors." +msgstr "Start abgebrochen wegen aufgetretenen Fehlern." + +#: ../pwm/pwm.c:54 +msgid "Use Xinerama screen information (default: 0/no)" +msgstr "Benutze Xinerama Screen Information (Vorgabe: 0/nein)" + +#: ../pwm/pwm.c:189 +msgid "PWM startup error log:\n" +msgstr "PWM-Start Fehlerausgabe:\n" + +#: ../../libextl/readconfig.c:86 +msgid "$HOME not set" +msgstr "Variable '$HOME' nicht gesetzt." + +#: ../../libextl/readconfig.c:113 +msgid "User directory not set. Unable to set session directory." +msgstr "" +"Benutzerverzeichnis nicht gesetzt." +"Konnte Sitzungsverzeichnis nicht setzen." + +#: ../../libextl/readconfig.c:247 +#, c-format +msgid "Falling back to %s." +msgstr "Yritetään tiedostoa %s." + +#: ../../libextl/readconfig.c:441 +#, c-format +msgid "Unable to create session directory \"%s\"." +msgstr "Konntedas Sitzungsverzeichnis \"%s\" nicht erstellen." + +#: ../../libextl/luaextl.c:113 +msgid "Lua stack full." +msgstr "Lua Stapelverarbeitung voll." + +# +#: ../../libextl/luaextl.c:139 +msgid "Unknown Lua error." +msgstr "Unbekannter Lua Fehler." + +#: ../../libextl/luaextl.c:456 +msgid "Stack trace:" +msgstr "Stapelverfolgung:" + +#: ../../libextl/luaextl.c:463 +#, c-format +msgid "" +"\n" +"(Unable to get debug info for level %d)" +msgstr "" +"\n" +"(Konnte Debuginformation für Level %d nicht finden.)" + +#: ../../libextl/luaextl.c:481 +msgid "" +"\n" +" [Skipping unnamed C functions.]" +msgstr "" +"\n" +" [Überspringe unbenannte C Funktionen.]" + +#: ../../libextl/luaextl.c:532 +msgid "Internal error." +msgstr "Interner Fehler." + +#: ../../libextl/luaextl.c:551 +msgid "Unable to initialize Lua." +msgstr "Konnte Lua nicht initialisieren." + +#: ../../libextl/luaextl.c:1277 +msgid "" +"Too many return values. Use a C compiler that has va_copy to support more." +msgstr "" +"Zu viele Rückgabewerte. Benutzen Sie einen C Compiler der 'va_copy' benutzt." + +#: ../../libextl/luaextl.c:1295 +#, c-format +msgid "Invalid return value (expected '%c', got lua type \"%s\")." +msgstr "" +"Ungültiger Rückgabewert (erwartet '%c', bekommener Luatype \"%s\")." + +#: ../../libextl/luaextl.c:1330 ../../libextl/luaextl.c:1594 +msgid "Stack full." +msgstr "Stapel voll." + +#: ../../libextl/luaextl.c:1570 +msgid "L1 call handler upvalues corrupt." +msgstr "L1 Abruf 'upvalues' fehlerhaft." + +#: ../../libextl/luaextl.c:1575 +msgid "Called function has been unregistered." +msgstr "Aufgerufene Funktion wurde noicht registriert." + +#: ../../libextl/luaextl.c:1587 +#, c-format +msgid "Attempt to call an unsafe function \"%s\" in restricted mode." +msgstr "Versuchte eine unsichere Funktion \"%s\" im begrenzten Modus aufzurufen." + +#: ../../libextl/luaextl.c:1603 +#, c-format +msgid "" +"Argument %d to %s is of invalid type. (Argument template is '%s', got lua " +"type %s)." +msgstr "" +"Argument %d von %s hat einen ungültigen Type (Argumentvorgabe ist '%s', " +"von Lua kam '%s'.)" + +#: ../../libextl/luaextl.c:1782 +#, c-format +msgid "" +"Function '%s' has more parameters than the level 1 call handler can handle" +msgstr "" +"Funktion %s hat mehr Parameter als der Level 1 Handler bearbeiten kann " + +# +#: ../../libextl/luaextl.c:2199 +msgid "Maximal serialisation depth reached." +msgstr "Maximum der Serialisierungstiefe erreicht." + +#: ../../libextl/luaextl.c:2220 +#, c-format +msgid "Unable to serialise type %s." +msgstr "Konnte den Type %s nicht serialisieren." + +#: ../../libextl/luaextl.c:2251 +msgid "-- This file has been generated by Ion. Do not edit.\n" +msgstr "-- Dies Datei wurde von Ion erzeugt. Bitte nicht verändern.\n" + +#: ../../libextl/misc.c:17 +#, c-format +msgid "" +"Type checking failed in level 2 call handler for parameter %d (got %s, " +"expected %s)." +msgstr "" +"Type Überprüfung im Level 2 Handler für den Parameter #%d fehlgeschlagen" +"(gekam %s, erwartet %s)." + +#: ../etc/cfg_floatws.lua:34 +msgid "Lower the frame." +msgstr "Frame in den Hintergrund." + +#: ../etc/cfg_bindings.lua:171 +msgid "Display frame context menu." +msgstr "Anzeige Frame Kontexmenü" + +# +#: ../etc/cfg_ionws.lua:44 +msgid "Flip at root" +msgstr "An der Wurzel drehen" + +# +#: ../etc/cfg_bindings.lua:43 +msgid "Go to n:th screen on multihead setup." +msgstr "Gehe zu n'ten Screen bei 'multihead' Aufbau." + +#: ../ext_statusbar/ext_statusbar.lua:308 +msgid "Failed to create statusbar." +msgstr "Konnte Statusbar nicht erzeugen." + +#: ../pwm/cfg_pwm_menus.lua:13 ../etc/cfg_menus.lua:10 +msgid "Help" +msgstr "Hilfe" + +#: ../mod_query/mod_query.lua:566 +msgid "SSH to:" +msgstr "SSH to:" + +#: ../etc/cfg_menus.lua:28 +msgid "Restart" +msgstr "Neustart" + +#: ../mod_menu/mod_menu.lua:231 +msgid "Cannot save selection." +msgstr "Konnte Auswahl nicht speichern." + +#: ../etc/cfg_bindings.lua:206 +msgid "End the resize mode." +msgstr "Beende den Anpassungsmode." + +# +# +#: ../etc/cfg_ionws.lua:48 ../etc/cfg_ionws.lua:58 +msgid "Vertically" +msgstr "Vertikal" + +#: ../ioncore/ioncore-bindings.lua:34 +msgid "Invalid guard %s." +msgstr "Ungültiger Wächter %s." + +#: ../etc/cfg_bindings.lua:109 +msgid "Query for Lua code to execute." +msgstr "Anfrage um Lua Code auszuführen." + +# +#: ../etc/cfg_bindings.lua:47 +msgid "Go to next/previous screen on multihead setup." +msgstr "Gehe zum nächsten/vorherigen Screen bei 'multihead' Aufbau." + +#: ../mod_query/mod_query.lua:344 +msgid "none" +msgstr "keine" + +#: ../etc/cfg_bindings.lua:219 +msgid "Shrink in specified direction." +msgstr "Verkleinern in angegebener Richtung." + +#: ../etc/cfg_bindings.lua:160 +msgid "Maximize the frame horizontally/vertically." +msgstr "Maximiere das Frame horizontal/vertikal." + +#: ../etc/cfg_bindings.lua:156 +msgid "Move current object within the frame left/right." +msgstr "" +"Bewege das aktuelle Objekt im Frame nach links/rechts." + +#: ../etc/cfg_ionws.lua:12 +msgid "Go to frame above/below/right/left of current frame." +msgstr "" +"Gehe zu Frame oben/unten/rechts/links" +"Bezogen auf aktuelles Frame" + +#: ../ext_statusbar/ext_statusbar.lua:289 +msgid "Screen not found." +msgstr "Screen nicht gefunden." + +#: ../etc/cfg_bindings.lua:182 +msgid "Resize the frame." +msgstr "Anpassen des Frame." + +#: ../etc/cfg_bindings.lua:189 +msgid "Move objects between frames by dragging and dropping the tab." +msgstr "Bewege Objekte zwischen Frames durch 'ziehen/fallen lassen' des Reiters." + +#: ../mod_query/mod_query.lua:99 +msgid "Could not find %s" +msgstr "Ei löydettyä %s:ää." + +#: ../ext_statusbar/ext_statusbar.lua:295 +msgid "Screen already has an stdisp. Refusing to create a statusbar." +msgstr "Screen hat bereits ein 'stdisp'. Erstellen der Statusbar wurde abgelehnt." + +#: ../etc/cfg_bindings.lua:112 +msgid "Query for host to connect to with SSH." +msgstr "Anfrage zur Verbindung mit SSH." + +# +#: ../etc/cfg_bindings.lua:229 +msgid "Move in specified direction." +msgstr "Bewegt in festgelegte Richtung" + +# +#: ../ext_statusbar/ext_statusbar.lua:27 +msgid "mail" +msgstr "Post" + +#: ../mod_query/mod_query.lua:807 +msgid "" +"\n" +"Transients:\n" +msgstr "" +"\n" +"Flüchtige:\n" + +#: ../etc/cfg_menu.lua:21 +msgid "Select next/previous menu entry." +msgstr "Wähle nächsten/vorherigen Menüeintrag." + +#: ../ioncore/ioncore-luaext.lua:30 +msgid "Recursive table - unable to deepcopy" +msgstr "Rekursive Tabelle - unmöglich zu kopieren." + +# +#: ../etc/cfg_bindings.lua:209 +msgid "Grow in specified direction." +msgstr "Wachse in angegebener Richtung." + +#: ../ioncore/ioncore-bindings.lua:68 +msgid "Invalid command" +msgstr "Ungültiges Kommando" + +#: ../mod_query/mod_query.lua:189 ../mod_menu/mod_menu.lua:245 +msgid "Too much result data" +msgstr "Zu viele Ergebnisdaten" + +#: ../mod_query/mod_query.lua:344 +msgid "Workspace type (%s):" +msgstr "Type des Arbeitsbereichs (%s):" + +#: ../etc/cfg_bindings.lua:36 +msgid "Go to previous active object." +msgstr "Gehe zu vorherigem aktivem Objekt" + +# +#: ../etc/cfg_query.lua:67 +msgid "Clear mark/cancel selection." +msgstr "Lösche Markierung/Abbruch Auswahl." + +#: ../mod_query/mod_query.lua:772 +msgid "No entry '%s'" +msgstr "Kein Eintrag '%s'." + +# +# +#: ../etc/cfg_bindings.lua:90 +msgid "Toggle fullscreen mode of current client window." +msgstr "Schalte das aktuelle Client Fenster in den Vollbildmode." + +#: ../etc/cfg_bindings.lua:103 +msgid "Query for manual page to be displayed." +msgstr "Anfrage um 'Manual' Seiten anzuzeigen." + +#: ../etc/cfg_bindings.lua:51 +msgid "Show the Ion manual page." +msgstr "Zeige die Ion 'Manual' Seite." + +#: ../etc/cfg_bindings.lua:60 +msgid "Display the main menu." +msgstr "Zeige das Hauptmenü." + +# +#: ../etc/cfg_bindings.lua:54 +msgid "Run a terminal emulator." +msgstr "Starte einen Terminal." + +#: ../etc/cfg_bindings.lua:121 +msgid "Query for workspace to go to or create a new one." +msgstr "Anfrage um auf einen Arbeitsbereich zu wechseln oder einen neuen zu erzeugen." + +#: ../etc/cfg_menus.lua:38 +msgid "Kill" +msgstr "Zerstören" + +#: ../pwm/cfg_pwm_menus.lua:25 ../etc/cfg_menus.lua:37 +msgid "Close" +msgstr "Schließen" + +#: ../mod_query/mod_query.lua:381 +msgid "Go to or create workspace:" +msgstr "Erzeuge einen Arbeitsbereich:" + +#: ../etc/cfg_query.lua:35 +msgid "Delete one word forward/backward." +msgstr "Lösche ein Wort vorwärts/rückwärts." + +#: ../etc/cfg_query.lua:17 +msgid "Go to end/beginning." +msgstr "Gehe zum Ende/Anfang." + +# +#: ../etc/cfg_ionws.lua:43 +msgid "Transpose" +msgstr "Vertauschen" + +#: ../etc/cfg_bindings.lua:152 +msgid "Switch to next/previous object within the frame." +msgstr "Wechsel zum nächsten/vorherigen Objekt im Frame" + +#: ../mod_query/mod_query.lua:402 +msgid "Restart Ion (y/n)?" +msgstr "Neustart Ion (y/n)?" + +#: ../mod_query/mod_query.lua:724 +msgid "Lua code: " +msgstr "Lua Code:" + +#: ../mod_query/mod_query.lua:367 +msgid "Attach window:" +msgstr "Fenster verknüpfen:" + +#: ../etc/cfg_bindings.lua:186 ../etc/cfg_floatws.lua:37 +msgid "Move the frame." +msgstr "Gehe zu Frame." + +#: ../pwm/cfg_pwm_menus.lua:11 ../etc/cfg_menus.lua:8 +msgid "Programs" +msgstr "Programme" + +#: ../etc/cfg_query.lua:23 +msgid "Skip one word forward/backward." +msgstr "Überspringe ein Wort vorwärts/rückwärts." + +#: ../mod_query/mod_query.lua:738 +msgid "Unknown menu %s." +msgstr "Unbekanntes Menü %s." + +#: ../etc/cfg_floatws.lua:12 +msgid "Backwards-circulate focus and raise the newly focused frame." +msgstr "Rückwärts laufender Fokus und zeige fokussiertes Frame." + +#: ../etc/cfg_menus.lua:44 +msgid "Window info" +msgstr "Fensterinformation" + +# +#: ../etc/cfg_query.lua:58 +msgid "Set mark/begin selection." +msgstr "Setze Markierungs/Anfangs - Auswahl." + +#: ../etc/cfg_menus.lua:30 +msgid "Restart TWM" +msgstr "Neustart TWM" + +#: ../etc/cfg_ionws.lua:9 +msgid "Split current frame vertically." +msgstr "Teile aktuelles Frame vertikal." + +# +# +#: ../etc/cfg_bindings.lua:57 +msgid "Create a new workspace of chosen default type." +msgstr "Erzeuge einen neuen Arbeitsbereich mit ausgewähltem Vorgabetype." + +# +#: ../etc/cfg_floatws.lua:29 +msgid "Raise the frame." +msgstr "Bringe Frame nach vorne." + +#: ../etc/cfg_floatws.lua:46 +msgid "(Un)stick" +msgstr "anheften/ablösen" + +#: ../mod_query/mod_query.lua:287 ../mod_query/mod_query.lua:297 +msgid "Could not find client window %s." +msgstr "Konnte Client Fenster %s nicht finden." + +#: ../etc/cfg_query.lua:82 +msgid "Close the query/message box, not executing bound actions." +msgstr "Schließe die Anfrage/Message Box und führe keine daran gebundenen Aktionen aus." + +# +#: ../etc/cfg_bindings.lua:168 +msgid "Query for a client window to attach to active frame." +msgstr "Anfrage für ein Client Fenster um sich mit einem aktivem Frame zu verbinden " + +#: ../mod_query/mod_query.lua:488 +msgid "Run:" +msgstr "Starte:" + +# +#: ../etc/cfg_panews.lua:11 +msgid "Resize the area." +msgstr "Verändere die Ansicht." + +# +#: ../etc/cfg_bindings.lua:95 +msgid "Kill client owning current client window." +msgstr "Beende aktuelles Client Fenster." + +#: ../mod_query/mod_query.lua:595 +msgid "Manual page (%s):" +msgstr "Handbuch für (%s):" + +# +#: ../etc/cfg_bindings.lua:124 +msgid "Query for a client window to go to." +msgstr "Anfrage wohin ein Client Fenster geschickt werden soll." + +# +#: ../etc/cfg_ionws.lua:39 +msgid "Destroy frame" +msgstr "Zerstöre Frame" + +#: ../mod_query/mod_query.lua:339 +msgid "Unknown error" +msgstr "Unbekannter Fehler" + +#: ../etc/cfg_bindings.lua:175 ../etc/cfg_panews.lua:8 +msgid "Begin move/resize mode." +msgstr "Beginne Bewegungs/Veränderungs - Modus." + +#: ../etc/cfg_bindings.lua:136 +msgid "Tag current object within the frame." +msgstr "Markiere aktuelles Objekt im Frame." + +#: ../etc/cfg_bindings.lua:140 +msgid "Switch to n:th object within the frame." +msgstr "Wechsel zum n'ten Objekt im Frame." + +#: ../etc/cfg_bindings.lua:203 +msgid "Cancel the resize mode." +msgstr "Abbruch des Veränderungsmodus." + +#: ../build/mkman.lua:196 +msgid "%s %s" +msgstr "%s %s" + +#: ../etc/cfg_query.lua:27 +msgid "Delete next character." +msgstr "Lösche nächsten Buchstaben." + +#: ../mod_query/mod_query.lua:356 +msgid "Go to window:" +msgstr "Gehe zu Fenster:" + +#: ../build/mkman.lua:182 +msgid "drag" +msgstr "ziehen" + +#: ../build/mkman.lua:181 +msgid "click" +msgstr "anklicken" +#: ../build/mkman.lua:180 +msgid "press" +msgstr "drücken" + +#: ../etc/cfg_menu.lua:27 +msgid "Clear the menu's typeahead find buffer." +msgstr "Lösche den Menüpuffer." + +#: ../etc/cfg_ionws.lua:47 +msgid "Split" +msgstr "Teilung" + +# +#: ../etc/cfg_bindings.lua:98 +msgid "" +"Send next key press to current client window. Some programs may not allow " +"this by default." +msgstr "" +"Sende den nächsten Tastendruck zum aktuellen Client Fenster. " +"Einige Programme unterstützen dies nicht in ihrer Voreinstellung." + +#: ../etc/cfg_menu.lua:11 +msgid "Close the menu." +msgstr "Schließe das Menü." + +#: ../etc/cfg_query.lua:87 +msgid "Scroll the message or completions up/down." +msgstr "Verschiebe die Nachricht oder Vervollständigung nach oben/unten." + +#: ../etc/cfg_query.lua:74 +msgid "Close the query and execute bound action." +msgstr "Schließe die Anfrage und führe die daran gebundene Aktion aus." + +# +#: ../etc/cfg_bindings.lua:31 +msgid "Switch to next/previous object within current screen." +msgstr "Wechsel zum nächsten/vorherigen Objekt im aktuellen Screen." + +#: ../etc/cfg_query.lua:39 +msgid "Delete to end of line." +msgstr "Lösche bis zum ende der Zeile." + +#: ../pwm/cfg_pwm_menus.lua:14 ../etc/cfg_menus.lua:11 +msgid "About Ion" +msgstr "Über Ion" + +#: ../mod_query/mod_query.lua:804 +msgid "" +"Title: %s\n" +"Class: %s\n" +"Role: %s\n" +"Instance: %s\n" +"XID: 0x%x" +msgstr "" +"Titel: %s\n" +"Klasse(class): %s\n" +"Rolle(role): %s\n" +"Instanz(instance): %s\n" +"XID: 0x%x" + +# +#: ../etc/cfg_query.lua:61 +msgid "Cut selection." +msgstr "Ausschneiden." + +#: ../etc/cfg_query.lua:53 +msgid "Paste from the clipboard." +msgstr "Einfügen aus Clipboard." + +# +#: ../etc/cfg_query.lua:45 +msgid "Select next/previous (matching) history entry." +msgstr "Auswahl des nächsten/vorherigen (Treffers) 'History' Eintrags." + +#: ../etc/cfg_menus.lua:43 +msgid "Clear tags" +msgstr "Lösche Markierungen" + +#: ../etc/cfg_query.lua:42 +msgid "Delete the whole line." +msgstr "Lösche die ganze Zeile." + +#: ../etc/cfg_query.lua:71 +msgid "Try to complete the entered text." +msgstr "Versuche den eingegebenen Text zu komplettieren." + +#: ../etc/cfg_query.lua:31 +msgid "Delete previous character." +msgstr "Lösche vorheriges Zeichen." + +#: ../mod_query/mod_query.lua:299 +msgid "Cannot attach: different root windows." +msgstr "Keine Verknüpfung: unterschiedliche Wurzelfenster." + +#: ../build/mkman.lua:183 +msgid "double click" +msgstr "Doppelklick" + +#: ../etc/cfg_query.lua:11 +msgid "Move one character forward/backward." +msgstr "Bewege ein Zeichen vorwärts/rückwärts." + +#: ../etc/cfg_menus.lua:29 +msgid "Restart PWM" +msgstr "Neustart PWM" + +#: ../etc/cfg_bindings.lua:115 +msgid "Query for file to edit." +msgstr "Anfrage um eine Datei zu bearbeiten." + +# +#: ../etc/cfg_bindings.lua:39 +msgid "Clear all tags." +msgstr "Lösche alle Markierungen." + +#: ../etc/cfg_bindings.lua:178 +msgid "Switch the frame to display the object indicated by the tab." +msgstr "Wechsle das Frame um das Objekt anzuzeigen welches durch den Reiter angegeben ist." + +# +# +#: ../etc/cfg_ionws.lua:50 ../etc/cfg_ionws.lua:60 +msgid "Horizontally" +msgstr "Horizontal" + +#: ../pwm/cfg_pwm_menus.lua:16 ../pwm/cfg_pwm_menus.lua:24 +msgid "New" +msgstr "Neu" + +#: ../mod_query/mod_query.lua:410 +msgid "Frame name:" +msgstr "Framename:" + +# +#: ../etc/cfg_bindings.lua:18 +msgid "" +"Switch to n:th object (workspace, full screen client window) within current " +"screen." +msgstr "" +"Wechsle zum n'ten Objekt (Arbeitsbereich, Vollbild Fenster) im aktuellen Arbeitsbereich." + +#: ../etc/cfg_bindings.lua:64 +msgid "Display the window list menu." +msgstr "Zeige das Menü mit der Fensterliste." + +#: ../etc/cfg_floatws.lua:26 +msgid "Toggle shade mode" +msgstr "Wechsle in den Schattenmodus" + +#: ../mod_query/mod_query.lua:393 +msgid "Exit Ion/Shutdown session (y/n)?" +msgstr "Beende Ion/Schließe Sitzung (y/n)?" + +#: ../etc/cfg_floatws.lua:15 +msgid "Raise/lower active frame." +msgstr "vorbringen/zurücklegen des aktiven Frames" + +#: ../mod_menu/mod_menu.lua:235 +msgid "Save look selection in %s?" +msgstr "Sichere Aussehen in %s?" + +#: ../etc/cfg_bindings.lua:82 +msgid "Close current object." +msgstr "Schließe aktuelles Objekt." + +# +#: ../etc/cfg_query.lua:64 +msgid "Copy selection." +msgstr "Kopiere Auswahl." + +#: ../etc/cfg_bindings.lua:164 +msgid "Attach tagged objects to this frame." +msgstr "Verknüpfe markierte Objekte mit diesem Frame." + +#: ../etc/cfg_ionws.lua:57 +msgid "Floating split" +msgstr "Fließende Teilung" + +#: ../mod_query/mod_query.lua:768 +msgid "%s menu:" +msgstr "menu: %s" + +#: ../ext_statusbar/ext_statusbar.lua:318 +msgid "Failed to create a timer for statusbar." +msgstr "Erzeugen des Timers für die Statusbar fehlgeschlagen." + +#: ../etc/cfg_ionws.lua:52 ../etc/cfg_ionws.lua:62 +msgid "Vertically at root" +msgstr "Vertikal an der Wurzel" + +# +# +#: ../etc/cfg_menu.lua:16 +msgid "Activate current menu entry." +msgstr "Aktiviere aktuellen Menüeintrag." + +#: ../ioncore/ioncore-efbb.lua:12 +msgid "" +"Making the following minimal emergency mappings:\n" +" F2 -> xterm\n" +" F11 -> restart\n" +" F12 -> exit\n" +" Mod1+C -> close\n" +" Mod1+K P/N -> WFrame.switch_next/switch_prev\n" +msgstr "" +"Erstelle die folgenden minimalen Rettungseinstellungen:\n" +" F2 -> xterm\n" +" F11 -> Neustart\n" +" F12 -> Beenden\n" +" Mod1+C -> Schließen\n" +" Mod1+K P/N -> nächstes/vorheriges Frame\n" + +# +#: ../etc/cfg_ionws.lua:45 +msgid "Transpose at root" +msgstr "Vertausche an der Wurzel" + +#: ../pwm/cfg_pwm_menus.lua:17 ../etc/cfg_menus.lua:12 +msgid "Styles" +msgstr "Aussehen ändern" + +#: ../etc/cfg_bindings.lua:106 +msgid "Query for command line to execute." +msgstr "Anfrage um Kommandozeile auszuführen." + +#: ../mod_query/mod_query.lua:323 +msgid "Unable to create workspace: no screen." +msgstr "Unmöglich Arbeitsbereich zu erzeugen: kein Screen." + +#: ../ext_statusbar/ext_statusbar.lua:27 +msgid "load" +msgstr "lade" + +# +#: ../etc/cfg_ionws.lua:42 +msgid "Flip" +msgstr "Umdrehen" + +#: ../etc/cfg_ionws.lua:41 +msgid "Flip&transpose" +msgstr "Umdrehen und Vertauschen" + +#: ../pwm/cfg_pwm_menus.lua:12 ../etc/cfg_menus.lua:9 +msgid "Lock screen" +msgstr "Sperre Screen" + +#: ../mod_query/mod_query.lua:449 +msgid "Edit file:" +msgstr "Bearbeite Datei:" + +#: ../etc/cfg_menus.lua:19 +msgid "XTerm" +msgstr "XTerm" + +#: ../build/mkman.lua:198 +msgid "%s %s at %s" +msgstr "%s %s auf %s" + +# +#: ../etc/cfg_bindings.lua:85 +msgid "" +"Nudge current client window. This might help with some programs' resizing " +"problems." +msgstr "" +"Anstupsen des aktuellen Fensters. Dies könnte helfen bei einigen Programmen " +" (xterm) mit Darstellungsproblemen." + +#: ../etc/cfg_ionws.lua:22 +msgid "Destroy current frame." +msgstr "Zerstöre aktuelles Frame." + +#: ../ioncore/ioncore-bindings.lua:53 +msgid "Error in command string: " +msgstr "Fehler in Kommandozeichenkette: " + +#: ../etc/cfg_ionws.lua:19 +msgid "Split current frame horizontally." +msgstr "Teile aktuelles Frame horizontal." + +#: ../etc/cfg_menus.lua:42 +msgid "Attach tagged" +msgstr "Markierte Objekte verknüpfen" + +#: ../etc/cfg_ionws.lua:54 ../etc/cfg_ionws.lua:64 +msgid "Horizontally at root" +msgstr "Horizontal an der Wurzel" + +#: ../etc/cfg_menus.lua:40 +msgid "(Un)tag" +msgstr "Markieren" + +#: ../mod_query/mod_query.lua:459 +msgid "View file:" +msgstr "Zeige Datei:" + +#: ../mod_query/mod_query.lua:421 +msgid "Workspace name:" +msgstr "Name des Arbeitsbereichs:" + +#: ../etc/cfg_menus.lua:31 +msgid "Exit" +msgstr "Beenden" + +#: ../etc/cfg_menus.lua:27 +msgid "Save" +msgstr "Speichern" + +# +#: ../pwm/cfg_pwm_menus.lua:15 +msgid "Workspaces" +msgstr "Arbeitsbereiche" + +#: ../etc/cfg_menus.lua:20 +msgid "Mozilla Firefox" +msgstr "Mozilla Firefox" + +#: ../ioncore/ioncore-bindings.lua:45 +msgid "Error compiling guard: %s" +msgstr "Fehler Erstellungswächter: %s" + +#: ../mod_menu/mod_menu.lua:271 +msgid "Refresh list" +msgstr "Aktualisiere Liste" + +#: ../etc/cfg_floatws.lua:9 +msgid "Circulate focus and raise the newly focused frame." +msgstr "Rückwärts laufender Fokus und zeige fokussiertes Frame." + +# +#: ../etc/cfg_menus.lua:21 +msgid "Run..." +msgstr "Starte..." + +#: ../etc/cfg_bindings.lua:118 +msgid "Query for file to view." +msgstr "Anfrage um Datei anzuzeigen." + +#: ../mod_query/mod_query.lua:511 +msgid "Failed to open ~/.ssh/known_hosts" +msgstr "Konnte ~/.ssh/known_hosts nicht öffnen." + +#: ../pwm/cfg_pwm_menus.lua:18 ../etc/cfg_menus.lua:13 +msgid "Session" +msgstr "Sitzung" + +#: ../pwm/cfg_pwm_menus.lua:27 +msgid "List" +msgstr "Anzeige" + +#~ msgid "Vertically/root" +#~ msgstr "Pystysuunnassa juuressa" + +# +# +#~ msgid "Horizontally/root" +#~ msgstr "Vaakasuunnassa juuressa" + +#~ msgid "Transpose parent" +#~ msgstr "Käännä vanhempi" + +#~ msgid "Flip parent" +#~ msgstr "Peilaa vanhempi" diff --git a/po/fi.po b/po/fi.po new file mode 100644 index 0000000..229dc45 --- /dev/null +++ b/po/fi.po @@ -0,0 +1,1760 @@ +# -*- encoding: iso-8859-1 -*- +# +# Finnish language translations for Ion3. +# +# Copyright (c) Tuomo Valkonen 2004. +# +# This file is distributed under the same license as the Ion3 package. +# Tuomo Valkonen , 2004. +# +msgid "" +msgstr "" +"Project-Id-Version: Ion3\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2006-11-11 17:36+0200\n" +"PO-Revision-Date: 2004-07-31 23:50+0300\n" +"Last-Translator: Tuomo Valkonen \n" +"Language-Team: none\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=iso-8859-1\n" +"Content-Transfer-Encoding: 8bit\n" + +#: ../ioncore/conf-bindings.c:96 +msgid "Insane key combination." +msgstr "Järjetön näppäinyhdistelmä." + +#: ../ioncore/conf-bindings.c:100 +msgid "Could not convert keysym to keycode." +msgstr "Näppäinsymbolin muunto näppäinkoodiksi epäonnistui." + +#: ../ioncore/conf-bindings.c:111 +#, c-format +msgid "Unknown button \"%s\"." +msgstr "Tuntematon nappi \"%s\"." + +#: ../ioncore/conf-bindings.c:116 +msgid "Insane button combination." +msgstr "Järjetön nappiyhdistelmä." + +#: ../ioncore/conf-bindings.c:123 ../ioncore/conf-bindings.c:130 +msgid "Insane modifier combination." +msgstr "Järjestön näppäinmuunninyhdistelmä." + +#: ../ioncore/conf-bindings.c:168 +#, c-format +msgid "Can not wait on modifiers when no modifiers set in \"%s\"." +msgstr "" +"Näppäinmuuntimia ei voida odottaa sidonnassa \"%s\", koska siinä ei ole " +"niitä." + +#: ../ioncore/conf-bindings.c:186 +#, c-format +msgid "Unable to add binding %s." +msgstr "Ei voitu lisätä sidontaa %s." + +#: ../ioncore/conf-bindings.c:191 +#, c-format +msgid "Unable to remove binding %s." +msgstr "Ei voitu poistaa sidontaa %s." + +#: ../ioncore/conf-bindings.c:230 +#, c-format +msgid "Unable to add submap for binding %s." +msgstr "Ei voida lisätä alikarttaa sidonnalle %s." + +# +#: ../ioncore/conf-bindings.c:260 +msgid "Binding type not set." +msgstr "Sidonnan tyyppiä ei ole asetettu." + +#: ../ioncore/conf-bindings.c:270 +#, c-format +msgid "Unknown binding type \"%s\"." +msgstr "Sidonnalle annettu tyyppi \"%s\" on tuntematon." + +#: ../ioncore/conf-bindings.c:292 +#, c-format +msgid "Unknown area \"%s\" for binding %s." +msgstr "Sidonnalle %s asetettu tuntematon alue \"%s.\"" + +#: ../ioncore/conf-bindings.c:333 +#, c-format +msgid "Unable to get bindmap entry %d." +msgstr "Sidontakartan alkion %d haussa epäonnistuttiin." + +#: ../ioncore/conf-bindings.c:375 +msgid "Unable to convert keysym to string." +msgstr "Näppäinsymbolia muunto merkkijonoksi epäonnistui." + +#: ../ioncore/conf-bindings.c:389 +msgid "Unable to convert button to string." +msgstr "Napin tunnistetteen muunto merkkijonoksi epäonnistui." + +# +#: ../ioncore/event.c:113 +msgid "Time request from X server failed." +msgstr "Ajan pyyntö X-palvelimelta epäonnistui." + +# +#: ../ioncore/exec.c:177 +msgid "Not saving state: running under session manager." +msgstr "Tilaa ei talleteta, koska olemme istunnonhallinnan alaisia." + +#: ../ioncore/strings.c:107 ../ioncore/strings.c:143 ../ioncore/strings.c:176 +msgid "Invalid multibyte string." +msgstr "Virheellinen multibyte-merkkijono." + +#: ../ioncore/strings.c:267 +#, c-format +msgid "Error compiling regular expression: %s" +msgstr "Virhe säännölisen lausekkeen käännöksessä: %s" + +#: ../ioncore/modules.c:158 +msgid "Invalid module name." +msgstr "Moduulin nimi on tuntematon." + +# +#: ../ioncore/modules.c:170 +msgid "The module is already loaded." +msgstr "Moduuli on jo ladattu." + +#: ../ioncore/modules.c:185 +msgid "" +"Module version information not found or version mismatch. Refusing to use." +msgstr "" +"Moduulin versiotieto joko puuttuu tai on väärä. Näin ollen sitä " +"kieltäydytään käyttämästä." + +#: ../ioncore/modules.c:196 +#, c-format +msgid "Unable to initialise module %s." +msgstr "Virhe moduulin %s alustuksessa." + +#: ../ioncore/modules.c:220 ../../libextl-3/readconfig.c:388 +#, c-format +msgid "Unable to find '%s' on search path." +msgstr "Hakupolulta ei löytynyt '%s':ää." + +#: ../ioncore/modules.c:291 +msgid "Unknown module." +msgstr "Tuntematon moduuli." + +#: ../ioncore/modules.c:299 +msgid "Unable to initialise module." +msgstr "Moduulin alustus epäonnistui." + +#: ../ioncore/modules.c:344 +msgid "No module to load given." +msgstr "Ladattava moduulia ei ole annettu." + +# +#: ../ioncore/property.c:355 ../ioncore/property.c:364 +msgid "Invalid arguments." +msgstr "Virheelliset parametrit." + +#: ../ioncore/screen.c:298 +msgid "act: " +msgstr "act: " + +# +#: ../ioncore/screen.c:481 +msgid "Only workspace may not be destroyed." +msgstr "Ainoata työpöytää ei voi tuhota." + +# +#: ../ioncore/screen.c:489 +msgid "Screens may not be destroyed." +msgstr "Näyttöjä ei voi tuhota." + +#: ../ioncore/screen.c:525 +msgid "Invalid offset." +msgstr "Virheellinen poikkeama." + +#: ../ioncore/screen.c:568 +#, c-format +msgid "Unable to create a workspace on screen %d." +msgstr "Työpöydän luonti ruudulle %d epäonnistui." + +#: ../ioncore/sizehint.c:151 +msgid "Invalid client-supplied width/height increment." +msgstr "Asiakkaan ilmoittama pysty/vaakalisäys koolle on virheellinen." + +#: ../ioncore/sizehint.c:159 +msgid "Invalid client-supplied aspect-ratio." +msgstr "Asiakkaan ilmoittama sivusuhde on virheellinen." + +#: ../ioncore/ioncore.c:74 +msgid "" +"This program is free software; you can redistribute it and/or\n" +"modify it under the terms of the GNU Lesser General Public\n" +"License as published by the Free Software Foundation; either\n" +"version 2.1 of the License, or (at your option) any later version.\n" +"\n" +"This program is distributed in the hope that it will be useful,\n" +"but WITHOUT ANY WARRANTY; without even the implied warranty of\n" +"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n" +"Lesser General Public License for more details.\n" +msgstr "" +"Tämä ohjelma on vapaa; tätä ohjelmaa on sallittu levittää edelleen\n" +"ja muuttaa GNU vähäisemmän yleisen lisenssin (LGPL lisenssin) ehtojen\n" +"mukaan sellaisina kuin Free Software Foundation on ne julkaissut; joko\n" +"Lisenssin version 2.1, tai (valinnan mukaan) minkä tahansa myöhemmän\n" +"version mukaisesti.\n" +"\n" +"Tätä ohjelmaa levitetään siinä toivossa, että se olisi hyödyllinen,\n" +"mutta ilman mitään takuuta; ilman edes hiljaista takuuta kaupallisesti\n" +"hyväksyttävästä laadusta tai soveltuvuudesta tiettyyn tarkoitukseen.\n" +"Katso LGPL lisenssistä lisää yksityiskohtia.\n" + +#: ../ioncore/ioncore.c:155 +msgid "No encoding given in LC_CTYPE." +msgstr "LC_CTYPE ei kerro enkoodausta." + +#: ../ioncore/ioncore.c:437 +#, c-format +msgid "Could not connect to X display '%s'" +msgstr "Yhteydenotto X-palvelimeen '%s' epäonnistui." + +#: ../ioncore/ioncore.c:489 +msgid "Could not find a screen to manage." +msgstr "Yhtään hallittavissa olevaa juuri-ikkunaa ei löytynyt." + +#: ../ioncore/xic.c:38 +msgid "Failed to open input method." +msgstr "Syöttömenetelmän avaaminen epäonnistui." + +#: ../ioncore/xic.c:43 +msgid "Input method doesn't support any style." +msgstr "Syöttömenetelmä ei tue yhtään syöttötapaa." + +#: ../ioncore/xic.c:58 +msgid "input method doesn't support my preedit type." +msgstr "Syöttömenetelmä ei tue kaivattua esimuokkaustyyliä." + +#: ../ioncore/xic.c:86 +msgid "Failed to create input context." +msgstr "Syöttökontekstin luonti epäonnistui." + +#: ../ioncore/clientwin.c:411 +#, c-format +msgid "The transient_for hint for \"%s\" points to itself." +msgstr "Ikkunan \"%s\" transient_for neuvo osoittaa itseensä." + +#: ../ioncore/clientwin.c:415 +#, c-format +msgid "" +"Client window \"%s\" has broken transient_for hint. (\"Extended WM hints\" " +"multi-parent brain damage?)" +msgstr "Ikkunan \"%s\" transient_for neuvo on rikki." + +#: ../ioncore/clientwin.c:420 +#, c-format +msgid "The transient_for window for \"%s\" is not on the same screen." +msgstr "Ikkunan \"%s\" transient_for neuvo osoittaa eri näytölle." + +#: ../ioncore/clientwin.c:440 ../ioncore/clientwin.c:527 +#: ../ioncore/clientwin.c:1300 +#, c-format +msgid "Window %#x disappeared." +msgstr "Ikkuna %#x katosi." + +#: ../ioncore/clientwin.c:547 +msgid "Unable to find a matching root window!" +msgstr "Vastaavaa juuri-ikkunaa ei löytynyt." + +#: ../ioncore/clientwin.c:582 +#, c-format +msgid "Unable to manage client window %#x." +msgstr "Asiakasikkunan %x hallittavaksi saattaminen epäonnistui." + +#: ../ioncore/clientwin.c:633 +msgid "Changes is WM_TRANSIENT_FOR property are unsupported." +msgstr "Muutoksia WM_TRANSIENT_FOR ominaisuudessa ei tueta." + +# +#: ../ioncore/clientwin.c:809 +msgid "Client does not support the WM_DELETE protocol." +msgstr "Asiakasikkuna ei tue WM_DELETE-protokollaa." + +#: ../ioncore/clientwin.c:1306 +msgid "Saved client window does not want to be managed." +msgstr "Talletettu asiakasikkuna ei halua sitä hallittavan." + +# +#: ../ioncore/colormap.c:96 +msgid "Unable to store colourmap watch info." +msgstr "Värikartan valvonnan alustaminen epäonnistui." + +#: ../ioncore/region.c:46 +msgid "Creating region with negative width or height!" +msgstr "Yritys luoda korkeudeltaan tai leveydeltään negatiivinen alue." + +#: ../ioncore/region.c:94 +#, c-format +msgid "Destroying object \"%s\" with client windows as children." +msgstr "Olio \"%s\", jolla on vielä asiakasikkunoita lapsena, tuhotaan." + +# +#: ../ioncore/region.c:395 +msgid "Failed to rescue some client windows - not closing." +msgstr "Joidenkin asiakasikkunoiden pelastaminen epäonnistui - ei suljeta." + +#: ../ioncore/attach.c:71 ../ioncore/pholder.c:46 +msgid "Unable to reparent." +msgstr "Vanhemman vaihto epäonnistui." + +#: ../ioncore/attach.c:157 ../ioncore/frame-pointer.c:282 +#, c-format +msgid "Attempt to make region %s manage its ancestor %s." +msgstr "Yritys saattaa alue %s hallitsemaan sen esivanhempaa %s." + +#: ../ioncore/manage.c:77 +msgid "Unable to find a screen for a new client window." +msgstr "Uudelle asiakasikkunalle ei löytynyt näyttöä." + +#: ../ioncore/rootwin.c:223 +#, c-format +msgid "Unable to redirect root window events for screen %d." +msgstr "Juuri-ikkunan %d viestien uudelleenohjaus epäonnistui." + +#: ../ioncore/rootwin.c:319 ../ioncore/rootwin.c:344 +msgid "Xinerama sanity check failed; overlapping screens detected." +msgstr "Xinerama-tiedon järkevyystarkastus havaitsi päällekkäisiä ruutuja." + +#: ../ioncore/rootwin.c:326 ../ioncore/rootwin.c:351 +msgid "Xinerama sanity check failed; zero size detected." +msgstr "Xinerama-tiedon järkevyystarkastus havaitsi päällekkäisiä ruutuja." + +#: ../ioncore/rootwin.c:379 ../ioncore/rootwin.c:397 +msgid "Don't know how to get Xinerama information for multiple X root windows." +msgstr "Xinerama-tietoa ei osata käyttää usealle juuri-ikkunalle." + +#: ../ioncore/rootwin.c:393 +msgid "Error retrieving Xinerama information." +msgstr "Xinerama-tietoja ei saatu." + +#: ../ioncore/rootwin.c:432 ../ioncore/rootwin.c:451 +#, c-format +msgid "Unable to setup Xinerama screen %d." +msgstr "Xinerama-ruudun %d hallittavaksi saattaminen epäonnistui." + +#: ../ioncore/rootwin.c:464 +#, c-format +msgid "Unable to setup X screen %d." +msgstr "X-ruudun %d hallittavaksi saattaminen epäonnistui." + +#: ../ioncore/names.c:90 +#, c-format +msgid "Corrupt instance number %s." +msgstr "Rikkinäinen instanssinumero %s." + +#: ../ioncore/saveload.c:125 +#, c-format +msgid "Unknown class \"%s\", cannot create region." +msgstr "Luokka \"%s\" on tuntematon. Aluetta ei voida luoda." + +#: ../ioncore/saveload.c:205 +#, c-format +msgid "" +"There were errors loading layout. Backing up current layout savefile as\n" +"%s.\n" +"If you are _not_ running under a session manager and wish to restore your\n" +"old layout, copy this backup file over the layout savefile found in the\n" +"same directory while Ion is not running and after having fixed your other\n" +"configuration files that are causing this problem. (Maybe a missing\n" +"module?)" +msgstr "" +"Talletetun sijoittelun latauksessa oli virheitä. Siitä tehdään varmuuskopio\n" +"%s.\n" +"Jos _et_ käytä istunnonhallintaohjelmaa ja haluat palauttaa edellisen\n" +"sijoittelun, kopioi tämä varmuuskopio uuden talletustiedoston päälle\n" +"samassa hakemistossa kun Ion ei ole ajossa, ja kun olet korjannut tämän\n" +"tilanteen aiheuttavat virheet (mahdollisesti puuttuva moduuli?) muissa\n" +"asetustiedostoissasi." + +#: ../ioncore/saveload.c:248 +msgid "Unable to get file for layout backup." +msgstr "Sijoittelun varmuuskopiolle ei voitu muodostaa tiedostonimeä." + +#: ../ioncore/saveload.c:252 +#, c-format +msgid "Backup file %s already exists." +msgstr "Varmuuskopio %s on jo olemassa." + +#: ../ioncore/saveload.c:258 +msgid "Failed backup." +msgstr "Varmuuskopionti epäonnistui." + +# +#: ../ioncore/saveload.c:263 +msgid "Unable to initialise layout on any screen." +msgstr "Sijoittelun alustus epäonnistui kaikilla näytöillä." + +#: ../ioncore/saveload.c:283 +#, c-format +msgid "Unable to get configuration for screen %d." +msgstr "Ruudun %d asetukset puuttuvat." + +#: ../ioncore/saveload.c:296 +msgid "Unable to save layout." +msgstr "Sijoittelun talletus epäonnistui." + +#: ../ioncore/conf.c:143 +msgid "User directory can not be set." +msgstr "Käyttäjän hakemistoa ei pystytä asettamaan." + +#: ../ioncore/conf.c:217 +msgid "Some bindmaps were empty, loading ioncore_efbb." +msgstr "Jotkut sidontakartat olivat tyhjiä. Ladataan ioncore_efbb." + +#: ../ioncore/fullscreen.c:98 +msgid "Failed to enter full screen mode." +msgstr "Vaihto kokoruudun tilaan epäonnistui." + +#: ../ioncore/fullscreen.c:120 +msgid "" +"Failed to return from full screen mode; remaining manager or parent from " +"previous location refused to manage us." +msgstr "" +"Asiakasikkunan paluu kokoruudun tilasta epäonnistui; edellinen hallitsija " +"tai vanhempi kieltäytyi hallitsemasta ikkunaa." + +# +#: ../ioncore/mplex.c:172 +msgid "Refusing to destroy - not empty." +msgstr "Tuhoamisesta kieltäydytään - ei tyhjä." + +# +#: ../ioncore/mplex.c:1678 +msgid "Invalid position setting." +msgstr "Virheellinen paikka-asetus." + +# +#: ../ioncore/mplex.c:1718 +msgid "Invalid action setting." +msgstr "Virheellinen toiminto-asetus." + +#: ../ioncore/gr.c:119 +#, c-format +msgid "Drawing engine %s is not registered!" +msgstr "Piirtomoottoria %s ei ole rekisteröity!" + +#: ../ioncore/gr.c:138 +#, c-format +msgid "Unable to find brush for style '%s'." +msgstr "Tyylille '%s' ei löytynyt pensseliä." + +#: ../ioncore/gr.c:408 +msgid "No drawing engines loaded, trying \"de\"." +msgstr "Yhtään piirtomoottoria ei ole ladattu. Yritetään oletusta \"de\"." + +#: ../ioncore/group.c:184 ../mod_tiling/tiling.c:96 +#, c-format +msgid "Error reparenting %s." +msgstr "Virhe alueen %s vanhemman vaihdossa." + +# +#: ../ioncore/group.c:481 ../mod_tiling/tiling.c:668 +msgid "Workspace not empty - refusing to destroy." +msgstr "Työpöytää ei voida tuhota, koska se ei ole tyhjä." + +#: ../ioncore/group.c:680 +msgid "'bottom' already set." +msgstr "'pohja' on jo asetettu." + +# +#: ../ioncore/navi.c:45 +msgid "Invalid parameter." +msgstr "Virheellinen parametri." + +#: ../ioncore/navi.c:72 +msgid "Invalid direction parameter." +msgstr "Virheellinen suunta." + +#: ../ioncore/group-ws.c:67 +#, c-format +msgid "Unknown placement method \"%s\"." +msgstr "Tuntematon sijoittelumenetelmä \"%s\"." + +# +# +#: ../mod_tiling/tiling.c:77 +msgid "Split not on workspace." +msgstr "Jako ei ole tällä työpöydällä." + +#: ../mod_tiling/tiling.c:373 +msgid "Unable to create a node for status display." +msgstr "Solmun luonti tilanäytölle epäonnistui." + +#: ../mod_tiling/tiling.c:386 +msgid "Unable to create new split for status display." +msgstr "Uuden jaon tekeminen tilanäytölle epäonnistui." + +#: ../mod_tiling/tiling.c:937 +msgid "Invalid direction" +msgstr "Virheellinen suunta." + +# +#: ../mod_tiling/tiling.c:970 ../mod_tiling/split.c:1018 +msgid "Invalid node." +msgstr "Epäkelpo solmu." + +# +#: ../mod_tiling/tiling.c:989 +msgid "Unable to split." +msgstr "Halkaisu ei ole mahdollista." + +#: ../mod_tiling/tiling.c:1092 +msgid "Nil frame." +msgstr "Kehys on asettamatta." + +#: ../mod_tiling/tiling.c:1097 +msgid "The frame is not managed by the workspace." +msgstr "Kehys ei ole tämän työpöydän hallinnoima." + +#: ../mod_tiling/tiling.c:1196 +msgid "Nil parameter." +msgstr "Parametri on asettamatta." + +#: ../mod_tiling/tiling.c:1201 +msgid "Manager doesn't match." +msgstr "Hallitsija on väärä." + +#: ../mod_tiling/tiling.c:1238 +msgid "The status display is not a valid parameter for this routine." +msgstr "Tilanäyttö ei ole kelpo parametri tälle toiminnolle." + +#: ../mod_tiling/tiling.c:1329 +msgid "Refusing to float split directly containing the status display." +msgstr "Tilanäytön suoraan sisältävää jakoa ei voida kelluttaa." + +#: ../mod_tiling/tiling.c:1392 +msgid "No suitable split here." +msgstr "Ei sopivaa jakoa tässä." + +# +#: ../mod_tiling/tiling.c:1428 +msgid "Could not get split tree." +msgstr "Halkaisupuu puuttuu." + +#: ../mod_tiling/tiling.c:1449 +msgid "Workspace already has a status display node." +msgstr "Työpöydällä on jo solmu tilanäytölle." + +# +#: ../mod_tiling/tiling.c:1487 +msgid "Missing region parameters." +msgstr "Alueen parameterit puuttuu." + +#: ../mod_tiling/tiling.c:1531 ../mod_tiling/splitfloat.c:780 +msgid "Invalid direction." +msgstr "Virheellinen suunta." + +# +#: ../mod_tiling/tiling.c:1606 +msgid "No split type given." +msgstr "Halkaisun tyyppiä ei ole asetettu." + +#: ../mod_tiling/tiling.c:1619 +msgid "Unknown split type." +msgstr "Tuntematon halkaisutyyppi." + +# +# +#: ../mod_tiling/tiling.c:1659 +msgid "The workspace is empty." +msgstr "Työpöytä on tyhjä." + +#: ../mod_tiling/placement.c:106 +#, c-format +msgid "" +"Ooops... could not find a region to attach client window to on workspace %s." +msgstr "Työpöydältä %s ei löytynyt aluetta johon liittää asiakasikkuna." + +# +#: ../mod_tiling/split.c:524 +msgid "Unable to move the status display out of way." +msgstr "Tilanäyttöä ei voida siirtää pois tieltä." + +#: ../mod_tiling/split.c:937 +msgid "REGION_RQGEOM_TRYONLY unsupported for status display." +msgstr "REGION_RQGEOM_TRYONLY:ä ei tueta tilanäytölle." + +# +#: ../mod_tiling/split.c:1082 +msgid "Splitting the status display is not allowed." +msgstr "Tilanäytön halkaisu ei ole sallittu." + +#: ../mod_tiling/split.c:1113 ../mod_tiling/splitfloat.c:903 +msgid "Unable to split: not enough free space." +msgstr "Ei voida halkaista: liian vähän tilaa." + +#: ../mod_tiling/split.c:1864 +#, c-format +msgid "Unable to get configuration for %s." +msgstr "Alueelle \"%s\" ei saatu asetuksia." + +#: ../mod_tiling/split-stdisp.c:602 ../mod_tiling/split-stdisp.c:627 +msgid "Status display in bad split configuration." +msgstr "Tilanäyttö huonossa halkaisuasetelmassa." + +#: ../mod_tiling/split-stdisp.c:667 +msgid "Status display badly located in split tree." +msgstr "Tilanäyttö on huonosti sijoittuneena halkaisupuuhun." + +#: ../mod_tiling/ops.c:28 +msgid "Already detached" +msgstr "Irroitettu jo" + +#: ../mod_tiling/ops.c:142 +msgid "Not member of a group" +msgstr "Ei jäsenenä missään ryhmässä" + +#: ../mod_tiling/ops.c:147 +msgid "Manager group already has bottom" +msgstr "Managerilla on jo 'pohja'" + +#: ../mod_query/wedln.c:777 +msgid "history" +msgstr "hist.täyd." + +#: ../mod_query/fwarn.c:34 +msgid "Error:\n" +msgstr "Virhe:\n" + +# +#: ../mod_menu/menu.c:537 +msgid "Empty menu." +msgstr "Tyhjä valikko." + +# +#: ../mod_sm/sm.c:110 +msgid "Failed to set session directory." +msgstr "Istuntohakemiston asetus epäonnistui." + +#: ../mod_sm/sm_session.c:86 +msgid "Too many ICE connections." +msgstr "Liian monta ICE-yhteyttä." + +#: ../mod_sm/sm_session.c:228 +msgid "Failed to save session state" +msgstr "Istunnon tilan tallentaminen epäonnistui." + +#: ../mod_sm/sm_session.c:247 +msgid "Failed to request save-yourself-phase2 from session manager." +msgstr "Save-yourself vaiheen kaksi pyyntö istunnonhallitsijalta epäonnistui." + +# +#: ../mod_sm/sm_session.c:296 +msgid "SESSION_MANAGER environment variable not set." +msgstr "Ympäristömuuttujaa SESSION_MANAGER ei ole asetettu." + +#: ../mod_sm/sm_session.c:301 +msgid "Session Manager: IceAddConnectionWatch failed." +msgstr "Virhe istunnonhallinan kutsussa IceAddConnectionWatch." + +# +#: ../mod_sm/sm_session.c:326 +msgid "Unable to connect to the session manager." +msgstr "Yhteydenotto istunnonhallintaohjelmaan epäonnistui." + +#: ../mod_sp/main.c:85 +msgid "Unable to create scratchpad." +msgstr "Suttausalueen luonti epäonnistui." + +#: ../mod_statusbar/main.c:75 +msgid "reading a pipe" +msgstr "putken luku" + +#: ../mod_statusbar/statusbar.c:1046 +#, c-format +msgid "[ %date || load: %load ] %filler%systray" +msgstr "" + +#: ../de/init.c:42 +#, c-format +msgid "Border attribute %s sanity check failed." +msgstr "Reunan arvon '%s' järkevyystarkistus epäonnistui." + +#: ../de/init.c:65 +#, c-format +msgid "Unknown border style \"%s\"." +msgstr "Reunan tyyli \"%s\" on tuntematon." + +#: ../de/init.c:97 +#, c-format +msgid "Unable to allocate colour \"%s\"." +msgstr "Värin \"%s\" varaaminen epäonnistui." + +#: ../de/init.c:160 +#, c-format +msgid "Corrupt substyle table %d." +msgstr "Alityylin %d taulu on rikki." + +#: ../de/init.c:193 +#, c-format +msgid "Unknown text alignment \"%s\"." +msgstr "Tuntematon tekstin tasaus \"%s\"." + +#: ../de/init.c:263 +#, c-format +msgid "'based_on' for %s points back to the style itself." +msgstr "Tyylin %s 'based_on' -asetus osoittaa itseensä." + +#: ../de/init.c:266 +#, c-format +msgid "Unknown base style \"%s\"." +msgstr "Tuntematon pohjatyyli \"%s\"." + +#: ../de/font.c:47 +#, c-format +msgid "" +"Fontset for font pattern '%s' implements context dependent drawing, which is " +"unsupported. Expect clutter." +msgstr "" +"Kirjaisinjoukko kirjaisinmallille '%s' vaatii kontekstiriippuvaista piirtoa, " +"jota ei tueta. Odotettavissa on sotkua." + +#: ../de/font.c:59 +#, c-format +msgid "Could not load font \"%s\", trying \"%s\"" +msgstr "Kirjaisimen \"%s\" lataaminen epäonnistui. Yritetään \"%s\":ää." + +#: ../de/font.c:63 +msgid "Failed to load fallback font." +msgstr "Hätävarakirjaisimen lataus epäonnistui." + +#: ../de/style.c:315 +#, c-format +msgid "Style %s still in use [%d] but the module is being unloaded!" +msgstr "Tyyli %s on vielä käytössä [%d], mutta moduulia poistetaan!" + +#: ../ion/ion.c:42 ../pwm/pwm.c:42 +msgid "X display to use" +msgstr "Käytettävä X-palvelin/näyttö" + +#: ../ion/ion.c:45 ../pwm/pwm.c:45 +msgid "Configuration file" +msgstr "Ensisijainen asetustiedosto" + +#: ../ion/ion.c:48 ../pwm/pwm.c:48 +msgid "Add directory to search path" +msgstr "Lisää hakemisto hakupolulle" + +#: ../ion/ion.c:51 ../pwm/pwm.c:51 +msgid "Manage default root window/non-Xinerama screen only" +msgstr "" +"Hallitse vain oletusarvoista juuri-ikkunaa. (Vastaa ensisijaista näyttöä jos " +"Xinerama ei ole käytössä.)" + +#: ../ion/ion.c:55 +msgid "Use Xinerama screen information (default: 1/yes)" +msgstr "Hyödynnä Xineramaa (oletus: 1/kyllä)" + +#: ../ion/ion.c:58 ../pwm/pwm.c:58 +msgid "Ignored: not compiled with Xinerama support" +msgstr "Jätetään huomiotta: ohjelmaa ei ole käännetty Xinerama-tuella" + +#: ../ion/ion.c:62 ../pwm/pwm.c:62 +msgid "Name of session (affects savefiles)" +msgstr "Istunnon nimi (vaikuttaa talletustiedostoihin)" + +#: ../ion/ion.c:65 ../pwm/pwm.c:65 +msgid "Session manager client ID" +msgstr "Istunnonhallinnan asiakastunniste" + +#: ../ion/ion.c:68 ../pwm/pwm.c:68 +msgid "Do not create startup error log and display it with xmessage." +msgstr "Älä luo käynnistysvirhelokia ja näytä sitä xmessage:lla." + +#: ../ion/ion.c:72 ../pwm/pwm.c:72 +msgid "Show this help" +msgstr "Näytä tämä aputeksti" + +#: ../ion/ion.c:75 ../pwm/pwm.c:75 +msgid "Show program version" +msgstr "Näytä ohjelman versio" + +#: ../ion/ion.c:78 ../pwm/pwm.c:78 +msgid "Show about text" +msgstr "Näytä tietoja ohjelmasta" + +#: ../ion/ion.c:93 +msgid "Could not get user configuration file directory." +msgstr "Käyttäjän asetustiedostohakemisto puuttuu, eikä sitä voitu luoda." + +#: ../ion/ion.c:107 +#, c-format +msgid "%s/welcome.txt" +msgstr "%s/welcome.fi.txt" + +#: ../ion/ion.c:140 ../pwm/pwm.c:87 +#, c-format +msgid "" +"Usage: %s [options]\n" +"\n" +msgstr "" +"Käyttö: %s [valintoja]\n" +"\n" + +#: ../ion/ion.c:200 ../pwm/pwm.c:150 +msgid "Invalid parameter to -xinerama." +msgstr "Virheellinen parametri -xinerama:lle" + +# +#: ../ion/ion.c:219 ../pwm/pwm.c:169 +msgid "Invalid command line." +msgstr "Virheellinen komentorivi." + +#: ../ion/ion.c:241 +msgid "Ion startup error log:\n" +msgstr "Ionin käynnistysvirheloki:\n" + +#: ../ion/ion.c:252 ../pwm/pwm.c:202 +msgid "Refusing to start due to encountered errors." +msgstr "Ohjelma kieltäytyy käynnistymästä tavattujen virheiden johdosta." + +#: ../pwm/pwm.c:55 +msgid "Use Xinerama screen information (default: 0/no)" +msgstr "Hyödynnä Xineramaa (oletus: 0/ei)" + +#: ../pwm/pwm.c:191 +msgid "PWM startup error log:\n" +msgstr "PWM:n käynnistysvirheloki:\n" + +#: ../../libextl-3/readconfig.c:86 +msgid "$HOME not set" +msgstr "Ympäristömuuttujaa HOME ei ole asetettu." + +#: ../../libextl-3/readconfig.c:113 +msgid "User directory not set. Unable to set session directory." +msgstr "" +"Käyttäjän hakemistoa ei ole asetettu, joten istuntohakemistoa ei voida " +"asettaa." + +#: ../../libextl-3/readconfig.c:254 +#, c-format +msgid "Falling back to %s." +msgstr "Yritetään tiedostoa %s." + +#: ../../libextl-3/readconfig.c:474 +#, c-format +msgid "Unable to create session directory \"%s\"." +msgstr "Istuntohakemiston \"%s\" luonti epäonnistui." + +#: ../../libextl-3/luaextl.c:117 +msgid "Lua stack full." +msgstr "Luan pino on täysi." + +# +#: ../../libextl-3/luaextl.c:143 +msgid "Unknown Lua error." +msgstr "Tuntematon Lua:n virhe." + +#: ../../libextl-3/luaextl.c:490 +msgid "Stack trace:" +msgstr "Pinojälki:" + +#: ../../libextl-3/luaextl.c:497 +#, c-format +msgid "" +"\n" +"(Unable to get debug info for level %d)" +msgstr "" +"\n" +"(Debuggaustiedot eivät ole saatavilla tasolle %d)" + +#: ../../libextl-3/luaextl.c:515 +msgid "" +"\n" +" [Skipping unnamed C functions.]" +msgstr "" +"\n" +" [Ohitetaan nimettömiä C-funktioita.]" + +#: ../../libextl-3/luaextl.c:566 +msgid "Internal error." +msgstr "Sisäinen virhe." + +#: ../../libextl-3/luaextl.c:585 +msgid "Unable to initialize Lua." +msgstr "Lua:n alustus epäonnistui." + +#: ../../libextl-3/luaextl.c:1336 +msgid "" +"Too many return values. Use a C compiler that has va_copy to support more." +msgstr "" +"Liian monta paluuarvoa. Useamman tukemiseksi käytä C-kääntäjää, joka tukee " +"va_copy:ä." + +#: ../../libextl-3/luaextl.c:1356 +msgid "Returned dead object." +msgstr "Kuollut olio palautettu." + +#: ../../libextl-3/luaextl.c:1359 +#, c-format +msgid "Invalid return value (expected '%c', got lua type \"%s\")." +msgstr "" +"Virheellinen paluuarvo (odotettiin tyyppiä '%c', mutta saatiin Lua:n tyyppi " +"\"%s\")." + +#: ../../libextl-3/luaextl.c:1395 ../../libextl-3/luaextl.c:1750 +msgid "Stack full." +msgstr "Pino täysi." + +#: ../../libextl-3/luaextl.c:1761 +#, c-format +msgid "Argument %d to %s is a dead object." +msgstr "Parametri %d %s:lle on kuollut olio." + +#: ../../libextl-3/luaextl.c:1764 +#, c-format +msgid "" +"Argument %d to %s is of invalid type. (Argument template is '%s', got lua " +"type %s)." +msgstr "" +"Parameteri %d funktiolle %s on väärää tyyppiä. (Parametripohja on '%s', " +"saatiin Lua:n tyyppi '%s'.)" + +#: ../../libextl-3/luaextl.c:1827 +msgid "L1 call handler upvalues corrupt." +msgstr "Tason 1 kutsunkäsittelijän \"upvalue\":t rikki." + +#: ../../libextl-3/luaextl.c:1832 +msgid "Called function has been unregistered." +msgstr "Kutsuttu funktio on poistettu." + +#: ../../libextl-3/luaextl.c:1843 +#, c-format +msgid "Attempt to call an unsafe function \"%s\" in restricted mode." +msgstr "Yritys kutsua turvatonta funktiota \"%s\" rajoitetussa tilassa." + +#: ../../libextl-3/luaextl.c:1956 +#, c-format +msgid "" +"Function '%s' has more parameters than the level 1 call handler can handle" +msgstr "" +"Funktiolla %s on enemmän parametrejä kuin mihin tason yksi kutsunkäsittelijä " +"kykenee." + +# +#: ../../libextl-3/luaextl.c:2373 +msgid "Maximal serialisation depth reached." +msgstr "Suurin mahdollinen talletuksen rekursiosyvyys saavutettu." + +#: ../../libextl-3/luaextl.c:2394 +#, c-format +msgid "Unable to serialise type %s." +msgstr "Tyyppiä %s ei voida tallettaa." + +#: ../../libextl-3/luaextl.c:2425 +msgid "-- This file has been generated by Ion. Do not edit.\n" +msgstr "-- Tämä tiedosto on Ionin luoma. Älä editoi sitä.\n" + +#: ../../libextl-3/misc.c:17 +#, c-format +msgid "" +"Type checking failed in level 2 call handler for parameter %d (got %s, " +"expected %s)." +msgstr "" +"Tyyppitarkistus epäonnistui tason 2 kutsunkäsittelijässä parametrille #%d " +"(oli %s, odotettiin %s)." + +msgid "Grow-D" +msgstr "Kasvu: alas" + +msgid "Grow-U" +msgstr "Kasvu: ylös" + +msgid "Grow-R" +msgstr "Kasvu: oikealle" + +msgid "Grow-L" +msgstr "Kasvu: vasemmalle" + +msgid "Pos-BR" +msgstr "Paikka: alaoikea" + +msgid "Pos-BL" +msgstr "Paikka: alavasen" + +msgid "Pos-TR" +msgstr "Paikka: yläoikea" + +msgid "Pos-TL" +msgstr "Paikka: ylävasen" + +msgid "Toggle floating dock." +msgstr "Näytä/piiloita kelluva laituri (dock)." + +msgid "Clear the menu's typeahead find buffer." +msgstr "Tyhjää valikon hakupuskuri." + +msgid "Select next/previous menu entry." +msgstr "Siirry seuraavaan/edelliseen valintaan." + +# +# +msgid "Activate current menu entry." +msgstr "Suorita valinta." + +msgid "Close the menu." +msgstr "Sulje valikko." + +msgid "New tiling" +msgstr "Uusi laatoitus" + +msgid "Detach" +msgstr "Irroita" + +# +msgid "Transpose" +msgstr "Käännä" + +# +msgid "Flip" +msgstr "Peilaa" + +# +# +msgid "Split horizontally" +msgstr "Jaa vaakasuunnassa" + +# +# +msgid "Split vertically" +msgstr "Jaa pystysuunnassa" + +# +msgid "At root" +msgstr "Juuressa" + +msgid "Below" +msgstr "Alapuolella" + +msgid "Above" +msgstr "Yläpuolella" + +msgid "At right" +msgstr "Oikealla" + +msgid "At left" +msgstr "Vasemmalla" + +msgid "Float split" +msgstr "Kelluta" + +# +msgid "Destroy frame" +msgstr "Tuhoa kehys" + +msgid "Tile frame, if no tiling exists on the workspace" +msgstr "Laatoita kehys, jos työpöydällä ei ole kehystä" + +msgid "Detach window from tiled frame" +msgstr "Irroita ikkuna laatoitetusta kehyksestä" + +msgid "Destroy current frame." +msgstr "Tuhoa tämänhetkinen kehys." + +msgid "Split current frame horizontally." +msgstr "Halkaise tämänhetkinen kehys vaakasuunnassa." + +msgid "Go to frame above/below/right/left of current frame." +msgstr "" +"Siirry tämänhetkisen kehyksen yllä/alla/vasemmalle/oikealla olevaan " +"kehykseen." + +msgid "Split current frame vertically." +msgstr "Halkaise tämänhetkinen kehys pystysuunnassa." + +msgid "Failed to create statusbar." +msgstr "Epäonnistuttiin tilarivin luonnissa" + +msgid "Screen already has an stdisp. Refusing to create a statusbar." +msgstr "Ruudulla on jo tilanäyttö. Kieltäydytään luomasta tilariviä." + +msgid "Screen not found." +msgstr "Ruutua ei löytynyt." + +msgid "Failed to start ion-statusd." +msgstr "Ion-statusd:n käynnistys eopäonnistui." + +msgid "Errors starting ion-statusd:\n" +msgstr "Ion-statusd:n käynnistysvirheet:\n" + +msgid "Could not find %s" +msgstr "Ei löydettyä %s:ää." + +msgid "ion-statusd quit." +msgstr "ion-statusd päätti suorituksen." + +msgid "Not a directory." +msgstr "Ei hakemisto." + +msgid "Invalid command" +msgstr "Virheellinen komento" + +msgid "Error in command string: " +msgstr "Virhe komentojonossa:" + +msgid "Error compiling guard: %s" +msgstr "Virhe vahdin käännössä. %s" + +msgid "Invalid guard %s." +msgstr "Virheellinen vahti %s." + +msgid "Main menu:" +msgstr "Päävalikko:" + +msgid "Context menu:" +msgstr "Kontekstivalikko:" + +msgid "Floating frame" +msgstr "Kelluva kehys" + +msgid "Tiled frame" +msgstr "Laatoitettu kehys" + +msgid "Tiling" +msgstr "Laatoitus" + +# +msgid "Workspace" +msgstr "Työpöytä" + +msgid "Screen" +msgstr "Näyttö" + +msgid "Frame" +msgstr "Kehys" + +msgid "Recursive table - unable to deepcopy" +msgstr "Rekursiivinen taulu - ei voida syväkopioida." + +msgid "" +"Making the following minimal emergency mappings:\n" +" F2 -> xterm\n" +" F11 -> restart\n" +" F12 -> exit\n" +" Mod1+C -> close\n" +" Mod1+K P/N -> WFrame.switch_next/switch_prev\n" +msgstr "" +"Tehdään seuraavat vähimmäiset hätäsidonnat:\n" +" F2 -> xterm\n" +" F11 -> uudelleenkäynnistys\n" +" F12 -> lopetus\n" +" Mod1+C -> sulkeminen\n" +" Mod1+K P/N -> vaihto kehyksen sisällä\n" + +msgid "Unable to append to non-table menu" +msgstr "Ei voida lisätä valikkoon, koska sitä ei ole määritelty tauluna " + +msgid "Cannot save selection." +msgstr "Ei voida tallettaa valintaa." + +msgid "Save look selection in %s?" +msgstr "Talletaanko ulkonäköasetus tiedostoon %s?" + +msgid "Too much result data" +msgstr "Liian suuri vastaus" + +msgid "Could not find client window %s." +msgstr "Asiakasikkunaa %s ei löytynyt." + +msgid "Cannot attach: different root windows." +msgstr "Ei voida liittää: eri juuri-ikkunat." + +msgid "Unknown error" +msgstr "Tuntematon virhe" + +msgid "Go to window:" +msgstr "Mene ikkunaan:" + +msgid "Attach window:" +msgstr "Liitä ikkuna:" + +msgid "Go to or create workspace:" +msgstr "Mene tai luo työpöytä:" + +msgid "Exit Ion/Shutdown session (y/n)?" +msgstr "Poistu Ionista/Lopeta istunto (kyllä: y, ei: n)?" + +msgid "Restart Ion (y/n)?" +msgstr "Uudelleenkäynnistä Ion (kyllä: y, ei: n)?" + +msgid "Frame name:" +msgstr "Kehyksen nimi:" + +msgid "Workspace name:" +msgstr "Työpöydän nimi:" + +msgid "Edit file:" +msgstr "Muokkaa tiedostoa:" + +msgid "View file:" +msgstr "Näytä tiedosto:" + +msgid "Run:" +msgstr "Suorita:" + +msgid "Failed to open ~/.ssh/known_hosts" +msgstr "Tiedostoa ~/.ssh/known_hosts avaaminen epäonnistui." + +msgid "Failed to open ~/.ssh/config" +msgstr "Tiedoston ~/.ssh/config avaaminen epäonnistui." + +msgid "SSH to:" +msgstr "Avaa SSH-yhteys:" + +msgid "Manual page (%s):" +msgstr "Ohjesivu (%s):" + +msgid "Lua code:" +msgstr "Lua-koodia:" + +msgid "Unknown menu %s." +msgstr "Tuntematon valikko %s." + +msgid "Missing submenu " +msgstr "Puuttuva alivalikko " + +msgid "%s:" +msgstr "" + +msgid "No entry '%s'" +msgstr "Tuntematon valinta '%s'." + +msgid "" +"\n" +"%sClass: %s\n" +"%sRole: %s\n" +"%sInstance: %s\n" +"%sXID: 0x%x" +msgstr "" +"\n" +"Luokka(class): %s\n" +"Rooli(role): %s\n" +"Instanssi(instance): %s\n" +"XID: 0x%x" + +msgid "Toggle scratchpad." +msgstr "Kytke suttausalue päälle/pois" + +msgid "Programs" +msgstr "Ohjelmat" + +msgid "Lock screen" +msgstr "Lukitse näyttö" + +msgid "Help" +msgstr "Ohjeet" + +# +msgid "Workspaces" +msgstr "Työpöydät" + +msgid "New" +msgstr "Uusi" + +msgid "Styles" +msgstr "Tyylit" + +msgid "Session" +msgstr "Istunto" + +msgid "Close" +msgstr "Sulje" + +msgid "List" +msgstr "Lista" + +# +msgid "" +"Switch to n:th object (workspace, full screen client window) within current " +"screen." +msgstr "" +"Siirry n:teen tämänhetkisen näytön lomittamaan olioon (työpöytä, kokoruudun " +"asiakasikkuna)." + +# +msgid "Switch to next/previous object within current screen." +msgstr "Siirry seuraavaan/edelliseen tämänhetkisen näytön lomittamaan olioon." + +msgid "Go to first region demanding attention or previously active one." +msgstr "" +"Mene ensimmäiseen huomiota vaativaan alueeseen, tai edelliseen aktiiviseen." + +# +msgid "Clear all tags." +msgstr "Poista kaikki merkinnät." + +# +msgid "Go to n:th screen on multihead setup." +msgstr "Siirry näytölle n usean näytön järjestelmässä." + +# +msgid "Go to next/previous screen on multihead setup." +msgstr "Mene seuraavalle/edelliselle näytölle usean näytön järjestelmässä." + +# +# +msgid "Create a new workspace of chosen default type." +msgstr "Luo uusi työpöytä ennalta asetettua tyyppiä." + +msgid "Display the main menu." +msgstr "Näytä päävalikko." + +msgid "Display the window list menu." +msgstr "Näytä ikkunavalikko." + +msgid "Forward-circulate focus." +msgstr "Kierrätä fokusta eteenpäin." + +msgid "Backward-circulate focus." +msgstr "Kierrätä fokusta taaksepäin" + +msgid "Raise focused object, if possible." +msgstr "Nosta fokusoitu olio, jos mahdollista." + +# +msgid "" +"Nudge the client window. This might help with some programs' resizing " +"problems." +msgstr "" +"Tönäise aktiivista asiakasikkunaa. Tämä saattaa auttaa joidenkin ohjelmien " +"koko-ongelmien kanssa." + +# +msgid "Kill client owning the client window." +msgstr "Tapa aktiivisen asiakasikkunan omistava ohjelma." + +# +msgid "" +"Send next key press to the client window. Some programs may not allow this " +"by default." +msgstr "" +"Lähetä seuraava näppäinpainallus aktiiviselle asiakasikkunalle. Kaikki " +"ohjelmat eivät välttämättä salli tätä oletuksena." + +msgid "Toggle client window group full-screen mode" +msgstr "Muuta asiakasikkunaryhmän kokoruudun tilaa" + +msgid "Close current object." +msgstr "Sulje aktiivinen olio." + +msgid "Query for manual page to be displayed." +msgstr "Kysy ohjesivua näytettäväksi." + +msgid "Show the Ion manual page." +msgstr "Näytä Ionin ohjesivu." + +# +msgid "Run a terminal emulator." +msgstr "Käynnistä pääte-emulaattori." + +msgid "Query for command line to execute." +msgstr "Kysy komentoriviä suoritettavaksi." + +msgid "Query for Lua code to execute." +msgstr "Kysy Lua-koodia ajettavaksi." + +msgid "Query for host to connect to with SSH." +msgstr "Ota SSH-yhteys kyseltävään koneeseen." + +msgid "Query for file to edit." +msgstr "Kysy tiedostoa muokattavaksi." + +msgid "Query for file to view." +msgstr "Kysy tiedostoa näytettäväksi." + +msgid "Query for workspace to go to or create a new one." +msgstr "Kysy työpöytää jolle siirtyä, tai luo uusi." + +# +msgid "Query for a client window to go to." +msgstr "Kysy asiakasikkunaa, johon siirtyä." + +# +msgid "Query for a client window to attach." +msgstr "Kysy liitettävää asiakasikkunaa." + +msgid "Display context menu." +msgstr "Näytä kontekstivalikko." + +msgid "Maximize the frame horizontally/vertically." +msgstr "Maksimoi kehys vaaka/pystysuunnassa." + +msgid "Begin move/resize mode." +msgstr "Aloita siirto ja koonmuutostila." + +msgid "Switch the frame to display the object indicated by the tab." +msgstr "Vaihda kehys näyttämään välilehden ilmoittama olio." + +msgid "Resize the frame." +msgstr "Muuta kehyksen kokoa." + +msgid "Move the frame." +msgstr "Siirrä kehystä." + +msgid "Move objects between frames by dragging and dropping the tab." +msgstr "Siirrä olioita kehysten välillä raahaamalla ja pudottamalla välilehti." + +msgid "Tag current object within the frame." +msgstr "Merkitse kehyksen tällä hetkellä näyttämä olio." + +msgid "Switch to n:th object within the frame." +msgstr "Siirry n:teen kehyksen jakavaan olioon." + +msgid "Switch to next/previous object within the frame." +msgstr "Siirry seuraavaan/edelliseen kehyksen jakavaan olioon." + +msgid "Move current object within the frame left/right." +msgstr "" +"Siirrä kehyksessä tällä hetkellä näytettävää oliota vasemmalle/oikealle." + +msgid "Attach tagged objects to this frame." +msgstr "Liitä merkityt oliot tähän kehykseen." + +msgid "Toggle shade mode" +msgstr "Kytke litistys päälle/pois." + +# +msgid "Raise the frame." +msgstr "Nosta kehys." + +msgid "Lower the frame." +msgstr "Alenna kehystä." + +msgid "Cancel the resize mode." +msgstr "Peruuta tilasta." + +msgid "End the resize mode." +msgstr "Siirry tilasta pois." + +# +msgid "Grow in specified direction." +msgstr "Kasvata vastaavaan suuntaan." + +msgid "Shrink in specified direction." +msgstr "Kutista vastaavaan suuntaan." + +# +msgid "Move in specified direction." +msgstr "Liiku vastaavaan suuntaan." + +msgid "About Ion" +msgstr "Tietoja Ionista" + +msgid "XTerm" +msgstr "" + +msgid "W3M" +msgstr "" + +msgid "Rxvt" +msgstr "" + +msgid "Opera" +msgstr "" + +msgid "Links" +msgstr "" + +msgid "Konqueror" +msgstr "" + +msgid "Dillo" +msgstr "" + +# +msgid "Run..." +msgstr "Suorita..." + +msgid "Save" +msgstr "Talleta" + +msgid "Restart" +msgstr "Uudelleenkäynnistä" + +msgid "Restart TWM" +msgstr "Käynnistä TWM" + +msgid "Exit" +msgstr "Poistu" + +msgid "Kill" +msgstr "Tapa" + +msgid "Toggle tag" +msgstr "Muuta lippua" + +msgid "Attach tagged" +msgstr "Liitä merkityt" + +msgid "Clear tags" +msgstr "Poista merkinnät" + +msgid "Window info" +msgstr "Ikkunan tiedot" + +# +msgid "New workspace" +msgstr "Uusi työpöytä" + +msgid "New empty workspace" +msgstr "Uusi tyhjä työpöytä" + +# +msgid "Close workspace" +msgstr "Sulje työpöytä" + +msgid "Move one character forward/backward." +msgstr "Siirry yksi merkki eteen/taakse." + +msgid "Go to end/beginning." +msgstr "Mene rivin alkuun/loppuun." + +msgid "Skip one word forward/backward." +msgstr "Ohita yksi sana eteen/taaksepäin" + +msgid "Delete next character." +msgstr "Poista seuraava merkki." + +msgid "Delete previous character." +msgstr "Tuhoa edellinen merkki." + +msgid "Delete one word forward/backward." +msgstr "Tuhoa yksi sana eteen/taakse." + +msgid "Delete to end of line." +msgstr "Tuhoa merkit rivin loppuun saakka." + +msgid "Delete the whole line." +msgstr "Tuhoa koko rivi." + +msgid "Transpose characters." +msgstr "Transponoi merkit." + +# +msgid "Select next/previous (matching) history entry." +msgstr "Näytä seuraava/edellinen vastaus muistista." + +msgid "Paste from the clipboard." +msgstr "Liimaa teksti leikelaudalta." + +# +msgid "Set mark/begin selection." +msgstr "Aloita tekstin valinta." + +# +msgid "Cut selection." +msgstr "Leikkaa valittu teksti." + +# +msgid "Copy selection." +msgstr "Kopioi valittu teksti." + +# +msgid "Clear mark/cancel selection." +msgstr "Lopeta tekstin valinta." + +msgid "Try to complete the entered text or cycle through completions." +msgstr "Yritä täydentää syötetty teksti tai selaa täydennyksiä." + +msgid "Complete from history" +msgstr "Täydennä historiasta" + +msgid "Close the query and execute bound action." +msgstr "Sulje kysely ja suorita sidottu toiminta." + +msgid "Close the query/message box, not executing bound actions." +msgstr "Sulje kysely/viesti suorittamatta sidottuja toimintoja." + +msgid "Scroll the message or completions up/down." +msgstr "Vieritä viestiä tai täydennyksiä ylös/alas." + +msgid "press" +msgstr "painallus" + +msgid "click" +msgstr "napsautus" + +msgid "drag" +msgstr "raahaus" + +msgid "double click" +msgstr "kaksoisnapsautus" + +msgid "%s %s" +msgstr "" + +msgid "%s %s at %s" +msgstr "%s %s osassa %s" + +#~ msgid "Could not find a complete workspace class. Please load some modules." +#~ msgstr "" +#~ "Yhtäkään täydellistä työpöytäluokkaa ei löydy. Ole hyvä ja lataa joitakin " +#~ "moduuleita." + +# +# +#~ msgid "Failed to rescue some client windows." +#~ msgstr "Joidenkin asiakasikkunoiden pelastaminen epäonnistui." + +#~ msgid "Same manager." +#~ msgstr "Sama manageri." + +# +# +#~ msgid "Invalid split type parameter." +#~ msgstr "Tuntematon jaon tapa." + +# +#~ msgid "Failure to create a new frame." +#~ msgstr "Uuden kehyksen luonti epäonnistui." + +# +#~ msgid "Region not managed by the workspace." +#~ msgstr "Alue ei ole työpöydän hallinnassa." + +#~ msgid "No geometry specified." +#~ msgstr "Geometria on asettamatta." + +#~ msgid "Unable to re-initialise workspace. Destroying." +#~ msgstr "Työpöydän uudelleenalustus epäonnistui, joten se tuhotaan." + +#~ msgid "Refusing to close non-empty workspace." +#~ msgstr "Työpöydän tuhoamisesta kieltäydytään, koska se ei ole tyhjä." + +#~ msgid "Malfunctioning placement hook; condition #%d." +#~ msgstr "Rikkinäinen koukku; tapaus #%d." + +#~ msgid "none" +#~ msgstr "ei mikään" + +# +#~ msgid "mail" +#~ msgstr "posti" + +#~ msgid "" +#~ "\n" +#~ "Transients:\n" +#~ msgstr "" +#~ "\n" +#~ "Väliaikaisikkunat:\n" + +#~ msgid "Workspace type (%s):" +#~ msgstr "Työpöydän tyyppi (%s):" + +#~ msgid "Go to previous active object." +#~ msgstr "Mene edelliseen aktiiviseen olioon." + +# +# +#~ msgid "Toggle fullscreen mode of current client window." +#~ msgstr "Kytke aktiivisen asiakasikkunan kokoruudun tila päälle/pois." + +#~ msgid "WStatusBar expected." +#~ msgstr "Odotettiin WStatusBaria." + +#~ msgid "Backwards-circulate focus and raise the newly focused frame." +#~ msgstr "Kierrätä fokusta taaksepäin ja nosta fokusoitu kehys." + +#~ msgid "(Un)stick" +#~ msgstr "Aseta tahmaus" + +# +#~ msgid "Query for a client window to attach to active frame." +#~ msgstr "Kysy asiakasikkunaa liitettäväksi kehykseen." + +# +#~ msgid "Resize the area." +#~ msgstr "Muuta kehyksen kokoa." + +#~ msgid "Raise/lower active frame." +#~ msgstr "Nosta/alenna aktiivinen kehys." + +#~ msgid "Restart PWM" +#~ msgstr "Käynnistä PWM" + +#~ msgid "Refresh list" +#~ msgstr "Virkistä lista" + +#~ msgid "Unable to create workspace: no screen." +#~ msgstr "Ei voida luoda työpöytää: ei ruutua." + +#~ msgid "load" +#~ msgstr "kuorma" + +#~ msgid "(Un)tag" +#~ msgstr "Vaihda merkintä" + +#~ msgid "Circulate focus and raise the newly focused frame." +#~ msgstr "Kierrätä fokusta ja nosta uudelleen fokusoitu kehys." + +# +#~ msgid "No function given." +#~ msgstr "Funktiota ei ole annettu." + +#~ msgid "Failed to create a timer for statusbar." +#~ msgstr "Epäonnistuttiin ajastimen luonnissa tilariville." + +#~ msgid "Vertically at root" +#~ msgstr "Pystysuunnassa juuressa" + +#~ msgid "Split" +#~ msgstr "Halkaise" + +# +#~ msgid "Transpose at root" +#~ msgstr "Käännä juuri" + +#~ msgid "Flip&transpose" +#~ msgstr "Peilaa&käännä" + +#~ msgid "Horizontally at root" +#~ msgstr "Vaakasuunnassa juuressa" + +#~ msgid "Frame may not be destroyed." +#~ msgstr "Kehystä ei saa tuhota." + +#~ msgid "Failed to rescue managed objects." +#~ msgstr "Hallittujen olioiden pelastaminen epäonnistui." + +# +#~ msgid "Could not find a root window." +#~ msgstr "Juuri-ikkunaa ei löytynyt." + +#~ msgid "Caught fatal signal %d. Dying without deinit." +#~ msgstr "" +#~ "Ohjelma lopettaa ilman jälkien putsausta tuhoisan signaalin %d " +#~ "vastaanottoon." + +#~ msgid "Caught signal %d. Dying." +#~ msgstr "Ohjelma lopettaa signaalin %d vastaanottoon." + +# +#~ msgid "Object destroyed while deferred actions are still pending." +#~ msgstr "Viivästettyä toimintoja jonossa vielä oliota tuhottaessa." + +#~ msgid "Unable to rescue \"%s\"." +#~ msgstr "Alueen \"%s\" pelastaminen epäonnistui." + +#~ msgid "Frame is not empty." +#~ msgstr "Kehys ei ole tyhjä." + +# +#~ msgid "Frame not managed by the workspace." +#~ msgstr "Kehys ei ole tämän työpöydän hallitsema." + +#~ msgid "Vertically/root" +#~ msgstr "Pystysuunnassa juuressa" + +# +# +#~ msgid "Horizontally/root" +#~ msgstr "Vaakasuunnassa juuressa" + +#~ msgid "Flip parent" +#~ msgstr "Peilaa vanhempi" diff --git a/po/ru.po b/po/ru.po new file mode 100644 index 0000000..01d0a08 --- /dev/null +++ b/po/ru.po @@ -0,0 +1,1637 @@ +# translation of ru.po to RUSSIAN +# # +# This file is distributed under the same license as the Ion3 package. +# Vassily Leushin , 2006. +# +msgid "" +msgstr "" +"Project-Id-Version: ru\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2004-10-11 18:33+0300\n" +"PO-Revision-Date: 2006-02-01 19:55+0300\n" +"Last-Translator: Vassily Leushin \n" +"Language-Team: RUSSIAN \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=KOI8-R\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Poedit-Language: Russian\n" +"X-Poedit-Country: RUSSIAN FEDERATION\n" +"X-Generator: KBabel 1.10.1\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" + +#: ../ioncore/conf-bindings.c:96 +msgid "Insane key combination." +msgstr "îÅÐÒÁ×ÉÌØÎÁÑ ËÏÍÂÉÎÁÃÉÑ ËÌÁ×ÉÛ." + +#: ../ioncore/conf-bindings.c:100 +msgid "Could not convert keysym to keycode." +msgstr "îÅ ÍÏÇÕ ÐÅÒÅ×ÅÓÔÉ keysym × keycode" + +#: ../ioncore/conf-bindings.c:111 +#, c-format +msgid "Unknown button \"%s\"." +msgstr "îÅÉÚ×ÅÓÔÎÁÑ ËÎÏÐËÁ \"%s\" " + +#: ../ioncore/conf-bindings.c:116 +msgid "Insane button combination." +msgstr "îÅÐÒÁ×ÉÌØÎÁÑ ËÏÍÂÉÎÁÃÉÑ ËÎÏÐÏË" + +#: ../ioncore/conf-bindings.c:123 +#: ../ioncore/conf-bindings.c:130 +msgid "Insane modifier combination." +msgstr "îÅÐÒÁ×ÉÌØÎÁÑ ËÏÍÂÉÎÁÃÉÑ ÍÏÄÉÆÉËÁÔÏÒÏ×" + +#: ../ioncore/conf-bindings.c:168 +#, c-format +msgid "Can not wait on modifiers when no modifiers set in \"%s\"." +msgstr "ïÖÉÄÁÎÉÅ ÍÏÄÉÆÉËÁÔÏÒÏ× ÎÅ×ÏÚÍÏÖÎÏ, ÅÓÌÉ ÏÎÉ ÎÅ ÕÓÔÁÎÏ×ÌÅÎÙ × \"%s\" " + +#: ../ioncore/conf-bindings.c:186 +#, c-format +msgid "Unable to add binding %s." +msgstr "îÅ×ÏÚÍÏÖÎÏ ÄÏÂÁ×ÉÔØ ÐÒÉ×ÑÚËÕ %s." + +#: ../ioncore/conf-bindings.c:191 +#, c-format +msgid "Unable to remove binding %s." +msgstr "îÅ×ÏÚÍÏÖÎÏ ÕÄÁÌÉÔØ ÐÒÉ×ÑÚËÕ %s." + +#: ../ioncore/conf-bindings.c:230 +#, c-format +msgid "Unable to add submap for binding %s." +msgstr "" + +# +#: ../ioncore/conf-bindings.c:260 +msgid "Binding type not set." +msgstr "ôÉÐ ÐÒÉ×ÑÚËÉ ÎÅ ÕÓÔÁÎÏ×ÌÅÎ" + +#: ../ioncore/conf-bindings.c:270 +#, c-format +msgid "Unknown binding type \"%s\"." +msgstr "îÅÉÚ×ÅÓÔÎÙÊ ÔÉÐ ÐÒÉ×ÑÚËÉ \"%s\"." + +#: ../ioncore/conf-bindings.c:291 +#, c-format +msgid "Unknown area \"%s\" for binding %s." +msgstr "îÅÉÚ×ÅÓÔÎÁÑ ÏÂÌÁÓÔØ \"%s\" ÄÌÑ ÐÒÉ×ÑÚËÉ %s." + +#: ../ioncore/conf-bindings.c:332 +#, c-format +msgid "Unable to get bindmap entry %d." +msgstr "" + +#: ../ioncore/conf-bindings.c:374 +msgid "Unable to convert keysym to string." +msgstr "îÅ ÍÏÇÕ ÐÅÒÅ×ÅÓÔÉ keysym × ÓÔÒÏËÕ." + +#: ../ioncore/conf-bindings.c:388 +msgid "Unable to convert button to string." +msgstr "îÅ ÍÏÇÕ ÐÅÒÅ×ÅÓÔÉ ËÎÏÐËÕ × ÓÔÒÏËÕ." + +# +#: ../ioncore/event.c:78 +msgid "Time request from X server failed." +msgstr "îÅÕÄÁÞÎÙÊ ÚÁÐÒÏÓ ×ÒÅÍÅÎÉ ÏÔ X ÓÅÒ×ÅÒÁ" + +# +#: ../ioncore/exec.c:101 +msgid "Could not find a root window." +msgstr "îÅ ÍÏÇÕ ÎÁÊÔÉ ËÏÒÎÅ×ÏÅ ÏËÎÏ." + +#: ../ioncore/exec.c:134 +msgid "reading a pipe" +msgstr "ÞÔÅÎÉÅ ÉÚ ËÁÎÁÌÁ" + +# +#: ../ioncore/exec.c:296 +msgid "Not saving state: running under session manager." +msgstr "óÏÓÔÏÑÎÉÅ ÎÅ ÓÏÈÒÁÎÅÎÏ: ÒÁÂÏÔÁ ÐÏÄ ÍÅÎÅÄÖÅÒÏÍ ÓÅÓÓÉÊ." + +#: ../ioncore/strings.c:107 +#: ../ioncore/strings.c:143 +msgid "Invalid multibyte string." +msgstr "îÅÐÒÁ×ÉÌØÎÁÑ ÍÎÏÇÏÂÁÊÔÎÁÑ ÓÔÒÏËÁ" + +#: ../ioncore/strings.c:234 +#, c-format +msgid "Error compiling regular expression: %s" +msgstr "ïÛÉÂËÁ ËÏÍÐÉÌÑÃÉÉ ÒÅÇ. ×ÙÒÁÖÅÎÉÑ: %s" + +#: ../ioncore/modules.c:158 +msgid "Invalid module name." +msgstr "îÅÐÒÁ×ÉÌØÎÏÅ ÉÍÑ ÍÏÄÕÌÑ." + +# +#: ../ioncore/modules.c:170 +msgid "The module is already loaded." +msgstr "äÁÎÎÙÊ ÍÏÄÕÌØ ÕÖÅ ÚÁÇÒÕÖÅÎ." + +#: ../ioncore/modules.c:185 +msgid "Module version information not found or version mismatch. Refusing to use." +msgstr "" +"éÎÆÏÒÍÁÃÉÑ Ï ×ÅÒÓÉÉ ÍÏÄÕÌÑ ÎÅ ÎÁÊÄÅÎÁ, ÉÌÉ ÎÅÓÏÏÔ×ÅÔÓÔ×ÉÅ ×ÅÒÓÉÊ " +"ïÔËÁÚ × ÉÓÐÏÌØÚÏ×ÁÎÉÉ." + +#: ../ioncore/modules.c:196 +#, c-format +msgid "Unable to initialise module %s." +msgstr "îÅ ÍÏÇÕ ÉÎÉÃÉÁÌÉÚÉÒÏ×ÁÔØ ÍÏÄÕÌØ %s ." + +#: ../ioncore/modules.c:220 +#: ../../libextl/readconfig.c:368 +#, c-format +msgid "Unable to find '%s' on search path." +msgstr "îÅ ÍÏÇÕ ÎÁÊÔÉ '%s' × ÐÕÔÉ ÐÏÉÓËÁ." + +#: ../ioncore/modules.c:291 +msgid "Unknown module." +msgstr "îÅÉÚ×ÅÓÔÎÙÊ ÍÏÄÕÌØ." + +#: ../ioncore/modules.c:299 +msgid "Unable to initialise module." +msgstr "îÅ ÍÏÇÕ ÉÎÉÃÉÁÌÉÚÉÒÏ×ÁÔØ ÍÏÄÕÌØ." + +#: ../ioncore/modules.c:344 +msgid "No module to load given." +msgstr "îÅ ÕËÁÚÁÎ ÍÏÄÕÌØ ÄÌÑ ÚÁÇÒÕÚËÉ." + +#: ../ioncore/screen.c:301 +msgid "act: " +msgstr "act: " + +#: ../ioncore/screen.c:490 +msgid "Invalid offset." +msgstr "îÅÐÒÁ×ÉÌØÎÙÊ ÏÔÓÔÕÐ." + +#: ../ioncore/screen.c:535 +msgid "Could not find a complete workspace class. Please load some modules." +msgstr "" +"îÅ ÍÏÇÕ ÎÁÊÔÉ ËÌÁÓÓ ÐÏÌÎÏÇÏ ÒÁÂÏÞÅÇÏ ÓÔÏÌÁ. ðÏÖÁÌÕÊÓÔÁ " +"ÚÁÇÒÕÚÉÔÅ ËÁËÉÅ-ÎÉÂÕÄØ ÍÏÄÕÌÉ." + +#: ../ioncore/screen.c:545 +#, c-format +msgid "Unable to create a workspace on screen %d." +msgstr "îÅ ÍÏÇÕ ÓÏÚÄÁÔØ ÒÁÂ. ÐÒÏÓÔÒÁÎÓÔ×Ï ÎÁ ÜËÒÁÎÅ %d ." + +#: ../ioncore/signal.c:291 +#, c-format +msgid "Caught fatal signal %d. Dying without deinit." +msgstr "ðÒÉÎÑÔ ÆÁÔÁÌØÎÙÊ ÓÉÇÎÁÌ %d. õÍÉÒÁÀ ÂÅÚ deinit'Á." + +#: ../ioncore/signal.c:300 +#, c-format +msgid "Caught signal %d. Dying." +msgstr "ðÏÌÕÞÅÎ ÓÉÇÎÁÌ %d. õÍÉÒÁÀ." + +#: ../ioncore/sizehint.c:150 +msgid "Invalid client-supplied width/height increment." +msgstr "ðÏÌÕÞÅÎ ÎÅÐÒÁ×ÉÌØÎÙÊ ÉÎËÒÅÍÅÎÔ ÛÉÒÉÎÙ/×ÙÓÏÔÙ ÏÔ ËÌÉÅÎÔÁ." + +#: ../ioncore/sizehint.c:158 +msgid "Invalid client-supplied aspect-ratio." +msgstr "ðÏÌÕÞÅÎÙ ÎÅÐÒÁ×ÉÌØÎÙÅ ÐÒÏÐÏÒÃÉÉ ÏÔ ËÌÉÅÎÔÁ." + +#: ../ioncore/ioncore.c:66 +msgid "" +"This program is free software; you can redistribute it and/or\n" +"modify it under the terms of the GNU Lesser General Public\n" +"License as published by the Free Software Foundation; either\n" +"version 2.1 of the License, or (at your option) any later version.\n" +"\n" +"This program is distributed in the hope that it will be useful,\n" +"but WITHOUT ANY WARRANTY; without even the implied warranty of\n" +"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n" +"Lesser General Public License for more details.\n" +msgstr "" + +#: ../ioncore/ioncore.c:385 +#, c-format +msgid "Could not connect to X display '%s'" +msgstr "îÅ ÍÏÇÕ ÓÏÅÄÉÎÉÔØÓÑ Ó X ÄÉÓÐÌÅÅÍ '%s'" + +#: ../ioncore/ioncore.c:438 +msgid "Could not find a screen to manage." +msgstr "îÅ ÍÏÇÕ ÎÁÊÔÉ ÜËÒÁÎ ÄÌÑ ÕÐÒÁ×ÌÅÎÉÑ." + +#: ../ioncore/xic.c:43 +msgid "Failed to open input method." +msgstr "íÅÔÏÄ ××ÏÄÁ ÎÅ ÏÔËÒÙÔ." + +#: ../ioncore/xic.c:48 +msgid "Input method doesn't support any style." +msgstr "íÅÔÏÄ ××ÏÄÁ ÎÅ ÐÏÄÄÅÒÖÉ×ÁÅÔ ÓÔÉÌÉ" + +#: ../ioncore/xic.c:63 +msgid "input method doesn't support my preedit type." +msgstr "íÅÔÏÄ ××ÏÄÁ ÎÅ ÐÏÄÄÅÒÖÉ×ÁÅÔ ÍÏÊ ÔÉÐ preedit'Á. " + +#: ../ioncore/xic.c:91 +msgid "Failed to create input context." +msgstr "ëÏÎÔÅËÓÔ ××ÏÄÁ ÎÅ ÓÏÚÄÁÎ." + +#: ../ioncore/clientwin.c:376 +#, c-format +msgid "The transient_for hint for \"%s\" points to itself." +msgstr "èÉÎÔ transient_for ÄÌÑ \"%s\" ÕËÁÚÙ×ÁÅÔ ÎÁ ÓÁÍÏÇÏ ÓÅÂÑ." + +#: ../ioncore/clientwin.c:380 +#, c-format +msgid "Client window \"%s\" has broken transient_for hint. (\"Extended WM hints\" multi-parent brain damage?)" +msgstr "" + +#: ../ioncore/clientwin.c:385 +#, c-format +msgid "The transient_for window for \"%s\" is not on the same screen." +msgstr "ïËÎÏ transient_for ÄÌÑ \"%s\" ÎÅ ÎÁ ÔÏÍ ÖÅ ÜËÒÁÎÅ." + +#: ../ioncore/clientwin.c:405 +#: ../ioncore/clientwin.c:492 +#: ../ioncore/clientwin.c:1554 +#, c-format +msgid "Window %#x disappeared." +msgstr "ïËÎÏ %#x ÉÓÞÅÚÌÏ." + +#: ../ioncore/clientwin.c:512 +msgid "Unable to find a matching root window!" +msgstr "îÅ ÍÏÇÕ ÎÁÊÔÉ ÓÏÏÔ×ÅÔÓÔ×ÕÀÝÅÅ ËÏÒÎÅ×ÏÅ ÏËÎÏ!" + +#: ../ioncore/clientwin.c:547 +#, c-format +msgid "Unable to manage client window %#x." +msgstr "îÅ ÍÏÇÕ ÕÐÒÁ×ÌÑÔØ ËÌÉÅÎÔÓËÉÍ ÏËÎÏÍ %#x." + +#: ../ioncore/clientwin.c:597 +msgid "Changes is WM_TRANSIENT_FOR property are unsupported." +msgstr "" + +# +#: ../ioncore/clientwin.c:874 +msgid "Client does not support the WM_DELETE protocol." +msgstr "ëÌÉÅÎÔ ÎÅ ÐÏÄÄÅÒÖÉ×ÁÅÔ ÐÒÏÔÏËÏÌ WM_DELETE. " + +#: ../ioncore/clientwin.c:1103 +#: ../mod_ionws/ionws.c:76 +#: ../mod_floatws/floatws.c:103 +#, c-format +msgid "Error reparenting %s." +msgstr "ïÛÉÂËÁ ÐÅÒÅÎÁÚÎÁÞÅÎÉÑ ÒÏÄÉÔÅÌÑ %s." + +#: ../ioncore/clientwin.c:1560 +msgid "Saved client window does not want to be managed." +msgstr "óÏÈÒÁÎÅÎÎÏÅ ÏËÎÏ ËÌÉÅÎÔÁ ÎÅ ÈÏÞÅÔ ÂÙÔØ ÐÏÄ ÕÐÒÁ×ÌÅÎÉÅÍ." + +# +#: ../ioncore/colormap.c:96 +msgid "Unable to store colourmap watch info." +msgstr "" + +#: ../ioncore/region.c:36 +msgid "Creating region with negative width or height!" +msgstr "óÏÚÄÁÎÉÅ ÒÅÇÉÏÎÁ Ó ÏÔÒÉÃÁÔÅÌØÎÏÊ ×ÙÓÏÔÏÊ ÉÌÉ ÛÉÒÉÎÏÊ!" + +#: ../ioncore/region.c:84 +#, c-format +msgid "Destroying object \"%s\" with client windows as children." +msgstr "õÄÁÌÅÎÉÅ ÏÂßÅËÔÁ \"%s\" Ó ÏËÎÁÍÉ ËÌÉÅÎÔÁ ËÁË ÐÏÔÏÍËÁÍÉ." + +#: ../ioncore/attach.c:80 +msgid "Unable to reparent." +msgstr "îÅ×ÏÚÍÏÖÎÏ ÐÅÒÅÎÁÚÎÁÞÉÔØ ÒÏÄÉÔÅÌÑ." + +#: ../ioncore/attach.c:112 +#: ../ioncore/frame-pointer.c:295 +#, c-format +msgid "Attempt to make region %s manage its ancestor %s." +msgstr "ðÏÐÙÔËÁ ÕÐÒÁ×ÌÅÎÉÑ ÒÅÇÉÏÎÏÍ %s ÅÇÏ ÐÒÅÄËÏÍ %s." + +# +#: ../ioncore/defer.c:93 +msgid "Object destroyed while deferred actions are still pending." +msgstr "ïÂßÅËÔ ÕÄÁÌÅÎ ÐÒÉ ÏÔÌÏÖÅÎÎÏÍ ÄÅÊÓÔ×ÉÉ." + +#: ../ioncore/manage.c:90 +msgid "Unable to find a screen for a new client window." +msgstr "îÅ ÍÏÇÕ ÎÁÊÔÉ ÜËÒÁÎ ÄÌÑ ÎÏ×ÏÇÏ ËÌÉÅÎÔÓËÏÇÏ ÏËÎÁ." + +#: ../ioncore/manage.c:187 +#, c-format +msgid "Unable to rescue \"%s\"." +msgstr "îÅ ÍÏÇÕ ÓÐÁÓÔÉ \"%s\"." + +#: ../ioncore/rootwin.c:222 +#, c-format +msgid "Unable to redirect root window events for screen %d." +msgstr "îÅ ÍÏÇÕ ÐÅÒÅÎÁÐÒÁ×ÉÔØ ÓÏÂÙÔÉÑ ËÏÒÎÅ×ÏÇÏ ÏËÎÁ ÄÌÑ ÜËÒÁÎÁ %d." + +#: ../ioncore/rootwin.c:316 +msgid "Xinerama sanity check failed; overlapping screens detected." +msgstr "ðÒÏ×ÅÒËÁ Xinerama ÎÅ ÕÄÁÌÁÓØ; ÏÂÎÁÒÕÖÅÎÏ ÎÁÌÏÖÅÎÉÅ ÜËÒÁÎÏ×." + +#: ../ioncore/rootwin.c:342 +msgid "Don't know how to get Xinerama information for multiple X root windows." +msgstr "îÅ ÚÎÁÀ ËÁË ÐÏÌÕÞÉÔØ ÉÎÆÏÒÍÁÃÉÀ ÐÏ ÍÎÏÖÅÓÔ×ÅÎÎÙÍ ËÏÒÎÅ×ÙÍ ÏËÎÁÍ × Xinerama." + +#: ../ioncore/rootwin.c:378 +#, c-format +msgid "Unable to setup Xinerama screen %d." +msgstr "îÅ ÍÏÇÕ ÎÁÓÔÒÏÉÔØ ÜËÒÁÎ Xinerama %d." + +#: ../ioncore/rootwin.c:389 +#, c-format +msgid "Unable to setup X screen %d." +msgstr "îÅ ÍÏÇÕ ÎÁÓÔÒÏÉÔØ ÜËÒÁÎ X %d." + +#: ../ioncore/names.c:90 +#, c-format +msgid "Corrupt instance number %s." +msgstr "îÅ×ÅÒÎÙÊ ÎÏÍÅÒ ÜËÚÅÍÐÌÑÒÁ %s." + +#: ../ioncore/saveload.c:55 +#, c-format +msgid "Unknown class \"%s\", cannot create region." +msgstr "îÅÉÚ×ÅÓÔÎÙÊ ËÌÁÓÓ \"%s\", ÎÅ ÍÏÇÕ ÓÏÚÄÁÔØ ÒÅÇÉÏÎ." + +#: ../ioncore/saveload.c:124 +#, c-format +msgid "" +"There were errors loading layout. Backing up current layout savefile as\n" +"%s.\n" +"If you are _not_ running under a session manager and wish to restore your\n" +"old layout, copy this backup file over the layout savefile found in the\n" +"same directory while Ion is not running and after having fixed your other\n" +"configuration files that are causing this problem. (Maybe a missing\n" +"module?)\n" +msgstr "" + +#: ../ioncore/saveload.c:167 +msgid "Unable to get file for layout backup." +msgstr "îÅ ÍÏÇÕ ÐÏÌÕÞÉÔØ ÒÅÚÅÒ×ÎÙÊ ÆÁÊÌ ÒÁÓÐÏÌÏÖÅÎÉÑ." + +#: ../ioncore/saveload.c:171 +#, c-format +msgid "Backup file %s already exists." +msgstr "ÒÅÚÅÒ×ÎÙÊ ÆÁÊÌ %s ÕÖÅ ÓÕÝÅÓÔ×ÕÅÔ." + +#: ../ioncore/saveload.c:177 +msgid "Failed backup." +msgstr "òÅÚÅÒ×ÎÁÑ ËÏÐÉÑ ÎÅ ÓÏÚÄÁÎÁ." + +# +#: ../ioncore/saveload.c:182 +msgid "Unable to initialise layout on any screen." +msgstr "îÅ ÍÏÇÕ ÉÎÉÃÉÁÌÉÚÉÒÏ×ÁÔØ ÒÁÓÐÏÌÏÖÅÎÉÅ ÎÉ ÎÁ ÏÄÎÏÍ ÜËÒÁÎÅ." + +#: ../ioncore/saveload.c:202 +#, c-format +msgid "Unable to get configuration for screen %d." +msgstr "îÅ ÍÏÇÕ ÐÏÌÕÞÉÔØ ËÏÎÆÉÇÕÒÁÃÉÀ ÜËÒÁÎÁ %d." + +#: ../ioncore/saveload.c:215 +msgid "Unable to save layout." +msgstr "îÅ ÍÏÇÕ ÓÏÈÒÁÎÉÔØ ÒÁÓÐÏÌÏÖÅÎÉÅ." + +#: ../ioncore/frame.c:122 +msgid "Frame is not empty." +msgstr "æÒÅÊÍ ÎÅ ÐÕÓÔ." + +#: ../ioncore/conf.c:134 +msgid "User directory can not be set." +msgstr "äÉÒÅËÔÏÒÉÑ ÐÏÌØÚÏ×ÁÔÅÌÑ ÎÅ ÕÓÔÁÎÏ×ÌÅÎÁ." + +#: ../ioncore/conf.c:197 +msgid "Some bindmaps were empty, loading ioncore-efbb." +msgstr "" + +#: ../ioncore/fullscreen.c:100 +msgid "Failed to enter full screen mode." +msgstr "ïÛÉÂËÁ ÐÏÌÎÏÜËÒÁÎÎÏÇÏ ÒÅÖÉÍÁ." + +#: ../ioncore/fullscreen.c:158 +msgid "WClientWin failed to return from full screen mode; remaining manager or parent from previous location refused to manage us." +msgstr "" + +# +#: ../ioncore/mplex.c:1247 +msgid "Invalid position setting." +msgstr "îÅÐÒÁ×ÉÌØÎÙÅ ÕÓÔÁÎÏ×ËÉ ÍÅÓÔÏÐÏÌÏÖÅÎÉÑ" + +# +#: ../ioncore/mplex.c:1280 +msgid "Invalid action setting." +msgstr "îÅÐÒÁ×ÉÌØÎÙÅ ÕÓÔÁÎÏ×ËÉ ÄÅÊÓÔ×ÉÑ." + +#: ../ioncore/gr.c:119 +#, c-format +msgid "Drawing engine %s is not registered!" +msgstr "ä×ÉÖÏË ÏÔÒÉÓÏ×ËÉ %s ÎÅ ÚÁÒÅÇÉÓÔÒÉÒÏ×ÁÎ." + +#: ../ioncore/gr.c:138 +#, c-format +msgid "Unable to find brush for style '%s'." +msgstr "îÅ ÍÏÇÕ ÎÁÊÔÉ ËÉÓÔØ ÄÌÑ ÓÔÉÌÑ '%s'." + +#: ../ioncore/gr.c:409 +msgid "No drawing engines loaded, trying \"de\"." +msgstr "îÉ ÏÄÎÏ Ä×ÉÖËÁ ÏÔÒÉÓÏ×ËÉ ÎÅ ÚÁÇÒÕÖÅÎÏ, ÐÒÏÂÕÀ \"ru\"." + +# +#: ../ioncore/hooks.c:219 +msgid "No function given." +msgstr "îÅÔ ÆÕÎËÃÉÉ." + +#: ../mod_ionws/ionws.c:311 +msgid "Unable to create a node for status display." +msgstr "îÅ ÍÏÇÕ ÓÏÚÄÁÔØ ÕÚÅÌ ÄÌÑ ÐÏËÁÚÁ ÓÔÁÔÕÓÁ." + +#: ../mod_ionws/ionws.c:323 +msgid "Unable to create new split for status display." +msgstr "îÅ ÍÏÇÕ ÓÏÚÄÁÔØ ÎÏ×ÏÅ ÒÁÚÄÅÌÅÎÉÅ ÄÌÑ ÐÏËÁÚÁ ÓÔÁÔÕÓÁ." + +# +#: ../mod_ionws/ionws.c:742 +msgid "Frame not managed by the workspace." +msgstr "æÒÅÊÍ ÎÅ ÕÐÒÁ×ÌÑÅÔÓÑ ÒÁÂÏÞÉÍ ÓÔÏÌÏÍ." + +# +#: ../mod_ionws/ionws.c:747 +msgid "Invalid direction parameter." +msgstr "îÅ×ÅÒÎÙÊ ÐÁÒÁÍÅÔÒ ÎÁÐÒÁ×ÌÅÎÉÑ." + +# +#: ../mod_ionws/ionws.c:765 +msgid "Unable to split." +msgstr "îÅ ÍÏÇÕ ÒÁÚÄÅÌÉÔØ." + +#: ../mod_ionws/ionws.c:803 +msgid "Nil frame." +msgstr "îÕÌÅ×ÏÊ ÆÒÅÊÍ" + +#: ../mod_ionws/ionws.c:807 +msgid "The frame is not managed by the workspace." +msgstr "äÁÎÎÙÊ ÆÒÅÊÍ ÎÅ ÕÐÒÁ×ÌÑÅÔÓÑ ÒÁÂÏÞÉÍ ÓÔÏÌÏÍ." + +#: ../mod_ionws/ionws.c:812 +msgid "Frame may not be destroyed." +msgstr "æÒÅÊÍ ÎÅ ÕÄÁÌÑÅÔÓÑ" + +#: ../mod_ionws/ionws.c:817 +msgid "Failed to rescue managed objects." +msgstr "" + +#: ../mod_ionws/ionws.c:1028 +msgid "Nil parameter." +msgstr "îÕÌÅ×ÏÊ ÐÁÒÁÍÅÔÒ." + +#: ../mod_ionws/ionws.c:1033 +msgid "Manager doesn't match." +msgstr "íÅÎÅÄÖÅÒ ÎÅ ÓÏÏÔ×ÅÔÓÔ×ÕÅÔ." + +# +#: ../mod_ionws/ionws.c:1055 +msgid "Could not get split tree." +msgstr "îÅ ÍÏÇÕ ÐÏÌÕÞÉÔØ ÄÅÒÅ×Ï ÒÁÚÄÅÌÅÎÉÊ." + +#: ../mod_ionws/ionws.c:1076 +msgid "Workspace already has a status display node." +msgstr "òÁÂÏÞÉÊ ÓÔÏÌ ÕÖÅ ÉÍÅÅÔ ÕÚÅÌ ÐÏËÁÚÁ ÓÔÁÔÕÓÁ." + +# +#: ../mod_ionws/ionws.c:1126 +msgid "Missing region parameters." +msgstr "ïÔÓÕÔÓÔ×ÕÀÔ ÐÁÒÁÍÅÔÒÙ ÒÅÇÉÏÎÁ." + +#: ../mod_ionws/ionws.c:1162 +#: ../mod_ionws/splitfloat.c:783 +msgid "Invalid direction." +msgstr "îÅÐÒÁ×ÉÌØÎÏÅ ÎÁÐÒÁ×ÌÅÎÉÅ" + +# +#: ../mod_ionws/ionws.c:1236 +msgid "No split type given." +msgstr "îÅÔ ÕËÁÚÁÎ ÔÉÐ ÒÁÚÄÅÌÅÎÉÑ" + +#: ../mod_ionws/ionws.c:1249 +msgid "Unknown split type." +msgstr "îÅÉÚ×ÅÓÔÎÙÊ ÔÉÐ ÒÁÚÄÅÌÅÎÉÑ" + +# +# +#: ../mod_ionws/ionws.c:1289 +msgid "The workspace is empty." +msgstr "òÁÂÏÞÉÊ ÓÔÏÌ ÐÕÓÔ." + +#: ../mod_ionws/placement.c:97 +#: ../mod_panews/placement.c:299 +#, c-format +msgid "Ooops... could not find a region to attach client window to on workspace %s." +msgstr "ïÊ... ÎÅ ÍÏÇÕ ÎÁÊÔÉ ÒÅÇÉÏÎ ÄÌÑ ÐÒÉÓÏÅÄÉÎÅÎÉÑ ÏËÎÁ ËÌÉÅÎÔÁ ÎÁ ÒÁÂÏÞÅÍ ÓÔÏÌÅ %s." + +#: ../mod_ionws/split.c:906 +msgid "REGION_RQGEOM_TRYONLY unsupported for status display." +msgstr "REGION_RQGEOM_TRYONLY ÎÅ ÐÏÄÄÅÒÖÉ×ÁÅÔÓÑ ÄÌÑ ÄÉÓÐÌÅÑ ÓÔÁÔÕÓÁ." + +# +#: ../mod_ionws/split.c:979 +msgid "Invalid node." +msgstr "îÅÐÒÁ×ÉÌØÎÙÊ ÕÚÅÌ." + +# +#: ../mod_ionws/split.c:1044 +msgid "Splitting the status display is not allowed." +msgstr "òÁÚÄÅÌÅÎÉÅ ÄÉÓÐÌÅÑ ÓÔÁÔÕÓÁ ÎÅ ÄÏÐÕÓËÁÅÔÓÑ." + +#: ../mod_ionws/split.c:1072 +#: ../mod_ionws/splitfloat.c:896 +msgid "Unable to split: not enough free space." +msgstr "îÅ ÍÏÇÕ ÒÁÚÄÅÌÉÔØ: ÎÅÄÏÓÔÁÔÏÞÎÏ Ó×ÏÂÏÄÎÏÇÏ ÐÒÏÓÔÒÁÎÓÔ×Á." + +# +#: ../mod_ionws/split.c:1558 +msgid "Unable to move the status display out of way of transpose." +msgstr "" + +#: ../mod_ionws/split.c:1722 +#, c-format +msgid "Unable to get configuration for %s." +msgstr "îÅ ÍÏÇÕ ÎÁÊÔÉ ÎÁÓÔÒÏÊËÉ ÄÌÑ \"%s\"." + +#: ../mod_ionws/split-stdisp.c:591 +#: ../mod_ionws/split-stdisp.c:616 +msgid "Status display in bad split configuration." +msgstr "" + +#: ../mod_ionws/split-stdisp.c:657 +msgid "Status display badly located in split tree." +msgstr "äÉÓÐÌÅÊ ÓÔÁÔÕÓÁ ÐÌÏÈÏ ÒÁÚÍÅÝÅÎ × ÄÅÒÅ×Å ÒÁÚÄÅÌÅÎÉÊ." + +#: ../mod_floatws/floatws.c:351 +#: ../mod_panews/panews.c:259 +msgid "Workspace may not be destroyed." +msgstr "òÁÂÏÞÉÊ ÓÔÏÌ ÎÅ ÍÏÖÅÔ ÂÙÔØ ÕÄÁÌÅÎ." + +#: ../mod_floatws/floatws.c:358 +msgid "Failed to rescue some client windows!" +msgstr "îÅËÏÔÏÒÙÅ ÏËÎÁ ËÌÉÅÎÔÏ× ÎÅ ÓÏÈÒÁÎÅÎÙ!" + +#: ../mod_floatws/floatws.c:370 +#: ../mod_panews/panews.c:254 +msgid "Refusing to close non-empty workspace." +msgstr "ïÔËÁÚ × ÚÁËÒÙÔÉÉ ÎÅ ÐÕÓÔÏÇÏ ÒÁÂ. ÐÒÏÓÔÒÁÎÓÔ×Á." + +# +#: ../mod_floatws/floatws.c:462 +msgid "Failure to create a new frame." +msgstr "îÏ×ÙÊ ÆÒÅÊÍ ÎÅ ÓÏÚÄÁÎ." + +# +#: ../mod_floatws/floatws.c:1037 +#: ../mod_floatws/floatws.c:1115 +msgid "Region not managed by the workspace." +msgstr "òÅÇÉÏÎ ÎÅ ÕÐÒÁ×ÌÑÅÔÓÑ ÒÁÂÏÞÉÍ ÓÔÏÌÏÍ." + +#: ../mod_floatws/floatws.c:1236 +msgid "No geometry specified." +msgstr "çÅÏÍÅÔÒÉÑ ÎÅ ÕËÁÚÁÎÁ." + +#: ../mod_floatws/placement.c:125 +#, c-format +msgid "Unknown placement method \"%s\"." +msgstr "îÅÉÚ×ÅÓÔÎÙÊ ÍÅÔÏÄ ÒÁÚÍÅÝÅÎÉÑ \"%s\"." + +#: ../mod_panews/panews.c:193 +msgid "Unable to re-initialise workspace. Destroying." +msgstr "îÅ ÍÏÇÕ ÒÅÉÎÉÃÉÁÌÉÚÉÒÏ×ÁÔØ ÒÁÂÏÞÅÅ ÐÒÏÓÔÒÁÎÓÔ×Ï. õÄÁÌÅÎÉÅ." + +#: ../mod_panews/placement.c:104 +#: ../mod_panews/placement.c:108 +#: ../mod_panews/placement.c:175 +#: ../mod_panews/placement.c:180 +#, c-format +msgid "Malfunctioning placement hook; condition #%d." +msgstr "" + +#: ../mod_query/fwarn.c:34 +msgid "Error:\n" +msgstr "ïÛÉÂËÁ:\n" + +# +#: ../mod_menu/menu.c:490 +msgid "Empty menu." +msgstr "ðÕÓÔÏÅ ÍÅÎÀ." + +# +#: ../mod_sm/sm.c:110 +msgid "Failed to set session directory." +msgstr "äÉÒÅËÔÏÒÉÑ ÓÅÓÓÉÉ ÎÅ ÕÓÔÁÎÏ×ÌÅÎÁ." + +#: ../mod_sm/sm_session.c:85 +msgid "Too many ICE connections." +msgstr "óÌÉÛËÏÍ ÍÎÏÇÏ ÓÏÅÄÉÎÅÎÉÊ 'ICE'." + +#: ../mod_sm/sm_session.c:227 +msgid "Failed to save session state" +msgstr "óÏÓÔÏÑÎÉÅ ÓÅÓÓÉÉ ÎÅ ÓÏÈÒÁÎÉÌÏÓØ" + +#: ../mod_sm/sm_session.c:246 +msgid "Failed to request save-yourself-phase2 from session manager." +msgstr "" + +# +#: ../mod_sm/sm_session.c:295 +msgid "SESSION_MANAGER environment variable not set." +msgstr "ÐÅÒÅÍÅÎÎÁÑ ÏËÒÕÖÅÎÉÑ SESSION_MANAGER ÎÅ ÕÓÔÁÎÏ×ÌÅÎÁ." + +#: ../mod_sm/sm_session.c:300 +msgid "Session Manager: IceAddConnectionWatch failed." +msgstr "" + +# +#: ../mod_sm/sm_session.c:325 +msgid "Unable to connect to the session manager." +msgstr "îÅ ÍÏÇÕ ÓÏÅÄÉÎÉÔØÓÑ Ó ÍÅÎÅÄÖÅÒÏÍ ÓÅÓÓÉÊ." + +#: ../mod_sp/main.c:137 +#, c-format +msgid "Unable to create scratchpad for screen %d." +msgstr "" + +#: ../de/init.c:41 +#, c-format +msgid "Border attribute %s sanity check failed." +msgstr "" + +#: ../de/init.c:64 +#, c-format +msgid "Unknown border style \"%s\"." +msgstr "îÅÉÚ×ÅÓÔÎÙÊ ÓÔÉÌØ ÂÏÒÄÀÒÁ \"%s\"." + +#: ../de/init.c:96 +#, c-format +msgid "Unable to allocate colour \"%s\"." +msgstr "îÅ ÍÏÇÕ ÒÁÚÍÅÓÔÉÔØ Ã×ÅÔ \"%s\"." + +#: ../de/init.c:160 +#, c-format +msgid "Corrupt substyle table %d." +msgstr "ðÏ×ÒÅÖÄÅÎÎÁÑ ÔÁÂÌÉÃÁ ÐÏÄÓÔÉÌÅÊ %d." + +#: ../de/init.c:193 +#, c-format +msgid "Unknown text alignment \"%s\"." +msgstr "îÅÉÚ×ÅÓÔÎÏÅ ×ÙÒÁ×ÎÉ×ÁÎÉÅ ÔÅËÓÔÁ \"%s\"." + +#: ../de/init.c:263 +#, c-format +msgid "'based_on' for %s points back to the style itself." +msgstr "'based_on' ÄÌÑ %s ÕËÁÚÙ×ÁÅÔ ÎÁ ÓÁÍ ÓÔÉÌØ." + +#: ../de/init.c:266 +#, c-format +msgid "Unknown base style \"%s\"." +msgstr "îÅÉÚ×ÅÓÔÎÙÊ ÂÁÚÏ×ÙÊ ÓÔÉÌØ \"%s\"." + +#: ../de/font.c:47 +#, c-format +msgid "Fontset for font pattern '%s' implements context dependent drawing, which is unsupported. Expect clutter." +msgstr "" + +#: ../de/font.c:58 +#, c-format +msgid "Could not load font \"%s\", trying \"%s\"" +msgstr "îÅ ÍÏÇÕ ÚÁÇÒÕÚÉÔØ ÛÒÉÆÔ \"%s\", ÐÒÏÂÕÀ \"%s\"" + +#: ../de/style.c:315 +#, c-format +msgid "Style %s still in use [%d] but the module is being unloaded!" +msgstr "óÔÉÌØ %s ×ÓÅ ÅÝÅ ÉÓÐÏÌØÚÕÅÔÓÑ [%d] ÎÏ ÍÏÄÕÌØ ×ÙÇÒÕÖÁÅÔÓÑ!" + +#: ../ion/ion.c:41 +#: ../pwm/pwm.c:41 +msgid "X display to use" +msgstr "éÓÐÏÌØÚÏ×ÁÔØ X ÄÉÓÐÌÅÊ" + +#: ../ion/ion.c:44 +#: ../pwm/pwm.c:44 +msgid "Configuration file" +msgstr "æÁÊÌ ËÏÎÆÉÇÕÒÁÃÉÉ" + +#: ../ion/ion.c:47 +#: ../pwm/pwm.c:47 +msgid "Add directory to search path" +msgstr "äÏÂÁ×ÉÔØ ÄÉÒÅËÔÏÒÉÀ Ë ÐÕÔÉ ÐÏÉÓËÁ" + +#: ../ion/ion.c:50 +#: ../pwm/pwm.c:50 +msgid "Manage default root window/non-Xinerama screen only" +msgstr "" + +#: ../ion/ion.c:54 +msgid "Use Xinerama screen information (default: 1/yes)" +msgstr "" + +#: ../ion/ion.c:57 +#: ../pwm/pwm.c:57 +msgid "Ignored: not compiled with Xinerama support" +msgstr "éÇÎÏÒÉÒÕÀ: ÓËÏÍÐÉÌÉÒÏ×ÁÎÏ ÂÅÚ ÐÏÄÄÅÒÖËÉ Xinerama" + +#: ../ion/ion.c:61 +#: ../pwm/pwm.c:61 +msgid "Name of session (affects savefiles)" +msgstr "" + +#: ../ion/ion.c:64 +#: ../pwm/pwm.c:64 +msgid "Session manager client ID" +msgstr "" + +#: ../ion/ion.c:67 +#: ../pwm/pwm.c:67 +msgid "Do not create startup error log and display it with xmessage." +msgstr "îÅ ÓÏÚÄÁ×ÁÔØ ÖÕÒÎÁÌ ÏÛÉÂÏË ÚÁÐÕÓËÁ, Á ÐÏËÁÚÙ×ÁÔØ ÅÇÏ Ó ÐÏÍÏÝØÀ xmessage." + +#: ../ion/ion.c:71 +#: ../pwm/pwm.c:71 +msgid "Show this help" +msgstr "ðÏËÁÚÁÔØ ÐÏÍÏÝØ" + +#: ../ion/ion.c:74 +#: ../pwm/pwm.c:74 +msgid "Show program version" +msgstr "ðÏËÁÚÁÔØ ×ÅÒÓÉÀ ÐÒÏÇÒÁÍÍÙ" + +#: ../ion/ion.c:77 +#: ../pwm/pwm.c:77 +msgid "Show about text" +msgstr "ðÏËÁÚÁÔØ 'ï ÐÒÏÇÒÁÍÍÅ...'" + +#: ../ion/ion.c:92 +msgid "Could not get user configuration file directory." +msgstr "îÅ ÍÏÇÕ ÐÏÌÕÞÉÔØ ÄÉÒÅËÔÏÒÉÀ Ó ÆÁÊÌÁÍÉ ÐÏÌØÚÏ×ÁÔÅÌØÓËÉÈ ÎÁÓÔÒÏÅË " + +#: ../ion/ion.c:106 +#, c-format +msgid "%s/welcome.txt" +msgstr "%s/welcome.ru.txt" + +#: ../ion/ion.c:139 +#: ../pwm/pwm.c:86 +#, c-format +msgid "" +"Usage: %s [options]\n" +"\n" +msgstr "" + +#: ../ion/ion.c:199 +#: ../pwm/pwm.c:149 +msgid "Invalid parameter to -xinerama." +msgstr "îÅÐÒÁ×ÉÌØÎÙÊ ÐÁÒÁÍÅÔÒ ÄÌÑ '-xinerama'." + +# +#: ../ion/ion.c:218 +#: ../pwm/pwm.c:168 +msgid "Invalid command line." +msgstr "îÅÐÒÁ×ÉÌØÎÁÑ ËÏÍÁÎÄÎÁÑ ÓÔÒÏËÁ" + +#: ../ion/ion.c:239 +msgid "Ion startup error log:\n" +msgstr "öÕÒÎÁÌ ÏÛÉÂÏË ÚÁÐÕÓËÁ Ion:\n" + +#: ../ion/ion.c:249 +#: ../pwm/pwm.c:199 +msgid "Refusing to start due to encountered errors." +msgstr "÷ ÚÁÐÕÓËÅ ÏÔËÁÚÁÎÏ × ÓÉÌÕ ×ÏÚÎÉËÛÉÈ ÏÛÉÂÏË." + +#: ../pwm/pwm.c:54 +msgid "Use Xinerama screen information (default: 0/no)" +msgstr "" + +#: ../pwm/pwm.c:189 +msgid "PWM startup error log:\n" +msgstr "öÕÒÎÁÌ ÏÛÉÂÏË ÚÁÐÕÓËÁ PWM:\n" + +#: ../../libextl/readconfig.c:86 +msgid "$HOME not set" +msgstr "$HOME ÎÅ ÕÓÔÁÎÏ×ÌÅÎÁ" + +#: ../../libextl/readconfig.c:113 +msgid "User directory not set. Unable to set session directory." +msgstr "äÉÒÅËÔÏÒÉÑ ÐÏÌØÚÏ×ÁÔÅÌÑ ÎÅ ÕÓÔÁÎÏ×ÌÅÎÁ. îÅ ÍÏÇÕ ÕÓÔÁÎÏ×ÉÔØ ÄÉÒÅËÔÏÒÉÀ ÓÅÓÓÉÉ." + +#: ../../libextl/readconfig.c:247 +#, c-format +msgid "Falling back to %s." +msgstr "ïÔËÁÔ Ë %s." + +#: ../../libextl/readconfig.c:441 +#, c-format +msgid "Unable to create session directory \"%s\"." +msgstr "îÅ ÍÏÇÕ ÓÏÚÄÁÔØ ÄÉÒÅËÔÏÒÉÀ ÓÅÓÓÉÉ \"%s\"." + +#: ../../libextl/luaextl.c:113 +msgid "Lua stack full." +msgstr "óÔÅË Lua ÐÏÌÏÎ." + +# +#: ../../libextl/luaextl.c:139 +msgid "Unknown Lua error." +msgstr "îÅÉÚ×ÅÓÔÎÁÑ ÏÛÉÂËÁ × Lua." + +#: ../../libextl/luaextl.c:456 +msgid "Stack trace:" +msgstr "ôÒÁÓÓÉÒÏ×ËÁ ÓÔÅËÁ:" + +#: ../../libextl/luaextl.c:463 +#, c-format +msgid "" +"\n" +"(Unable to get debug info for level %d)" +msgstr "" +"\n" +"(îÅ×ÏÚÍÏÖÎÏ ÐÏÌÕÞÉÔØ ÏÔÌÁÄÏÞÎÕÀ ÉÎÆÏÒÍÁÃÉÀ ÄÌÑ ÕÒÏ×ÎÑ %d)" + +#: ../../libextl/luaextl.c:481 +msgid "" +"\n" +" [Skipping unnamed C functions.]" +msgstr "" + +#: ../../libextl/luaextl.c:532 +msgid "Internal error." +msgstr "÷ÎÕÔÒÅÎÎÑÑ ÏÛÉÂËÁ." + +#: ../../libextl/luaextl.c:551 +msgid "Unable to initialize Lua." +msgstr "îÅ ÍÏÇÕ ÉÎÉÃÉÁÌÉÚÉÒÏ×ÁÔØ Lua." + +#: ../../libextl/luaextl.c:1277 +msgid "Too many return values. Use a C compiler that has va_copy to support more." +msgstr "óÌÉÛËÏÍ ÍÎÏÇÏ ×ÏÚ×ÒÁÝÁÅÍÙÈ ÚÎÁÞÅÎÉÊ. éÓÐÏÌØÚÕÊÔÅ ËÏÍÐÉÌÑÔÏÒ C Õ ËÏÔÏÒÏÇÏ ÅÓÔØ va_copy ÄÌÑ ÂÏÌØÛÅÊ ÐÏÄÄÅÒÖËÉ" + +#: ../../libextl/luaextl.c:1295 +#, c-format +msgid "Invalid return value (expected '%c', got lua type \"%s\")." +msgstr "îÅÄÏÐÕÓÔÉÍÏÅ ×ÏÚ×ÒÁÝÁÅÍÏÅ ÚÎÁÞÅÎÉÅ (ÏÖÉÄÁÌÏÓØ '%c', ÐÏÌÕÞÅÎ lua-ÔÉÐ \"%s\")." + +#: ../../libextl/luaextl.c:1330 +#: ../../libextl/luaextl.c:1594 +msgid "Stack full." +msgstr "óÔÅË ÐÏÌÏÎ." + +#: ../../libextl/luaextl.c:1570 +msgid "L1 call handler upvalues corrupt." +msgstr "" + +#: ../../libextl/luaextl.c:1575 +msgid "Called function has been unregistered." +msgstr "" + +#: ../../libextl/luaextl.c:1587 +#, c-format +msgid "Attempt to call an unsafe function \"%s\" in restricted mode." +msgstr "ðÏÐÙÔËÁ ×ÙÚÏ×Á ÎÅÂÅÚÏÐÁÓÎÏÊ ÆÕÎËÃÉÉ \"%s\" × ÏÇÒÁÎÉÞÅÎÎÏÍ ÒÅÖÉÍÅ. " + +#: ../../libextl/luaextl.c:1603 +#, c-format +msgid "Argument %d to %s is of invalid type. (Argument template is '%s', got lua type %s)." +msgstr "" + +#: ../../libextl/luaextl.c:1782 +#, c-format +msgid "Function '%s' has more parameters than the level 1 call handler can handle" +msgstr "" + +# +#: ../../libextl/luaextl.c:2199 +msgid "Maximal serialisation depth reached." +msgstr "äÏÓÔÉÇÎÕÔÁ ÍÁËÓÉÍÁÌØÎÁÑ ÇÌÕÂÉÎÁ ÓÅÒÉÁÌÉÚÁÃÉÉ." + +#: ../../libextl/luaextl.c:2220 +#, c-format +msgid "Unable to serialise type %s." +msgstr "îÅ×ÏÚÍÏÖÎÏ ÓÅÒÉÁÌÉÚÉÒÏ×ÁÔØ ÔÉÐ %s." + +#: ../../libextl/luaextl.c:2251 +msgid "-- This file has been generated by Ion. Do not edit.\n" +msgstr "-- äÁÎÎÙÊ ÆÁÊÌ ÂÙÌ ÓÏÚÄÁÎ éÏÎÏÍ. îÅ ÒÅÄÁËÔÉÒÏ×ÁÔØ.\n" + +#: ../../libextl/misc.c:17 +#, c-format +msgid "Type checking failed in level 2 call handler for parameter %d (got %s, expected %s)." +msgstr "" + +#: ../etc/cfg_floatws.lua:34 +msgid "Lower the frame." +msgstr "ïÐÕÓÔÉÔØ ÆÒÅÊÍ" + +#: ../etc/cfg_bindings.lua:171 +msgid "Display frame context menu." +msgstr "ðÏËÁÚÁÔØ ËÏÎÔÅËÓÔÎÏÅ ÍÅÎÀ ÆÒÅÊÍÁ." + +# +#: ../etc/cfg_ionws.lua:44 +msgid "Flip at root" +msgstr "ðÅÒ×ÅÒÎÕÔØ × ËÏÒÎÅ×ÏÍ" + +# +#: ../etc/cfg_bindings.lua:43 +msgid "Go to n:th screen on multihead setup." +msgstr "" + +#: ../ext_statusbar/ext_statusbar.lua:308 +msgid "Failed to create statusbar." +msgstr "óÔÁÔÕÓÂÁÒ ÎÅ ÓÏÚÄÁÎ" + +#: ../pwm/cfg_pwm_menus.lua:13 +#: ../etc/cfg_menus.lua:10 +msgid "Help" +msgstr "ðÏÍÏÝØ" + +#: ../mod_query/mod_query.lua:566 +msgid "SSH to:" +msgstr "SSH to:" + +#: ../etc/cfg_menus.lua:28 +msgid "Restart" +msgstr "ðÅÒÅÚÁÐÕÓË" + +#: ../mod_menu/mod_menu.lua:231 +msgid "Cannot save selection." +msgstr "îÅ ÍÏÇÕ ÓÏÈÒÁÎÉÔØ ×ÙÄÅÌÅÎÉÅ" + +#: ../etc/cfg_bindings.lua:206 +msgid "End the resize mode." +msgstr "úÁ×ÅÒÛÉÔØ ÒÅÖÉÍ ÒÁÚÍÅÒÁ." + +# +# +#: ../etc/cfg_ionws.lua:48 +#: ../etc/cfg_ionws.lua:58 +msgid "Vertically" +msgstr "÷ÅÒÔÉËÁÌØÎÏ" + +#: ../ioncore/ioncore-bindings.lua:34 +msgid "Invalid guard %s." +msgstr "" + +#: ../etc/cfg_bindings.lua:109 +msgid "Query for Lua code to execute." +msgstr "ïÞÅÒÅÄØ ÎÁ ×ÙÐÏÌÎÅÎÉÅ ËÏÄÁ Lua." + +# +#: ../etc/cfg_bindings.lua:47 +msgid "Go to next/previous screen on multihead setup." +msgstr " " + +#: ../mod_query/mod_query.lua:344 +msgid "none" +msgstr "ÎÉÞÅÇÏ" + +#: ../etc/cfg_bindings.lua:219 +msgid "Shrink in specified direction." +msgstr "õÍÅÎØÛÉÔØ × ÕËÁÚÁÎÎÏÍ ÎÁÐÒÁ×ÌÅÎÉÉ" + +#: ../etc/cfg_bindings.lua:160 +msgid "Maximize the frame horizontally/vertically." +msgstr "òÁÚ×ÅÒÎÕÔØ ÆÒÅÊÍ ÐÏ ×ÅÒÔÉËÁÌÉ/ÇÏÒÉÚÏÎÔÁÌÉ." + +#: ../etc/cfg_bindings.lua:156 +msgid "Move current object within the frame left/right." +msgstr "ðÅÒÅÍÅÓÔÉÔØ ÔÅËÕÝÉÊ ÏÂßÅËÔ × ÐÒÅÄÅÌÁÈ ÆÒÅÊÍÁ ×ÐÒÁ×Ï/×ÌÅ×Ï" + +#: ../etc/cfg_ionws.lua:12 +msgid "Go to frame above/below/right/left of current frame." +msgstr "ðÅÒÅÊÔÉ Ë ÆÒÅÊÍÕ ×ÙÛÅ/ÎÉÖÅ/ÐÒÁ×ÅÅ/ÌÅ×ÅÅ ÔÅËÕÝÅÇÏ ÆÒÅÊÍÁ" + +#: ../ext_statusbar/ext_statusbar.lua:289 +msgid "Screen not found." +msgstr "üËÒÁÎ ÎÅ ÎÁÊÄÅÎ" + +#: ../etc/cfg_bindings.lua:182 +msgid "Resize the frame." +msgstr "éÚÍÅÎÉÔØ ÒÁÚÍÅÒ ÆÒÅÊÍÁ" + +#: ../etc/cfg_bindings.lua:189 +msgid "Move objects between frames by dragging and dropping the tab." +msgstr "ðÅÒÅÍÅÓÔÉÔØ ÏÂßÅËÔÙ ÍÅÖÄÕ ÆÒÅÊÍÁÍÉ ÐÒÉ ÐÏÍÏÝÉ ÐÅÒÅÔÁÓËÉ×ÁÎÉÑ ÔÁÂÏ×." + +#: ../mod_query/mod_query.lua:99 +msgid "Could not find %s" +msgstr "îÅ ÍÏÇÕ ÎÁÊÔÉ %s:" + +#: ../ext_statusbar/ext_statusbar.lua:295 +msgid "Screen already has an stdisp. Refusing to create a statusbar." +msgstr "üËÒÁÎ ÕÖÅ ÉÍÅÅÔ stdisp. ïÔËÁÚ × ÓÏÚÄÁÎÉÉ ÓÔÁÔÕÓÂÁÒÁ." + +#: ../etc/cfg_bindings.lua:112 +msgid "Query for host to connect to with SSH." +msgstr "úÁÐÒÏÓ ÈÏÓÔÁ ÄÌÑ ÓÏÅÄÉÎÅÎÉÑ ÞÅÒÅÚ SSH." + +# +#: ../etc/cfg_bindings.lua:229 +msgid "Move in specified direction." +msgstr "ðÅÒÅÍÅÓÔÉÔØ × ÕËÁÚÁÎÎÏÍ ÎÁÐÒÁ×ÌÅÎÉÉ." + +# +#: ../ext_statusbar/ext_statusbar.lua:27 +msgid "mail" +msgstr "ÐÏÞÔÁ" + +#: ../mod_query/mod_query.lua:807 +msgid "" +"\n" +"Transients:\n" +msgstr "" + +#: ../etc/cfg_menu.lua:21 +msgid "Select next/previous menu entry." +msgstr "×ÙÂÒÁÔØ ÓÌÅÄ/ÐÒÅÄ ÐÕÎËÔ ÍÅÎÀ" + +#: ../ioncore/ioncore-luaext.lua:30 +msgid "Recursive table - unable to deepcopy" +msgstr "òÅËÕÒÓÉ×ÎÁÑ ÔÁÂÌÉÃÁ - ÇÌÕÂÏËÏÅ ËÏÐÉÒÏ×ÁÎÉÅ ÎÅ×ÏÚÍÏÖÎÏ" + +# +#: ../etc/cfg_bindings.lua:209 +msgid "Grow in specified direction." +msgstr "÷ÙÒÁÓÔÉ × ÕËÁÚÁÎÎÏÍ ÎÁÐÒÁ×ÌÅÎÉÉ." + +#: ../ioncore/ioncore-bindings.lua:68 +msgid "Invalid command" +msgstr "îÅÐÒÁ×ÉÌØÎÁÑ ËÏÍÁÎÄÁ" + +#: ../mod_query/mod_query.lua:189 +#: ../mod_menu/mod_menu.lua:245 +msgid "Too much result data" +msgstr "óÌÉÛËÏÍ ÍÎÏÇÏ ËÏÎÅÞÎÙÈ ÄÁÎÎÙÈ " + +#: ../mod_query/mod_query.lua:344 +msgid "Workspace type (%s):" +msgstr "ôÉÐ ÒÁÂÏÞÅÇÏ ÓÔÏÌÁ (%s):" + +#: ../etc/cfg_bindings.lua:36 +msgid "Go to previous active object." +msgstr "ÐÅÒÅÊÔÉ Ë ÓÌÅÄ. ÁËÔÉ×ÎÏÍÕ ÏÂßÅËÔÕ" + +# +#: ../etc/cfg_query.lua:67 +msgid "Clear mark/cancel selection." +msgstr "ïÞÉÓÔËÁ ÍÅÔËÉ/ÏÔÍÅÎÁ ×ÙÄÅÌÅÎÉÑ" + +#: ../mod_query/mod_query.lua:772 +msgid "No entry '%s'" +msgstr "îÅÔ ÐÕÎËÔÁ '%s'" + +# +# +#: ../etc/cfg_bindings.lua:90 +msgid "Toggle fullscreen mode of current client window." +msgstr "ðÅÒÅËÌÀÞÉÔØ ÔÅËÕÝÅÅ ÏËÎÏ × ÐÏÌÎÏÜËÒÁÎÎÙÊ ÒÅÖÉÍ." + +#: ../etc/cfg_bindings.lua:103 +msgid "Query for manual page to be displayed." +msgstr "úÁÐÒÏÓ ÎÁ ÏÔÏÂÒÁÖÅÎÉÅ ÓÔÒÁÎÉÃÙ man." + +#: ../etc/cfg_bindings.lua:51 +msgid "Show the Ion manual page." +msgstr "ðÏËÁÚÁÔØ ÓÔÒÁÎÉÃÕ ÒÕËÏ×ÏÄÓÔ×Á ÐÏ éÏÎ." + +#: ../etc/cfg_bindings.lua:60 +msgid "Display the main menu." +msgstr "ðÏËÁÚÁÔØ ÇÌÁ×ÎÏÅ ÍÅÎÀ" + +# +#: ../etc/cfg_bindings.lua:54 +msgid "Run a terminal emulator." +msgstr "úÁÐÕÓË ÜÍÕÌÑÔÏÒÁ ÔÅÒÍÉÎÁÌÁ" + +#: ../etc/cfg_bindings.lua:121 +msgid "Query for workspace to go to or create a new one." +msgstr "" + +#: ../etc/cfg_menus.lua:38 +msgid "Kill" +msgstr "õÂÉÔØ" + +#: ../pwm/cfg_pwm_menus.lua:25 +#: ../etc/cfg_menus.lua:37 +msgid "Close" +msgstr "úÁËÒÙÔØ" + +#: ../mod_query/mod_query.lua:381 +msgid "Go to or create workspace:" +msgstr "ðÅÒÅÊÔÉ Ë ÉÌÉ ÓÏÚÄÁÔØ ÒÁÂÏÞÉÊ ÓÔÏÌ:" + +#: ../etc/cfg_query.lua:35 +msgid "Delete one word forward/backward." +msgstr "õÄÁÌÉÔØ ÏÄÎÏ ÓÌÏ×Ï ×ÐÅÒÅÄÉ/ÓÚÁÄÉ." + +#: ../etc/cfg_query.lua:17 +msgid "Go to end/beginning." +msgstr "ðÅÒÅÊÔÉ × ËÏÎÅÃ/ÎÁÞÁÌÏ" + +# +#: ../etc/cfg_ionws.lua:43 +msgid "Transpose" +msgstr "ðÅÒÅÍÅÓÔÉÔØ" + +#: ../etc/cfg_bindings.lua:152 +msgid "Switch to next/previous object within the frame." +msgstr "ðÅÒÅËÌÀÞÉÔØÓÑ ÎÁ ÓÌÅÄ/ÐÒÅÄ ÏÂßÅËÔ ×ÎÕÔÒÉ ÆÒÅÊÍÁ." + +#: ../mod_query/mod_query.lua:402 +msgid "Restart Ion (y/n)?" +msgstr "ðÅÒÅÚÁÐÕÓÔÉÔØ éÏÎ (y/n)?" + +#: ../mod_query/mod_query.lua:724 +msgid "Lua code: " +msgstr "ëÏÄ Lua:" + +#: ../mod_query/mod_query.lua:367 +msgid "Attach window:" +msgstr "ðÒÉÃÅÐÉÔØ ÏËÎÏ:" + +#: ../etc/cfg_bindings.lua:186 +#: ../etc/cfg_floatws.lua:37 +msgid "Move the frame." +msgstr "ðÅÒÅÍÅÓÔÉÔØ ÆÒÅÊÍ." + +#: ../pwm/cfg_pwm_menus.lua:11 +#: ../etc/cfg_menus.lua:8 +msgid "Programs" +msgstr "ðÒÏÇÒÁÍÍÙ" + +#: ../etc/cfg_query.lua:23 +msgid "Skip one word forward/backward." +msgstr "ðÒÏÐÕÓÔÉÔØ ÏÄÎÏ ÓÌÏ×Ï ×ÐÅÒÅÄ/ÎÁÚÁÄ." + +#: ../mod_query/mod_query.lua:738 +msgid "Unknown menu %s." +msgstr "îÅÉÚ×ÅÓÔÎÏÅ ÍÅÎÀ %s." + +#: ../etc/cfg_floatws.lua:12 +msgid "Backwards-circulate focus and raise the newly focused frame." +msgstr "" + +#: ../etc/cfg_menus.lua:44 +msgid "Window info" +msgstr "éÎÆÏÒÍÁÃÉÑ Ï ÏËÎÅ" + +# +#: ../etc/cfg_query.lua:58 +msgid "Set mark/begin selection." +msgstr "" + +#: ../etc/cfg_menus.lua:30 +msgid "Restart TWM" +msgstr "ðÅÒÅÚÁÐÕÓË TWM" + +#: ../etc/cfg_ionws.lua:9 +msgid "Split current frame vertically." +msgstr "òÁÚÄÅÌÉÔØ ÔÅËÕÝÉÊ ÆÒÅÊÍ ÐÏ ×ÅÒÔÉËÁÌÉ" + +# +# +#: ../etc/cfg_bindings.lua:57 +msgid "Create a new workspace of chosen default type." +msgstr "óÏÚÄÁÔØ ÎÏ×ÙÊ ÒÁÂÏÞÉÊ ÓÔÏÌ Ó ÔÉÐÏÍ ×ÙÂÒÁÎÎÙÍ ÐÏ-ÕÍÏÌÞÁÎÉÀ." + +# +#: ../etc/cfg_floatws.lua:29 +msgid "Raise the frame." +msgstr "ðÏÄÎÑÔØ ÆÒÅÊÍ" + +#: ../etc/cfg_floatws.lua:46 +msgid "(Un)stick" +msgstr "" + +#: ../mod_query/mod_query.lua:287 +#: ../mod_query/mod_query.lua:297 +msgid "Could not find client window %s." +msgstr "îÅ ÍÏÇÕ ÎÁÊÔÉ ÏËÎÏ ËÌÉÅÎÔÁ %s." + +#: ../etc/cfg_query.lua:82 +msgid "Close the query/message box, not executing bound actions." +msgstr "" + +# +#: ../etc/cfg_bindings.lua:168 +msgid "Query for a client window to attach to active frame." +msgstr "" + +#: ../mod_query/mod_query.lua:488 +msgid "Run:" +msgstr "úÁÐÕÓÔÉÔØ:" + +# +#: ../etc/cfg_panews.lua:11 +msgid "Resize the area." +msgstr "éÚÍÅÎÉÔØ ÒÁÚÍÅÒ ÏÂÌÁÓÔÉ." + +# +#: ../etc/cfg_bindings.lua:95 +msgid "Kill client owning current client window." +msgstr "õÂÉÔØ ËÌÉÅÎÔÁ ÔÅËÕÝÅÇÏ ÏËÎÁ." + +#: ../mod_query/mod_query.lua:595 +msgid "Manual page (%s):" +msgstr "óÔÒÁÎÉÃÁ ÒÕËÏ×ÏÄÓÔ×Á (%s):" + +# +#: ../etc/cfg_bindings.lua:124 +msgid "Query for a client window to go to." +msgstr "" + +# +#: ../etc/cfg_ionws.lua:39 +msgid "Destroy frame" +msgstr "õÄÁÌÉÔØ ÆÒÅÊÍ" + +#: ../mod_query/mod_query.lua:339 +msgid "Unknown error" +msgstr "ÎÅÉÚ×ÅÓÔÎÁÑ ÏÛÉÂËÁ" + +#: ../etc/cfg_bindings.lua:175 +#: ../etc/cfg_panews.lua:8 +msgid "Begin move/resize mode." +msgstr "÷ÈÏÄ × ÒÅÖÉÍ ÐÅÒÅÍÅÝÅÎÉÑ/ÒÁÚÍÅÒÁ" + +#: ../etc/cfg_bindings.lua:136 +msgid "Tag current object within the frame." +msgstr "" + +#: ../etc/cfg_bindings.lua:140 +msgid "Switch to n:th object within the frame." +msgstr "" + +#: ../etc/cfg_bindings.lua:203 +msgid "Cancel the resize mode." +msgstr "ïÔÍÅÎÁ ÒÅÖÉÍÁ ÒÁÚÍÅÒÁ." + +#: ../build/mkman.lua:196 +msgid "%s %s" +msgstr "%s %s" + +#: ../etc/cfg_query.lua:27 +msgid "Delete next character." +msgstr "õÄÁÌÉÔØ ÓÌÅÄÕÀÝÉÊ ÓÉÍ×ÏÌ" + +#: ../mod_query/mod_query.lua:356 +msgid "Go to window:" +msgstr "ðÅÒÅÊÔÉ Ë ÏËÎÕ:" + +#: ../build/mkman.lua:182 +msgid "drag" +msgstr "ÐÅÒÅÔÁÝÉÔØ" + +#: ../build/mkman.lua:181 +msgid "click" +msgstr "ËÌÉËÎÕÔØ" + +#: ../build/mkman.lua:180 +msgid "press" +msgstr "ÎÁÖÁÔØ" + +#: ../etc/cfg_menu.lua:27 +msgid "Clear the menu's typeahead find buffer." +msgstr "" + +#: ../etc/cfg_ionws.lua:47 +msgid "Split" +msgstr "òÁÚÄÅÌÉÔØ" + +# +#: ../etc/cfg_bindings.lua:98 +msgid "Send next key press to current client window. Some programs may not allow this by default." +msgstr "" + +#: ../etc/cfg_menu.lua:11 +msgid "Close the menu." +msgstr "úÁËÒÙÔØ ÍÅÎÀ" + +#: ../etc/cfg_query.lua:87 +msgid "Scroll the message or completions up/down." +msgstr "" + +#: ../etc/cfg_query.lua:74 +msgid "Close the query and execute bound action." +msgstr "" + +# +#: ../etc/cfg_bindings.lua:31 +msgid "Switch to next/previous object within current screen." +msgstr "ðÅÒÅËÌÀÞÉÔØÓÑ ÎÁ ÓÌÅÄ./ÐÒÅÄ. ÏÂßÅËÔ ×ÎÕÔÒÉ ÔÅËÕÝÅÇÏ ÜËÒÁÎÁ." + +#: ../etc/cfg_query.lua:39 +msgid "Delete to end of line." +msgstr "õÄÁÌÉÔØ ÄÏ ËÏÎÃÁ ÓÔÒÏËÉ." + +#: ../pwm/cfg_pwm_menus.lua:14 +#: ../etc/cfg_menus.lua:11 +msgid "About Ion" +msgstr "ï Ion" + +#: ../mod_query/mod_query.lua:804 +msgid "" +"Title: %s\n" +"Class: %s\n" +"Role: %s\n" +"Instance: %s\n" +"XID: 0x%x" +msgstr "" +"îÁÚ×ÁÎÉÅ: %s\n" +"ëÌÁÓÓ(class): %s\n" +"òÏÌØ(role): %s\n" +"üËÚÅÍÐÌÑÒ(instance): %s\n" +"XID: 0x%x" + +# +#: ../etc/cfg_query.lua:61 +msgid "Cut selection." +msgstr "÷ÙÒÅÚÁÔØ ×ÙÄÅÌÅÎÎÏÅ." + +#: ../etc/cfg_query.lua:53 +msgid "Paste from the clipboard." +msgstr "÷ÓÔÁ×ÉÔØ ÉÚ ËÌÉÐÂÏÒÄÁ." + +# +#: ../etc/cfg_query.lua:45 +msgid "Select next/previous (matching) history entry." +msgstr "÷ÙÂÒÁÔØ ÓÌÅÄ./ÐÒÅÄ. (ÓÏÏÔ×ÅÔÓÔ×ÕÀÝÉÊ) ÐÕÎËÔ ÉÓÔÏÒÉÉ." + +#: ../etc/cfg_menus.lua:43 +msgid "Clear tags" +msgstr "ïÞÉÓÔÉÔØ ÔÅÇÉ" + +#: ../etc/cfg_query.lua:42 +msgid "Delete the whole line." +msgstr "õÄÁÌÉÔØ ÓÔÒÏËÕ ÃÅÌÉËÏÍ" + +#: ../etc/cfg_query.lua:71 +msgid "Try to complete the entered text." +msgstr "ðÏÐÙÔËÁ ÚÁ×ÅÒÛÉÔØ ××ÅÄÅÎÎÙÊ ÔÅËÓÔ." + +#: ../etc/cfg_query.lua:31 +msgid "Delete previous character." +msgstr "õÄÁÌÉÔØ ÐÒÅÄÙÄÕÝÉÊ ÓÉÍ×ÏÌ." + +#: ../mod_query/mod_query.lua:299 +msgid "Cannot attach: different root windows." +msgstr "îÅ ÍÏÇÕ ÐÒÉÃÅÐÉÔØ: ÒÁÚÎÙÅ ËÏÒÎÅ×ÙÅ ÏËÎÁ." + +#: ../build/mkman.lua:183 +msgid "double click" +msgstr "Ä×ÏÊÎÏÊ ËÌÉË" + +#: ../etc/cfg_query.lua:11 +msgid "Move one character forward/backward." +msgstr "ðÅÒÅÍÅÓÔÉÔØÓÑ ÎÁ ÏÄÉÎ ÓÉÍ×ÏÌ ×ÐÅÒÅÄ/ÎÁÚÁÄ." + +#: ../etc/cfg_menus.lua:29 +msgid "Restart PWM" +msgstr "ðÅÒÅÚÁÐÕÓË PWM" + +#: ../etc/cfg_bindings.lua:115 +msgid "Query for file to edit." +msgstr "úÁÐÒÏÓ ÎÁ ÒÅÄÁËÔÉÒÏ×ÁÎÉÅ ÆÁÊÌÁ." + +# +#: ../etc/cfg_bindings.lua:39 +msgid "Clear all tags." +msgstr "ïÞÉÓÔÉÔØ ×ÓÅ ÔÅÇÉ." + +#: ../etc/cfg_bindings.lua:178 +msgid "Switch the frame to display the object indicated by the tab." +msgstr "ðÅÒÅËÌÀÞÉÔØ ÆÒÅÊÍ ÎÁ ÐÏËÁÚ ÏÂßÅËÔÁ ÕËÁÚÁÎÎÏÇÏ ÔÁÂÏÍ." + +# +# +#: ../etc/cfg_ionws.lua:50 +#: ../etc/cfg_ionws.lua:60 +msgid "Horizontally" +msgstr "çÏÒÉÚÏÎÔÁÌØÎÏ" + +#: ../pwm/cfg_pwm_menus.lua:16 +#: ../pwm/cfg_pwm_menus.lua:24 +msgid "New" +msgstr "îÏ×ÙÊ" + +#: ../mod_query/mod_query.lua:410 +msgid "Frame name:" +msgstr "éÍÑ ÆÒÅÊÍÁ:" + +# +#: ../etc/cfg_bindings.lua:18 +msgid "Switch to n:th object (workspace, full screen client window) within current screen." +msgstr "ðÅÒÅËÌÀÞÉÔØÓÑ ÎÁ n-ÎÙÊ ÏÂßÅËÔ (ÒÁÂ.ÓÔÏÌ, ÐÏÌÎÏÜËÒÁÎÎÏÅ ÏËÎÏ) ×ÎÕÔÒÉ ÔÅËÕÝÅÇÏ ÜËÒÁÎÁ." + +#: ../etc/cfg_bindings.lua:64 +msgid "Display the window list menu." +msgstr "ðÏËÁÚÁÔØ ÍÅÎÀ ÓÐÉÓËÁ ÏËÏÎ." + +#: ../etc/cfg_floatws.lua:26 +msgid "Toggle shade mode" +msgstr "ðÅÒÅËÌÀÞÉÔØ ÒÅÖÉÍ ÔÅÎÉ" + +#: ../mod_query/mod_query.lua:393 +msgid "Exit Ion/Shutdown session (y/n)?" +msgstr "÷ÙÈÏÄ ÉÚ éÏÎ/úÁ×ÅÒÛÉÔØ ÓÅÓÓÉÀ (y/n)?" + +#: ../etc/cfg_floatws.lua:15 +msgid "Raise/lower active frame." +msgstr "ðÏÄÎÑÔØ/ÏÐÕÓÔÉÔØ ÁËÔÉ×ÎÙÊ ÆÒÅÊÍ" + +#: ../mod_menu/mod_menu.lua:235 +msgid "Save look selection in %s?" +msgstr "óÏÈÒÁÎÉÔØ ×ÎÅÛÎÉÊ ×ÉÄ × %s?" + +#: ../etc/cfg_bindings.lua:82 +msgid "Close current object." +msgstr "úÁËÒÙÔØ ÔÅËÕÝÉÊ ÏÂßÅËÔ." + +# +#: ../etc/cfg_query.lua:64 +msgid "Copy selection." +msgstr "ëÏÐÉÒÏ×ÁÔØ ×ÙÄÅÎÅÎÉÅ" + +#: ../etc/cfg_bindings.lua:164 +msgid "Attach tagged objects to this frame." +msgstr "ðÒÉÃÅÐÉÔØ ÏÂßÅËÔ Ó ÔÅÇÏÍ Ë ÜÔÏÍÕ ÆÒÅÊÍÕ." + +#: ../etc/cfg_ionws.lua:57 +msgid "Floating split" +msgstr "ðÌÁ×ÁÀÝÅÅ ÒÁÚÄÅÌÅÎÉÅ." + +#: ../mod_query/mod_query.lua:768 +msgid "%s menu:" +msgstr "%s ÍÅÎÀ:" + +#: ../ext_statusbar/ext_statusbar.lua:318 +msgid "Failed to create a timer for statusbar." +msgstr "ôÁÊÍÅÒ ÄÌÑ ÓÔÁÔÕÓÂÁÒÁ ÎÅ ÓÏÚÄÁÎ." + +#: ../etc/cfg_ionws.lua:52 +#: ../etc/cfg_ionws.lua:62 +msgid "Vertically at root" +msgstr "÷ÅÒÔÉËÁÌØÎÏ × ËÏÒÎÅ×ÏÍ" + +# +# +#: ../etc/cfg_menu.lua:16 +msgid "Activate current menu entry." +msgstr "áËÔÉ×ÉÒÏ×ÁÔØ ÔÅËÕÝÉÊ ÐÕÎËÔ ÍÅÎÀ." + +#: ../ioncore/ioncore-efbb.lua:12 +msgid "" +"Making the following minimal emergency mappings:\n" +" F2 -> xterm\n" +" F11 -> restart\n" +" F12 -> exit\n" +" Mod1+C -> close\n" +" Mod1+K P/N -> WFrame.switch_next/switch_prev\n" +msgstr "" +"Making the following minimal emergency mappings:\n" +" F2 -> xterm\n" +" F11 -> restart\n" +" F12 -> exit\n" +" Mod1+C -> close\n" +" Mod1+K P/N -> WFrame.switch_next/switch_prev\n" + +# +#: ../etc/cfg_ionws.lua:45 +msgid "Transpose at root" +msgstr "ðÅÒÅÍÅÓÔÉÔØ × ËÏÒÎÅ×ÏÍ" + +#: ../pwm/cfg_pwm_menus.lua:17 +#: ../etc/cfg_menus.lua:12 +msgid "Styles" +msgstr "óÔÉÌÉ" + +#: ../etc/cfg_bindings.lua:106 +msgid "Query for command line to execute." +msgstr "úÁÐÒÏÓ ÎÁ ÉÓÐÏÌÎÅÎÉÅ ËÏÍÁÎÄÎÏÊ ÓÔÒÏËÉ" + +#: ../mod_query/mod_query.lua:323 +msgid "Unable to create workspace: no screen." +msgstr "îÅ ÍÏÇÕ ÓÏÚÄÁÔØ ÒÁÂÏÞÉÊ ÓÔÏÌ: ÎÅÔ ÜËÒÁÎÁ." + +#: ../ext_statusbar/ext_statusbar.lua:27 +msgid "load" +msgstr "ÚÁÇÒÕÚËÁ" + +# +#: ../etc/cfg_ionws.lua:42 +msgid "Flip" +msgstr "ðÅÒÅ×ÅÒÎÕÔØ" + +#: ../etc/cfg_ionws.lua:41 +msgid "Flip&transpose" +msgstr "ðÅÒÅ×ÅÒÎÕÔØ É ÐÅÒÅÍÅÓÔÉÔØ" + +#: ../pwm/cfg_pwm_menus.lua:12 +#: ../etc/cfg_menus.lua:9 +msgid "Lock screen" +msgstr "úÁÂÌÏËÉÒÏ×ÁÔØ ÜËÒÁÎ" + +#: ../mod_query/mod_query.lua:449 +msgid "Edit file:" +msgstr "òÅÄÁËÔÉÒÏ×ÁÔØ ÆÁÊÌ:" + +#: ../etc/cfg_menus.lua:19 +msgid "XTerm" +msgstr "XTerm" + +#: ../build/mkman.lua:198 +msgid "%s %s at %s" +msgstr "%s %s ÎÁ %s" + +# +#: ../etc/cfg_bindings.lua:85 +msgid "Nudge current client window. This might help with some programs' resizing problems." +msgstr "" + +#: ../etc/cfg_ionws.lua:22 +msgid "Destroy current frame." +msgstr "õÄÁÌÉÔØ ÔÅËÕÝÉÊ ÆÒÅÊÍ." + +#: ../ioncore/ioncore-bindings.lua:53 +msgid "Error in command string: " +msgstr "ïÛÉÂËÁ × ÓÔÒÏËÅ ËÏÍÁÎÄÙ:" + +#: ../etc/cfg_ionws.lua:19 +msgid "Split current frame horizontally." +msgstr "òÁÚÄÅÌÉÔØ ÔÅËÕÝÉÊ ÆÒÅÊÍ ÐÏ ÇÏÒÉÚÏÎÔÁÌÉ." + +#: ../etc/cfg_menus.lua:42 +msgid "Attach tagged" +msgstr "ðÒÉÃÅÐÉÔØ ÏËÎÁ Ó ÔÅÇÁÍÉ" + +#: ../etc/cfg_ionws.lua:54 +#: ../etc/cfg_ionws.lua:64 +msgid "Horizontally at root" +msgstr "çÏÒÉÚÏÎÔÁÌØÎÏ × ËÏÒÎÅ×ÏÍ" + +#: ../etc/cfg_menus.lua:40 +msgid "(Un)tag" +msgstr "ðÏÓÔÁ×ÉÔØ/ÕÄÁÌÉÔØ ÔÅÇ" + +#: ../mod_query/mod_query.lua:459 +msgid "View file:" +msgstr "ðÒÏÓÍÏÔÒ ÆÁÊÌÁ:" + +#: ../mod_query/mod_query.lua:421 +msgid "Workspace name:" +msgstr "éÍÑ ÒÁÂÏÞÅÇÏ ÓÔÏÌÁ:" + +#: ../etc/cfg_menus.lua:31 +msgid "Exit" +msgstr "÷ÙÈÏÄ" + +#: ../etc/cfg_menus.lua:27 +msgid "Save" +msgstr "óÏÈÒÁÎÉÔØ" + +# +#: ../pwm/cfg_pwm_menus.lua:15 +msgid "Workspaces" +msgstr "òÁÂÏÞÉÅ ÓÔÏÌÙ" + +#: ../etc/cfg_menus.lua:20 +msgid "Mozilla Firefox" +msgstr "Mozilla Firefox" + +#: ../ioncore/ioncore-bindings.lua:45 +msgid "Error compiling guard: %s" +msgstr "" + +#: ../mod_menu/mod_menu.lua:271 +msgid "Refresh list" +msgstr "ïÂÎÏ×ÉÔØ ÓÐÉÓÏË" + +#: ../etc/cfg_floatws.lua:9 +msgid "Circulate focus and raise the newly focused frame." +msgstr "ðÅÒÅÄÁÔØ ÆÏËÕÓ É ÐÏÄÎÑÔØ ÆÒÅÊÍ Ó ÎÏ×ÙÍ ÆÏËÕÓÏÍ." + +# +#: ../etc/cfg_menus.lua:21 +msgid "Run..." +msgstr "÷ÙÐÏÌÎÉÔØ..." + +#: ../etc/cfg_bindings.lua:118 +msgid "Query for file to view." +msgstr "úÁÐÒÏÓ ÐÒÏÓÍÏÔÒÁ ÆÁÊÌÁ." + +#: ../mod_query/mod_query.lua:511 +msgid "Failed to open ~/.ssh/known_hosts" +msgstr "îÅ ÍÏÇÕ ÏÔËÒÙÔØ ~/.ssh/known_hosts" + +#: ../pwm/cfg_pwm_menus.lua:18 +#: ../etc/cfg_menus.lua:13 +msgid "Session" +msgstr "óÅÓÓÉÑ" + +#: ../pwm/cfg_pwm_menus.lua:27 +msgid "List" +msgstr "óÐÉÓÏË" + diff --git a/pwm/Makefile b/pwm/Makefile new file mode 100644 index 0000000..84c3946 --- /dev/null +++ b/pwm/Makefile @@ -0,0 +1,73 @@ +## +## PWM Makefile +## + +# System-specific configuration is in system.mk +TOPDIR=.. +include $(TOPDIR)/build/system-inc.mk + +# List of modules to possibly preload +include $(TOPDIR)/modulelist.mk + +###################################### + +SOURCES=pwm.c + +ETC = cfg_pwm.lua + +TARGETS = pwm3 + +INCLUDES += $(X11_INCLUDES) +INCLUDES += $(LIBMAINLOOP_INCLUDES) $(LIBTU_INCLUDES) $(LIBEXTL_INCLUDES) +INCLUDES += -I.. + +LIBS += $(X11_LIBS) $(XINERAMA_LIBS) +LIBS += $(WHOLEA) $(LIBMAINLOOP_LIBS) $(LIBEXTL_LIBS) $(LIBTU_LIBS) $(NO_WHOLEA) +LIBS += $(LUA_LIBS) $(DL_LIBS) +LIBS += -lm + +ifeq ($(PRELOAD_MODULES),1) +EXT_OBJS += $(foreach mod, $(PWM_MODULE_LIST), ../$(mod)/$(mod).a) +DEPEND_DEPENDS += preload.c +SOURCES += preload.c +TO_CLEAN += preload.c +LIBS += -lSM -lICE +else +LINKOPTS = $(EXPORT_DYNAMIC) +WHOLEA = -Wl,-whole-archive +NO_WHOLEA = -Wl,-no-whole-archive +endif + +EXT_OBJS += ../ioncore/ioncore.a + +DEFINES += -DETCDIR=\"$(ETCDIR)\" -DSHAREDIR=\"$(SHAREDIR)\" \ + -DEXTRABINDIR=\"$(EXTRABINDIR)\" -DMODULEDIR=\"$(MODULEDIR)\" \ + -DLCDIR=\"$(LCDIR)\" -DLOCALEDIR=\"$(LOCALEDIR)\" + +ifndef PWM_ETCDIR +PWM_ETCDIR = $(ETCDIR) +else +DEFINES += -DPWM_ETCDIR=\"$(PWM_ETCDIR)\" +endif + +CFLAGS += $(XOPEN_SOURCE) $(C99_SOURCE) + +###################################### + +include $(TOPDIR)/build/rules.mk + +###################################### + +pwm3: $(OBJS) $(EXT_OBJS) + $(CC) $(LINKOPTS) $(OBJS) $(WHOLEA) $(EXT_OBJS) $(NO_WHOLEA) $(LDFLAGS) -o $@ + +preload.c: + $(LUA) ../build/mkpreload.lua $(PWM_MODULE_LIST) > preload.c + +_install: + $(INSTALLDIR) $(BINDIR) + $(INSTALL) -m $(BIN_MODE) pwm3 $(BINDIR) + $(INSTALLDIR) $(PWM_ETCDIR) + for i in $(ETC); do \ + $(INSTALL) -m $(DATA_MODE) $$i $(PWM_ETCDIR); \ + done diff --git a/pwm/cfg_pwm.lua b/pwm/cfg_pwm.lua new file mode 100644 index 0000000..1fe7df4 --- /dev/null +++ b/pwm/cfg_pwm.lua @@ -0,0 +1,97 @@ +-- +-- PWM main configuration file +-- +-- This file only includes some settings that are rather frequently altered, +-- and the differences between PWM and Ion. The rest of the settings are in +-- cfg_ioncore.lua and individual modules' configuration files +-- (cfg_modulename.lua). +-- + +-- Set default modifiers. Alt should usually be mapped to Mod1 on +-- XFree86-based systems. The flying window keys are probably Mod3 +-- or Mod4; see the output of 'xmodmap'. +--META="Mod1+" +--ALTMETA="" + +-- Some basic settings +ioncore.set{ + -- Maximum delay between clicks in milliseconds to be considered a + -- double click. + --dblclick_delay=250, + + -- For keyboard resize, time (in milliseconds) to wait after latest + -- key press before automatically leaving resize mode (and doing + -- the resize in case of non-opaque move). + --kbresize_delay=1500, + + -- Opaque resize? + --opaque_resize=false, + + -- Movement commands warp the pointer to frames instead of just + -- changing focus. Enabled by default. + --warp=true, +} + +-- cfg_ioncore contains configuration of the Ion 'core' +dopath("cfg_ioncore") + +-- Load some modules. +--dopath("cfg_modules") +--dopath("mod_query") +dopath("mod_menu") +--dopath("mod_tiling") +--dopath("mod_panews") +--dopath("mod_statusbar") +dopath("mod_dock") +--dopath("mod_sp") + + +-- +-- PWM customisations to bindings and menus +-- + + +-- Unbind anything using mod_query and rebinding to mod_menu where +-- applicable. + +defbindings("WScreen", { + kpress(ALTMETA.."F12", "mod_menu.menu(_, _sub, 'mainmenu', {big=true})"), +}) + +defbindings("WMPlex.toplevel", { + kpress(ALTMETA.."F1", nil), + kpress(META.. "F1", "ioncore.exec_on(_, ':man pwm3')"), + kpress(ALTMETA.."F3", nil), + kpress(META.. "F3", nil), + kpress(ALTMETA.."F4", nil), + kpress(ALTMETA.."F5", nil), + kpress(ALTMETA.."F6", nil), + kpress(ALTMETA.."F9", nil), + kpress(META.."G", nil), + kpress(META.."A", nil), +}) + +defbindings("WFrame", { + kpress(META.."M", "mod_menu.menu(_, _sub, 'ctxmenu')"), +}) + +-- Make a new main menu with additional workspace menu. + +defmenu("mainmenu", { + submenu("Programs", "appmenu"), + menuentry("Lock screen", "ioncore.exec_on(_, 'xlock')"), + menuentry("Help", "ioncore.exec_on(_, ':man pwm3')"), + submenu("Workspaces", "wsmenu"), + menuentry("New", "ioncore.create_ws(_)"), + submenu("Styles", "stylemenu"), + submenu("Session", "sessionmenu"), +}) + +-- Workspace menu +defmenu("wsmenu", { + menuentry("New", "ioncore.create_ws(_)"), + menuentry("Close", "WRegion.rqclose(_sub)", + "_sub:WGroupWS"), + submenu("List", "workspacelist"), +}) + diff --git a/pwm/pwm.c b/pwm/pwm.c new file mode 100644 index 0000000..773aace --- /dev/null +++ b/pwm/pwm.c @@ -0,0 +1,241 @@ +/* + * ion/pwm/pwm.c + * + * Copyright (c) Tuomo Valkonen 1999-2006. + * + * Ion is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include "../version.h" + + +/* Options. Getopt is not used because getopt_long is quite gnu-specific + * and they don't know of '-display foo' -style args anyway. + * Instead, I've reinvented the wheel in libtu :(. + */ +static OptParserOpt pwm_opts[]={ + {OPT_ID('d'), "display", OPT_ARG, "host:dpy.scr", + DUMMY_TR("X display to use")}, + + {'c', "conffile", OPT_ARG, "config_file", + DUMMY_TR("Configuration file")}, + + {'s', "searchdir", OPT_ARG, "dir", + DUMMY_TR("Add directory to search path")}, + + {OPT_ID('o'), "oneroot", 0, NULL, + DUMMY_TR("Manage default root window/non-Xinerama screen only")}, + +#if defined(CF_XINERAMA) || defined(CF_SUN_XINERAMA) + {OPT_ID('x'), "xinerama", OPT_ARG, "1|0", + DUMMY_TR("Use Xinerama screen information (default: 0/no)")}, +#else + {OPT_ID('x'), "xinerama", OPT_ARG, "?", + DUMMY_TR("Ignored: not compiled with Xinerama support")}, +#endif + + {OPT_ID('s'), "session", OPT_ARG, "session_name", + DUMMY_TR("Name of session (affects savefiles)")}, + + {OPT_ID('S'), "smclientid", OPT_ARG, "client_id", + DUMMY_TR("Session manager client ID")}, + + {OPT_ID('N'), "noerrorlog", 0, NULL, + DUMMY_TR("Do not create startup error log and display it " + "with xmessage.")}, + + {'h', "help", 0, NULL, + DUMMY_TR("Show this help")}, + + {'V', "version", 0, NULL, + DUMMY_TR("Show program version")}, + + {OPT_ID('a'), "about", 0, NULL, + DUMMY_TR("Show about text")}, + + END_OPTPARSEROPTS +}; + + +static void help() +{ + int i; + printf(TR("Usage: %s [options]\n\n"), prog_execname()); + for(i=0; pwm_opts[i].descr!=NULL; i++) + pwm_opts[i].descr=TR(pwm_opts[i].descr); + optparser_printhelp(OPTP_MIDLONG, pwm_opts); + printf("\n"); +} + + +int main(int argc, char*argv[]) +{ + const char *cfgfile="cfg_pwm"; + const char *display=NULL; + char *cmd=NULL; + int stflags=IONCORE_STARTUP_NOXINERAMA; + int opt; + ErrorLog el; + FILE *ef=NULL; + char *efnam=NULL; + bool may_continue=FALSE; + bool noerrorlog=FALSE; + + libtu_init(argv[0]); + + if(!ioncore_init("pwm3", argc, argv, LOCALEDIR)) + return EXIT_FAILURE; + + extl_add_searchdir(EXTRABINDIR); /* ion-completefile */ + extl_add_searchdir(MODULEDIR); + extl_add_searchdir(ETCDIR); +#ifdef PWM_ETCDIR + extl_add_searchdir(PWM_ETCDIR); +#endif + extl_add_searchdir(SHAREDIR); + extl_add_searchdir(LCDIR); + extl_set_userdirs("pwm3"); + + optparser_init(argc, argv, OPTP_MIDLONG, pwm_opts); + + while((opt=optparser_get_opt())){ + switch(opt){ + case OPT_ID('d'): + display=optparser_get_arg(); + break; + case 'c': + cfgfile=optparser_get_arg(); + break; + case 's': + extl_add_searchdir(optparser_get_arg()); + break; + case OPT_ID('S'): + ioncore_g.sm_client_id=optparser_get_arg(); + break; + case OPT_ID('o'): + stflags|=IONCORE_STARTUP_ONEROOT; + break; + case OPT_ID('x'): + { + const char *p=optparser_get_arg(); + if(strcmp(p, "1")==0) + stflags&=~IONCORE_STARTUP_NOXINERAMA; + else if(strcmp(p, "0")==0) + stflags|=IONCORE_STARTUP_NOXINERAMA; + else + warn(TR("Invalid parameter to -xinerama.")); + } + break; + case OPT_ID('s'): + extl_set_sessiondir(optparser_get_arg()); + break; + case OPT_ID('N'): + noerrorlog=TRUE; + break; + case 'h': + help(); + return EXIT_SUCCESS; + case 'V': + printf("%s\n", ION_VERSION); + return EXIT_SUCCESS; + case OPT_ID('a'): + printf("%s\n", ioncore_aboutmsg()); + return EXIT_SUCCESS; + default: + warn(TR("Invalid command line.")); + help(); + return EXIT_FAILURE; + } + } + + if(!noerrorlog){ + /* We may have to pass the file to xmessage so just using tmpfile() + * isn't sufficient. + */ + libtu_asprintf(&efnam, "%s/pwm-%d-startup-errorlog", P_tmpdir, + getpid()); + if(efnam==NULL){ + warn_err(); + }else{ + ef=fopen(efnam, "wt"); + if(ef==NULL){ + warn_err_obj(efnam); + free(efnam); + efnam=NULL; + }else{ + cloexec_braindamage_fix(fileno(ef)); + fprintf(ef, TR("PWM startup error log:\n")); + errorlog_begin_file(&el, ef); + } + } + } + + if(ioncore_startup(display, cfgfile, stflags)) + may_continue=TRUE; + +fail: + if(!may_continue) + warn(TR("Refusing to start due to encountered errors.")); + + if(ef!=NULL){ + pid_t pid=-1; + if(errorlog_end(&el) && ioncore_g.dpy!=NULL){ + fclose(ef); + pid=fork(); + if(pid==0){ + ioncore_setup_environ(DefaultScreen(ioncore_g.dpy)); + if(!may_continue) + XCloseDisplay(ioncore_g.dpy); + else + close(ioncore_g.conn); + libtu_asprintf(&cmd, CF_XMESSAGE " %s", efnam); + if(cmd==NULL){ + warn_err(); + }else if(system(cmd)==-1){ + warn_err_obj(cmd); + } + unlink(efnam); + exit(EXIT_SUCCESS); + } + if(!may_continue && pid>0) + waitpid(pid, NULL, 0); + }else{ + fclose(ef); + } + if(pid<0) + unlink(efnam); + free(efnam); + } + + if(!may_continue) + return EXIT_FAILURE; + + ioncore_mainloop(); + + /* The code should never return here */ + return EXIT_SUCCESS; +} diff --git a/system.mk b/system.mk new file mode 100644 index 0000000..06ce3b1 --- /dev/null +++ b/system.mk @@ -0,0 +1,195 @@ +## +## System settings +## + + +## +## Installation paths +## + +PREFIX=/usr/local + +# Unless you are creating a package conforming to some OS's standards, you +# probably do not want to modify the following directories: + +# Main binaries +BINDIR=$(PREFIX)/bin +# Configuration .lua files +ETCDIR=$(PREFIX)/etc/ion3 +# Some .lua files and ion-* shell scripts +SHAREDIR=$(PREFIX)/share/ion3 +# Manual pages +MANDIR=$(PREFIX)/share/man +# Some documents +DOCDIR=$(PREFIX)/share/doc/ion3 +# Nothing at the moment +INCDIR=$(PREFIX)/include/ion3 +# Nothing at the moment +LIBDIR=$(PREFIX)/lib +# Modules +MODULEDIR=$(LIBDIR)/ion3/mod +# Compiled Lua source code +LCDIR=$(LIBDIR)/ion3/lc +# ion-completefile (does not belong in SHAREDIR being a binary file) +EXTRABINDIR=$(LIBDIR)/ion3/bin +# For ion-completeman system-wide cache +VARDIR=/var/cache/ion3 +# Message catalogs +LOCALEDIR=$(PREFIX)/share/locale + + +## +## Modules +## + +# Set PRELOAD_MODULES=1 if your system does not support dynamically loaded +# modules through 'libdl' or has non-standard naming conventions. +#PRELOAD_MODULES=1 + +# Flags to link with libdl. +DL_LIBS=-ldl + + +## +## Lua +## + +# If you have installed Lua 5.1 from the official tarball without changing +# paths, this should do it. +LUA_DIR=/usr/local +LUA_LIBS = -L$(LUA_DIR)/lib -llua +LUA_INCLUDES = -I$(LUA_DIR)/include +LUA=$(LUA_DIR)/bin/lua +LUAC=$(LUA_DIR)/bin/luac + +# If you are using the Debian packages, the following settings should be +# what you want. +#LUA_LIBS=`pkg-config --libs lua5.1` +#LUA_INCLUDES=`pkg-config --cflags lua5.1` +#LUA=`which lua5.1` +#LUAC=`which luac5.1` + + +## +## X libraries, includes and options +## + +X11_PREFIX=/usr/X11R6 +# SunOS/Solaris +#X11_PREFIX=/usr/openwin + +X11_LIBS=-L$(X11_PREFIX)/lib -lX11 -lXext +X11_INCLUDES=-I$(X11_PREFIX)/include + +# Change commenting to disable Xinerama support +XINERAMA_LIBS=-lXinerama +DEFINES += -DCF_XINERAMA +# For Solaris +#XINERAMA_LIBS= +#DEFINES += -DCF_SUN_XINERAMA + +# XFree86 libraries up to 4.3.0 have a bug that will cause Ion to segfault +# if Opera is used when i18n support is enabled. The following setting +# should work around that situation. +DEFINES += -DCF_XFREE86_TEXTPROP_BUG_WORKAROUND + +# Use the Xutf8 routines (XFree86 extension) instead of Xmb routines in +# an UTF8 locale. +#DEFINES += -DCF_DE_USE_XUTF8 + +# Remap F11 key to SunF36 and F12 to SunF37? You may want to set this +# on SunOS. +#DEFINES += -DCF_SUN_F1X_REMAP + + +## +## libc +## + +# You may uncomment this if you know your system has +# asprintf and vasprintf in the c library. (gnu libc has.) +# If HAS_SYSTEM_ASPRINTF is not defined, an implementation +# in sprintf_2.2/ is used. +#HAS_SYSTEM_ASPRINTF=1 + + +# If you're on an archaic system (such as relatively recent *BSD releases) +# without even dummy multibyte/widechar and localisation support, you may +# have to uncomment the following line: +#DEFINES += -DCF_NO_LOCALE + +# On some other systems you may something like this: +#EXTRA_LIBS += -lintl +#EXTRA_INCLUDES += + + +## +## C compiler +## + +CC=gcc + +# Same as '-Wall -pedantic' without '-Wunused' as callbacks often +# have unused variables. +WARN= -W -Wimplicit -Wreturn-type -Wswitch -Wcomment \ + -Wtrigraphs -Wformat -Wchar-subscripts \ + -Wparentheses -pedantic -Wuninitialized + +CFLAGS=-g -Os $(WARN) $(DEFINES) $(EXTRA_INCLUDES) $(INCLUDES) +LDFLAGS=-g -Os $(EXTRA_LIBS) $(LIBS) +EXPORT_DYNAMIC=-Xlinker --export-dynamic + +# The following options are mainly for development use and can be used +# to check that the code seems to conform to some standards. Depending +# on the version and vendor of you libc, the options may or may not have +# expected results. If you define one of C99_SOURCE or XOPEN_SOURCE, you +# may also have to define the other. + +#C89_SOURCE=-ansi + +#POSIX_SOURCE=-D_POSIX_SOURCE + +# Most systems +#XOPEN_SOURCE=-D_XOPEN_SOURCE -D_XOPEN_SOURCE_EXTENDED +# SunOS, (Irix) +#XOPEN_SOURCE=-D__EXTENSIONS__ + +#C99_SOURCE=-std=c99 -DCF_HAS_VA_COPY + +# The -DCF_HAS_VA_COPY option should allow for some optimisations, and +# in some cases simply defining +#C99_SOURCE=-DCF_HAS_VA_COPY +# might allow for those optimisations to be taken without any special +# libc or compiler options. + + +## +## make depend +## + +DEPEND_FILE=.depend +DO_MAKE_DEPEND=$(CC) -MM $(DEFINES) $(EXTRA_INCLUDES) $(INCLUDES) +MAKE_DEPEND=$(DO_MAKE_DEPEND) $(SOURCES) > $(DEPEND_FILE) + +## +## AR +## + +AR=ar +ARFLAGS=cr +RANLIB=ranlib + + +## +## Install & strip +## + +INSTALL=sh $(TOPDIR)/install-sh -c +INSTALLDIR=mkdir -p + +BIN_MODE=755 +DATA_MODE=644 + +STRIP=strip + +RM=rm diff --git a/utils/Makefile b/utils/Makefile new file mode 100644 index 0000000..2c58b50 --- /dev/null +++ b/utils/Makefile @@ -0,0 +1,33 @@ +## +## Ion utils/ Makefile +## + +# System-specific configuration is in system.mk +TOPDIR=.. +include $(TOPDIR)/build/system-inc.mk + +###################################### + +SUBDIRS=ion-completefile +INSTALL_SUBDIRS=$(SUBDIRS) + +SHELLSCRIPTS = ion-runinxterm ion-completeman + +TARGETS = ion-completeman + +###################################### + +include $(TOPDIR)/build/rules.mk + +###################################### + +_install: + $(INSTALLDIR) $(SHAREDIR) + for i in $(SHELLSCRIPTS); do \ + $(INSTALL) -m $(BIN_MODE) $$i $(SHAREDIR); \ + done + +%: %.in + sed 's#@SHAREDIR@#$(SHAREDIR)#g' $< | \ + sed 's#@VARDIR@#$(VARDIR)#g' > $@ + chmod $(BIN_MODE) $@ diff --git a/utils/ion-completefile/Makefile b/utils/ion-completefile/Makefile new file mode 100644 index 0000000..e974d11 --- /dev/null +++ b/utils/ion-completefile/Makefile @@ -0,0 +1,30 @@ +## +## Ion-completefile Makefile +## + +# System-specific configuration is in system.mk +TOPDIR=../.. +include $(TOPDIR)/build/system-inc.mk + +###################################### + +LIBS += $(LIBTU_LIBS) +INCLUDES += $(LIBTU_INCLUDES) +CFLAGS += $(XOPEN_SOURCE) + +SOURCES=ion-completefile.c + +TARGETS=ion-completefile + +###################################### + +include $(TOPDIR)/build/rules.mk + +###################################### + +ion-completefile: $(SOURCES) + $(CC) $< $(CFLAGS) $(LDFLAGS) -o $@ + +_install: + $(INSTALLDIR) $(EXTRABINDIR) + $(INSTALL) -s -m $(BIN_MODE) ion-completefile $(EXTRABINDIR) diff --git a/utils/ion-completefile/ion-completefile.c b/utils/ion-completefile/ion-completefile.c new file mode 100644 index 0000000..6e68e0a --- /dev/null +++ b/utils/ion-completefile/ion-completefile.c @@ -0,0 +1,711 @@ +/* + * ion/share/ion-completefile/ion-completefile.c + */ + +/****************************************************************************/ +/* */ +/* Copyright 1992 Simmule Turner and Rich Salz. All rights reserved. */ +/* */ +/* This software is not subject to any license of the American Telephone */ +/* and Telegraph Company or of the Regents of the University of California. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose on */ +/* any computer system, and to alter it and redistribute it freely, subject */ +/* to the following restrictions: */ +/* 1. The authors are not responsible for the consequences of use of this */ +/* software, no matter how awful, even if they arise from flaws in it. */ +/* 2. The origin of this software must not be misrepresented, either by */ +/* explicit claim or by omission. Since few users ever read sources, */ +/* credits must appear in the documentation. */ +/* 3. Altered versions must be plainly marked as such, and must not be */ +/* misrepresented as being the original software. Since few users */ +/* ever read sources, credits must appear in the documentation. */ +/* 4. This notice may not be removed or altered. */ +/* */ +/****************************************************************************/ +/* */ +/* This is a line-editing library, it can be linked into almost any */ +/* program to provide command-line editing and recall. */ +/* */ +/* Posted to comp.sources.misc Sun, 2 Aug 1992 03:05:27 GMT */ +/* by rsalz@osf.org (Rich $alz) */ +/* */ +/****************************************************************************/ +/* */ +/* The version contained here has some modifications by awb@cstr.ed.ac.uk */ +/* (Alan W Black) in order to integrate it with the Edinburgh Speech Tools */ +/* library and Scheme-in-one-defun in particular. All modifications to */ +/* to this work are continued with the same copyright above. That is */ +/* This version editline does not have the the "no commercial use" */ +/* restriction that some of the rest of the EST library may have */ +/* awb Dec 30 1998 */ +/* */ +/****************************************************************************/ +/* $Revision: 1.2 $ + ** + ** History and file completion functions for editline library. + */ + + +/* + * Adapted for use with Ion and tilde-expansion added by + * Tuomo Valkonen , 2000-08-23. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define STRDUP(X) scopy(X) +#define DISPOSE(X) free(X) +#define NEW(T, X) ALLOC_N(T, X) +#define STATIC static +#define EL_CONST const +#define SIZE_T int +#define MEM_INC 64 +#define COPYFROMTO(new, p, len) \ + (void)memcpy((char *)(new), (char *)(p), (int)(len)) + +#ifndef NGROUPS +/* Hopefully one of these is defined... */ +#define NGROUPS NGROUPS_MAX +#endif + +typedef struct dirent DIRENTRY; + +static void el_add_slash(char *path,char *p) +{ + struct stat sb; + + if (stat(path, &sb) >= 0) + (void)strcat(p, S_ISDIR(sb.st_mode) ? "/" : " "); +} + +static int el_is_directory(char *path) +{ + struct stat sb; + + if ((stat(path, &sb) >= 0) && S_ISDIR(sb.st_mode)) + return 1; + else + return 0; +} + +static int el_is_executable(char *path) +{ + unsigned int t_uid = geteuid(); + gid_t t_gids[NGROUPS]; + int groupcount; + struct stat sb; + int i; + + groupcount = getgroups(NGROUPS, t_gids); + + if((stat(path, &sb) >= 0) && S_ISREG(sb.st_mode)) { + /* Normal file, see if we can execute it. */ + + if (sb.st_mode & S_IXOTH) { /* All can execute */ + return (1); + } + if (sb.st_uid == t_uid && (sb.st_mode & S_IXUSR)) { + return (1); + } + if (sb.st_mode & S_IXGRP) { + for (i = 0; i < groupcount; i++) { + if (sb.st_gid == t_gids[i]) { + return (1); + } + } + } + } + return (0); +} + + +/* + ** strcmp-like sorting predicate for qsort. + */ +/*STATIC int compare(EL_CONST void *p1,EL_CONST void *p2) + { + EL_CONST char **v1; + EL_CONST char **v2; + * + v1 = (EL_CONST char **)p1; + v2 = (EL_CONST char **)p2; + return strcmp(*v1, *v2); + }*/ + +/* + ** Fill in *avp with an array of names that match file, up to its length. + ** Ignore . and .. . + */ +static int FindMatches(char *dir,char *file,char ***avp) +{ + char **av; + char **new; + char *p; + DIR *dp; + DIRENTRY *ep; + SIZE_T ac; + SIZE_T len; + + if(*dir=='\0') + dir="."; + + if ((dp = opendir(dir)) == NULL) + return 0; + + av = NULL; + ac = 0; + len = strlen(file); + while ((ep = readdir(dp)) != NULL) { + p = ep->d_name; + if (p[0] == '.' && (p[1] == '\0' || (p[1] == '.' && p[2] == '\0'))) + continue; + if (len && strncmp(p, file, len) != 0) + continue; + + if ((ac % MEM_INC) == 0) { + if ((new = NEW(char*, ac + MEM_INC)) == NULL) + break; + if (ac) { + COPYFROMTO(new, av, ac * sizeof (char **)); + DISPOSE(av); + } + *avp = av = new; + } + + if ((av[ac] = STRDUP(p)) == NULL) { + if (ac == 0) + DISPOSE(av); + break; + } + ac++; + } + + /* Clean up and return. */ + (void)closedir(dp); + /* if (ac) + qsort(av, ac, sizeof (char **), compare);*/ + return ac; +} + + +/* + ** Slight modification on FindMatches, to search through current PATH + ** + */ +static int FindFullPathMatches(char *file,char ***avp) +{ + char **av; + char **new; + char *p; + char *path = getenv("PATH"); + char t_tmp[1024]; + char *t_path; + char *t_t_path; + DIR *dp; + DIRENTRY*ep; + SIZE_T ac; + SIZE_T len; + + t_path = strdup(path); + t_t_path = t_path; + + av = NULL; + ac = 0; + + t_t_path = strtok(t_path, ":\n\0"); + while (t_t_path) { + if ((dp = opendir(t_t_path)) == NULL) { + t_t_path = strtok(NULL, ":\n\0"); + continue; + } + + len = strlen(file); + while ((ep = readdir(dp)) != NULL) { + p = ep->d_name; + if (p[0] == '.' && (p[1] == '\0' || (p[0] == '.' && + p[1] == '.' && + p[2] == '\0'))) { + continue; + } + if (len && strncmp(p, file, len) != 0) { + continue; + } + + snprintf(t_tmp, 1024, "%s/%s", t_t_path, p); + if(!el_is_executable(t_tmp)) { + continue; + } + + if ((ac % MEM_INC) == 0) { + if ((new = NEW(char*, ac + MEM_INC)) == NULL) { + break; + } + if (ac) { + COPYFROMTO(new, av, ac * sizeof (char **)); + DISPOSE(av); + } + *avp = av = new; + } + if ((av[ac] = STRDUP(p)) == NULL) { + if (ac == 0) + DISPOSE(av); + break; + } + ac++; + } + (void)closedir(dp); + t_t_path = strtok(NULL, ":\n\0"); + + + } /* t_path-while */ + + /* Clean up and return. */ + + /*if (ac) + qsort(av, ac, sizeof (char **), compare); */ + free(t_path); + + return ac; +} + + +/* + ** Split a pathname into allocated directory and trailing filename parts. + */ +STATIC int SplitPath(const char *path,char **dirpart,char **filepart) +{ + static char DOT[] = "./"; + char *dpart; + char *fpart; + + if ((fpart = strrchr(path, '/')) == NULL) { + /* No slashes in path */ + if ((dpart = STRDUP(DOT)) == NULL) + return -1; + if ((fpart = STRDUP(path)) == NULL) { + DISPOSE(dpart); + return -1; + } + }else{ + if ((dpart = STRDUP(path)) == NULL) + return -1; + /* Include the slash -- Tuomo */ + dpart[fpart - path + 1] = '\0'; + if ((fpart = STRDUP(++fpart)) == NULL) { + DISPOSE(dpart); + return -1; + } + /* Root no longer a special case due above -- Tuomo + if (dpart[0] == '\0') + { + dpart[0] = '/'; + dpart[1] = '\0'; + } + */ + } + *dirpart = dpart; + *filepart = fpart; + return 0; +} + +/* + ** Split a pathname into allocated directory and trailing filename parts. + */ +STATIC int SplitRelativePath(const char *path,char **dirpart,char **filepart) +{ + static char DOT[] = "./"; + static char EOL[] = "\0"; + char *dpart; + char *fpart; + + if ((fpart = strrchr(path, '/')) == NULL) { + /* No slashes in path */ + if ((dpart = STRDUP(EOL)) == NULL) + return -1; + if ((fpart = STRDUP(path)) == NULL) { + DISPOSE(dpart); + return -1; + } + } + else { + if ((dpart = STRDUP(path)) == NULL) + return -1; + /* Include the slash -- Tuomo */ + dpart[fpart - path + 1] = '\0'; + if ((fpart = STRDUP(++fpart)) == NULL) { + DISPOSE(dpart); + return -1; + } + /* Root no longer a special case due above -- Tuomo + if (dpart[0] == '\0') + { + dpart[0] = '/'; + dpart[1] = '\0'; + } + */ + } + *dirpart = dpart; + *filepart = fpart; + return 0; +} + +/* + ** Attempt to complete the pathname, returning an allocated copy. + ** Fill in *unique if we completed it, or set it to 0 if ambiguous. + */ +#if 0 +static char *el_complete(char *pathname,int *unique) +{ + char **av; + char *dir; + char *file; + char *new; + char *p; + SIZE_T ac; + SIZE_T end; + SIZE_T i; + SIZE_T j; + SIZE_T len; + + if (SplitPath(pathname, &dir, &file) < 0) + return NULL; + + if ((ac = FindMatches(dir, file, &av)) == 0) { + DISPOSE(dir); + DISPOSE(file); + return NULL; + } + + p = NULL; + len = strlen(file); + if (ac == 1) { + /* Exactly one match -- finish it off. */ + *unique = 1; + j = strlen(av[0]) - len + 2; + if ((p = NEW(char, j + 1)) != NULL) { + COPYFROMTO(p, av[0] + len, j); + if ((new = NEW(char, strlen(dir) + strlen(av[0]) + 2)) != NULL) { + (void)strcpy(new, dir); + (void)strcat(new, "/"); + (void)strcat(new, av[0]); + el_add_slash(new, p); + DISPOSE(new); + } + } + } + else { + *unique = 0; + if (len) { + /* Find largest matching substring. */ + for (i = len, end = strlen(av[0]); i < end; i++) + for (j = 1; j < ac; j++) + if (av[0][i] != av[j][i]) + goto breakout; + breakout: + if (i > len) { + j = i - len + 1; + if ((p = NEW(char, j)) != NULL) { + COPYFROMTO(p, av[0] + len, j); + p[j - 1] = '\0'; + } + } + } + } + + /* Clean up and return. */ + DISPOSE(dir); + DISPOSE(file); + for (i = 0; i < ac; i++) + DISPOSE(av[i]); + DISPOSE(av); + return p; +} +#endif + +static int complete_homedir(const char *username, char ***cp_ret, char **beg) +{ + struct passwd *pw; + char *name; + char **cp; + int n=0, l=strlen(username); + + *cp_ret=NULL; + + for(pw=getpwent(); pw!=NULL; pw=getpwent()){ + name=pw->pw_name; + + if(l && strncmp(name, username, l)) + continue; + + name=scat3("~", name, "/"); + + if(name==NULL){ + warn_err(); + continue; + } + + cp=REALLOC_N(*cp_ret, char*, n, n+1); + + if(cp==NULL){ + warn_err(); + free(name); + if(*cp_ret!=NULL) + free(*cp_ret); + }else{ + cp[n]=name; + n++; + *cp_ret=cp; + } + } + + endpwent(); + + if(n==1){ + name=**cp_ret; + name[strlen(name)-1]='\0'; + pw=getpwnam(name+1); + if(pw!=NULL && pw->pw_dir!=NULL){ + name=scat(pw->pw_dir, "/"); + if(name!=NULL){ + free(**cp_ret); + **cp_ret=name; + } + } + } + + return n; +} + +/* + * ret: 0 not a home directory, + * 1 home directory (repath set) + * 2 someone's home directory + */ +static int tilde_complete(char *path, char **retpath) +{ + char *home; + char *p; + struct passwd *pw; + + if(*path!='~') + return 0; + + if(*(path+1)!='/' && *(path+1)!='\0'){ + p=strchr(path, '/'); + + if(p==NULL) + return 2; + + *p='\0'; + pw=getpwnam(path+1); + *p='/'; + + if(pw==NULL) + return 0; + + home=pw->pw_dir; + }else{ + p=path+1; + home=getenv("HOME"); + } + + if(home!=NULL){ + if(*p=='\0') + *retpath=scat3(home, p, "/"); + else + *retpath=scat(home, p); + } + + return (*retpath!=NULL); +} + + +/* + ** Return all possible completions. + */ +int do_complete_file(char *pathname, char ***avp, char **beg, + void *unused) +{ + char *dir; + char *file, *path=NULL, *tt; + int ac=0, i; + + switch(tilde_complete(pathname, &path)){ + case 0: + i=SplitPath(pathname, &dir, &file); + break; + case 2: + return complete_homedir(pathname+1, avp, beg); + default: + i=SplitPath(path, &dir, &file); + } + + if(i<0) + return 0; + + ac=FindMatches(dir, file, avp); + + DISPOSE(file); + + if(ac==0 && path!=NULL){ + *avp=ALLOC(char*); + if(*avp==NULL) + return 0; + **avp=path; + return 1; + }else if(path!=NULL){ + free(path); + } + + /* Identify directories with trailing / */ + for(i=0; i&1 "Usage: $0 (-complete what|-mkusercache|-mksyscache)" + exit 1 +fi + + +filterpath() { + sed 's:^.*/\([^/]*\.[0-9].*\)$:\1:p; d' +} + +filtersect() { + sed 's:^\(.*\)\.[0-9].*$:\1:p; d' +} + +grepper() { + if test "$tocompl" = "" -a "$section" = ""; then + cat + else + if test "$section" = ""; then + section="[0-9]" + fi + grep "^$tocompl.*\.$section" + fi +} + +scan() { + if test "x$ION_MANPATH" != "x"; then + mpath="$ION_MANPATH" + elif test "x$MANPATH" != "x"; then + mpath="$MANPATH" + else + mpprog=`which manpath` + if test "x$mpprog" = x; then + echo "Please set MANPATH, ION_MANPATH or put 'manpath' on PATH" > /dev/stderr + exit 1 + fi + mpath=`$mpprog` + fi + + for p in `echo "$mpath"|tr : '\n'`; do + find "$p" -type f -o -type l | filterpath + done +} + + +cachefile="" + +if test "x$HOME" != x; then + usercache="$HOME/.ion3/mancache" +fi + +syscache="@VARDIR@/mancache" + +case "$action" in + complete) + if test "x$usercache" != x -a -f "$usercache"; then + cachefile="$usercache" + fi + + if test -f "$syscache"; then + cachefile="$syscache" + fi + + # Empty "common part" of completions. + echo "$beg" + + if test "x$cachefile" != x; then + grepper < "$cachefile" | filtersect + else + scan | grepper | filtersect + fi + ;; + mkusercache) + if test "x$usercache" != x; then + scan > "$usercache" + else + echo >&2 "\$HOME not set." + fi + ;; + mksyscache) + mkdir -p "@VARDIR@" + scan > "$syscache" + ;; +esac diff --git a/utils/ion-runinxterm b/utils/ion-runinxterm new file mode 100644 index 0000000..fc16dac --- /dev/null +++ b/utils/ion-runinxterm @@ -0,0 +1,43 @@ +#!/bin/sh + +test "$XTERMCMD" || XTERMCMD="xterm" + +title="" +wait="" + +while test $# -ge 0; do + if test "$1" = "--"; then + shift + break + elif test "$1" = "-phase2"; then + shift + "$@" && test "$wait" = "" || { + echo "Press enter..." + read nothing + } + exit + elif test "$1" = "-T"; then + if test $# -lt 2; then + echo error + exit 0 + fi + title="$2" + shift 2 + elif test "$1" = "-w"; then + wait="$1" + shift + else + break + fi +done + +if test $# -lt 1; then + echo error + exit 0 +fi + +if test "$title" = ""; then + title="$*" +fi + +exec $XTERMCMD -T "$title" -e $0 $wait -phase2 "$@" diff --git a/version.h b/version.h new file mode 100644 index 0000000..e5a60c9 --- /dev/null +++ b/version.h @@ -0,0 +1,2 @@ +#define ION_VERSION "3ds-20061223" +#define ION_API_VERSION "3-"ION_VERSION -- 2.39.5