pax_global_header00006660000000000000000000000064117453066120014517gustar00rootroot0000000000000052 comment=26db7deba3d04d98023ccf310e602ff628404b37 notion-3+2012042300/000077500000000000000000000000001174530661200135625ustar00rootroot00000000000000notion-3+2012042300/ChangeLog000066400000000000000000000001031174530661200153260ustar00rootroot00000000000000(The ChangeLog will be generated by release scripts from svn logs) notion-3+2012042300/LICENSE000066400000000000000000000737371174530661200146100ustar00rootroot00000000000000 Copyright (c) Tuomo Valkonen 1999-2009. Unless otherwise indicated in components taken from elsewhere, this software is licensed under the GNU Lesser General Public License, version 2.1 ("LGPL", reproduced below), extended and modified with the following terms: If the name Ion(tm) or other names that can be associated with the Ion project are used to distribute this software, then: - A version that does not significantly differ from one of the copyright holder's releases, must be provided by default. - Versions not based on the copyright holder's latest release (on the corresponding "branch", such as Ion3(tm)), must within 28 days of this release, be prominently marked as (potentially) obsolete and unsupported. - Significantly altered versions may be provided only if the user explicitly requests for those modifications to be applied, and is prominently notified that the software is no longer considered the standard version, and is not supported by the copyright holder. The version string displayed by the program must describe these modifications and the "support void" status. Versions for which the above conditions are not satisfied, must be renamed so that they can not be associated with the Ion project, their executables must be given names that do not conflict with the copyright holder's version, and neither the copyright holder nor the Ion project may be referred to for support. In the text of sections 0-2, 4-12, and 14-16 of the LGPL, "this License" is to be understood to refer to the LGPL extended with these terms and, where applicable, possible similar terms related to the names of other works forming a whole. Sections 3 and 13 of the LGPL are void. Where contradictory, these additional terms take precedence over the LGPL. End of terms. Explanations Trademarks: With the terms above primarily appealing to copyright law, should any of the indicated trademarks be found invalid, does not excuse you from the conditions imposed by those terms. The use of these names in contexts other than redistribution of this software and modifications, is outside the scope of the terms above, and governed by applicable trademark or other laws. With regard to modules and other extensions to Ion(tm), the permission is hereby granted to use "Ion" as part of the name, provided that it occurs in a form suggesting that the work is supported by neither the copyright holder nor the Ion project: "Foo for Ion" instead of "Ion Foo", etc. Significant change: Bug fixes are insignificant as additions. Basic changes that are needed to install or run the software on a target platform, are insignificant. Additionally, basic/small configuration changes to better integrate the software with the target platform, without obstructing the standard behaviour, are insignificant. Everything else is significant, unless expressly declared otherwise by the copyright holder. Distributions: For example, suppose an aggregate distribution of software provides an `installpkg` command for installing packages. Then the action `installpkg ion3` (resp. `installpkg ion`) should provide the latest release of Ion3 (resp. the latest stable release) 28 days from release date at the latest, or prominently notify the user that the provided version is (likely to be) obsolete and unsupported. The latest release being provided by default, or prominently appearing in a listing, constitutes prominent marking of earlier releases as obsolete. Specific versions (including modified versions) may be provided if the user explicitly requests for those, within the constraints set above. The intent of these terms is to curb the power that "distributions", as the primary sources of software for many users, have in defining what is perceived as Ion. By providing significantly modified versions and out-dated development snapshots without prominently mentioning this fact, they do not present the work in a light that the author can agree with, and create a burden of dealing with (new) users seeking for support for such versions. --- GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 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! notion-3+2012042300/Makefile000066400000000000000000000012621174530661200152230ustar00rootroot00000000000000## ## Notion Makefile ## # System-specific configuration is in system.mk include build/system-inc.mk # List of modules include modulelist.mk ###################################### INSTALL_SUBDIRS=\ $(MODULE_LIST) \ ioncore notion 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) $(DESTDIR)$(DOCDIR) for i in $(DOCS); do \ $(INSTALL) -m $(DATA_MODE) $$i $(DESTDIR)$(DOCDIR); \ done relocatable_build: $(MAKE) RELOCATABLE=1 PREFIX= notion-3+2012042300/README000066400000000000000000000126771174530661200144570ustar00rootroot00000000000000 Notion === Copyright (c) the Notion team 2010-2011. Copyright (c) Tuomo Valkonen 1999-2009. http://notion.sf.net Building and installing ----------------------- See also: https://sourceforge.net/apps/mediawiki/notion/index.php?title=Development 1. Make sure you have the following tools and libraries installed (along with, of course, standard X11 and libc stuff). * GNU make * Lua 5.1 * gettext On Ubuntu (and probably Debian), the following packages are required: build-essential lua5.1 liblua5.1-0-dev libx11-dev libxext-dev libsm-dev gettext If you also build the RandR and Xinerama modules, you will need: libxinerama-dev libxrandr-dev 2. Edit `system.mk` to suit your system. In particular, if you're on Ubuntu or Debian, you'll need to uncomment the other set of LUA* variables. 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. Notion will refuse to start if it can not find all the necessary uncorrupt configuration files either in `$PREFIX/etc/notion/` or in `~/.notion/`. 6. How to best set up `startx` or whatever to start Notion 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 Notion. 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 modify the bindings in `cfg_notioncore.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. Notion 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`. 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 `~/.notion/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_MANPATH` environment variable to the list of paths where the system stores manual pages. Configuration ------------- For help on modifying Notion's configuration files, PLEASE READ THE DOCUMENT "Configuring and extending Notion with Lua" available from the Notion 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 Notion web page listed at the top of this file. Credits ------- Notion was written by the Notion team, based on Ion which was written by Tuomo Valkonen. The dock module was written by Tom Payne and Per Olofsson. `utils/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 seems to have been originally written by Tomohiro Kubota, but see the file for details. Various (minor) 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/`. The code in `de/unicode' (producing `de/precompose.c') is taken from xterm. See `libtu/README` for code by others integrated into libtu. notion-3+2012042300/RELNOTES000066400000000000000000001430101174530661200147370ustar00rootroot00000000000000 ion-3-20081002 -------------- Some minor fixes again, including a few that should've already been in the previous release. ion-3-20080825 -------------- This release again fixes some issues found with the previous one. ion-3-20080707 -------------- This is yet another maintenance release fixing a few issues. ion-3-20080411 -------------- This is a maintenance release that fixes a few issues in the previous release. ion-3-20080207 -------------- . ion-3rc-20080103 ---------------- This release features a few minor fixes to behavioural regressions in the previous release, as well as some build configuration file self-documentation improvements. ion-3rc-20071220 ---------------- There are a few minor fixes in this RC, plus a major one related to layout restore under a session manager. ion-3rc-20071130 ---------------- This RC features various minor fixes, and some clarifications and simplifications to the license, which people seem to have had trouble understanding. ion-3rc-20071109 ---------------- It seems people are a bit slow, or only scaring them with a final release makes them report issues. So, still no stable release but a candidate, with many minor fixes and improvements wrt. important omissions. The most important changes are better behaviour of the unsplit operation, better handling of main window being closed before a transient, and use of the workspace's working directory for run query file name completion. ion-3rc-20070927 ---------------- Another attempt at last "rc" release. ion-3rc-20070902 ---------------- This release features a few minor fixes. If no major problems are discovered, this is likely to be the last "rc" release before the first and hopefully final "stable" release. ion-3rc-20070720 ---------------- This fourth "rc" release fixes some minor bugs and omissions in the previous release. ion-3rc-20070708 ---------------- This third "rc" release again simply fixes some minor problems in the previous release. ion-3rc-20070608 ---------------- This is the second "rc" release, and contains primarily bug fixes and other minor improvements. ion-3rc-20070506 ---------------- This is the first "rc" or "(stable) release candidate" release of Ion3. This means that there will not be any further major changes to it. Bugs will be fixed, and as an exception to the general feature freeze, some hooks may still be added, if deemed useful. Translations may also be included. After no new bugs (that can not be deemed features) have been found in this or following "rc" releases, the stable Ion3 will be released. Most of the changes in this release to the previous one already are bug fixes, in addition to some general clean-up. Other notable changes are: * Winprop matching improvements: it is possible to match against `is_dockapp` and `is_transient` (booleans), and there's a hack to support `class` and `instance` for dockapps too. * The default configuration puts dockapps in the statusbar's tray area. * The autoconf kludge has been removed, as I will have nothing to do with it, and nobody else seems to support or maintain it either. * Tab numbers are displayed when releasing modifiers in the `Mod1+K` submap. Distributors should pay heed to the name policy notice in the LICENSE file. ion-3ds-20070318 ---------------- This may be the last "ds" release if Ion3; if all goes well, the next release is likely to be an "rc". Some minor improvements will still be done, and minor requests may be accepted, so be quick. The most notable changes in this release from the previous one are: * Transients and queries in too small frames are "unsqueezed" out of them, unless disabled with `ioncore.set{ unsqueeze = false }`. * Some changes in default `cfg_notion.lua`: instead of loading various other files, just `cfg_default.lua` is loaded instead. * `mod_tiling.untile` (available in the context menu) may be used to decompose tilings into floating frames. (This is the approximate opposite of `mod_tiling.mkbottom` and the "new tiling" context menu entry.) * Improved context menus. * Slightly improved defaults style, using the drawing engine's added possibility of partial borders. * Various fixes and other improvements, in particular in relation to focus code. ion-3ds-20070203 ---------------- * There's been changes in padding/spacing usage in styles. Frames now surround stuff inside borders not with `spacing` pixels, but with `padding_pixels`. Spacing is only used to space things within the borders (all the tabs and the client window from each other). Consequently, custom styles may need to be updated to reflect this, by increasing the padding, possibly also altering/removing colour (to set it to background colour). * The use of drawing engine attributes has also changed, and strict ordering of them in the styles is no longer necessary. Additionally, much more attributes are supported, including the name of every statusbar meter. * Xinerama support is gone. In addition to being problematic as such, it had bit-rotted, and I will not waste time fixing it. * `ioncore.set` no longer supports the `default_ws_params` parameter for configuring the default layout. It is replaced by `ioncore.deflayout("default", { ... })`, which allows configuring other layouts as well, known by `mod_query.query_workspace` (F9). Some default layouts are configured in `cfg_layout.lua`. * It is now possible to automatically create new workspaces for windows with the `new_group` winprop. 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. notion-3+2012042300/TODO.README000066400000000000000000000004161174530661200152070ustar00rootroot00000000000000 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 notion-3+2012042300/TODO.riot000066400000000000000000000405411174530661200152320ustar00rootroot00000000000000From 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 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: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 Wed Jan 10 03:06:06 EET 2007 Message-Id: Date: Wed Jan 10 03:06:06 EET 2007 Status: RO X-Riot-Version: 1ds-20060502 From: Riot X-Riot-Edited: Wed Jan 10 03:06:06 EET 2007 Content-Type: text/plain; charset=utf-8 Subject: Trays? In-Reply-To: Trays? 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? notion-3+2012042300/build/000077500000000000000000000000001174530661200146615ustar00rootroot00000000000000notion-3+2012042300/build/libs.mk000066400000000000000000000015171174530661200161470ustar00rootroot00000000000000LIBS_SUBDIRS = libmainloop LIBMAINLOOP_DIR = $(TOPDIR)/libmainloop LIBMAINLOOP_INCLUDES = -I$(TOPDIR) LIBMAINLOOP_LIBS = -L$(LIBMAINLOOP_DIR) -lmainloop ifeq ($(wildcard $(TOPDIR)/libtu/obj.h),) #External libtu, feel free to edit LIBTU_DIR = $(TOPDIR)/../libtu LIBTU_INCLUDES = LIBTU_LIBS = -ltu else #In-tree libtu LIBS_SUBDIRS += libtu LIBTU_DIR = $(TOPDIR)/libtu LIBTU_INCLUDES = -I$(TOPDIR) LIBTU_LIBS = -L$(LIBTU_DIR) -ltu endif ifeq ($(wildcard $(TOPDIR)/libextl/luaextl.h),) #External libextl, feel free to edit LIBEXTL_DIR = $(TOPDIR)/../libextl LIBEXTL_INCLUDES = LIBEXTL_LIBS = -lextl MKEXPORTS = libextl-mkexports else #In-tree libextl LIBS_SUBDIRS += libextl LIBEXTL_DIR = $(TOPDIR)/libextl LIBEXTL_INCLUDES = -I$(TOPDIR) LIBEXTL_LIBS = -L$(LIBEXTL_DIR) -lextl MKEXPORTS = $(LUA) $(LIBEXTL_DIR)/libextl-mkexports endif notion-3+2012042300/build/mkman.lua000066400000000000000000000174561174530661200165040ustar00rootroot00000000000000-- -- 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), ...) 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={} local dummy = function() end p.META="Mod1+" p.ALTMETA="" p.dopath=dummy p.defmenu=dummy p.defctxmenu=dummy p.menuentry=dummy p.submenu=dummy p.submap_enter=dummy p.submap_leave=dummy p.submap_wait=dummy p.ioncore={ set=dummy, } function p.bdoc(text) return {action = "doc", text = text} end function p.submap(kcb_, list) if not list then return function(lst) return p.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 ins(t, v) if not t.seen then t.seen={} end if (not v.kcb) or v.submap then -- Submap rebinds are not presently handled table.insert(t, v) else local id=v.action..":"..v.kcb..":"..(v.area or "") local i=t.seen[id] if i then t[i].invalid=true end if v.cmd then table.insert(t, v) t.seen[id]=#t else -- Unbind only t.seen[id]=nil end end end function p.defbindings(context, bnd) if not bindings[context] then bindings[context]={} else -- Reset documentation table.insert(bindings[context], { action = "doc", text = nil }) end for _, v in ipairs(bnd) do ins(bindings[context], v) end end 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 -- ipairs doesn't like nil values, that e.g. submap_wait dummy might generate for i=1,#t do local v=t[i] if v and not v.invalid then if v.kcb then v.kcb=string.gsub(v.kcb, "AnyModifier%+", "") end if v.action=="doc" then if outi==0 or #out[outi].bindings>0 then outi=outi+1 end 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 end if outi~=0 and #out[outi].bindings==0 then out[outi]=nil 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 or "?")..'\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({...}) 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 -- }}} notion-3+2012042300/build/mkpreload.lua000066400000000000000000000010331174530661200173370ustar00rootroot00000000000000 io.stdout:write([[ /* Automatically generated. */ #include ]]); for _, v in ipairs({...}) 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({...}) do io.stdout:write(string.format( ' {"%s", %s_init, %s_deinit, FALSE},\n', v, v, v)); end io.stdout:write([[ {NULL, NULL, NULL, FALSE} }; ]]) notion-3+2012042300/build/rules.mk000066400000000000000000000124431174530661200163500ustar00rootroot00000000000000## ## 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 ifdef EXTRA_EXECUTABLE EXECUTABLE := $(EXTRA_EXECUTABLE) BINDIR_ := $(EXTRABINDIR) endif ifdef EXECUTABLE BINDIR_ ?= $(BINDIR) EXECUTABLE_ := $(EXECUTABLE)$(BIN_SUFFIX) TARGETS := $(TARGETS) $(EXECUTABLE_) 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 ifdef MAKE_EXPORTS # 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_EXTRA_DEPS) $(MKEXPORTS) -module $(MAKE_EXPORTS) -o $(EXPORTS_C) -h $(EXPORTS_H) \ $(SOURCES) $(MKEXPORTS_EXTRAS) # Exports documentation ###################################### EXPORTS_DOC = exports.tex TO_CLEAN := $(TO_CLEAN) $(EXPORTS_DOC) _exports_doc: $(EXPORTS_DOC) $(EXPORTS_DOC): $(SOURCES) $(LUA_SOURCES) $(MKEXPORTS_EXTRA_DEPS) $(MKEXPORTS) -mkdoc -module $(MAKE_EXPORTS) -o $(EXPORTS_DOC) \ $(SOURCES) $(LUA_SOURCES) $(MKEXPORTS_EXTRAS) else # !MAKE_EXPORTS EXPORTS_C = EXPORTS_H = EXPORTS_DOC = endif # !MAKE_EXPORTS # Compilation and linking ###################################### OBJS=$(subst .c,.o,$(SOURCES) $(EXPORTS_C)) ifdef EXECUTABLE ifdef MODULE_LIST ifdef MODULE_PATH ifeq ($(PRELOAD_MODULES),1) EXT_OBJS += $(foreach mod, $(MODULE_LIST), $(MODULE_PATH)/$(mod)/$(mod).a) DEPEND_DEPENDS += preload.c SOURCES += preload.c TO_CLEAN += preload.c else # !PRELOAD_MODULES LDFLAGS += $(EXPORT_DYNAMIC) WHOLEA = -Wl,-whole-archive NO_WHOLEA = -Wl,-no-whole-archive endif # !PRELOAD_MODULES preload.c: $(LUA) $(TOPDIR)/build/mkpreload.lua $(MODULE_LIST) > preload.c endif # MODULE_PATH endif # MODULE_LIST ifeq ($(RELOCATABLE),1) DEFINES += -DCF_RELOCATABLE_BIN_LOCATION=\"$(BINDIR_)/$(EXECUTABLE)\" endif DEFINES += -DCF_EXECUTABLE=\"$(EXECUTABLE)\" $(EXECUTABLE_): $(OBJS) $(EXT_OBJS) $(CC) $(OBJS) $(WHOLEA) $(EXT_OBJS) $(NO_WHOLEA) $(LDFLAGS) -o $@ executable_install: $(INSTALLDIR) $(DESTDIR)$(BINDIR_) $(INSTALLBIN) $(EXECUTABLE_) $(DESTDIR)$(BINDIR_) endif # EXECUTABLE ifdef MODULE ifneq ($(PRELOAD_MODULES),1) CC_PICFLAGS=-fPIC -DPIC LD_SHAREDFLAGS=-shared %.o: %.c $(EXPORTS_H) $(CC) $(CC_PICFLAGS) $(CFLAGS) -c $< -o $@ # notion might not link to Xext, so modules will have to link to it themselves # if they need it: LIBS += $(X11_LIBS) $(MODULE).so: $(OBJS) $(EXT_OBJS) $(CC) $(LD_SHAREDFLAGS) $(LDFLAGS) $(OBJS) $(EXT_OBJS) $(LIBS) -o $@ module_install: module_stub_install $(INSTALLDIR) $(DESTDIR)$(MODULEDIR) $(INSTALLBIN) $(MODULE).so $(DESTDIR)$(MODULEDIR) else # PRELOAD_MODULES PICOPT=-fPIC -DPIC LINKOPT=-shared %.o: %.c $(EXPORTS_H) $(CC) $(CFLAGS) -c $< -o $@ $(MODULE).a: $(OBJS) $(EXT_OBJS) $(AR) $(ARFLAGS) $@ $+ $(RANLIB) $@ module_install: module_stub_install endif # PRELOAD_MODULES module_stub_install: $(INSTALLDIR) $(DESTDIR)$(LCDIR) $(INSTALL) -m $(DATA_MODE) $(MODULE).lc $(DESTDIR)$(LCDIR) ifndef MODULE_STUB $(MODULE).lc: echo "ioncore.load_module('$(MODULE)') package.loaded['$(MODULE)']=true" | $(LUAC) -o $@ - else LUA_SOURCES += $(MODULE_STUB) endif #MODULE_STUB else # !MODULE %.o: %.c $(EXPORTS_H) $(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) $(DESTDIR)$(LCDIR) for i in $(LUA_COMPILED); do \ $(INSTALL) -m $(DATA_MODE) $$i $(DESTDIR)$(LCDIR); \ done etc_install: $(INSTALLDIR) $(DESTDIR)$(ETCDIR) for i in $(ETC); do \ $(INSTALL) -m $(DATA_MODE) $$i $(DESTDIR)$(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 # Defaults ###################################### INSTALL_STRIP ?= -s INSTALLBIN ?= $(INSTALL) $(INSTALL_STRIP) -m $(BIN_MODE) notion-3+2012042300/build/system-inc.mk000066400000000000000000000005501174530661200173050ustar00rootroot00000000000000# 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 notion-3+2012042300/config.h000066400000000000000000000016701174530661200152040ustar00rootroot00000000000000/* * config.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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_RESIZE_DELAY 1500 #define CF_XMESSAGE "xmessage -file " #define CF_EDGE_RESISTANCE 16 #define CF_RAISE_DELAY 500 #define CF_STATUSBAR_SYSTRAY_HEIGHT 24 #define CF_VISIBILITY_CONSTRAINT 16 /* = CF_EDGE_RESISTANCE */ #define CF_USERTIME_DIFF_CURRENT 2000 #define CF_USERTIME_DIFF_NEW 4000 /* 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_STDISP_MIN_SZ 8 #endif /* ION_CONFIG_H */ notion-3+2012042300/contrib/000077500000000000000000000000001174530661200152225ustar00rootroot00000000000000notion-3+2012042300/contrib/LICENSE000066400000000000000000001054021174530661200162310ustar00rootroot00000000000000If no license, terms of usage or any clauses are stated in the code of a script itself, then GPLv3 is applied. NotionScriptsCollections are formerly independent works of different authors. Therefore, some of the scripts may have included own license and terms of usage in their code as comments. If so, that explicetely given clauses take precedence over the generally used GPLv3 and should be considered as its license/clauses/terms. GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. 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 them 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 prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. 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. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey 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; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If 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 convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU 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 that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. 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. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 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. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. 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 state 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 3 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, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program 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, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU 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 Lesser General Public License instead of this License. But first, please read . notion-3+2012042300/contrib/README000066400000000000000000000012261174530661200161030ustar00rootroot00000000000000The scripts are initially from: http://folk.ntnu.no/bronner/temp/ion3/repos/ion-scripts-3/ Please see LICENSE. Installing and using a script ----------------------------- To use a script put them into your ~/.notion or your global notion directory and make sure you load the script with dopath("scriptname") in your cfg_notion.lua. Statusbar scripts ----------------- Add the monitor(s) provided by the script into the template in cfg_statusbar.lua. Omit any "statusd_" prefixes of the monitor names. Styles ------ After adding them to your notion directory, do F12->styles/refresh-list. After that the new styles will be available in your F12 menu. notion-3+2012042300/contrib/create_gitindex.sh000077500000000000000000000011741174530661200207220ustar00rootroot00000000000000#!/bin/sh cat index.html \ | sed -e 's/href="scripts\/\([^"]*\)"/href="gitweb.cgi\?p=notion\/contrib;a=blob_plain;f=scripts\/\1;hb=HEAD"/g' \ | sed -e 's/href="keybindings\/\([^"]*\)"/href="gitweb.cgi\?p=notion\/contrib;a=blob_plain;f=keybindings\/\1;hb=HEAD"/g' \ | sed -e 's/href="styles\/\([^"]*\)"/href="gitweb.cgi\?p=notion\/contrib;a=blob_plain;f=styles\/\1;hb=HEAD"/g' \ | sed -e 's/href="statusbar\/\([^"]*\)"/href="gitweb.cgi\?p=notion\/contrib;a=blob_plain;f=statusbar\/\1;hb=HEAD"/g' \ | sed -e 's/href="statusd\/\([^"]*\)"/href="gitweb.cgi\?p=notion\/contrib;a=blob_plain;f=statusd\/\1;hb=HEAD"/g' \ > index_git.html notion-3+2012042300/contrib/exact-version000066400000000000000000000122151174530661200177350ustar00rootroot00000000000000commit 5ff1a271e18d5afc701719c78cb4daea0d2754cc Author: Arnout Engelen Date: Thu Jan 5 22:12:15 2012 +0100 script to generate a version of the index that should work from gitweb commit 01a566050bc10bec41ecf8a876b1fb1d19857017 Author: Arnout Date: Thu Dec 8 02:26:53 2011 +0100 Announce availability of _NET_CLIENT_LIST in _NET_SUPPORTED commit c8258bb94fdb73943f8087427a71ba7508b38bf1 Merge: cf175d0 5b49875 Author: Arnout Engelen Date: Sun Nov 20 22:04:56 2011 +0100 Merge branch 'master' of https://github.com/jhamb/NotionScriptsCollection commit cf175d0ba12d47d64b21a8b9e640920a311e365d Author: Arnout Engelen Date: Thu Nov 3 13:28:10 2011 +0100 Document getting and contributing to the collection (briefly) commit 5b498755863f63d6fd1a14061416a60ac036dbdf Author: Juri Hamburg Date: Mon Oct 31 22:06:03 2011 +0100 fix typos in comments of statusd_laptopstatus.lua commit 0f3bde9325ed281924bbeba9a2c296939a6eb7e0 Author: Juri Hamburg Date: Mon Oct 31 22:01:34 2011 +0100 add estimated discharging time for sysfs interface (with ringbuffer for more stable estimated time value) commit f5e61c645954ff05ec28d81fad4d1934bd2f3049 Author: Juri Hamburg Date: Mon Oct 31 01:22:34 2011 +0100 Strings n/a and AC as global variables. commit ab012ed77728ade7c5df1cd5401cfe6c606f0c54 Author: Juri Hamburg Date: Mon Oct 31 01:12:50 2011 +0100 Close file before function return. Fix AC string issue. commit b94a2ad8aad6b629ed58ee0c226c497a6d10bc89 Author: Juri Hamburg Date: Sun Oct 30 23:58:31 2011 +0100 added initial sysfs support commit ba51e6e3e9a6a6c9049000871b3935348fdb93d9 Merge: f2bdedd 55f3765 Author: Juri Hamburg Date: Sun Oct 30 18:11:47 2011 +0100 Merge remote-tracking branch 'notion-sourceforge/master' commit 55f37657f32f81a4a439e2c0893e2f7d852ad798 Author: Arnout Date: Tue Sep 13 23:54:18 2011 +0200 ion->notion commit 86a23f2b70566880d1389048a1d06206cf2a4479 Author: Arnout Date: Tue Sep 13 23:46:30 2011 +0200 add statusd_bitcoin, _drivers and _xmms2 to index.html commit 6f875863af8af4ec4446ba2383b328de7ae46215 Merge: 47d26ce 2c4ed81 Author: Arnout Date: Tue Sep 13 23:27:34 2011 +0200 Merge branch 'anion3_updates' commit 2c4ed8196fe73f6413c1cb50113e701d74eb8ce7 Author: Arnout Date: Tue Sep 13 22:38:00 2011 +0200 index.html (including verification script) commit 50488960609388ae838fb74aa22a329f70ecd3d2 Author: Arnout Date: Tue Sep 13 22:37:49 2011 +0200 install-scripts.sh commit 645b95d8cf2f5b4ff9187325db4a68fb88a1a8b7 Author: Arnout Date: Tue Sep 13 22:33:15 2011 +0200 Add look_ootput_dark.lua commit 280b105886ccee661b737a74cb5ac3a60e8b88b1 Author: Arnout Date: Tue Sep 13 22:31:53 2011 +0200 Be more lenient in what we receive from aumix commit 4ae309d32a8be5e0e9ecfb8cdee4a1bbeae93fe3 Author: Arnout Date: Tue Sep 13 22:21:42 2011 +0200 Only update the workspace on screen_managed_changed_hook or region_notify_hook with 'how' = 'name' commit f2bdedd06a71c79fc958aa1749b2564af1558b64 Author: Voker57 Date: Fri Jan 28 15:19:36 2011 +0300 Sorting drives' names commit e066ff3e3e059fdcdf1a88b123b31209f2b4dc67 Author: Voker57 Date: Fri Jan 28 15:13:04 2011 +0300 Improved bitcoin monitor commit 47d26ce87fcae3932f43a94265f8921b14fde439 Author: Voker57 Date: Thu Jan 20 16:27:55 2011 +0300 Small fix to drives monitor commit aac6224c464587d4792111f3d6d4874ab55d443c Author: Voker57 Date: Thu Jan 20 16:25:52 2011 +0300 Drives monitor for statusd, initial version commit c80ee291d82bc4475d05e9fc5be7425f875ec18e Author: Voker57 Date: Thu Jan 20 01:28:49 2011 +0300 statusd bitcoind monitor commit 4978258e7ade2ff72f3389a0337d9afd1b81f0f6 Author: Voker57 Date: Thu Jan 20 01:28:31 2011 +0300 statusd xmms2 monitor commit 0c648217174b6243445b38d4dd64b34534625a32 Author: Etan Reisner Date: Mon Jun 14 23:32:46 2010 -0400 Call obj_typename rather than using __typename directly. commit 4892f199ff11759f5c4084af0cbeeafe313aa4cd Author: Juri Hamburg Date: Mon Jun 14 13:06:14 2010 +0200 Usage of statusbar scripts corrected. commit 7e09de62f9d2c99bf016210f70de806966301b32 Author: Juri Hamburg Date: Thu Jun 10 11:54:23 2010 +0200 improve/bugfix autoprop.lua commit a5318d1444353ae3e88e95d39cb85e939a5b2be9 Author: Juri Hamburg Date: Thu Jun 10 11:16:44 2010 +0200 initial commit from http://folk.ntnu.no/bronner/temp/ion3/repos/ion-scripts-3/ snapshot. commit 9575b9521020a8758c2e30bee46d8b16ee7333a7 Author: Juri Hamburg Date: Thu Jun 10 11:06:42 2010 +0200 Initial commit: License and Readme notion-3+2012042300/contrib/index.html000066400000000000000000000664171174530661200172350ustar00rootroot00000000000000 Notion scripts collection

Notion scripts collection

Here are some scripts for Notion. All of them are in the public domain unless otherwise mentioned in the source file.

Please note that some of the scripts are not up to date, and do not work with the latest version. Those that are known to need working, are over-striked. Please fix them, if you need them.

Scripts

adapt_menus.lua
Functions for creating a hierarchy of menus for various clients available. Key features are an attempt to organise by type hints (from the window titles) and collapse submenus when they contain only a few items.
alt_resize.lua
Possibly more intuitive resizing bindings.
app.lua
Start an application if it's not running, but go to it if it's already started. There's also a function to replace query_editfile which will use a running emacs instance (starting it if necessary, of course).
autoprop.lua
Automatically create a winprop for the given client targeted to the given frame. Allows them to be saved and reloaded automatically.
bindsearch.lua
Search the current bindings table by key or by command.
bookmarks.lua
Bookmarks support
cfg_dock2.lua
A dock configuration with lots of added control.
closeorkill.lua
Kill client on second close try if it did not respond to close
collapse.lua
Collapse frames on a WTiling into a single frame
ctrl_statusbar.lua
Menu-driven controller for the statusbar. Modules can be enabled/ disabled from the menu, without requiring to edit the configuration file.
cwin_sp.lua
Create per-clientwin scratchpads
document_menus.lua
Navigate the filesystem using notion menus.
enumerate.lua
Adds "X." in front of the client windows, which may make switching between client-windows with MOD+n a little easier .
environment_placement_hook.lua
Linux-only placement hook which detects the presence of an ION_USE_WS environment variable in the processes of new windows, and uses that to determine where to place them.
exec_show.lua
Execute some shell-command (tail, head, grep etc) and display the result.
float-sb.lua
Example of a floating toggleable statusbar.
frame_client_menu.lua
Adds a 'Client windows' submenu to a frame's context menu, the menu contains all the clients in the current frame.
go_frame_or_desk.lua
selects frame in the specified direction. If there is no frame at that location, instead the next workspace in the direction becomes active
goto_multihead.lua
A version of goto_dir that may be useful on multihead setups.
heuristics.lua
Window placement heuristics for tiled workspaces
histcompl.lua
History completion support for the line editor
lock_frame.lua
'Lock' selected frames so they don't close via the keyboard.
min_tabs.lua
Show tabs precisely when two or more windows in an notion frame
move_current.lua
Move current window in a frame to another frame in specified direction
mp.lua
Mark next mapped window to be attached to a specified object
mpd.lua
Control a MusicPD server
named_floating_groupws.lua
Toggle (and create) floating WGroupWS:s by name.
named_scratchpad.lua
Toggle (and create) scratchpads by name.
net_client_list.lua
Maintain the _NET_CLIENT_LIST property (and the _NET_CLIENT_LIST_STACKING property incorrectly) on the root window.
nest_ws.lua
Attach workspaces to windows, to e.g. have a WIonWS inside a WFloatWS, or vice versa
nextact.lua
Go to first found region with activity flag set.
notifybox.lua
Show notification boxes, similar to the activity notification.
nowarp_scratchpad.lua
Keeps notion from warping the pointer when activating a scratchpad region.
panel.lua
Facilitates placing arbitrary windows with the is_panel winprop set to true, as the stdisp.
query_url.lua
Open a URL, completing on Opera bookmark file. Should be easy to support other browsers.
rotate_statusbar.lua
Rotate the statusbar between different templates. This will automatically load all of the modules for notion-statusd.
rss_feed.lua
A simple rss-reader script
rss_feed_hh.lua
Some small improvements to match rss_feed my personal taste.
schedule.lua
Schedule some messages to show up at specified times in the statusbar.
send_to_ws.lua
Quickly send to another workspace.
show_submap.lua
Displays an infowin with the currently active submap.
simple_bindings.lua
Add a first pass at the simple bindings script.
stock.lua
An applet for retrieving and displaying stock market information from http://finance.yahoo.com. You can set up a portfolio and monitor its intraday performance.
switch_bindings.lua
Switch between key-bindings. You can temporarily disable keybindings and restore them later when necessary.
tabmenu.lua
A grabmenu-based alternative to tabs.
weather.lua
This script allows you to retreive weather information from one or more weather observation stations and display it in the statusbar.
xinerama_switcher.lua
This is similar to (and base on) go_frame_or_desk.lua but adds the ability to move between screens too. Left/right switch between screens, up/down between workspaces.
xkbion.lua
This script allows you to use independent keyboard layouts for different windows in Notion. It uses a window property to store the XKB groups, so you can restart Notion without losing settings for each window.
zoom.lua
Simulates larswm-like window zooming.

Key bindings

cfg_mouse.lua
Make the mouse pull its weight.
dans_bindings.lua
Keybindings for more intuitive WIonWS navigation.
emacs_bindings.lua
Emacs-like keybindings
vim_bindings.lua
Vim-like keybindings for Notion queries.

Styles

look_alex.lua
Black tabs, green text and borders.
look_asm.lua
A clean grey-blue theme with not too big tabs.
look_atme.lua
A style which just looks nice.
look_awesome.lua
Inspired by look_clean, comes fully-equipped with big, friendly tabs, tasty single-pixel borders, and a sprinkling of transparency. Makes use of the Terminus font.
look_awesome_sm.lua
A style based on look_awesome above, but with smaller fonts and paddings and no transparency by default. This style makes use of the 'nexus' font for tabs.
look_awesome_yaarg.lua
A style based on look-awesome-sm above, but with darker colours to give a more somber feel.
look_bas.lua
A style which colors fit the mozilla-bb background. It makes use of the techy artwiz font.
look_blue.lua
A blue/gray theme based on look_violet.lua, with a nice clean look.
look_bluecurve.lua
A theme that fits the colors of the bluecurve-theme by Redhat
look_cleanpastel.lua
Similar to stock look-clean* styles, but greenish.
look_cleansteel.lua
The stock look-brownsteel style with borders removed on tiled workspaces for cleaner look.
look_cleansteel_trans.lua
Based on look_cleansteel, but with transparent backgrounds, smaller font sizes, and a coloured notification.
look_cleanwhite.lua
A bright theme that fits white terminals and white Emacs windows.
look_cool.lua
A style based on look-awesome, made to fit the KDE color scheme "plastic-grey". Made by Steffen Liebergeld (26.08.2004).
look_gtk2.lua
A theme that fits the default GTK+2 colors. It makes use of the terminus and artwiz fonts.
look_minimalist.lua
A simple gold/red style with relatively large frame borders designed to increase usability with the minimal tabs modification.
look_moy.lua
Grey and violet simple theme.
look_ootput.lua
A non-obtrusive style with clear distinctions between active borders and less active borders. This style makes use of the Profont font.
look_ootput_dark.lua
A darker version of look_ootput that is easier on the eyes.
look_outback.lua
A warm style with sandy hints and distinctive contrast. Screenshot.
look_qt.lua
A drawing engine configuration file that reads settings from Qt configuration file ~/.qt/qtrc.
look_tibi.lua
A style which clearly emphasizes active elements and thus lets you quickly recognize them. The `accent' color is configurable.
look_tiny.lua
A major modification of look_minimalist, aiming to keep everything small while not making anything unattractive.
look_tiny_min_tabs.lua
A major modification of look_minimalist, aiming to keep everything small while not making anything unattractive. For use with min_tabs.
look_whitecode.lua
Clean white/light grey theme. Comes handy for glare displays as reflection is not as evil as with a dark theming. I recommend background color #808080 for a cool look in floating workspaces. (Tested only on my LCD, so no idea what it looks like on a CRT)

Statusbar

All statusbar monitors are loaded automatically based on the statusbar template. Do not attempt to load them manually (especially not the statusd scripts). Please see instructions below for more information.

Internal monitors

statusbar_act.lua
Activity (urgency flag, new unacted-upon transients, etc.) display for the statusbar.
statusbar_external.lua
Show output of external programs or scripts in statusbar. If you are using ion-3ds-20060107 or later, using statusd_exec.lua instead of this old version is suggested.
statusbar_fname.lua
Show the focused frame name in the statusbar.
statusbar_workspace.lua
Show current workspace name in statusbar, or a list of workspace numbers with the active one indicated.
statusbar_wsname.lua
Shows the current workspace name in the statusbar, can also show a list of workspaces and indicate the current one. The current workspace indicator is customizable. This script also allows for per-head workspace information.

Notion-statusd monitors

These monitors are run within the separate notion-statusd program. Note that many of them depend on the Linux /proc filesystem and thus will not work on other systems.
statusd_amarok.lua
Notion-statusd monitor reporting the current song from Amarok.
statusd_apm.lua
Notion-statusd monitor reporting APM status; works on OpenBSD 4.0 by executing external program every minute. Now with hints for battery state and external A/C connection.
statusd_apm2.lua
Notion-statusd monitor reporting APM status; works on FreeBSD by executing apm as an external program every minute. Added hints for a bit more color. Even though this is apm2, save it as statusd_apm.lua.
statusd_batt.lua
Notion-statusd monitor reporting battery status; works with apm.
statusd_binclock.lua
Notion-statusd binary clock in two possible modes: Plain binary (numbers) and character mode (dots, lines, etc).
statusd_bitcoin.lua
Notion-statusd monitor reporting bitcoin speed in khash/s and balance
statusd_bsdbatt.lua
Notion-statusd monitor for FreeBSD ACPI CMBATT status. Uses the sysctl interface.
statusd_cpufreq.lua
Notion-statusd monitor reporting current CPU speed in KHz, MHz, or GHz. Uses the /proc filesystem and cpufreq.
statusd_cpuspeed.lua
Notion-statusd monitor reporting current CPU speed; works with apm.
statusd_cpustat.lua
Notion-statusd monitor reporting current CPU stats using the Linux /proc/stat interface.
statusd_df.lua
Notion-statusd monitor reporting free disk space. Depends on df.
statusd_dgs.lua
Notion-statusd monitor reporting on any games waiting for a move on www.dragongoserver.net.
statusd_drives.lua
Notion-statusd monitor reporting on connected drives
statusd_exec.lua
Show output of external programs or scripts in statusbar. Uses non-blocking reads and can be used with both continously outputting and periodically run programs. Allows highlighting with regexp matching.
statusd_flashing.lua
Notion-statusd multi-purpose monitor. Show blinking alarms when some defined files or directories have changed. It can be used to monitor email inboxes, security logs, etc.
statusd_fortune.lua
Notion-statusd monitor displaying fortunes.
statusd_iface.lua
Notion-statusd monitor reporting currently used network interface.
statusd_inetaddr.lua
Notion-statusd monitor reporting current IP address from ifconfig.
statusd_info.lua
Notion-statusd monitor reporting current CPU, RAM, and swap usage. Depends on top and free.
statusd_iwinfo.lua
Notion-statusd monitor reporting minimal wireless info.
statusd_laptopstatus.lua
Notion-statusd monitor reporting CPU speed & temperature and battery status; works with acpi.
statusd_linuxbatt.lua
Notion-statusd monitor reporting battery percentage and status using the Linux /proc/acpi interface.
statusd_maildir.lua
Notion-statusd monitor showing mailcount of a Maildir.
statusd_mcpu.lua
Notion-statusd monitor reporting multiple CPUs usage (average and per-CPU); works with /proc/stat.
statusd_mem.lua
Notion-statusd monitor reporting current memory usage with selective alarms and non blocking I/O. Depends on Linux free command.
statusd_meminfo.lua
Notion-statusd monitor reporting current memory and swap usage. Depends on the Linux /proc/meminfo interface.
statusd_moc.lua
Notion-statusd monitor reporting moc information and status.
statusd_mocmon.lua
Notion-statusd monitor reporting moc information. This is actually just a modification of statusd_xmmsip.lua using the idea of non-blocking I/O as shown in statusd_mocp.lua.
statusd_mocp.lua
Notion-statusd monitor reporting moc status. This is a stripped down, non-blocking I/O version of the above. It is useful with rotate_statusbar.lua.
statusd_mpd-socket.lua
Notion-statusd monitor reporting mpd information and status. Uses a socket interface to avoid some performance issues with the original.
statusd_mpd.lua
Notion-statusd monitor reporting mpd information and status.
statusd_netmon.lua
Notion-statusd network monitor reporting network activity.
statusd_nginfo.lua
Notion-statusd monitor reporting Nagios server(s) status.
statusd_nmaild.lua
Notion-statusd Maildir monitor with selective alarms, counters for different filters and optional command launcher when a new email is detected.
statusd_orpheus.lua
Notion-statusd monitor reporting song currently being played by orpheus.
statusd_pytone.lua
Notion-statusd monitor reporting song currently being played by pytone.
statusd_sysmon.lua
Notion-statusd monitor for reporting various system resources.
statusd_ticker.lua
Notion-statusd monitor which scrolls the output of specified programs.
statusd_uname.lua
Notion-statusd monitor for reporting uname. It is primarily a simple example of how to use statusd.popen_bgread() with coroutines for non-blocking I/O.
statusd_uptime.lua
Notion-statusd monitor reporting the system uptime.
statusd_volume.lua
Notion-statusd monitor reporting sound volume values (currently, master and pcm). Depends on aumix.
statusd_volume2.lua
Notion-statusd monitor reporting master sound volume and state (on or muted). Depends on amixer. Despite being named "statusd_volume2.lua", the script needs to be relabeled "statusd_volume.lua" to work.
statusd_weather.lua
Notion-statusd monitor reporting weather.
statusd_xmms.lua
Notion-statusd monitor reporting song currently selected in xmms' playlist.
statusd_xmms2.lua
Song as "Artist - Title"
statusd_xmmsip.lua
Notion-statusd monitor(s) reporting information from a xmms-infopipe-plugin's pipe. Can be customized in various ways.

Instructions

Using

To use any of the scripts, follow these directions.
  1. Create the directory ~/.notion/, if it does not exist yet.
  2. Copy the script in this directory.
  3. Depending on the type of script, do the following.
    Proper scripts
    Copy cfg_notion.lua in ~/.notion/, if it does not contain one already. This file can usually be found in either /usr/local/etc/notion/ or /etc/X11/notion/. Then add the suitably modified line
    dopath("name of script without extension")
    
    at the end of the file (in ~/.notion/).
    Statusbar
    Make sure you have cfg_statusbar.lua in ~/.notion/, and add one of the monitors provided by the script in the template without the statusd_ or such prefix.
    Styles
    That's it. You can choose styles/refresh-list from the F12 main menu to have a running instance of Ion find it, and then choose the style from the same menu.
Some scripts may need further setup. For more information, see the scripts themselves, and the documentation.

Downloading a copy

A copy of this collection is shipped with each release of Notion. If you really need the latest version get it from git:
git clone git://notion.git.sourceforge.net/gitroot/notion/contrib
This will get you a fully functional copy of the repository in the directory contrib.

To update a previously downloaded repository, use git pull. The command git log can be used to view the list of changes, and there is also an RSS feed of the most recent changes.

Contributing

This section needs to be improved. For now, create a patch and submit it to the sf.net patches tracker, publish it on the mailinglists, or both. notion-3+2012042300/contrib/index_git.html000066400000000000000000001037011174530661200200640ustar00rootroot00000000000000 Notion scripts collection

Notion scripts collection

Here are some scripts for Notion. All of them are in the public domain unless otherwise mentioned in the source file.

Please note that some of the scripts are not up to date, and do not work with the latest version. Those that are known to need working, are over-striked. Please fix them, if you need them.

Scripts

adapt_menus.lua
Functions for creating a hierarchy of menus for various clients available. Key features are an attempt to organise by type hints (from the window titles) and collapse submenus when they contain only a few items.
alt_resize.lua
Possibly more intuitive resizing bindings.
app.lua
Start an application if it's not running, but go to it if it's already started. There's also a function to replace query_editfile which will use a running emacs instance (starting it if necessary, of course).
autoprop.lua
Automatically create a winprop for the given client targeted to the given frame. Allows them to be saved and reloaded automatically.
bindsearch.lua
Search the current bindings table by key or by command.
bookmarks.lua
Bookmarks support
cfg_dock2.lua
A dock configuration with lots of added control.
closeorkill.lua
Kill client on second close try if it did not respond to close
collapse.lua
Collapse frames on a WTiling into a single frame
ctrl_statusbar.lua
Menu-driven controller for the statusbar. Modules can be enabled/ disabled from the menu, without requiring to edit the configuration file.
cwin_sp.lua
Create per-clientwin scratchpads
document_menus.lua
Navigate the filesystem using notion menus.
enumerate.lua
Adds "X." in front of the client windows, which may make switching between client-windows with MOD+n a little easier .
environment_placement_hook.lua
Linux-only placement hook which detects the presence of an ION_USE_WS environment variable in the processes of new windows, and uses that to determine where to place them.
exec_show.lua
Execute some shell-command (tail, head, grep etc) and display the result.
float-sb.lua
Example of a floating toggleable statusbar.
frame_client_menu.lua
Adds a 'Client windows' submenu to a frame's context menu, the menu contains all the clients in the current frame.
go_frame_or_desk.lua
selects frame in the specified direction. If there is no frame at that location, instead the next workspace in the direction becomes active
goto_multihead.lua
A version of goto_dir that may be useful on multihead setups.
heuristics.lua
Window placement heuristics for tiled workspaces
histcompl.lua
History completion support for the line editor
lock_frame.lua
'Lock' selected frames so they don't close via the keyboard.
min_tabs.lua
Show tabs precisely when two or more windows in an notion frame
move_current.lua
Move current window in a frame to another frame in specified direction
mp.lua
Mark next mapped window to be attached to a specified object
mpd.lua
Control a MusicPD server
named_floating_groupws.lua
Toggle (and create) floating WGroupWS:s by name.
named_scratchpad.lua
Toggle (and create) scratchpads by name.
net_client_list.lua
Maintain the _NET_CLIENT_LIST property (and the _NET_CLIENT_LIST_STACKING property incorrectly) on the root window.
nest_ws.lua
Attach workspaces to windows, to e.g. have a WIonWS inside a WFloatWS, or vice versa
nextact.lua
Go to first found region with activity flag set.
notifybox.lua
Show notification boxes, similar to the activity notification.
nowarp_scratchpad.lua
Keeps notion from warping the pointer when activating a scratchpad region.
panel.lua
Facilitates placing arbitrary windows with the is_panel winprop set to true, as the stdisp.
query_url.lua
Open a URL, completing on Opera bookmark file. Should be easy to support other browsers.
rotate_statusbar.lua
Rotate the statusbar between different templates. This will automatically load all of the modules for notion-statusd.
rss_feed.lua
A simple rss-reader script
rss_feed_hh.lua
Some small improvements to match rss_feed my personal taste.
schedule.lua
Schedule some messages to show up at specified times in the statusbar.
send_to_ws.lua
Quickly send to another workspace.
show_submap.lua
Displays an infowin with the currently active submap.
simple_bindings.lua
Add a first pass at the simple bindings script.
stock.lua
An applet for retrieving and displaying stock market information from http://finance.yahoo.com. You can set up a portfolio and monitor its intraday performance.
switch_bindings.lua
Switch between key-bindings. You can temporarily disable keybindings and restore them later when necessary.
tabmenu.lua
A grabmenu-based alternative to tabs.
weather.lua
This script allows you to retreive weather information from one or more weather observation stations and display it in the statusbar.
xinerama_switcher.lua
This is similar to (and base on) go_frame_or_desk.lua but adds the ability to move between screens too. Left/right switch between screens, up/down between workspaces.
xkbion.lua
This script allows you to use independent keyboard layouts for different windows in Notion. It uses a window property to store the XKB groups, so you can restart Notion without losing settings for each window.
zoom.lua
Simulates larswm-like window zooming.

Key bindings

cfg_mouse.lua
Make the mouse pull its weight.
dans_bindings.lua
Keybindings for more intuitive WIonWS navigation.
emacs_bindings.lua
Emacs-like keybindings
vim_bindings.lua
Vim-like keybindings for Notion queries.

Styles

look_alex.lua
Black tabs, green text and borders.
look_asm.lua
A clean grey-blue theme with not too big tabs.
look_atme.lua
A style which just looks nice.
look_awesome.lua
Inspired by look_clean, comes fully-equipped with big, friendly tabs, tasty single-pixel borders, and a sprinkling of transparency. Makes use of the Terminus font.
look_awesome_sm.lua
A style based on look_awesome above, but with smaller fonts and paddings and no transparency by default. This style makes use of the 'nexus' font for tabs.
look_awesome_yaarg.lua
A style based on look-awesome-sm above, but with darker colours to give a more somber feel.
look_bas.lua
A style which colors fit the mozilla-bb background. It makes use of the techy artwiz font.
look_blue.lua
A blue/gray theme based on look_violet.lua, with a nice clean look.
look_bluecurve.lua
A theme that fits the colors of the bluecurve-theme by Redhat
look_cleanpastel.lua
Similar to stock look-clean* styles, but greenish.
look_cleansteel.lua
The stock look-brownsteel style with borders removed on tiled workspaces for cleaner look.
look_cleansteel_trans.lua
Based on look_cleansteel, but with transparent backgrounds, smaller font sizes, and a coloured notification.
look_cleanwhite.lua
A bright theme that fits white terminals and white Emacs windows.
look_cool.lua
A style based on look-awesome, made to fit the KDE color scheme "plastic-grey". Made by Steffen Liebergeld (26.08.2004).
look_gtk2.lua
A theme that fits the default GTK+2 colors. It makes use of the terminus and artwiz fonts.
look_minimalist.lua
A simple gold/red style with relatively large frame borders designed to increase usability with the minimal tabs modification.
look_moy.lua
Grey and violet simple theme.
look_ootput.lua
A non-obtrusive style with clear distinctions between active borders and less active borders. This style makes use of the Profont font.
look_ootput_dark.lua
A darker version of look_ootput that is easier on the eyes.
look_outback.lua
A warm style with sandy hints and distinctive contrast. Screenshot.
look_qt.lua
A drawing engine configuration file that reads settings from Qt configuration file ~/.qt/qtrc.
look_tibi.lua
A style which clearly emphasizes active elements and thus lets you quickly recognize them. The `accent' color is configurable.
look_tiny.lua
A major modification of look_minimalist, aiming to keep everything small while not making anything unattractive.
look_tiny_min_tabs.lua
A major modification of look_minimalist, aiming to keep everything small while not making anything unattractive. For use with min_tabs.
look_whitecode.lua
Clean white/light grey theme. Comes handy for glare displays as reflection is not as evil as with a dark theming. I recommend background color #808080 for a cool look in floating workspaces. (Tested only on my LCD, so no idea what it looks like on a CRT)

Statusbar

All statusbar monitors are loaded automatically based on the statusbar template. Do not attempt to load them manually (especially not the statusd scripts). Please see instructions below for more information.

Internal monitors

statusbar_act.lua
Activity (urgency flag, new unacted-upon transients, etc.) display for the statusbar.
statusbar_external.lua
Show output of external programs or scripts in statusbar. If you are using ion-3ds-20060107 or later, using statusd_exec.lua instead of this old version is suggested.
statusbar_fname.lua
Show the focused frame name in the statusbar.
statusbar_workspace.lua
Show current workspace name in statusbar, or a list of workspace numbers with the active one indicated.
statusbar_wsname.lua
Shows the current workspace name in the statusbar, can also show a list of workspaces and indicate the current one. The current workspace indicator is customizable. This script also allows for per-head workspace information.

Notion-statusd monitors

These monitors are run within the separate notion-statusd program. Note that many of them depend on the Linux /proc filesystem and thus will not work on other systems.
statusd_amarok.lua
Notion-statusd monitor reporting the current song from Amarok.
statusd_apm.lua
Notion-statusd monitor reporting APM status; works on OpenBSD 4.0 by executing external program every minute. Now with hints for battery state and external A/C connection.
statusd_apm2.lua
Notion-statusd monitor reporting APM status; works on FreeBSD by executing apm as an external program every minute. Added hints for a bit more color. Even though this is apm2, save it as statusd_apm.lua.
statusd_batt.lua
Notion-statusd monitor reporting battery status; works with apm.
statusd_binclock.lua
Notion-statusd binary clock in two possible modes: Plain binary (numbers) and character mode (dots, lines, etc).
statusd_bitcoin.lua
Notion-statusd monitor reporting bitcoin speed in khash/s and balance
statusd_bsdbatt.lua
Notion-statusd monitor for FreeBSD ACPI CMBATT status. Uses the sysctl interface.
statusd_cpufreq.lua
Notion-statusd monitor reporting current CPU speed in KHz, MHz, or GHz. Uses the /proc filesystem and cpufreq.
statusd_cpuspeed.lua
Notion-statusd monitor reporting current CPU speed; works with apm.
statusd_cpustat.lua
Notion-statusd monitor reporting current CPU stats using the Linux /proc/stat interface.
statusd_df.lua
Notion-statusd monitor reporting free disk space. Depends on df.
statusd_dgs.lua
Notion-statusd monitor reporting on any games waiting for a move on www.dragongoserver.net.
statusd_drives.lua
Notion-statusd monitor reporting on connected drives
statusd_exec.lua
Show output of external programs or scripts in statusbar. Uses non-blocking reads and can be used with both continously outputting and periodically run programs. Allows highlighting with regexp matching.
statusd_flashing.lua
Notion-statusd multi-purpose monitor. Show blinking alarms when some defined files or directories have changed. It can be used to monitor email inboxes, security logs, etc.
statusd_fortune.lua
Notion-statusd monitor displaying fortunes.
statusd_iface.lua
Notion-statusd monitor reporting currently used network interface.
statusd_inetaddr.lua
Notion-statusd monitor reporting current IP address from ifconfig.
statusd_info.lua
Notion-statusd monitor reporting current CPU, RAM, and swap usage. Depends on top and free.
statusd_iwinfo.lua
Notion-statusd monitor reporting minimal wireless info.
statusd_laptopstatus.lua
Notion-statusd monitor reporting CPU speed & temperature and battery status; works with acpi.
statusd_linuxbatt.lua
Notion-statusd monitor reporting battery percentage and status using the Linux /proc/acpi interface.
statusd_maildir.lua
Notion-statusd monitor showing mailcount of a Maildir.
statusd_mcpu.lua
Notion-statusd monitor reporting multiple CPUs usage (average and per-CPU); works with /proc/stat.
statusd_mem.lua
Notion-statusd monitor reporting current memory usage with selective alarms and non blocking I/O. Depends on Linux free command.
statusd_meminfo.lua
Notion-statusd monitor reporting current memory and swap usage. Depends on the Linux /proc/meminfo interface.
statusd_moc.lua
Notion-statusd monitor reporting moc information and status.
statusd_mocmon.lua
Notion-statusd monitor reporting moc information. This is actually just a modification of statusd_xmmsip.lua using the idea of non-blocking I/O as shown in statusd_mocp.lua.
statusd_mocp.lua
Notion-statusd monitor reporting moc status. This is a stripped down, non-blocking I/O version of the above. It is useful with rotate_statusbar.lua.
statusd_mpd-socket.lua
Notion-statusd monitor reporting mpd information and status. Uses a socket interface to avoid some performance issues with the original.
statusd_mpd.lua
Notion-statusd monitor reporting mpd information and status.
statusd_netmon.lua
Notion-statusd network monitor reporting network activity.
statusd_nginfo.lua
Notion-statusd monitor reporting Nagios server(s) status.
statusd_nmaild.lua
Notion-statusd Maildir monitor with selective alarms, counters for different filters and optional command launcher when a new email is detected.
statusd_orpheus.lua
Notion-statusd monitor reporting song currently being played by orpheus.
statusd_pytone.lua
Notion-statusd monitor reporting song currently being played by pytone.
statusd_sysmon.lua
Notion-statusd monitor for reporting various system resources.
statusd_ticker.lua
Notion-statusd monitor which scrolls the output of specified programs.
statusd_uname.lua
Notion-statusd monitor for reporting uname. It is primarily a simple example of how to use statusd.popen_bgread() with coroutines for non-blocking I/O.
statusd_uptime.lua
Notion-statusd monitor reporting the system uptime.
statusd_volume.lua
Notion-statusd monitor reporting sound volume values (currently, master and pcm). Depends on aumix.
statusd_volume2.lua
Notion-statusd monitor reporting master sound volume and state (on or muted). Depends on amixer. Despite being named "statusd_volume2.lua", the script needs to be relabeled "statusd_volume.lua" to work.
statusd_weather.lua
Notion-statusd monitor reporting weather.
statusd_xmms.lua
Notion-statusd monitor reporting song currently selected in xmms' playlist.
statusd_xmms2.lua
Song as "Artist - Title"
statusd_xmmsip.lua
Notion-statusd monitor(s) reporting information from a xmms-infopipe-plugin's pipe. Can be customized in various ways.

Instructions

Using

To use any of the scripts, follow these directions.
  1. Create the directory ~/.notion/, if it does not exist yet.
  2. Copy the script in this directory.
  3. Depending on the type of script, do the following.
    Proper scripts
    Copy cfg_notion.lua in ~/.notion/, if it does not contain one already. This file can usually be found in either /usr/local/etc/notion/ or /etc/X11/notion/. Then add the suitably modified line
    dopath("name of script without extension")
    
    at the end of the file (in ~/.notion/).
    Statusbar
    Make sure you have cfg_statusbar.lua in ~/.notion/, and add one of the monitors provided by the script in the template without the statusd_ or such prefix.
    Styles
    That's it. You can choose styles/refresh-list from the F12 main menu to have a running instance of Ion find it, and then choose the style from the same menu.
Some scripts may need further setup. For more information, see the scripts themselves, and the documentation.

Downloading a copy

A copy of this collection is shipped with each release of Notion. If you really need the latest version get it from git:
git clone git://notion.git.sourceforge.net/gitroot/notion/contrib
This will get you a fully functional copy of the repository in the directory contrib.

To update a previously downloaded repository, use git pull. The command git log can be used to view the list of changes, and there is also an RSS feed of the most recent changes.

Contributing

This section needs to be improved. For now, create a patch and submit it to the sf.net patches tracker, publish it on the mailinglists, or both. notion-3+2012042300/contrib/install-scripts.sh000066400000000000000000000015001174530661200207050ustar00rootroot00000000000000#!/bin/bash # by Dan Weiner # Public Domain # This script will install the scripts in SCRIPT_DIRS, via symlinks, # to INSTALL_LOCATION. # Make sure INSTALL_LOCATION doesn't contain anything important. # It may get overwritten! INSTALL_LOCATION="$HOME/.notion/lib" SCRIPT_DIRS="keybindings scripts statusd styles" ######################################################################## for DIR in $SCRIPT_DIRS ; do [ -d $DIR ] && continue echo "$0: error: missing $DIR/." > /dev/stderr ; exit 1 done mkdir -p "$INSTALL_LOCATION" || exit 1 find $SCRIPT_DIRS -type f |\ while read FILE ; do echo "$FILE" ln -s -f "$PWD/$FILE" "$INSTALL_LOCATION" && continue echo "$0: error: ln returned $?." > /dev/stderr ; exit 1 done exit 0 notion-3+2012042300/contrib/keybindings/000077500000000000000000000000001174530661200175305ustar00rootroot00000000000000notion-3+2012042300/contrib/keybindings/cfg_mouse.lua000066400000000000000000000160741174530661200222120ustar00rootroot00000000000000-- cfg_mouse.lua -- -- (Canaan Hadley-Voth) -- -- A set of bindings intended to make the mouse pull its weight. -- Fluxbox users should feel right at home. This will be less -- useful without a mouse wheel, but still worth considering. -- -- Partly it's an experiment to see how far one can get -- without using a keyboard in a window manager geared -- toward keyboard users. But there's nothing inherently -- anti-mouse about Ion, and with minor adjustments I find it -- extremely capable. -- -- To use, add to cfg_ion.lua: dopath("cfg_mouse") defbindings("WScreen", { -- Switching workspaces can be done from anywhere using Mod1 and -- the wheel, or without the keyboard by using the wheel over -- the left, right or bottom edges of the screen, or over a border -- or an empty frame. -- -- (Although a couple of the looks don't have any space at edges -- of the screen, most of them do.) mpress("Button4", "WScreen.switch_prev(_)"), mpress("Button5", "WScreen.switch_next(_)"), mpress(MOD1.."Button4", "WScreen.switch_prev(_)"), mpress(MOD1.."Button5", "WScreen.switch_next(_)"), -- When a WClientWin is set to fullscreen mode, the mouse -- can do little about it unless there happen to be gaps -- where the "screen" area shines through. Since this is -- often the case esp. with fullscreen terminal emulators, I've -- included button2 to exit fullscreen mode, and button3 -- brings up the main menu by default in cfg_bindings. mpress("Button2", "WClientWin.set_fullscreen(_sub, 'toggle')"), }) defbindings("WFrame", { bdoc("Bring up a context menu from anywhere in the frame."), mpress(MOD1.."Shift+Button3", "mod_menu.pmenu(_, _sub, 'ctxmenu')"), bdoc("Toggle tagged."), mpress(MOD1.."Button2", "_sub:set_tagged('toggle')"), bdoc("Attach tagged regions."), mpress(MOD1.."Shift+Button2", "_:attach_tagged()"), -- For mouse users I highly recommend button2 as a mainmenu, -- otherwise it would be difficult to start programs using the mouse. -- In Ion it's relevant which frame has focus when you start -- something, so a file manager would work poorly for that purpose -- unless it happened to be in the right frame. mpress("Button1@client", "mod_menu.pmenu(_, _sub, 'mainmenu')"), mpress("Button2@border", "mod_menu.pmenu(_, _sub, 'mainmenu')"), bdoc("Maximize vertical on double click"), mdblclick("Button1", "WFrame.maximize_vert(_)"), -- Use the wheel over the tab bar to scroll through windows in a frame. -- Oddly enough, it still works if you've hidden the tab bar. -- Just use the wheel over the top edge of the screen. -- Or use Mod4 and the wheel to avoid having to aim at anything. mpress("Button5@tab", "WScreen.switch_next(_)"), mpress("Button4@tab", "WScreen.switch_prev(_)"), mpress("Button5", "_:parent():switch_next()"), mpress("Button4", "_:parent():switch_prev()"), mpress(MOD1.."Button4", "_:parent():switch_prev()"), mpress(MOD1.."Button5", "_:parent():switch_next()"), bdoc("Drag without aiming for a tab, using Mod1+Shift"), mdrag(MOD1.."Shift+Button1", "WFrame.p_tabdrag(_)"), --[[ These should already have happened in cfg_bindings.lua. -- Uncomment if necessary. 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(MOD1.."Button3", "WFrame.p_resize(_)"), bdoc("Move the frame."), mdrag(MOD1.."Button1", "WFrame.p_move(_)"), bdoc("Move objects between frames by dragging and dropping the tab."), mdrag("Button1@tab", "WFrame.p_tabdrag(_)"), mpress("Button3", "mod_menu.pmenu(_, _sub, 'ctxmenu')"), --]] -- Using alsa-utils, by any chance? Have some volume control. --[[ mpress(MOD1.."Control+Button4", "ioncore.exec_on(_, 'amixer set PCM 1+')"), mpress(MOD1.."Control+Button5", "ioncore.exec_on(_, 'amixer set PCM 1-')"), --]] }) if MOD4 then defbindings("WFrame", { -- There were only a few bindings involving MOD4, so -- I've fixed this to work with or without. -- On my system it's the windows key, defined in -- ~/.ion3/cfg_ion.lua as MOD4="Mod4+" bdoc("Show/hide tab"), mpress(MOD4.."Button1", "WFrame.set_tabbar(_, 'toggle')"), bdoc("Quickly rename a frame."), mpress(MOD4.."Shift+Button3", "mod_query.query_renameframe(_)"), mpress(MOD4.."Button5", "WScreen.switch_next(_)"), mpress(MOD4.."Button4", "WScreen.switch_prev(_)"), }) else defbindings("WFrame", { bdoc("Show/hide tab"), mclick(MOD1.."Shift+Button1", "WFrame.set_tabbar(_, 'toggle')"), bdoc("Quickly rename a frame."), mpress(MOD1.."Shift+Control+Button3", "mod_query.query_renameframe(_)"), mpress(MOD1.."Shift+Button5", "WScreen.switch_next(_)"), mpress(MOD1.."Shift+Button4", "WScreen.switch_prev(_)"), }) end if mod_ionws then defbindings("WFrame-on-WIonWS", { mpress("Button2@tab", "mod_menu.pmenu(_, _sub, 'mainmenu')"), }) end if mod_sp then defbindings("WScratchpad", { -- button1 to move the scratchpad -- button2 to drag a tab out of the scratchpad onto another frame -- button2 (full click) to hide the scratchpad -- button3 is still a frame context menu -- -- What's missing? mainmenu via mouse, as described above. -- -- Why is that not important? Because you can button2 on any tab -- or border in the background to bring up the main menu, and any -- applications you start will land in the scratchpad. They can't -- go anywhere else. mdrag("Button1@tab", "_:p_move()"), mdrag("Button2@tab", "_:p_tabdrag()"), mclick("Button2", "mod_sp.set_shown_on(_:screen_of(), 'toggle')"), }) end if mod_query then defbindings("WInput", { bdoc("No sense reaching for the keyboard just to bat away a WMessage"), mpress("Button3", "WInput.cancel(_)"), }) end if mod_menu then defbindings("WMenu", { bdoc("...or to close a menu."), mpress("Button3", "WMenu.cancel(_)"), }) end -- Some additional items should go in the defctxmenu for WFrame -- (or some other menu called by pmenu) in order to effectively -- avoid the keyboard. Paste into cfg_menus.lua if desired. -- --[[ menuentry("Toggle Tab", "WFrame.set_tabbar(_, 'toggle')"), menuentry("Fullscreen", "WClientWin.set_fullscreen(_sub, 'toggle')", "_sub:WClientWin"), menuentry("Show Dock", "mod_dock.set_floating_shown_on(_:screen_of(), 'toggle')"), menuentry("Show SP", "mod_sp.set_shown_on(_:screen_of(), 'toggle')"), --]] -- This is a gratuitous alternative way of displaying the scratchpad -- with a mouse click. The dockapp wmnet takes an argument for a command -- to be executed on a button1 click. The command reaches into the running -- Ion3 and toggles the scratchpad. -- -- Depends on mod_ionflux and (obviously) wmnet. Also presumes 1st screen. --[[ wmnet --withdrawn --execute "ionflux -e \"mod_sp.set_shown_on(ioncore.region_list('WScreen')[1], 'toggle')\"" & --]] notion-3+2012042300/contrib/keybindings/dans_bindings.lua000066400000000000000000000023661174530661200230440ustar00rootroot00000000000000-- Dan's bindings: these allow you to navigate ion in a sane way. -- -- MOD1 + L/R cycles between clients in a frame. -- MOD1 + U/D cycles between workspaces. -- -- MOD1 + shift + L/R/U/D navigates frames intuitively on a WIonWS. -- -- MOD1 + control + L/R moves clients on a frame. -- MOD1 + control + U/D moves clients between workspaces. (TODO) -- Caution: these may break the default bindings. UP="Up" ; DOWN="Down" ; LEFT="Left" ; RIGHT="Right" -- UP="K" ; DOWN="J" ; LEFT="H" ; RIGHT="L" -- UP="W" ; DOWN="S" ; LEFT="A" ; RIGHT="D" defbindings("WScreen", { kpress(MOD1..UP, "WScreen.switch_prev(_)"), kpress(MOD1..DOWN, "WScreen.switch_next(_)"), }) defbindings("WFrame", { kpress(MOD1..LEFT.."+Control", "WFrame.dec_index(_, _sub)", "_sub:non-nil"), kpress(MOD1..RIGHT.."+Control", "WFrame.inc_index(_, _sub)", "_sub:non-nil"), kpress(MOD1..LEFT, "WFrame.switch_prev(_)"), kpress(MOD1..RIGHT, "WFrame.switch_next(_)"), }) defbindings("WTiling", { kpress(MOD1..UP.."+Shift", "WTiling.goto_dir(_, 'above')"), kpress(MOD1..DOWN.."+Shift", "WTiling.goto_dir(_, 'below')"), kpress(MOD1..LEFT.."+Shift", "WTiling.goto_dir(_, 'left')"), kpress(MOD1..RIGHT.."+Shift", "WTiling.goto_dir(_, 'right')"), }) notion-3+2012042300/contrib/keybindings/emacs_bindings.lua000066400000000000000000000236661174530661200232150ustar00rootroot00000000000000-- Emacs-like keyboard configuration for Ion, version 3. -- Written by Matthieu MOY (Matthieu.Moy@imag.fr) on February 15, 2005. -- No copyright. -- Please note: You will almost certainly want to change Ion3's default -- META binding to Alt (as this will interfere with Emacs). -- See the FAQ or the wiki entries on this subject. -- Use cfg_debian.lua on Debian installations to make the change global -- across ion3 upgrades. --[[ Copy this file in your ~/.ion3/ directory Add the line dopath("emacs-bindings") At the *end* of your ~/.ion3/cfg_ion.lua file, or in ~/.ion3/cfg_user.lua These bindings are not for every ion3 function. By copying the file, you will add bindings for ion3. You will still have to use the default ion3 bindings for any functions not bound here. Comments/Feedback welcome. See the changelog at the end. --]] --[[ List of the bindings. These are executed after the main ion3 bindings. What you should do is familiarize yourself with the main ion3 bindings and then review these additions. The Emacs bindings should provide additional bindings *not* replace the default ion3 bindings. Note that ion bindings use "META+" but since we're all Emacs users here, I left it in Emacs form "META-". Ion binding Emacs binding Description WTiling: META-x 0 C-x 0 Destroy the current frame META-x 1 C-x 1 Move all windows to single frame; destroy rest META-x 2 C-x 2 Split current frame vertically META-x 3 C-x 3 Split current frame horizontally META-x META-x 2 Split current floating frame vertically META-x META-x 3 Split current floating frame horizontally META-x o C-x o Cycle to the next workspace WMPlex: META-x b C-x b Query client win to attach to active frame META-x k C-x k Close current object META-u META-x k Kill client owning current client window META-q C-q Enter next character literally META-Ctrl-Space Attach tagged objects to this frame META-Shift-: C-: Query for Lua code to execute WScreen META-Shift-: C-: Query for Lua code to execute (XXX DUP?) META-x b C-x b Query for a client window to go to META-x META-b C-x C-b Display the window list menu META-x w Query for workspace to go to (or create) META-x META-w Display list of workspaces WEdln: M-f M-f Skip one word forward M- M- Skip one word forward M-b M-b Skip one word backward M- M- Skip one word backward M-d M-d Delete one word forward M- M- Delete one word backward C-k C-k Delete from current position to end of line C-w C-w Cut selection C-y C-y Paste from the clipboard C- C-SPC Set mark/begin selection C-g C-g Clear mark/cancel selection or abort M-p M-p Select next (matching) history entry M-n M-n Select previous (matching) history entry TODO: Global M-h k C-h k Describe key WEdlin M-t M-t Transpose word --]] Emacs = {}; -- Download it from http://modeemi.cs.tut.fi/~tuomov/repos/ion-scripts-3/scripts/collapse.lua -- Debian includes it in the ion3-scripts package. dopath("collapse") -- -- WTiling -- function WTiling.other_client(ws, sub) if ws:current() == ioncore.goto_next(sub, 'right') then ioncore.goto_next(sub, 'bottom') end end Emacs.WTiling = { bdoc("Unbind META-TAB"), kpress(META.."Tab", nil), submap(META.."x", { bdoc("Destroy current frame."), kpress ("AnyModifier+0", "WTiling.unsplit_at(_, _sub)"), bdoc("Move all windows on WTiling to single frame and destroy rest"), kpress ("AnyModifier+1", "collapse.collapse(_)"), bdoc("Split current frame vertically"), kpress ("AnyModifier+2", "WTiling.split_at(_, _sub, 'top', true)"), bdoc("Split current frame horizontally"), kpress ("AnyModifier+3", "WTiling.split_at(_, _sub, 'left', true)"), submap(META.."x", { bdoc("Split current floating frame vertically"), kpress("AnyModifier+2", "WTiling.split_at(_, _sub, 'floating:bottom', true)"), bdoc("Split current floating frame horizontally"), kpress("AnyModifier+3", "WTiling.split_at(_, _sub, 'floating:right', true)"), }), bdoc("Cycle to the next workspace"), kpress ("AnyModifier+o", "WTiling.other_client(_, _sub)") }), } defbindings("WTiling", Emacs.WTiling) -- -- WMPlex -- -- odds: -- * C-TAB, C-S-TAB, C-SPACE, C-C-SPACE, C-u C-x k are not emacs binding -- * C-:, C-! should maybe M-, M-!: (we would need to set MOD2, probably -- not a good idea) -- Emacs.WMPlex = { submap(META.."x", { bdoc("Query for a client window to attach to active frame."), kpress("b", "mod_query.query_attachclient(_)"), bdoc("Close current object."), kpress("k", "WRegion.rqclose_propagate(_, _sub)"), }), submap(META.."u", { submap(META.."x", { bdoc("Kill client owning current client window."), kpress("k", "WClientWin.kill(_sub)", "_sub:WClientWin"), }) }), bdoc("Send next key press to current client window. ".. "Some programs may not allow this by default."), kpress(META.."q", "WClientWin.quote_next(_sub)", "_sub:WClientWin"), -- Interferes with mod_sp binding -- bdoc("Tag current object within the frame."), -- kpress(META.."space", "WRegion.set_tagged(_sub, 'toggle')"), bdoc("Attach tagged objects to this frame."), kpress(META.."Control+space", "WFrame.attach_tagged(_)"), bdoc("Query for Lua code to execute."), kpress(META.."Shift+colon", "mod_query.query_lua(_)"), } defbindings("WMPlex", Emacs.WMPlex) -- -- WScreen -- -- odds: -- * C-x w, C-x C-w are not emacs bindings -- * C-x b should really attach *and* focus the client -- Emacs.WScreen = { bdoc("Query for Lua code to execute."), kpress(META.."Shift+colon", "mod_query.query_lua(_)"), -- This conflicts with going to the first display in multihead setup. -- bdoc("Query for command line to execute."), -- kpress(META.."Shift+exclam", "mod_query.query_exec(_)"), submap(META.."x", { bdoc("Query for a client window to go to."), kpress("b", "mod_query.query_gotoclient(_)"), bdoc("Display the window list menu."), kpress(META.."b", "mod_menu.menu(_, _sub, 'windowlist')"), bdoc("Query for workspace to go to or create a new one."), kpress("w", "mod_query.query_workspace(_)"), bdoc("Display a list of workspaces."), kpress(META.."w", "mod_menu.menu(_, _sub, 'workspacelist')"), }), } defbindings("WScreen", Emacs.WScreen); -- -- WEdln -- -- odds: -- M-d, M-backspace don't copy to the clipboard -- M-p, M-n do eshell like history, C-p, C-n model-line like history -- function WEdln:cut_to_eol () self:set_mark() self:eol() self:cut() end function WEdln:clear_mark_or_abort () if -1 ~= self:mark() then self:clear_mark() else self:cancel() end end Emacs.WEdln = { bdoc("Skip one word forward."), kpress("Mod1+f", "WEdln.skip_word(_)"), bdoc("Skip one word backward."), kpress("Mod1+Right", "WEdln.skip_word(_)"), bdoc("Skip one word backward."), kpress("Mod1+b", "WEdln.bskip_word(_)"), bdoc("Skip one word backward."), kpress("Mod1+Left", "WEdln.bskip_word(_)"), bdoc("Delete one word forward."), kpress("Mod1+d", "WEdln.kill_word(_)"), bdoc("Delete one word backward."), kpress("Mod1+BackSpace", "WEdln.bkill_word(_)"), bdoc("Delete from current position to the end of the line."), kpress("Control+K", "WEdln.cut_to_eol(_)"), bdoc("Cut selection."), kpress("Control+W", "WEdln.cut(_)"), bdoc("Paste from the clipboard."), kpress("Control+Y", "WEdln.paste(_)"), bdoc("Set mark/begin selection."), kpress("Control+space", "WEdln.set_mark(_)"), bdoc("Clear mark/cancel selection or abort."), kpress("Control+G", "WEdln.clear_mark_or_abort(_)"), bdoc("Select next (matching) history entry."), kpress("Mod1+p", "WEdln.history_prev(_, true)"), bdoc("Select previous (matching) history entry."), kpress("Mod1+n", "WEdln.history_next(_, true)"), } defbindings("WEdln", Emacs.WEdln) -- ChangeLog: -- -- David Hansen (all public domain) -- -- * DEFAULT_MOD -> MOD1 -- * WEdln: M-f, M-b, M-backspace, M-d, M-p, M-n, M-right, M-left -- * WEdln: C-g clear mark or abort if the mark is not set -- * WEdln: C-k copy to clipboard -- * WMPlex: removed C-! C-:, C-x b (they are also in WScreen) -- * WMPlex: C-q, C-x C-k, C-u C-x C-k -- * WScreen: C-x C-b, C-x w, C-x C-w -- * WIonWS: C-x 2, C-x 3 a bit more emacs like -- * WIonWS: fixed C-x o -- * WIonWS: removed C-x C-x * -- -- Tyranix (all public domain) -- * Added comments about how emacs-bindings.lua relates to the defaults -- * Added bdoc() comments to each binding -- * Rearranged code so the bdoc comments are easier to read -- * Added a main listing of every binding in this file along with what you -- should think in Emacs terms -- * Remove META-! because it conflicts with moving between multihead displays -- * Changed MOD1 to META as this is the new name for it in newer ion3 -- * Added Shift+ to the META-: and META-exclam -- * Removed META- because it interferes with the scratch pad (mod_sp). -- -- Adam Di Carlo (all public domain, April 27 2007) -- * For compatability with ion3 20070318, change WIonWS to WTiling. -- * Disable non-working and overly obtrusive META-TAB and META-Shift-TAB -- remapping.notion-3+2012042300/contrib/keybindings/vim_bindings.lua000066400000000000000000000251561174530661200227140ustar00rootroot00000000000000-- vim-bindings.lua -- -- This only affects queries. -- -- Features: -- Vi-keys for normal and insert modes. -- Vim-style highlighting. -- Numbered prefixes work for most maneuvers. -- -- Limitations: -- All WEdln objects share the same mode and undo history. -- No r replace -- No . repeat last action -- No character search. -- 3cw will work; c3w won't. -- Not all unused symbols are silenced in normal mode -- (so don't type them) -- Use Shift+Insert to paste from other apps, p to paste yanked text. -- vim={clip="",old="",oldpoint=0} function vim.normal_mode() defbindings("WEdln", { kpress("a", "{WEdln.forward(_), vim.insert_mode(), vim.savehist(_)}"), kpress("Shift+a", "{WEdln.eol(_), vim.insert_mode(), vim.savehist(_)}"), kpress("b", "vim.multiply(WEdln.bskip_word, _)"), kpress("Shift+b", "vim.multiply(WEdln.bskip_word, _)"), submap("c", { kpress("b", "vim.yank(_, 'b', true, true)"), kpress("c", "vim.yank(_, 'd', true, true)"), kpress("h", "vim.yank(_, 'h', true, true)"), kpress("l", "vim.yank(_, 'l', true, true)"), kpress("w", "vim.yank(_, 'w', true, true)"), kpress("0", "vim.yank(_, '0', true, true)"), kpress("Shift+6", "vim.yank(_, '0', true, true)"), kpress("Shift+4", "vim.yank(_, 'D', true, true)"), }), kpress("Shift+c", "vim.yank(_, 'D', true, true)"), submap("d", { kpress("b", "vim.yank(_, 'b', true)"), kpress("d", "vim.yank(_, 'd', true)"), kpress("h", "vim.yank(_, 'h', true)"), kpress("l", "vim.yank(_, 'l', true)"), kpress("w", "vim.yank(_, 'w', true)"), kpress("0", "vim.yank(_, '0', true)"), kpress("Shift+6", "vim.yank(_, '0', true)"), kpress("Shift+4", "vim.yank(_, 'D', true)"), }), kpress("Shift+d", "vim.yank(_, 'D', true)"), kpress("e", "{_:forward(), vim.multiply(WEdln.skip_word,_), _:back()}"), kpress("Shift+e", "{_:forward(), vim.multiply(WEdln.skip_word,_), _:back()}"), kpress("f", "vim.cleardigit()"), kpress("g", "vim.cleardigit()"), kpress("h", "vim.multiply(WEdln.back,_)"), kpress("i", "{vim.insert_mode(), vim.savehist(_)}"), kpress("Shift+i", "{WEdln.bol(_), vim.insert_mode(), vim.savehist(_)}"), kpress("j", "vim.multiply(WEdln.history_next, _)"), kpress("k", "vim.multiply(WEdln.history_prev, _)"), kpress("Control+n", "vim.multiply(WEdln.history_next, _)"), kpress("Control+p", "vim.multiply(WEdln.history_prev, _)"), kpress("l", "vim.multiply(WEdln.forward, _)"), kpress("m", "vim.cleardigit()"), kpress("n", "vim.cleardigit()"), kpress("o", "vim.cleardigit()"), kpress("p", "{WEdln.forward(_), vim.multiply(vim.paste,_,true)," .."WEdln.back(_)}"), kpress("Shift+p", "vim.multiply(vim.paste,_,true)"), kpress("q", "vim.cleardigit()"), kpress("r", "vim.cleardigit()"), kpress("s", "vim.yank(_, 'l', true, true)"), kpress("Shift+s", "vim.yank(_, 'd', true, true)"), kpress("t", "vim.cleardigit()"), kpress("u", "vim.undo(_)"), kpress("v", "vim.highlight(_)"), kpress("w", "vim.multiply(WEdln.skip_word, _)"), kpress("Shift+w", "vim.multiply(WEdln.skip_word, _)"), kpress("x", "vim.x(_)"), kpress("Shift+x", "vim.yank(_, 'h', true)"), kpress("y", "vim.yank(_)"), kpress("Shift+y", "vim.yank(_, 'd')"), kpress("z", "vim.cleardigit()"), kpress("0", "vim.zero(_)"), kpress("1", "vim.dodigit(1)"), kpress("2", "vim.dodigit(2)"), kpress("3", "vim.dodigit(3)"), kpress("4", "vim.dodigit(4)"), kpress("5", "vim.dodigit(5)"), kpress("6", "vim.dodigit(6)"), kpress("7", "vim.dodigit(7)"), kpress("8", "vim.dodigit(8)"), kpress("9", "vim.dodigit(9)"), kpress("Shift+6", "{vim.cleardigit(), WEdln.bol(_)}"), kpress("Shift+4", "{vim.cleardigit(), WEdln.eol(_)}"), kpress("BackSpace", "vim.multiply(WEdln.back, _)"), kpress("space", "vim.multiply(WEdln.forward, _)"), kpress("Shift+grave", "vim.multiply(vim.switchcase,_,true)"), kpress("Control+A", "vim.multiply(vim.increment, _)"), kpress("Control+X", "vim.multiply(vim.decrement, _)"), kpress("Shift+f", "vim.cleardigit()"), kpress("Shift+g", "vim.cleardigit()"), kpress("Shift+h", "vim.cleardigit()"), kpress("Shift+j", "vim.cleardigit()"), kpress("Shift+k", "vim.cleardigit()"), kpress("Shift+l", "vim.cleardigit()"), kpress("Shift+m", "vim.cleardigit()"), kpress("Shift+n", "vim.cleardigit()"), kpress("Shift+o", "vim.cleardigit()"), kpress("Shift+q", "vim.cleardigit()"), kpress("Shift+r", "vim.cleardigit()"), kpress("Shift+t", "vim.cleardigit()"), kpress("Shift+u", "vim.cleardigit()"), kpress("Shift+v", "vim.cleardigit()"), kpress("Shift+z", "vim.cleardigit()"), kpress("Shift+0", "vim.cleardigit()"), kpress("Shift+1", "vim.cleardigit()"), kpress("Shift+2", "vim.cleardigit()"), kpress("Shift+3", "vim.cleardigit()"), kpress("Shift+5", "vim.cleardigit()"), kpress("Shift+7", "vim.cleardigit()"), kpress("Shift+8", "vim.cleardigit()"), kpress("Shift+9", "vim.cleardigit()"), }) end function vim.insert_mode() for _,edln in pairs(ioncore.region_list("WEdln")) do edln:clear_mark() end defbindings("WEdln", { kpress("Control+w", "WEdln.bkill_word(_)"), kpress("Control+n", "WEdln.next_completion(_, true)"), kpress("Control+p", "WEdln.prev_completion(_, true)"), kpress("Escape", "vim.normal_mode()"), kpress("Control+bracketleft", "vim.normal_mode()"), kpress("BackSpace", "WEdln.backspace(_)"), kpress("Mod1+BackSpace", "WEdln.bkill_word(_)"), kpress("a", nil), kpress("b", nil), kpress("c", nil), kpress("d", nil), kpress("e", nil), kpress("f", nil), kpress("g", nil), kpress("h", nil), kpress("i", nil), kpress("j", nil), kpress("k", nil), kpress("l", nil), kpress("m", nil), kpress("n", nil), kpress("o", nil), kpress("p", nil), kpress("q", nil), kpress("r", nil), kpress("s", nil), kpress("t", nil), kpress("u", nil), kpress("v", nil), kpress("w", nil), kpress("x", nil), kpress("y", nil), kpress("z", nil), kpress("0", nil), kpress("1", nil), kpress("2", nil), kpress("3", nil), kpress("4", nil), kpress("5", nil), kpress("6", nil), kpress("7", nil), kpress("8", nil), kpress("9", nil), kpress("Shift+a", nil), kpress("Shift+b", nil), kpress("Shift+c", nil), kpress("Shift+d", nil), kpress("Shift+e", nil), kpress("Shift+f", nil), kpress("Shift+g", nil), kpress("Shift+h", nil), kpress("Shift+i", nil), kpress("Shift+j", nil), kpress("Shift+k", nil), kpress("Shift+l", nil), kpress("Shift+m", nil), kpress("Shift+n", nil), kpress("Shift+o", nil), kpress("Shift+p", nil), kpress("Shift+q", nil), kpress("Shift+r", nil), kpress("Shift+s", nil), kpress("Shift+t", nil), kpress("Shift+u", nil), kpress("Shift+v", nil), kpress("Shift+w", nil), kpress("Shift+x", nil), kpress("Shift+y", nil), kpress("Shift+z", nil), kpress("Shift+0", nil), kpress("Shift+1", nil), kpress("Shift+2", nil), kpress("Shift+3", nil), kpress("Shift+4", nil), kpress("Shift+5", nil), kpress("Shift+6", nil), kpress("Shift+7", nil), kpress("Shift+8", nil), kpress("Shift+9", nil), kpress("space", nil), kpress("Shift+grave", nil), kpress("Control+A", nil), kpress("Control+X", nil), }) end defbindings("WInput", { kpress("Control+C", "{vim.cleardigit(), WInput.cancel(_)}"), kpress("Control+F", "WInput.scrollup(_)"), kpress("Control+B", "WInput.scrolldown(_)"), kpress("Page_Up", "WInput.scrollup(_)"), kpress("Page_Down", "WInput.scrolldown(_)"), }) defbindings("WEdln", { kpress("Shift+Insert", "WEdln.paste(_)"), kpress("Return", "{vim.cleardigit(), WEdln.finish(_)}"), }) function vim.yank(edln, how, kill, insert) vim.old=edln:contents() vim.oldpoint=edln:point() local first,last local n=1 if vim.digit1 then n=vim.digit1 end if vim.digit10 then n=n+10*vim.digit10 end if edln:mark() < 0 then edln:set_mark() for i=1,n do if how=="b" then edln:bskip_word() elseif how=="D" then edln:eol() elseif how=="d" then edln:bol() edln:set_mark() edln:eol() elseif how=="h" then edln:back() elseif how=="l" then edln:forward() elseif how=="w" then edln:skip_word() elseif how=="0" then edln:bol() end end end first=edln:mark()+1 last=edln:point() if first>last then first=last+1 last=edln:mark() end vim.clip=string.sub(edln:contents(), first, last) if kill then edln:cut() else edln:copy() end edln:clear_mark() -- just in case? vim.cleardigit() if insert then vim.insert_mode() end end function vim.undo(edln) local str=edln:contents() edln:kill_line() edln:insstr(vim.old) vim.old=str edln:bol() for i=1,vim.oldpoint do edln:forward() end end function vim.savehist(edln) vim.old=edln:contents() vim.oldpoint=edln:point() end function vim.zero(edln) if vim.digit1 then vim.dodigit(0) else edln:bol() vim.cleardigit() end end function vim.x(edln) -- x is only a backspace when there is nothing ahead. if string.len(edln:contents()) == edln:point() then vim.yank(edln, 'h', true) else vim.yank(edln, 'l', true) end end function vim.highlight(edln) if edln:mark() > -1 then edln:clear_mark() else edln:set_mark() end vim.cleardigit() end function vim.paste(edln) -- if something is highlighted, replace it. local clip2=vim.clip if edln:mark() > -1 then first=edln:mark()+1 last=edln:point() if first>last then first=last+1 last=edln:mark() end vim.clip=string.sub(edln:contents(), first, last) edln:cut() end edln:insstr(clip2) vim.cleardigit() end function vim.multiply(f, edln, addtohistory) if edln and addtohistory then vim.old=edln:contents() vim.oldpoint=edln:point() end local n=1 if vim.digit1 then n=vim.digit1 end if vim.digit10 then n=n+10*vim.digit10 end for i=1,n do f(edln) end vim.cleardigit() end function vim.dodigit(digit) vim.digit10=vim.digit1 vim.digit1=digit end function vim.cleardigit() -- This function needs to go almost everywhere. Doing the wrong thing -- 10 times because a number was pressed ages ago, would be bad. vim.digit1=nil vim.digit10=nil end function vim.switchcase(edln) local c=string.sub(edln:contents(), edln:point()+1, edln:point()+1) if string.find(c, "%u") then edln:delete() edln:insstr(string.lower(c)) elseif string.find(c, "%l") then edln:delete() edln:insstr(string.upper(c)) else edln:forward() end end function vim.decrement(edln) local c=string.sub(edln:contents(), edln:point()+1, edln:point()+1) if string.find(c, "%d") then edln:delete() c=c-1 if c==-1 then c=9 end edln:insstr(tostring(c)) edln:back() end end function vim.increment(edln) local c=string.sub(edln:contents(), edln:point()+1, edln:point()+1) if string.find(c, "%d") then edln:delete() c=c+1 if c==10 then c=0 end edln:insstr(tostring(c)) edln:back() end end vim.normal_mode() vim.insert_mode() notion-3+2012042300/contrib/scripts/000077500000000000000000000000001174530661200167115ustar00rootroot00000000000000notion-3+2012042300/contrib/scripts/adapt_menus.lua000066400000000000000000000155231174530661200217220ustar00rootroot00000000000000-- this builds up a menu framework for performing some client operation -- say, jumping to the client or attaching the client ot the current -- frame. it assumes that helpful applications either tend to have titles of -- the form or will allow their title s to be set to something roughly of the -- form ":
", so the top-level menu performs a split on the --type, then the first level -- of submenus contains the details portion of the titles. (title entries -- that don't fit the "type:" pattern get stuffed into an submenu labelled "misc.".) -- however, if -- a first level menu would exceed a certain size then a grouping into -- another layer of submenus is performed on the basis of the first -- letter of detail string (suppressing sub-menus that would be below a -- certain number of elements). note the aim is NOT consistency across -- invocations but to try and on-the-fly make clients as easy to find & access -- as possible (in an some overall sense). the acutal algorithm is a bit ad-hoc. -- this was motivated by usage of Ion3 where every frame had it's tab-bar -- hidden (partly because with LOTS of windows the tabs become -- so small that trying to read them doesn't work too well) and has -- become the primary navigation mechanism. it'll probably be of less -- interest when there aren't enough windows to make tabs ineffective. -- the four useful client-functions -- closing, jumping-to, tagging and attaching-here -- -- are provided. anyone coming up with other useful functions is encouraged to -- add them to the version of this file on the ion3-scripts archive -- the commented out keybindings at the end are just to show how the -- functionality might be used. -- tested to work on ion-3ds-20050820. -- Changelog -- Sep 2005, initial version, David Tweed function mkentryJump(frame,tgt,name) return ioncore.menuentry(name, function() tgt:goto() end) end function mkentryClose(frame,tgt,name) return ioncore.menuentry(name, function() tgt:rqclose() end) end function mkentryTag(frame,tgt,name) return ioncore.menuentry(name, function() tgt:set_tagged("toggle") end) end function attachHere(frame,tgt) if tgt:manager()==frame then tgt:goto() else frame:attach(tgt,{switchto=true}) end end function mkentryAttach(frame,tgt,name) return ioncore.menuentry(name, function() attachHere(frame,tgt) end) end function sizeSubmenus(tbl) local noMenus=0 local totSz=0 for i,w in pairs(tbl) do totSz=totSz+table.getn(w) noMenus=noMenus+1 end return totSz,noMenus end --treat two tables as lists and concatenate them (ie ignore keys) function extend(lst1,lst2) local lst=lst1 for _,v in pairs(lst2) do table.insert(lst,v) end return lst end -- for fonts where the width of two spaces is the same as the -- width of any digit this alignment hack will work function prependNum(dict) table.sort(dict, function(a, b) return a.name < b.name end) local cnt=0 for i,w in pairs(dict) do local aligner if cnt<10 then aligner=" " else aligner="" end w.name=string.format("%u.%s %s",cnt,aligner,w.name) cnt=cnt+1 end end function collapseTwoLevels(menu,mkentry,dict,frame,attr) for j,w in pairs(dict) do for k,w1 in pairs(w) do table.insert(menu,mkentry(frame,w1.obj,w1[attr])) end end end function insertListIntoMenu(dest,mkentry,dict,frame,strKey) for i,v in pairs(dict) do table.insert(dest,mkentry(frame,v.obj,v[strKey])) end end function makelistEngine(frame,title,mkentry,lst, maxSzForMainMenuMerge) local subEntryDB={} for i,tgt in pairs(lst) do local n=tgt:name() local c="misc." local rest=n local s,f=string.find(n,"^([^ :]+[:])") if s then c=string.sub(n,s,f) rest=string.sub(n,f+1) else rest=n end local r1=string.sub(rest,1,1) if not(subEntryDB[c]) then subEntryDB[c]={} end if not(subEntryDB[c][r1]) then subEntryDB[c][r1]={} end -- store enough so that we can apply mkentry once we know where in the -- hierarchy this should appear (and thus what it's text string should be) table.insert(subEntryDB[c][r1],{title=n,rest=rest,obj=tgt}) end local mainMenu={} for i,v in pairs(subEntryDB) do local sz,noSubMenus=sizeSubmenus(v) local sndLevMenu={} --if we'd have a group of submenus of total size less than maxSzForMainMenuMerge, --put them directly onto the main menu without submenus if sz<=maxSzForMainMenuMerge then -- use the full title on "menu direct entires" collapseTwoLevels(mainMenu,mkentry,v,frame,"title") else if sz<=5 or noSubMenus<=2 then -- use sub-instance title for "sub menu all entries direct entries" collapseTwoLevels(sndLevMenu,mkentry,v,frame,"rest") else for j,w in pairs(v) do --if we'd only have 1 or 2 element submenu, stick them in submenu directly if table.getn(w)<=2 then -- use the sub-instance on "sub-menu direct entries" insertListIntoMenu(sndLevMenu,mkentry,w,frame,"rest") else local thdLevMenu={} -- use the sub-instance on "sub-sub-menu entries" insertListIntoMenu(thdLevMenu,mkentry,w,frame,"rest") prependNum(thdLevMenu) table.insert(sndLevMenu,ioncore.submenu(j.."...",thdLevMenu,1)) end end end prependNum(sndLevMenu) table.insert(mainMenu,ioncore.submenu(i,sndLevMenu,1)) end end prependNum(mainMenu) table.insert(mainMenu,table.getn(mainMenu)+1,ioncore.menuentry("------------------"..title.."------------------", "nil")) return mainMenu end --[[ -- some examples of usage defbindings("WFrame", { kpress(MOD1.."F8", "mod_menu.menu(_,_sub,makelistEngine(_,'jump',mkentryJump,extend(ioncore.clientwin_list(),ioncore.region_list('WGenWS')),1))"), kpress(MOD2.."F8", "mod_menu.menu(_,_sub,makelistEngine(_,'jumpWS',mkentryJump,ioncore.region_list('WGenWS'),4))"), kpress(MOD1.."F7", "mod_menu.menu(_, _sub,makelistEngine(_,'attach',mkentryAttach,ioncore.clientwin_list(),1))"), kpress(MOD2.."F7", "mod_menu.menu(_, _sub,makelistEngine(_,'close',mkentryClose,ioncore.clientwin_list(),1))"), kpress(MOD1.."F6", "mod_menu.menu(_, _sub,makelistEngine(_,'raise',mkentryJump,extend(_:llist(1),ioncore.region_list('WGenWS')),4))"), kpress(MOD2.."F6", "mod_menu.menu(_, _sub,makelistEngine(_,'close',mkentryClose,_:llist(1),4))"), }) --]] notion-3+2012042300/contrib/scripts/alt_resize.lua000066400000000000000000000033761174530661200215660ustar00rootroot00000000000000-- -- Alternative resizing for ion-devel -- -- By Per Olofsson (public domain) -- -- -- More intuitive resizing keys for Ion: -- -- key function -- -------------------------- -- left shrink leftwards -- right grow rightwards -- up shrink upwards -- down grow downwards -- -- The meaning of the corresponding keys are reversed when the window -- is at the right or bottom edge of the workspace. -- -- Add the code to ionws.lua to make use of it. You probably shouldn't -- delete any bindings for keys that are not redefined here. -- alt_resize={} function alt_resize.resize(m, f, dir) local ws = f:manager() if dir == "left" then if ws:right_of(f) then m:resize( 0,-1, 0, 0) else m:resize( 1, 0, 0, 0) end elseif dir == "right" then if ws:right_of(f) then m:resize( 0, 1, 0, 0) else m:resize(-1, 0, 0, 0) end elseif dir == "up" then if ws:below(f) then m:resize( 0, 0, 0,-1) else m:resize( 0, 0, 1, 0) end elseif dir == "down" then if ws:below(f) then m:resize( 0, 0, 0, 1) else m:resize( 0, 0,-1, 0) end end end defbindings("WMoveresMode", { kpress("Left", "alt_resize.resize(_, _sub, 'left' )"), kpress("Right", "alt_resize.resize(_, _sub, 'right')"), kpress("Up", "alt_resize.resize(_, _sub, 'up' )"), kpress("Down", "alt_resize.resize(_, _sub, 'down' )"), kpress("F", "alt_resize.resize(_, _sub, 'left' )"), kpress("B", "alt_resize.resize(_, _sub, 'right')"), kpress("P", "alt_resize.resize(_, _sub, 'up' )"), kpress("N", "alt_resize.resize(_, _sub, 'down' )"), }) notion-3+2012042300/contrib/scripts/app.lua000066400000000000000000000073511174530661200202020ustar00rootroot00000000000000-- -- By Jeremy Hankins -- Multi-head support added by Johannes Segitz -- Cycling contributed by -- Antti Kaihola -- -- Time-stamp: <2005-11-16 12:02:46 Jeremy Hankins> -- <2005-08-26 11:35:00 kaihola> -- -- With these functions you can bind a key to start an application if -- it's not already running, or cycle its windows if it is. You can -- use it by doing something like this in your bindings file: -- -- kpress(META.."C", "app.byname('xterm -title shell', 'shell')"), -- kpress(META.."T", "app.byclass('emacs', 'Emacs')"), -- kpress(META.."M", "app.byinstance('xterm -name muttTerm -e mutt', 'XTerm', 'muttTerm')"), -- -- The byname function expects an executable and a window title, the -- byclass expects a class, the byinstance expects a class and an -- instance. -- -- NOTE: cycling windows requires ioncore.current(), so it will only -- work with snapshot XXX or later. -- -- If you use a multihead setup you can use something like this to start -- applictions on your current screen -- -- kpress(MOD1.."C", "app.byname('xterm -title shell', 'shell', _)"), -- kpress(MOD1.."T", "app.byclass('emacs', 'Emacs', _)"), -- -- For emacs users there's also app.emacs_eval, and app.query_editfile, -- which interacts with any currently running emacs process (using -- gnuclient), or starts emacs to run the given command. -- app.query_editfile is a replacement for query_lib.query_editfile to -- use the currently running emacs rather than ion-edit. -- app={} function app.match_class(class, instance) -- Return matching client windows as a table. -- If the current window is among them, it's placed last on the list -- and its successor at the beginning of the list. This facilitates -- cycling multiple windows of an application. local result = {} local offset = 0 local currwin = ioncore.current() ioncore.clientwin_i(function (win) if class == win:get_ident().class then if instance then if instance == win:get_ident().instance then table.insert(result, table.getn(result)-offset+1, win) end else table.insert(result, table.getn(result)-offset+1, win) end end if win == currwin then -- Current client window found, continue filling the table from -- the beginning. offset = table.getn(result) end return true end) return result end function app.byname(prog, name, where) local win = ioncore.lookup_clientwin(name) if win then ioncore.defer(function () win:goto() end) else if where then ioncore.exec_on(where, prog) else ioncore.exec(prog) end end end function app.byclass(prog, class, where) local win = app.match_class(class)[1] if win then ioncore.defer(function () win:goto() end) else if where then ioncore.exec_on(where, prog) else ioncore.exec(prog) end end end function app.byinstance(prog, class, instance, where) local win = app.match_class(class, instance)[1] if win then ioncore.defer(function () win:goto() end) else if where then ioncore.exec_on(where, prog) else ioncore.exec(prog) end end end function app.emacs_eval(expr) local emacswin = app.match_class("Emacs")[1] if emacswin then ioncore.exec("gnuclient -batch -eval '"..expr.."'") ioncore.defer(function () emacswin:goto() end) else ioncore.exec("emacs -eval '"..expr.."'") end end function app.query_editfile(mplex, dir) local function handler(file) app.emacs_eval("(find-file \""..file.."\")") end mod_query.do_query(mplex, 'Edit file:', dir or mod_query.get_initdir(), handler, mod_query.file_completor) end notion-3+2012042300/contrib/scripts/autoprop.lua000066400000000000000000000034261174530661200212720ustar00rootroot00000000000000-- Automatically create a winprop for the WClientWin given. -- Basic functionality by pinko -- Extended by Etan Reisner --defbindings("WFrame", { ---- add winprop permanentlu -- kpress(META.."W", "autoprop(_sub, _, true)", "_sub:WGroupCW"), ---- add winprop for this session only -- kpress(META.."E", "autoprop(_sub, _, false)", "_sub:WGroupCW"), ---- remove winprop (permanently) -- kpress(META.."Q", "autoprop(_sub, nil, X)"), --}) -- Use autoprop(_sub, nil, X) to remove a winprop local savefile="autoprops" local autoprops={} function autoprop(cwin_group, dest, save) -- if obj_typename(cwin_group) ~= "WGroupCW" then -- return -- end local p={} local cwin = cwin_group:bottom() if type(dest)=="nil" then name=nil else name=dest:name() end p.class="*" p.role="*" p.instance="*" p.target=name p=table.join(cwin:get_ident(), p) defwinprop{ class=p.class, instance=p.instance, role=p.role, target=name } if save and p.target then autoprops[p.class..p.role..p.instance]=p end if p.target==nil then autoprops[p.class..p.role..p.instance]=nil end end local function save_autoprops() ioncore.write_savefile(savefile, autoprops) end local function load_autoprops() local d=ioncore.read_savefile(savefile) if not d then return end table.foreach(d, function(k, tab) defwinprop{ class=tab.class, instance=tab.instance, role=tab.role, target=tab.target } autoprops[tab.class..tab.role..tab.instance]=tab end) end local function init() load_autoprops() ioncore.get_hook("ioncore_snapshot_hook"):add(save_autoprops) end init() notion-3+2012042300/contrib/scripts/bindsearch.lua000066400000000000000000000050071174530661200215200ustar00rootroot00000000000000-- bindsearch.lua -- -- Canaan Hadley-Voth. -- -- Bindings are spread across distant files, some are created -- dynamically, and some are negated by others. I use this to -- tell me exactly what is bound right now. -- -- Search (in all sections) for bound keys or bound commands. -- There can be multiple search terms separated by spaces, but -- they would have to appear in the specified order. Output -- is a WMessage with a section of the bindings table. -- --[[ Example: bindsearch("K X") yields: WIonWS [kcb] = Mod1+K [action] = kpress [submap] = table: 0x8254478 [cmd] = WIonWS.unsplit_at(_, _sub) [action] = kpress [kcb] = AnyModifier+X [func] = function: 0x80de2d0 cmdsearch("unsplit") would have found the same thing. A search for " " shows all bindings. --]] -- In versions older than 20051210, print() is not defined to send wmessages. -- Uncommenting this next line might be necessary. -- local function print(msg) mod_query.message( ioncore.region_list("WScreen")[1], tostring(msg) ) end local function strtable(tabl, f) local msg="" if not f then f=function(x) return x end end n=#tabl if n>0 then msg=tostring(n).." objects:" end for i in pairs(tabl) do msg=msg.." ["..tostring(i).."] = "..tostring(f(tabl[i])).."\n" end return msg end -- Possible fields are "kcb" (default), "cmd", and "class". function bindsearch(str, field) local btabl = table.copy(ioncore.getbindings(), true) local fullmsg="" local fullkcb="" local class="" str=string.gsub(str, "%+", "%%%+") str=string.gsub(str, " ", ".*") --string.gsub(str, "@", "%%%@") local function domap(class, t, msg, kcb, show_all) local found for index,bind in pairs(t) do found=false if bind.area then fullkcb=kcb.." "..bind.kcb.."@"..bind.area else fullkcb=kcb.." "..bind.kcb end fullkcb=string.gsub(fullkcb, "AnyModifier", " ") if (field==nil or field=="kcb") and string.find(fullkcb, str) then found=true elseif field=="cmd" and bind.cmd and string.find(string.lower(bind.cmd), string.lower(str)) then found=true elseif field=="class" and class==str then found=true end if bind.submap then domap(class, bind.submap, msg..strtable(bind), fullkcb, found) elseif found or show_all then fullmsg=fullmsg..msg..strtable(bind) end end end for class, bindings in pairs(btabl) do domap(class, bindings, class.."\n", "", false) end print(fullmsg) end function cmdsearch(str) bindsearch(str, "cmd") end notion-3+2012042300/contrib/scripts/bookmarks.lua000066400000000000000000000012031174530661200214000ustar00rootroot00000000000000-- -- Bookmarks support for Ion3 -- -- MOD1+b n Go to bookmark n (n=0..9) -- MOD1+b Shift+n Set bookmark n -- local bms={} bookmarks={} function bookmarks.set(bm, frame) bms[bm]=frame end function bookmarks.goto(bm) if bms[bm] then bms[bm]:goto() end end for k=0, 9 do local bm=tostring(k) defbindings("WScreen", { submap(MOD1.."b", { kpress(bm, function() bookmarks.goto(bm) end), }) }) defbindings("WFrame", { submap(MOD1.."b", { kpress("Shift+"..bm, function(frame) bookmarks.set(bm, frame) end), }) }) end notion-3+2012042300/contrib/scripts/cfg_dock2.lua000066400000000000000000000233471174530661200212460ustar00rootroot00000000000000-- -- cfg_dock2.lua (pinko version) -- -- An improved dock module configuration. -- Remove the "2" and save to ~/.ion3, replacing any current -- cfg_dock.lua. The script couldn't be put in a separate file -- because it affects how the dock is created. -- -- Features: -- -- * key bindings for all functions that were in the button3 menu. -- * switch between floating and embedded mode. -- (Doing this restarts ion, so don't use with config files open.) -- * button3 menu includes missing positions and ajusts to dock mode. -- * flip or transpose the dock. -- * bindings to move a floating dock by keyboard. -- * mouse drag on border moves floating dock. -- * move the dock anywhere, not just the sides and corners. -- * attach current window to dock. -- * support multiple docks if there are multiple screens. -- -- Screenshot: http://silenceisdefeat.org/~chv/pics/screenshots/2docks.png -- -- -- Mod1+K D E toggle between floating and embedded modes -- Mod1+K D F flip -- Mod1+K D T transpose -- Mod1+K D R "resize mode" (it will move but not resize) -- Mod1+K D A attach clientwin to dock -- Button1 flip -- Button2 transpose -- ___________ -- |7 8 9| Mod1+K D (number) to move a floating dock. -- | | Same for embedded dock, limited to 1,3,7,9. -- |4 5 6| -- | | -- |1 2 3| -- ----------- -- -- 2005-11-09: -- added this header. -- changed name from "dock" to "dockN", N being a screen id. -- flipped numbered bindings upside down to match keypad. -- multiple screen support -- arbitrary x,y position survives ion restart. -- List IDs of all screens on which a dock should be created: -- i.e., {0,1}, {0,2,4} local use_these_screens={0} docktable=ioncore.read_savefile("dock_settings") if docktable==nil then docktable={} end function get_dock(scr) for i in pairs(scr:llist(2)) do if obj_is(scr:llist(2)[i],"WDock") then return scr:llist(2)[i] end end for i in pairs(ioncore.region_list("WDock")) do if scr==ioncore.region_list("WDock")[i]:screen_of() then return ioncore.region_list("WDock")[i] end end end function reset_dockgeom(scr) ioncore.defer(function() get_dock(scr):rqgeom({x=docktable[scr:id()].x, y=docktable[scr:id()].y}) end) end for i in pairs(use_these_screens) do scr=use_these_screens[1] io.stdout:write(i) io.stdout:write(scr) if docktable[scr]==nil then docktable[scr] = { screen=0, mode="floating", pos="tl", grow="down", floating_hidden=false, x=0, y=0 } end -- Create a dock based on a saved settings file. curdock=mod_dock.create{ -- Dock mode: embedded|floating mode=docktable[scr].mode, -- The screen to create the dock on screen=scr, -- 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=docktable[scr].pos, -- Growth direction: left|right|up|down grow=docktable[scr].grow, -- Whether new dockapps should be added automatically to this dock is_auto=true, -- Show floating dock initially? floating_hidden=docktable[scr].floating_hidden, -- Name of the dock name="dock"..scr, } reset_dockgeom(ioncore.find_screen_id(scr)) end ioncore.write_savefile("dock_settings", docktable) defbindings("WScreen", { bdoc("Toggle floating dock."), kpress(MOD1.."D", "mod_dock.set_floating_shown_on(_, 'toggle')"), }) defbindings("WMPlex", { submap(MOD1.."K", { submap("D", { kpress("A", "get_dock(_:screen_of()):attach(_sub)", "_sub:WClientWin"), }), }), }) defbindings("WScreen", { submap(MOD1.."K", { submap("D", { kpress("KP_7", "get_dock(_):set{pos='tl'}"), kpress("KP_8", "get_dock(_):set{pos='tc'}"), kpress("KP_9", "get_dock(_):set{pos='tr'}"), kpress("KP_4", "get_dock(_):set{pos='ml'}"), kpress("KP_5", "get_dock(_):set{pos='mc'}"), kpress("KP_6", "get_dock(_):set{pos='mr'}"), kpress("KP_1", "get_dock(_):set{pos='bl'}"), kpress("KP_2", "get_dock(_):set{pos='bc'}"), kpress("KP_3", "get_dock(_):set{pos='br'}"), kpress("7", "get_dock(_):set{pos='tl'}"), kpress("8", "get_dock(_):set{pos='tc'}"), kpress("9", "get_dock(_):set{pos='tr'}"), kpress("4", "get_dock(_):set{pos='ml'}"), kpress("5", "get_dock(_):set{pos='mc'}"), kpress("6", "get_dock(_):set{pos='mr'}"), kpress("1", "get_dock(_):set{pos='bl'}"), kpress("2", "get_dock(_):set{pos='bc'}"), kpress("3", "get_dock(_):set{pos='br'}"), kpress("R", "get_dock(_):begin_kbresize()"), kpress("F", "flip_dock_direction(get_dock(_))"), kpress("T", "cycle_dock_direction(get_dock(_))"), kpress("E", "swap_dockmodes(_)"), kpress("G", "reset_dockgeom(_:screen_of())"), }), }), }) -- Dock settings menu. For this to work, mod_menu must have been loaded -- previously. if mod_menu then function dock_settings_menu(dock) if docktable[dock:screen_of():id()].mode=="floating" then return { menuentry("Hide dock", "mod_dock.set_floating_shown_on(".. "_:screen_of(), 'toggle')"), menuentry("Pos-TL", "_:set{pos='tl'}"), menuentry("Pos-TC", "_:set{pos='tc'}"), menuentry("Pos-TR", "_:set{pos='tr'}"), menuentry("Pos-ML", "_:set{pos='ml'}"), menuentry("Pos-MC", "_:set{pos='mc'}"), menuentry("Pos-MR", "_:set{pos='mr'}"), menuentry("Pos-BL", "_:set{pos='bl'}"), menuentry("Pos-BC", "_:set{pos='bc'}"), 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'}"), menuentry("Embed dock", "swap_dockmodes(_:screen_of())"), } else return { 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'}"), menuentry("Float dock", "swap_dockmodes(_:screen_of())"), } end end defbindings("WDock", { mpress("Button3", "mod_menu.pmenu(_, _sub, dock_settings_menu(_))"), }) end -- To use any of the dock's mouse bindings, -- you'll have to find a bit of dead space for clicking in. -- Near the border should always work. -- (This is even necessary for Mod..Button bindings.) defbindings("WDock", { mpress("Button2", "cycle_dock_direction(_)"), mclick("Button1", "flip_dock_direction(_)"), -- You can actually drag a floating dock anywhere. -- Position will be lost when ion restarts. mdrag("Button1", "_:p_move()"), mdrag(MOD1.."Button1", "_:p_move()"), }) function flip_dock_direction(dock) save_dock_settings() local dockgrow = dock:get().grow if dockgrow=="left" then dock:set{grow='right'} elseif dockgrow=="right" then dock:set{grow='left'} elseif dockgrow=="up" then dock:set{grow='down'} elseif dockgrow=="down" then dock:set{grow='up'} end reset_dockgeom(dock:screen_of()) end function cycle_dock_direction(dock) save_dock_settings() local dockget = dock:get() local dockgeom = dock:geom() local screengeom = dock:screen_of():geom() if dockget.grow=="left" then dock:set{grow='up'} if dockgeom.w + dockgeom.y > screengeom.h then dock:rqgeom({x=dockgeom.x, y=screengeom.h - dockgeom.w}) else reset_dockgeom(dock:screen_of()) end elseif dockget.grow=="right" then dock:set{grow='down'} if dockgeom.w + dockgeom.y > screengeom.h then dock:rqgeom({x=dockgeom.x, y=screengeom.h - dockgeom.w}) else reset_dockgeom(dock:screen_of()) end elseif dockget.grow=="up" then dock:set{grow='right'} if dockgeom.x + dockgeom.h > screengeom.w then dock:rqgeom({x=screengeom.w - dockgeom.h, y=dockgeom.y}) else reset_dockgeom(dock:screen_of()) end elseif dockget.grow=="down" then dock:set{grow='left'} if dockgeom.x + dockgeom.h > screengeom.w then dock:rqgeom({x=screengeom.w - dockgeom.h, y=dockgeom.y}) else reset_dockgeom(dock:screen_of()) end end end function save_dock_settings() local dockget, dockgeom --docktable={} for i in pairs(ioncore.region_list('WDock')) do dock=ioncore.region_list('WDock')[i] dockget = dock:get() dockgeom = dock:geom() scr = dock:screen_of():id() if not docktable[scr] then docktable[scr]={} end docktable[scr].screen = scr docktable[scr].x = dockgeom.x docktable[scr].y = dockgeom.y docktable[scr].grow = dockget.grow docktable[scr].floating_hidden = dock:screen_of():l2_is_hidden(dock) if docktable[scr].mode=="embedded" then docktable[scr].pos=string.gsub(string.gsub(dockget.pos, "m", "t"), "c", "l") else docktable[scr].pos = dockget.pos end end ioncore.write_savefile("dock_settings", docktable) end function swap_dockmodes(scr) if docktable[scr:id()].mode=="embedded" then docktable[scr:id()].mode="floating" else docktable[scr:id()].mode="embedded" end save_dock_settings() ioncore.restart() end -- -- This makes sure dock settings are saved any time ion restarts. -- ioncore.get_hook("ioncore_deinit_hook"):add( function() save_dock_settings() end ) notion-3+2012042300/contrib/scripts/closeorkill.lua000066400000000000000000000016101174530661200217340ustar00rootroot00000000000000-- -- closeorkill.lua -- -- This script will first attempt to close a region and, if that fails on -- a client window and this script is called again within MAX_DIFFTIME -- (5 secs) time, kill the application. -- -- To use, change (when using the default bindings) -- kpress_waitrel(DEFAULT_MOD.."C", close_sub_or_self) -- in ioncore-bindings.lua to -- kpress_waitrel(DEFAULT_MOD.."C", close_or_kill_sub_or_self) -- local MAX_DIFFTIME=5 local last_tried, last_tried_time function close_or_kill(reg) local time=os.time() if last_tried==reg and obj_is(reg, "WClientWin") then if os.difftime(time, last_tried_time)<=MAX_DIFFTIME then reg:kill() last_tried=nil return end end last_tried=reg last_tried_time=time reg:close() end function close_or_kill_sub_or_self(reg) close_or_kill(reg:active_sub() or reg) end notion-3+2012042300/contrib/scripts/collapse.lua000066400000000000000000000015201174530661200212140ustar00rootroot00000000000000-- Move all windows on a WTiling to a single frame and destroy the rest. -- (like C-x 1 in Emacs) -- This is the ion3 version. collapse={} function collapse.take_frame_to_here (region, current) if region ~= current then region:managed_i(function (cwin) ioncore.defer(function () current:attach(cwin) end) return true end) ioncore.defer(function () region:rqclose() end) end return true end function collapse.collapse(ws) local current = ws:current() ws:managed_i (function (region) return collapse.take_frame_to_here(region, current) end) current:goto() end notion-3+2012042300/contrib/scripts/ctrl_statusbar.lua000066400000000000000000000167051174530661200224610ustar00rootroot00000000000000-- USAGE: back-up the cfg_statusbar.lua. Rename this one to cfg_statusbar.lua, -- make changes in settings.active and settings.inactive (look below) to meet -- your needs (for farther changes, look into save_statusbar.lua, which will -- be created by this script). Also notice init_settings and configurations. -- Now restart ion3, right-click on the statusbar (or press F11) and see the -- magic :) -- -- Author: Sadrul Habib Chowdhury (imadil at gmail dot com) ctrl_statusbar = {} -- initializing settings for statusbar, as in mod_statusbar.create in cfg_statusbar.lua if not ctrl_statusbar.init_settings then ctrl_statusbar.init_settings = { screen = 0, pos = 'bl', date_format='%a %Y-%b-%d %H:%M', } end -- settings for individual modules, as in mod_statusbar.launch_statusd in cfg_statusbar.lua if not ctrl_statusbar.configurations then ctrl_statusbar.configurations = { -- Load meter --[[ load={ update_interval=10*1000, important_threshold=1.5, critical_threshold=4.0 }, --]] -- Mail meter --[[ mail={ update_interval=60*1000, mbox=os.getenv("MAIL") }, --]] netmon={ --device = "lo", show_avg=1, show_count = 0, --avg_sec = 60, --interval = 1*1000 } } end if not ctrl_statusbar.settings then ctrl_statusbar.settings = { seperator = '||', opening = "[ ", closing = " ]", -- whatever you add in active will be put in a single string -- one after the other in the same order, and that's what you'll -- see in the statusbar. Also, take a look into save_statusbar.lua active = { "%date", "eth0: % %>netmon", "%schedule", }, -- if there are monitors which you may want to use only occasionally, -- then put them in here inactive = { "uptime: %uptime", "%rss", }, screen = 0, save_filename = "save_statusbar.lua", -- where are the settings saved? auto_save = 1, -- should the changes be saved automatically? } end -- Construct the template for the statusbar function ctrl_statusbar.get_template() local st = ctrl_statusbar.settings.opening for _, s in pairs(ctrl_statusbar.settings.active) do if _ > 1 then st = st .. " " .. ctrl_statusbar.settings.seperator .. " " .. s else st = st .. s end end st = st .. ctrl_statusbar.settings.closing return st end -- Refresh the statusbar local function refresh_statusd() local st = ctrl_statusbar.get_template() local tbl = mod_statusbar.template_to_table(st) ctrl_statusbar.sb:set_template_table(tbl) if ctrl_statusbar.settings.auto_save == 1 then ctrl_statusbar.save_template() end end -- Add a new module function ctrl_statusbar.add_module(mp, cmd) table.insert(ctrl_statusbar.settings.active, cmd) refresh_statusd() end -- Delete a module completely (Possible only for disabled modules) function ctrl_statusbar.delete_module(ind) table.remove(ctrl_statusbar.settings.inactive, ind) end -- Change positions of two monitors function ctrl_statusbar.exchange(from, to) table.insert(ctrl_statusbar.settings.active, to, table.remove(ctrl_statusbar.settings.active, from)) refresh_statusd() end -- Move a monitor from active to inactive function ctrl_statusbar.disable_active(_) table.insert(ctrl_statusbar.settings.inactive, table.remove(ctrl_statusbar.settings.active, _)) refresh_statusd() end -- Move a monitor from inactive to active function ctrl_statusbar.insert_active(_, ind) if ind == -1 then table.insert(ctrl_statusbar.settings.active, table.remove(ctrl_statusbar.settings.inactive, _)) else table.insert(ctrl_statusbar.settings.active, ind, table.remove(ctrl_statusbar.settings.inactive, _)) end refresh_statusd() end -- Save the templates function ctrl_statusbar.save_template() local t = ioncore.get_paths() local f = io.open(t.userdir .. "/" .. ctrl_statusbar.settings.save_filename, "w") if not f then error(TR("couldn't save.")) return end local sv = "ctrl_statusbar.save = {\nactive = {\n" for _, s in pairs(ctrl_statusbar.settings.active) do sv = sv .. '"' .. s .. '",\n' end sv = sv .. "},\ninactive = {\n" for _, s in pairs(ctrl_statusbar.settings.inactive) do sv = sv .. '"' .. s .. '",\n' end sv = sv .. "}\n}" f:write(sv) f:close() end -- Create and initialize the statusbar function ctrl_statusbar.init() dopath(ctrl_statusbar.settings.save_filename, true) -- if anything is saved, read them if ctrl_statusbar.save then ctrl_statusbar.settings.active = ctrl_statusbar.save.active ctrl_statusbar.settings.inactive = ctrl_statusbar.save.inactive end ctrl_statusbar.init_settings.template = ctrl_statusbar.get_template() -- this is a very ugly hack to make sure all the statusd_ scripts get loaded for _, v in pairs(ctrl_statusbar.settings.inactive) do ctrl_statusbar.init_settings.template = ctrl_statusbar.init_settings.template .. " " .. v end ctrl_statusbar.sb = mod_statusbar.create(ctrl_statusbar.init_settings) mod_statusbar.launch_statusd(ctrl_statusbar.configurations) refresh_statusd() end -- Get just the name of a monitor local function get_monitor_name(s) s = string.gsub(s, '%% ', '') -- remove the "% " from the template s = string.gsub(s, '^.-%%[<>|]?0*[0-9]*(%w+).-$', '%1') return s end -- pardon the obfuscation, i don't know why i did that function ctrl_statusbar.show_list(mplex) ret = {} for _, s in pairs(ctrl_statusbar.settings.active) do s = get_monitor_name(s) sub = {} for __, t in pairs(ctrl_statusbar.settings.active) do if _ ~= __ then t = get_monitor_name(t) if _ < __ then t = "Move after " .. t else t = "Move before " .. t end table.insert(sub, menuentry(t, "ctrl_statusbar.exchange(\"" .. _ .. "\", \"" .. __ .. "\")")) end end table.insert(sub, menuentry("Disable " ..s, "ctrl_statusbar.disable_active(\"" .. _ .. "\")")) table.insert(ret, submenu(s, sub)) end for _, s in pairs(ctrl_statusbar.settings.inactive) do s = get_monitor_name(s) sub = {} table.insert(sub, menuentry("Insert " ..s.." at the beginning", "ctrl_statusbar.insert_active(\"" .. _ .. "\", \"1\")")) for __, t in pairs(ctrl_statusbar.settings.active) do t = get_monitor_name(t) table.insert(sub, menuentry("Insert after " .. t, "ctrl_statusbar.insert_active(\"" .. _ .. "\", \"" .. (__+1) .. "\")")) end table.insert(sub, menuentry("Delete " ..s, "ctrl_statusbar.delete_module(\"" .. _ .. "\")")) table.insert(ret, submenu('! ' .. s, sub)) end table.insert(ret, menuentry("Add a new Template", "mod_query.query(_, TR('New template'), nil, ctrl_statusbar.add_module, function () end, 'ctrl_statusbar')")) table.insert(ret, menuentry("Save template", ctrl_statusbar.save_template)) return ret end ctrl_statusbar.init() ioncore.defbindings("WScreen", { kpress(MOD1.."V", "mod_menu.bigmenu(_, _sub, ctrl_statusbar.show_list)") }) ioncore.defbindings("WStatusBar", { mpress("Button3", "mod_menu.pmenu(_, _sub, ctrl_statusbar.show_list)"), }) notion-3+2012042300/contrib/scripts/cwin_sp.lua000066400000000000000000000101721174530661200210570ustar00rootroot00000000000000-- Creates scratchpads on a per-frame/per-clientwin basis. -- I realized that sometimes it might be useful to have a scratchpad for -- notes and things for only one frame/cwin, and not globally. -- This script creates two bindings: -- CTRL-"space" to create/toggle per-frame scratchpads -- and -- CTRL-META-"space" to create/toggle per-cwin scratchpads -- The per-frame sps are smarter and won't stay showing across desktops, the -- per-cwin sps are dumber and will. local cwinsps = nil local cwinsps_rev = nil local framesps = nil local framesps_rev = nil --{{{ load_sps local function load_sps() local cwint = ioncore.read_savefile("cwin_sps") local framet = ioncore.read_savefile("frame_sps") --{{{ Tuomo does this in check_and_create in mod_sp so I can do it here local hook hook = ioncore.get_hook("ioncore_post_layout_setup_hook") if hook then hook:remove(load_sps) end --}}} -- Initialize these here so I can test for nil to see if I've done my startup stuff cwinsps = setmetatable({}, {__mode="kv"}) cwinsps_rev = setmetatable({}, {__mode="kv"}) framesps = setmetatable({}, {__mode="kv"}) framesps_rev = setmetatable({}, {__mode="kv"}) if cwint then for k,v in pairs(cwint) do local reg, sp reg = ioncore.lookup_region(k) sp = ioncore.lookup_region(v) if reg and sp then cwinsps[reg] = sp cwinsps_rev[sp] = reg end end end if framet then for k,v in pairs(framet) do local reg, sp reg = ioncore.lookup_region(k) sp = ioncore.lookup_region(v) if reg and sp then framesps[reg] = sp framesps_rev[sp] = reg end end end end --}}} load_sps --{{{ save_sps local function save_sps() local t = {} for k,v in pairs(cwinsps) do if obj_exists(k) and obj_exists(v) then t[k:name()] = v:name() end end ioncore.write_savefile("cwin_sps", t) t = {} for k,v in pairs(framesps) do if obj_exists(k) and obj_exists(v) then t[k:name()] = v:name() end end ioncore.write_savefile("frame_sps", t) end --}}} save_sps --{{{ toggle_cwin_sp function toggle_cwin_sp(parent, cwin) local g = {} local cg = {} local pg = {} local sp = nil if not cwinsps then load_sps() end if cwin then sp = cwinsps[cwin] pg = parent:geom() cg = cwin:geom() g.x = pg.x + cg.x g.y = pg.y + cg.y g.w = cg.w g.h = cg.h end if not sp or not obj_exists(sp) then local sp_rev = cwinsps_rev[parent] if sp_rev then mod_sp.set_shown(parent, 'toggle') else local scr = cwin:screen_of() local name = cwin:name().."_cwin_sp" sp = scr:attach_new({type = 'WFrame', layer = 2, name = name, sizepolicy = 3, frame_style = 'frame-scratchpad', geom = g}) cwinsps[cwin] = sp cwinsps_rev[sp] = cwin end else mod_sp.set_shown(sp, 'toggle') end end --}}} toggle_cwin_sp --{{{ toggle_frame_sp function toggle_frame_sp(frame) local sp = nil if not framesps then load_sps() end sp = framesps[frame] if (not sp or not obj_exists(sp)) then local sp_rev = framesps_rev[frame] if sp_rev then mod_sp.set_shown(frame, 'toggle') else local name = frame:name().."_frame_sp" sp = frame:attach_new({type = 'WFrame', layer = 2, name = name, sizepolicy = 3, frame_style = 'frame-scratchpad'}) framesps[frame] = sp framesps_rev[sp] = frame end else mod_sp.set_shown(sp, 'toggle') end end --}}} toggle_frame_sp --{{{ Init local function setup_hooks() local hook hook = ioncore.get_hook("ioncore_post_layout_setup_hook") if hook then hook:add(load_sps) end hook = ioncore.get_hook("ioncore_snapshot_hook") if hook then hook:add(save_sps) end end --}}} Init --{{{ Bindings defbindings("WFrame", { -- kpress(CTRL.."space", "toggle_frame_sp(_)"), -- kpress(CTRL..META.."space", "toggle_cwin_sp(_, _sub)"), kpress("Control+space", "toggle_frame_sp(_)"), kpress(META.."Control+space", "toggle_cwin_sp(_, _sub)"), }) --}}} setup_hooks() -- vim:foldmethod=marker notion-3+2012042300/contrib/scripts/document_menus.lua000066400000000000000000000077541174530661200224560ustar00rootroot00000000000000-- document_menus.lua -- -- Canaan Hadley-Voth -- -- Navigate the filesystem by way of ion menu. -- -- One way to implement this is to define a menu containing useful folders, -- and use that as a starting point for binding to (see below). -- -- 2005-12-20: -- * Added a second possible action. Define docmenus.exec and exec2 below. -- Selecting an item normally (Return or right arrow) will use exec, -- Control+Return will use exec2. -- * Script no longer puts a temp file in the session dir. -- * Better handling of filenames with single and double quotes. defmenu("useful folders", { -- Define commonly used locations here. -- Or skip this defmenu and put something like the following under mainmenu... submenu("/", function() return docmenus.getfiletable("/") end, {noautoexpand=true}), submenu("~", function() return docmenus.getfiletable(os.getenv("HOME")) end, {noautoexpand=true}), submenu(".ion3/", function() return docmenus.getfiletable(ioncore.get_paths().userdir) end, {noautoexpand=true}), -- Noautoexpand makes mod_query.query_menu skip this menu's expansion, which -- would probably have crashed ion. (Though using document_menus to -- expand filenames in a query is kind of like using wine to run firefox. -- mod_query.query_editfile is sufficient for that.) }) defbindings("WMPlex", { kpress(MOD1.."minus", "mod_menu.menu(_, _sub, 'useful folders')"), }) defbindings("WScreen", { submap(MOD1.."K", { kpress("minus", "docmenus.toggle_dotfiles()"), }), }) defbindings("WMenu", { kpress("Control+Return", "docmenus.finish2(_)"), }) -- This script creates menus far larger than Ion would -- normally handle. The default scroll behavior is going to seem -- inadequate, and this is how you would speed it up: --mod_menu.set({ scroll_delay=5, scroll_amount=6 }) if not docmenus then -- Specify something other than run-mailcap if necessary. docmenus={ show_dotfiles = true, exec = "run-mailcap --action=edit", exec2 = "xterm -e vi", } end function docmenus.getfiletable(dirpath) local filetable = {} local menufile local safedirpath -- os shell is used for the menu's directory listing. -- -- lscommand can be customized, to a point. -- Scrap the -L to keep from following symbolic links. -- Just don't remove -p. local lscommand = "ls -Lp" if docmenus.show_dotfiles then lscommand = lscommand.." -a" end if string.sub(dirpath, -1) ~= "/" then dirpath = dirpath.."/" end safedirpath = string.gsub(dirpath, "\\", "\\\\") safedirpath = string.gsub(safedirpath, "\"", "\\\"") local ls = assert(io.popen(lscommand.." \""..safedirpath.."\"", "r")) for menufile in pairs(ls:lines()) do if menufile == "./" then menufile = "." end if string.sub(menufile, 3) == "ls:" then -- ls error message on broken link. skip it elseif menufile == "../" then -- I'd prefer not to show parent directory as a folder. elseif string.sub(menufile, -1) == "/" then table.insert(filetable, docmenus.subdir(menufile, dirpath..menufile)) else table.insert(filetable, docmenus.file(menufile, dirpath..menufile)) end end ls:close() return filetable end function docmenus.file(menustr,path) path = string.shell_safe(path) path = string.gsub(path, "\\", "\\\\") path = string.gsub(path, "\"", "\\\"") return menuentry(menustr, "docmenus.doexec(_, \""..path.."\")" ) end function docmenus.doexec(mplex, path) local cmd if docmenus.use_exec2 then cmd = docmenus.exec2.." "..path else cmd = docmenus.exec.." "..path end ioncore.exec_on(mplex, cmd) docmenus.use_exec2 = false end function docmenus.subdir(menustr,path) return submenu(menustr, function() return docmenus.getfiletable(path) end, {noautoexpand=true}) end function docmenus.toggle_dotfiles() docmenus.show_dotfiles=not docmenus.show_dotfiles end function docmenus.finish2(wmenu) docmenus.use_exec2 = true wmenu:finish() end notion-3+2012042300/contrib/scripts/enumerate.lua000066400000000000000000000033511174530661200214030ustar00rootroot00000000000000-- enumerate.lua: prepends "X. " in front of the title of a client-window so -- that it's easier to quickly switch between windows using -- mod1+n -- -- Notes: May require the latest release -- -- Author: Sadrul Habib Chowdhury (Adil) -- slightly modified by René van Bevern local hook = nil enumerate = {} function enumerate.update_client(win) local frm = win:manager() if not obj_is(frm, "WFrame") then return end local name = win:name() -- need to detect whether name already has an "X. " at the beginning -- if there is one, get rid of it s, __, n = string.find(name, "%d+%. (.*)") if s == 1 then name = n end name = (frm:get_index(win) + 1) .. ". " .. name print("setting name: " .. name) win:set_name(name) end function enumerate.update() local list = ioncore.clientwin_list() if not list then return end for _, v in pairs(list) do enumerate.update_client(v) end end function enumerate.defer(reg, ev) if obj_is(reg, "WClientWin") then if ev then local atom = ioncore.x_get_atom_name("", ev) if atom ~= "_NET_WM_NAME" and atom ~= "WM_NAME" then return end ioncore.defer(enumerate.update_client(reg)) return end ioncore.defer(enumerate.update) end end function enumerate.init() local events = { "region_activated_hook", "clientwin_property_change_hook", } for _, name in pairs(events) do hook = ioncore.get_hook(name) if hook then hook:add(enumerate.defer) end end enumerate.update() end enumerate.init() notion-3+2012042300/contrib/scripts/environment_placement_hook.lua000066400000000000000000000036121174530661200250320ustar00rootroot00000000000000-- -- environment_placement_hook.lua -- -- Linux-only placement hook which detects the presence of an ION_USE_WS -- environment variable in the processes of new windows, and uses that to -- determine where to place them. -- -- ION_USE_WS should be exported in the same terminal the app is started -- from, or something like this could be typed at the "Run:" prompt: -- -- export ION_USE_WS="framename" && firefox -- -- Or simply use the run_here query provided to "Run (in current frame):" -- -- Note: there are a lot of apps that this will not work with. -- defbindings("WMPlex", { kpress("Shift+F3", "run_here(_)") }) local pid_prop_atom = ioncore.x_intern_atom("_NET_WM_PID", false) local function pidof(w) return ioncore.x_get_window_property(w:xid(), pid_prop_atom, 0, 0, true)[1] end local function get_environment_of(pid) local file = io.open("/proc/"..tostring(pid).."/environ") local envstr = string.format("%q", file:read("*a")) file:close() local rv = {} for k, v in string.gfind(envstr, "(.-)=(.-)\\000") do rv[k] = v end return rv end local function environ_placement_hook(w, t) if t.tfor then return false end -- ignore transients with this PID local pid = tostring(pidof(w)) local env = get_environment_of(pid) local ws = env.ION_USE_WS if not ws then return false end ws = ioncore.lookup_region(ws) if not ws then return false end ws:attach(w, {switchto=true}) return true end hook = ioncore.get_hook("clientwin_do_manage_alt") if hook then hook:add(environ_placement_hook) end -- TODO: attempt to handle ":" for terminal apps. local function qhandler(mplex, str) mod_query.exec_handler(mplex, "export ION_USE_WS=".. string.shell_safe(mplex:name()).." && "..str) end function run_here(mplex) mod_query.query(mplex, "Run (in current frame):", nil, qhandler, mod_query.exec_completor, "run") end notion-3+2012042300/contrib/scripts/exec_show.lua000066400000000000000000000027341174530661200214060ustar00rootroot00000000000000-- exec_show.lua: executes a command and displays the result -- -- this script is meant to be used when the output for the command -- doesn't have more than a screenful of lines (approx 30-35 lines). -- eg. tail, head, grep, ps ... etc. -- -- CAUTION -- DO NOT use this to execute something like xterm, vi or anything -- similar (use the given key-bindings like MOD2..F2/F3/F4/F5 for those). -- -- do let me know if this doesn't work for someone. -- -- Author -- Sadrul Habib Chowdhury (Adil) -- imadil at gmail dot com function show_result(mp, msg) mod_query.message(mp, msg) end function my_exec_handler(mp, cmd) -- not defer-ing was causing some probs ioncore.defer( function () local f = io.popen(cmd, 'r') if not f then show_result(mp, 'error executing command: ' .. cmd) return end local s = f:read('*a') if s then show_result(mp, s) else show_result(mp, 'no output') end f:close() -- remove this, and watch your life get ruined (thanx Tuomo) end ) end function exec_and_show(mplex) mod_query.query(mplex, TR("Exec and display:"), nil, my_exec_handler, mod_query.exec_completor, "execdisplay") end defbindings("WMPlex", { bdoc("Execute a command and show the result."), kpress(MOD1.."F4", "exec_and_show(_)"), -- change the binding to your liking }) notion-3+2012042300/contrib/scripts/float-sb.lua000066400000000000000000000006601174530661200211250ustar00rootroot00000000000000-- -- Example of floating toggleable statusbar -- local floatsbscr=ioncore.find_screen_id(0) local floatsb floatsb=floatsbscr:attach_new{ type="WStatusBar", unnumbered=true, sizepolicy='northwest', template='/ %date /', passive=true, level=2 } local function toggle_floatsb() floatsbscr:set_hidden(floatsb, 'toggle') end ioncore.defbindings("WScreen", { kpress(META.."D", toggle_floatsb) }) notion-3+2012042300/contrib/scripts/frame_client_menu.lua000066400000000000000000000034301174530661200230700ustar00rootroot00000000000000--[[ Author: Etan Reisner Email: deryni@gmail.com Summary: Adds a submenu to the frame context menu which contains the cwins in that frame. Version: 0.1 Last Updated: 2007-01-29 Copyright (c) Etan Reisner 2007 --]] -- Usage: -- This will show the frame context query menu with the added submenu. -- kpress(META.."M", "clientlist_menu(_, _sub)") -- This will show the frame context popup menu with the added submenu. -- kpress(META.."M", "clientlist_menu(_, _sub, true)") -- -- Use frame_client_menu_set({start_on_current=true/false}) to control whether -- the popup submenu will start on the current frame or index 1. local settings = {start_on_current = false} function frame_client_menu_set(tab) settings.start_on_current = tab.start_on_current end function frame_client_menu_get() return table.copy(settings) end function clientlist_menu(frame, sub, popup) local start_on_current = false if mod_menu then local framemenu = ioncore.getmenu("ctxmenu")(frame, sub) local myframemenu = {} local initial = 1 frame:mx_i(function(reg) if reg == sub then initial = #myframemenu + 1 end myframemenu[#myframemenu + 1] = ioncore.menuentry(reg:current():name(), function() reg:goto() end) return true end) if not settings.start_on_current then initial = 1 end framemenu[#framemenu + 1] = ioncore.submenu("Client windows", myframemenu, {initial=initial}) if popup then mod_menu.menu(frame, sub, framemenu) else mod_query.query_menu(frame, framemenu, "Context menu:") end end end -- vim: set expandtab sw=4: notion-3+2012042300/contrib/scripts/go_frame_or_desk.lua000066400000000000000000000032101174530661200226750ustar00rootroot00000000000000-- Goes to the frame in the specified direction. If there is no frame in the -- given direction, it goes to the next workspace in the direction, being: -- left = previous workspace -- right = next workspace -- -- By Rene van Bevern , 2005 -- Public Domain -- -- If you are about to go to a frame that would be left to the leftmost frame, -- the function switches to a previous workspace and goes to its rightmost frame. -- If you are about to go to a frame that would be right of the rightmost frame, -- the function switches to the next workspace and goes to its leftmost frame. -- -- To use this function you need to bind keys for WIonWS, for exmaple: -- --defbindings("WIonWS", { -- kpress(MOD1.."Up", "go_frame_or_desk_up(_)"), -- kpress(MOD1.."Down", "go_frame_or_desk_down(_)"), -- kpress(MOD1.."Right", "go_frame_or_desk_right(_)"), -- kpress(MOD1.."Left", "go_frame_or_desk_left(_)"), --}) function go_frame_or_desk(workspace, direction) local region = workspace:current() local screen = workspace:screen_of() if workspace:nextto(region,direction,false) then workspace:goto_dir(direction) elseif direction == "left" then screen:switch_prev() screen:current():farthest("right"):goto() elseif direction == "right" then screen:switch_next() screen:current():farthest("left"):goto() end end function go_frame_or_desk_left(reg) go_frame_or_desk(reg, "left") end function go_frame_or_desk_right(reg) go_frame_or_desk(reg, "right") end function go_frame_or_desk_up(reg) go_frame_or_desk(reg, "up") end function go_frame_or_desk_down(reg) go_frame_or_desk(reg, "down") end notion-3+2012042300/contrib/scripts/goto_multihead.lua000066400000000000000000000016021174530661200224170ustar00rootroot00000000000000-- A version of ioncore.goto_next that may be useful on multihead setups -- Replace ioncore.goto_next with goto_multihead.goto_next in cfg_tiling.lua -- to use it. goto_multihead={} function goto_multihead.goto_next(ws, dir) if dir=="up" or dir=="down" then ioncore.goto_next(ws:current(), dir) return end local nxt, nxtscr nxt=ioncore.navi_next(ws:current(), dir, {nowrap=true}) if not nxt then local otherdir local fid=ioncore.find_screen_id if dir=="right" then otherdir="left" nxtscr=fid(ws:screen_of():id()+1) or fid(0) else otherdir="right" nxtscr=fid(ws:screen_of():id()-1) or fid(-1) end nxt=nxtscr:current():current() if obj_is(nxt, "WTiling") then nxt=nxt:farthest(otherdir) end end nxt:goto() end notion-3+2012042300/contrib/scripts/heuristics.lua000066400000000000000000000131131174530661200215750ustar00rootroot00000000000000-- -- Window placement heuristics for WIonWS:s -- -- (c) Tuomo Valkonen 2003. -- -- -- In calculating penalties, the area of client window not overlapped by a -- frame is multiplied by -- heuristics.shrink_penalty, -- the area of frame not overlapped by a client window is multiplied by -- heuristics.splurge_penalty, -- and the distance from the center of current frame to the frame under -- consideration is multiplied by -- heuristics.dist_penalty. -- If there is no current frame, this value is taken to be zero. These three -- values are summed to obtain the final penalty and the frame with the -- smallest penalty is chosen. -- -- To disable heuristics for a particular window (class), set -- ignore_heuristics -- in a matching winprop. -- -- To use this code to place windows with user given position in -- the frame they mostly overlap, set -- heuristics.userpos_by_overlap=true. -- and, to disable other heuristics , -- heuristics.only_overlap=true. -- if not heuristics then heuristics = { shrink_penalty=20, splurge_penalty=1, dist_penalty=1500, userpos_by_overlap=true, only_overlap=false, } end -- Some helper functions {{ -- Returns an iterator that goes through the list L returning two values -- at a time with step 1 between calls. local function gettwo(L) return coroutine.wrap(function() for i=1,table.getn(L)-1 do coroutine.yield(L[i], L[i+1]) end end) end local function l2metric(a, b) return math.sqrt((a.x-b.x)^2+(a.y-b.y)^2) end local function geom_center(g) return {x=g.x+g.w/2, y=g.y+g.h/2} end local function geom2rect(geom) return {x1=geom.x, y1=geom.y, x2=geom.x+geom.w, y2=geom.y+geom.h} end local function geom2rect0(geom) return {x1=0, y1=0, x2=geom.w, y2=geom.h} end local function rect_within(C, R) return (R.x1>=C.x1 and R.x2<=C.x2 and R.y1>=C.y1 and R.y2<=C.y2) end local function rect_area(A) return (A.x2-A.x1)*(A.y2-A.y1) end -- }}} -- Penalty calculation {{ -- Divide the (rectangular) area spanned by two rectangles into nine smaller -- rectangles so that each of these smaller rectangles either are fully -- overlapped or not overlapped at all by each of the original rectangles -- $A$ and $B$. For example: -- \begin{verbatim} -- AAAA 11123 -- AAABB 44456 -- AAABB => 44456 -- BB 77789 -- \end{verbatim} local function divide_rectangles(A, B) local rects={} local X={A.x1, A.x2, B.x1, B.x2} local Y={A.y1, A.y2, B.y1, B.y2} table.sort(X) table.sort(Y) for x1, x2 in gettwo(X) do for y1, y2 in gettwo(Y) do local R={x1=x1, x2=x2, y1=y1, y2=y2} if rect_within(A, R) then R.a=1 else R.a=0 end if rect_within(B, R) then R.b=1 else R.b=0 end table.insert(rects, R) end end return rects end -- Calculate $\int w \circ (\chi_A-\chi_B) d\lambda$ for rectangles $A$ -- and $B$ and some $w$ with domain $\{-1, 0, 1\}$. local function rect_penalty(A, B, w) local p=0 local rects=divide_rectangles(A, B) for _, R in rects do p=p+rect_area(R)*w[R.a-R.b] end return p end -- Calculate the penalty for placing \var{cwin} in \var{frame} -- current frame's (if any) center being \var{center}.n -- The penalty is given by -- \[ -- d(A, B)+\text{dist penalty}*d_2(\text{center}, \text{center of frame}) -- \] -- where $d$ is given by the above function with -- \[ -- w(x)=\begin{cases} -- \text{shrink penalty}, & x=-1, \\ -- 0, x=0, \\ -- \text{splurge penalty}, & x=1. \\ -- \end{cases} -- \] -- If \var{userpos} is set, then the geometries of \var{frame} and \var{cwin} -- are used for $A$ and $B$, respectively. Otherwise $A$ and $B$ are -- rectangles with $(x, y)=(0, 0)$ and width and height given by \var{frame}'s -- and \var{cwin}'s geometries. function heuristics.penalty(frame, cwin, userpos, center) local w={ [-1] = heuristics.shrink_penalty, [0] = 0, [1] = heuristics.splurge_penalty } local p=rect_penalty(geom2rect0(frame:geom()), geom2rect0(cwin:geom()), w) if center then local c2=geom_center(frame:geom()) p=p+l2metric(center, c2)*heuristics.dist_penalty end return p end -- Calculate the area of \var{cwin} not overlapped by \var{frame}. function heuristics.nonoverlap_penalty(frame, cwin) local w={[-1] = 1, [0] = 0, [1] = 0} return rect_penalty(geom2rect(frame:geom()), geom2rect(cwin:geom()), w) end -- }}} -- Interface {{{ function heuristics.get_frame(ws, cwin, userpos) if not userpos and heuristics.only_overlap then return nil end local wp=get_winprop(cwin) if wp and wp.ignore_heuristics then return nil end local frames=ws:managed_list() local current=ws:current() local minpen, minframe, pen, center if current then local g=current:geom() center=geom_center(current:geom()) end for _, frame in frames do if userpos and heuristics.userpos_by_overlap then pen=heuristics.nonoverlap_penalty(frame, cwin) else pen=heuristics.penalty(frame, cwin, userpos, center) end if not minframe or pen=1 then table.insert(res, str) end end return (#res>0 and res) or res2 end function history_completor(wedln) wedln:set_completions(complhist(wedln:contents())) end defbindings("WEdln", { kpress("Shift+Tab", history_completor), }) notion-3+2012042300/contrib/scripts/lock_frame.lua000066400000000000000000000040271174530661200215210ustar00rootroot00000000000000--[[ Author: James Gray, Etan Reisner Email: james at solitaryfield dot org, deryni@unreliablesource.net IRC: yaarg, deryni Summary: Allows locking Version: 0.2 Last Updated: Fri Jun 22 00:59:06 EDT 2007 --]] -- Usage: -- F11 toggles the lock of keyboard close and resize on the currently active -- frame. -- META-F11 will save the name of the frame and lock it again in subsequent -- sessions of ion. -- -- You will need to rebind your close and kill bindings to use the -- check_before_close and check_before_kill functions for this script to do -- anything. -- -- Adding -- de.substyle("*-*-*-locked", { -- background_colour = "yellow", -- }), -- -- de.substyle("*-*-*-*-locked_saved", { -- background_colour = "white", -- }), -- to your theme's de.defstyle("tab-frame", {...}) style will cause locked and -- locked+saved frames to be highlighted. local locks = setmetatable({}, {__mode="k"}) local saved = {} function lock_frame(frame, save) local name = frame:name() if locks[frame] then locks[frame] = nil frame:set_grattr("locked", "unset") if save and name then frame:set_grattr("locked_saved", "unset") saved[name] = nil end else locks[frame] = true frame:set_grattr("locked", "set") if save and name then frame:set_grattr("locked_saved", "set") saved[name] = true end end end function check_before_kill(reg) if not locks[reg] then WClientWin.kill(reg) end end function check_before_close(reg, sub) if (not locks[reg]) and (not locks[sub]) then WRegion.rqclose_propagate(reg, sub) end end ioncore.defbindings("WFrame",{ kpress("F11", "lock_frame(_)"), kpress(META.."F11", "lock_frame(_, true)") }) function save_locked() ioncore.write_savefile("saved_lock_frame", saved) end function load_locked() local locked = ioncore.read_savefile("saved_lock_frame") or {} for k,v in pairs(locked) do local reg = ioncore.lookup_region(k) lock_frame(reg, true) end end local hook = ioncore.get_hook("ioncore_deinit_hook") if hook then hook:add(save_locked) end hook = nil load_locked() notion-3+2012042300/contrib/scripts/min_tabs.lua000066400000000000000000000060471174530661200212170ustar00rootroot00000000000000--[[ min_tabs.lua lua code to auto show/hide tabs as the number of windows in an ion frame changes from/to 1 this affects windows on WIonWS and WPaneWS workspaces but leave windows on WFloatWS's alone Originally created by David Tweed 22 Feb 2007 John Schreiner: ignore transients to prevent empty frames 14 Dec 2006 David Roundy: modified to work with latest ion3 11 Feb 2005 David Tweed: modified to work with ion3 svn versions 15 Feb 2005 Cas Cremers: only frames with one window hide the tabbar (as opposed to <= 2) 01 Sep 2005 Cas Cremers: single client windows that are tagged still show the tabbar. - Easier to work with using a style with slightly wider frame borders and more vivid "active frame" colours. - The keybinding META..T is defined at the end of the file: this should override the normal binging, thus min_tabs should be loaded *after* the normal definition of META..T. It is not appropriate to have key bindings hard-coded in extensions, but I don't currently know of a better way. - Currently supports ion3 with old toggling as well as new toggling function conventions; this can be removed later on. one way to enable this is by adding dopath("min_tabs") to cfg_ion.lua ]] function show_only_necessary_tabs_in_frame(fp) if WFrame.mode(fp) == 'floating' then -- Escape: floatws thing should not be handled return end -- First the logic, then the propagation back to ion -- It should *not* be shown if there is only one app -- However, this (single) app should not be tagged, -- because then we would want to show it. -- Assume the tabbar should be shown. local show_bar = true if WMPlex.mx_count(fp) == 1 then local rg = fp:mx_nth(0) if not rg:is_tagged() then show_bar = false end end -- Propagate choice ioncore.defer(function() -- don't touch transient frames if fp:mode() ~= "transient" then if show_bar then fp:set_mode("tiled") else fp:set_mode("tiled-alt") end end end) end function show_only_necessary_tabs_in_frame_wrapper(ftable) show_only_necessary_tabs_in_frame(ftable.reg) end function min_tabs_setup_hook() local hk=ioncore.get_hook("frame_managed_changed_hook") hk:add(show_only_necessary_tabs_in_frame_wrapper) end function min_tabs_tag_wrapper(fr,reg) -- Note the ugly code: this actually caters for two versions of ion3 -- I am only including this because I like Ubuntu [CC] local oldversion = (reg["toggle_tag"] ~= nil) if oldversion then -- old version (for Ubuntu, can be removed later on) reg:toggle_tag() else -- new version reg:set_tagged("toggle") end -- recompute tabbar state show_only_necessary_tabs_in_frame(fr) end --[[ Special keybinding override for this extension ]] defbindings("WMPlex.toplevel", { bdoc("Tag current object within the frame."), kpress(META.."T", "min_tabs_tag_wrapper(_,_sub)", "_sub:non-nil"), }) min_tabs_setup_hook() notion-3+2012042300/contrib/scripts/move_current.lua000066400000000000000000000012471174530661200221300ustar00rootroot00000000000000-- Move current window in a frame to another frame in specified direction move_current={} function move_current.move(ws, dir) local frame=ws:current() local cwin=frame:current() local frame2=ioncore.navi_next(frame,dir) if frame2 then frame2:attach(cwin, { switchto=true }) end cwin:goto() end defbindings("WTiling", { submap("Mod1+K", { kpress("Up", function(ws) move_current.move(ws, "up") end), kpress("Down", function(ws) move_current.move(ws, "down") end), kpress("Left", function(ws) move_current.move(ws, "left") end), kpress("Right", function(ws) move_current.move(ws, "right") end), }), }) notion-3+2012042300/contrib/scripts/mp.lua000066400000000000000000000010421174530661200200250ustar00rootroot00000000000000-- Mark next mapped window to be attached to a specified object local marked function mark_for_attach(frame) marked=frame end local function copy(t) local ct={} for k, v in t do ct[k]=v end return ct end local orig_get_winprop=get_winprop local function marked_get_winprop(cwin) local props=orig_get_winprop(cwin) if not marked then return props end local newprops=copy(props or {}) newprops.target=marked:name() marked=nil return newprops end get_winprop=marked_get_winprop notion-3+2012042300/contrib/scripts/mpd.lua000066400000000000000000000077271174530661200202110ustar00rootroot00000000000000-- Small interface to MusicPD -- -- Author: Steve Jothen -- -- Requires netcat -- -- Change your path/settings according to your setup -- -- defbindings("WScreen", { -- kpress("KP_6", "MusicPD.next()"), -- next song -- kpress("KP_4", "MusicPD.previous()"), -- previous song -- kpress("KP_2", "MusicPD.volume_down()"), -- volume down settings.volume_delta units -- kpress("KP_8", "MusicPD.volume_up()"), -- volume up .... -- kpress("KP_5", "MusicPD.toggle_play()") -- toggle play/pause/stop -- }) local netcat = "/bin/netcat" local settings = { hostname = "localhost", password = nil, port = 6600, volume_delta = 10 } MusicPD = {} function MusicPD.file_exists() if io.open(netcat, "r") then -- check to see if file exists return true else return false end end -- creates the appropriate string to call with io.popen -- function MusicPD.create_command(command) local arguments = nil if settings.password then arguments = string.format( "echo -n \"password %s\n%s\nclose\n\" | %s %s %d", settings.password, command, netcat, settings.hostname, settings.port) else arguments = string.format( "echo -n \"%s\nclose\n\" | %s %s %d", command, netcat, settings.hostname, settings.port) end return arguments end -- calls the command and returns table of key, value pairs -- function MusicPD.call_command(command) local arg_cmd = MusicPD.create_command(command) local values = {} if MusicPD.file_exists() then local handle = io.popen(arg_cmd, "r") local line = handle:read("*l") while line do local _, _, key, value = string.find(line, "(.+):%s(.+)") if key then values[string.lower(key)] = value end line = handle:read("*l") end handle:close() end return values end function MusicPD.next() MusicPD.call_command("next") end function MusicPD.previous() MusicPD.call_command("previous") end function MusicPD.pause() MusicPD.call_command("pause") end function MusicPD.stop() MusicPD.call_command("stop") end function MusicPD.volume_up() local stats = MusicPD.call_command("status") local cur_volume = tonumber(stats.volume) local new_volume if cur_volume == 100 then return nil elseif cur_volume + settings.volume_delta > 100 then new_volume = string.format("setvol %d", 100) else new_volume = string.format("setvol %d", settings.volume_delta + cur_volume) end MusicPD.call_command(new_volume) end function MusicPD.volume_down() local stats = MusicPD.call_command("status") local cur_volume = tonumber(stats.volume) local new_volume if cur_volume == 0 then return nil elseif cur_volume - settings.volume_delta < 0 then new_volume = string.format("setvol %d", 0) else new_volume = string.format("setvol %d", cur_volume - settings.volume_delta) end MusicPD.call_command(new_volume) end function MusicPD.toggle_random() local stats = MusicPD.call_command("status") local random = tonumber(stats.random) if random == 0 then MusicPD.call_command("random 1") elseif random == 1 then MusicPD.call_command("random 0") end end function MusicPD.toggle_repeat() local stats = MusicPD.call_command("status") local rpt = tonumber(stats["repeat"]) if rpt == 0 then MusicPD.call_command("repeat 1") else MusicPD.call_command("repeat 0") end end function MusicPD.toggle_play() local stats = MusicPD.call_command("status") if stats.state == "play" then MusicPD.call_command("pause") elseif stats.state == "pause" then MusicPD.call_command("pause") elseif stats.state == "stop" then MusicPD.call_command("play") end end defbindings("WScreen", { kpress("KP_6", "MusicPD.next()"), kpress("KP_4", "MusicPD.previous()"), kpress("KP_2", "MusicPD.volume_down()"), kpress("KP_8", "MusicPD.volume_up()"), kpress("KP_5", "MusicPD.toggle_play()"), kpress("KP_7", "MusicPD.toggle_repeat()"), kpress("KP_9", "MusicPD.toggle_random()") }) notion-3+2012042300/contrib/scripts/named_floating_groupws.lua000066400000000000000000000015751174530661200241610ustar00rootroot00000000000000--[[ Author: Etan Reisner Email: deryni@gmail.com Summary: Toggle (and create) floating WGroupWS:s by name. Version: 0.1 Last Updated: 2007-01-29 Copyright (c) Etan Reisner 2007 --]] -- Example: This will create a level 2 floatws named example_floatws -- kpress(MOD4.."e", "named_groupws(_, 'example_groupws')") function named_groupws(reg, name) local named_groupws local scr = reg:screen_of() named_groupws = ioncore.lookup_region(name, "WGroupWS") if not named_groupws then named_groupws = scr:attach_new({ type="WGroupWS", name=name, unnumbered=true, hidden=true, }) end scr:set_hidden(named_groupws, 'toggle') end -- vim: set expandtab sw=4: notion-3+2012042300/contrib/scripts/named_scratchpad.lua000066400000000000000000000023541174530661200227000ustar00rootroot00000000000000--[[ Author: Etan Reisner Email: deryni@gmail.com Summary: Toggle (and create) scratchpads by name. Version: 0.2 Last Updated: 2007-01-23 Copyright (c) Etan Reisner 2007 --]] -- Usage: This will create a scratchpad named example_sp -- kpress(MOD4.."space", "named_scratchpad(_, 'example_sp')") function named_scratchpad(reg, name) local named_sp local default_w, default_h = 640, 480 local scr = reg:screen_of() local geom_scr = scr:geom() local geom_loc = { w = math.min(geom_scr.w, default_w), h = math.min(geom_scr.h, default_h), } geom_loc.x = (geom_scr.w - geom_loc.w) / 2 geom_loc.y = (geom_scr.h - geom_loc.h) / 2 named_sp = ioncore.lookup_region(name, "WFrame") if not named_sp then named_sp = scr:attach_new({ type="WFrame", name=name, unnumbered=true, modal=true, hidden=true, sizepolicy=5, geom=geom_loc, }) end mod_sp.set_shown(named_sp, "toggle") end -- vim: set expandtab sw=4: notion-3+2012042300/contrib/scripts/nest_ws.lua000066400000000000000000000036471174530661200211100ustar00rootroot00000000000000-- Nest workspaces inside Frames. -- Matthieu Moy , February 15th 2005. -- Made into a function (and to only show valid workspace types) by Etan Reisner July 03 2006. -- Public domain. -- This adds a submenu to the client context menu. -- Usage: -- Add defbindings("WFrame", { -- kpress(META.."M", "nest_ws_menu(_, 'Context menu: ', 'Attach')"), -- Query menu -- }) -- -- or kpress(META.."M", "nest_ws_menu(_, _sub, 'Attach')"), -- Popup menu -- -- This will override the default binding for the context menu and provide you -- with a context menu with the nest_ws submenu. -- LEGACY USAGE. -- -- This defines a menu to be used as a submenu for WFrames. -- Add the line -- submenu("Attach", "menuattach"), -- to the definition defctxmenu("WFrame", { ... }) function nest_ws_menu(frame, cwin, name) if mod_menu then local framemenu = ioncore.getmenu("ctxmenu")(frame, cwin) local nestmenu = {} if mod_ionws then table.insert(nestmenu, menuentry("WIonWS", "_:attach_new({type=\"WIonWS\" }):goto()")) end if mod_floatws then table.insert(nestmenu, menuentry("WFloatWS", "_:attach_new({type=\"WFloatWS\"}):goto()")) end if mod_panews then table.insert(nestmenu, menuentry("WPaneWS", "_:attach_new({type=\"WPaneWS\" }):goto()")) end if #nestmenu > 0 then local name = name or 'Attach' table.insert(framemenu, submenu(name, nestmenu)) end if (type(cwin) == "userdata" and obj_typename(cwin) == "WClientWin") or not cwin then mod_menu.menu(frame, cwin, framemenu) else local prompt = cwin mod_query.query_menu(frame, framemenu, prompt) end end end -- Legacy defmenu("menuattach", { menuentry("WIonWS", "_:attach_new({type=\"WIonWS\" }):goto()"), menuentry("WFloatWS", "_:attach_new({type=\"WFloatWS\"}):goto()"), menuentry("WPaneWS", "_:attach_new({type=\"WPaneWS\" }):goto()"), }) notion-3+2012042300/contrib/scripts/net_client_list.lua000066400000000000000000000047341174530661200226030ustar00rootroot00000000000000--[[ Author: Etan Reisner Email: deryni@unreliablesource.net Summary: Maintains the _NET_CLIENT_LIST property (and the _NET_CLIENT_LIST_STACKING property incorrectly) on the root window. Last Updated: 2007-07-22 Copyright (c) Etan Reisner 2007 --]] local atom_window = ioncore.x_intern_atom("WINDOW", false) local atom_client_list = ioncore.x_intern_atom("_NET_CLIENT_LIST", false) local atom_client_list_stacking = ioncore.x_intern_atom("_NET_CLIENT_LIST_STACKING", false) local function add_client(cwin) if not cwin then return end local rootwin = cwin:rootwin_of() local list = {n=0} ioncore.clientwin_i(function (cwin) list.n = list.n + 1 list[list.n] = cwin:xid() return true end) list.n = nil ioncore.x_change_property(rootwin:xid(), atom_client_list, atom_window, 32, "replace", list) ioncore.x_change_property(rootwin:xid(), atom_client_list_stacking, atom_window, 32, "replace", list) end local function remove_client(xid) local rootwin = ioncore.current():rootwin_of() local list = {n=0} ioncore.clientwin_i(function (cwin) list.n = list.n + 1 list[list.n] = cwin:xid() return true end) list.n = nil ioncore.x_change_property(rootwin:xid(), atom_client_list, atom_window, 32, "replace", list) ioncore.x_change_property(rootwin:xid(), atom_client_list_stacking, atom_window, 32, "replace", list) end local function net_mark_supported(atom) if (ioncore.rootwin) then local rootwin = ioncore.rootwin() local atom_atom = ioncore.x_intern_atom("ATOM", false) local atom_net_supported = ioncore.x_intern_atom("_NET_SUPPORTED", false) ioncore.x_change_property(rootwin:xid(), atom_net_supported, atom_atom, 32, "append", {atom}) end end add_client(ioncore.current()) do local hook hook = ioncore.get_hook("clientwin_mapped_hook") if hook then hook:add(add_client) end hook = nil hook = ioncore.get_hook("clientwin_unmapped_hook") if hook then hook:add(remove_client) end net_mark_supported(atom_client_list); end notion-3+2012042300/contrib/scripts/nextact.lua000066400000000000000000000005521174530661200210640ustar00rootroot00000000000000-- Go to first found region with activity flag set. local function filteri(f, l) local res={} for _, v in ipairs(l) do if f(v) then table.insert(res, v) end end return res end function goto_nextact() local l=filteri(WRegion.is_activity, ioncore.clientwin_list()) if l[1] then l[1]:goto() end end notion-3+2012042300/contrib/scripts/notifybox.lua000066400000000000000000000016271174530661200214430ustar00rootroot00000000000000 notifybox={} local default="*notifybox*" local function get_box(name) return ioncore.lookup_region(name, "WInfoWin") end function notifybox.show(message, name, location, style, scr) if not name then name=default end if not scr then scr=ioncore.find_screen_id(0) end local box=get_box(name) if not box then box=scr:attach_new{ type="WInfoWin", name=name, sizepolicy=location or "southeast", unnumbered=true, level=10, passive=true, style=style, geom={w=1, h=1, x=0, y=0}, } end -- Hack: attach_new doesn't get geometries right if we pass msg -- directly to it. box:set_text(message, scr:geom().w) end function notifybox.hide(name) local box=get_box(name or default) if box then box:rqclose() end end notion-3+2012042300/contrib/scripts/nowarp_scratchpad.lua000066400000000000000000000010661174530661200231210ustar00rootroot00000000000000--[[ Author: Etan Reisner Email: deryni@gmail.com Summary: Keeps ion from warping the pointer when activating a scratchpad region. Version: 0.1 Last Updated: 2007-01-23 Copyright (c) Etan Reisner 2007 --]] local function do_warp_alt(reg) -- Make sure this is enough to always identify a scratchpad. if reg.mode and reg:mode() == "unknown" then return true end return false end local function setup_hooks() local hook hook = ioncore.get_hook("region_do_warp_alt") if hook then hook:add(do_warp_alt) end end setup_hooks() -- vim: set expandtab sw=4: notion-3+2012042300/contrib/scripts/panel.lua000066400000000000000000000024631174530661200205200ustar00rootroot00000000000000-- -- This script allows making panels and other windows the stdisp. -- -- Usage: -- -- defwinprop { -- class = 'SomePanel', -- instance = 'somepanel', -- is_panel = true, -- -- panel_fullsize = false, -- -- panel_pos = 'bl', -- -- min_size = { w = something, h = sensible }, -- } -- -- Set panel.check_exists to disable checking for existing stdisp. -- panel={} panel.check_exists=true local function dflt(x, y) if x~=nil then return x else return y end end function panel.manage(cwin, tab) local prop=ioncore.getwinprop(cwin) if prop and prop.is_panel then local scr=cwin:screen_of() local exists=false if panel.check_exists then local current=scr:get_stdisp() if current and current.reg then exists=true end end if not exists then return scr:set_stdisp{ reg = cwin, fullsize = dflt(prop.panel_fullsize, true), pos = dflt(prop.panel_pos, 'bl'), } end end return false end function panel.reg() ioncore.get_hook("clientwin_do_manage_alt"):add(panel.manage) end function panel.unreg() ioncore.get_hook("clientwin_do_manage_alt"):remove(panel.manage) end panel.reg() notion-3+2012042300/contrib/scripts/query_url.lua000066400000000000000000000045041174530661200214460ustar00rootroot00000000000000-- Prompt for a URL and open it -- Use Opera bookmarks for completion -- (c) Reuben Thomas 2005 (rrt@sc3d.org); released under the GPL -- -- Completes on full URL, name of bookmark, or URL with initial http[s]://[www.] stripped -- -- Use something like: -- -- kpress(MOD2.."F3", 'query_url(_)'), -- -- The browser defaults to "sensible-browser"; configure below: local browser = "sensible-browser" -- Read Opera bookmarks function readOperaBookmarks () local bookmarks, names = {}, {} -- Parse bookmarks file -- Make a list of bookmarks and their names for use as completions. -- Also make a map of names to URLs. local f = io.lines (os.getenv ("HOME") .. "/.opera/opera6.adr") local nameTag = "\tNAME=" local nameOff = string.len (nameTag) + 1 local namePat = "^" .. nameTag local urlTag = "\tURL=" local urlOff = string.len (urlTag) + 1 local urlPat = "^" .. urlTag local l = f () while l do if string.find (l, namePat) then local name = string.sub (l, nameOff) l = f () if string.find (l, urlPat) then local url = string.sub (l, urlOff) table.insert (bookmarks, url) -- TODO: Make the name and stripped URL processing below -- independent of browsing Opera's bookmarks, and add other -- bookmark formats local urlIndex = table.getn (bookmarks) names[name] = urlIndex table.insert (bookmarks, name) local strippedUrl = string.gsub (url, "^https?://", "") strippedUrl = string.gsub (strippedUrl, "^www\.", "") if url ~= strippedUrl then names[strippedUrl] = urlIndex table.insert (bookmarks, strippedUrl) end end else l = f () end end return bookmarks, names end -- Prompt for a URL, matching by name or URL function query_url (mplex) local bookmarks, names = readOperaBookmarks () local function handler (mplex, str) -- If str is the name of a URL, get the url instead local index = names[str] if index then str = bookmarks[index] end -- FIXME: Ion needs a standard way of invoking a browser ioncore.exec_on (mplex, browser .. ' "' .. str .. '"') end local function completor (wedln, what) wedln:set_completions (mod_query.complete_from_list (bookmarks, what)) end mod_query.query (mplex, "URL: ", nil, handler, completor) end notion-3+2012042300/contrib/scripts/rotate_statusbar.lua000066400000000000000000000124641174530661200230110ustar00rootroot00000000000000-- rotate_statusbar.lua --[[ This replacement for cfg_statusbar.lua will rotate between statusbars at a user defined interval. This is handy for status information you are not interested in all the time such as what song you are playing or wireless strength and so on. Usage: 1) Back-up the existing ~/.ion3/cfg_statusbar.lua (if present). Rename this file called rotate_statusbar.lua to ~/.ion3/cfg_statusbar.lua. 2) Make changes to rotate_statusbar.settings.allbars (see below "USER DEFINED"). Each table represents a new status bar that will be shown. 3) For further changes, look into save_statusbar.lua, which will be created by this script). Also notice init_settings and configurations. 4) Now restart ion3 (hit 'F12' then type 'session/restart') This is heavily based on ctrl_statusbar.lua by Sadrul Habib Chowdhury. -tyranix (all public domain) TODO: All meters (whether visible or not) run in the background. It would be nice to only run the visible meters to avoid wasting CPU. - Can I access that functionality from here? --]] if not mod_statusbar then return end rotate_statusbar = { counter=0 } -- Initializing settings for statusbar -- This is similar to what is done in mod_statusbar.create in cfg_statusbar.lua if not rotate_statusbar.init_settings then rotate_statusbar.init_settings = { screen=0, -- First screen pos='bl', -- Bottom left corner fullsize=false, systray=true, -- In the systray -- Date format goes here instead of in the configurations below because -- it is loaded by ion3? -- date_format='%a %Y-%b-%d %H:%M', } end -- Settings for individual modules to override the defaults in their -- respective files. -- -- To find the defaults, for instance, foo = {} would be in statusd_foo.lua. -- -- Similar to the settings in mod_statusbar.launch_statusd in cfg_statusbar.lua if not rotate_statusbar.configurations then rotate_statusbar.configurations = { -- Load meter --[[ load = { update_interval=10*1000, important_threshold=1.5, critical_threshold=4.0 }, --]] -- Mail meter --[[ mail = { update_interval=60*1000, mbox=os.getenv("MAIL") }, --]] } end -- USER DEFINED User should change these if not rotate_statusbar.settings then rotate_statusbar.settings = { -- Change statusbars every 60 seconds. update_interval = 60*1000, -- Add a table for each status bar you want to have rotated. -- Template. Tokens %string are replaced with the value of the -- corresponding meter. -- -- 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. all_statusbars = { -- Make sure the defaults work for people. "[ %date ]", "[ %date || %load ]", "[ %date || %load || %date ]", -- Examples of other usage -- "[ %date || %load || %mocp_title (%mocp_currenttime %mocp_timeleft) ]", -- "[ %date || %load || %uptime ]", -- "[ %date || %load || %netmon ]", }, } end -- Construct the template for the statusbar function rotate_statusbar.get_template() -- Rotate through the pre-defined status bars if rotate_statusbar.counter >= table.getn(rotate_statusbar.settings.all_statusbars) then rotate_statusbar.counter = 0 end rotate_statusbar.counter = rotate_statusbar.counter + 1 return rotate_statusbar.settings.all_statusbars[rotate_statusbar.counter] end -- Create and initialize the statusbar function rotate_statusbar.init() rotate_statusbar.init_settings.template = rotate_statusbar.settings.all_statusbars[1] -- This is a very ugly hack to make sure all the statusd_ scripts get loaded -- Build a template with all the variables so the C program is called with -- the right modules to load. for _, t in pairs(rotate_statusbar.settings.all_statusbars) do rotate_statusbar.init_settings.template = rotate_statusbar.init_settings.template .. " " .. t end rotate_statusbar.sb = mod_statusbar.create(rotate_statusbar.init_settings) mod_statusbar.launch_statusd(rotate_statusbar.configurations) end -- Refresh the statusbar continually function update_rotate_statusbar() local st = rotate_statusbar.get_template() local tbl = mod_statusbar.template_to_table(st) rotate_statusbar.sb:set_template_table(tbl) rotate_statusbar_timer:set(rotate_statusbar.settings.update_interval, update_rotate_statusbar) end -- Initialize the statusd with the initial configuration rotate_statusbar.init() -- Create a timer to continually change the status bar -- rotate_statusbar_timer = statusd.create_timer() rotate_statusbar_timer = ioncore.create_timer() update_rotate_statusbar() notion-3+2012042300/contrib/scripts/rss_feed.lua000066400000000000000000000126261174530661200212150ustar00rootroot00000000000000-- rss_feed.lua -- -- Use: -- You have to dopath() this script for it to work. I have set a binding -- MOD1+F12 for a popup-menu. You can also add %rss to the statusbar, which -- will show a scrolling list of titles. You might want to give it a try -- before disabling. :) -- -- NOTES: -- This is not a useful script. This script was written just to demonstrate -- what idle time can do to a man. -- -- Author: -- Sadrul Habib Chowdhury (Adil) -- imadil at gmail dot com if not rss_feed then rss_feed = { interval=250, -- how often does the scroller update? width = 40, -- the width of the scroller.. duh refresh = 30 * (60*1000), -- how often to pull from the server? url = { -- guess what {"Slashdot", "http://slashdot.org/index.rss"}, {"CNN", "http://rss.cnn.com/rss/cnn_latest.rss"}, {"BBC", "http://newsrss.bbc.co.uk/rss/newsonline_world_edition/front_page/rss.xml"}, -- add other sites } } end local scroller_timer = nil -- the timer for the scroller local rss_timer = nil -- timer for the rss refresh rss_desc = "" local rss_string = "" local current = 1 local rss_length = 0 local feeds = {} -- -- remove html-entities -- local function clean(s) s = string.gsub(s, "\r", "") s = string.gsub(s, "\n", "") s = string.gsub(s, "&", "&") s = string.gsub(s, """, "\"") s = string.gsub(s, " ", " ") s = string.gsub(s, ">", ">") s = string.gsub(s, "<", "<") -- add any other conversions necessary here return s end local function reconstruct_rss(src, tbl) for _, info in pairs(tbl) do rss_string = rss_string .. " --- "..src.."# "..clean(info[1]) end end -- -- If you have been looking for the worst possible rss-parser ever, -- it's your lucky day. -- local function get_next(str) local t, d, r = nil,"","" if str == nil then return t,d,r end __, _ = string.find(str, "") if not __ then return t,d,r end e, s = string.find(r, "") if not e then return t,d,r end t = string.sub(r, _+1, e-1) r = string.sub(r, s+1) __, _ = string.find(r, "") if not __ then return t,d,r end e, s = string.find(r, "") if not e then return t,d,r end d = string.sub(r, _+1, e-1) r = string.sub(r, s+1) return t,d,r end local function parse_rss(str) local data = "" while str do data = data .. str str = coroutine.yield() end local src = "" local ret = "" local new = {} __, _ = string.find(data, "[^\n]+") if __ then src = string.sub(data, __, _) end -- for title, desc in string.gfind(data,".-(.-).-(.-).-") do title, desc, data = get_next(data) while title do title = clean(title) desc = clean(desc) rss_desc = rss_desc .. "\nTitle: "..title.."\n"..desc.."\n" table.insert(new, {title, desc}) title,desc,data = get_next(data) end feeds[src] = new rss_string = "" table.foreach(feeds, reconstruct_rss) rss_length = string.len(rss_string) rss_string = rss_string .. string.sub(rss_string, 1, rss_feed.width) count = 1 rss_timer:set(rss_feed.refresh, retrieve) end local function retrieve_rss(src, str) ioncore.popen_bgread("echo "..src.."&& curl " .. str .. " 2>/dev/null", coroutine.wrap(parse_rss)) end -- -- pull feed from each server -- function retrieve() local str = "" for _, entry in pairs(rss_feed.url) do retrieve_rss(entry[1], entry[2]) end end local function get_string() return rss_string end local function scroll() local st = get_string() local show = string.sub(st, current, current+rss_feed.width) mod_statusbar.inform("rss", "- "..show.." -") mod_statusbar.update() scroller_timer:set(rss_feed.interval, scroll) current = current + 1 if current > rss_length then current = 1 end end local function init_rss() rss_timer = ioncore.create_timer() rss_length = rss_feed.width rss_string = string.rep('- ', rss_length) if mod_statusbar then -- do this iff mod_statusbar is loaded scroller_timer = ioncore.create_timer() mod_statusbar.inform("rss_template", string.rep("x", rss_feed.width)) scroll() end retrieve() end function show_menu() local ret = {} local count = 1 local function sub_rss_menu(tbl) local ret = {} for q, d in pairs(tbl) do local title = clean(d[1]) local desc = clean(d[2]) table.insert(ret, menuentry(tostring(count)..". "..title, "mod_query.message(_, '" .. string.gsub(desc, "'", "\\'") .. "')")) count = count + 1 end if count == 1 then table.insert(ret, menuentry("No feed from server", "nil")) end return ret end table.insert(ret, menuentry("Re-read from server", "retrieve()")) for title, tbl in pairs(feeds) do count = 1 table.insert(ret, submenu("RSS Feeds from "..title, sub_rss_menu(tbl))) end return ret end init_rss() ioncore.defbindings("WScreen", { kpress(MOD1.."Shift+D", "mod_query.message(_, rss_desc)"), kpress(MOD1.."F12", 'mod_menu.bigmenu(_, _sub, show_menu)'), }) notion-3+2012042300/contrib/scripts/rss_feed_hh.lua000066400000000000000000000154611174530661200216740ustar00rootroot00000000000000-- rss_feed.lua -- -- Use: -- You have to dopath() this script for it to work. I have set a binding -- MOD1+F11 for a popup-menu. You can also add %rss to the statusbar, which -- will show a scrolling list of titles. You might want to give it a try -- before disabling. :) -- -- NOTES: -- This is not a useful script. This script was written just to demonstrate -- what idle time can do to a man. -- -- Author: -- Sadrul Habib Chowdhury (Adil) -- imadil at gmail dot com -- -- Modified by Henning Hasemann -- hhasemann at web dot de -- -- Changes -- * Added Flag which allows to toggle if a feed will be displayed in -- the statusbar -- * A feed in the menu now opens the coresponding url in firefox -- * Now uses MOD1+F11 by default, so it doesnt overlap with the -- menu anymore -- * I found the source-information in the statusbar distrubing so I removed -- it if not rss_feed then rss_feed = { interval=250, -- how often does the scroller update? width = 40, -- the width of the scroller.. duh refresh = 30 * (60*1000), -- how often to pull from the server? url = { -- guess what {"Slashdot", "http://rss.slashdot.org/Slashdot/slashdot", 1}, {"GLSA", "http://www.gentoo.org/rdf/en/glsa-index.rdf", 0}, {"TV 20:15", "http://www.tvmovie.de/tv-programm/2015rss.xml", 1}, -- add other sites } } end local scroller_timer = nil -- the timer for the scroller local rss_timer = nil -- timer for the rss refresh rss_desc = "" local rss_string = "" local current = 1 local rss_length = 0 local feeds = {} -- -- remove html-entities -- local function clean(s) s = string.gsub(s, "\r", "") s = string.gsub(s, "\n", "") s = string.gsub(s, "&", "&") s = string.gsub(s, """, "\"") s = string.gsub(s, " ", " ") s = string.gsub(s, ">", ">") s = string.gsub(s, "<", "<") -- add any other conversions necessary here return s end local function reconstruct_rss(src, tbl) for _, info in pairs(tbl) do if info[4] == 1 then rss_string = rss_string .. " -- "..clean(info[1]) end end end -- -- If you have been looking for the worst possible rss-parser ever, -- it's your lucky day. -- local function get_next(str) local t, d, r, l = nil,"","","" if str == nil then return t,d,r,l end __, _ = string.find(str, "") if not __ then return t,d,r,l end e, s = string.find(r, "") if not e then return t,d,r,l end t = string.sub(r, _+1, e-1) r = string.sub(r, s+1) r = string.sub(str, _) __, _ = string.find(r, "") if not __ then return t,d,r,l end e, s = string.find(r, "") if not e then return t,d,r,l end l = string.sub(r, _+1, e-1) r = string.sub(r, s+1) __, _ = string.find(r, "") if not __ then return t,d,r,l end e, s = string.find(r, "") if not e then return t,d,r,l end d = string.sub(r, _+1, e-1) r = string.sub(r, s+1) return t,d,r,l end local function parse_rss(str, show_in_sb, show_name) local data = "" while str do data = data .. str str = coroutine.yield() end local src = "" local ret = "" local new = {} __, _ = string.find(data, "[^\n]+") if __ then src = string.sub(data, __, _) end -- for title, desc in string.gfind(data,".-(.-).-(.-).-") do title, desc, data, link = get_next(data) while title do title = clean(title) desc = clean(desc) link = clean(link) rss_desc = rss_desc .. "\n"..title.."----------------------------\n"..desc.."\n" table.insert(new, {title, desc, link, show_in_sb, show_name}) title,desc,data,link = get_next(data) end feeds[src] = new rss_string = "" table.foreach(feeds, reconstruct_rss) rss_length = string.len(rss_string) rss_string = rss_string .. string.sub(rss_string, 1, rss_feed.width) count = 1 rss_timer:set(rss_feed.refresh, retrieve) end local function retrieve_rss(src, str, show_in_sb, show_name) local parse = coroutine.wrap(parse_rss) local interpret_line = function(str) parse(str, show_in_sb, show_name) end ioncore.popen_bgread("echo "..src.."&& curl " .. str .. " 2>/dev/null", interpret_line) end -- -- pull feed from each server -- function retrieve() local str = "" for _, entry in pairs(rss_feed.url) do retrieve_rss(entry[1], entry[2], entry[3], entry[4]) end end local function get_string() return rss_string end local function scroll() local st = get_string() local show = string.sub(st, current, current+rss_feed.width) mod_statusbar.inform("rss", "- "..show.." -") mod_statusbar.update() scroller_timer:set(rss_feed.interval, scroll) current = current + 1 if current > rss_length then current = 1 end end local function init_rss() rss_timer = ioncore.create_timer() rss_length = rss_feed.width rss_string = string.rep('- ', rss_length) if mod_statusbar then -- do this iff mod_statusbar is loaded scroller_timer = ioncore.create_timer() mod_statusbar.inform("rss_template", string.rep("x", rss_feed.width)) scroll() end retrieve() end function show_menu() local ret = {} local count = 1 local function sub_rss_menu(tbl) local ret = {} for q, d in pairs(tbl) do local title = clean(d[1]) local desc = clean(d[2]) table.insert(ret, menuentry(tostring(count)..". "..title, "mod_query.message(_, '" .. string.gsub(desc, "'", "\\'") .. "')")) count = count + 1 end if count == 1 then table.insert(ret, menuentry("No feed from server", "nil")) end return ret end local function view(tbl) local ret = {} for q, d in pairs(tbl) do local title = clean(d[1]) local desc = clean(d[2]) local link = clean(d[3]) table.insert(ret, menuentry(tostring(count)..". "..title, "ioncore.popen_bgread('firefox " .. link .. "')")) count = count + 1 end if count == 1 then table.insert(ret, menuentry("No feed from server", "nil")) end return ret end table.insert(ret, menuentry("Re-read from server", "retrieve()")) for title, tbl in pairs(feeds) do count = 1 table.insert(ret, submenu("RSS Feeds from "..title, view(tbl))) end return ret end init_rss() ioncore.defbindings("WScreen", { kpress(MOD1.."Shift+D", "mod_query.message(_, rss_desc)"), kpress(MOD1.."F11", 'mod_menu.bigmenu(_, _sub, show_menu)'), }) notion-3+2012042300/contrib/scripts/schedule.lua000066400000000000000000000126651174530661200212220ustar00rootroot00000000000000-- schedule.lua: schedule notification script -- -- You can schedule some messages to show up in the statusbar at -- specified times. -- -- Syntax: -- y -- or -- m -- or -- d -- or -- -- -- Current year/month/date will be used if you do not specify one. Messages -- need not be within quotes. Some examples follow: -- -- Example: -- y 2005 4 1 2 0 "April fool!!" --> 1st April, 2005, 2 am -- m 4 5 2 0 "Some message" --> 5th April of the current year, 2 am -- d 5 2 0 "Other message" --> 5th of the current month and year, 2 am -- 20 0 Last message --> 8 pm of today -- -- Note: -- The script now saves notification information in a file in the userdir -- (usually ~/.ion3/schedule_save.lua). So the notifications stay after a -- restart. -- -- Author: -- Sadrul Habib Chowdhury (Adil) -- imadil at gmail dot com if not mod_statusbar then return end -- -- how often should the script check for scheduled messages? -- if not schedule then schedule = { interval = 60 * 1000, -- check every minute save_filename = "schedule_save.lua" } end local timer = nil -- the timer local list = nil -- doubly linked list of messages -- -- extract the first token -- function schedule.get_token(str) local bg, fn bg, fn = string.find(str, "%w+%s+") return string.gsub(string.sub(str, bg, fn), "%s+", ""), string.sub(str, fn+1) end -- -- show the notification when scheduled -- function schedule.notify() local l = list local time = os.time() if l and time > l.time then io.stderr:write("schedule: " .. l.message .. "\n") mod_statusbar.inform("schedule", l.message) mod_statusbar.inform("schedule_hint", "important") mod_statusbar.update() list = l.next if list then list.prev = nil end l = nil schedule.save_file() end timer:set(schedule.interval, schedule.notify) end -- -- very hackish -- function schedule.insert_into_list(time, message) local l = list local t = nil if os.time() > time then return false end if l == nil then l = {} l.prev = nil l.next = nil l.time = time l.message = message list = l return true end while l do if l.time > time then l = l.prev break end if l.next then l = l.next else break end end t = {} t.time = time t.message = message if l == nil then list.prev = t t.next = list t.prev = nil list = t else t.next = l.next t.prev = l l.next = t end return true end -- -- add messages in the queue -- function schedule.add_message(mplex, str) local token = nil local tm = os.date("*t") token, str = schedule.get_token(str) if token == "y" then tm.year, str = schedule.get_token(str) tm.month, str = schedule.get_token(str) tm.day, str = schedule.get_token(str) tm.hour, str = schedule.get_token(str) elseif token == "m" then tm.month, str = schedule.get_token(str) tm.day, str = schedule.get_token(str) tm.hour, str = schedule.get_token(str) elseif token == "d" then tm.day, str = schedule.get_token(str) tm.hour, str = schedule.get_token(str) else tm.hour = token end tm.min, str = schedule.get_token(str) if schedule.insert_into_list(os.time(tm), str) then mod_query.message(mplex, "Notification \"" .. str .. "\" scheduled at " .. os.date("[%a %d %h %Y] %H:%M", os.time(tm))) schedule.save_file() else mod_query.message(mplex, "dude, can't schedule notification for past!") end end function schedule.ask_message(mplex) mod_query.query(mplex, TR("Schedule notification:"), nil, schedule.add_message, nil, "schedule") end -- -- show the scheduled notifications -- function schedule.show_all(mplex) local l = list local output = "List of scheduled notifications:" while l do output = output .. "\n" .. os.date("[%a %d %h %Y] %H:%M", l.time) .. " => " .. l.message l = l.next end mod_query.message(mplex, output) end -- -- save in file -- function schedule.save_file() if not list then return end local t = ioncore.get_paths() -- saving the file in userdir local f = io.open(t.userdir .."/".. schedule.save_filename, "w") if not f then return end local l = list while l do f:write("schedule.insert_into_list("..l.time..", \""..l.message.."\")\n") l = l.next end f:close() end -- -- the key bindings -- defbindings("WMPlex", { -- you can change the key bindings to your liking kpress(MOD1.."F5", "schedule.ask_message(_)"), kpress(MOD1.."Shift+F5", function () -- clear notification mod_statusbar.inform("schedule", "") mod_statusbar.inform("schedule_hint", "") mod_statusbar.update() end), kpress(MOD1.."Control+F5", "schedule.show_all(_)"), }) dopath(schedule.save_filename, true) -- read any saved notifications timer = ioncore.create_timer() timer:set(schedule.interval, schedule.notify) mod_statusbar.inform("schedule", "") notion-3+2012042300/contrib/scripts/send_to_ws.lua000066400000000000000000000070051174530661200215620ustar00rootroot00000000000000-- send_to_ws.lua -- -- written by Canaan Hadley-Voth, ugggg at hotmail -- -- Sends a clientwin to another workspace. -- -- On a tiled workspace, the frame sent to will be the most recently active. -- Maximized windows are skipped over in search of an actual workspace. -- Focus follows if jumpto is set. -- -- send_to_ws(cwin, action, jumpto) -- send_to_new_ws(cwin, wstype, jumpto) -- -- The action argument can be a specific workspace number OR it can be -- 'left', 'right', or 'any'. -- -- Workspace numbers start at 0 because WScreen.switch_nth starts at 0. -- (In both cases the bindings make it look like they start at 1.) -- -- -- These are the bindings I use, as an example. defbindings("WMPlex", { submap(META.."K", { submap("bracketleft", { kpress("H", "send_to_ws(_sub, 'left', true)"), kpress("L", "send_to_ws(_sub, 'right', true)"), kpress("1", "send_to_ws(_sub, 0, true)"), kpress("2", "send_to_ws(_sub, 1, true)"), kpress("3", "send_to_ws(_sub, 2, true)"), kpress("4", "send_to_ws(_sub, 3, true)"), kpress("5", "send_to_ws(_sub, 4, true)"), kpress("6", "send_to_ws(_sub, 5, true)"), kpress("7", "send_to_ws(_sub, 6, true)"), kpress("8", "send_to_ws(_sub, 7, true)"), kpress("9", "send_to_ws(_sub, 8, true)"), kpress("F", "send_to_new_ws(_sub, 'WGroupWS')"), }), }), }) function send_to_ws(cwin, action, jumpto) local destws, destwsindex, destwstype local offset=0 local scr=cwin:screen_of() local curws=cwin:manager() while curws:manager()~=scr do curws=curws:manager() end local curwsindex=scr:get_index(curws) if type(action)=="string" then if action=="left" then offset=-1 elseif action=="right" then offset=1 elseif action=="any" then -- send to the right if a workspace exists there, otherwise left if not(WMPlex.mx_nth(scr, curwsindex+1)) then send_to_ws(cwin, 'left', jumpto) return else send_to_ws(cwin, 'right', jumpto) return end end -- Skip over fullscreen client windows or frames or whatever else, -- until an actual workspace is found. destwsindex=curwsindex destws=scr:mx_nth(destwsindex+offset) destwstype=obj_typename(destws) while destwstype~="WGroupWS" and destwstype~="WTiling" do destwsindex=destwsindex+offset destws=scr:mx_nth(destwsindex) destwstype=obj_typename(destws) -- If there are no workspaces to the right, make one. -- (workspace type created can be set to "WGroupWS" instead. -- If there are no workspaces to the left, give up. if destwsindex>scr:mx_count() then send_to_new_ws(cwin, "WTiling") return elseif destwsindex<0 then return end end elseif type(action)=="number" then destwsindex=action destws=scr:mx_nth(destwsindex) destwstype=obj_typename(destws) if destwsindex==curwsindex then return end end if destwstype=="WTiling" then destws:current():attach(cwin, {switchto=true}) elseif destwstype=="WGroupWS" and obj_typename(destws:current())=="WTiling" then destws:current():current():attach(cwin, {switchto=true}) elseif destwstype=="WGroupWS" then destws:attach_framed(cwin) end if jumpto then cwin:goto() end end function send_to_new_ws(cwin, wstype, jumpto) local scr=cwin:screen_of() local n=scr:mx_count() local newws if wstype=="WGroupWS" then newws=scr:attach_new{type="WGroupWS"} newws:attach_framed(cwin) else newws=scr:attach_new{type="WTiling"} newws:current():attach(cwin) end if jumpto then cwin:goto() end end notion-3+2012042300/contrib/scripts/show_submap.lua000066400000000000000000000034131174530661200217440ustar00rootroot00000000000000--[[ Author: Etan Reisner Email: deryni@unreliablesource.net Summary: Displays an infowin with the currently active submap. Version: 0.1 Last Updated: 2007-07-04 Copyright (c) Etan Reisner 2007. --]] local sswins = setmetatable({}, {__mode="kv"}) local function sswin_set_hidden(sswin, state) if not sswin then return end local mgr = ioncore.find_manager(sswin, "WScreen") mgr:set_hidden(sswin, state) end function show_submap_win(reg, keystr) local scr, sswin scr = reg:screen_of() sswin = sswins[scr] ioncore.defer(function() if not sswin then sswin = scr:attach_new({ type="WInfoWin", name="show_submap_iw"..scr:id(), hidden=true, unnumbered=true, sizepolicy="free", geom={x=0, y=0, w=1, h=1}, style="show_submap", }) sswins[scr] = sswin end sswin_set_hidden(sswin, "unset") sswin:set_text(keystr or "", -1) end) end function hide_submap_win() local scr = ioncore.current():screen_of() sswin_set_hidden(sswins[scr], "set") end function setup() local hook = ioncore.get_hook("ioncore_submap_ungrab_hook") if hook then hook:add(hide_submap_win) end for section, bindings in pairs(ioncore.getbindings()) do for _,binds in pairs(bindings) do if binds.submap then defbindings(section, {submap(binds.kcb, {submap_enter("show_submap_win(_, '"..binds.kcb.."')")})}) end end end end setup() -- vim: set sw=4 sts=4 et: notion-3+2012042300/contrib/scripts/simple_bindings.lua000066400000000000000000000105641174530661200225700ustar00rootroot00000000000000--[[ Author: Etan Reisner Email: deryni@gmail.com Summary: Read a simplified binding file to let people set up some custom bindings without needing to mess with lua directly. To use create a 'bindings' file in ~/.ion3 . Last Updated: 2007-08-17 Copyright (c) Etan Reisner 2007 --]] --[[ Examples: go_to_workspace1 = "M-S-1" go_to_workspace4 = "M-S-4" xterm = "M-x" urxvt = "M-u" --]] --[[ Available bindings attach detach go_to_workspace# next_frame_up next_frame_down next_frame_left next_frame_right move_tab_left move_tab_right split_horiz split_vert split_horiz_top split_vert_top any other single word will be interpreted as the name of a program to run. --]] --[[ cycle windows --]] --{{{ Standalone hacks if not ioncore then ioncore = {get_paths = function() return {userdir="/home/deryni/.ion3"} end} warn = print loadstring = function(str) return function() print(str) end end end --}}} Standalone hacks local bindings = {} bindings.go_to_workspace = {section = "WScreen", funcstr = "WScreen.switch_nth(_, NUM)"} bindings.split_at = {section = "WTiling", funcstr = "WTiling.split_at(_, _sub, 'DIR', true)"} bindings.split_top = {section = "WTiling", funcstr = "WTiling.split_top(_, 'DIR')"} bindings.next_tab = {section = "WFrame.toplevel", funcstr = "WFrame.switch_next(_)"} bindings.prev_tab = {section = "WFrame.toplevel", funcstr = "WFrame.switch_prev(_)"} bindings.next_frame = {section = "WTiling", funcstr = "ioncore.goto_next(_sub, 'DIR', {no_ascend=_})"} bindings.move_tab = {section = "WFrame.toplevel", funcstr = "WFrame.DIR_index(_, _sub)"} bindings.tag = {section = "WFrame.toplevel", funcstr = "WRegion.set_tagged(_sub, 'toggle')"} bindings.attach_tagged = {section = "WFrame.toplevel", funcstr = "ioncore.tagged_attach(_)"} bindings.close_window = {section = "WMPlex", funcstr = "WRegion.rqclose_propagate(_, _sub)"} bindings.kill_window = {section = "WClientWin", "WClientWin.kill(_)"} bindings.detach = {section = "WMPlex", funcstr = "ioncore.detach(_chld, 'toggle')"} bindings.exec = {section = "WMPlex.toplevel", funcstr = "ioncore.exec_on(_, 'PROG')"} local paths, chunk, ok, err, fenv paths = ioncore.get_paths() chunk, err = loadfile(paths.userdir.."/bindings") if not chunk then warn("Failed to load bindings file") return end fenv = {} chunk = setfenv(chunk, fenv) ok, err = pcall(chunk) if not ok then warn("Failed to run the bindings file: "..err) return end local sections = {} for k,v in pairs(fenv) do local section, bindstr, keystr keystr = v:gsub("([MCS])%-", {M = "Mod1+", C = "Control+", S = "Shift+"}) if bindings[k] then bindstr = bindings[k].funcstr section = bindings[k].section end if (not bindstr) and k:match("^..tach$") then bindstr = bindings.detach.funcstr section = bindings.detach.section end local action, num = k:match("^(go_to_workspace)(%d+)$") if (not bindstr) and action and num then bindstr = bindings[action].funcstr:gsub("NUM", num - 1) section = bindings[action].section end local dir = k:match("^next_frame_(%w+)$") if (not bindstr) and dir then bindstr = bindings.next_frame.funcstr:gsub("DIR", dir) section = bindings.next_frame.section end local dir = k:match("^move_tab_(%w+)$") if (not bindstr) and dir then local dir = (dir == "right") and "inc" or "dec" bindstr = bindings.move_tab.funcstr:gsub("DIR", dir) section = bindings.move_tab.section end local dir, top = k:match("^split_([^_]+)_?(%w*)") if (not bindstr) and dir then local action = (top == "top") and "split_top" or "split_at" local dir = (dir == "horiz") and "right" or "bottom" bindstr = bindings[action].funcstr:gsub("DIR", dir) section = bindings[action].section end if not bindstr then bindstr = bindings.exec.funcstr:gsub("PROG", k) section = bindings.exec.section end if not sections[section] then sections[section] = {} end section = sections[section] section[#section + 1] = {bindstr = bindstr, keystr = keystr} end for section, tab in pairs(sections) do local str = [[defbindings("]]..section.."\", {\n" for i,bind in pairs(tab) do str = str.."\tkpress(\""..bind.keystr..[[", "]]..bind.bindstr.."\"),\n" end str = str.."})" loadstring(str)() end -- vim: set expandtab sw=4: notion-3+2012042300/contrib/scripts/stock.lua000066400000000000000000000321411174530661200205400ustar00rootroot00000000000000-- stock.lua -- ABOUT -- An Ion3 applet for retrieving and displaying stock market information -- from http://finance.yahoo.com. You can set up a portfolio and monitor -- its intraday performance. -- QUICKSTART -- 1. In template of you cfg_statusbar.lua insert: "%stock" (without quotes) -- 2. Insert, in you cfg_ion.lua or run: dopath("stock") -- 3. press MOD1+F10 to get the menu -- 4. Add a ticket: e.g. "^N225" (without quotes) to monitor the Nikkei index. -- COMMANDS -- Here's the list of available commands: -- - add-a-ticket: add a Yahoo ticket to monitor (e.g. "^N225" - -- without quotes). You can also insert the quantity, separated by a -- a comma: "TIT.MI,100" will insert 100 shares of TIT.MI in your portfolio. -- - delete-a-ticket: remove a ticket -- - suspend-updates: to stop retrieving data from Yahoo -- - resume-updates: to resume retrieving data from Yahoo -- - toggle-visibility: short or data display. You can configure -- the string for the short display. -- - update: force monitor to update data. -- CONFIGURATION AND SETUP -- You may configure this applet in cfg_statusbar.cfg -- In mod_statusbar.launch_statusd{) insert something like this: -- -- stock configuration options -- stock = { -- tickets = {"^N225", "^SPMIB", "TIT.MI"}, -- interval = 5 * 60 * 1000, -- check every 5 minutes -- off_msg = "*Stock*", -- string to be displayed in "short" mode -- susp_msg = "(Stock suspended)", -- string to be displayed when data -- -- retrieval is suspended -- susp_msg_hint = "critical", -- hint for suspended mode -- unit = { -- delta = "%", -- }, -- important = { -- delta = 0, -- }, -- critical = { -- delta = 0, -- } -- }, -- You can set "important" and "critical" thresholds for each meter. -- PORTFOLIO -- If you want to monitor a portfolio you can set it up in the configuration with -- something like this: -- stock = { -- off_msg = "*MyStock", -- portfolio = { -- ["TIT.MI"] = 2000, -- ["IBZL.MI"] = 1500, -- }, -- }, -- where numbers represent the quantities of shares you posses. When -- visibility will be set to OFF, in the statusbar (if you use ONLY the -- %stock meter) you will get a string ("MyStock" in the above example) -- red or green depending on the its global performance. -- FEEDBACK -- Please report your feedback, bugs reports, features request, to the -- above email address. -- REVISIONS -- 2006-07-10 first release -- LEGAL -- Copyright (C) 2006 Andrea Rossato -- 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 software 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 -- Have fun! -- Andrea Rossato arossato AT istitutocolli DOT org -- key bindings -- you can (should) change the key bindings to your liking defbindings("WMPlex", { kpress(MOD1.."F10", "mod_query.query_menu(_, 'stockmenu', 'StockMonitor Menu: ')"), kpress(MOD1.."Shift+F10", "StockMonitor.add_ticket(_)"), }) defmenu("stockmenu", { menuentry("update now", "StockMonitor.update()"), menuentry("add a ticket", "StockMonitor.add_ticket(_)"), menuentry("delete a Ticket", "StockMonitor.del_ticket(_)"), menuentry("toggle visibility", "StockMonitor.toggle()"), menuentry("suspend updates", "StockMonitor.suspend()"), menuentry("resume updates", "StockMonitor.resume()"), } ) -- our stuff local function new_stock() local this = { -- -- configuration -- config = { tickets = {}, interval = 5 * 60 * 1000, -- check every 5 minutes off_msg = "*Stock*", susp_msg = "(Stock suspended)", susp_msg_hint = "critical", toggle = "on", portfolio = {}, unit = { delta = "%", }, important = { delta = 0, }, critical = { delta = 0, } }, -- end configuration status_timer = ioncore.create_timer(), url = "http://finance.yahoo.com/d/quotes.csv", data = { }, backup = { }, paths = ioncore.get_paths(), } -- some needed global functions function table.merge(t1, t2) local t=table.copy(t1, false) for k, v in pairs(t2) do t[k]=v end return t end function math.dpr(number, signs_q) local pattern = "%d+" if signs_q == nil then signs_q = 2 end if signs_q ~= 0 then pattern = pattern.."%." end for i = 1, tonumber (signs_q) do pattern = pattern.."%d" end return string.gsub (number, "("..pattern..")(.*)", "%1") end -- gets configuration values and store them in this.config (public) function this.process_config() this.get_sb() local c = ioncore.read_savefile("cfg_statusd") if c.stock then this.config = table.merge(this.config, c.stock) end c = ioncore.read_savefile("cfg_stock") if c then this.config.portfolio = table.merge(this.config.portfolio, c) end this.process_portfolio() end -- gets tickets from portfolio function this.process_portfolio() for t,q in pairs(this.config.portfolio) do if t then table.insert(this.config.tickets, t) end end end -- gets the statusbar obj and makes a backup of the sb table (just in case) function this.get_sb() for _, sb in pairs(mod_statusbar.statusbars()) do this.StatusBar = sb end ioncore.write_savefile("stock_sb", this.StatusBar:get_template_table()) end -- removes a meter from the statusbar and inserts a new template chunk function this.sb_insert_tmpl_chunk(chunk, meter) local pos, old_sb_chunk this.restore_sb(meter) local old_sb = this.StatusBar:get_template_table() for a, item in pairs(old_sb) do if item.meter == meter then pos = a old_sb_chunk = item break end end this.backup[meter] = old_sb_chunk mod_statusbar.inform("start_insert_by_"..meter, "") mod_statusbar.inform("end_insert_by_"..meter, "") local new_sb_chunk = mod_statusbar.template_to_table("%start_insert_by_"..meter.." ".. chunk.." %end_insert_by_"..meter) local new_sb = old_sb table.remove(new_sb, pos) for i,v in pairs(new_sb_chunk) do table.insert(new_sb, pos, v) pos = pos + 1 end this.StatusBar:set_template_table(new_sb) end -- restores the statusbar with the original meter function this.restore_sb(meter) local st, en local old_sb = this.StatusBar:get_template_table() for a, item in pairs(old_sb) do if item.meter == "start_insert_by_"..meter then st = a end if item.meter == "end_insert_by_"..meter then en = a break end end local new_sb = old_sb if en then for a=st, en, 1 do table.remove(new_sb, st) end table.insert(new_sb, st, this.backup[meter]) this.StatusBar:set_template_table(new_sb) mod_statusbar.inform(meter, "") end end -- gets ticket's data function this.get_record(t) local command = "wget -O "..this.paths.sessiondir.."/".. string.gsub(t,"%^" ,"")..".cvs "..this.url.."?s=".. string.gsub(t,"%^" ,"%%5E").. "\\&f=sl1d1t1c1ohgv\\&e=.csv" os.execute(command) local f = io.open(this.paths.sessiondir .."/"..string.gsub(t,"%^" ,"")..".cvs", "r") if not f then return end local s=f:read("*all") f:close() os.execute("rm "..this.paths.sessiondir .."/"..string.gsub(t,"%^" ,"")..".cvs") return s end -- parses ticket's data and store them in this.data.ticketname function this.process_record(s) local _,_,t = string.find(s, '"%^?(.-)".*' ) t = string.gsub(t , "%.", "") this.data[t] = { raw_data = s, } _, _, this.data[t].ticket, this.data[t].quote, this.data[t].date, this.data[t].time, this.data[t].difference, this.data[t].open, this.data[t].high, this.data[t].low, this.data[t].volume = string.find(s, '"(.-)",(.-),"(.-)","(.-)",(.-),(.-),(.-),(.-),(.-)' ) if tonumber(this.data[t].difference) ~= nil then this.data[t].delta = math.dpr((this.data[t].difference / (this.data[t].quote - this.data[t].difference) * 100), 2) else this.data[t] = nil end end -- updates tickets' data function this.update_data() for _, v in pairs(this.config.tickets) do this.process_record(this.get_record(v)) end end -- gets threshold info function this.get_hint(meter, val) local hint = "normal" local crit = this.config.critical[meter] local imp = this.config.important[meter] if crit and tonumber(val) < crit then hint = "critical" elseif imp and tonumber(val) >= imp then hint = "important" end return hint end -- gets the unit (if any) of each meter function this.get_unit(meter) local unit = this.config.unit[meter] if unit then return unit end return "" end -- gets the quantity (if any) of each ticket function this.get_quantity(t) local quantity = this.config.portfolio[t] if quantity then return quantity end return 1 end -- notifies data to statusbar function this.notify() local newtmpl = "" local perf = 0 local base = 0 for i,v in pairs(this.data) do newtmpl = newtmpl..v.ticket..": %stock_delta_"..i.." " perf = perf + this.get_quantity(v.ticket) + (v.delta * this.get_quantity(v.ticket) / 100) base = base + (this.get_quantity(v.ticket)) for ii,vv in pairs(this.data[i]) do mod_statusbar.inform("stock_"..ii.."_"..i.."_hint", this.get_hint(ii, vv)) mod_statusbar.inform("stock_"..ii.."_"..i, vv..this.get_unit(ii)) end end if this.config.toggle == "off" then if perf > base then mod_statusbar.inform("stock",this.config.off_msg ) mod_statusbar.inform("stock_hint", "important") else mod_statusbar.inform("stock",this.config.off_msg ) mod_statusbar.inform("stock_hint", "critical") end else this.sb_insert_tmpl_chunk(newtmpl, "stock") end mod_statusbar.update() end -- checks if the timer is set and ther restarts function this.restart() if not this.status_timer then this.resume() end this.loop() end -- main loop function this.loop() if this.status_timer ~= nil and mod_statusbar ~= nil then this.update_data() this.notify() this.status_timer:set(this.config.interval, this.loop) end end -- public methods function this.update() this.loop() end function this.add_ticket(mplex) local handler = function(mplex, str) local _,_,t,_,q = string.find(str, "(.*)(,)(%d*)") ioncore.write_savefile("debug", { ["q"] = q, ["t"] = t}) if q then this.config.portfolio[t] = tonumber(q) else this.config.portfolio[str] = 1 end ioncore.write_savefile("cfg_stock", this.config.portfolio) this.process_portfolio() this.restart() end mod_query.query(mplex, TR("Add a ticket (format: ticketname - e.g. ^N225 or tickename,quantity e.g: ^N225,100):"), nil, handler, nil, "stock") end function this.del_ticket(mplex) local handler = function(mplex, str) for i,v in pairs(this.config.tickets) do if this.config.tickets[i] == str then this.config.tickets[i] = nil break end end this.config.portfolio[str] = nil ioncore.write_savefile("cfg_stock", this.config.portfolio) this.data[string.gsub(str,"[%^%.]" ,"")] = nil this.restart() end mod_query.query(mplex, TR("Delete a ticket (format: tickename e.g. ^N225):"), nil, handler, nil, "stock") end function this.suspend() this.restore_sb("stock") mod_statusbar.inform("stock",this.config.susp_msg ) mod_statusbar.inform("stock_hint", this.config.susp_msg_hint) mod_statusbar.update() this.status_timer = nil end function this.resume() this.status_timer = ioncore.create_timer() this.restart() end function this.toggle() if this.config.toggle == "on" then this.config.toggle = "off" this.restore_sb("stock") mod_statusbar.inform("stock",this.config.off_msg ) else this.config.toggle = "on" end this.restart() end -- constructor function this.init() this.process_config() this.loop() end this.init() -- return this return { update = this.update, add_ticket = this.add_ticket, del_ticket = this.del_ticket, toggle = this.toggle, suspend = this.suspend, resume = this.resume, data = this.data, config = this.config, } end -- there we go! StockMonitor = new_stock() notion-3+2012042300/contrib/scripts/switch_bindings.lua000066400000000000000000000033151174530661200225740ustar00rootroot00000000000000-- switch_bindings.lua -- -- Switch between different set of keybindings. You can use it to temporarily -- disable the keybindings by pressing META+F8. All the subsequent keystrokes -- will be sent to the application, until you press META+F8 again, at which -- point the previous bindings will be restored. -- -- Author: Sadrul Habib Chowdhury (Adil) -- Adjustments by: Canaan Hadley-Voth -- -- -- second set of bindings -- local alternate = { WScreen = { kpress(META.."F8", "toggle_bindings()"), } } function toggle_bindings() local save = table.copy(ioncore.getbindings(), true) local kcb_area -- First empty all the bindings for name, bindings in pairs(save) do for index, bind in pairs(bindings) do if bind.area then kcb_area = bind.kcb.."@"..bind.area else kcb_area = bind.kcb end -- This can most definitely be improved if bind.action == "kpress" then ioncore.defbindings(name, {kpress(bind.kcb, nil)}) -- "kpress_wairel" appearing as bind.action elseif string.find(bind.action, "kpress_wai") then ioncore.defbindings(name, {kpress_wait(bind.kcb, nil)}) elseif bind.action == "mpress" then ioncore.defbindings(name, {mpress(kcb_area, nil)}) elseif bind.action == "mdrag" then ioncore.defbindings(name, {mdrag(kcb_area, nil)}) elseif bind.action == "mclick" then ioncore.defbindings(name, {mclick(kcb_area, nil)}) elseif bind.action == "mdblclick" then ioncore.defbindings(name, {mdblclick(kcb_area, nil)}) end end end -- Store the last saved on for a, b in pairs(alternate) do ioncore.defbindings(a, b) end alternate = save end ioncore.defbindings("WScreen", { kpress(META.."F8", "toggle_bindings()"), }) notion-3+2012042300/contrib/scripts/tabmenu.lua000066400000000000000000000030501174530661200210450ustar00rootroot00000000000000-- -- tabmenu.lua -- -- By Tuomo Valkonen, 2007. -- -- This script introduces a tabmenu function that can be used to -- display a grabmenu corresponding to the tabs of a frame. It can -- be useful if you choose to disable the tab-bars of frames, to -- replace the META+K N/P bindings with the following: -- -- ioncore.defbindings("WFrame.toplevel", { -- submap(META.."K", { -- kpress("N", "tabmenu.tabmenu(_, _sub, 'next')"), -- kpress("P", "tabmenu.tabmenu(_, _sub, 'prev')"), -- }) -- }) -- -- tabmenu={} -- WRegion.displayname isn't exported ATM, so here's a hacky -- implementation. function tabmenu.hack_displayname(reg) local name=reg:name() if obj_is(reg, "WGroupCW") then local b=reg:bottom() if b then name=b:name() end end return name end function tabmenu.tabmenu(_, _sub, index) local i=1 local m={} _:mx_i(function(r) local e=menuentry(tabmenu.hack_displayname(r), function() r:goto() end) table.insert(m, e) if r==_sub then i=#m end return true end) if #m==0 then table.insert(m, menuentry("", function() end)) i=nil elseif index=='next' then i=((i==#m and 1) or i+1) elseif index=='prev' then i=((i==1 and #m) or i-1) elseif index=='first' then i=1 elseif index=='last' then i=#m end mod_menu.grabmenu(_, nil, m, {initial=i}) end notion-3+2012042300/contrib/scripts/weather.lua000066400000000000000000000266641174530661200210710ustar00rootroot00000000000000-- weather.lua -- -- NOTE: this script should be rewritten as a statusd script -- -- weather.lua: an Ion3 applet for displaying weather information -- INTRODUCTION -- I discovered Ion last week, after reading an article on linux.com. I -- installed it and, in a matter of a few hours, I decided to stick to -- it. How can I say: I've been dreaming of a WM to be run from the -- command line... and this is what Ion3 can do. And what a kind of a -- command line! A Lua interpreter! I must confess I've never heard about -- Lua before, and it was quite a surprise (I had the same feeling with -- JavaScript some time ago). -- I decided to write this applet mostly to learn and explore Lua, -- especially for object-oriented programming. -- ABOUT -- This applet can be used to monitor the weather condition(s) of one or -- more weather observation station(s). Data will be retrieved from -- http://weather.noaa.gov and displayed in the statusbar. -- USAGE -- You need to dopath() this script (e.g. from cfg_ion3.lua): -- dopath("weather") -- - To monitor one station you can insert, in your cfg_statusbar.lua, within -- mod_statusbar.launch_statusd{}, something like: -- weather = { -- station = "KNYC", -- } -- In your template insert something like: -- %weather_location: %weather_tempC %weather_humidity (%weather_time) -- Here's the list of all available data: -- %weather_location -- %weather_country -- %weather_date -- %weather_time (time of the latest report: your local time) -- %weather_tempF (Fahrenheit) -- %weather_tempC (Celsius) -- %weather_dewpointF (Fahrenheit) -- %weather_dewpointC (Celsius) -- %weather_humidity -- %weather_pressure (hPa) -- %weather_wind -- %weather_windspeed (MPH) -- %weather_sky -- %weather_weather -- - If you want to monitor more stations you need to create a monitor -- object for each one with the new_wm() function. After -- dopath("weather") write something like: -- mymonitor1 = new_wm("KNYC") -- mymonitor2 = new_wm("LIBP") -- You can create a new monitor also at run time. Get the Lua code prompt -- (usually MOD1+F3) and run: -- mymonitor3 = new_wm("LIBC") -- Do not set any station in cfg_statusbar.lua, since that station would -- be used by *all* monitors. -- Each monitor will output the data either in %weater_meter_XXXX, where XXXX is -- the station code (KNYC), and in %weather_meter. -- CONFIGURATION -- Default configuration values, that will apply to *all* monitors, can be -- written in cfg_statusbar.lua in your ~/.ion3 directory. -- Each monitor can be also configured at run-time. -- For instance: if you are monitoring only one station, get the Lua code -- prompt (usually MOD1+F3) and run: -- WeatherMonitor.config.unit.tempC = "C" -- or -- WeatherMonitor.config.critical.tempC = "15" -- You can save run-time configuration with the command: -- WeatherMonitor.save_config() -- This configuration will not overwrite the default station to be -- monitored. -- COMMANDS -- Monitors are objects that export some public methods. You can use -- these methods to change the state of the objects. -- You can change configuration value and save them (see CONFIGURATION) -- or issues some commands. -- Suppose you have 2 monitors: -- mon1 = new_wm("LIBP") -- mon2 = new_wm("KNYC") -- (Remember: in single mode the name of the object is WeatherMonitor.) -- Get the Lua code prompt (usually MOD1+F3) and: -- 1. to force one monitor to update the data: -- mon1.update() -- 2. to show the full report of the station: -- mon2.show_data(_) -- 3. to update data and show the report: -- WeatherMonitor.update_and_show(_) -- 4. to chnage station (only for run-time): -- WeatherMonitor.set_station(_) -- 5. to change one monitor configuration: -- mon1.config.critical.tempC = "15" -- Obviously you can create key-bindings to run these commands. -- REVISIONS -- 2006-07-07 first release -- LEGAL -- Copyright (C) 2006 Andrea Rossato -- 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 software 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 -- Have fun! -- Andrea Rossato arossato AT istitutocolli DOT org function new_wm(station) local this = { config = { station = station, interval =30 * 60 * 1000, -- check every 30 minutes unit = { tempC = "°", tempF = "F", humidity = "%", pressure = "hPa", windspeed = "MPH", }, -- Threshold information. These values should likely be tweaked to -- suit local conditions. important = { tempC = 10, tempF = 50, windspeed = 5, humidity = 30, }, critical = { tempC = 26, tempF = 78, windspeed = 20, humidity = 60, }, }, status_timer = ioncore.create_timer(), url = "http://weather.noaa.gov/pub/data/observations/metar/decoded/", paths = ioncore.get_paths(), data = { timezone = tonumber(os.date("%z")) }, } -- process configuration: -- - config in cfg_statusbar.lua will overwrite default config -- - run-time configuration (to be saved at run-time with this.save_config()) -- will overwrite default config but will be overwtitten by cfg_statusbar.lua this.process_config = function() table.merge = function(t1, t2) local t=table.copy(t1, false) for k, v in pairs(t2) do t[k]=v end return t end if not this.config.station then this.config.station = "LIPB" end local config = ioncore.read_savefile("cfg_weather_"..this.config.station) if config then this.config = table.merge(this.config, config) end config = ioncore.read_savefile("cfg_statusd") if config.weather then this.config = table.merge(this.config, config.weather) end end -- retrive data from server this.update_data = function() -- wget options -- -b go into the background (otherwise it will hang ion startup -- until data file is download) -- -o output error log (specify filename if you want to save output) local command = "wget -b -o /dev/null -O "..this.paths.sessiondir.."/"..this.config.station..".dat ".. this.url..this.config.station..".TXT" os.execute(command) local f = io.open(this.paths.sessiondir .."/".. this.config.station..".dat", "r") if not f then return end local s=f:read("*all") f:close() this.raw_data = s os.execute("rm "..this.paths.sessiondir .."/".. this.config.station..".dat") end -- process retrived data and store them in this.data this.process_data = function() local s = this.raw_data _, _, this.data.location, this.data.country = string.find(s, "^(.+)%,%s(.+)%(%u+%)" ) _, _, this.data.date, this.data.time = string.find(s, ".+%/%s([%d.]+)%s(%d+)%sUTC" ) _, _, this.data.wind, this.data.windspeed = string.find(s, "Wind%:%s(.+%sat%s(%d+)%sMPH)" ) _, _, this.data.sky = string.find(s, "Sky%sconditions:%s(.-)%c" ) _, _, this.data.tempF, this.data.tempC = string.find(s, "Temperature:%s([%d%.]+)%sF%s%(([%d%.]+)%sC%)%c" ) _, _, this.data.dewpointF, this.data.dewpointC = string.find(s, "Dew%sPoint:%s([%d%.]+)%sF%s%(([%d%.]+)%sC%)" ) _, _, this.data.humidity = string.find(s, "Relative%sHumidity:%s(%d+)%%") _, _, this.data.pressure = string.find(s, "Pressure%s.+%((.+)%shPa%)" ) _, _, this.data.weather = string.find(s, "Weather:%s(.-)%c" ) this.format_time() end -- format teh time string to get hh:mm this.format_time = function() local time if this.data.time then time = tonumber(this.data.time) + tonumber(this.data.timezone) else return end if time > 2400 then time = tostring(time - 2400) end if string.match(time, "^%d%d$") then time = "00"..time end if string.match(time, "^%d%d%d$") then time = "0"..time end this.data.time = tostring(time):gsub("(%d%d)(%d%d)","%1%:%2") end -- get threshold information this.get_hint = function(meter, val) local hint = "normal" local crit = this.config.critical[meter] local imp = this.config.important[meter] if crit and tonumber(val) > crit then hint = "critical" elseif imp and tonumber(val) > imp then hint = "important" end return hint end -- get the unit of each meter this.get_unit = function(meter) local unit = this.config.unit[meter] if unit then return unit end return "" end -- update information for mod_statusbar this.notify = function() for i,v in pairs(this.data) do mod_statusbar.inform("weather_"..i.."_"..this.config.station.."_hint", this.get_hint(i, v)) mod_statusbar.inform("weather_"..i.."_hint", this.get_hint(i, v)) if not v then v = "N/A" end mod_statusbar.inform("weather_"..i.."_"..this.config.station, v..this.get_unit(i)) mod_statusbar.inform("weather_"..i, v..this.get_unit(i)) end mod_statusbar.update() end -- -- some public methods -- -- save object state (each monitor will store its configuration in -- a file named cfg_weather_XXXX where XXXX is the station code -- this configuration will apply only to the monitor watching that -- station. In other words, you cannot set the default station for -- the monitor with this.set_station() (see comments below). this.save_config = function() ioncore.write_savefile("cfg_weather_"..this.config.station, this.config) end -- restarts the object this.update = function() this.init() end -- shows full report this.show_data = function(mplex) mod_query.message(mplex, this.raw_data) end -- updates data and shows updated full report this.update_and_show = function(mplex) this.init() this.show_data(mplex) end -- changes station. the new station will not be saved: -- to change station edit the configuration or start the -- monitor with the station as a paramenter, like: -- mymon = new_wm("KNYC") this.set_station = function(mplex) local handler = function(mplex, str) this.config.station = str; this.init() end mod_query.query(mplex, TR("Enter a station code:"), nil, handler, nil, "weather") end -- constructor this.init = function() this.update_data() this.process_data() if mod_statusbar ~= nil then this.notify() this.status_timer:set(this.config.interval, this.init) end end -- initialize the object this.process_config() this.init() return { config = this.config, data = this.data, save_config = this.save_config, update = this.update, show_data = this.show_data, update_and_show = this.update_and_show, set_station = this.set_station, } end -- start default monitor WeatherMonitor = new_wm() notion-3+2012042300/contrib/scripts/xinerama_switcher.lua000066400000000000000000000072401174530661200231330ustar00rootroot00000000000000-- xinerama_switcher.lua -- -- Goes to the frame in the specified direction. If there is no frame in the -- given direction (the user is trying to move off the edge of the screen), it -- switches workspace or screen. I assume that left/right switch between -- screens, since xinerama displays are usually next to each other. Up/down, -- on the other hand switch between workspaces. For me, the next workspace is -- down as if I was reading a webpage. -- -- The following keybindings may be useful: -- --defbindings("WIonWS", { -- kpress(MOD1.."Up", "xinerama_switcher_up(_)"), -- kpress(MOD1.."Down", "xinerama_switcher_down(_)"), -- kpress(MOD1.."Right", "xinerama_switcher_right(_)"), -- kpress(MOD1.."Left", "xinerama_switcher_left(_)"), --}) -- -- Based on go_desk_or_desk lua by Rene van Bevern , 2005 -- -- This script is (c) copyright 2005 by martin f. krafft -- and released under the terms of the Artistic Licence. -- -- Version 2005.08.14.1515 -- -- TODO: prevent switching screens when there is none on that side. E.g. -- moving off the left edge of the left screen should either do nothing or -- wrap to the right side of the right screen. This likely needs -- a configuration file which would also solve the problem about assigning -- left/right to next/prev according to xinerama configuration. -- When solving this, don't just solve it for dual-head. :) -- function goto_right_screen() -- do whatever it takes to switch to the screen on the right -- this may be either the previous or next screen, depending on how you set -- up xinerama. return ioncore.goto_prev_screen() --return ioncore.goto_next_screen() end function goto_left_screen() -- do whatever it takes to switch to the screen on the right -- this may be either the previous or next screen, depending on how you set -- up xinerama. --return ioncore.goto_prev_screen() return ioncore.goto_next_screen() end function goto_workspace_down(screen) -- do whatever it takes to switch to the workspace below -- in my book, that's the next workspace screen:switch_next() end function goto_workspace_up(screen) -- do whatever it takes to switch to the workspace below -- in my book, that's the previous workspace screen:switch_prev() end function xinerama_switcher(workspace, direction) if workspace:nextto(workspace:current(), direction, false) then -- there is a region in the sought direction, so just go to it workspace:goto_dir(direction) else -- the user is trying to move off the edge of the screen if direction == "left" then -- we move to the screen on the left local screen = goto_left_screen() screen:current():farthest("right"):goto() elseif direction == "right" then -- we move to the screen on the right local screen = goto_right_screen() screen:current():farthest("left"):goto() elseif direction == "up" then -- we switch to the workspace above this one local screen = workspace:screen_of() goto_workspace_up(screen) screen:current():farthest("down"):goto() elseif direction == "down" then -- we switch to the workspace below this one local screen = workspace:screen_of() goto_workspace_down(screen) screen:current():farthest("up"):goto() end end end -- helper functions for easier keybindings (see top of the file) function xinerama_switcher_left(reg) xinerama_switcher(reg, "left") end function xinerama_switcher_right(reg) xinerama_switcher(reg, "right") end function xinerama_switcher_up(reg) xinerama_switcher(reg, "up") end function xinerama_switcher_down(reg) xinerama_switcher(reg, "down") end notion-3+2012042300/contrib/scripts/xkbion.lua000066400000000000000000000142341174530661200207120ustar00rootroot00000000000000-- xkbion.lua -- TODO: make xkbion_set understand some simple presets -- (c) Sergey Redin -- Thanks: -- smersh at users.sf.net (author of xkbind) for the original idea --[[ -- This script allows you to use independent keyboard layouts for different windows in Ion3. -- It uses a window property to store the XKB groups, so you can restart Ion without losing -- settings for each window. -- Example usage. This is what I have in my cfg_ion.lua: dopath("mod_xkb") dopath("xkbion.lua") xkbion_set { {name="EN", hint="", action = function() mod_xkb.lock_group(0) end}, {name="RU", hint="important", action = function() mod_xkb.lock_group(1) end}, key="Caps_Lock", statusname = "xkbion", } xkbion_set { {name="num", command="numlockx on"}, {name="<->", command="numlockx off"}, key="Num_Lock", statusname="num", atomname="XKBION_NUM", } xkbion_set { {name="----", hint="", action = function() mod_xkb.lock_modifiers(2, 0) end}, {name="CAPS", hint="critical", action = function() mod_xkb.lock_modifiers(2, 2) end}, key="Caps_Lock", statusname = "caps", atomname="XKBION_CAPS", } -- Edit this to suit your needs. -- Please note, if you want to use Caps_Lock key to change the keyboard groups like I do, -- do not forget to add "grp:caps_toggle" to your XKB settings, just to prevent X from using -- this key also for swiching keyboard registers. -- At least one group definition must be present. -- "name" is only neseccary if you want to use mod_statusbar to indicate current XKB group. -- "hint" is only necessary if you want to highlight your XKB group in statusbar, possible -- values are standard values provided by the mod_statusbar: important, normal, critical -- "command" and "action" are also unneseccary but xkbion.lua is not particulary useful -- without them. :) The same thing for "key". -- The last thing to say about xkbion_set() parameters is that if you call xkbion_set -- more than once (like I do it for XKB groups and NumLock state) you must choose different -- "atomname" values. The default for atomname is XKBION_GROUP. -- The second xkbion_set() call (numlock section) is here mostly for the example. Most users -- will need only one call, for changing XKB group. Please also note that you can define more -- than two groups in call to xkbion_set(). -- You can use this line in cfg_statusbar.lua to indicate the current XKB group: template="... %xkbion ...", -- If your Ion does not have mod_xkb, you may try the following: xkbion_set { {name="EN", command="setxkbmap us -option grp:caps_toggle"}, {name="RU", command="setxkbmap ru winkeys -option grp:caps_toggle"}, key="Caps_Lock", statusname = "xkbion", } ]] function xkbion_set (groups) -- the only global created by xkbion.lua if not groups or type(groups) ~= "table" then error("bad args") end if not groups[1] or type(groups[1]) ~= "table" then error("default group is undefined") end -- window_group_prop(w) - get XKBION_GROUP integer property of window `w' (set it to 1 if it's not yet defined) -- window_group_prop(w, group) - set XKBION_GROUP property of window `w' to integer `group' -- "XKBION_GROUP" is just the default name local window_group_prop do local XA_INTEGER = 19 local atom = ioncore.x_intern_atom( tostring( groups.atomname or "XKBION_GROUP" ) ) if not atom or type(atom) ~= "number" then error("Cannot intern atom " .. atomname) end window_group_prop = function(w, gnum) if not w or type(w) ~= "userdata" or not w.xid or type(w.xid) ~= "function" then return 1 end local xid = tonumber( w:xid() ) if gnum == nil then local t = ioncore.x_get_window_property( xid, atom, XA_INTEGER, 1, true ) if t and type(t) == "table" and t[1] ~= nil then do return tonumber(t[1]) end else gnum = 1 end else gnum = tonumber(gnum) end -- we're here if the second argument is set or if the window does not have our property yet ioncore.defer( function() ioncore.x_change_property( xid, atom, XA_INTEGER, 32, "replace", {gnum} ) end ) return gnum end end local set_group do local current_gnum = 1 local first_time = true local statusname = groups.statusname if statusname and type(statusname) ~= "string" then statusname = nil end set_group = function(w, do_increment) local gnum if w then gnum = window_group_prop(w) else gnum = 1 end if do_increment then gnum = gnum + 1 end local g = groups[gnum] if not g then gnum, g = 1, groups[1] end if not g then return end -- error in settings, groups[1] not defined if first_time then first_time = false elseif gnum == current_gnum then return end window_group_prop(w, gnum) -- it's OK to call it even it `w' is nil if g.command then ioncore.exec(g.command) end if g.action then ioncore.defer(g.action) end current_gnum = gnum local group_name = g.name local hint_name = g.hint if statusname and group_name and type(group_name) == "string" then mod_statusbar.inform(statusname, group_name) mod_statusbar.inform(statusname.."_hint", hint_name) ioncore.defer(mod_statusbar.update) end end end ioncore.get_hook("region_notify_hook"):add( function(reg, action) if (obj_typename(reg) == "WClientWin") and (action == "activated") then set_group(reg) end end ) local key = groups.key if key and type(key) == "string" then defbindings("WClientWin", { kpress(key, function (_, _sub) set_group(_, true) end, "_sub:WClientWin") }) end set_group() -- initialize end -- xkbion_set() notion-3+2012042300/contrib/scripts/zoom.lua000066400000000000000000000065771174530661200204170ustar00rootroot00000000000000-- zoom.lua -- -- exchanges current client window with the active client window of the frame -- called 'zoomframe' -- resembling the behavior of larswm -- -- +---------------------------------------------------+-----------------+ -- | | | -- | | | -- | | | -- | | Client 1 | -- | | | -- | | | -- | Z o o m f r a m e w i t h +-----------------+ -- | z o o m e d C l i e nt | | -- | | | -- | | Client 2 | -- | | | -- | | | -- | | | -- +--------------------------------+------------------+-----------------+ -- | | | -- | | | -- | Client 4 | Client 3 | -- | | | -- | | | -- +--------------------------------+------------------------------------+ -- -- Example: zoom_client on "Client 2" will put "Client 2" into the zoom frame -- and "zoomed Client" into the frame of "Client 2" -- -- By Rene van Bevern , 2005 -- Modifications by Etan Reisner -- Public Domain -- -- Example keybindings: -- -- defbindings("WFrame", { -- kpress(META.."z", "zoom_client(_, _sub)") -- kpress(META.."z", "zoom_client(_, _sub, {swap=true})") -- kpress(META.."z", "zoom_client(_, _sub, {swap=false, goto=true})") -- } -- -- zoom_client accepts a table that may contain the options 'swap' -- and 'goto'. Both are by default set to 'true'. -- -- 'goto' controls whether to switch to the zoomframe -- -- 'swap' controls whether to bring the zoomframe's active client -- into the current frame -- -- -- Example configuration setup: -- -- zoom_client_set({zoomframename = 'foo'}) local settings = { zoomframename = 'zoomframe' } function zoom_client_set(tab) settings = table.join(tab, settings) end function zoom_client(curframe, curclient, options) local zoomframe = ioncore.lookup_region(settings.zoomframename, 'WFrame') if options == nil then options = {} end if options.goto == nil then options.goto = true end if options.swap == nil then options.swap = true end if (not zoomframe) or (curframe == zoomframe) then return end local zoomclient = zoomframe:lcurrent(1) if curclient then zoomframe:attach(curclient) end if zoomclient and options.swap then curframe:attach(zoomclient) end if zoomclient and options.goto then zoomclient:goto() -- make it activated in the frame end if curclient and options.goto then curclient:goto() end end notion-3+2012042300/contrib/statusbar/000077500000000000000000000000001174530661200172325ustar00rootroot00000000000000notion-3+2012042300/contrib/statusbar/statusbar_act.lua000066400000000000000000000025541174530661200226020ustar00rootroot00000000000000-- -- statusbar_act.lua -- -- Copyright (c) Tuomo Valkonen 2006. -- Copyright (c) Etan Reisner 2007. -- -- 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 notifyact() local act = ioncore.activity_first() local actcount = 0 if act then mod_statusbar.inform('act_first', act:name()) mod_statusbar.inform('act_first_hint', 'activity') local function count_act(reg) actcount = actcount + 1 return true end ioncore.activity_i(count_act) mod_statusbar.inform('act_n', tostring(actcount)) mod_statusbar.inform('act_n_hint', 'activity') else mod_statusbar.inform('act_first', '--') mod_statusbar.inform('act_first_hint', 'normal') mod_statusbar.inform('act_n', '0') mod_statusbar.inform('act_n_hint', 'normal') end end local function hookhandler(reg, how) if how ~= "activity" then return end ioncore.defer(function() notifyact() mod_statusbar.update() end) end ioncore.get_hook('region_notify_hook'):add(hookhandler) notifyact() mod_statusbar.inform('act_first_template', 'Mxxxxxxxxxxxxxx') mod_statusbar.update(true) notion-3+2012042300/contrib/statusbar/statusbar_external.lua000066400000000000000000000154061174530661200236550ustar00rootroot00000000000000-- -- statusbar_external.lua -- if not statusbar_external then statusbar_external = { -- a simple example which shows utc time and date -- in %udate every second with 'date -u' { template_key = 'dateb', external_program = 'date', execute_delay = 1 * 1000, }, { template_key = 'udate', external_program = 'date -u', execute_delay = 5 * 1000, }, -- a more complicated example showing the usage -- percentage of hda1 every 30 seconds in %hda1u -- with df, grep and sed. { template_key = 'hda1u', external_program = 'df|grep hda1|sed "s/.*\\(..%\\).*/\\1/"', execute_delay = 30 * 1000, meter_length = 4, hint_regexp = { important = '9[456].', critical = { '9[789].', '1...', }, }, }, } end -- -- -- What is this? -- -- This is a simple "general" statusbar lua script which -- lets you show output of external programs in ion -- statusbar. This is convenient if you are not familiar -- with lua but can handle yourself with some other -- language enough to output what you would want the -- statusbar to show. -- -- -- -- How to use it? -- -- Short version: -- -- 1 ) modify settings variable -- 2 ) update template to include specified keys -- 3 ) dopath( "statusbar_external" ) in cfg_user.lua -- -- -- Longer version: -- -- There is an example settings-variable which can be -- modified to specify your own programs. The needed -- parameters are the following : -- -- * template_key -- An unique name for the meter. You must add this -- name with preceding % (for example %meter_name) -- into your statusbar template which is usually set -- in cfg_statusbar.lua. -- -- * external_program -- The program to execute. The last line of each -- flushed output this program writes to stdout is -- inserted as the content of the meter. -- -- * execute_delay -- The delay to wait before running the program again -- once it stops sending data. Some programs just -- send the data and exit while others continue -- sending data periodically. This value can be used -- to control the update rate for the first type of -- programs. -- -- * meter_length (optional) -- The space reserved for this meter in characters. -- If the value is not specified or is less than 1 -- then the space is determined by the length of the -- data being shown but some people do not want their -- statusbar width to change randomly. -- -- * hint_regexp (optional) -- These regular expressions (in Lua syntax) are -- matched with the data and in case of a match the -- named hint is set to the meter. The value can be -- either a string containing the regexp or a table -- containing several possible regexps (Lua regexp -- doesn't have an 'or'). By default the themes define -- a color for 'important' and 'critical' hints. -- -- After this script is loaded it starts executing all -- the provided programs and updates the statusbar every -- time one of them prints (and flushes) something. -- -- Also this is not a normal statusd script so it -- doesn't get loaded by template name automatically -- when ion starts. You must add -- -- dopath( "statusbar_external" ) -- -- to some script that is being loaded in Ion. For -- example ~/.ion3/cfg_user.lua is a good place. Note -- that cfg_statusbar.lua is not loaded inside the -- Ion process but in a separate process and thus this -- script can not be loaded from that file! -- -- --------------------------------------------------------- local timers = {} local callbacks = {} -- Just print out stderr for now. local function flush_stderr(partial_data) local result = "" while partial_data do result = result .. partial_data -- Yield until we get more input. popen_bgread will call resume on us. partial_data = coroutine.yield() end print(result, "\n") end -- Execute a program. This will continuously retrieve output from the program. function start_execute(key) -- Read output from the program and (if data) then update the statusbar local handle_output_changes = coroutine.create( function(input_data) local key = input_data local data = "" local partial_data = "" -- Keep reading data until we have it all while partial_data do data = data .. partial_data -- Yield until we get more input. -- popen_bgread will call resume on us. partial_data = coroutine.yield() end -- If no updates, then just schedule another run if not data or data == "" then return timers[key]:set(statusbar_external[key].execute_delay, callbacks[key]) end -- remove all but the last line data = string.gsub( data, "\n$", "" ) data = string.gsub( data, ".*\n", "" ); local sets = statusbar_external[key] local t_key = sets.template_key local t_length = sets.meter_length local h_regexp = sets.hint_regexp if not tlength or t_length < 1 then t_length = string.len( data ) end mod_statusbar.inform( t_key, data ) mod_statusbar.inform( t_key .. "_hint", '' ) mod_statusbar.inform(t_key .. "_template", string.rep( ' ', t_length)) if not h_regexp then h_regexp = {} end for hint, re in pairs( h_regexp ) do if ( type( re ) == 'string' ) then re = { re } end for _, r in ipairs( re ) do if ( string.find( data, r ) ) then mod_statusbar.inform(t_key .. "_hint", hint) end end end mod_statusbar.update() return timers[key]:set(statusbar_external[key].execute_delay, callbacks[key]) end ) -- We need to pass it the key before calling a background call. coroutine.resume(handle_output_changes, key) ioncore.popen_bgread(statusbar_external[key].external_program, function(cor) coroutine.resume(handle_output_changes, cor) end, coroutine.wrap(flush_stderr)) end -- Start all for key in pairs(statusbar_external) do timers[key] = ioncore.create_timer() callbacks[key] = loadstring("start_execute("..key..")") start_execute(key) end -- -- Copyright (c) Antti Vähäkotamäki 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. -- notion-3+2012042300/contrib/statusbar/statusbar_fname.lua000066400000000000000000000006451174530661200231200ustar00rootroot00000000000000-- Copyright (c) Emanuele Giaquinta 2007 local function get_fname() local name="" local cur=ioncore.find_manager(ioncore.current(), "WFrame") if cur ~= nil then name = cur:name() end mod_statusbar.inform('fname', name) end local function hookhandler(reg, how) ioncore.defer(function() get_fname() mod_statusbar.update() end) end ioncore.get_hook("region_notify_hook"):add(hookhandler) notion-3+2012042300/contrib/statusbar/statusbar_workspace.lua000066400000000000000000000063351174530661200240320ustar00rootroot00000000000000-- statusbar_workspace.lua -- -- Show current workspace name or number in the statusbar. -- -- Put any of these in cfg_statusbar.lua's template-line: -- %workspace_name -- %workspace_frame -- %workspace_pager -- %workspace_name_pager -- %workspace_num_name_pager -- -- This is an internal statusbar monitor and does NOT require -- a dopath statement (effective after a 2006-02-12 build). -- -- version 1 -- author: Rico Schiekel -- -- version 2 -- added 2006-02-14 by Canaan Hadley-Voth: -- * %workspace_pager shows a list of workspace numbers -- with the current one indicated: -- -- 1i 2i [3f] 4p 5c -- -- i=WIonWS, f=WFloatWS, p=WPaneWS, c=WClientWin/other -- -- * %workspace_frame - name of the active frame. -- -- * Added statusbar_ to the filename (since it *is* -- an internal statusbar monitor) so that it works without -- a "dopath" call. -- -- * Removed timer. Only needs to run on hook. -- Much faster this way. -- -- version 3 -- update for ion-3rc-20070506 on 2007-05-09 -- by Kevin Granade -- -- Updated to use new wx_ api -- Replaced region_activated_hook with region_notify_hook -- Added %workspace_name_pager, which works similarly to %workspace_pager, -- but instead displays the name of each workspace -- Added display for WGroupWS to %workspace_pager, displayed as 'g' -- local function update_frame() local fr ioncore.defer( function() local cur=ioncore.current() if obj_is(cur, "WClientWin") and obj_is(cur:parent(), "WMPlex") then cur=cur:parent() end fr=cur:name() mod_statusbar.inform('workspace_frame', fr) mod_statusbar.update() end) end local function update_workspace() local scr=ioncore.find_screen_id(0) local curws = scr:mx_current() local wstype, c local pager="" local name_pager="" local name_pager_plus="" local curindex = scr:get_index(curws)+1 n = scr:mx_count(1) for i=1,n do tmpws=scr:mx_nth(i-1) wstype=obj_typename(tmpws) if wstype=="WIonWS" then c="i" elseif wstype=="WFloatWS" then c="f" elseif wstype=="WPaneWS" then c="p" elseif wstype=="WGroupWS" then c="g" else c="c" end if i==curindex then name_pager_plus=name_pager_plus.." ["..tmpws:name().."]" name_pager=name_pager.." ["..tmpws:name().."]" pager=pager.." ["..(i)..c.."] " else name_pager_plus=name_pager_plus.." "..(i)..":"..tmpws:name() name_pager=name_pager.." "..tmpws:name() pager=pager.." "..(i)..c.." " end end local fr,cur -- Older versions without an ioncore.current() should -- skip update_frame. update_frame() ioncore.defer( function() mod_statusbar.inform('workspace_pager', pager) mod_statusbar.inform('workspace_name', curws:name()) mod_statusbar.inform('workspace_name_pager', name_pager) mod_statusbar.inform('workspace_num_name_pager', name_pager_plus) mod_statusbar.update() end) end local function update_workspace_wrap(reg, how) if how ~= "name" then return end update_workspace() end ioncore.get_hook("region_notify_hook"):add(update_workspace_wrap) ioncore.get_hook("screen_managed_changed_hook"):add(update_workspace) notion-3+2012042300/contrib/statusbar/statusbar_wsname.lua000066400000000000000000000103301174530661200233140ustar00rootroot00000000000000--[[ Author: Etan Reisner Email: deryni@gmail.com Summary: Adds a handful of statusbar meters displaying the current workspace name, all workspace names, etc. in a variety of manners. Version: 0.6 Last Updated: 2007-01-23 Copyright (c) Etan Reisner 2007 --]] -- Usage: -- Add %wsname (or %wsname_full) to your statusbar template. -- -- For multiple head setups with multiple statusbars, -- use %wsname_{screennum} in your template (%wsname is the same as %wsname_0). -- -- The %wsname_fullall meter shows a combined %wsname_full display for all screens. -- -- The %wsname_pre meter contains the workspaces earlier in the list than the -- current workspace. -- -- The %wsname_post meter contains the workspaces later in the list than the -- current workspace. if not mod_statusbar then return end local defaults = { marker = "*", template = "xxxxxxxxxxx", all_marker = " - ", } local wsname = table.join(wsname or {}, defaults) local function get_ws_name(t) local reg = t["reg"] if not obj_is(reg, "WScreen") then return end local ws_names_all, wsname_pre, wsname_post = nil, "", "" local function inform(screen) if screen:mx_count() == 0 then return end local ws_names, before, id = nil, true, screen:id() local function compose_ws_names(ws) local marker, current = "", false local wsn = ws:name() or "?" if ws == screen:mx_current() then marker = wsname.marker before = false current = true end if not ws_names then ws_names = marker..wsn..marker else ws_names = ws_names.." "..marker..wsn..marker end if before and not current then if wsname_pre == "" then wsname_pre = wsn else wsname_pre = wsname_pre.." "..wsn end elseif not current then if wsname_post == "" then wsname_post = wsn else wsname_post = wsname_post.." "..wsn end end return true end screen:mx_i(compose_ws_names) mod_statusbar.inform("wsname_"..id, screen:mx_current():name()) mod_statusbar.inform("wsname_full_"..id, ws_names) mod_statusbar.inform("wsname_full_"..id.."_template", ws_names:len()) mod_statusbar.inform("wsname_pre_"..id, wsname_pre) mod_statusbar.inform("wsname_pre_"..id.."_template", wsname_pre:len()) mod_statusbar.inform("wsname_post_"..id, wsname_post) mod_statusbar.inform("wsname_post_"..id.."_template", wsname_post:len()) if id == 0 then mod_statusbar.inform("wsname", screen:mx_current():name()) mod_statusbar.inform("wsname_full", ws_names) mod_statusbar.inform("wsname_full_template", ws_names:len()) mod_statusbar.inform("wsname_pre", wsname_pre) mod_statusbar.inform("wsname_pre_template", wsname_pre:len()) mod_statusbar.inform("wsname_post", wsname_post) mod_statusbar.inform("wsname_post_template", wsname_post:len()) end if not ws_names_all then ws_names_all = ws_names else ws_names_all = ws_names_all..wsname.all_marker..ws_names end end ioncore.region_i(inform, "WScreen") mod_statusbar.inform("wsname_fullall", ws_names_all) mod_statusbar.inform("wsname_fullall_template", string.len(ws_names_all)) ioncore.defer(function() mod_statusbar.update() end) end local function setup_hooks() local hook hook = ioncore.get_hook("screen_managed_changed_hook") if hook then hook:add(get_ws_name) end end -- Init setup_hooks() local function inform(screen) local template local id = screen:id() if wsname[id] and wsname[id].template then template = wsname[id].template else template = wsname.template end mod_statusbar.inform("wsname_"..id.."_template", template) return true end mod_statusbar.inform("wsname_template", wsname.template) ioncore.region_i(inform, "WScreen") mod_statusbar.update(true) notion-3+2012042300/contrib/statusd/000077500000000000000000000000001174530661200167115ustar00rootroot00000000000000notion-3+2012042300/contrib/statusd/statusd_amarok.lua000066400000000000000000000020411174530661200224320ustar00rootroot00000000000000-- statusd_amarok.lua : Mark Tran -- Display current track from Amarok if not statusd_amarok then statusd_amarok = { interval = 3*1000 } end local function amarok_dcop() local f = io.popen('dcop amarok player nowPlaying 2> /dev/null', 'r') local amarok = f:read('*l') f:close() return amarok end local function amarok_status() local f = io.popen('dcop amarok player status 2> /dev/null', 'r') local status = f:read('*l') f:close() return status end local function change_display() local status = amarok_status() if status == "0" then return "" elseif status == "1" then local amarok = amarok_dcop() if (amarok ~= nil) then return amarok.." (paused)" end elseif status == "2" then return amarok_dcop() else return "" end end local amarok_timer local function update_amarok() statusd.inform("amarok", change_display()) amarok_timer:set(statusd_amarok.interval, update_amarok) end amarok_timer = statusd.create_timer() update_amarok() notion-3+2012042300/contrib/statusd/statusd_apm.lua000066400000000000000000000045111174530661200217410ustar00rootroot00000000000000-- Adds capability for OpenBSD APM info in ion3 statusbar. -- -- Originally written by Greg Steuck and released into the Public Domain -- 2006-10-28 modified by Darrin Chandler -- to work with OpenBSD 4.0 apm output. -- -- To install: -- Save this file as ~/.ion3/statusd_apm.lua, -- Change ~/.ion3/cfg_statusbar.lua like so: -- Add "apm={}," to mod_statusbar.launch_statusd, -- Modify "template" to include %apm_ variables -- e.g. template="[ %date || load:% %>load || bat: %apm_pct%%, A/C %apm_ac ]," -- -- Available variables: -- %apm_state high, low, critical -- %apm_pct battery life (in percent) -- %apm_estimate battery life (in minutes) -- %apm_ac External A/C charger state -- %apm_mode Adjustment mode (manual, auto, cool running) -- %apm_speed CPU speed local unknown = "?", "?", "?", "?", "?", "?" function get_apm() local f=io.popen('/usr/sbin/apm', 'r') if not f then return unknown end local s=f:read('*all') f:close() local _, _, state, pct, estimate, ac, mode, speed = string.find(s, "Battery state: (%S+), ".. "(%d+)%% remaining, ".. "([0-9]*) minutes life estimate\n".. "A/C adapter state: ([^\n]*)\n".. "Performance adjustment mode:%s(.+)%s".. "%((.+)%)\n".. ".*" ) if not state then return unknown end return state, pct, estimate, ac, mode, speed end local function inform(key, value) statusd.inform("apm_"..key, value) end local apm_timer local function update_apm() local state, pct, estimate, ac, mode, speed = get_apm() local hint if statusd ~= nil then inform("state", state) inform("pct", pct) inform("estimate", estimate) if state == "high" then hint = "normal" elseif state == "low" then hint = "important" else hint = "critical" end if hint ~= nil then inform("state_hint", hint) inform("pct_hint", hint) inform("estimate_hint", hint) end inform("ac", ac) if ac == "connected" then hint = "important" else hint = "critical" end inform("ac_hint", hint) inform("mode", mode) inform("speed", speed) apm_timer:set(30*1000, update_apm) else io.stdout:write("Batt: "..pct.."% ("..state.. ", "..estimate.." mins)\n".. "A/C: "..ac.."\n".. "Mode: "..mode.."\n".. "CPU Speed: "..speed.."\n" ) end end if statusd ~= nil then apm_timer = statusd.create_timer() end update_apm() notion-3+2012042300/contrib/statusd/statusd_apm2.lua000066400000000000000000000047571174530661200220370ustar00rootroot00000000000000-- Public domain, written by Greg Steuck -- Edited and updated by Gerald Young -- ion3script@gwy.org -- This works on FreeBSD's apm (5.x) program added some color to indicate -- AC connection and status (Charging, low, critical) -- Allows displaying apm information in the statusbar. -- To install: -- save this file into ~/.ion3/statusd_apm.lua, -- copy the default cfg_statusbar.lua to ~/.ion3, edit it to include (~line 81): -- -- Battery meter -- --[[ -- apm={}, -- --]] -- add some of the following fields into your template in cfg_statusbar.lua: -- %apm_ac: A/C cable on-line (connected) off-line (disconnected) -- %apm_pct: percent of remaining battery -- %apm_estimate: time remaining based upon usage ... or whatever apm thinks. -- %apm_state: Status: charging/high/low/critical -- in cfg_statusbar.lua, about line 28, add the next line without the leading "--" -- template="[ %date || load:% %>load_1min || battery: %apm_pct AC: %apm_ac Status: %apm_state ]", -- If you've already customized your template= line, then simply add the field(s) where you want. local unknown = "?", "?", "?", "?" -- Runs apm utility and grabs relevant pieces from its output. -- Most likely will only work on OpenBSD due to reliance on its output pattern. function get_apm() local f=io.popen('/usr/sbin/apm', 'r') if not f then return unknown end local s=f:read('*all') f:close() local _, _, ac, state, pct, estimate = string.find(s, "AC Line status: (.*)\n".. "Battery Status: (.*)\n".. "Remaining battery life: (.*)\n".. "Remaining battery time: (.*)\n" ) if not state then return unknown end return state, pct, estimate, ac end local function inform(key, value) statusd.inform("apm_"..key, value) end local apm_timer = statusd.create_timer() local function update_apm() local state, pct, estimate, ac = get_apm() local stateinf if state=="low" then stateinf = "important" end if state == "critical" then stateinf = "critical" end if state == "charging" then stateinf = "important" end inform("state", state) inform("state_hint", stateinf) inform("pct", pct) inform("estimate", estimate) if ac == "off-line" then stateinf="critical" end if ac == "on-line" then stateinf="important" end inform("ac", ac) inform("ac_hint", stateinf) apm_timer:set(60*1000, update_apm) end update_apm() notion-3+2012042300/contrib/statusd/statusd_batt.lua000066400000000000000000000024641174530661200221230ustar00rootroot00000000000000-- statusd_batt.lua -- -- Copyright (c) Relu Patrascu 2004. -- -- 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. -- -- Nov. 5, 2004 -- Disclaimer -- Neither am I a lua expert nor do I have the time to invest in writing -- better code here. I simply needed this utility and it works OK for me. -- I give no guarantees of any kind for this code. Suggestions for -- improvement are welcome. -- ikoflexer at gmail dot com if not statusd_batt then statusd_batt={ interval=10*1000 } end local battInfoStr local function get_batt_procapm() local f=io.open('/proc/apm', 'r') if not f then return "" end local s=f:read('*l') f:close() local st, en, battInfoStr = string.find(s, '(%d+%% [-]*%d+ [%?]*[%a]*)$') battInfoStr = string.gsub(battInfoStr, '-1.*',"-(= ") return tostring(battInfoStr) end local function get_batt_fn() return get_batt_procapm end local get_batt, batt_timer local function update_batt() statusd.inform("batt", get_batt()) batt_timer:set(statusd_batt.interval, update_batt) end -- Init get_batt=get_batt_fn() batt_timer=statusd.create_timer() update_batt() notion-3+2012042300/contrib/statusd/statusd_binclock.lua000066400000000000000000000130671174530661200227560ustar00rootroot00000000000000-------------------------------------------------------------------------------------- -- -- PURPOSE: -- Binary clock for Ion3 in two posible formats: -- --> Space waste: [0001:000111] and Alien Code: [ ..:" . ..] -- (You can modify the characters of 'alien' template to get a good looking clock) -- --> This is an example template (could be better): - -===^--- - I will explain it. -- -- HOW TO READ: -- In the "mono-character" Ion display, you need to read characters as : and =, -- like two different characters: one at the top, one at the bottom. -- -- In the example, equals are actually two different bars, one at the top and other -- at the bottom. The little triangle (a square or some weird symbol if your font -- doesn't support that character) represents a separator between hours and seconds so: -- -- - Upper-left represents hours: 2^4 .. 2^0 in 12 hours format. -- (4 characters from 'right to left' after separator) -- - Bottom-left represents minutes: 2^6 ... 2^0 -- (6 characters from 'right to left' after separator) -- - Right side, after separator, represents seconds: 2^6 ... 2^0 -- -- Every character on dots code (AKA: alien) can be replaced: -- - Bottom character or symbol ["_"] -- - Upper character -- - Both characters [Example: "=" or ":" ] -- - Separator character [Example: "^", "|"] -- -- ------ USAGE: ---------------------------------------------------------------------- -- -- Put [%binclock_10] on $HOME/.ion3/cfg_statusbar.lua to get a beauty -- [0110:101100] or [%binclock] to get an ugly [. .:' ]. -- By my side, I preffer the ugly dots and numbers character display. -- -- It is relaxing for me to see the dots dancing so I added extra space to -- show seconds. If you like that, don't forget to put this in settings: -- binclock = { show_seconds = true } * See other options at the beggining -- of the code. -------------------------------------------------------------------------------------- -- -- LICENSE: -- GPL2 Copyright(C)2006 Mario García H. -- (See http://www.gnu.org/licenses/gpl.html to read complete license) -- -- T.STAMP: Fri Dec 15 13:37:54 2006 -- -- VERSION: 0.1.c -- -- DEPENDS: No dependencies. -- -- INSECTS: -- * On non UTF-8 environments, default values for separator symbol and -- upper symbol are prone to not work. So, change them in settings please. -- ** If you put the clock on the left corner the left black spaces will disapear, -- use l_border (left border) or r_border characters in settings to avoid that. -- -- LAST CHANGES: -- 0.1 -> 0.1.c -- [ Changed 'while' for 'repeat' (It is almost 5X faster)] -- [ Removed 2 useless tables ] -- [ Changed if then else statements for logical statements: -- is more clean and goes a little faster] -- [ Simplified some string concatenations (A real slow down on Lua code). ] -- [ Some cleaning on this help text ] -- -- CONTACT: -- drosophila (at) nmental (dot) com -- ----- SEE MORE DETAILED SETTINGS HERE: --------------------------------------------- local defaults = { update_interval = 2000, show_seconds = true, -- EXAMPLES: separator = "°" or string.char(176), -- "|", or "^", r_border = "", -- "|", or ">" l_border = "", -- "|", or "<" top_sym = "¯" or string.char(175), -- string.char(183), if your locals are not UTF-8 low_sym = "-" or "_", -- ".", both_sym = "=", -- ":", empty_sym = [[ ]] or string.char(32), -- string.char(168) --> 164, 168, 176, 179 ?? color = "normal" -- "critical", "important", ...(white, red, green, etc.) -- It is not color, just and alias for alarm-types. } local settings = table.join(statusd.get_config("binclock"), defaults) ------------------------------------------------------------------------------------- local binary_timer local pattern = {} local function convert_time() --> Six cycles to get binary pattern = {} local hours, minutes, seconds =-- tonumber(os.date("%I")), tonumber(os.date("%M")), tonumber(os.date("%S")) local i, j = 32, 1 repeat pattern[j] = hours >=i and 1 or 0 pattern[j + 7] = minutes >=i and 1 or 0 pattern[j + 14] = seconds >=i and 1 or 0 hours = hours >= i and hours - i or hours minutes = minutes >= i and minutes - i or minutes seconds = seconds >= i and seconds - i or seconds i, j = i/2, j + 1 until j == 7 pattern[7], pattern[14] = settings.separator, settings.separator end local function inform_dots() --> Six extra cycles to get alien chars aligned local dots, dots_s = "", "" -- for i = 1, 6, 1 do dots = pattern[i] == 1 and pattern[i+7] == 1 and dots..settings.both_sym or pattern[i] == 1 and pattern[i+7] == 0 and dots..settings.top_sym or pattern[i] == 0 and pattern[i+7] == 1 and dots..settings.low_sym or dots..settings.empty_sym dots_s = pattern[i+14] == 1 and dots_s..settings.low_sym or dots_s..settings.empty_sym end -- if not settings.show_seconds then statusd.inform("binclock", dots) statusd.inform("binclock_hint", settings.color) else statusd.inform("binclock", settings.l_border..dots..settings.separator..-- dots_s..settings.r_border) statusd.inform("binclock_hint", settings.color) end end local function inform_ceros() if not settings.show_seconds then statusd.inform("binclock_10", table.concat(pattern, "", 3, 14)) statusd.inform("binclock_10_hint", settings.color) else statusd.inform("binclock_10", table.concat(pattern, "", 3)) statusd.inform("binclock_10_hint", settings.color) end end local function update_binary() convert_time() inform_ceros() inform_dots() binary_timer:set(settings.update_interval, update_binary) end binary_timer = statusd.create_timer() update_binary() notion-3+2012042300/contrib/statusd/statusd_bitcoin.lua000066400000000000000000000022261174530661200226140ustar00rootroot00000000000000-- Bitcoin (฿) daemon monitor -- %bitcoin_speed - current speed in khash/s -- %bitcoin_balance - current balance -- by Voker57 -- Public domain local defaults={ update_interval=30 * 1000 } local settings=table.join(statusd.get_config("bitcoin"), defaults) local bitcoin_timer local function get_bitcoin_speed() local hps = io.popen("bitcoind gethashespersec 2>/dev/null") if not hps then return "N/A" end local answ = hps:read("*a") if not answ or answ == "" then return "N/A" end return string.format("%0.2f", answ / 1000) end local function get_bitcoin_balance() local bl = io.popen("bitcoind getbalance 2>/dev/null") if not bl then return "N/A" end local answ = bl:read("*a") if not answ or answ == "" then return "N/A" end return string.format("%0.2f", answ) end local function update_bitcoin() local bitcoin_speed = get_bitcoin_speed() local bitcoin_balance = get_bitcoin_balance() statusd.inform("bitcoin_balance", bitcoin_balance) statusd.inform("bitcoin_speed", bitcoin_speed) bitcoin_timer:set(settings.update_interval, update_bitcoin) end -- Init bitcoin_timer=statusd.create_timer() update_bitcoin() notion-3+2012042300/contrib/statusd/statusd_bsdbatt.lua000066400000000000000000000040341174530661200226070ustar00rootroot00000000000000-- -- Copyright (c) 2005 Russell A. Jackson -- All rights reserved. -- -- Redistribution and use in source and binary forms, with or without -- modification, are permitted provided that the following conditions -- are met: -- 1. Redistributions of source code must retain the above copyright -- notice, this list of conditions and the following disclaimer. -- 2. Redistributions in binary form must reproduce the above copyright -- notice, this list of conditions and the following disclaimer in the -- documentation and/or other materials provided with the distribution. -- -- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND -- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -- ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE -- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -- SUCH DAMAGE. -- -- statusd_bsdbatt.lua -- -- Uses sysctl interface to fetch ACPI CMBAT status on FreeBSD. -- Add bsdbatt_life and bsdbatt_time fields to status bar template. -- function get_bsdbatt() local f=io.popen('sysctl -n hw.acpi.battery.life', 'r') local life=f:read('*l') f:close() f=io.popen('sysctl -n hw.acpi.battery.time', 'r') local time=f:read('*l') f:close() life = life.."%" if time == "-1" then time = "AC" else time = time.."m" end return life, time end function update_bsdbatt() local life, time = get_bsdbatt() statusd.inform("bsdbatt_life", life) statusd.inform("bsdbatt_time", time) bsdbatt_timer:set(1000, update_bsdbatt) end bsdbatt_timer = statusd.create_timer() update_bsdbatt() notion-3+2012042300/contrib/statusd/statusd_cpufreq.lua000066400000000000000000000022451174530661200226330ustar00rootroot00000000000000-- statusd_cpufreq.lua -- -- Public domain -- -- Use the key "cpufreq_[KMG]" to get the current CPU frequency in -- K/M/GHz, according to /sys/devices/system/cpu/cpuX/cpufreq/. (This -- has the advantage of being a much "rounder" number than the one in -- /proc/cpuinfo, as provided by statusd_cpuspeed.lua.) -- -- The "cpu" option to the statusd settings for cpufreq modifies which -- cpu we look at. local defaults={ update_interval=2*1000, cpu=0 } local settings=table.join(statusd.get_config("cpufreq"), defaults) function get_cpufreq() local f=io.open('/sys/devices/system/cpu/cpu'.. settings.cpu ..'/cpufreq/scaling_cur_freq') local cpufreq_K = f:read('*a') f:close() local cpufreq_M = cpufreq_K / 1000 local cpufreq_G = cpufreq_M / 1000 return tostring(cpufreq_K), tostring(cpufreq_M), tostring(cpufreq_G) end function update_cpufreq() local cpufreq_K, cpufreq_M, cpufreq_G = get_cpufreq() statusd.inform("cpufreq_K", cpufreq_K) statusd.inform("cpufreq_M", cpufreq_M) statusd.inform("cpufreq_G", cpufreq_G) cpufreq_timer:set(settings.update_interval, update_cpufreq) end cpufreq_timer = statusd.create_timer() update_cpufreq() notion-3+2012042300/contrib/statusd/statusd_cpuspeed.lua000066400000000000000000000025021174530661200227720ustar00rootroot00000000000000-- statusd_cpuspeed.lua -- -- Copyright (c) Relu Patrascu 2004. -- -- 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. -- -- Nov. 5, 2004 -- Disclaimer -- Neither am I a lua expert nor do I have the time to invest in writing -- better code here. I simply needed this utility and it works OK for me. -- I give no guarantees of any kind for this code. Suggestions for -- improvement are welcome. -- ikoflexer at gmail dot com if not statusd_cpuspeed then statusd_cpuspeed={ interval=10*1000 } end local cpuspeed local function get_cpuspeed_proc() local st,en local f=io.open('/proc/cpuinfo', 'r') if not f then return "" end local s=f:read('*a') f:close() st, en, cpuspeed = string.find(s, 'cpu MHz[%s]*:[%s]*(%d+%.%d+)') return tostring(cpuspeed) end local function get_cpuspeed_fn() return get_cpuspeed_proc end local get_cpuspeed, cpuspeed_timer local function update_cpuspeed() statusd.inform("cpuspeed", get_cpuspeed()) cpuspeed_timer:set(statusd_cpuspeed.interval, update_cpuspeed) end -- Init get_cpuspeed=get_cpuspeed_fn() cpuspeed_timer=statusd.create_timer() update_cpuspeed() notion-3+2012042300/contrib/statusd/statusd_cpustat.lua000066400000000000000000000062701174530661200226530ustar00rootroot00000000000000-- $Id: statusd_cpustat.lua 80 2007-03-10 00:16:09Z tibi $ -- statusd_cpustat.lua -- CPU monitor for Ion3's statusbar -- version : 0.3 -- date : 2007-03-10 -- author : Tibor Csögör -- Shows the CPU utilization of the system similiar to top(1). -- This script depends on the /proc filesystem and thus only works on Linux. -- Tested with kernel 2.6.16. -- Configuration: -- The following placeholders (with a "cpustat_" prefix) can be used in the -- statusbar template: `user', `nice', `idle', `system', `iowait', `irq', -- `softirq' and `steal'. In addition, `system_adj' sums up the last 5 fields. -- For a compact output simply use the `cpustat' placeholder. -- This software is in the public domain. -------------------------------------------------------------------------------- local defaults = { update_interval = 1000, -- 1 second } local settings = table.join(statusd.get_config("cpustat"), defaults) local last_stat, current_stat local last_uptime, current_uptime = 0, 0 local first_run = true function math.round(number, precision) local m = 10^(precision or 0) return math.floor((number*m)+(1/2)) / m end -- this function reads stats from /proc/stat and /proc/uptime and calculates the -- CPU time used in the measurement interval local function get_cpustat() local f1, f2, s, s2 f1 = io.open('/proc/stat', 'r') f2 = io.open('/proc/uptime', 'r') if ((f1 == nil ) or (f2 == nil)) then return nil end s = f1:read("*l") s2 = f2:read("*l") f1:close() f2:close() last_uptime = current_uptime local tmp, tmp, up1, up2 tmp, tmp, up1, up2 = string.find(s2, "(%d+)\.(%d+)%s%d") current_uptime = tonumber(up1 .. up2) local uptime_interv = current_uptime - last_uptime local t = {} for tmp in string.gfind(s, "%s+(%d+)") do table.insert(t, tonumber(tmp)) end last_stat = current_stat current_stat = t if (first_run) then last_stat = t first_run = false end local c = {} for i = 1, table.getn(t) do table.insert(c, math.round(((current_stat[i] - last_stat[i]) / uptime_interv) * 100)) end -- adjusted system CPU time (= system + iowait + irq + softirq + steal) table.insert(c, math.round((((current_stat[3] - last_stat[3]) + (current_stat[5] - last_stat[5]) + (current_stat[6] - last_stat[6]) + (current_stat[7] - last_stat[7]) + (current_stat[8] - last_stat[8])) / uptime_interv) * 100)) return c end local cpustat_timer = statusd.create_timer() local function update_cpustat() local t = get_cpustat() if (t == nil) then return nil end statusd.inform("cpustat_user", t[1] .. "%") statusd.inform("cpustat_nice", t[2] .. "%") statusd.inform("cpustat_system", t[3] .. "%") statusd.inform("cpustat_system_adj", t[9] .. "%") statusd.inform("cpustat_idle", t[4] .. "%") statusd.inform("cpustat_iowait", t[5] .. "%") statusd.inform("cpustat_irq", t[6] .. "%") statusd.inform("cpustat_softirq", t[7] .. "%") statusd.inform("cpustat_steal", t[8] .. "%") statusd.inform("cpustat", string.format("%3d%% us,%3d%% sy,%3d%% ni", t[1], t[9], t[2])) cpustat_timer:set(settings.update_interval, update_cpustat) end update_cpustat() -- EOF notion-3+2012042300/contrib/statusd/statusd_df.lua000066400000000000000000000066141174530661200215630ustar00rootroot00000000000000-- $Id: statusd_df.lua 60 2006-11-14 11:19:29Z tibi $ -- statusd_df.lua -- disk space monitor for Ion3's statusbar -- version : 0.1 -- date : 2006-11-14 -- author : Tibor Csögör -- Shows the disk space usage of given file systems. -- The standard UNIX tool df(1) is used to obtain the needed data. This script -- assumes that df understands the -k flag and produces an output like the -- following (the header line is ignored): -- Filesystem 1K-blocks Used Available Use% Mounted on -- /dev/hda6 2403420 33264 2248064 2% /tmp -- Configuration: -- The `template' string controls how the output will be formated. It may -- contain the following keywords: -- PLACEHOLDER DESCRIPTION EXAMPLE -- ----------- ----------- ------- -- mpoint mount point /home -- fs file system /dev/sda2 -- size size of fs 40G -- used used space 12.6G -- usedp used space in % 31.5% -- avail available space 27.4G -- availp available space in % 68.5% -- The `fslist' holds the file systems (mount points) to be monitored. -- `separator' is placed between the entries in the output. Statistics are -- updated every `update_interval' milliseconds. -- This software is in the public domain. -------------------------------------------------------------------------------- local defaults = { template = "[%mpoint: %avail (%availp) free]", fslist = { "/" }, separator = " ", update_interval = 1000, -- 1 second } local settings = table.join(statusd.get_config("df"), defaults) local df_timer = statusd.create_timer() function math.round(num, idp) local mult = 10^(idp or 0) return math.floor(num * mult + 0.5) / mult end local function guess_mem_unit(amount) amount = tonumber(amount) if (amount < 1024) then return amount .. "k" elseif (amount >= 1024) and (amount < 1048576) then return math.round((amount / 1024), 0) .. "M" elseif (amount > 1048576) then return math.round((amount / 1048576), 1) .. "G" end end local function get_df() local df_table = {} local f = io.popen('df -k', 'r') if (f == nil) then return nil end f:read("*line") -- skip header line local s = f:read("*a") f:close() local i = 0 while (i < string.len(s)) do local j, fsname, fssize, fsused, fsavail, fsusedp, mpoint i, j, fsname, fssize, fsused, fsavail, fsusedp, mpoint = string.find(s, "(/%S+)%s+(%d+)%s+(%d+)%s+(%d+)%s+(%d+)%%?%s(%S+)\n", i) if (i == nil) then return nil end df_table[mpoint] = { mpoint=mpoint, fs=fsname, size=guess_mem_unit(tonumber(fssize)), used=guess_mem_unit(tonumber(fsused)), avail=guess_mem_unit(tonumber(fsavail)), usedp=tonumber(fsusedp), availp=((100 - tonumber(fsusedp)) .. "%") } i = j+1 end return df_table end local function update_df() local t = get_df() if (t == nil) then return nil end local df_str = "" for i=1, #settings.fslist do local s = string.gsub(settings.template, "%%(%w+)", function (arg) if (t[settings.fslist[i]] ~= nil) then return t[settings.fslist[i]][arg] end return nil end) df_str = df_str .. settings.separator .. s end df_str = string.sub(df_str, #settings.separator + 1) statusd.inform("df", df_str) df_timer:set(settings.update_interval, update_df) end update_df() -- EOF notion-3+2012042300/contrib/statusd/statusd_dgs.lua000066400000000000000000000060711174530661200217440ustar00rootroot00000000000000-- -- By Jeremy Hankins -- -- Time-stamp: <2005-06-28 15:55:31 Jeremy Hankins> -- -- statusd_dgs.lua: Check for moves waiting on www.dragongoserver.net -- -- This monitor will check your status page on www.dragongoserver.net -- for games waiting on a move. Simply add %dgs to the statusd -- template, and set a login and password. -- -- In all honesty, I'd be surprised if there are more than a couple of -- people who might use this script -- the intersection of go players -- (and dgs users at that) and ion users can't be that large. -- -- TO USE: Put %dgs in the template in cfg_statusbar.lua (e.g., -- "dgs:% %dgs") and set the login and password. Something like: -- -- dgs={ -- login = "mylogin", -- password = "ABCxyz", -- }, -- -- in mod_statusbar.launch_statusd. -- -- NOTE: the wget command is used to fetch the status page and the -- account info is passed on the command line. This means that you need -- wget for this monitor to work, and that your account info (including -- password) will be available on your local system via ps. If that -- concerns you, don't use this script -- or provide a patch. :) -- -- BUG: Occasionally get_moves returns nil because it gets no output -- from the wget. This is either because I'm not using io.popen() -- properly (I don't have docs for it) or, perhaps, a bug elsewhere. -- When this happens you'll see a ? (with the critical hint set) instead -- of a number for waiting moves. if not statusd_dgs then statusd_dgs = { -- These should be set in cfg_statusbar: --login = "", --password = "", interval = 20*60*1000, -- update every 20 minutes, retry = 60 * 1000, -- on error, retry in a minute -- These shouldn't need to be changed. login_url = "http://www.dragongoserver.net/login.php", --qick_url = "http://www.dragongoserver.net/quick_status.php" } end local timer local meter = "dgs" local settings = table.join(statusd.get_config(meter), statusd_dgs) local check_cmd = "wget -qO - --post-data 'userid="..settings.login .."&passwd="..settings.password.."' '"..settings.login_url.."'" if not settings.login or not settings.password then statusd.warn("You need to set a login and password.") return end -- -- get the move count -- local function get_moves() local f = io.popen(check_cmd, 'r') if not f then return nil end local output = f:read('*a') f:close() if string.len(output) == 0 then return nil end local s, e, count = 0, 0, 0 repeat s, e = string.find(output, 'href="game%.php%?gid=%d+"', e) if s then count = count + 1 end until not s return count end -- -- update the meter -- local function update() local moves = get_moves() if moves then if moves > 0 then statusd.inform(meter.."_hint", "important") else statusd.inform(meter.."_hint", "normal") end statusd.inform(meter, tostring(moves)) timer:set(settings.interval, update) else statusd.inform(meter.."_hint", "critical") statusd.inform(meter, "?") timer:set(settings.retry, update) end end timer = statusd.create_timer() update() notion-3+2012042300/contrib/statusd/statusd_drives.lua000066400000000000000000000025211174530661200224570ustar00rootroot00000000000000-- Disk drives monitor -- %drives - list of currently connected drives -- by Voker57 -- Public domain local defaults={ update_interval=500 } local settings=table.join(statusd.get_config("drives"), defaults) local drives_timer local function get_drives() local lsres = io.popen("ls /dev/sd* | sort") return lsres:lines() end local function update_drives() local drives_list = get_drives() local drive_table = {} for drive in drives_list do if drive:match("/([^/]+)$") then local drive_name = drive:match("/([^/]+)$") local drive_base = drive_name:sub(1,3) local drive_partition = drive_name:sub(4,-1) if drive_table[drive_base] then table.insert(drive_table[drive_base], drive_partition) else drive_table[drive_base] = {drive_partition} end end end local result_table = {} for drive, partitions in pairs(drive_table) do local result = drive local parts = "" for k,i in pairs(partitions) do if i ~= "" then parts = parts..i.." " end end if parts ~= "" then result = result.."["..parts:sub(1,-2).."]" end table.insert(result_table, result) end table.sort(result_table) statusd.inform("drives", table.concat(result_table, " ")) drives_timer:set(settings.update_interval, update_drives) end -- Init drives_timer=statusd.create_timer() update_drives()notion-3+2012042300/contrib/statusd/statusd_exec.lua000066400000000000000000000152171174530661200221150ustar00rootroot00000000000000-- -- statusd_exec.lua -- -- -- -- What is this? -- -- This is a simple "general" statusd Lua script which -- lets you show output of external programs in ion -- statusbar. This is convenient if you are not familiar -- with Lua but can handle yourself with some other -- language enough to output what you would want the -- statusbar to show. -- -- After this script is loaded it starts executing all -- the provided programs (wether a placeholder exists in -- template or not) and updates the statusbar every time -- one of them prints (and flushes) a line. -- -- -- How to use it? -- -- 1 ) update statusbar template to include %exec_something -- 2 ) specify parameters in cfg_statusbar.lua like this: -- -- mod_statusbar.launch_statusd{ -- -- [ ... some other config here ... ], -- -- exec = { -- -- -- show date each second in %exec_date with.. date! -- -- date = { -- program = 'date', -- retry_delay = 1 * 1000, -- }, -- -- -- show percentage of hda1 every 30 seconds in -- -- %exec_hda1u with df, grep and sed. -- -- also highlights entry if usage is high. -- -- hda1u = { -- program = 'df|grep hda1|sed "s/.*\\(..%\\).*/\\1/"', -- retry_delay = 30 * 1000, -- meter_length = 4, -- hint_regexp = { -- important = '9[456].', -- critical = { -- '9[789].', -- '1...', -- }, -- }, -- }, -- -- -- keep continous track of a remote machine in -- -- %exec_remote with ssh, bash and uptime. -- -- if the connection breaks, wait 60 seconds and retry -- -- remote = { -- program = 'ssh -tt myhost.com "while (uptime);' .. -- 'do sleep 1; done"', -- retry_delay = 60 * 1000, -- }, -- } -- } -- -- -- Description of variables: -- -- * program -- The program to execute. The last line of each -- flushed output this program writes to stdout is -- inserted as the content of the meter. -- -- * retry_delay (optional) -- The delay to wait (ms) before running the program again -- once it stops sending data. Some programs just -- send the data and exit while others continue -- sending data periodically. This value can be used -- to control the update rate for the first type of -- programs. You can also specify a value for the other -- type of programs to retry if the program stops for -- some reason. -- -- * meter_length (optional) -- The space reserved for this meter in characters. -- If the value is not specified or is less than 1 -- then the space is determined by the length of the -- data being shown but some people do not want their -- statusbar width to change randomly. -- -- * hint_regexp (optional) -- These regular expressions (in Lua syntax) are -- matched with the data and in case of a match the -- named hint is set to the meter. The value can be -- either a string containing the regexp or a table -- containing several possible regexps (Lua regexp -- doesn't have an 'or'). By default the themes define -- a color for 'important' and 'critical' hints. -- -- local settings = table.join( statusd.get_config("exec"), {} ) local start_execution = nil local timers = {} -- Function for starting and restarting execution of a process start_execution = function ( key ) -- Set up a function for receiving the data -- and informing the statusbar local process_input = function ( new_data ) local received_data = "" -- When we get new data, output the last complete line while new_data do received_data = received_data .. new_data -- Erase old data which we do not need anymore received_data = string.gsub( received_data, ".*\n(.*\n.*)", "%1" ) -- Set the last complete line as the current line local current_line = string.gsub( received_data, "(.*)\n.*", "%1" ) local t_key = "exec_" .. key local t_length = settings[key].meter_length local h_regexp = settings[key].hint_regexp if not tlength or t_length < 1 then t_length = string.len( current_line ) end statusd.inform( t_key .. "_hint", '' ) statusd.inform( t_key .. "_template", string.rep( 'x', t_length ) ) statusd.inform( t_key, current_line ) if not h_regexp then h_regexp = {} end for hint, re in pairs( h_regexp ) do if ( type( re ) == 'string' ) then re = { re } end for _, r in ipairs( re ) do if ( string.find( current_line, r ) ) then statusd.inform(t_key .. "_hint", hint) end end end -- Wait for bgread to signal that more data -- is available or the program has exited new_data = coroutine.yield() end -- Program has exited. -- Start a timer for a new execution if set. if settings[key].retry_delay then timers[key]:set( settings[key].retry_delay, function() start_execution(key) end ) end end -- Set up a simple function for printing errors local process_error = function( new_data ) while new_data do io.stderr:write( "ion-statusd [statusd_exec, key='" .. key .. "']: " .. new_data ) io.stderr:flush() new_data = coroutine.yield() end end -- Execute the program in the background and create -- coroutine functions to handle input and error data statusd.popen_bgread( settings[key].program, coroutine.wrap( process_input ), coroutine.wrap( process_error ) ) end -- Now start execution of all defined processes for key in pairs(settings) do timers[key] = statusd.create_timer() start_execution(key) end -- -- Copyright (c) Antti Vähäkotamäki 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. -- notion-3+2012042300/contrib/statusd/statusd_flashing.lua000066400000000000000000000175611174530661200227700ustar00rootroot00000000000000------------------------------------------------------------------------------------ -- -- DESCRIPTION: -- [ Multi Purpose Monitor for Ion3] -- It detects if security logs, mboxes, maildirs, some files, etcetera were -- changed. If they were changed in fact, shows a flashing (blinking) alarm with a -- message text specified on settings (or default = !!). -- -- If you specify your mail inbox it will do a flashing advise of new email. -- If you specify a security log it will reflect your security warnings. -- -- You could specify whatever files or directories that do you want to monitor. --> All in Unix* like Oses is a file... -- -- PLEASE READ THIS: -- * This is another toy for Ion3. -- ** It is not intended to replace or use Gamin, F.A.M. like libs. -- *** This is not a full whistles and sparkles power security monitor like Tripwire. -- **** It is not a good idea to rely on this script to take care of sensible information. -- ***** The level of recursion is only of one level for directories. -- It only provides a very simple way to know if: -- -- -> Inode number was modified or -- -> Size has changed or -- -> User-group has changed or -- -> Access permisions have changed or -- -> Name changes (including file deletion and moving) then -- ___________________________________________________________ -- It flashes a specified message for 'certain specified time' -- -- PUPOSE: -- To share a quick method for timers control and blinking patterns on Ion3 statusbars, -- I mean, to fill statusbar(s) with annoying moving things. So don't use it ;] -- ------- USAGE EXAMPLE : ------------------------------------------------------------ -- -- If you don't know how to get this working please refer to Ion manual pages, -- Ion home page or some other scripts. Then, write something like this on cfg_statusd.lua : -- -- mod_statusbar.create{ -- template = "whatever.. %flashing ..whatever", --> Modify this part. -- }, -- ----> And add somethig like this to: -- -- mod_statusbar.launch_statusd{ -- -- flashing = { -- files = {"/mnt/Feed_My_Dog", "~/Mail"} --> The intended purpose files (logs, mail) -- log = ".ion3/flashing.log" --> Some file in your $HOME[...] path. -- NOTE: $HOME is assumed by 'log'. -- Paths not in $HOME are invalid. -- update_interval = 3000, --> Time in milliseconds to update info. -- flash_interval = 300, --> Speed of flashing pattern. (msecs.) -- alarm_message = "!!", --> Flashing Message (defaults are a good bet). -- normal_message = "--", --> Normal status message. -- turn_off = 60, --> This, avoids to show the annoying -- message for ever. The value represents -- }, cycles (10 * flash_interval msecs.) -- } ------------------------------------------------------------------------------------ -- -- LICENSE: GPL2 Copyright(C)2006 Mario García H. -- (See http://www.gnu.org/licenses/gpl.html to read complete license) -- -- T.STAMP: Sun Dec 10 02:08:39 2006 -- -- DEPENDS: None at all. -- -- INSECTS: You are the entomologist. You tell me. -- -- NOTES ON USAGE: -- - This script creates his own log of activity. You can choose a name and path on settings. -- - If you remove the log, alarms will cease (rm -f *.log). Is not necessary to restart Ion. -- - If you change the settings, the log will be auto-removed and re-written to reflect the changes -- without false alarms. -- - If, for some impossible circumstance, the status of some file or directory is normal again, -- flashing will cease the annoying flashing thing by him self. -- - If alarms are activated on certain time and you exit Ion current session, the -- next session in Ion you will see the annoying thing on your screen... again. -- - If you exit Ion and then you do changes to a file, the next time on Ion the alarms will do blinking. -- - The minimum number for flashing interval (blink) is 300. -- -- CONTACT: -- G.H. < drosophila (at) Nmental (dot) com> -- ------------------------------------------------------------------------------------ local defaults = { files = { "~/.ion3" }, --> If you like so much Ion, this is the better default. -- If you change this setting, the log will be auto-updated! log = ".ion3/flashing.log", --> Where do you like to log? Paths not in $HOME [...] -- are invalid. If you change this file, please, remove -- your self the last file used. You are warned. -- NOTE: $HOME is assumed by 'log'. alarm_message = "!!", --> Put here the alarm message. normal_message = "--", --> Put here the normal status message. update_interval = 5*1000, --> Check your files every X milliseconds. flash_interval = 400, --> Blinking interval in milliseconds. turn_off = 500, --> Turn Off the alarm if it annoys you too much time: -- (turn_off*flash_interval) milliseconds. --> If you want permanent alarms: 3*999*999 is OK. } local settings = table.join(statusd.get_config("flashing"), defaults) ---SOME INSANE LOCALS :------------------------------------------------------------- local flashing_timer local is_ok = true local flash = false local home = os.getenv("HOME") local message_lenght = string.len(settings.alarm_message) local turn_off = settings.turn_off settings.flash_interval = settings.flash_interval > 299 and settings.flash_interval or 300 ---CONFIRM SOME INFO :-------------------------------------------------------------- local function confirm_someinfo() local keys = table.concat(settings.files, " * ") local log_file = home.."/"..settings.log local check_status = function() local check = io.popen("ls -liL --color=none "..table.concat(settings.files, " ")) local status = check:read("*a"); check:close() return status end local new_log = function() local new = io.open(log_file, "w") if not new then return false else new:write(keys.."\n"..check_status()) new:close() os.execute("chmod 400 "..log_file) new = io.open(log_file, "r") local log = new:read("*a"); new:close() return log end end local check_changes = function() local file = io.open(log_file, "r") if not file then return else local changes = file:read("*l"); file:close() return changes ~= keys and os.execute("rm -f "..log_file) or 1 end end if not turn_off then turn_off = settings.turn_off --> Reinitialize turn_off if flash was done is_ok = true os.execute("rm -f "..log_file) end check_changes() local file = io.open(log_file, "r") local log = file == nil and new_log() or file:read("*a") is_ok = keys.."\n"..check_status() == log return end ---FLASHING PATERNS :--------------------------------------------------------------- local function flash_alarm() turn_off = turn_off > 0 and turn_off - 1 or false --> Regresive shut down counter. -- this is an extra ;> local show = function() statusd.inform("flashing", settings.alarm_message) statusd.inform("flashing_hint", "critical") return true end local hide = function() statusd.inform("flashing", string.rep(".", message_lenght)) return false end flash = flash == false and show() or hide() --> Do the real hide and show (flash) return end ---FUNCTIONS CALLINGS :------------------------------------------------------------- local function update_timer() confirm_someinfo() --> A function checking something ... and changing between states if is_ok then statusd.inform("flashing", settings.normal_message) statusd.inform("flashing_hint", "normal") flashing_timer:set(settings.update_interval, update_timer) --> Take different update_interval else flashing_timer:set(settings.flash_interval, update_timer) --> Take different update_interval flash_alarm() end end ------------------------------------------------------------------------------------ flashing_timer = statusd.create_timer() update_timer() notion-3+2012042300/contrib/statusd/statusd_fortune.lua000066400000000000000000000142671174530661200226570ustar00rootroot00000000000000-- statusd_fortune.lua v20060323 -- -- Displays a fortune (in a sane way for one line). -- My first attempt at writing in lua and for ion. -- -- Written by Hendrik Iben < hiben at tzi dot de > -- -- How to use : -- fortune provides a monitor for statusd. -- This monitor is feed with a fortune obtained from the famous fortune- -- program. But as fortunes can span several lines and the ion-statusbar -- only provides one line there is some conversion done. -- -- As a user you can influence the way this conversion is done by specifying -- with what string empty space and newlines are to be replaced. Or just -- leave the defaults. -- -- For a start you can simple add '%fortune' to your statusbar-template. -- -- Requirements: -- ion3 (tested with 20060305 gentoo ebuild) -- lua (tested with 5.0.2) -- fortune (any version will do) or anything that returns text :-) -- -- Options : -- The settings you can specify (and their defaults) are : -- interval : minimum time between fortune updates (60 seconds) -- minwait : additional time to always wait for a fortune (6 seconds) -- cpers : number of characters to increase waiting by one second (20) -- command : command to obtain a fortune (fortune -s) -- prefix : head of every fortune -- postfix : tail of every fortune -- spacereplace : string that replaces empty space of more than 3 characters -- (' - ') -- newline : string that replaces newlines (' > ') -- newlinetrick : try to be smart when killing newlines (true) -- -- Setting 'interval' and 'minwait' together might seem useless but when -- calculating the waiting time the maximum of 'minwait' and the time -- resulting from 'cpers' is added to 'interval'. The defaults for 'minwait' -- and 'cpers' are taken from fortunes source. -- -- The default command triggers a short fortune (see: man fortune) as long -- fortunes tend to fill more than one line in the status-bar. You may wish -- to enable offending fortunes but I personally don't like them that much... -- In my opinion the statusbar should offer things like scrolling text but -- eventually I will implement this in a later release of this monitor... -- -- Enabling 'newlinetrick' basicly makes this program look at the ending and -- beginning of the two lines and check if they are *meant* to be separated. -- Currently this merges lines that end and begin with lower-case-letters and -- silently removes newlines in front of 'cite-signs' (e.g. -- "...blah\n-- Hendrik" becomes "...blah -- Hendrik"). -- -- Example configuration : -- cfg_statusbar (rotate_statusbar that is) -- ... -- ... -- rotate_statusbar.configurations = { -- ... -- ... -- fortune = { -- prefix = "Fortune : ", -- interval = 30*1000, -- }, -- ... -- ... -- -- rotate_statusbar.settings = { -- ... -- ... -- all_statusbars = { -- "[ %date || %xmmsip_user ]%filler%systray", -- "[ %fortune ]%filler%systray", -- ... -- ... -- -- This setup makes the basic fortune update all 30 seconds and prepends -- every fortune with 'Fortune : '. Also using rotate_statusbar I decided -- to give fortunes the full bar turning of the date-monitor so I don't -- perceive how much time I spend on watching fortunes instead of doing -- more creative things... :-) -- -- Happy fortunes for you! -- -- Feel free to contact me if you discover bugs or want to comment on this. -- -- Hendrik -- -- You are free to distribute this software under the terms of the GNU -- General Public License Version 2. if not statusd_fortune then statusd_fortune={ interval=60*1000, minwait=6*1000, cpers=20, command="fortune -s", prefix="", postfix="", spacereplace=" - ", newline=" > ", newlinetrick=true, } end local settings = table.join (statusd.get_config("fortune"), statusd_fortune) -- do things to strings... -- did I mention I write Haskell normally ? :-) local function flatspace(s) return string.gsub( -- 1. remaining controls string.gsub( -- 2. newlines not covered by newlinetrick string.gsub( -- 3. spaces string.gsub( -- 4. remove trailing control + space string.gsub(s, "^[%c%s]+", "") -- 5. remove leading control + space , "[%c%s]+$" , "" ) -- > 4. , "%s%s%s%s+" , settings.spacereplace ) -- > 3. , "\n+" , settings.newline ) -- > 2. , "%c" , "" ) -- > 1. end -- our timer local fortune_timer -- on error we get the message, but a bit to late... local last_error = "error pending..." -- receives data from settings.command output local function receive_fortune_status(partial_data) local fortunestring = "" while partial_data do fortunestring = fortunestring .. partial_data partial_data = coroutine.yield() end if (not fortunestring) or (fortunestring == "") then -- we get here if e.g. the command was not found... fortunestring = "could not obtain a fortune! ("..last_error..")" end -- perform newline-merging if settings.newlinetrick then -- trick 1 : wrapped sentences fortunestring = string.gsub(fortunestring, "(%l)\n(%l)", "%1 %2") -- trick 2 : 'cite-signs' fortunestring = string.gsub(fortunestring, "([^\n]*)[%c%s]*(--.*)", "%1 %2") end -- flatspace is such a silly name... local f = flatspace(fortunestring) -- calculate time to wait local waittime = settings.interval + math.max( settings.minwait -- minimun ,1000 * (string.len(f) / settings.cpers) -- maybe more ) -- feed the monitor statusd.inform("fortune", settings.prefix..f..settings.postfix) -- update callback fortune_timer:set(waittime, update_fortune) end local function flush_stderr(partial_data) local r = "" while partial_data do r = r .. partial_data partial_data = coroutine.yield() end -- update last error to give the user a hint... last_error = r end --- monitor-update function update_fortune() statusd.popen_bgread( settings.command , coroutine.wrap(receive_fortune_status) , coroutine.wrap(flush_stderr) ) end -- initialize our timer fortune_timer = statusd.create_timer() -- begin update_fortune() notion-3+2012042300/contrib/statusd/statusd_iface.lua000066400000000000000000000034411174530661200222340ustar00rootroot00000000000000-- statusd_iface.lua -- -- Copyright (c) Relu Patrascu 2004. -- -- 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. -- -- Nov. 5, 2004 -- Disclaimer -- Neither am I a lua expert nor do I have the time to invest in writing -- better code here. I simply needed this utility and it works OK for me. -- I give no guarantees of any kind for this code. Suggestions for -- improvement are welcome. -- ikoflexer at gmail dot com if not statusd_iface then statusd_iface={ interval=10*1000 } end local iface local function get_iface_proc() local st, en local f=io.open('/proc/net/wireless', 'r') if not f then return "" end -- first 2 lines -- headers local s=f:read('*l') s=f:read('*l') -- the third line has wifi info s=f:read('*l') if not s then f=io.open('/proc/net/dev', 'r') if not f then return "net off" end -- first 2 lines -- headers, next line check for lo s=f:read('*l') s=f:read('*l') s=f:read('*l') st, en, iface = string.find(s, '(%w+:)') if iface == 'lo:' then s=f:read('*l') end if not s then return "net off" end st, en, iface = string.find(s, '(%w+:)') f:close() return tostring(iface) end st, en, iface = string.find(s, '(%w+:)') f:close() return tostring(iface) end local function get_iface_fn() return get_iface_proc end local get_iface, iface_timer local function update_iface() statusd.inform("iface", get_iface()) iface_timer:set(statusd_iface.interval, update_iface) end -- Init get_iface=get_iface_fn() iface_timer=statusd.create_timer() update_iface() notion-3+2012042300/contrib/statusd/statusd_inetaddr.lua000066400000000000000000000043671174530661200227670ustar00rootroot00000000000000-- statusd_inetaddr.lua -- -- Copyright (c) Relu Patrascu 2004. -- -- 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. -- -- Nov. 5, 2004 -- Disclaimer -- Neither am I a lua expert nor do I have the time to invest in writing -- better code here. I simply needed this utility and it works OK for me. -- I give no guarantees of any kind for this code. Suggestions for -- improvement are welcome. -- ikoflexer at gmail dot com if not statusd_inetaddr then statusd_inetaddr={ interval=10*1000 } end local function get_iface_proc() local st, en, iface local f=io.open('/proc/net/wireless', 'r') if not f then return "" end -- first 2 lines -- headers local s=f:read('*l') s=f:read('*l') -- the third line has wifi info s=f:read('*l') if not s then f=io.open('/proc/net/dev', 'r') if not f then return "net off" end -- first 2 lines -- headers, next line check for lo s=f:read('*l') s=f:read('*l') s=f:read('*l') st, en, iface = string.find(s, '(%w+:)') if iface == 'lo:' then s=f:read('*l') end if not s then return "net off" end st, en, iface = string.find(s, '(%w+:)') f:close() return tostring(iface) end st, en, iface = string.find(s, '(%w+:)') f:close() return tostring(iface) end local function get_inetaddr_ifcfg() local inetaddr local st,en,iface = string.find(tostring(get_iface_proc()), '(%w+):') local f = io.popen("/sbin/ifconfig " .. iface) if not f then inetaddr="" else local ifconfig_info = f:read('*a') f:close() st,en,inetaddr = string.find(ifconfig_info, 'inet addr:(%d+\.%d+\.%d+\.%d+)') if not inetaddr then inetaddr="" end end return tostring(" " .. inetaddr) end local function get_inetaddr_fn() return get_inetaddr_ifcfg end local get_inetaddr, inetaddr_timer local function update_inetaddr() statusd.inform("inetaddr", get_inetaddr()) inetaddr_timer:set(statusd_inetaddr.interval, update_inetaddr) end -- Init get_inetaddr=get_inetaddr_fn() inetaddr_timer=statusd.create_timer() update_inetaddr() notion-3+2012042300/contrib/statusd/statusd_info.lua000066400000000000000000000126751174530661200221310ustar00rootroot00000000000000-- statusd_info.lua -- CPU, Mem, and Swap information script -- Written by Randall Wald -- email: randy@rwald.com -- Released under the GPL -- -- Due to bug in "top b -n 1", uses "top b -n 2 -d 1 -p 0|grep Cpu|tail -n 1" -- to get information about CPU usage and "free" to get information -- about memory and swap usage. -- -- If this script fails, manually check the output of "top b -n 2 -d 1 -p 0|grep -i cpu". -- If it doesn't look similar to the below (in particular, if the -- "Cpu(s):" part is differently capitalized or punctuated), -- change the grep and the regexp in get_CPU_info() to match. -- Cpu(s): 16.9% us, 5.1% sy, 0.0% ni, 70.8% id, 6.5% wa, 0.1% hi, 0.5% si -- PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND -- Cpu(s): 74.5% us, 3.9% sy, 0.0% ni, 19.6% id, 0.0% wa, 0.0% hi, 2.0% si -- PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND -- -- Available monitors: -- %info_CPU_user Percentage of CPU used by user programs -- %info_CPU_system Percentage of CPU used by services -- %info_CPU_idle Percentage of CPU idle -- %info_CPU_ni The time the CPU has spent running users'’processes that have been niced. -- %info_CPU_wa Amount of time the CPU has been waiting for I/O to complete. -- %info_CPU_hi The amount of time the CPU has been servicing hardware interrupts. -- %info_CPU_si The amount of time the CPU has been servicing software interrupts. -- %info_RAM_total Total amount of RAM -- %info_RAM_used Amount of RAM used -- %info_RAM_free Amount of RAM free -- %info_RAM_shared Amount of RAM shared -- %info_RAM_buffers Amount of RAM in buffers -- %info_RAM_cached Amount of RAM cached -- %info_swap_total Total amount of swap -- %info_swap_used Amount of swap currently used -- %info_swap_free Amount of swap currently free -- -- Update Interval: -- (Note that the units are milliseconds) local update_interval = 0.1 * 1000 -- Memory monitors need a factor: -- b - "" -- k - "K" -- m - "M" -- g - "G" local mem_dimension = "M" -- Defines the factor for dividing the memory amount if mem_dimension == "" then mem_factor = 1 elseif mem_dimension == "K" then mem_factor = 1024 elseif mem_dimension == "M" then mem_factor = 1024^2 else mem_factor = 1024^3 end local function get_CPU_info() local f=io.popen('top b -n 2 -d 1 -p 0|grep Cpu|tail -n 1','r') local s=f:read('*all') f:close() local _, _, info_CPU_user, info_CPU_system, info_CPU_ni, info_CPU_idle, info_CPU_wa, info_CPU_hi, info_CPU_si = string.find(s, "Cpu%(s%):%s*(%d+%.%d+%%)%s*us,%s*(%d+%.%d+%%)%s*sy,%s*(%d+%.%d+%%)%s*ni,%s*(%d+%.%d+%%)%s*id,%s*(%d+%.%d+%%)%s*wa,%s*(%d+%.%d+%%)%s*hi,%s*(%d+%.%d+%%)%s*si") return info_CPU_user.."", info_CPU_system.."", info_CPU_ni.."", info_CPU_idle.."", info_CPU_wa.."", info_CPU_hi.."", info_CPU_si.."" end local function process_memory(value) local memory = value / mem_factor -- Truncate to just two digits after the decimal place memory = string.gsub(memory,"(%d+%.%d%d)(%d*)","%1") return memory end local function get_RAM_info() local f=io.popen('free -b','r') local s=f:read('*all') f:close() local _, _, info_RAM_total, info_RAM_used, info_RAM_free, info_RAM_shared, info_RAM_buffers, info_RAM_cached = string.find(s, "Mem:%s+(%d+)%s+(%d+)%s+(%d+)%s+(%d+)%s+(%d+)%s+(%d+)") info_RAM_total = process_memory(info_RAM_total) info_RAM_used = process_memory(info_RAM_used) info_RAM_free = process_memory(info_RAM_free) info_RAM_shared = process_memory(info_RAM_shared) info_RAM_buffers = process_memory(info_RAM_buffers) info_RAM_cached = process_memory(info_RAM_cached) local _, _, info_swap_total, info_swap_used, info_swap_free = string.find(s, "Swap:%s+(%d+)%s+(%d+)%s+(%d+)") info_swap_total = process_memory(info_swap_total) info_swap_used = process_memory(info_swap_used) info_swap_free = process_memory(info_swap_free) return info_RAM_total..mem_dimension, info_RAM_used..mem_dimension, info_RAM_free..mem_dimension, info_RAM_shared..mem_dimension, info_RAM_buffers..mem_dimension, info_RAM_cached..mem_dimension, info_swap_total..mem_dimension, info_swap_used..mem_dimension, info_swap_free..mem_dimension end local function inform_info(name, value) if statusd ~= nil then statusd.inform(name, value) else io.stdout:write(name..": "..value.."\n") end end if statusd ~= nil then status_timer = statusd.create_timer() end local function update_info() local info_CPU_user, info_CPU_system, info_CPU_ni, info_CPU_idle, info_CPU_wa, info_CPU_hi, info_CPU_si = get_CPU_info() local info_RAM_total, info_RAM_used, info_RAM_free, info_RAM_shared, info_RAM_buffers, info_RAM_cached, info_swap_total, info_swap_used, info_swap_free = get_RAM_info() inform_info("info_CPU_user", info_CPU_user) inform_info("info_CPU_system", info_CPU_system) inform_info("info_CPU_ni", info_CPU_ni) inform_info("info_CPU_idle", info_CPU_idle) inform_info("info_CPU_wa", info_CPU_wa) inform_info("info_CPU_hi", info_CPU_hi) inform_info("info_CPU_si", info_CPU_si) inform_info("info_RAM_total", info_RAM_total) inform_info("info_RAM_used", info_RAM_used) inform_info("info_RAM_free", info_RAM_free) inform_info("info_RAM_shared", info_RAM_shared) inform_info("info_RAM_buffers", info_RAM_buffers) inform_info("info_RAM_cached", info_RAM_cached) inform_info("info_swap_total", info_swap_total) inform_info("info_swap_used", info_swap_used) inform_info("info_swap_free", info_swap_free) if statusd ~= nil then status_timer:set(update_interval, update_info) end end update_info() notion-3+2012042300/contrib/statusd/statusd_iwinfo.lua000066400000000000000000000052461174530661200224650ustar00rootroot00000000000000-- statusd_iwinfo.lua -- -- Copyright (c) Relu Patrascu 2004. -- -- 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. -- -- Nov. 5, 2004 -- Disclaimer -- Neither am I a lua expert nor do I have the time to invest in writing -- better code here. I simply needed this utility and it works OK for me. -- I give no guarantees of any kind for this code. Suggestions for -- improvement are welcome. -- ikoflexer at gmail dot com -- Nov 17, 2004 -- ikoflexer at gmail dot com -- -- Made it return a space " " instead of empty string "" -- when /proc/net/wireless doesn't report any interface. -- Otherwise old info lingers in the status bar. if not statusd_iwinfo then statusd_iwinfo = { interval = 10*1000 } end local iwinfo_timer local function get_iwinfo_iwcfg() local f = io.open('/proc/net/wireless', 'r') if not f then return end -- first 2 lines -- headers f:read('*l') f:read('*l') -- the third line has wifi info local s = f:read('*l') f:close() local st, en, iface = 0, 0, 0 if not s then return end st, en, iface = string.find(s, '(%w+):') local f1 = io.popen("/sbin/iwconfig " .. iface) if not f1 then return else local iwOut = f1:read('*a') f1:close() st,en,proto = string.find(iwOut, '(802.11[%-]*%a*)') st,en,ssid = string.find(iwOut, 'ESSID[=:]"([%w+%s*]*)"', en) st,en,bitrate = string.find(iwOut, 'Bit Rate[=:]([%s%w%.]*%/%a+)', en) bitrate = string.gsub(bitrate, "%s", "") st,en,linkq = string.find(iwOut, 'Link Quality[=:](%d+%/%d+)', en) st,en,signal = string.find(iwOut, 'Signal level[=:](%-%d+)', en) st,en,noise = string.find(iwOut, 'Noise level[=:](%-%d+)', en) return proto, ssid, bitrate, linkq, signal, noise end end local function update_iwinfo() local proto, ssid, bitrate, linkq, signal, noise = get_iwinfo_iwcfg() -- In case get_iwinfo_iwcfg doesn't return any values we don't want stupid lua -- errors about concatenating nil values. ssid = ssid or "N/A" bitrate = bitrate or "N/A" linkq = linkq or "N/A" signal = signal or "N/A" noise = noise or "N/A" proto = proto or "N/A" statusd.inform("iwinfo_cfg", ssid.." "..bitrate.." "..linkq.." "..signal.."/"..noise.."dBm "..proto) statusd.inform("iwinfo_ssid", ssid) statusd.inform("iwinfo_bitrate", bitrate) statusd.inform("iwinfo_linkq", linkq) statusd.inform("iwinfo_signal", signal) statusd.inform("iwinfo_noise", noise) statusd.inform("iwinfo_proto", proto) iwinfo_timer:set(statusd_iwinfo.interval, update_iwinfo) end -- Init iwinfo_timer = statusd.create_timer() update_iwinfo() notion-3+2012042300/contrib/statusd/statusd_laptopstatus.lua000066400000000000000000000305111174530661200237260ustar00rootroot00000000000000-- statusd_laptopstatus.lua v0.0.3 (last modified 2011-10-30) -- -- Copyright (C) 2005 Jari Eskelinen -- modified by René van Bevern for error handling -- modified by Juri Hamburg for sysfs support (2011) -- -- Permission to copy, redistirbute, modify etc. is granted under the terms -- of GNU GENERAL PUBLIC LICENSE Version 2. -- -- This is script for displaying some interesting information about your -- laptops power saving in Ion's status monitor. Script is very Linux -- specific (uses procfs or sysfs interface) and needs ACPI-support (don't -- know exactly but 2.6.x kernels should be fine). Also if you have some -- kind of exotic hardware (multiple processors, multiple batteries etc.) -- this script probably will fail or show incorrect information. -- -- The script will try procfs interface first. If that fails, it will try to -- use the sysfs interface. -- -- Just throw this script under ~/.ion3 (or ~/.notion) and add following keywords to your -- cfg_statusbar.lua's template-line with your own taste: -- -- %laptopstatus_cpuspeed -- %laptopstatus_temperature -- %laptopstatus_batterypercent -- %laptopstatus_batterytimeleft -- %laptopstatus_batterydrain -- -- Template example: template="[ %date || load:% %>load || CPU: %laptopstatus_cpuspeed %laptopstatus_temperature || BATT: %laptopstatus_batterypercent %laptopstatus_batterytimeleft %laptopstatus_batterydrain ]" -- -- You can also run this script with lua interpreter and see if you get -- right values. -- -- NOTICE: This is my first ion/lua-script, so probably this can be done better. -- Feel free to improve this script. -- -- TODO: * Is it stupid to use file:read("*all"), can this cause infinite -- loops in some weird situations? -- * Do not poll for information not defined in template to be used -- * Auto-detect right acpi devices instead of hardcoded BATT0 etc. -- -- SETTINGS -- NA = "n/a" AC = "*AC*" if not statusd_laptopstatus then statusd_laptopstatus = { interval = 10, -- Polling interval in seconds temperature_important = 66, -- Temperature which will cause important hint temperature_critical = 71, -- Temperature which will cause critical hint batterypercent_important = 10, -- Battery percent which will cause important hint batterypercent_critical = 5, -- Battery percent which will cause critical hint batterytimeleft_important = 600, -- Battery time left (in secs) which will cause important hint batterytimeleft_critical = 300, -- Battery time left (in secs) which will cause critical hint --procfs ac_state = {"/proc/acpi/ac_adapter/AC/state", "/proc/acpi/ac_adapter/ACAD/state", "/proc/acpi/ac_adapter/ADP0/state", "/proc/acpi/ac_adapter/ADP1/state"}, temp_info = {"/proc/acpi/thermal_zone/THRM/temperature", "/proc/acpi/thermal_zone/THM/temperature", "/proc/acpi/thermal_zone/THM0/temperature", "/proc/acpi/thermal_zone/THM1/temperature"}, bat_info = {"/proc/acpi/battery/BAT0/info", "/proc/acpi/battery/BAT1/info"}, bat_state = {"/proc/acpi/battery/BAT0/state", "/proc/acpi/battery/BAT1/state"}, --sysfs temp_info_sysfs = {"/sys/class/thermal/thermal_zone0/temp"}, ac_state_sysfs = {"/sys/class/power_supply/AC/online"}, bat_now_sysfs = {"/sys/class/power_supply/BAT0/energy_now"}, bat_full_sysfs = {"/sys/class/power_supply/BAT0/energy_full"} } end statusd_laptopstatus=table.join(statusd.get_config("laptopstatus"), statusd_laptopstatus) -- -- CODE -- local laptopstatus_timer local RingBuffer = {} RingBuffer.__index = RingBuffer function RingBuffer.create(size) local buf = {} setmetatable(buf,RingBuffer) buf.size = size buf.elements = {} buf.current = 1 return buf end function RingBuffer:add(val) if self.current > self.size then self.current = 1 end self.elements[self.current] = val self.current = self.current + 1 end function average(array) local sum = 0 for i,v in ipairs(array) do sum = sum + v end if table.getn(array) ~= 0 then return sum / table.getn(array) else return 0 end end rates_buffer = RingBuffer.create(20) function try_open(files, mode) for _, file in pairs(files) do local fd = io.open(file, mode) if fd then return fd, file end end end local function get_cpu() local mhz, hint if pcall(function () local status local file = io.open("/proc/cpuinfo", "r") status, _, mhz = string.find(file:read("*all"), "cpu MHz%s+: (%d+)") if not status then error("could not get MHz") end file:close() end) then return {speed=string.format("%4dMHz", math.ceil(mhz/5)*5), hint=hint} else return {speed=NA, hint=hint} end end local function get_ac() local file = try_open(statusd_laptopstatus.ac_state, "r") local ac_str = file:read("*all") file:close() if not string.find(ac_str, "state:%s+on.line") then return 0 else return 1 end end local function get_ac_sysfs() local file = try_open(statusd_laptopstatus.ac_state_sysfs, "r") if file == nil then return 0 end local ac_on = tonumber(file:read("*all")) file:close() return ac_on end local function get_thermal_sysfs() local temp, hint = nil, "normal" if pcall(function () local file = try_open(statusd_laptopstatus.temp_info_sysfs, "r") temp = file:read("*all") temp = tonumber(temp)/1000 file:close(); end) then if temp >= statusd_laptopstatus.temperature_important then hint = "important" end if temp >= statusd_laptopstatus.temperature_critical then hint = "critical" end return {temperature=string.format("%02dC", temp), hint=hint} else return {temperature=NA, hint = "hint"} end end local function get_thermal_procfs() local temp, hint = nil, "normal" if pcall(function () local status local file=try_open(statusd_laptopstatus.temp_info, "r") status, _, temp = string.find(file:read("*all"), "temperature:%s+(%d+).*") if not status then error("could not get temperature") end temp = tonumber(temp) file:close(); end) then if temp >= statusd_laptopstatus.temperature_important then hint = "important" end if temp >= statusd_laptopstatus.temperature_critical then hint = "critical" end return {temperature=string.format("%02dC", temp), hint=hint} else return {temperature=NA, hint = "hint"} end end local function get_thermal() if try_open(statusd_laptopstatus.temp_info) ~= nil then return get_thermal_procfs() else return get_thermal_sysfs() end end local rate_sysfs local last_power local function get_battery_sysfs() local percenthint = "normal" local timelefthint = "normal" local full, now if pcall(function () local file=try_open(statusd_laptopstatus.bat_now_sysfs) now = tonumber(file:read("*all")) file:close(); local file=try_open(statusd_laptopstatus.bat_full_sysfs) full = tonumber(file:read("*all")) end) then local percent = math.floor(now / full * 100 + 5/10) local timeleft if get_ac_sysfs() == 1 then timeleft = AC elseif last_power ~= nil and now < last_power then rate_sysfs = last_power - now rates_buffer:add(rate_sysfs) secs = statusd_laptopstatus.interval * (now / average(rates_buffer.elements)) mins = secs / 60 hours = math.floor(mins / 60) mins = math.floor(mins - (hours * 60)) timeleft = string.format("%02d:%02d", hours, mins) else timeleft = NA end last_power = now if percent <= statusd_laptopstatus.batterypercent_important then percenthint = "important" end if percent <= statusd_laptopstatus.batterypercent_critical then percenthint = "critical" end return { percent=string.format("%02d%%", percent), timeleft=timeleft, drain=NA, percenthint=percenthint, timelefthint=timelefthint} else return { percent=NA, timeleft=NA, drain=NA, percenthint=percenthint, timelefthint=timelefthint} end end local function get_battery_procfs() local percenthint = "normal" local timelefthint = "normal" local lastfull, rate, rateunit, remaining if pcall(function () local status local file=try_open(statusd_laptopstatus.bat_info, "r") local infocontents = file:read("*all") file:close(); local file=try_open(statusd_laptopstatus.bat_state, "r") local statecontents = file:read("*all") file:close(); status, _, lastfull = string.find(infocontents, "last full capacity:%s+(%d+).*") if not status then error("could not get full battery capacity") end lastfull = tonumber(lastfull) if string.find(statecontents, "present rate:%s+unknown.*") then rate = -1 else status, _, rate, rateunit = string.find(statecontents, "present rate:%s+(%d+)(.*)") if not status then error("could not get battery draining-rate") end rate = tonumber(rate) end status, _, remaining = string.find(statecontents, "remaining capacity:%s+(%d+).*") if not status then error("could not get remaining capacity") end remaining = tonumber(remaining) end) then local percent = math.floor(remaining / lastfull * 100 + 5/10) local timeleft local hours, secs, mins if get_ac() == 1 then timeleft = AC elseif rate <= 0 then timeleft = NA else secs = 3600 * (remaining / rate) mins = secs / 60 hours = math.floor(mins / 60) mins = math.floor(mins - (hours * 60)) timeleft = string.format("%02d:%02d", hours, mins) end if secs ~= nil and secs <= statusd_laptopstatus.batterytimeleft_important then timelefthint = "important" end if secs ~= nil and secs <= statusd_laptopstatus.batterytimeleft_critical then timelefthint = "critical" end if percent <= statusd_laptopstatus.batterypercent_important then percenthint = "important" end if percent <= statusd_laptopstatus.batterypercent_critical then percenthint = "critical" end return { percent=string.format("%02d%%", percent), timeleft=timeleft, drain=tostring(rate)..rateunit, percenthint=percenthint, timelefthint=timelefthint} else return { percent=NA, timeleft=NA, drain=NA, percenthint=percenthint, timelefthint=timelefthint} end end --if no procfs battery state is found, try sysfs local function get_battery() if try_open(statusd_laptopstatus.bat_state) ~= nil then return get_battery_procfs() else return get_battery_sysfs() end end local last_timeleft = nil local function update_laptopstatus () cpu = get_cpu() thermal = get_thermal() battery = get_battery() -- Informing statusd OR printing to stdout if statusd not present if statusd ~= nil then statusd.inform("laptopstatus_cpuspeed", cpu.speed) statusd.inform("laptopstatus_cpuspeed_template", "xxxxMHz") statusd.inform("laptopstatus_cpuspeed_hint", cpu.hint) statusd.inform("laptopstatus_temperature", thermal.temperature) statusd.inform("laptopstatus_temperature_template", "xxxC") statusd.inform("laptopstatus_temperature_hint", thermal.hint) statusd.inform("laptopstatus_batterypercent", battery.percent) statusd.inform("laptopstatus_batterypercent_template", "xxx%") statusd.inform("laptopstatus_batterypercent_hint", battery.percenthint) if battery.timeleft ~= NA or last_timeleft == AC then statusd.inform("laptopstatus_batterytimeleft", battery.timeleft) last_timeleft = battery.timeleft end statusd.inform("laptopstatus_batterytimeleft_template", "hh:mm") statusd.inform("laptopstatus_batterytimeleft_hint", battery.timelefthint) statusd.inform("laptopstatus_batterydrain", battery.drain) laptopstatus_timer:set(statusd_laptopstatus.interval*1000, update_laptopstatus) else io.stdout:write("CPU: "..cpu.speed.." "..thermal.temperature.." || BATT: "..battery.percent.." "..battery.timeleft.."\n") end end if statusd ~= nil then laptopstatus_timer = statusd.create_timer() end update_laptopstatus() notion-3+2012042300/contrib/statusd/statusd_linuxbatt.lua000066400000000000000000000041361174530661200232010ustar00rootroot00000000000000-- statusd_linuxbatt.lua -- -- Public domain -- -- Uses the /proc/acpi interface to get battery percentage. -- -- Use the key "linuxbatt" to get the battery percentage; use -- "linuxbatt_state" to get a symbol indicating charging "+", -- discharging "-", or charged " ". -- -- Now uses lua functions instead of bash, awk, dc. MUCH faster! -- -- The "bat" option to the statusd settings for linuxbatt modifies which -- battery we look at. local defaults={ update_interval=15*1000, bat=0, important_threshold=30, critical_threshold=10, } local settings=table.join(statusd.get_config("linuxbatt"), defaults) function linuxbatt_do_find_capacity() local f=io.open('/proc/acpi/battery/BAT'.. settings.bat ..'/info') local infofile=f:read('*a') f:close() local i, j, capacity = string.find(infofile, 'last full capacity:%s*(%d+) .*') return capacity end local capacity = linuxbatt_do_find_capacity() function get_linuxbatt() local f=io.open('/proc/acpi/battery/BAT'.. settings.bat ..'/state') local statefile=f:read('*a') f:close() local i, j, remaining = string.find(statefile, 'remaining capacity:%s*(%d+) .*') local percent = math.floor( remaining * 100 / capacity ) local i, j, statename = string.find(statefile, 'charging state:%s*(%a+).*') if statename == "charging" then return percent, "+" elseif statename == "discharging" then return percent, "-" else return percent, " " end end function update_linuxbatt() local perc, state = get_linuxbatt() statusd.inform("linuxbatt", tostring(perc)) statusd.inform("linuxbatt_state", state) if perc < settings.critical_threshold then statusd.inform("linuxbatt_hint", "critical") elseif perc < settings.important_threshold then statusd.inform("linuxbatt_hint", "important") else statusd.inform("linuxbatt_hint", "normal") end linuxbatt_timer:set(settings.update_interval, update_linuxbatt) end linuxbatt_timer = statusd.create_timer() update_linuxbatt() notion-3+2012042300/contrib/statusd/statusd_maildir.lua000066400000000000000000000054371174530661200226150ustar00rootroot00000000000000-- statusd_maildir.lua - Gets new and total counts of mails in a Maildir structure -- Copyright (c) 2005 Brett Parker -- -- 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -- This exports the variables %maildir_MAILDIRNAME, %maildir_MAILDIRNAME_new -- and %maildir_MAILDIRNAME_total to the status bar where MAILDIRNAME is -- the key in the maildirs setting. -- -- The 2 settings available in the cfg_statusbar.lua are: -- interval - this is the number of milliseconds between each check -- maildirs - this is a key value list of Maildirs, the key is used -- for MAILDIRNAME above. -- -- The defaults update every 10 seconds with a maildir of ~/Maildir/ if not statusd_maildir then statusd_maildir={ interval=10000, maildirs = {INBOX="~/Maildir/"}, } end local settings = table.join (statusd.get_config("maildir"), statusd_maildir) local function get_num_files(directory) local f = io.popen('/bin/ls -U1 '..directory, 'r') local count = 0 local line = f:read() if line then repeat count = count + 1 line = f:read() until not line end f:close() return count end local function get_maildir_counts(maildir) local newcount = get_num_files(maildir..'/new/') local curcount = get_num_files(maildir..'/cur/') return newcount, newcount + curcount end local maildir_timer local function update_maildir() for key, maildir in pairs(settings.maildirs) do local new, total = get_maildir_counts(maildir) statusd.inform("maildir_"..key, new.."/"..total) statusd.inform("maildir_"..key.."_new", tostring(new)) statusd.inform("maildir_"..key.."_total", tostring(total)) if new>0 then statusd.inform("maildir_"..key.."_hint", "important") statusd.inform("maildir_"..key.."_new_hint", "important") statusd.inform("maildir_"..key.."_total_hint", "important") else statusd.inform("maildir_"..key.."_hint", "normal") statusd.inform("maildir_"..key.."_new_hint", "normal") statusd.inform("maildir_"..key.."_total_hint", "normal") end end maildir_timer:set(settings.interval, update_maildir) end maildir_timer = statusd.create_timer() update_maildir() notion-3+2012042300/contrib/statusd/statusd_mcpu.lua000066400000000000000000000106231174530661200221310ustar00rootroot00000000000000-- -- statusd_mcpu.lua - multi CPU monitor for ion3 statusbar -- -- version 0.1 by Jakub RužiÄka , 2008-08-14 -- -- -- Small and hopefully efficient monitor for multiple CPU using Linux -- /proc/stat. -- -- in cfg_statusbar.lua you can -- -- use following meters: -- %mcpu - avearge load of all CPUs -- %mcpu_n - average load of n-th CPU (zero-indexed - %mcpu_0, %mcpu_1 etc.) -- -- set options: -- interval - update interval -- prc_char - percent character (may be '') -- important_threshold - important hint above this % ( >100 to disable ) -- critical_threshold - critical hint above this % ( >100 to disable ) -- -- for example you can put the following in your template for dual core system: -- template = ' cpu: %mcpu [ %mcpu_0, %mcpu_1 ] ' -- -- NOTES: -- -- * It's very easy to add avg and per-cpu user, nice, system, idle, iowait, irq -- and softirg meters using a_* values as described in update_mcpu() but I -- don't want/need these so you have to add them yourself. -- -- * Script will (or should) work for one CPU but will produce mcpu and -- redundant mcpu_0. If you want it to be efficient on one CPU, just edit it or -- if you think the script should take care of it(extra ifs... pfff), mail me. -- local defaults = { interval = 1000, prc_char = '%', important_threshold = 60, critical_threshold = 90, } local settings = table.join(statusd.get_config("mcpu"), defaults) -- how many CPUs? local f=io.popen("cat /proc/stat | grep '^cpu' | wc -l","r") if not f then print "Failed to find out number of CPUs." return 0 end local cpus = tonumber( f:read('*a') ) - 1 f:close() -- ugly global init :o] user, nice, system, idle, iowait, irq, softirq = {}, {}, {}, {}, {}, {}, {} a_user, a_nice, a_system, a_idle, a_iowait, a_irq, a_softirq = {}, {}, {}, {}, {}, {}, {} for i = 0,cpus do user[i], nice[i], system[i], idle[i], iowait[i], irq[i], softirq[i] = 0, 0, 0, 0, 0, 0, 0 a_user[i], a_nice[i], a_system[i], a_idle[i], a_iowait[i], a_irq[i], a_softirq[i] = 0, 0, 0, 0, 0, 0, 0 end -- refresh values function mcpu_refresh() local i=0 -- not sure if breaking this cycle closes file automaticaly as it should... for line in io.lines( '/proc/stat' ) do suc, _, n_user, n_nice, n_system, n_idle, n_iowait, n_irq, n_softirq = string.find( line, '^cpu%d?%s+(%d+)%s+(%d+)%s+(%d+)%s+(%d+)%s+(%d+)%s+(%d+)%s+(%d+)%s+%d+%s+%d+%s*$' ) if suc then -- current values -- a_user[i], a_nice[i], a_system[i], a_idle[i], a_iowait[i], a_irq[i], a_softirq[i] = n_user - user[i], n_nice - nice[i], n_system - system[i], n_idle - idle[i], n_iowait - iowait[i], n_irq - irq[i], n_softirq - softirq[i] -- last -- user[i], nice[i], system[i], idle[i], iowait[i], irq[i], softirq[i] = n_user, n_nice, n_system, n_idle, n_iowait, n_irq, n_softirq else if i < cpus then -- this will probably suck on single CPU systems print( string.format( "Getting CPU info for all CPUs failed. (i=%d,cpus=%d)", i, cpus) ) break end end i = i + 1 end end --- main --- local mcpu_timer = statusd.create_timer() local function update_mcpu() local all local prc = {}, lprc mcpu_refresh() for i = 0,cpus do all = a_user[i] + a_nice[i] + a_system[i] + a_idle[i] + a_iowait[i] + a_irq[i] + a_softirq[i] prc[i] = math.floor( (1.0 - a_idle[i]/all ) * 100 + 0.5 ) end -- summary for all CPUs lprc = prc[0] statusd.inform('mcpu', lprc..settings.prc_char ) -- you can put here something like: -- statusd.inform('mcpu_user', a_user[0]..settings.prc_char ) -- hint if lprc >= settings.critical_threshold then statusd.inform( "mcpu_hint", "critical" ) elseif lprc >= settings.important_threshold then statusd.inform( "mcpu_hint", "important" ) else statusd.inform( "mcpu_hint", "normal" ) end -- each CPU (zero indexed) for i = 1,cpus do lprc = prc[i] statusd.inform('mcpu_'..(i-1), lprc..settings.prc_char ) -- again, this is possible: -- statusd.inform('mcpu_'..(i-1)..'_user', a_user[i]..settings.prc_char ) -- hint local hint='mcpu_'..(i-1)..'_hint' if lprc >= settings.critical_threshold then statusd.inform( hint, "critical" ) elseif lprc >= settings.important_threshold then statusd.inform( hint, "important" ) else statusd.inform( hint, "normal" ) end end mcpu_timer:set(settings.interval, update_mcpu) end update_mcpu() notion-3+2012042300/contrib/statusd/statusd_mem.lua000066400000000000000000000077271174530661200217560ustar00rootroot00000000000000------------------------------------------------------------------------------------------- -- -- PURPOSE: -- Shows system available memory catching [free] command outputs. -- It is intended to make it simpler than statusd_meminfo, plus user configurable -- measurement units and alarms for "all" available memory metters. -- -- USAGE: -- Just set any of the following labels on cfg_statusbar.lua: %mem_hused, %mem_shared -- %mem_free, %mem_hfree, %mem_swap, %mem_used, %mem_cached. Example: [MF: %mem_free] -- -- MEANINGS: --> ** "mem_hfree" poses as "htop free memory" or "mem_free +cached +buffers", -- in oposition, "mem_hused" is "mem_used -cached -buffers"; other labels have -- transparent meanings. -- ------- CONFIG EXAMPLE: ------------------------------------------------------------------ -- -- To modify settings is quite simple and flexible, write (on cfg_statusbar.lua) -- something like this, without comments: -- mem = { -- update_interval = 15*1000, --> Milliseconds -- free_alarm = 25, --> Limits percentaje ... -- used_alarm = 65, -- units = "m" --> "g" or "k" too -- } -- Write only the settings that do you want to change or leave this section as is... --> ** "update_interval" means "time in milliseconds to update info (default = 15)" -- "xx_alarm" means "do a color advise when memory *percentage* reaches this value". -- (both defaults are 50). "units" means Gb "g", Mb "m" or Kb "k" (default = "m") ------------------------------------------------------------------------------------------ -- -- NOTES: -- * Alarms for used memory are inverse to alarms for free memory (think about it...) -- "mem_total" label is useless. If total memory varies, its time to open your -- hardware and check this script from barebone. Seriously, may be your video or wifi -- devices were claiming some free R.A.M. on your machine start-up. -- However, I included "mem_total" just in case. -- ** This script has non blocking I/O. -- -- LICENSE: -- GPL2 Copyright(C)2006 Mario Garcia H. -- (Please see http://www.gnu.org/licenses/gpl.html to read complete license) -- -- T.STAMP: Thu Dec 7 03:28:04 2006 -- -- DEPENDS: "free" command. Probably, all GNU/Linux distros have one. -- -- INSECTS: Not known. -- -- CONTACT: -- G.H. -- ------- DEFAULT SETTINGS :----------------------------------------------------------------- local mem_timer local defaults = { update_interval = 15*1000, free_alarm = 50, used_alarm = 50, units = "m" } local settings = table.join(statusd.get_config("mem"), defaults) ------- MEM MONITOR :---------------------------------------------------------------------- local function show_meminfo(status) while status do local ok, _, total, used, free, shared, buffers, cached =-- string.find(status, "Mem:%s+(%d+)%s+(%d+)%s+(%d+)%s+(%d+)%s+(%d+)%s+(%d+)") -- if not ok then statusd.inform("mem_template", "--") return end -- statusd.inform("mem_total", total) statusd.inform("mem_used", used) statusd.inform("mem_free", free) statusd.inform("mem_shared", shared) statusd.inform("mem_buffers", buffers) statusd.inform("mem_cached", cached) statusd.inform("mem_hused", tostring(used - cached - buffers)) statusd.inform("mem_hfree", tostring(free + cached + buffers)) -- statusd.inform("mem_used_hint", used*100/total >= settings.used_alarm and "critical" or "important") statusd.inform("mem_hused_hint", (used - cached - buffers)*100/total >= settings.used_alarm and "critical" or "important") statusd.inform("mem_free_hint", free*100/total <= settings.free_alarm and "critical" or "important") statusd.inform("mem_hfree_hint", (free + cached + buffers)*100/total <= settings.free_alarm and "critical" or "important") -- status = coroutine.yield() end end local function update_mem() statusd.popen_bgread("free -"..settings.units.."o", coroutine.wrap(show_meminfo)) mem_timer:set(settings.update_interval, update_mem) end mem_timer = statusd.create_timer() update_mem() notion-3+2012042300/contrib/statusd/statusd_meminfo.lua000066400000000000000000000070551174530661200226240ustar00rootroot00000000000000-- $Id: statusd_meminfo.lua 59 2006-11-14 11:17:02Z tibi $ -- statusd_meminfo.lua -- memory and swap usage monitor for Ion3's statusbar -- version : 0.1 -- date : 2006-11-14 -- author : Tibor Csögör -- Shows the memory and swap usage of the system. -- This script depends on the /proc filesystem and thus only works on Linux. -- Tested with kernel 2.6.16. -- Configuration: -- The placeholders `mem_total', `mem_used', `mem_free', `mem_buffers', -- `mem_cached', `swap_total', `swap_used' and `swap_free' hold the same -- information as the corresponding fieds in top(1). `mem_used_adj' and -- `mem_free_adj' are adjusted values, here buffers and cached memory count as -- free. Placeholders suffixed with `_p' yield percentage values. -- Example usage: -- "MEM: %meminfo_mem_free_adj free, SWAP: %meminfo_swap_used used". -- This software is in the public domain. -------------------------------------------------------------------------------- local defaults = { update_interval = 1000, -- 1 second } local settings = table.join(statusd.get_config("meminfo"), defaults) local meminfo_timer = statusd.create_timer() function math.round(num, idp) local mult = 10^(idp or 0) return math.floor(num * mult + 0.5) / mult end local function guess_mem_unit(amount) amount = tonumber(amount) if (amount < 1024) then return amount .. "k" elseif (amount >= 1024) and (amount < 1048576) then return math.round((amount / 1024)) .. "M" elseif (amount > 1048576) then return math.round((amount / 1048576), 1) .. "G" end end local function get_meminfo() local meminfo_table = {} local f = io.open('/proc/meminfo', 'r') if (f == nil) then return nil end local s = f:read("*a") f:close() local i = 0 while (i < string.len(s)) do local j, k, v i, j, k, v = string.find(s, "([%w_]+):%s+(%d+) kB\n", i) if (i == nil) then return nil end meminfo_table[k] = tonumber(v) i = j+1 end return meminfo_table end local function update_meminfo() local t = get_meminfo() if (t == nil) then return nil end statusd.inform("meminfo_mem_total", guess_mem_unit(t.MemTotal)) statusd.inform("meminfo_mem_used", guess_mem_unit(t.MemTotal - t.MemFree)) statusd.inform("meminfo_mem_used_p", math.round(((t.MemTotal-t.MemFree)/t.MemTotal)*100) .. "%") statusd.inform("meminfo_mem_used_adj", guess_mem_unit(t.MemTotal-(t.MemFree+t.Buffers+t.Cached))) statusd.inform("meminfo_mem_used_adj_p", math.round( ((t.MemTotal-(t.MemFree+t.Buffers+t.Cached))/t.MemTotal)*100) .. "%") statusd.inform("meminfo_mem_free", guess_mem_unit(t.MemFree)) statusd.inform("meminfo_mem_free_p", math.round((t.MemFree/t.MemTotal)*100) .. "%") statusd.inform("meminfo_mem_free_adj", guess_mem_unit(t.MemFree+t.Buffers+t.Cached)) statusd.inform("meminfo_mem_free_adj_p", math.round(((t.MemFree+t.Buffers+t.Cached)/t.MemTotal)*100) .. "%") statusd.inform("meminfo_mem_buffers", guess_mem_unit(t.Buffers)) statusd.inform("meminfo_mem_cached", guess_mem_unit(t.Cached)) statusd.inform("meminfo_swap_total", guess_mem_unit(t.SwapTotal)) statusd.inform("meminfo_swap_free", guess_mem_unit(t.SwapFree)) statusd.inform("meminfo_swap_free_p", math.round((t.SwapFree/t.SwapTotal)*100) .. "%") statusd.inform("meminfo_swap_used", guess_mem_unit(t.SwapTotal-t.SwapFree)) statusd.inform("meminfo_swap_used_p", math.round(((t.SwapTotal-t.SwapFree)/t.SwapTotal)*100) .. "%") meminfo_timer:set(settings.update_interval, update_meminfo) end update_meminfo() -- EOF notion-3+2012042300/contrib/statusd/statusd_moc.lua000066400000000000000000000146771174530661200217600ustar00rootroot00000000000000-- statusd_moc.lua -- -- Public domain -- -- keys: -- moc (moc_title + "[paused]", "[stopped]", or "[not playing]") -- moc_state ("PAUSE", "PLAY", "STOP", or "OFF") -- moc_state2 ("paused", "playing", "stopped", or "off") -- moc_file } -- moc_title } -- moc_artist } -- moc_songtitle } -- moc_album } -- moc_totaltime }-- (as in "mocp -i") -- moc_currenttime } -- moc_timeleft } -- moc_totalsec } -- moc_currentsec } -- moc_bitrate } -- moc_rate } -- moc_leftsec (moc_totalsec - moc_currentsec) -- moc_percent (current time / total time) -- moc_progress (a progress bar, made of characters---see settings below) -- -- settings: -- update_interval -- moc_dir (relative to ion's working directory) -- progress_char1 (character for unfilled progress bar area) -- progress_char2 (character for filled progress bar area) -- progress_length (length of the progress bar in characters) -- color_hints (boolean-- use color hints for the "state" montitors) local home=os.getenv("HOME") home=(home and home.."/" or "") local defaults={ update_interval=3*1000, moc_dir=home..".moc", progress_char1=" ", progress_char2="|", progress_length="50", color_hints=false, } local settings=table.join(statusd.get_config("moc"), defaults) function do_query_mocp() local f=io.popen('mocp -i') local moc_status = f:read('*a') f:close() local i, j, moc_state = string.find( moc_status, 'State: (%C*)') local i, j, moc_file = string.find( moc_status, 'File: (%C*)') local i, j, moc_title = string.find( moc_status, 'Title: (%C*)') local i, j, moc_artist = string.find( moc_status, 'Artist: (%C*)') local i, j, moc_songtitle = string.find( moc_status, 'SongTitle: (%C*)') local i, j, moc_album = string.find( moc_status, 'Album: (%C*)') local i, j, moc_totaltime = string.find( moc_status, 'TotalTime: (%C*)') local i, j, moc_currenttime = string.find( moc_status, 'CurrentTime: (%C*)') local i, j, moc_timeleft = string.find( moc_status, 'TimeLeft: (%C*)') local i, j, moc_totalsec = string.find( moc_status, 'TotalSec: (%C*)') local i, j, moc_currentsec = string.find( moc_status, 'CurrentSec: (%C*)') local i, j, moc_bitrate = string.find( moc_status, 'Bitrate: (%C*)') local i, j, moc_rate = string.find( moc_status, 'Rate: (%C*)') if moc_state == "STOP" then return "[stopped]", moc_state, "stopped", mocp_not_playing() else local moc_leftsect, moc_percent, moc_progress = "--", "--", "--" if moc_totalsec and moc_currentsec then moc_leftsec = tostring(moc_totalsec - moc_currentsec) moc_percent = tostring(math.floor(moc_currentsec * 100 / moc_totalsec)) moc_progress = moc_make_progress_bar(moc_percent) end if moc_state == "PLAY" then return moc_title, moc_state, "playing", moc_file, moc_title, moc_artist, moc_songtitle, moc_album, moc_totaltime, moc_currenttime, moc_timeleft, moc_totalsec, moc_currentsec, moc_bitrate, moc_rate, moc_leftsec, moc_percent, moc_progress else return moc_title.." [paused]" , moc_state, "paused", moc_file, moc_title, moc_artist, moc_songtitle, moc_album, moc_totaltime, moc_currenttime, moc_timeleft, moc_totalsec, moc_currentsec, moc_bitrate, moc_rate, moc_leftsec, moc_percent, moc_progress end end end function moc_make_progress_bar(p) local total_chars = settings.progress_length local filled_chars = math.ceil(p * total_chars / 100) local str = "" for i=1,filled_chars do str = str..settings.progress_char2 end for i=filled_chars+1,total_chars do str = str..settings.progress_char1 end return str end function mocp_not_playing() return "--", "--", "--", "--", "--", "--:--", "--:--", "--:--", "--", "--", "--", "--", "--", "--", moc_make_progress_bar(0) end function get_moc() local f=io.open(settings.moc_dir.."/pid") if f then f:close() return do_query_mocp() else return "[not running]", "OFF", "off", mocp_not_playing() end end function update_moc() local moc, moc_state, moc_state2, moc_file, moc_title, moc_artist, moc_songtitle, moc_album, moc_totaltime, moc_currenttime, moc_timeleft, moc_totalsec, moc_currentsec, moc_bitrate, moc_rate, moc_leftsec, moc_percent, moc_progress = get_moc() statusd.inform("moc", moc) statusd.inform("moc_state", moc_state) statusd.inform("moc_state2", moc_state2) if settings.color_hints then if moc_state2 == "playing" then statusd.inform("moc_state_hint", "green") statusd.inform("moc_state2_hint", "green") elseif moc_state2 == "paused" then statusd.inform("moc_state_hint", "yellow") statusd.inform("moc_state2_hint", "yellow") elseif moc_state2 == "stopped" then statusd.inform("moc_state_hint", "red") statusd.inform("moc_state2_hint", "red") elseif moc_state2 == "off" then statusd.inform("moc_state_hint", "grey") statusd.inform("moc_state2_hint", "grey") end end statusd.inform("moc_file", moc_file) statusd.inform("moc_title", moc_title) statusd.inform("moc_artist", moc_artist) statusd.inform("moc_songtitle", moc_songtitle) statusd.inform("moc_album", moc_album) statusd.inform("moc_totaltime", moc_totaltime) statusd.inform("moc_currenttime", moc_currenttime) statusd.inform("moc_timeleft", moc_timeleft) statusd.inform("moc_totalsec", moc_totalsec) statusd.inform("moc_currentsec", moc_currentsec) statusd.inform("moc_bitrate", moc_bitrate) statusd.inform("moc_rate", moc_rate) statusd.inform("moc_leftsec", moc_leftsec) statusd.inform("moc_percent", moc_percent) statusd.inform("moc_progress", moc_progress) moc_timer:set(settings.update_interval, update_moc) end moc_timer = statusd.create_timer() update_moc() -- vim:tw=0: notion-3+2012042300/contrib/statusd/statusd_mocmon.lua000066400000000000000000000222761174530661200224640ustar00rootroot00000000000000-- statusd_mocmon.lua v20060323 -- -- Just another monitor for moc. -- Believe it or not, while I was working on fixing the crash-bug in -- statusd_xmmsip.lua I stumbled over statusd_mocp.lua for popen_bgread. -- This was the moment I realized a) I finally found an audio player I really -- and fully like (I had my journey on console players the day before) and -- b) I should not have written xmmsip as I am going to moc up my life... :-) -- -- But when I took a closer look at the moc scripts I really did not like what -- they offer and adapting the mocmon stuff for them is quite easy... -- -- Written by Hendrik Iben < hiben at tzi dot de > -- -- How to use : -- mocmon provides a lot of statusd-monitors -- You can compose the text displayed in your statusbar using these individual -- monitors or/and you may specify a string with a setup of the values you -- are interested in. -- -- The monitors : -- mocmon_file : Current file played -- mocmon_title : Current file's title -- mocmon_songtitle : Current song's title -- mocmon_artist : Artist performing current song -- mocmon_album : Album of current song -- mocmon_sectime : Length of file in seconds (0 for streams) -- mocmon_time : Length of file in minutes:seconds or '>>>' for streams -- mocmon_secpos : Position in file in seconds -- mocmon_pos : Position in file in minutes:seconds -- mocmon_secleft : Time left in seconds (-1 for streams) -- mocmon_left : Time left in minutes:seconds or "<<<" for streams -- mocmon_state : moc's status : (PLAY, PAUSE, STOP, NOT RUNNING) -- mocmon_sstate : same as above but all lowercase -- mocmon_bitrate : Current bitrate -- mocmon_rate : Sampling frequency -- -- mocmon_user : String resulting from 'user_format'-string -- or 'stopped'-string -- -- While it is perfectly legal to use these directly in your statusbar I -- recommend you to only use mocmon_user and configure the format string. -- This way you do not get a '?' for every monitor when moc is not running... -- -- Setting up the 'user_format'-string is done by forming a string where each -- mocmon_x-monitor is referenced to by '%x%' -- -- example : -- "...%mocmon_state %mocmon_title ..blub..." in cfg_statusbar template -- -- -> "%state% %title%" in mocmon-config 'user_format' -- and "...%mocmon_user ..blub..." in cfg_statusbar template -- -- The mentionend 'stopped'-string is specified in mocmon-config as -- 'stopped' and will replace %mocmon_user% whenever moc is stopped or -- mocp is not found. -- -- If you are finally convinced that mocmon_user is the only monitor you need -- you may disable telling statusd about the other monitors. I have no idea, if -- this has much impact on performance but who knows... -- -- mocmon-config -> do_monitors = false -- -- speaking of configuration you might want a description of what the settings -- do and what the defaults are : -- -- mocinfo : command to query moc (mocp -i) -- interval : delay between checking moc (5 seconds) -- do_monitors : inform statusd of all monitors or just of mocmon_user (true) -- user_format : template for mocmon_user (see in code) -- stoppped : replacement for mocmon_user when mocp is not found or moc -- is stoppped (see in code) -- -- Requirements : -- ion3 (tested with 20060305 gentoo ebuild) -- lua (tested with 5.0.2) -- moc (tested with 2.4.0) -- -- Serving suggestions : -- I use the rotate_statusbar replacement for the standard statusbar as I -- want to display a lot of things but not always... -- moc-status information takes quite a bit of space so you might consider -- doing it a bit like me .. but of course in your own special and -- unique way. ;-) -- -- This is my current setup : -- -- cfg_statusbar (rotate_statusbar) -- -- ... -- rotate_statusbar.configurations = { -- ... -- ... -- mocmon = { -- interval = 1 * 1000, -- user_format = "(%sstate%) %artist% - %songtitle% (%pos%/%left%/%time%)", -- stoppped = "MOC up your life!", -- do_monitors = false, -- }, -- ... -- ... -- -- rotate_statusbar.settings = { -- ... -- ... -- all_statusbars = { -- "[ %date || MOC %mocmon_user ]%filler%systray", -- "[ %fortune ]%filler%systray", -- ... -- ... -- -- This setup updates the information every second showing the status of -- moc, the current title, the time that is left and the total time -- of the current song. -- Additionally when moc is not running a get a reminder to turn it on, -- and as I do not need the other monitors they are disabled. -- -- Happy listening! -- -- Feel free to contact me if you discover bugs or want to comment on this. -- -- -- You are free to distribute this software under the terms of the GNU -- General Public License Version 2. if not statusd_mocmon then statusd_mocmon = { interval=5*1000, do_monitors = true, user_format = "(%sstate%) %artist% - %songtitle% (%pos%/%left%/%time%)", stopped = "moc is stopped...", mocinfo = "mocp -i", } end -- merge external settings with defaults local settings = table.join (statusd.get_config("mocmon"), statusd_mocmon) -- mapping to moc-Info local valueassoc = { mocmon_file = "File", mocmon_title = "Title", mocmon_songtitle = "SongTitle", mocmon_artist = "Artist", mocmon_album = "Album", mocmon_sectime = "TotalSec", mocmon_time = "TotalTime", mocmon_secpos = "CurrentSec", mocmon_pos = "CurrentTime", mocmon_left = "TimeLeft", mocmon_secleft = "secleft", mocmon_state = "State", mocmon_sstate = "sstate", mocmon_bitrate = "Bitrate", mocmon_rate = "Rate", } -- changes all 'nil's in the infotable to '?' (for statusbar) -- needed when the infopipe is not running or incompatible... local function fixTable(it) for _, v in pairs(valueassoc) do if it[v] == nil then it[v] = "?" end end return it end -- some convenience values local function addSpecialValues(it) local state = it[valueassoc["mocmon_state"]] if state ~= nil then it[valueassoc["mocmon_sstate"]] = string.lower(state) end local sectime = it[valueassoc["mocmon_sectime"]] local left = it[valueassoc["mocmon_left"]] if (not sectime) or (sectime == "") then it[valueassoc["mocmon_sectime"]]=0 it[valueassoc["mocmon_time"]]=">>>" end if (not left) or (left == "") then it[valueassoc["mocmon_left"]]="<<<" it[valueassoc["mocmon_secleft"]]=-1 else local _, _, min, sec = string.find(left, "([0-9]+):([0-9]+)") if min and sec then it[valueassoc["mocmon_secleft"]]=min*60+sec else it[valueassoc["monmon_secleft"]]=0 end end return it end -- this formats the user_format-string local function makeUserString(s, it) local rval = s for k, v in pairs(valueassoc) do -- it would be annoying to type 'mocmon_' in front -- of everything... _, _, stripped = string.find(k, "mocmon_(.*)") rval = string.gsub(rval, "%%"..stripped.."%%", it[v]) end return rval end -- our update timer local mocmon_timer -- retrive information from moc - if found. -- calculate some additional values and clean the table -- from nilS -- After that update statusd-monitors if the users wishes -- and create the user-string from the template local function fetch_data(partial_data) local infotable = { } -- tabula rasa local mocdata = "" while partial_data do mocdata = mocdata .. partial_data partial_data = coroutine.yield() end local running = true -- assume the best if mocdata and mocdata ~= "" then for attribute, value in string.gfind(mocdata, "([^:]*):([^\n]*)\n") do infotable[attribute] = string.gsub(value, "^%s*(.*)", "%1") end else -- obvious... running = false infotable[valueassoc["mocmon_state"]]="NOT RUNNING" end -- compute things like time left or divide things by 1000... infotable = addSpecialValues(infotable) -- scan for nil-values and fix them with '?' infotable = fixTable(infotable) local stopped = false if infotable[valueassoc["mocmon_state"]] == "STOP" then stopped = true end -- if we are to update the monitors... if settings.do_monitors then for k, v in pairs(valueassoc) do -- do so, but coerce to string statusd.inform(k, ""..infotable[v]) end end -- create appropriate user-string if running and (not stopped) then statusd.inform("mocmon_user", makeUserString(settings.user_format, infotable)) else statusd.inform("mocmon_user", settings.not_running) end mocmon_timer:set(settings.interval, update_mocmon) end -- fetch error (and discard...) local function flush_stderr(partial_data) local result = "" while partial_data do result = result .. partial_data partial_data = coroutine.yield() end -- I don't know what to do with the actual error... -- This would be called a lot when mocp is not -- found... end -- update monitor with bgread function update_mocmon() statusd.popen_bgread( settings.mocinfo , coroutine.wrap(fetch_data) , coroutine.wrap(flush_stderr) ) end -- intialize timer mocmon_timer = statusd.create_timer() -- start updating update_mocmon() notion-3+2012042300/contrib/statusd/statusd_mocp.lua000066400000000000000000000243701174530661200221270ustar00rootroot00000000000000-- statusd_mocp.lua --[[ statusd for moc (Music On Console). This is called mocp for two reasons. First, there is already a statusd_moc.lua. Secondly, the actual executable is called mocp on Debian because moc is taken by Qt. Moc is a great replacement for xmms. It's even better in an ion3 environment because it's all console based. Keys are now dynamically generated by the output from "mocp -i". If mocp adds a new field, it will be statusd.inform()ed. No more messy tables or redundant variables. The keys follow the ion convention and are lowercased. You can also use the new user_defined_* variables. This lets you have a template for when mocp is playing or paused (with lots of information) and a very simple one for when it is off. If you use the keys directly, then you will get a lot of "?" when mocp is off. You can either edit this file directly or edit your cfg_statusbar.lua (see below). I recommend editing your cfg_statusbar.lua. The format of the user_defined_* is to use the normal keys listed below but without the mocp_ prefix. For instance, in your cfg_statusbar.lua file you could have: if not rotate_statusbar.configurations then rotate_statusbar.configurations = { -- Moc meter mocp = { -- Update the statusbar every 2 seconds. update_interval = 2 * 1000, -- Template when moc is playing music user_defined_play = 'moc: %state "%title" ' .. "(%currenttime / %totaltime)", -- Template when moc is paused user_defined_pause = 'moc: %state "%songtitle" %bitrate ' .. "[%currenttime | %totaltime]", -- State is the only value reported when mocp is stopped. user_defined_stop = "moc: %state", -- State is the only value reported when mocp is off. user_defined_off = "", }, } end And then in your statusbar template, use %mocp_user_defined. It will use one of the user_defined_* templates depending on mocp's state. Keys currently reported by "mocp -i": %mocp_state (can be "PLAY", "PAUSE", "STOP", or "OFF") %mocp_file (e.g. "/music/cdbaby/Celldweller_-_Switchback_-_2001.ogg") %mocp_title (e.g. "Celldweller - Switchback - 2001 (The Beta Cessions)") %mocp_artist (e.g. "Celldweller") %mocp_songtitle (e.g. "Switchback - 2001") %mocp_album (e.g. "The Beta Cessions") %mocp_totaltime (e.g. "04:35") %mocp_timeleft (e.g. "04:22") %mocp_totalsec (e.g. "275") %mocp_currenttime (e.g. "00:13") %mocp_currentsec (e.g. "13") %mocp_bitrate (e.g. "87Kbps") %mocp_rate (e.g. "44KHz") This is not generated by mocp's output. The format is determined by the template in cfg_statusbar.lua. %mocp_user_defined (String based on user_defined_* and moc's state) Default settings you can change from your statusbar configuration: update_interval How frequently to change the status socket_dir Where mocp's socket is relative to ion's working directory command Command to execute to get information from mocp user_defined_play Statusbar template when mocp is playing user_defined_pause Statusbar template when mocp is paused user_defined_stop Statusbar template when mocp is stopped user_defined_off Statusbar template when mocp is not running Usage: 1) If you do not have ~/.ion3/cfg_statusbar.lua, copy that file from Ion3 to your ~/.ion3 directory. On Debian, it is in /etc/X11/ion3/cfg_statusbar.lua. 2) Ion3 will load the appropriate modules if they are in the template at startup. So place one or more of the above %mocp_* fields into the template in ~/.ion3/cfg_statusbar.lua. 3) You can use the user_defined_* keys instead of using the keys in step #2. To do this, open up ~/.ion3/cfg_statusbar.lua and go to the section with the defaults. Add on_template and off_template in the table for mocp (you may have to add one similar to the load or mail). Then in your ~/.ion3/cfg_statusbar.lua, simply add %mocp_user_defined and it will expand either user_defined_* at runtime. 4) Restart ion3 (Hit F12 and type 'session/restart') Alternative Usage: you could use rotate_statusbar.lua so you can rotate between templates. This is useful when you have too much information for one statusbar template or you only care to know the info periodically. To do this, copy rotate_statusbar.lua to ~/.ion3/cfg_statusbar.lua and do steps 2-4 above. You can test this out independent of ion. This will print all of the key=value pairs statusd.inform() was sent. 1) Copy statusd_mocp.lua to ~/.ion3/ 2) Run '/usr/lib/ion3/ion-statusd -m mocp' This will dump out all of the updates to the terminal as they happen 3) Hit control+c when you are done testing. All public domain based on statusd_moc.lua. It also borrows some ideas from rss_feed.lua which is public domain too. I used the idea of an on/off template from statusd_mocmon.lua. tyranix [tyranix at gmail] --]] local defaults={ -- Update every 2 seconds update_interval=2*1000, -- ~/.moc is where moc stores preferences and the socket socket_dir=os.getenv("HOME").."/.moc", -- Command to get information from mocp. command="mocp -i 2>/dev/null", -- User defined template. If mocp is off, we replace it with the other -- template. This template format is similar to the statusbar template -- except that I only replace %mocp_* keys. Leave off the mocp_ part -- because we don't need that here. user_defined_play = 'moc: %state "%title" (%currenttime / %totaltime)', -- User defined template when moc is paused. user_defined_pause = 'moc: %state "%songtitle" %bitrate ' .. "[%currenttime | %totaltime]", -- User defined template when moc is stopped. Only the state key can be -- replaced in this string. user_defined_stop = "moc: %state", -- User defined template. This is what is displayed if moc is turned off. -- Only the state key can be replaced. -- When moc isn't running, I prefer to not display anything. user_defined_off = "", } local mocp_timer = nil local mocp_values = {} -- Overwrite our defaults with the user's settings local settings = table.join(statusd.get_config("mocp"), defaults) -- This must come after the above settings definition. local mocp_templates = { PLAY = settings.user_defined_play, PAUSE = settings.user_defined_pause, STOP = settings.user_defined_stop, OFF = settings.user_defined_off, } -- Used by gsub on each pattern matched. This sets the values. -- 'name' and 'setting' are the two referenced patterns (in parentheses). local function set_mocp_values(name, value) mocp_values[string.lower(name)] = value end -- Used by gsub on each pattern matched. This returns the value for a template. local function get_mocp_values(name) if not mocp_values[name] then return "?" else return mocp_values[name] end end -- Tell statusd to update its keys with our new values. local function inform_statusd(mocp_status) local name, value mocp_values = {} -- If we didn't get any output from mocp, then it is turned off. if not mocp_status or mocp_status == "" then mocp_values["state"] = "OFF" else -- Go through the output from 'mocp -i' and find the values. -- All of mocp's output has this format: Name: setting\n -- Even when there is no setting, there's always a space. -- The 20 at the end prevents it from going into an infinite loop if -- the user specified some strange option. It makes at most 20 -- substitutions. string.gsub(mocp_status, "(%w+): ([^\n]*)", set_mocp_values, 20) end -- Report all of the known values from mocp -i (not user defined). -- For off/stopped, it is only the state. for name, value in pairs(mocp_values) do statusd.inform("mocp_" .. name, value) end -- Point to the right user defined template depending on moc's state local template = mocp_templates[mocp_values["state"]]; if not template then template = "statusd_mocp: Error! Invalid state '" .. mocp_values["state"] .. "' found" end -- Report the user's template local result = string.gsub(template, "%%([a-z0-9_]+)", get_mocp_values) statusd.inform("mocp_user_defined", result) end -- Continually read input until partial_data is nil then parse it. local function read_from_mocp(partial_data) -- statusd.popen_bgread() will return data as it becomes available. -- The easiest way to handle this is to keep concatenating the data -- until it runs out. There's no guarantee that popen_bgread() will -- return full lines (up to a newline). local mocp_status = "" while partial_data do mocp_status = mocp_status .. partial_data -- Yield until we get more input. popen_bgread will call resume on us. partial_data = coroutine.yield() end -- After we have read all the data, then inform statusd. inform_statusd(mocp_status) -- Continually call the update function. mocp_timer:set(settings.update_interval, update_mocp) end -- Continually read from stderr. It just prints it out to stdout now. -- This should never be called because we're dumping it to /dev/null. local function print_stderr(partial_data) local mocp_error = "" while partial_data do mocp_error = mocp_error .. partial_data -- Yield until we get more input. popen_bgread will call resume on us. partial_data = coroutine.yield() end print(mocp_error, "\n") end -- Main loop for mocp. function update_mocp() -- If there is no PID file, then moc is turned off. local f=io.open(settings.socket_dir.."/pid") if not f then -- mocp is turned off read_from_mocp() else f:close() -- Tell ion to start up mocp and keep reading partial -- chunks until it is complete. This is better for -- performance because ion doesn't have to block here. statusd.popen_bgread(settings.command, coroutine.wrap(read_from_mocp), coroutine.wrap(print_stderr)) end end -- Timer so we can keep telling statusd what the current value is. mocp_timer = statusd.create_timer() update_mocp() notion-3+2012042300/contrib/statusd/statusd_mpd-socket.lua000066400000000000000000000153251174530661200232370ustar00rootroot00000000000000-- statusd for MPD (Music Player Daemon) -- by Marc Hartstein -- -- version 1.0 -- -- Based on a script by delirium@hackish.org -- Feel free to contact me with any bugs or suggestions for improvement. -- requires LuaSocket -- http://www.tecgraf.puc-rio.br/~diego/professional/luasocket/home.html -- INSTALLATION -- -- statusd_mpd-socket.lua is intended as a drop-in replacement for -- statusd_mpd.lua. It should work with your existing statusd_mpd.lua -- configuration, if any. -- -- To achieve this, statusd_mpd-socket.lua needs to be located by statusd as -- statusd_mpd.lua -- -- The easiest way to accomplish this is to create a symbolic link in your -- ~/.ion3 directory from statusd_mpd.lua -> wherever you have placed -- statusd_mpd-socket.lua -- -- Don't forget to include %mpd in a statusbar template in cfg_statusbar.lua -- -- CONFIGURATION -- -- See the defaults table below for the configurable settings and their default -- values. You should create a mpd table in cfg_statusbar.lua to customize -- these settings as for any other statusd plugin. local defaults={ -- 500 or less makes seconds increment relatively smoothly while playing update_interval = 500, -- how long to go to sleep when we can't talk to mpd so we don't spam the -- system with connection attempts every half-second retry_interval = 60*1000, -- 1m -- mpd server info (localhost:6600 are mpd defaults) address = "localhost", port = 6600, -- mpd password (if any) password = nil, -- display template -- --- -- can use the following: -- track metadata: %artist, %title, %num, %album, %year, %len -- conditional metadata: %artist_or_album -- current track position: %pos -- escape for the percent character: %% -- -- %artist_or_album will display the artist if any, otherwise it will -- display the album name. I find this useful for Broadway recordings. -- a default template template = "%artist - %num - %title (%pos / %len)" } local settings = table.join(statusd.get_config("mpd"), defaults) local mpd_timer local last_success -- load namespace local socket = require("socket") local mpd_socket -- set up a try function which closes the socket if there's an error local try = socket.newtry(function() mpd_socket:close() end) local open_socket = socket.protect(function() -- connect to the server mpd_socket = socket.try(socket.connect(settings.address, settings.port)) mpd_socket:settimeout(100) local data -- buffer for reads data = try(mpd_socket:receive()) if data == nil or string.sub(data,1,6) ~= "OK MPD" then mpd_socket:close() return nil, "mpd not running" end -- send password (if necessary) if settings.password ~= nil then try(mpd_socket:send("password " .. settings.password .. "\n")) repeat data = try(mpd_socket:receive()) until data == nil or string.sub(data,1,2) == "OK" or string.sub(data,1,3) == "ACK" if data == nil or string.sub(data,1,2) ~= "OK" then mpd_socket:close() return nil, "bad mpd password" end end return true end) local get_mpd_status = socket.protect(function() local data -- buffer for reads local success = false local info = {} -- 'status' -- %pos, %len, and current state (paused/stopped/playing) try(mpd_socket:send("status\n")) repeat data = try(mpd_socket:receive()) if data == nil then break end local _,_,attrib,val = string.find(data, "(.-): (.*)") if attrib == "time" then _,_,info.pos,info.len = string.find(val, "(%d+):(%d+)") info.pos = string.format("%d:%02d", math.floor(info.pos / 60), math.mod(info.pos, 60)) info.len = string.format("%d:%02d", math.floor(info.len / 60), math.mod(info.len, 60)) elseif attrib == "state" then info.state = val end until string.sub(data,1,2) == "OK" or string.sub(data,1,3) == "ACK" if data == nil or string.sub(data,1,2) ~= "OK" then mpd_socket:close() return nil, "error querying mpd status" end -- 'currentsong' -- song information try(mpd_socket:send("currentsong\n")) repeat data = try(mpd_socket:receive()) if data == nil then break end local _,_,attrib,val = string.find(data, "(.-): (.*)") if attrib == "Artist" then info.artist = val elseif attrib == "Title" then info.title = val elseif attrib == "Album" then info.album = val elseif attrib == "Track" then info.num = val elseif attrib == "Date" then info.year = val end until string.sub(data,1,2) == "OK" or string.sub(data,1,3) == "ACK" if data == nil or string.sub(data,1,2) ~= "OK" then mpd_socket:close() return nil, "error querying current song" end -- %artist_or_album if info.artist == nil then info.artist_or_album = info.album else info.artist_or_album = info.artist end success = true -- done querying; now build the string if info.state == "play" then local mpd_st = settings.template -- fill in %values mpd_st = string.gsub(mpd_st, "%%([%w%_]+)", function (x) return(info[x] or "") end) mpd_st = string.gsub(mpd_st, "%%%%", "%%") return success, mpd_st elseif info.state == "pause" then return success, "Paused" else return success, "No song playing" end end) local init_mpd -- forward declaration local function update_mpd() -- update unless there's an error that's not yet twice in a row, to allow -- for transient errors due to load spikes local success, mpd_st = get_mpd_status() if success or not last_success then statusd.inform("mpd", mpd_st) end if not success and not last_success then -- something's wrong, try to reopen the connection init_mpd() else mpd_timer:set(settings.update_interval, update_mpd) end last_success = success end init_mpd = function () -- Open the socket --statusd.inform("mpd","Opening connection...") success,errstr = open_socket() if not success then statusd.inform("mpd",errstr) -- go to sleep for a while, then try again mpd_timer:set(settings.retry_interval, init_mpd) else update_mpd() end end -- Initialize -- Sending a template we don't honor just confuses the statusbar --send_template() -- Go to sleep immediately, so a slow connect doesn't break the whole statusbar mpd_timer=statusd.create_timer() mpd_timer:set(2000,init_mpd) statusd.inform("mpd", "Initializing") notion-3+2012042300/contrib/statusd/statusd_mpd.lua000066400000000000000000000102451174530661200217450ustar00rootroot00000000000000-- statusd for MPD (Music Player Daemon) -- bugs/requests/comments: delirium@hackish.org -- requires that netcat is available in the path local defaults={ -- 500 or less makes seconds increment relatively smoothly while playing update_interval=500, -- mpd server info (localhost:6600 are mpd defaults) address="localhost", port=6600, -- mpd password (if any) password=nil, -- display template -- --- -- can use the following: -- track metadata: %artist, %title, %num, %album, %year, %len -- current track position: %pos -- escape for the percent character: %% -- a default template template = "%artist - %num - %title (%pos / %len)" } local settings=table.join(statusd.get_config("mpd"), defaults) local success local last_success local function saferead(file) local data, err, errno repeat data, err, errno = file:read() until errno ~= 4 -- EINTR return data, err, errno end local function get_mpd_status() local cmd_string = "status\ncurrentsong\nclose\n" if settings.password ~= nil then cmd_string = "password " .. settings.password .. "\n" .. cmd_string end cmd_string = string.format('echo -n "%s" | netcat %s %d', cmd_string, settings.address, settings.port) last_success = success success = false local mpd = io.popen(cmd_string, "r") -- welcome msg local data = saferead(mpd) if data == nil or string.sub(data,1,6) ~= "OK MPD" then mpd:close() return "mpd not running" end -- 'password' response (if necessary) if settings.password ~= nil then repeat data = saferead(mpd) until data == nil or string.sub(data,1,2) == "OK" or string.sub(data,1,3) == "ACK" if data == nil or string.sub(data,1,2) ~= "OK" then mpd:close() return "bad mpd password" end end local info = {} -- 'status' response repeat data = saferead(mpd) if data == nil then break end local _,_,attrib,val = string.find(data, "(.-): (.*)") if attrib == "time" then _,_,info.pos,info.len = string.find(val, "(%d+):(%d+)") info.pos = string.format("%d:%02d", math.floor(info.pos / 60), math.mod(info.pos, 60)) info.len = string.format("%d:%02d", math.floor(info.len / 60), math.mod(info.len, 60)) elseif attrib == "state" then info.state = val end until string.sub(data,1,2) == "OK" or string.sub(data,1,3) == "ACK" if data == nil or string.sub(data,1,2) ~= "OK" then mpd:close() return "error querying mpd status" end -- 'currentsong' response repeat data = saferead(mpd) if data == nil then break end local _,_,attrib,val = string.find(data, "(.-): (.*)") if attrib == "Artist" then info.artist = val elseif attrib == "Title" then info.title = val elseif attrib == "Album" then info.album = val elseif attrib == "Track" then info.num = val elseif attrib == "Date" then info.year = val end until string.sub(data,1,2) == "OK" or string.sub(data,1,3) == "ACK" if data == nil or string.sub(data,1,2) ~= "OK" then mpd:close() return "error querying current song" end mpd:close() success = true -- done querying; now build the string if info.state == "play" then local mpd_st = settings.template -- fill in %values mpd_st = string.gsub(mpd_st, "%%([%w%_]+)", function (x) return(info[x] or "") end) mpd_st = string.gsub(mpd_st, "%%%%", "%%") return mpd_st elseif info.state == "pause" then return "Paused" else return "No song playing" end end local mpd_timer local function update_mpd() -- update unless there's an error that's not yet twice in a row, to allow -- for transient errors due to load spikes local mpd_st = get_mpd_status() if success or not last_success then statusd.inform("mpd", mpd_st) end mpd_timer:set(settings.update_interval, update_mpd) end -- Init mpd_timer=statusd.create_timer() update_mpd() notion-3+2012042300/contrib/statusd/statusd_netmon.lua000066400000000000000000000151001174530661200224600ustar00rootroot00000000000000-- statusd_netmon.lua: monitor the speed of a network interface -- -- Thanx to Tuomo for pointing out a problem in the previous script. -- -- In case this doesn't work for someone, do let me know :) -- -- Author -- Sadrul Habib Chowdhury (Adil) -- imadil at gmail dot com -- -- Support for per-stat monitors and thresholds added by Jeremy -- Hankins. -- -- -- Monitor values available with this monitor: -- -- netmon -- netmon_kbsin -- netmon_kbsout -- netmon_avgin -- netmon_avgout -- netmon_count -- -- To use the average values or the count you need show_avg and -- show_count turned on, respectively. If you want the default format -- (which you get with %netmon) but with colors for important and -- critical thresholds, try: -- -- %netmon_kbsin/%netmon_kbsout (%netmon_avgin/%netmon_avgout) if not statusd_netmon then statusd_netmon = { device = "eth0", show_avg = 1, -- show average stat? avg_sec = 60, -- default, shows average of 1 minute show_count = 0, -- show tcp connection count? interval = 1*1000, -- update every second -- Threshold information. These values should likely be tweaked to -- suit local conditions. important = { kbsin = 1/10, kbsout = 1/10, avgin = 1/10, avgout = 1/10, count = 4, }, critical = { kbsin = 30, kbsout = 30, avgin = 5, avgout = 5, count = 50, } } end local timer = nil -- the timer local positions = {} -- positions where the entries will be local last = {} -- the last readings local history_in = {} -- history to calculate the average local history_out = {} local total_in, total_out = 0, 0 local counter = 0 -- local settings = table.join(statusd.get_config("netmon"), statusd_netmon) -- -- tokenize the string -- local function tokenize(str) local ret = {} local i = 0 local k = nil for k in string.gfind(str, '(%w+)') do ret[i] = k i = i + 1 end return ret end -- -- get the connection count -- local function get_connection_count() local f = io.popen('netstat -st', 'r') if not f then return nil end local output = f:read('*a') if string.len(output) == 0 then return nil end local s, e, connections = string.find(output, '%s+(%d+)%s+connections established%s') f:close() return tonumber(connections) end -- -- calculate the average -- local function calc_avg(lin, lout) if counter == settings.avg_sec then counter = 0 end total_in = total_in - history_in[counter] + lin history_in[counter] = lin total_out = total_out - history_out[counter] + lout history_out[counter] = lout counter = counter + 1 return total_in/settings.avg_sec, total_out/settings.avg_sec end -- -- parse the information -- local function parse_netmon_info() local s local lin, lout for s in io.lines('/proc/net/dev') do local f = string.find(s, settings.device) if f then local t = tokenize(s) return t[positions[0]], t[positions[1]] end end return nil, nil end -- -- Return a hint value for the given meter -- local function get_hint(meter, val) local hint = "normal" local crit = settings.critical[meter] local imp = settings.important[meter] if crit and val > crit then hint = "critical" elseif imp and val > imp then hint = "important" end return hint end -- -- update the netmon monitor -- local function update_netmon_info() local s local lin, lout local function fmt(num) return(string.format("%.1fK", num)) end lin, lout = parse_netmon_info() if not lin or not lout then -- you should never reach here statusd.inform("netmon", "oops") statusd.inform("netmon_hint", "critical") return end last[0], lin = lin, lin - last[0] last[1], lout = lout, lout - last[1] local kbsin = lin/1024 local kbsout = lout/1024 local output = string.format("%.1fK/%.1fK", kbsin, kbsout) if settings.show_avg == 1 then local avgin, avgout = calc_avg(lin/1024, lout/1024) output = output .. string.format(" (%.1fK/%.1fK)", avgin, avgout) statusd.inform("netmon_avgin", fmt(avgin)) statusd.inform("netmon_avgin_hint", get_hint("avgin", avgin)) statusd.inform("netmon_avgout", fmt(avgout)) statusd.inform("netmon_avgout_hint", get_hint("avgout", avgout)) end if settings.show_count == 1 then local count = get_connection_count() if count then output = "[" .. tostring(count) .. "] " .. output statusd.inform("netmon_count", tostring(count)) statusd.inform("netmon_count_hint", get_hint("count", count)) else output = "[?] " .. output statusd.inform("netmon_count", "?") statusd.inform("netmon_count_hint", "critical") end end statusd.inform("netmon_kbsin", fmt(kbsin)) statusd.inform("netmon_kbsin_hint", get_hint("kbsin", kbsin)) statusd.inform("netmon_kbsout", fmt(kbsout)) statusd.inform("netmon_kbsout_hint", get_hint("kbsout", kbsout)) statusd.inform("netmon", output) timer:set(settings.interval, update_netmon_info) end -- -- is everything ok to begin with? -- local function sanity_check() local f = io.open('/proc/net/dev', 'r') local e if not f then return false end local s = f:read('*line') s = f:read('*line') -- the second line, which should give -- us the positions of the info we seek local t = tokenize(s) local n = table.getn(t) local i = 0 for i = 0,n do if t[i] == "bytes" then positions[0] = i break end end i = positions[0] + 1 for i=i,n do if t[i] == "bytes" then positions[1] = i break end end if not positions[0] or not positions[1] then return false end s = f:read('*a') -- read the whole file if not string.find(s, settings.device) then return false -- the device does not exist end return true end -- -- start the timer -- local function init_netmon_monitor() if sanity_check() then timer = statusd.create_timer() last[0], last[1] = parse_netmon_info() if settings.show_avg == 1 then for i=0,settings.avg_sec-1 do history_in[i], history_out[i] = 0, 0 end end statusd.inform("netmon_template", "xxxxxxxxxxxxxxxxxxxxxxx") update_netmon_info() else statusd.inform("netmon", "oops") statusd.inform("netmon_hint", "critical") end end init_netmon_monitor() notion-3+2012042300/contrib/statusd/statusd_nginfo.lua000066400000000000000000000071671174530661200224560ustar00rootroot00000000000000-- -- statusd_nginfo.lua -- -- Made by Raffaello Pelagalli (raffa at niah.org) -- -- Started on Sun Mar 9 00:22:31 2008 Raffaello Pelagalli -- Last update Thu May 8 23:29:32 2008 Raffaello Pelagalli -- -- 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 -- -- Nagios checking script -- Reports nagios status in ion status bar -- Sample configuration: -- mod_statusbar.launch_statusd{ -- ... -- nginfo = { -- urls = { -- "http://user1:password1@server1.domain1.tld/cgi-bin/nagios2/nginfo.pl", -- "http://user2:password2@server2.domain2.tld/nagios/cgi-bin/nginfo.pl", -- }, -- } -- ... -- } -- -- Need to be used with nginfo.pl script from -- http://redstack.net/blog/index.php/2008/05/08/nagios-status-report-in-ion3-statusbar.html require "lxp" local ng_timer local error = false local status = {0, 0, 0, 0} local defaults = { update_interval=30*1000, urls = { }, } local settings = table.join(statusd.get_config("nginfo"), defaults) nginfo_callbacks = { StartElement = function (parser, name) if (name == "current_state") then nginfo_callbacks.CharacterData = function (parser, val) status[tonumber(val) + 1] = status[tonumber(val) + 1] + 1 end end end, EndElement = function (parser, name) if (name == "current_state") then nginfo_callbacks.CharacterData = false end end, CharacterData = false, } function parse (data) p = lxp.new(nginfo_callbacks) p:parse(b) p:close() end function get_nginfo () status = {0, 0, 0, 0} error = false local http = require("socket.http") socket.http.TIMEOUT=10 local errstr = " ERROR while reading data" for n, url in pairs(settings.urls) do b, c, h = http.request(url) if not (c == 200) then error = true errstr = errstr .. " (NET " .. tostring(c) .. ")" else local st, err = pcall(parse, b) if not st then error = true errstr = errstr .. " (XML" .. err .. ")" end end end if not error then errstr = "" end return "OK: " .. tostring(status[1]) .. ", WARN: " .. tostring(status[2]) .. ", ERROR: " .. tostring(status[3]) .. ", UNKN: " .. tostring(status[4]) .. errstr end local function update_nginfo() statusd.inform("nginfo", get_nginfo()) if (status[3] > 0 or status[4] > 0) then statusd.inform("nginfo_hint", "critical") elseif (status[2] > 0) then statusd.inform("nginfo_hint", "important") else statusd.inform("nginfo_hint", "normal") end ng_timer:set(settings.update_interval, update_nginfo) end -- Init ng_timer=statusd.create_timer() update_nginfo() notion-3+2012042300/contrib/statusd/statusd_nmaild.lua000066400000000000000000000150431174530661200224320ustar00rootroot00000000000000-------------------------------------------------------------------------------------------- -- -- PURPOSE: -- Flexible Maildir monitor with configurable alarms and optional launcher for external -- commands. The name "nmaild" stands for "aNy-mail-directories". -- -------------------------------------------------------------------------------------------- --> Meters: %nmaild_new, %nmaild_read, %nmaild_allnew and %nmaild_allread <-- -------------------------------------------------------------------------------------------- -- -- If you do not customize default settings, this script detects the environment -- variable "$MAILDIR". If that variable is not set, then ~/Maildir will be used as -- default path. Otherwise, %nmaild_new and %nmaild_read are counters for new and -- read emails into the first maildir found in settings. %nmaild_allnew and -- %nmaild_allread are counters of new and read emails into the other mail directories -- specified. -- -- (Example: ~/Maildir/home, ~/Maildir/work, ~/Maildir/forum_lists, ~/Mail/FSCK, -- spam, etc. where ~Maild/home is the main directory, others are counted as all*) -- -- USAGE: -- * If the directory ~/.ion3 does not exist create it and copy cfg_statusbar.lua -- (the file is located somewhere in your /usr/share/ion3 or /usr/local/share/ion3 -- or /etc/X11/ion3 depending on ion3 installation). We need that file located on -- our $HOME to edit and override Ion3 system configurations. -- -- ** The file should look like this: -- template = "[Mail %nmaild_new:%nmaild_all|%nmaild_allnew%nmaild_allread]" -- -- After you restart ion3 (killall -USR1 ion3) or (session/restart on Ion menu), -- the statusbar will look like this: -- -- [Mail: 1:1|4:23] where |1:1| is meaning one mail new and one mail read in first dir. -- -- You can change this script behavior by writting something like this in -- cfg_statusbar.lua: -- -----> CONFIGURATION EXAMPLE: ------------------------------------------------------------ -- -- nmaild = { Values not in config will be assumed from defaults -- update_interval = 15*1000, --> Miliseconds (default is 15 seconds) -- check = { --> Put here the list of maildirs to 'check' for. -- "~/Maildir", Please, pay attention to the logical structure. -- "~/Maildir/work", -- "~/Maildir/copies", -- "~/Security/spam", -- "~/Maildir/lists", -- "/xmail/office/susan", -- "/Mail/common", -- }, -- -- new = {"critical", 1}, --> Alarms: For new, read, allnew and allread. -- read = {"important", 5}, -------------------------- -- allnew = {"critical", 5}, Syntax: mailfilter = {"hint", value}, -- allread = {"important", 10}, -- exec_on_new = "play ~/mew_wmail.mp3" --> Execute something on new email arrival. -- If you want to deactivate exec_on_new, -- just erase it from settings. -- If you need to specify very complex commands -- the best way is to replace the quotes for -- [[ at start and ]] at the end. -- -- }, --> Take care, write correct config endings. -- -- Meanings: -- -------------------------- -- "hint" means the color of alarm: If you are -- daredevil, edit the file lookcommon_XXX.lua -- (You must have a copy into ~/.ion3 directory) -- to change colors or to add more (xcolors). -- If 'value' reaches (is >= than) the number -- you put here, alarms will be displayed !!. -- ------------------------------------------------------------------------------------------- -- -- Internal cycles are provided by string.gsub() so, hopefully, we avoid unnecesary -- use of lines and variables. This script will do only four (4) callings, no matters -- the number of maildirs specified in cfg_statusbar. -- -- VERSION: 1.a -- -- LAST CHANGES: -- Added "exec_on_new" function. You don't need to use it, but it does more fancy the -- script. Now, it can do audio advices, launch your email client or show [g-k-x]message -- when new emails are detected. -- -- TO-DO: -- To show the amounts of disk used. If someone finds interesting that addition... -- -- LICENCE: -- GPL2 Copyright (C) 2006 Mario García H. -- See http://www.gnu.org/licenses/gpl.html to read complete licence) -- -- T.STAMP: sat nov 18 01:03:50 COT 2006 -- -- CONTACT: -- -- ------ DEFAULT CONFIGURATION : ----------------------------------------------------------- local nmaild_timer local last_count = { new = 0, allnew = 0 } local defaults = { update_interval = 5000, check = { os.getenv("MAILDIR") or "~/Maildir" }, exec_on_new = false, --> Use this to play sounds , to execute an new = { "critical", 1 }, -- email client, etc. on new email arrival. read = { "important", 5 }, allnew = { "critical", 5 }, allread = {"important", 20 } } local settings = table.join(statusd.get_config("nmaild"), defaults) ------ SCRIPT : -------------------------------------------------------------------------- local function exec_on_new() if settings.exec_on_new then os.execute(settings.exec_on_new.." 2>/dev/null &") end return end local get_count = function(nmaild, label) local read_dirs = io.popen("du -a " ..nmaild.. " 2>/dev/null", "r") local count = read_dirs:read("*a"); read_dirs:close() _, count = string.gsub(count, "%d+%.%C+%.%C+", "") --> Simple filter. local hint = count >= settings[label][2] and settings[label][1] or "normal" statusd.inform("nmaild_" ..label, tostring(count)) statusd.inform("nmaild_" ..label.. "_hint", hint) if (label == "new" or label == "allnew") and (count > last_count[label]) then last_count[label] = count exec_on_new() return end if count == 0 and (label == "new" or label == "allnew") then last_count[label] = 0 return end end local function plan_count() get_count(settings.check[1] .."/new", "new"); coroutine.yield() get_count(settings.check[1] .."/cur", "read"); coroutine.yield() get_count(table.concat(settings.check, "/new ", 2) .."/new", "allnew"); coroutine.yield() get_count(table.concat(settings.check, "/cur ", 2) .."/cur", "allread") return end local function update_nmaild() local threads = coroutine.create(plan_count) --> Trying to avoid read bottlenecks ,> while coroutine.resume(threads) do end --> Without threads the timer will die :( nmaild_timer:set(settings.update_interval, update_nmaild) end nmaild_timer = statusd.create_timer() update_nmaild() notion-3+2012042300/contrib/statusd/statusd_orpheus.lua000066400000000000000000000012221174530661200226450ustar00rootroot00000000000000-- Stolen from statusd_pytone.lua by Norbert Tretkowski if not statusd_orpheus then statusd_orpheus={ interval=1*1000, infofile=os.getenv("HOME").."/.orpheus/currently_playing", } end local function get_orpheus_status() local f=io.open(statusd_orpheus.infofile,'r') if not f then return "n/a" end local playing=f:read() return playing end local orpheus_timer local function update_orpheus() statusd.inform("orpheus", get_orpheus_status()) orpheus_timer:set(statusd_orpheus.interval, update_orpheus) end -- Init --get_inet_addr=get_inet_addr_fn() orpheus_timer=statusd.create_timer() update_orpheus() notion-3+2012042300/contrib/statusd/statusd_pytone.lua000066400000000000000000000014611174530661200225030ustar00rootroot00000000000000-- Stolen from statusd_mpd.lua -- Responsible for bugs and so on: Alexander Wirt -- To get this working you have to enable playerinfofile in your pytonerc -- If you have the info file at an unusal place overwrite the infofile setting if not statusd_pytone then statusd_pytone={ interval=1*1000, infofile=os.getenv("HOME").."/.pytone/playerinfo", } end local function get_pytone_status() local f=io.open(statusd_pytone.infofile,'r') if not f then return "n/a" end local playing=f:read() return playing end local pytone_timer local function update_pytone() statusd.inform("pytone", get_pytone_status()) pytone_timer:set(statusd_pytone.interval, update_pytone) end -- Init --get_inet_addr=get_inet_addr_fn() pytone_timer=statusd.create_timer() update_pytone() notion-3+2012042300/contrib/statusd/statusd_sysmon.lua000066400000000000000000000154661174530661200225270ustar00rootroot00000000000000-- filename : statusd_sysmon.lua -- version : 2.0 -- date : 2005-03-29 -- -- system monitor for Ion3 WM statusbar -- Written by Vladimir Chizhov < jagoterr *at* gmail *dot* com > -- I'll apreciate any comments, bug reports and feature requests :) -- -- Shows memory, swap and filesystem statistics, based on 'free' and 'df' commands output. -- Allows lua-expression evaluation with the values. -- -- This script can work not only as a Ion3 statusd monitor, but also as a standalone script. -- If it works as a Ion statusd monitor, it reads user settings from statusd. if not statusd_sysmon then statusd_sysmon = { -- UPDATE INTERVAL -- interval=10*1000, -- TEMPLATE STRING -- -- COMMON: -- %% - percents symbol (%) -- ${expression} - evaluates lua expression -- Shortly about expressions: -- - may contain all macroses (of format %macros_name or -- %{macros_name macros_params*}) -- - may contain global function calls -- - may contain arithmetic, logical operations etc. -- - there is a global function 'dpr' defined in this script -- for decreasing double numbers' precision (see this -- function comments for more information and the example templates) -- -- RAM: -- %mem_total - total available memory -- %mem_used - used memory -- %mem_free - free memory -- %mem_buffers - buffered memory -- %mem_cached - cached memory -- %mem_shared - shared memory -- -- SWAP: -- %swap_total - total available swap space -- %swap_used - used swap space -- %swap_free - free swap space -- -- FS (FileSystem): -- %{fs_total mount_point} - total available space on filesystem -- %{fs_used mount_point} - used space on filesystem -- %{fs_free mount_point} - free space on filesystem -- -- TEMPLATE EXAMPLES: -- -- simple swap statistics -- template = "swap used %swap_used of %swap_total" -- -- used swap in percents with one sign after comma! -- how? so... try to understand, I think it's not very hard, if you read the previous comments -- template = "swap used by ${dpr (%swap_used / %swap_total * 100)}%%" -- -- RAM used, excluding buffers and cached memory -- template = "RAM used: ${%mem_used - %mem_buffers - %mem_cached}" -- -- root filesystem simple info -- note, that you should specify the actual mount point for filesystem, accordingly to /etc/mtab -- template = "/ used by %{fs_used /} of %{fs_total /}" -- -- DEFAULT TEMPLATE: -- template = "RAM: %mem_used / %mem_total MB (${dpr (%mem_used / %mem_total * 100, 1)} %%) * SWAP: %swap_used / %swap_total MB (${dpr (%swap_used / %swap_total * 100, 1)} %%) * /: %{fs_used /} / %{fs_total /} (${dpr (%{fs_used /} / %{fs_total /} * 100, 1)} %%)", -- DIMENSION for monitors -- -- b - bytes -- k - kilobytes -- m - megabytes -- g - gigabytes -- -- dimension for RAM and SWAP -- mem_dimension = "m", -- dimension for filesystems -- fs_dimension = "g", } end local settings if statusd ~= nil then settings = table.join (statusd.get_config ("sysmon"), statusd_sysmon) else settings = statusd_sysmon end local factors = { b = 1, k = 1024, m = 1024^2, g = 1024^3, } local metrics = {} -- -------------------------------------------------------------------------- -- Decreases the precision of the floating number -- @param number the number to decrease it's precision -- @param signs_q the quantity of signs after the comma to be left function dpr (number, signs_q) local pattern = "%d+" if signs_q == nil then signs_q = 2 end if signs_q ~= 0 then pattern = pattern.."%." end for i = 1, tonumber (signs_q) do pattern = pattern.."%d" end return string.gsub (number, "("..pattern..")(.*)", "%1") end -- -------------------------------------------------------------------------- -- Retrieves information from 'free' command output local function parse_free_command () local f = io.popen ('free -'..settings.mem_dimension, 'r') local s = f:read('*a') f:close() local st, en st, en, metrics.mem_total, metrics.mem_used, metrics.mem_free, metrics.mem_shared, metrics.mem_buffers, metrics.mem_cached = string.find(s, 'Mem:%s*(%d+)%s+(%d+)%s+(%d+)%s+(%d+)%s+(%d+)%s+(%d+)') st, en, metrics.swap_total, metrics.swap_used, metrics.swap_free = string.find(s, 'Swap:%s*(%d+)%s+(%d+)%s+(%d+)') end -- -------------------------------------------------------------------------- -- Retrieves information from 'df' command output local function parse_df_command () local factor = factors[settings.fs_dimension] local f = io.popen ('df --block-size=1 -P', 'r') -- local s = f:read ("*l") for line in f:lines () do local st, en, fs, total, used, free, mount_point = string.find (line, '(%S+)%s+(%d+)%s+(%d+)%s+(%d+)%s+[%-%d]-%%?%s+(.*)') if fs ~= nil then metrics["fs_total->"..mount_point] = string.gsub (total / factor, '(%d+%.%d%d)(.*)', "%1") metrics["fs_used->"..mount_point] = string.gsub (used / factor, '(%d+%.%d%d)(.*)', "%1") metrics["fs_free->"..mount_point] = string.gsub (free / factor, '(%d+%.%d%d)(.*)', "%1") end end f:close () end local sysmon_timer -- -------------------------------------------------------------------------- -- Main calculating of values, template parsing and Ion statusd updating local function update_sysmon () local sysmon_st = settings.template parse_free_command () parse_df_command () -- filling the template by actual values sysmon_st = string.gsub (sysmon_st, "%%%{([%w%_]+)%s+(%S-)%}", function (arg1, arg2) return(metrics[arg1.."->"..arg2] or "") end) sysmon_st = string.gsub (sysmon_st, "%%([%w%_]+)", function (arg1) return (metrics[arg1] or "") end) sysmon_st = string.gsub (sysmon_st, "%$%{(.-)%}", function (arg1) return loadstring("return "..arg1)() end) -- replacing the '%%' macros with the '%' symbol sysmon_st = string.gsub (sysmon_st, "%%%%", "%%") if statusd ~= nil then statusd.inform ("sysmon_template", sysmon_st) statusd.inform ("sysmon", sysmon_st) sysmon_timer:set (settings.interval, update_sysmon) else io.stdout:write (sysmon_st.."\n") end end -- -------------------------------------------------------------------------- -- Init if statusd ~= nil then sysmon_timer = statusd.create_timer () end update_sysmon () notion-3+2012042300/contrib/statusd/statusd_ticker.lua000066400000000000000000000064731174530661200224560ustar00rootroot00000000000000-- little ticker boondoggle -- -- Selects amongst a set of specified commands and scrolls them not only line by line -- but also within each line. Waits and marks ends of lines, beginning of command. -- Should work great with an rss reader though I didn't care to try. -- -- Author: Matus Telgarsky < mtelgars at andrew dot cmu dot edu > -- -- I couldn't sleep and felt like learning lua. -- -- local settings = { timer = nil, commands = { 'printf "hello world"', 'fortune', 'df --si', }, random = true, line_len = 50, step = 2, new = { hint = "critical", interval = 1 * 1000, }, line = { hint = "important", interval = 1 * 1000, }, scroll = { hint = "normal", interval = 0.375 * 1000, }, update_fun = nil, --forward decl hack } local message = { fd = nil, s = nil, len = nil, pos = nil, } local function ticker_timer(style) statusd.inform("ticker_hint", settings[style].hint) settings.timer:set(settings[style].interval, settings.update_fun) end local function ticker_line_init() message.len = string.len(message.s) message.pos = 0 end local function ticker_single() return settings.commands[1] end local function ticker_random() --don't do same twice, uniformly distribute chances across others local newpos = math.random(1, settings.commands.count-1) if newpos >= settings.commands.pos then newpos = newpos + 1 end settings.commands.pos = newpos return settings.commands[settings.commands.pos] end local function ticker_rotate() local c = settings.commands[settings.commands.pos] if settings.commands.pos == settings.commands.count then settings.commands.pos = 1 else settings.commands.pos = settings.commands.pos + 1 end return c end local function ticker_update() if message.fd == nil then message.fd = io.popen(settings.commands.get(), 'r') message.s = message.fd:read() if message.s then --XXX this is a bug workaround! ticker_line_init() end ticker_timer('new') elseif message.s == nil then message.s = message.fd:read() if message.s == nil then message.fd:close() message.fd = nil else ticker_line_init() end ticker_timer('line') elseif message.pos + settings.line_len >= message.len then message.s = nil --hang out at the end of a line ticker_timer('line') else message.pos = message.pos + settings.step ticker_timer('scroll') end if message.s ~= nil then statusd.inform("ticker", string.sub(message.s, message.pos, message.pos + settings.line_len)) end end if statusd ~= nil then settings.timer = statusd.create_timer() statusd.inform("ticker_template", string.rep('x', settings.line_len)) settings.update_fun = ticker_update settings.commands.count = table.getn(settings.commands) if settings.commands.count == 1 then settings.commands.get = ticker_single elseif settings.random then settings.commands.pos = 1 settings.commands.get = ticker_random else settings.commands.pos = 1 settings.commands.get = ticker_rotate end ticker_update() end notion-3+2012042300/contrib/statusd/statusd_uname.lua000066400000000000000000000044001174530661200222660ustar00rootroot00000000000000-- statusd_uname.lua --[[ Example of how to use statusd.popen_bgread() with coroutines to avoid blocking while reading I/O. This only has one key to keep it simple: %uname_a Normal Usage: 1) If you do not have ~/.ion3/cfg_statusbar.lua, copy that file from Ion3 to your ~/.ion3 directory. On Debian, it is in /etc/X11/ion3/cfg_statusbar.lua. 2) Ion3 will load the appropriate modules if they are in the template at startup. So place '%uname_a' into the template in ~/.ion3/cfg_statusbar.lua. Also, if you want to test this independent of ion, you can do this: 1) Copy statusd_uname.lua to ~/.ion3/ 2) Run '/usr/lib/ion3/ion-statusd -m uname' This will dump out all of the updates to the terminal as they happen 3) Hit control+c when you are done testing. License: Public domain tyranix [ tyranix at gmail ] --]] local defaults={ -- Update every minute update_interval=60*1000, } local uname_timer = nil local settings=table.join(statusd.get_config("uname"), defaults) -- Parse the output and then tell statusd when we have all of the value. function parse_uname(partial_data) -- Keep reading partial_data until it returns nil local result = "" while partial_data do result = result .. partial_data -- statusd.popen_bgread() will resume us when it has more data partial_data = coroutine.yield() end -- If we have a new result, tell statusd if result and result ~= "" then statusd.inform("uname_a", result) end -- Setup the next execution. uname_timer:set(settings.update_interval, update_uname) end -- If we get any stderr, just print it out. local function flush_stderr(partial_data) -- Continually grab stderr local result = "" while partial_data do result = result .. partial_data partial_data = coroutine.yield() end if result and result ~= "" then print("STDERR:", result, "\n") end end -- Query mocp to get information for statusd. function update_uname() statusd.popen_bgread('uname -a', coroutine.wrap(parse_uname), coroutine.wrap(flush_stderr)) end -- Timer so we can keep telling statusd what the current value is. uname_timer = statusd.create_timer() update_uname() -- vim:tw=0: notion-3+2012042300/contrib/statusd/statusd_uptime.lua000066400000000000000000000016271174530661200224740ustar00rootroot00000000000000-- statusd_uptime.lua -- -- Author -- Sadrul Habib Chowdhury (Adil) -- imadil at gmail dot com -- -- how often should the monitor be updated? -- if not statusd_uptime then statusd_uptime={ interval=30*1000, } end local timer = nil -- the timer -- -- update the uptime monitor -- local function get_uptime_info() local f=io.popen('uptime', 'r') timer:set(statusd_uptime.interval, get_uptime_info) if not f then statusd.inform("uptime", "oops") return end local s=f:read('*line') f:close() s = string.gsub(s, ", +%d+ user.+", "") -- unnecessary s = string.gsub(s, "%d+:%d+:%d+ up +", "") -- time is unnecessary statusd.inform("uptime", s) end -- -- start the timer -- local function init_uptime_monitor() timer = statusd.create_timer() statusd.inform("uptime_template", "xxxxxxxxxxxxxxx") get_uptime_info() end init_uptime_monitor() notion-3+2012042300/contrib/statusd/statusd_volume.lua000066400000000000000000000017211174530661200224730ustar00rootroot00000000000000-- Public domain, written by Benjamin Sigonneau -- Allows displaying volume information in the statusbar. -- -- add some of the following fields into your template in cfg_statusbar.lua: -- %volume_master -- %volume_pcm local unknown = "??", "??" local function get_volume() local f=io.popen('aumix -q','r') local s=f:read('*all') f:close() local _, _, master, pcm = string.find(s, "vol[0-9]? (%d*), .*\n".. "pcm[0-9]? (%d*), .*\n" ) if not master then return unknow elseif not pcm then return unknow end return master.."%", pcm.."%" end local function inform(key, value) statusd.inform("volume_"..key, value) end local volume_timer = statusd.create_timer() local function update_volume() local master, pcm = get_volume() inform("master", master) inform("pcm", pcm) -- update every 10 seconds volume_timer:set(10*1000, update_volume) end update_volume() notion-3+2012042300/contrib/statusd/statusd_volume2.lua000066400000000000000000000035771174530661200225700ustar00rootroot00000000000000-- statusd_volume2.lua -- Volume level and state information script -- Written by Randall Wald -- email: randy@rwald.com -- Released under the GPL -- -- Based on a public domain script written by Benjamin Sigonneau -- -- This script uses "amixer" to find volume information. If you don't have -- "amixer," this script will fail. Sorry. -- Though this is labeled "statusd_volume2.lua", rename it to -- "statusd_volume.lua" to make it work. -- -- Available monitors: -- %volume_level Volume level, as a percentage from 0% to 100% -- %volume_state The string "" if unmuted, "MUTE " if muted -- -- Example use: -- template="[ %date || || vol: %volume_level %volume state]" -- (note space between monitors but lack of space after %volume_state) -- This will print -- [ || || vol: 54% ] -- when unmuted but -- [ || || vol: 54% MUTE ] -- when muted. local function get_volume() local f=io.popen('amixer','r') local s=f:read('*all') f:close() local _, _, master_level, master_state = string.find(s, "%[(%d*%%)%] %[(%a*)%]") local sound_state = "" if master_state == "off" then sound_state = "MUTE " end return master_level.."", sound_state.."" end local function inform_volume(name, value) if statusd ~= nil then statusd.inform(name, value) else io.stdout:write(name..": "..value.."\n") end end local function inform_state(value) if statusd ~= nil then statusd.inform("volume_state", value) else io.stdout:write("volume_state"..value.."\n") end end if statusd ~= nil then volume_timer = statusd.create_timer() end local function update_volume() local master_level, sound_state = get_volume() inform_volume("volume_level", master_level) inform_volume("volume_state", sound_state) if statusd ~= nil then volume_timer:set(500, update_volume) end end update_volume() notion-3+2012042300/contrib/statusd/statusd_weather.lua000066400000000000000000000122141174530661200226220ustar00rootroot00000000000000-- statusd_weather.lua -- statusd_weather.lua: an Ion3 statusd applet for displaying weather information -- based on weather.lua by Andrea Rossato arossato AT istitutocolli DOT org -- Here's the list of all available data: -- %weather_location -- %weather_country -- %weather_date -- %weather_time (UTC) -- %weather_timestamp (timestamp of the latest report: your local time.) -- %weather_tempF (Fahrenheit) -- %weather_tempC (Celsius) -- %weather_dewpointF (Fahrenheit) -- %weather_dewpointC (Celsius) -- %weather_humidity -- %weather_pressure (hPa) -- %weather_wind -- %weather_windspeed (MPH) -- %weather_windchillF (Fahrenheit) -- %weather_windchillC (Celsius) -- %weather_sky -- %weather_weather -- LEGAL -- Copyright (C) 2006 Andrea Rossato arossato AT istitutocolli DOT org -- Copyright (C) 2008 Sergey Kozhemyakin luc AT server DOT by -- 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 software 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 -- Have a nice weather :) local defaults = { update_interval = 30 * 60 * 1000, -- every 30 minutes station = "UMGG", timestamp_format = "%F %H:%M", -- strftime format gmt_offset = nil, -- offset from gmt in seconds. should be calculated automaticaly but may be specified manualy } local settings = table.join(statusd.get_config("weather"), defaults) local weather_timer = statusd.create_timer() local wget_timer = statusd.create_timer() local get_weather local function calculate_gmt_offset() -- offset (+|-)(\d\d)(\d\d) local off = {} _, _, off.sign, off.hour, off.min = string.find(os.date("%z", os.time()), "([-+])(%d%d)(%d%d)") settings.gmt_offset = (off.hour * 60 + off.min) * 60 if off.sign == '-' then settings.gmt_offset = -settings.gmt_offset end end local function reformat_time(date, time) local dt = {} _, _, dt.year, dt.month, dt.day = string.find(date, "(%d+)%.(%d+)%.(%d+)") _, _, dt.hour, dt.min = string.find(time, "(%d%d)(%d%d)") os.setlocale(os.getenv("LANG"), "time") return os.date(settings.timestamp_format, os.time(dt) + settings.gmt_offset) end local function parse_weather(s) local w = {} _, _, w.location, w.country = string.find(s, "^(.+)%,%s(.+)%(%u+%)" ) _, _, w.date, w.time = string.find(s, ".+%/%s([%d.]+)%s(%d+)%sUTC" ) _, _, w.wind, w.windspeed = string.find(s, "Wind%:%s(.+%sat%s(%d+)%sMPH)" ) _, _, w.sky = string.find(s, "Sky%sconditions:%s(.-)%c" ) _, _, w.tempF, w.tempC = string.find(s, "Temperature:%s(-?%d*%.?%d+)%sF%s%((-?%d*%.?%d+)%sC%)%c" ) _, _, w.dewpointF, w.dewpointC = string.find(s, "Dew%sPoint:%s(-?%d*%.?%d+)%sF%s%((-?%d*%.?%d+)%sC%)" ) _, _, w.windchillF, w.windchillC = string.find(s, "Windchill:%s(-?%d*%.?%d+)%sF%s%((-?%d*%.?%d+)%sC%)" ) _, _, w.humidity = string.find(s, "Relative%sHumidity:%s(%d+)%%") _, _, w.pressure = string.find(s, "Pressure%s%b():.-%((%d+)%shPa%)" ) _, _, w.weather = string.find(s, "Weather:%s(.-)%c" ) if w.date and w.time then w.timestamp = reformat_time(w.date, w.time) else w.timestamp = "undef" end return w end local function load_weather() local f = io.open ("/tmp/".. settings.station..".dat") if not f then return nil end local s = f:read("*all") f:close() os.execute("rm ".."/tmp/".. settings.station..".dat") return parse_weather(s) end local function update_weather() local w = load_weather() if w then for i,v in pairs(w) do if not v then v = "N/A" end statusd.inform("weather_"..i, v); end end weather_timer:set(settings.update_interval, get_weather) end local function check_wget() local filename = "/tmp/wget_"..settings.station.."_status.txt" local f = io.open (filename) if not f then weather_timer:set(settings.update_interval, get_weather) return nil end local s = f:read("*all") f:close() if not string.match(s, "100%%") then -- wget still downloading. resetting timer wget_timer:set(2000, check_wget) else -- wget finished. so we need parse file and update info os.execute("rm "..filename) update_weather() end end get_weather = function () local url = "http://weather.noaa.gov/pub/data/observations/metar/decoded/" local command = "wget -b -o /tmp/wget_"..settings.station.."_status.txt -O /tmp/"..settings.station..".dat "..url..settings.station..".TXT" os.execute(command) -- start watching timer -- when wget will exit -- update weather wget_timer:set(2000, check_wget) end if not settings.gmt_offset then calculate_gmt_offset() end get_weather() notion-3+2012042300/contrib/statusd/statusd_xmms.lua000066400000000000000000000020141174530661200221440ustar00rootroot00000000000000-- statusd_xmms.lua -- -- Gets title of song currently selected in xmms' playlist. -- Depends on xmms and pyxmms-remote -- Inspired by statusd_mpd.lua -- Written by Peter Randeu < ranpet at sbox dot tugraz dot at > -- -- You are free to distribute this software under the terms of the GNU -- General Public License Version 2. if not statusd_xmms then statusd_xmms={ interval=10*1000, } end local settings = table.join (statusd.get_config("xmms"), statusd_xmms) local function get_xmms_status() local f = io.popen('pyxmms-remote get_playlist_pos', 'r') local pl_num = f:read() if not pl_num then return "pyxmms-remote not available" end f:close() f = io.popen('pyxmms-remote get_playlist_title ' .. pl_num, 'r') title = f:read() f:close() return title end local xmms_timer local function update_xmms() statusd.inform("xmms", get_xmms_status()) xmms_timer:set(settings.interval, update_xmms) end xmms_timer = statusd.create_timer() update_xmms() notion-3+2012042300/contrib/statusd/statusd_xmms2.lua000066400000000000000000000014231174530661200222310ustar00rootroot00000000000000-- Real simple xmms2 monitor -- %xmms2 - current song in format "Artist - Title" -- by Voker57 -- Public domain local defaults={ -- 500 or less makes seconds increment relatively smoothly while playing update_interval=500 } local settings=table.join(statusd.get_config("xmms2"), defaults) local xmms2_timer local function get_xmms2_status() local xmms2 = io.popen("xmms2 current") if xmms2 == nil then return nil else local r = "" local t = "" repeat r = r..t t = xmms2:read() until t == nil return r end end local function update_xmms2() local xmms2_st = get_xmms2_status() statusd.inform("xmms2", xmms2_st) xmms2_timer:set(settings.update_interval, update_xmms2) end -- Init xmms2_timer=statusd.create_timer() update_xmms2()notion-3+2012042300/contrib/statusd/statusd_xmmsip.lua000066400000000000000000000244721174530661200225110ustar00rootroot00000000000000-- statusd_xmmsip.lua v20060323 -- -- Interface to the XMMS InfoPipe-Plugin -- Inspired by the high cpu attention statusd_xmms.lua + python demand... -- ( no offense, just observation... :-) -- (using statusd_xmms while writing this... ;-) ) -- -- Written by Hendrik Iben < hiben at tzi dot de > -- -- How to use : -- xmmsip provides a lot of statusd-monitors -- You can compose the text displayed in your statusbar using these individual -- monitors or/and you may specify a string with a setup of the values you -- are interested in. -- -- The monitors : -- xmmsip_pllen : Length of the playlist (number of entries) -- xmmsip_plcur : Current song in playlist (number of entry) -- xmmsip_file : Current file played -- xmmsip_title : Current file's title -- xmmsip_usectime : Length of file in milliseconds (-1 for streams) -- xmmsip_time : Length of file in minutes:seconds or '>>>' for streams -- xmmsip_usecpos : Position in file in milliseconds -- xmmsip_pos : Position in file in minutes:seconds -- xmmsip_usecleft : Time left in milliseconds (0 for streams) -- xmmsip_left : Time left in minutes:seconds or "<<<" for streams -- xmmsip_status : XMMS's status : (Playing, Paused, Stopped, Not running) -- xmmsip_sstatus : same as above but all lowercase -- xmmsip_bitrate : Current bitrate (not average bitrate) -- xmmsip_kbitrate : Current bitrate in kilobit per seconds (using 1000 for k) -- xmmsip_freq : Sampling frequency in Hz -- xmmsip_kfreq : Sampling frequency in kHz -- xmmsip_channels : Current channels (1,2,?) -- xmmsip_xmmsproto : Protocol version of XMMS -- xmmsip_infover : Version of InfoPipe-Plugin -- -- xmmsip_user : String resulting from 'user_format'-string -- or 'not_running'-string -- -- While it is perfectly legal to use these directly in your statusbar I -- recommend you to only use xmmsip_user and configure the format string. -- This way you do not get a '?' for every monitor when XMMS is not running... -- -- Setting up the 'user_format'-string is done by forming a string where each -- xmmsip_x-monitor is referenced to by '%x%' -- -- example : -- "...%xmmsip_status %xmmsip_title ..blub..." in cfg_statusbar template -- -- -> "%status% %title%" in xmmsip-config 'user_format' -- and "...%xmmsip_user ..blub..." in cfg_statusbar template -- -- The mentionend 'not_running'-string is specified in xmmsip-config as -- 'not_running' and will replace %xmmsip_user% whenever the XMMS InfoPipe -- is not running (or found...) -- -- If you are finally convinced that xmmsip_user is the only monitor you need -- you may disable telling statusd about the other monitors. I have no idea, if -- this has much impact on performance but who knows... -- -- xmmsip-config -> do_monitors = false -- -- speaking of configuration you might want a description of what the settings -- do and what the defaults are : -- -- interval : delay between checking the pipe (5 seconds) -- user : user name (for xmms-session) (current user) -- xmms_session : the xmms-session to use (0) -- do_monitors : inform statusd of all monitors or just of xmmsip_user (true) -- user_format : template for xmmsip_user (see in code) -- not_running : replacement for xmmsip_user when no pipe is found -- (see in code) -- -- Requirements : -- ion3 (tested with 20060305 gentoo ebuild) -- lua (tested with 5.0.2) -- xmms-infopipe v1.3 or compatible -- I have not tested pipes from/for other players... -- -- Serving suggestions : -- I use the rotate_statusbar replacement for the standard statusbar as I -- want to display a lot of things but not always... -- XMMS-status information takes quite a bit of space so you might consider -- doing it a bit like me .. but of course in your own special and -- unique way. ;-) -- -- This is my current setup : -- -- cfg_statusbar (rotate_statusbar) -- -- ... -- rotate_statusbar.configurations = { -- ... -- ... -- xmmsip = { -- interval = 1 * 1000, -- user_format = "XMMS (%status%) %title% (%left%/%time%)", -- not_running = "Turn on the Radio!", -- do_monitors = false, -- }, -- ... -- ... -- -- rotate_statusbar.settings = { -- ... -- ... -- all_statusbars = { -- "[ %date || %xmmsip_user ]%filler%systray", -- "[ %fortune ]%filler%systray", -- ... -- ... -- -- This setup updates the information every second showing the status of -- xmms, the current title, the time that is left and the total time -- of the current song. -- Additionally when xmms is not running a get a reminder to turn it on, -- and as I do not need the other monitors they are disabled. -- -- Happy listening! -- -- Feel free to contact me if you discover bugs or want to comment on this. -- -- -- You are free to distribute this software under the terms of the GNU -- General Public License Version 2. if not statusd_xmmsip then statusd_xmmsip = { interval=5*1000, user = os.getenv("USER"), xmms_session = 0, do_monitors = true, user_format = "%status%: %title% (%pos%/%time%, %plcur%/%pllen%, %kfreq%kHz@%kbitrate%kbps)", not_running = "Not runnning...", } end -- merge external settings with defaults local settings = table.join (statusd.get_config("xmmsip"), statusd_xmmsip) -- location of the infopipe local xmmsinfopipe = "/tmp/xmms-info" -- info-pipe is named by user and session if settings.user ~= nil -- should not happen... but default should be okay anyway... then xmmsinfopipe = xmmsinfopipe .. "_" .. settings.user .. "." .. settings.xmms_session end -- mapping to InfoPipe-keys local valueassoc = { xmmsip_pllen = "Tunes in playlist", xmmsip_plcur = "Currently playing", xmmsip_file = "File", xmmsip_title = "Title", xmmsip_usectime = "uSecTime", xmmsip_time = "Time", xmmsip_usecpos = "uSecPosition", xmmsip_pos = "Position", xmmsip_left = "left", xmmsip_usecleft = "usecleft", xmmsip_status = "Status", xmmsip_sstatus = "sstatus", xmmsip_bitrate = "Current bitrate", xmmsip_kbitrate = "kbitrate", xmmsip_freq = "Samping Frequency", -- will this ever get fixed ? xmmsip_kfreq = "kfreq", xmmsip_channels = "Channels", xmmsip_xmmsproto = "XMMS protocol version", xmmsip_infover = "InfoPipe Plugin version", } -- changes all 'nil's in the infotable to '?' (for statusbar) -- needed when the infopipe is not running or incompatible... local function fixTable(it) for _, v in pairs(valueassoc) do if it[v] == nil then it[v] = "?" end end return it end -- some convenience values local function addSpecialValues(it) local bitrate = it[valueassoc["xmmsip_bitrate"]] if bitrate ~= nil then it[valueassoc["xmmsip_kbitrate"]] = math.floor(bitrate / 1000) end local status = it[valueassoc["xmmsip_status"]] if status ~= nil then it[valueassoc["xmmsip_sstatus"]] = string.lower(status) end local freq = it[valueassoc["xmmsip_freq"]] if freq ~= nil then it[valueassoc["xmmsip_kfreq"]] = math.floor(freq / 1000) end local usectime = it[valueassoc["xmmsip_usectime"]] local usecpos = it[valueassoc["xmmsip_usecpos"]] -- streaming leaves time at zero if ( ( usectime ~= nil ) and ( usecpos ~= nil ) ) then -- force lua to coerce this to a number... if ((usectime + 0) > 0) then -- not streaming local usecleft = usectime - usecpos local mins = math.floor( usecleft / (60 * 1000) ) local secs = math.floor( math.mod( usecleft, 60 * 1000 ) / 1000 ) local left = string.format("%i:%02i", mins, secs ) it[valueassoc["xmmsip_usecleft"]] = usecleft it[valueassoc["xmmsip_left"]] = left else -- streaming -- I would so like to use unicode for the -- moebius but ion does not like it - yet... it[valueassoc["xmmsip_time"]]=">>>" it[valueassoc["xmmsip_left"]]="<<<" it[valueassoc["xmmsip_usecleft"]]=0 end end return it end -- this formats the user_format-string local function makeUserString(s, it) local rval = s for k, v in pairs(valueassoc) do -- it would be annoying to type 'xmmsip_' in front -- of everything... _, _, stripped = string.find(k, "xmmsip_(.*)") rval = string.gsub(rval, "%%"..stripped.."%%", it[v]) end return rval end -- our update timer local xmmsip_timer -- retrive information from the pipe - if it exists, -- calculate some additional values and clean the table -- from nilS -- After that update statusd-monitors if the users wishes -- and create the user-string from the template local function fetch_data(partial_data) local infotable = { } -- tabula rasa local pipedata = "" while partial_data do pipedata = pipedata .. partial_data partial_data = coroutine.yield() end local running = true -- assume the best if pipedata and pipedata ~= "" then for attribute, value in string.gfind(pipedata, "([^:]*):%s*([^\n]*)\n") do infotable[attribute] = value end else -- obvious... running = false infotable[valueassoc["xmmsip_status"]]="Not running" end -- compute things like time left or divide things by 1000... infotable = addSpecialValues(infotable) -- scan for nil-values and fix them with '?' infotable = fixTable(infotable) -- if we are to update the monitors... if settings.do_monitors then for k, v in pairs(valueassoc) do -- do so, but coerce to string statusd.inform(k, ""..infotable[v]) end end -- create appropriate user-string if running then statusd.inform("xmmsip_user", makeUserString(settings.user_format, infotable)) else statusd.inform("xmmsip_user", settings.not_running) end xmmsip_timer:set(settings.interval, update_xmmsip) end -- fetch error (and discard...) local function flush_stderr(partial_data) local result = "" while partial_data do result = result .. partial_data partial_data = coroutine.yield() end -- I don't know what to do with the actual error... -- This would be called a lot when the pipe is not -- running... end -- update monitor with bgread function update_xmmsip() statusd.popen_bgread( 'cat ' .. xmmsinfopipe , coroutine.wrap(fetch_data) , coroutine.wrap(flush_stderr) ) end -- intialize timer xmmsip_timer = statusd.create_timer() -- start updating update_xmmsip() notion-3+2012042300/contrib/styles/000077500000000000000000000000001174530661200165455ustar00rootroot00000000000000notion-3+2012042300/contrib/styles/look_alex.lua000066400000000000000000000075221174530661200212330ustar00rootroot00000000000000 if not gr.select_engine("de") then return end de.reset() de.defstyle("*", { shadow_colour = "#00aa00", highlight_colour = "#00aa00", -- background_colour = "#000000", foreground_colour = "#00aa00", 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 = "#000000", highlight_colour = "#000000", padding_colour = "#00aa00", -- transparent_background = true, -- background_colour = "#000000", foreground_colour = "#ffffff", padding_pixels = 2, highlight_pixels = 1, shadow_pixels = 1, de.substyle("active", { shadow_colour = "#000000", highlight_colour = "#000000", padding_colour = "#00aa00", foreground_colour = "#ffffff", }), }) de.defstyle("frame-ionframe", { based_on = "frame", border_style = "inlaid", padding_pixels = 1, spacing = 1, }) de.defstyle("frame-floatframe", { based_on = "frame", border_style = "ridge", }) de.defstyle("tab", { based_on = "*", font = "-*-helvetica-medium-r-normal-*-12-*-*-*-*-*-*-*", de.substyle("active-selected", { shadow_colour = "#00aa00", highlight_colour = "#00aa00", background_colour = "#000000", foreground_colour = "#00aa00", }), de.substyle("active-unselected", { shadow_colour = "#006600", highlight_colour = "#006600", background_colour = "#000000", foreground_colour = "#006600", }), de.substyle("inactive-selected", { shadow_colour = "#006600", highlight_colour = "#006600", background_colour = "#000000", foreground_colour = "#006600", }), de.substyle("inactive-unselected", { shadow_colour = "#003300", highlight_colour = "#003300", background_colour = "#000000", foreground_colour = "#003300", }), text_align = "center", }) de.defstyle("tab-frame", { based_on = "tab", de.substyle("*-*-*-*-activity", { shadow_colour = "#00aa00", highlight_colour = "#00aa00", background_colour = "#000000", foreground_colour = "#00aa00", }), }) de.defstyle("tab-frame-ionframe", { based_on = "tab-frame", spacing = 1, }) de.defstyle("tab-menuentry", { based_on = "tab", text_align = "left", highlight_pixels = 0, shadow_pixels = 0, --give pmenus the same brightness as the other menus. de.substyle("inactive-selected", { shadow_colour = "#00aa00", highlight_colour = "#00aa00", background_colour = "#000000", foreground_colour = "#00aa00", }), de.substyle("inactive-unselected", { shadow_colour = "#006600", highlight_colour = "#006600", background_colour = "#000000", foreground_colour = "#006600", }), }) 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 = "#00aa00", highlight_colour = "#00aa00", background_colour = "#000000", foreground_colour = "#00aa00", 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 = "#00aa00", highlight_colour = "#00aa00", background_colour = "#000000", foreground_colour = "#00aa00", }), }) gr.refresh() notion-3+2012042300/contrib/styles/look_asm.lua000066400000000000000000000101151174530661200210520ustar00rootroot00000000000000 if not gr.select_engine("de") then return end de.reset() de.defstyle("*", { foreground_colour = "#ffffff", background_colour = "#708090", shadow_colour = "#405060", highlight_colour = "#708090", padding_colour= "#405060", padding_pixels = 0, highlight_pixels = 1, shadow_pixels = 0, spacing = 0, -- border_style = "elevated", border_style = "ridge", font = "-*-helvetica-medium-r-normal-*-12-*-*-*-*-*-*-*", text_align = "center", }) de.defstyle("frame", { based_on = "*", background_colour = "#040810", shadow_colour = "#405060", highlight_colour = "#708090", padding_colour= "#708090", shadow_pixels = 1, highlight_pixels = 1, spacing = 1, padding_pixels = 1, }) de.defstyle("frame-tiled", { based_on = "frame", border_style = "inlaid", padding_pixels = 0, --highlight_pixels = 0, spacing = 0, shadow_pixels = 0, highlight_pixels = 1, }) de.defstyle("frame-floating", { based_on = "frame", shadow_colour = "#405060", highlight_colour = "#708090", padding_pixels = 0, shadow_pixels = 1, highlight_pixels = 1, spacing = 0, border_style = "elevated", floatframe_tab_min_w = 10000, floatframe_bar_max_w_q = 1, }) de.defstyle("tab", { based_on = "*", de.substyle("*-*-*-*-activity", { shadow_colour = "#506070", highlight_colour = "#506070", background_colour = "#993020", padding_colour= "#993020", foreground_colour = "#eeeeee", }), de.substyle("active-selected", { shadow_colour = "#708090", highlight_colour = "#708090", background_colour = "#607080", padding_colour= "#607080", foreground_colour = "#ffffff", }), de.substyle("active-unselected", { shadow_colour = "#607080", highlight_colour = "#607080", background_colour = "#405060", padding_colour= "#405060", foreground_colour = "#a0a0a0", }), de.substyle("inactive-selected", { shadow_colour = "#607080", highlight_colour = "#607080", background_colour = "#405060", padding_colour= "#405060", foreground_colour = "#c0c0c0", }), de.substyle("inactive-unselected", { shadow_colour = "#506070", highlight_colour = "#506070", background_colour = "#304050", padding_colour= "#304050", foreground_colour = "#a0a0a0", }), font = "-*-helvetica-medium-r-normal-*-12-*-*-*-*-*-*-*", border_style = "ridge", padding_pixels = 0, shadow_pixels = 0, highlight_pixels = 1, spacing = 1, text_align = "center", }) de.defstyle("tab-frame", { based_on = "tab", spacing = 0, }) de.defstyle("tab-frame-tiled", { based_on = "tab-frame", spacing = 1, }) de.defstyle("tab-menuentry", { based_on = "tab", text_align = "left", highlight_pixels = 0, shadow_pixels = 0, spacing = 1, padding_pixels = 0, }) de.defstyle("tab-menuentry-big", { based_on = "tab-menuentry", font = "-*-helvetica-medium-r-normal-*-12-*-*-*-*-*-*-*", border_style = "inlaid", shadow_pixels = 0, highlight_pixels = 0, padding_pixels = 5, spacing = 1, }) de.defstyle("input", { based_on = "*", background_colour = "#304050", padding_pixels = 1, highlight_pixels = 0, shadow_pixels = 0, border_style = "elevated", de.substyle("*-cursor", { background_colour = "#ffffff", foreground_colour = "#000000", }), de.substyle("*-selection", { background_colour = "#506070", foreground_colour = "#ffffff", }), }) de.defstyle("input-menu", { based_on = "*", padding_colour = "#607080", de.substyle("active", { shadow_colour = "#304050", highlight_colour = "#708090", background_colour = "#506070", foreground_colour = "#ffffff", }), }) de.defstyle("stdisp", { based_on = "*", shadow_pixels = 0, highlight_pixels = 0, padding_pixels = 1, spacing = 1, text_align = "center", background_colour = "#506070", }) gr.refresh() notion-3+2012042300/contrib/styles/look_atme.lua000066400000000000000000000072721174530661200212320ustar00rootroot00000000000000-- look_atme.lua drawing engine configuration file for Ion. -- -- Author: Sadrul Habib Chowdhury (Adil) -- imadil |at| gmail |dot| com if not gr.select_engine("de") then return end de.reset() de.defstyle("*", { shadow_colour = "grey", highlight_colour = "grey", background_colour = "#eeeeff", foreground_colour = "#444466", padding_pixels = 0, highlight_pixels = 1, shadow_pixels = 1, border_style = "elevated", font = "fixed", text_align = "center", }) de.defstyle("frame", { based_on = "*", padding_colour = "#444466", background_colour = "black", transparent_background = true, de.substyle("active", { shadow_colour = "grey", highlight_colour = "grey", }), }) de.defstyle("frame-ionframe", { ionframe_bar_inside_border = true, }) de.defstyle("frame-tiled", { based_on = "frame", padding_pixels = 0, highlight_pixels = 0, shadow_pixels = 0, spacing = 1, }) de.defstyle("tab", { based_on = "*", de.substyle("active-selected", { shadow_colour = "#eeeeff", highlight_colour = "#eeeeff", background_colour = "#444466", foreground_colour = "#eeeeff", transparent_background = false, }), de.substyle("active-unselected", { shadow_colour = "#666688", highlight_colour = "#666688", background_colour = "#666688", foreground_colour = "#eeeeff", }), de.substyle("inactive-selected", { shadow_colour = "white", highlight_colour = "white", background_colour = "#999999", foreground_colour = "white", }), de.substyle("inactive-unselected", { shadow_colour = "#a0a0a0", highlight_colour = "#a0a0a0", background_colour = "#a0a0a0", foreground_colour = "white", }), text_align = "center", }) de.defstyle("tab-frame", { based_on = "tab", de.substyle("*-*-*-*-activity", { shadow_colour = "#664444", highlight_colour = "#664444", background_colour = "#ffff00", foreground_colour = "#990000", }), }) de.defstyle("tab-frame-tiled", { based_on = "tab-frame", spacing = 1, }) de.defstyle("tab-menuentry", { based_on = "tab", text_align = "left", }) de.defstyle("tab-menuentry-pmenu", { text_align = "left", de.substyle("*-selected", { shadow_colour = "#eeeeff", highlight_colour = "#eeeeff", background_colour = "#444466", foreground_colour = "#eeeeff", }), de.substyle("*-unselected", { shadow_colour = "#666688", highlight_colour = "#666688", background_colour = "#666688", foreground_colour = "#eeeeff", }), }) 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 = "#444466", foreground_colour = "#eeeeff", padding_pixels = 1, highlight_pixels = 1, shadow_pixels = 1, border_style = "elevated", de.substyle("*-cursor", { background_colour = "white", foreground_colour = "#444466", }), de.substyle("*-selection", { background_colour = "#aaaaaa", foreground_colour = "white", }), }) de.defstyle("stdisp", { based_on = "*", shadow_pixels = 0, highlight_pixels = 0, background_colour = "#444466", foreground_colour = "#eeeeff", text_align = "left", de.substyle("important", { foreground_colour = "green", }), de.substyle("critical", { foreground_colour = "red", background_colour = "yellow", }), transparent_background = false, }) gr.refresh() notion-3+2012042300/contrib/styles/look_awesome.lua000066400000000000000000000102601174530661200217330ustar00rootroot00000000000000-- look-awesome.lua drawing engine configuration file for Ion. if not gr.select_engine("de") then return end de.reset() de.defstyle("*", { shadow_colour = "#99A", highlight_colour = "#99A", background_colour = "#667", foreground_colour = "#FFF", padding_colour = "#99A", transparent_background = false, border_style = "elevated", highlight_pixels = 0, shadow_pixels = 0, padding_pixels = 0, spacing = 0, font = "-xos4-terminus-medium-r-normal--14-*-*-*-*-*-*-*", text_align = "center", }) de.defstyle("frame", { based_on = "*", -- this sets the color between tabs as well, I could not figure out any way to make it transparent background_colour = "black", transparent_background = true, -- de.substyle("active", { -- }), -- de.substyle("inactive", { -- }), }) de.defstyle("frame-ionframe", { based_on = "frame", -- de.substyle("active", { -- }), -- de.substyle("inactive", { -- }), }) de.defstyle("frame-floatframe", { based_on = "frame", padding_pixels = 1, de.substyle("active", { padding_colour = "#99A", }), de.substyle("inactive", { padding_colour = "#666", }), }) de.defstyle("tab", { based_on = "*", highlight_pixels = 1, shadow_pixels = 1, padding_pixels = 1, spacing = 1, transparent_background = true, text_align = "center", de.substyle("active-selected", { shadow_colour = "#99A", highlight_colour = "#99A", background_colour = "#667", foreground_colour = "#FFF", }), de.substyle("active-unselected", { shadow_colour = "#667", highlight_colour = "#667", background_colour = "#334", foreground_colour = "#999", }), de.substyle("inactive-selected", { shadow_colour = "#666", highlight_colour = "#666", background_colour = "#333", foreground_colour = "#888", }), de.substyle("inactive-unselected", { shadow_colour = "#333", highlight_colour = "#333", background_colour = "#111", foreground_colour = "#777", }), }) de.defstyle("tab-frame", { based_on = "tab", padding_pixels = 3, -- de.substyle("*-*-tagged", { -- }), -- de.substyle("*-*-*-dragged", { -- }), de.substyle("active-*-*-*-activity", { shadow_colour = "red", highlight_colour = "red", background_colour = "#800", foreground_colour = "#FFF", }), de.substyle("inactive-*-*-*-activity", { shadow_colour = "#800", highlight_colour = "#800", background_colour = "#400", foreground_colour = "#888", }), }) de.defstyle("tab-frame-ionframe", { based_on = "tab-frame", }) de.defstyle("tab-frame-floatframe", { based_on = "tab-frame", padding_pixels = 4, }) de.defstyle("tab-menuentry", { based_on = "tab", padding_pixels = 6, spacing = 4, font = "-xos4-terminus-medium-r-normal--16-*-*-*-*-*-*-*", text_align = "left", -- de.substyle("*-*-submenu", { -- }), }) de.defstyle("tab-menuentry-big", { based_on = "tab-menuentry", padding_pixels = 8, font = "-xos4-terminus-medium-r-normal--28-*-*-*-*-*-*-*", }) de.defstyle("input", { based_on = "*", foreground_colour = "#FFF", background_colour = "#667", padding_colour = "#667", transparent_background = false, border_style = "elevated", padding_pixels = 2, }) de.defstyle("input-edln", { based_on = "input", de.substyle("*-cursor", { background_colour = "#FFF", foreground_colour = "#667", }), de.substyle("*-selection", { background_colour = "#AAA", foreground_colour = "#334", }), }) de.defstyle("input-message", { based_on = "input", }) de.defstyle("input-menu", { based_on = "input", transparent_background = true, highlight_pixels = 0, shadow_pixels = 0, padding_pixels = 0, spacing = 0, }) de.defstyle("input-menu-big", { based_on = "input-menu", }) de.defstyle("moveres_display", { based_on = "input", }) de.defstyle("dock", { --based_on = "frame-ionframe", border = 7, outline_style = "each", }) gr.refresh() notion-3+2012042300/contrib/styles/look_awesome_sm.lua000066400000000000000000000076711174530661200224460ustar00rootroot00000000000000-- look-awesome-sm.lua drawing engine configuration file for Ion. if not gr.select_engine("de") then return end de.reset() de.defstyle("*", { shadow_colour = "#99A", highlight_colour = "#99A", background_colour = "#667", foreground_colour = "#FFF", padding_colour = "#99A", border_style = "elevated", highlight_pixels = 0, shadow_pixels = 0, padding_pixels = 0, spacing = 0, font = "7x13", text_align = "center", }) de.defstyle("frame", { based_on = "*", background_colour = "black", transparent_background = false, }) de.defstyle("frame-floatframe", { based_on = "frame", padding_pixels = 1, de.substyle("active", { padding_colour = "#99A", }), de.substyle("inactive", { padding_colour = "#666", }), }) de.defstyle("tab", { based_on = "*", highlight_pixels = 1, shadow_pixels = 1, padding_pixels = 1, spacing = 1, transparent_background = false, text_align = "center", de.substyle("active-selected", { shadow_colour = "#99A", highlight_colour = "#99A", background_colour = "#667", foreground_colour = "#FFF", }), de.substyle("active-unselected", { shadow_colour = "#667", highlight_colour = "#667", background_colour = "#334", foreground_colour = "#999", }), de.substyle("inactive-selected", { shadow_colour = "#666", highlight_colour = "#666", background_colour = "#333", foreground_colour = "#888", }), de.substyle("inactive-unselected", { shadow_colour = "#333", highlight_colour = "#333", background_colour = "#111", foreground_colour = "#777", }), }) de.defstyle("tab-frame", { based_on = "tab", font = "nexus", padding_pixels = 1, de.substyle("active-*-*-*-activity", { shadow_colour = "red", highlight_colour = "red", background_colour = "#800", foreground_colour = "#FFF", }), de.substyle("inactive-*-*-*-activity", { shadow_colour = "#800", highlight_colour = "#800", background_colour = "#400", foreground_colour = "#888", }), }) de.defstyle("tab-frame-ionframe", { based_on = "tab-frame", }) de.defstyle("tab-frame-floatframe", { based_on = "tab-frame", padding_pixels = 1, }) de.defstyle("tab-menuentry", { based_on = "tab", padding_pixels = 1, spacing = 2, text_align = "left", }) de.defstyle("tab-menuentry-bigmenu", { based_on = "tab-menuentry", padding_pixels = 7, }) de.defstyle("tab-menuentry-pmenu", { based_on = "tab-menuentry", de.substyle("inactive-selected", { shadow_colour = "#99A", highlight_colour = "#99A", background_colour = "#667", foreground_colour = "#FFF", }), de.substyle("inactive-unselected", { shadow_colour = "#667", highlight_colour = "#667", background_colour = "#334", foreground_colour = "#999", }), }) de.defstyle("input", { based_on = "*", foreground_colour = "#FFF", background_colour = "#667", padding_colour = "#667", transparent_background = false, border_style = "elevated", padding_pixels = 2, }) de.defstyle("input-edln", { based_on = "input", de.substyle("*-cursor", { background_colour = "#FFF", foreground_colour = "#667", }), de.substyle("*-selection", { background_colour = "#AAA", foreground_colour = "#334", }), }) de.defstyle("input-message", { based_on = "input", }) de.defstyle("input-menu", { based_on = "input", transparent_background = false, highlight_pixels = 0, shadow_pixels = 0, padding_pixels = 0, spacing = 0, }) de.defstyle("input-menu-bigmenu", { based_on = "input-menu", }) de.defstyle("moveres_display", { based_on = "input", }) de.defstyle("dock", { based_on = "*", }) gr.refresh() notion-3+2012042300/contrib/styles/look_awesome_yaarg.lua000066400000000000000000000122531174530661200231220ustar00rootroot00000000000000--[[ look_awesome_yaarg.lua (based on look awesome ) To completely yaargify Ion, the terminal emulator background is recommended to be set to gray25 as does the root window. Transparent frames by default wallpaper can be good sometimes... Also note this theme uses terminus font. Setup: Drop into ~/.ion3/ or install in the relevant system-wide directory Author: James Gray (yaarg in #ion) Date: Fri Feb 3 00:13:43 GMT 2006 ]] if not gr.select_engine("de") then return end de.reset() de.defstyle("*", { shadow_colour = "gray25", highlight_colour = "gray25", padding_colour = "gray25", foreground_colour = "white", background_colour = "gray25", border_style = "inlaid", highlight_pixels = 0, shadow_pixels = 0, padding_pixels = 0, spacing = 0, font = "-*-terminus-*-*-normal--12-*-*-*-*-*-*-*", text_align = "center", }) de.defstyle("frame", { based_on = "*", background_colour = "gray25", transparent_background = true, shadow_pixels = 1, padding_pixels = 0, highlight_pixels = 0, spacing = 1, shadow_colour = "gray30", highlight_colour = "gray28", }) de.defstyle("frame-floatframe", { based_on = "frame", padding_pixels = 1, de.substyle("active", { padding_colour = "gray30", }), de.substyle("inactive", { padding_colour = "#808080", }), }) de.defstyle("tab", { based_on = "*", highlight_pixels = 1, shadow_pixels = 1, padding_pixels = 0, spacing = 0, transparent_background = false, text_align = "center", de.substyle("active-selected", { shadow_colour = "#808080", highlight_colour = "#808080", background_colour = "gray33", foreground_colour = "white", }), de.substyle("active-unselected", { shadow_colour = "#808080", highlight_colour = "#808080", background_colour = "gray30", foreground_colour = "white", }), de.substyle("inactive-selected", { shadow_colour = "#808080", highlight_colour = "#808080", background_colour = "gray33", foreground_colour = "white", }), de.substyle("inactive-unselected", { shadow_colour = "#808080", highlight_colour = "#808080", background_colour = "gray30", foreground_colour = "white", }), }) de.defstyle("stdisp", { padding = 0, shadow_pixels = 0, padding_pixels = 0, spacing = 1, highlight_pixels = 0, shadow_colour = "black", highlight_colour = "black", background_colour = "gray40", foreground_colour = "white", }) de.defstyle("tab-frame", { based_on = "tab", font = "-*-terminus-*-*-normal--12-*-*-*-*-*-*-*", padding_pixels = 1, spacing = 0, shadow_colour = "red", padding_colour = "red", highlight_colour = "red", background_colour = "red", de.substyle("active-*-*-*-activity", { shadow_colour = "red", highlight_colour = "red", background_colour = "#808080", foreground_colour = "white", }), de.substyle("inactive-*-*-*-activity", { shadow_colour = "#808080", highlight_colour = "#808080", background_colour = "#808080", foreground_colour = "#808080", }), }) de.defstyle("tab-frame-ionframe", { based_on = "tab-frame", }) de.defstyle("tab-frame-floatframe", { based_on = "tab-frame", padding_pixels = 0, }) de.defstyle("tab-menuentry", { based_on = "tab", padding_pixels = 1, spacing = 2, text_align = "left", }) de.defstyle("tab-menuentry-bigmenu", { based_on = "tab-menuentry", padding_pixels = 7, }) de.defstyle("tab-menuentry-pmenu", { based_on = "tab-menuentry", de.substyle("inactive-selected", { shadow_colour = "#808080", highlight_colour = "#808080", background_colour = "#CCCCCC", foreground_colour = "#FFF", }), de.substyle("inactive-unselected", { shadow_colour = "#667", highlight_colour = "#667", background_colour = "#334", foreground_colour = "#999", }), }) de.defstyle("input", { based_on = "*", foreground_colour = "white", background_colour = "grey30", padding_colour = "white", transparent_background = false, border_style = "elevated", padding_pixels = 2, }) de.defstyle("input-edln", { based_on = "input", de.substyle("*-cursor", { background_colour = "white", foreground_colour = "black", }), de.substyle("*-selection", { background_colour = "#AAA", foreground_colour = "#334", }), }) de.defstyle("input-message", { based_on = "input", }) de.defstyle("input-menu", { based_on = "input", transparent_background = false, highlight_pixels = 0, shadow_pixels = 0, padding_pixels = 0, spacing = 0, }) de.defstyle("input-menu-bigmenu", { based_on = "input-menu", }) de.defstyle("moveres_display", { based_on = "input", }) de.defstyle("dock", { based_on = "*", }) gr.refresh() notion-3+2012042300/contrib/styles/look_bas.lua000066400000000000000000000066061174530661200210510ustar00rootroot00000000000000-- look-bb.lua for use with the bb background. -- Bas Kok 20040916 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 = "-artwiz-snap-*-*-*-*-*-100-*-*-*-*-*-*", text_align = "center", transparent_background = true, }) de.defstyle("frame", { based_on = "*", shadow_colour = "#404040", highlight_colour = "#707070", padding_colour = "#505050", background_colour = "#1c2636", foreground_colour = "#b6b4b8", padding_pixels = 0, highlight_pixels = 0, shadow_pixels = 0, de.substyle("active", { shadow_colour = "#0f1729", highlight_colour = "#637782", padding_colour = "#4b7d96", foreground_colour = "#b6b4b8", }), transparent_background = true, }) de.defstyle("frame-ionframe", { based_on = "frame", border_style = "inlaid", padding_pixels = 0, spacing = 0, }) de.defstyle("frame-floatframe", { based_on = "frame", border_style = "ridge" }) de.defstyle("tab", { based_on = "*", font = "-artwiz-snap-*-*-*-*-*-100-*-*-*-*-*-*", de.substyle("active-selected", { shadow_colour = "#435663", highlight_colour = "#435663", background_colour = "#546b7c", foreground_colour = "#b6b4b8", }), de.substyle("active-unselected", { shadow_colour = "#435663", highlight_colour = "#435663", background_colour = "#435663", foreground_colour = "#a0a0a0", }), de.substyle("inactive-selected", { shadow_colour = "#161d21", highlight_colour = "#161d21", background_colour = "#232c33", foreground_colour = "#a0a0a0", }), de.substyle("inactive-unselected", { shadow_colour = "#161d21", highlight_colour = "#161d21", background_colour = "#161d21", foreground_colour = "#a0a0a0", }), text_align = "center", }) de.defstyle("tab-frame", { based_on = "tab", de.substyle("*-*-*-*-activity", { shadow_colour = "#404040", highlight_colour = "#707070", background_colour = "#990000", foreground_colour = "#eeeeee", }), }) de.defstyle("tab-frame-ionframe", { based_on = "tab-frame", spacing = 0, }) 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 = "-artwiz-snap-*-*-*-*-*-100-*-*-*-*-*-*", padding_pixels = 7, }) de.defstyle("input", { based_on = "*", shadow_colour = "#404040", highlight_colour = "#707070", background_colour = "#1c2636", foreground_colour = "#b6b4b8", border_style = "elevated", de.substyle("*-cursor", { background_colour = "#b6b4b8", foreground_colour = "#1c2636", }), de.substyle("*-selection", { background_colour = "#505050", foreground_colour = "#b6b4b8", }), }) de.defstyle("input-menu", { based_on = "*", de.substyle("active", { shadow_colour = "#0f1729", highlight_colour = "#637782", background_colour = "#5892b0", foreground_colour = "#b6b4b8", }), }) gr.refresh() notion-3+2012042300/contrib/styles/look_blue.lua000066400000000000000000000040761174530661200212320ustar00rootroot00000000000000-- -- look_blue, based on look-cleanviolet -- if not gr.select_engine("de") then return end -- Clear existing styles from memory. de.reset() -- Base style de.defstyle("*", { highlight_colour = "#eeeeff", shadow_colour = "#eeeeff", background_colour = "#9999bb", foreground_colour = "#444477", 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", }) de.defstyle("frame-tiled", { based_on = "frame", shadow_pixels = 0, highlight_pixels = 0, padding_pixels = 0, spacing = 1, }) de.defstyle("tab", { based_on = "*", font = "-*-helvetica-medium-r-normal-*-12-*-*-*-*-*-*-*", de.substyle("active-selected", { highlight_colour = "#9999bb", shadow_colour = "#9999bb", background_colour = "#34639f", foreground_colour = "#eeeeff", }), de.substyle("inactive-selected", { highlight_colour = "#dddddd", shadow_colour = "#dddddd", background_colour = "#7c95b4", foreground_colour = "#333366", }), }) 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 = 4, }) de.defstyle("input", { based_on = "*", text_align = "left", spacing = 0, highlight_colour = "#9999bb", shadow_colour = "#9999bb", background_colour = "#34639f", foreground_colour = "#eeeeff", de.substyle("*-selection", { background_colour = "#9999ff", foreground_colour = "#333366", }), de.substyle("*-cursor", { background_colour = "#ccccff", foreground_colour = "#9999aa", }), }) dopath("lookcommon_clean") -- Refresh objects' brushes. gr.refresh() notion-3+2012042300/contrib/styles/look_bluecurve.lua000066400000000000000000000070021174530661200222670ustar00rootroot00000000000000-- look_bluecurve.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 = "#e6e6e6", 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 = "#ffffff", highlight_colour = "#a1a1a1", padding_colour = "#e6e6e6", background_colour = "#e6e6e6", foreground_colour = "#ffffff", padding_pixels = 2, highlight_pixels = 1, shadow_pixels = 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" }) de.defstyle("tab", { based_on = "*", font = "-*-helvetica-medium-r-normal-*-14-*-*-*-*-*-*-*", de.substyle("active-selected", { shadow_colour = "#404679", highlight_colour = "#c0c6f9", background_colour = "#8086b9", foreground_colour = "#ffffff", }), de.substyle("active-unselected", { shadow_colour = "#a1a1a1", highlight_colour = "#ffffff", background_colour = "#e6e6e6", foreground_colour = "#000000", }), de.substyle("inactive-selected", { shadow_colour = "#a1a1a1", highlight_colour = "#ffffff", background_colour = "#e6e6e6", foreground_colour = "#000000", }), de.substyle("inactive-unselected", { shadow_colour = "#ffffff", highlight_colour = "#a1a1a1", background_colour = "#e6e6e6", foreground_colour = "#a0a0a0", }), text_align = "center", }) de.defstyle("tab-frame", { based_on = "tab", de.substyle("*-*-*-*-activity", { shadow_colour = "#404040", highlight_colour = "#707070", background_colour = "#990000", foreground_colour = "#eeeeee", }), }) de.defstyle("tab-frame-tiled", { based_on = "tab-frame", spacing = 1, }) 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 = "#e6e6e6", foreground_colour = "#000000", 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", }), }) de.defstyle("stdisp", { based_on = "*", shadow_pixels = 0, highlight_pixels = 0, text_align = "left", --font = "fixed", de.substyle("important", { foreground_colour = "green", }), de.substyle("critical", { foreground_colour = "red", }), }) gr.refresh() notion-3+2012042300/contrib/styles/look_cleanpastel.lua000066400000000000000000000042511174530661200225710ustar00rootroot00000000000000-- look-cleanpastel.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-*-12-*-*-*-*-*-*-*", text_align = "center", }) de.defstyle("frame", { based_on = "*", padding_colour = "#d8d8d8", background_colour = "#000000", transparent_background = false, }) de.defstyle("frame-ionframe", { based_on = "frame", shadow_pixels = 0, highlight_pixels = 0, padding_pixels = 0, spacing = 1, }) de.defstyle("tab", { based_on = "*", font = "-*-helvetica-bold-r-normal-*-10-*-*-*-*-*-*-*", de.substyle("active-selected", { shadow_colour = "#e0f0e0", highlight_colour = "#b0c0b6", background_colour = "#70b0a6", foreground_colour = "#000000", }), de.substyle("inactive-unselected", { shadow_colour = "#ffffff", highlight_colour = "#ffffff", background_colour = "#d8d8d8", foreground_colour = "#606060", }), text_align = "center", }) de.defstyle("tab-frame", { based_on = "tab", de.substyle("*-*-*-*-activity", { shadow_colour = "#ffffff", highlight_colour = "#ffffff", background_colour = "#990000", foreground_colour = "#eeeeee", }), }) de.defstyle("tab-frame-ionframe", { based_on = "tab-frame", spacing = 1, }) 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-*-18-*-*-*-*-*-*-*", padding_pixels = 10, }) de.defstyle("input-edln", { based_on = "*", de.substyle("*-cursor", { background_colour = "#000000", foreground_colour = "#d8d8d8", }), de.substyle("*-selection", { background_colour = "#f0c000", foreground_colour = "#000000", }), }) gr.refresh() notion-3+2012042300/contrib/styles/look_cleansteel.lua000066400000000000000000000074451174530661200224250ustar00rootroot00000000000000-- look-cleansteel.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.substyle("active", { -- shadow_colour = "#203040", -- highlight_colour = "#607080", -- padding_colour = "#405060", -- foreground_colour = "#ffffff", --}), border_style = "ridge" }) de.defstyle("frame-tiled", { based_on = "frame", spacing = 1, padding_pixels = 0, highlight_pixels = 0, shadow_pixels = 0, }) --de.defstyle("frame-floatframe", { -- based_on = "frame", --}) de.defstyle("tab", { based_on = "*", font = "-*-helvetica-medium-r-normal-*-10-*-*-*-*-*-*-*", 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-frame", { based_on = "tab", de.substyle("*-*-*-*-activity", { shadow_colour = "#404040", highlight_colour = "#707070", background_colour = "#990000", foreground_colour = "#eeeeee", }), }) de.defstyle("tab-frame-ionframe", { based_on = "tab-frame", spacing = 1, }) 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", }), }) de.defstyle("stdisp", { based_on = "*", shadow_pixels = 0, highlight_pixels = 0, background_colour = "#000000", foreground_colour = "grey", text_align = "left", de.substyle("important", { foreground_colour = "green", }), de.substyle("critical", { foreground_colour = "red", }), }) gr.refresh() notion-3+2012042300/contrib/styles/look_cleansteel_trans.lua000066400000000000000000000110741174530661200236250ustar00rootroot00000000000000-- look-cleansteel_trans.lua drawing engine configuration file for Ion. -- Based on the default cleansteel look, but with added transparency -- and a few other things that I like. -Steve Pomeroy 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-*-10-*-*-*-*-*-*-*", text_align = "center", transparent_background = false, }) 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.substyle("active", { -- shadow_colour = "#203040", -- highlight_colour = "#607080", -- padding_colour = "#405060", -- foreground_colour = "#ffffff", --}), border_style = "ridge", transparent_background = true, }) de.defstyle("frame-tiled", { based_on = "frame", spacing = 1, padding_pixels = 0, highlight_pixels = 0, shadow_pixels = 0, }) --de.defstyle("frame-floatframe", { -- based_on = "frame", --}) de.defstyle("tab", { based_on = "*", font = "-*-helvetica-medium-r-normal-*-10-*-*-*-*-*-*-*", 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", transparent_background = true, }) de.defstyle("tab-frame", { based_on = "tab", de.substyle("*-*-*-*-activity", { shadow_colour = "#404040", highlight_colour = "#707070", background_colour = "#990000", foreground_colour = "#eeeeee", }), }) de.defstyle("tab-frame-ionframe", { based_on = "tab-frame", spacing = 1, }) 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-*-*-*-*-*-*-*-*-*", padding_pixels = 7, }) de.defstyle("input", { based_on = "*", shadow_colour = "#404040", highlight_colour = "#707070", background_colour = "#000030", foreground_colour = "#ffffff", padding_pixels = 1, highlight_pixels = 1, shadow_pixels = 1, transparent_background = false, border_style = "elevated", de.substyle("*-cursor", { background_colour = "#ffffff", foreground_colour = "#000000", }), de.substyle("*-selection", { background_colour = "#505050", foreground_colour = "#ffffff", }), }) de.defstyle("input-edln", { based_on = "input", -- this is taken from the URxvt default font on my system. -SP font = "-misc-fixed-medium-r-semicondensed--13-*-*-*-c-60-*-*", }) de.defstyle("input-menu", { based_on = "*", transparent_background = true, de.substyle("active", { shadow_colour = "#304050", highlight_colour = "#708090", background_colour = "#506070", foreground_colour = "#ffffff", }), }) de.defstyle("stdisp", { based_on = "*", shadow_pixels = 0, highlight_pixels = 0, background_colour = "#000000", foreground_colour = "grey", text_align = "left", de.substyle("important", { foreground_colour = "green", }), de.substyle("critical", { foreground_colour = "red", }), de.substyle("actnotify", { foreground_colour = "red", }), }) de.defstyle("actnotify", { based_on = "*", shadow_colour = "#404040", highlight_colour = "#707070", background_colour = "#990000", foreground_colour = "#eeeeee", }) gr.refresh() notion-3+2012042300/contrib/styles/look_cleanwhite.lua000066400000000000000000000035141174530661200224220ustar00rootroot00000000000000-- look_cleanwhite.lua a bright theme fitting white terminals -- -- uses Terminus fonts -- -- -- René van Bevern if not gr.select_engine("de") then return end de.reset() de.defstyle("*", { shadow_colour = "grey70", highlight_colour = "grey70", background_colour = "grey90", foreground_colour = "black", padding_pixels = 1, highlight_pixels = 1, shadow_pixels = 1, spacing = 1, border_style = "elevated", font = "-xos4-terminus-medium-r-normal-*-12-*-72-72-*-60-iso10646-1", text_align = "center", }) de.defstyle("frame", { based_on = "*", de.substyle("active", { padding_colour = "black", }), padding_colour = "grey70", background_colour = "white", shadow_colour = "white", highlight_colour = "white", transparent_background = false, }) de.defstyle("tab", { based_on = "*", highlight_pixels = 1, shadow_pixels = 1, padding_pixels = 0, highlight_colour = "grey70", shadow_colour = "grey70", de.substyle("active-selected", { shadow_colour = "black", highlight_colour = "black", background_colour = "darkslategray", foreground_colour = "white", }), de.substyle("inactive-unselected", { background_colour = "#d8d8d8", foreground_colour = "#606060", }), text_align = "center", }) de.defstyle("tab-menuentry", { based_on = "tab", text_align = "left", spacing = 1, }) de.defstyle("tab-menuentry-big", { based_on = "tab-menuentry", padding_pixels = 10, }) de.defstyle("input-edln", { based_on = "*", de.substyle("*-cursor", { background_colour = "#000000", foreground_colour = "#d8d8d8", }), de.substyle("*-selection", { background_colour = "#f0c000", foreground_colour = "#000000", }), }) gr.refresh()notion-3+2012042300/contrib/styles/look_cool.lua000066400000000000000000000115721174530661200212360ustar00rootroot00000000000000-- look-awesome.lua drawing engine configuration file for Ion. if not gr.select_engine("de") then return end de.reset() de.defstyle("*", { shadow_colour = "#3C5268", highlight_colour = "#9DAABA", background_colour = "#3C5268", foreground_colour = "#FFFFFF", padding_colour = "#000000",--#778BA0", transparent_background = false, border_style = "elevated", highlight_pixels = 0, shadow_pixels = 0, padding_pixels = 0, spacing = 0, font = "-xos4-terminus-medium-r-normal--14-*-*-*-*-*-*-*", text_align = "center", }) de.defstyle("frame", { based_on = "*", -- this sets the color between tabs as well, I could not figure out any way to make it transparent background_colour = "#000000",--#778BA0", transparent_background = true, de.substyle("active", { shadow_colour = "#3C5268", highlight_colour = "#9DAABA", padding_colour = "#000000",--#778BA0", background_colour = "#3C5268", }), -- de.substyle("inactive", { -- }), }) de.defstyle("frame-ionframe", { based_on = "frame", -- de.substyle("active", { -- }), -- de.substyle("inactive", { -- }), }) de.defstyle("frame-floatframe", { based_on = "frame", padding_pixels = 1, de.substyle("active", { padding_colour = "#99A", }), de.substyle("inactive", { padding_colour = "#666", }), }) de.defstyle("tab", { based_on = "*", highlight_pixels = 1, shadow_pixels = 1, padding_pixels = 0, spacing = 1, transparent_background = true, text_align = "center", de.substyle("active-selected", { shadow_colour = "#3C5268", highlight_colour = "#3C5268", background_colour = "#3C5268", foreground_colour = "#FFFFFF", }), de.substyle("active-unselected", { shadow_colour = "#A7B5C6", highlight_colour = "#A7B5C6", background_colour = "#9DAABA", foreground_colour = "#000000", }), de.substyle("inactive-selected", { shadow_colour = "#3C5268", highlight_colour = "#A7B5C6", background_colour = "#9DAABA", foreground_colour = "#000000", }), de.substyle("inactive-unselected", { shadow_colour = "#3C5268",--#939FAE", highlight_colour = "#A7B5C6", background_colour = "#9DAABA", foreground_colour = "#4C4C4C", }), }) de.defstyle("tab-frame", { based_on = "tab", padding_pixels = 3, -- de.substyle("*-*-tagged", { -- }), -- de.substyle("*-*-*-dragged", { -- }), de.substyle("active-*-*-*-activity", { shadow_colour = "red", highlight_colour = "red", background_colour = "#800", foreground_colour = "#FFF", }), de.substyle("inactive-*-*-*-activity", { shadow_colour = "#800", highlight_colour = "#800", background_colour = "#400", foreground_colour = "#888", }), }) de.defstyle("tab-frame-ionframe", { based_on = "tab-frame", }) de.defstyle("tab-frame-floatframe", { based_on = "tab-frame", padding_pixels = 4, }) de.defstyle("tab-menuentry", { based_on = "tab", padding_pixels = 6, spacing = 0, font = "-xos4-terminus-medium-r-normal--16-*-*-*-*-*-*-*", text_align = "left", -- de.substyle("*-*-submenu", { -- }), }) de.defstyle("tab-menuentry-big", { based_on = "tab-menuentry", padding_pixels = 8, font = "-xos4-terminus-medium-r-normal--28-*-*-*-*-*-*-*", }) de.defstyle("tab-menuentry-pmenu", { based_on = "tab-menuentry", de.substyle("inactive-selected", { shadow_colour = "#3C5268", highlight_colour = "#3C5268", background_colour = "#3C5268", foreground_colour = "#FFFFFF", }), de.substyle("inactive-unselected", { shadow_colour = "#A7B5C6", highlight_colour = "#A7B5C6", background_colour = "#9DAABA", foreground_colour = "#000000", }), }) de.defstyle("input", { based_on = "*", foreground_colour = "#000000", background_colour = "#9DAABA", padding_colour = "#9CAAB4", transparent_background = false, border_style = "elevated", padding_pixels = 3, }) de.defstyle("input-edln", { based_on = "input", }) de.defstyle("input-message", { based_on = "input", }) de.defstyle("input-menu", { based_on = "input", de.substyle("*-selection", { background_color = "#AAA", foreground_color = "#334", }), de.substyle("*-cursor", { background_color = "#FFF", foreground_color = "#667", }), transparent_background = false, background_color = "#AAA", foreground_color = "#334", highlight_pixels = 0, shadow_pixels = 0, padding_pixels = 0, spacing = 0, }) de.defstyle("input-menu-big", { based_on = "input-menu", }) de.defstyle("moveres_display", { based_on = "input-menu", }) de.defstyle("dock", { based_on = "*", }) gr.refresh() notion-3+2012042300/contrib/styles/look_gtk2.lua000066400000000000000000000071621174530661200211510ustar00rootroot00000000000000-- theme that goes well with the gtk2 default colors -- it makes use of terminus als artwiz fonts -- -- By Rene van Bevern if not gr.select_engine("de") then return end de.reset() de.defstyle("*", { shadow_colour = "#9c9a94", highlight_colour = "#ffffff", background_colour = "#dedbd6", foreground_colour = "#000000", padding_pixels = 0, highlight_pixels = 1, shadow_pixels = 1, border_style = "elevated", -- font = "-*-helvetica-medium-r-normal-*-*-120-*-*-*-*-iso8859-15", font = "-*-helvetica-medium-r-normal-*-*-120-*-*-*-*-iso8859-1", text_align = "center", }) de.defstyle("frame", { based_on = "*", shadow_colour = "#9c9a94", highlight_colour = "#ffffff", padding_colour = "#dedbd6", background_colour = "#dedbd6", transparent_background = true, foreground_colour = "#ffffff", padding_pixels = 2, highlight_pixels = 1, shadow_pixels = 1, de.substyle("active", { shadow_colour = "#9c9a94", highlight_colour = "#ffffff", background_colour = "#dedbd6", foreground_colour = "#ffffff", }), }) de.defstyle("frame-ionframe", { based_on = "frame", border_style = "inlaid", padding_pixels = 1, spacing = 2, }) de.defstyle("frame-floatframe", { based_on = "frame", border_style = "ridge", padding_pixels = 1 }) de.defstyle("tab", { based_on = "*", border_style = "groove", font = "anorexia", de.substyle("active-selected", { shadow_colour = "#1a3954", highlight_colour = "#7aa9d4", background_colour = "#4a6984", foreground_colour = "#ffffff", }), de.substyle("active-unselected", { shadow_colour = "#9c9a94", highlight_colour = "#ffffff", background_colour = "#dedbd6", foreground_colour = "#000000", }), de.substyle("inactive-selected", { shadow_colour = "#9c9a94", highlight_colour = "#ffffff", background_colour = "#efebe7", foreground_colour = "#97979e", }), de.substyle("inactive-unselected", { shadow_colour = "#9c9a94", highlight_colour = "#ffffff", background_colour = "#dedbd6", foreground_colour = "#97979e", }), text_align = "center", }) de.defstyle("tab-frame", { based_on = "tab", de.substyle("*-*-*-*-activity", { shadow_colour = "#777777", highlight_colour = "#eeeeee", background_colour = "#990000", foreground_colour = "#eeeeee", }), }) de.defstyle("tab-frame-ionframe", { based_on = "tab-frame", spacing = 3, }) de.defstyle("tab-menuentry", { based_on = "tab", spacing = 2, highlight_pixels = 1, shadow_pixels = 1, text_align = "left", font = "fixed", de.substyle("inactive-selected", { shadow_colour = "#1a3954", highlight_colour = "#7aa9d4", background_colour = "#4a6984", foreground_colour = "#ffffff", }), de.substyle("inactive-unselected", { shadow_colour = "#9c9a94", highlight_colour = "#ffffff", background_colour = "#dedbd6", foreground_colour = "#000000", }), }) de.defstyle("tab-menuentry-big", { based_on = "tab-menuentry", font = "-*-terminus-medium-r-*-*-17-120-*-*-*-*-iso8859-15", }) de.defstyle("input", { based_on = "tab", font = "-*-terminus-medium-r-*-*-17-120-*-*-*-*-iso8859-15", de.substyle("*-cursor", { background_colour = "#000000", foreground_colour = "#aaaaaa", }), de.substyle("*-selection", { background_colour = "#aaaaaa", foreground_colour = "black", }), }) gr.refresh() notion-3+2012042300/contrib/styles/look_minimalist.lua000066400000000000000000000106001174530661200224370ustar00rootroot00000000000000-- look_minimalist.lua drawing engine configuration file for Ion. --one basic style motivated by the need to work with min_tabs.lua --borders are slightly wider, and active-selected colour is much more --vivid so it's easy to tell which frame has the focus even without tabs --also, use relatively small font for the tabs -- which are always on --screen -- but use a much bigger & easier on the eye font for pop-up --menus which only take up space on screen transiently 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-*-10-*-*-*-*-*-*-*", text_align = "center", }) de.defstyle("frame", { based_on = "*", shadow_colour = "#404000", highlight_colour = "#c0c000", padding_colour = "#808000", background_colour = "#000000", foreground_colour = "#000000", padding_pixels = 1, highlight_pixels = 1, shadow_pixels = 1, de.substyle("active", { shadow_colour = "#ff0000", highlight_colour = "#ff0000", padding_colour = "#ff0000", background_colour = "#d8d8d8", foreground_colour = "#000000", }), }) de.defstyle("frame-ionframe", { based_on = "frame", border_style = "inlaid", padding_pixels = 1, spacing = 0, }) de.defstyle("frame-floatframe", { based_on = "frame", border_style = "ridge" }) de.defstyle("tab", { based_on = "*", font = "-*-helvetica-medium-r-normal-*-10-*-*-*-*-*-*-*", padding_pixels = 0, highlight_pixels = 0, shadow_pixels = 0, de.substyle("active-selected", { shadow_colour = "#df2900", highlight_colour = "#ff5900", background_colour = "#ff3900", foreground_colour = "#000000", }), de.substyle("active-unselected", { shadow_colour = "#dfb700", highlight_colour = "#ffe700", background_colour = "#ffc700", foreground_colour = "#000000", }), de.substyle("inactive-selected", { shadow_colour = "#6b2900", highlight_colour = "#ab6900", background_colour = "#8b4900", foreground_colour = "#000000", }), de.substyle("inactive-unselected", { shadow_colour = "#6b5500", highlight_colour = "#ab9500", background_colour = "#8b7500", foreground_colour = "#000000", }), text_align = "center", }) de.defstyle("tab-frame", { based_on = "tab", de.substyle("*-*-*-*-activity", { shadow_colour = "#600000", highlight_colour = "#ff0000", background_colour = "#32bc32", foreground_colour = "#ff0000", }), }) de.defstyle("tab-frame-ionframe", { based_on = "tab-frame", spacing = 0, }) de.defstyle("tab-menuentry", { based_on = "tab", font = "-*-helvetica-medium-r-normal-*-24-*-*-*-*-*-*-*", text_align = "left", highlight_pixels = 0, shadow_pixels = 0, --make tab menus bright rather than drab even with inactive tabs de.substyle("inactive-unselected", { shadow_colour = "#dfb700", highlight_colour = "#ffe700", background_colour = "#ffc700", foreground_colour = "#000000", }), }) de.defstyle("tab-menuentry-big", { based_on = "tab-menuentry", font = "-*-helvetica-medium-r-normal-*-24-*-*-*-*-*-*-*", padding_pixels = 7, de.substyle("inactive-unselected", { shadow_colour = "#dfb700", highlight_colour = "#ffe700", background_colour = "#ffc700", foreground_colour = "#000000", }), }) 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, font = "-*-helvetica-medium-r-normal-*-24-*-*-*-*-*-*-*", border_style = "elevated", de.substyle("*-cursor", { background_colour = "#000000", foreground_colour = "#d8d8d8", }), de.substyle("*-selection", { background_colour = "#f0c000", foreground_colour = "#000000", }), }) de.defstyle("stdisp", { based_on = "*", shadow_pixels = 0, highlight_pixels = 0, text_align = "left", }) gr.refresh() notion-3+2012042300/contrib/styles/look_moy.lua000066400000000000000000000164041174530661200211050ustar00rootroot00000000000000-- 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 = "#dddddd", padding_colour = "#aaaaaa", background_colour = "#000000", foreground_colour = "#ffffff", padding_pixels = 1, highlight_pixels = 1, shadow_pixels = 1, de.substyle("active", { border_style = "inlaid", shadow_colour = "#4444AA", highlight_colour = "#ccaaff", background_colour = "#aaaaaa", foreground_colour = "#ffffff", }), }) de.defstyle("frame-ionframe", { based_on = "frame", border_style = "inlaid", padding_pixels = 1, spacing = 0, de.substyle("active", { highlight_pixels = 10, shadow_pixels = 10, padding_pixels = 10, border_style = "inlaid", shadow_colour = "#4422AA", padding_colour = "#CCAADD", highlight_colour = "#aa99CC", background_colour = "#aaaaaa", foreground_colour = "#ffffff", }), }) de.defstyle("frame-floatframe", { based_on = "frame", border_style = "ridge" }) de.defstyle("tab", { based_on = "*", font = "-*-helvetica-medium-r-normal-*-12-*-*-*-*-*-*-*", de.substyle("active-selected", { shadow_colour = "#8888aa", highlight_colour = "#333366", background_colour = "#666699", foreground_colour = "#eeeeee", }), de.substyle("active-unselected", { shadow_colour = "#777777", highlight_colour = "#cccccc", background_colour = "#aaa5aa", foreground_colour = "#000000", }), de.substyle("inactive-selected", { shadow_colour = "#aaaadd", highlight_colour = "#777788", background_colour = "#9999aa", foreground_colour = "#000000", }), de.substyle("inactive-unselected", { shadow_colour = "#777777", highlight_colour = "#cccccc", background_colour = "#bbbbbb", foreground_colour = "#222222", }), text_align = "center", }) de.defstyle("tab-frame", { based_on = "tab", de.substyle("*-*-*-*-activity", { shadow_colour = "#777777", highlight_colour = "#eeeeee", background_colour = "#990000", foreground_colour = "#eeeeee", }), }) de.defstyle("tab-frame-ionframe", { based_on = "tab-frame", spacing = 0, }) de.defstyle("tab-frame-floatframe", { based_on = "tab-frame", de.substyle("active-selected", { shadow_colour = "#333366", highlight_colour = "#8888aa", background_colour = "#666699", foreground_colour = "#eeeeee", }), de.substyle("active-unselected", { shadow_colour = "#cccccc", highlight_colour = "#777777", background_colour = "#aaa5aa", foreground_colour = "#000000", }), de.substyle("inactive-selected", { shadow_colour = "#777788", highlight_colour = "#aaaadd", background_colour = "#9999aa", foreground_colour = "#000000", }), de.substyle("inactive-unselected", { shadow_colour = "#cccccc", highlight_colour = "#777777", background_colour = "#aaaaaa", foreground_colour = "#000000", }), }) de.defstyle("tab-menuentry", { based_on = "tab", text_align = "left", highlight_pixels = 1, shadow_pixels = 1, }) de.defstyle("tab-menuentry-big", { based_on = "tab-menuentry", font = "-*-helvetica-medium-r-normal-*-17-*-*-*-*-*-*-*", padding_pixels = 7, }) de.defstyle("input", { based_on = "*", font = "-Adobe-Courier-Medium-R-Normal--12-120-75-75-M-70-ISO8859-1", shadow_colour = "#777777", highlight_colour = "#eeeeee", background_colour = "#aaaaaa", foreground_colour = "#100040", padding_pixels = 1, highlight_pixels = 1, shadow_pixels = 1, border_style = "elevated", de.substyle("*-cursor", { background_colour = "#300070", foreground_colour = "#aaaaaa", }), de.substyle("*-selection", { background_colour = "#8080aa", foreground_colour = "#300070", }), }) gr.refresh() notion-3+2012042300/contrib/styles/look_ootput.lua000066400000000000000000000070161174530661200216320ustar00rootroot00000000000000-- look-ootput.lua drawing engine configuration file for Ion. ---- if not gr.select_engine("de") then return end de.reset() de.defstyle("*", { shadow_colour = "grey20", highlight_colour = "grey20", background_colour = "black", foreground_colour = "#778bb3", padding_pixels = 1, highlight_pixels = 0, shadow_pixels = 0, border_style = "elevated", font = "profont11", text_align = "center", }) de.defstyle("frame", { based_on = "*", padding_colour = "grey20", transparent_background = true, background_colour = "black", de.substyle("active", { shadow_colour = "black", highlight_colour = "black", padding_colour = "#778bb3", background_colour = "black", }), }) de.defstyle("frame-ionframe", { based_on = "frame", padding_pixels = 1, highlight_pixels = 0, shadow_pixels = 0, spacing = 0, }) de.defstyle("tab", { based_on = "*", de.substyle("active-selected", { shadow_colour = "#374b83", highlight_colour = "#374b83", background_colour = "#778bb3", foreground_colour = "black", padding_colour = "black", }), de.substyle("active-unselected", { foreground_colour = "grey40", padding_colour = "#000060", }), de.substyle("inactive-selected", { foreground_colour = "#778bb3", padding_colour = "#000060", }), de.substyle("inactive-unselected", { foreground_colour = "grey40", padding_colour = "#000060", }), text_align = "center", }) de.defstyle("tab-frame", { based_on = "tab", de.substyle("*-*-*-*-activity", { background_colour = "#ff8171", foreground_colour = "black", }), }) de.defstyle("tab-menuentry", { based_on = "*", text_align = "left", de.substyle("active-selected", { shadow_colour = "#374b83", highlight_colour = "#374b83", background_colour = "#778bb3", foreground_colour = "black", }), de.substyle("active-unselected", { foreground_colour = "#778bb3", }), de.substyle("inactive-selected", { background_colour = "#97abd3", foreground_colour = "black", }), de.substyle("inactive-unselected", { foreground_colour = "grey40", }), }) de.defstyle("tab-menuentry-big", { based_on = "tab-menuentry", padding_pixels = 5, }) de.defstyle("input", { based_on = "*", shadow_colour = "black", highlight_colour = "black", background_colour = "#778bb3", foreground_colour = "black", de.substyle("*-cursor", { background_colour = "white", foreground_colour = "black", }), de.substyle("*-selection", { background_colour = "#aaaaaa", foreground_colour = "black", }), }) de.defstyle("stdisp", { based_on = "tab", background_colour = "#000000", padding_colour = "#000000", de.substyle("important", { foreground_colour = "#ffff00", }), de.substyle("critical", { foreground_colour = "#ff0000", }), de.substyle("gray", { foreground_colour = "#505050", }), de.substyle("red", { foreground_colour = "#ff0000", }), de.substyle("green", { foreground_colour = "#00ff00", }), de.substyle("blue", { foreground_colour = "#0000ff", }), de.substyle("cyan", { foreground_colour = "#00ffff", }), de.substyle("magenta", { foreground_colour = "#ff00ff", }), de.substyle("yellow", { foreground_colour = "#ffff00", }), }) de.defstyle("dock", { outline_style = "all", }) gr.refresh() notion-3+2012042300/contrib/styles/look_ootput_dark.lua000066400000000000000000000061401174530661200226300ustar00rootroot00000000000000-- look_ootput_dark.lua drawing engine configuration file for Ion. ---- if not gr.select_engine("de") then return end de.reset() de.defstyle("*", { background_colour = "black", foreground_colour = "#204a87", font = "profont11", border_style = "elevated", highlight_colour = "grey20", highlight_pixels = 0, padding_pixels = 1, shadow_colour = "grey20", shadow_pixels = 0, spacing = 0, text_align = "center", }) de.defstyle("frame", { based_on = "*", padding_colour = "grey10", transparent_background = true, de.substyle("active", { shadow_colour = "black", highlight_colour = "black", padding_colour = "#204a87", }), }) de.defstyle("frame-ionframe", { based_on = "frame", }) de.defstyle("tab", { based_on = "*", de.substyle("active-selected", { shadow_colour = "#374b83", highlight_colour = "#374b83", background_colour = "#204a87", foreground_colour = "black", padding_colour = "black", }), de.substyle("active-unselected", { foreground_colour = "grey40", padding_colour = "#000060", }), de.substyle("inactive-selected", { padding_colour = "#000060", }), de.substyle("inactive-unselected", { foreground_colour = "grey40", padding_colour = "#000060", }), text_align = "center", }) de.defstyle("tab-frame", { based_on = "tab", de.substyle("*-*-*-*-activity", { foreground_colour = "#ff8171", }), }) de.defstyle("tab-menuentry", { based_on = "*", text_align = "left", de.substyle("active-selected", { shadow_colour = "#374b83", highlight_colour = "#374b83", background_colour = "#204a87", foreground_colour = "black", }), de.substyle("active-unselected", { }), de.substyle("inactive-selected", { background_colour = "#97abd3", foreground_colour = "black", }), de.substyle("inactive-unselected", { foreground_colour = "grey40", }), }) de.defstyle("tab-menuentry-big", { based_on = "tab-menuentry", }) de.defstyle("input", { based_on = "*", shadow_colour = "black", highlight_colour = "black", de.substyle("*-cursor", { background_colour = "white", foreground_colour = "black", }), de.substyle("*-selection", { foreground_colour = "#aaaaaa", }), }) de.defstyle("stdisp", { based_on = "tab", padding_colour = "black", de.substyle("important", { foreground_colour = "#ffff00", }), de.substyle("critical", { foreground_colour = "#ff0000", }), de.substyle("gray", { foreground_colour = "#505050", }), de.substyle("red", { foreground_colour = "#ff0000", }), de.substyle("green", { foreground_colour = "#00ff00", }), de.substyle("blue", { foreground_colour = "#0000ff", }), de.substyle("cyan", { foreground_colour = "#00ffff", }), de.substyle("magenta", { foreground_colour = "#ff00ff", }), de.substyle("yellow", { foreground_colour = "#ffff00", }), }) de.defstyle("dock", { outline_style = "all", }) gr.refresh() notion-3+2012042300/contrib/styles/look_outback.lua000066400000000000000000000071411174530661200217270ustar00rootroot00000000000000-- look_outback.lua : Mark Tran -- This style looks best when the alternative style is applied to "full" -- workspace frames. You can enable it by applying: _:set_mode('tiled-alt') -- as Lua code. -- -- View http://modeemi.fi/~tuomov/ion/faq/entries/Hiding_the_tab-bar.html -- for further details. if not gr.select_engine("de") then return end de.reset() de.defstyle("*", { border_style = "elevated", background_colour = "#453832", foreground_colour = "#ffffff", highlight_pixels = 0, padding_pixels = 0, shadow_pixels = 0, highlight_colour = "#000000", padding_colour = "#453832", shadow_colour = "#000000", font = "-xos4-terminus-*-r-normal--12-120-*-*-c-*-iso8859-1", text_align = "center", }) de.defstyle("frame", { based_on = "*", border_style = "inlaid", spacing = 1, background_colour = "#f5deb3", foreground_colour = "#000000", padding_pixels = 1, padding_colour = "#453832", }) de.defstyle("frame-floating", { bar = "inside", padding_pixels = 1, spacing = 0, }) de.defstyle("frame-tiled-alt", { bar = "inside", spacing = 0, }) de.defstyle("frame-transient", { spacing = 0, }) de.defstyle("tab", { based_on = "*", highlight_pixels = 1, padding_pixels = 1, shadow_pixels = 1, highlight_colour = "#352722", shadow_colour = "#352722", spacing = 1, de.substyle("active-selected", { background_colour = "#983008", foreground_colour = "#ffffff", highlight_colour = "#d04008", shadow_colour = "#d04008", }), de.substyle("active-unselected", { background_colour = "#453832", foreground_colour = "#ffffff", }), de.substyle("inactive-selected", { background_colour = "#453832", foreground_colour = "#ffffff", }), de.substyle("inactive-unselected", { background_colour = "#453832", foreground_colour = "#ffffff", }), }) de.defstyle("tab-frame-floating", { based_on = "tab", highlight_pixels = 0, shadow_pixels = 0, }) de.defstyle("tab-frame-tiled-alt", { based_on = "tab", highlight_pixels = 0, shadow_pixels = 0, }) de.defstyle("tab-menuentry", { based_on = "tab", highlight_pixels = 0, padding_pixels = 3, shadow_pixels = 0, text_align = "left", de.substyle("inactive-selected", { background_colour = "#983008", foreground_colour = "#ffffff", }), }) de.defstyle("tab-menuentry-big", { padding_pixels = 5, }) de.defstyle("input", { background_colour = "#352722", padding_pixels = 2, }) de.defstyle("input-edln", { background_colour = "#453832", highlight_pixels = 1, shadow_pixels = 1, highlight_colour = "#473e3b", shadow_colour = "#473e3b", de.substyle("*-cursor", { background_colour = "#ffffff", foreground_colour = "#000000", }), de.substyle("*-selection", { background_colour = "#983008", foreground_colour = "#ffffff", }), }) de.defstyle("input-message", { background_colour = "#453832", }) de.defstyle("stdisp-statusbar", { based_on = "*", de.substyle("important", { foreground_colour = "green", }), de.substyle("critical", { foreground_colour = "red", }), }) de.defstyle("actnotify", { based_on = "*", highlight_pixels = 1, padding_pixels = 1, shadow_pixels = 1, background_colour = "#983008", foreground_colour = "#ffffff", highlight_colour = "#d04008", shadow_colour = "#d04008", }) de.defstyle("moveres_display", { based_on = "actnotify", }) gr.refresh() notion-3+2012042300/contrib/styles/look_qt.lua000066400000000000000000000105111174530661200207160ustar00rootroot00000000000000-- Look-qtrc 0.1 for the Ion window manager. -- Based on look-cleanviolet. -- -- I am using the QtCurve Gtk+ theme; now Qt, Gtk+ and Ion share the same -- color theme automatically. :-) -- -- http://www.kde-look.org/content/show.php?content=5065 -- -- Johan "Ion" Kiviniemi local xft = false local basefont local tabfont local menufont --if gr.select_engine("xftde") then if false then xft = true basefont = "xft:Bitstream Vera Sans:size=9:weight=bold" tabfont = "xft:Bitstream Vera Sans:size=8:weight=bold" menufont = "xft:Bitstream Vera Sans:size=10:weight=bold" elseif gr.select_engine("de") then xft = false basefont = "-*-bitstream vera sans-bold-r-normal-*-9-*-*-*-*-*-*-*" font = "-*-bitstream vera sans-bold-r-normal-*-8-*-*-*-*-*-*-*" menufont = "-*-bitstream vera sans-bold-r-normal-*-10-*-*-*-*-*-*-*" else return end local qtrcfile = os.getenv("HOME").."/.qt/qtrc" local palette = {} -- palette[1] = active, palette[2] = inactive, palette[3] = disabled for i = 1, 3 do palette[i] = {} for j = 1, 16 do palette[i][j] = "#000000" end end local inpalette = false local paletteid = 0 for line in io.lines(qtrcfile) do if string.find(line, "^%[") then inpalette = false end if string.find(line, "^%[Palette%]") then inpalette = true end if inpalette then if string.find(line, "^active=") then paletteid = 1 elseif string.find(line, "^inactive=") then paletteid = 2 elseif string.find(line, "^disabled=") then paletteid = 3 else paletteid = 0 end if paletteid > 0 then local i = 1 for v in string.gfind(line, "(#[0-9a-fA-F]+)") do palette[paletteid][i] = v i = i+1 end end end end -- Clear existing styles from memory. de.reset() -- Base style de.defstyle("*", { highlight_colour = palette[1][4], shadow_colour = palette[1][5], background_colour = palette[1][2], foreground_colour = palette[1][1], shadow_pixels = 1, highlight_pixels = 1, padding_pixels = 1, spacing = 0, border_style = "elevated", font = basefont, text_align = "left", }) de.defstyle("frame", { based_on = "*", padding_colour = palette[1][2], background_colour = palette[1][1], transparent_background = false, }) de.defstyle("frame-ionframe", { based_on = "frame", shadow_pixels = 0, highlight_pixels = 0, padding_pixels = 0, spacing = 1, }) de.defstyle("tab", { based_on = "*", font = tabfont, de.substyle("active-selected", { highlight_colour = palette[1][13], shadow_colour = palette[1][13], background_colour = palette[1][13], foreground_colour = palette[1][14], }), de.substyle("inactive-selected", { highlight_colour = palette[1][4], shadow_colour = palette[1][5], background_colour = palette[1][2], foreground_colour = palette[1][1], }), }) de.defstyle("tab-frame", { based_on = "tab", de.substyle("*-*-*-*-activity", { -- Red tab highlight_colour = "#ffffff", shadow_colour = "#ffffff", background_colour = "#990000", foreground_colour = "#ffffff", }), }) de.defstyle("tab-frame-ionframe", { based_on = "tab-frame", spacing = 1, bar_inside_frame = true, }) de.defstyle("tab-menuentry", { based_on = "tab", text_align = "left", spacing = 1, }) de.defstyle("tab-menuentry-pmenu", { based_on = "tab-menuentry", de.substyle("inactive-selected", { highlight_colour = palette[1][13], shadow_colour = palette[1][13], background_colour = palette[1][13], foreground_colour = palette[1][14], }), }) de.defstyle("tab-menuentry-big", { based_on = "tab-menuentry", font = menufont, padding_pixels = 5, }) de.defstyle("input", { based_on = "*", text_align = "left", spacing = 1, highlight_colour = palette[1][4], shadow_colour = palette[1][5], background_colour = palette[1][2], foreground_colour = palette[1][1], de.substyle("*-selection", { background_colour = "#0000ff", foreground_colour = "#00ff00", }), de.substyle("*-cursor", { background_colour = palette[1][1], foreground_colour = palette[1][2], }), }) -- Refresh objects' brushes. gr.refresh() notion-3+2012042300/contrib/styles/look_tibi.lua000066400000000000000000000115241174530661200212260ustar00rootroot00000000000000-- $Id: look_tibi.lua 78 2007-02-07 18:36:32Z tibi $ -- version : 0.2 -- date : 2007-02-07 -- author : Tibor Csögör -- This style highlights active elements with an `accent' color. Bright and -- dimmed variants emphasize the level of importance. The corresponding neutral -- colors are (roughly) the non-saturated versions. -- The author likes the color scheme `gold' best, however, feel free to -- experiment with the accent color(s). -- This software is in the public domain. -- color configuration --------------------------------------------------------- -- gold local my_accent_color_bright = "lightgoldenrod1" local my_accent_color_normal = "gold1" local my_accent_color_dimmed = "gold2" local my_accent_color_dark = "gold3" -- green -- local my_accent_color_bright = "#c2ffc2" -- local my_accent_color_normal = "palegreen1" -- local my_accent_color_dimmed = "palegreen2" -- local my_accent_color_dark = "palegreen3" -- blue -- local my_accent_color_bright = "lightblue1" -- local my_accent_color_normal = "skyblue1" -- local my_accent_color_dimmed = "skyblue2" -- local my_accent_color_dark = "skyblue3" -- plum -- local my_accent_color_bright = "#ffd3ff" -- local my_accent_color_normal = "plum1" -- local my_accent_color_dimmed = "plum2" -- local my_accent_color_dark = "plum3" -------------------------------------------------------------------------------- -- neutral colors local my_neutral_color_normal = "grey85" local my_neutral_color_dimmed = "grey70" local my_neutral_color_dark = "grey20" if not gr.select_engine("de") then return end de.reset() de.defstyle("*", { padding_pixels = 0, spacing = 0, foreground_colour = "black", background_colour = my_accent_color_bright, highlight_pixels = 1, highlight_colour = "black", shadow_pixels = 1, shadow_colour = "black", border_style = "elevated", }) de.defstyle("frame", { based_on = "*", background_colour = "black", }) de.defstyle("frame-floating", { based_on = "frame", padding_pixels = 0, highlight_pixels = 3, highlight_colour = my_neutral_color_normal, shadow_pixels = 3, shadow_colour = my_neutral_color_normal, de.substyle("active", { highlight_colour = my_accent_color_normal, shadow_colour = my_accent_color_normal, }), }) de.defstyle("frame-tiled", { based_on = "frame", spacing = 1, padding_pixels = 3, highlight_pixels = 2, highlight_colour = my_neutral_color_dark, shadow_pixels = 2, shadow_colour = my_neutral_color_dark, de.substyle("active", { highlight_colour = my_accent_color_normal, shadow_colour = my_accent_color_normal, }), }) de.defstyle("tab", { based_on = "*", spacing = 2, padding_pixels = 2, highlight_pixels = 0, shadow_pixels = 0, foreground_colour = "black", background_colour = my_neutral_color_dimmed, text_align = "center", font = "-*-helvetica-bold-r-normal-*-10-*-*-*-*-*-*-*", de.substyle("active-selected", { background_colour = my_accent_color_normal, }), de.substyle("active-unselected", { background_colour = my_accent_color_dark, }), de.substyle("inactive-selected", { background_colour = my_neutral_color_normal, }), }) de.defstyle("input-edln", { based_on = "*", padding_pixels = 2, foreground_colour = "black", background_colour = my_accent_color_bright, highlight_pixels = 1, highlight_colour = my_accent_color_dark, shadow_pixels = 1, shadow_colour = my_accent_color_dark, font ="-*-lucidatypewriter-medium-r-*-*-10-*-*-*-*-*-*-*", de.substyle("*-cursor", { foreground_colour = my_accent_color_bright, background_colour = "black", }), de.substyle("*-selection", { foreground_colour = "black", background_colour = my_accent_color_dimmed, }), }) de.defstyle("stdisp", { based_on = "*", padding_pixels = 4, shadow_pixels = 0, highlight_pixels = 0, background_colour = my_neutral_color_dark, foreground_colour = "white", font ="-*-lucida-medium-r-*-*-10-*-*-*-*-*-*-*", }) de.defstyle("tab-menuentry", { based_on = "*", text_align = "left", padding_pixels = 4, spacing = 0, shadow_pixels = 0, highlight_pixels = 0, font = "-*-helvetica-medium-r-normal-*-10-*-*-*-*-*-*-*", foreground_colour = "black", background_colour = my_accent_color_bright, de.substyle("inactive-selected", { background_colour = my_accent_color_dimmed, }), }) de.defstyle("actnotify", { based_on = "*", padding_pixels = 4, highlight_pixels = 2, highlight_colour = my_accent_color_normal, shadow_pixels = 2, shadow_colour = my_accent_color_normal, background_colour = "red", foreground_colour = "white", font = "-*-helvetica-bold-r-normal-*-10-*-*-*-*-*-*-*", }) gr.refresh() -- EOF notion-3+2012042300/contrib/styles/look_tiny.lua000066400000000000000000000051361174530661200212640ustar00rootroot00000000000000-- look_tiny.lua: an ion3 style for small screens...like mine :( -- based on look_minimalist.lua if not gr.select_engine("de") then return end de.reset() de.defstyle("*", { bar_inside_border = false, background_colour = "#000000", foreground_colour = "#ffffff", padding_colour = "#505050", padding_pixels = 0, highlight_pixels = 0, shadow_pixels = 0, border_style = "ridge", font = "-*-lucida-medium-r-normal-*-10-*-*-*-*-*-*-*", text_align = "left", transparent_background = true, }) de.defstyle("frame", { based_on = "*", spacing = 2, }) de.defstyle("frame-floating", { based_on = "frame", highlight_colour = "#888888", shadow_colour = "#333333", highlight_pixels = 1, shadow_pixels = 1, padding_pixels = 3, }) de.defstyle("tabstyle", { based_on = "*", background_colour = "#222222", de.substyle("active-unselected", { padding_colour = "#353577", background_colour = "#000044", }), de.substyle("inactive-selected", { padding_colour = "#707070", background_colour = "#505050", }), de.substyle("active-selected", { padding_colour = "#5555cc", background_colour = "#223399", }), }) de.defstyle("tab", { based_on = "tabstyle", spacing = 2, padding_pixels=1, }) de.defstyle("tab-menuentry", { based_on = "tabstyle", font = "-*-lucida-medium-r-normal-*-12-*-*-*-*-*-*-*", text_align = "left", }) de.defstyle("tab-menuentry-big", { based_on = "tab-menuentry", font = "-*-lucida-medium-r-normal-*-24-*-*-*-*-*-*-*", }) de.defstyle("input", { based_on = "*", padding_pixels = 1, font = "-*-lucida-medium-r-normal-*-14-*-*-*-*-*-*-*", de.substyle("*-cursor", { background_colour = "#00ff00", foreground_colour = "#000000", }), de.substyle("*-selection", { foreground_colour = "#5555cc", }), }) de.defstyle("stdisp", { based_on = "tab", background_colour = "#000000", padding_colour = "#000000", de.substyle("important", { foreground_colour = "#ffff00", }), de.substyle("critical", { foreground_colour = "#ff0000", }), de.substyle("gray", { foreground_colour = "#505050", }), de.substyle("red", { foreground_colour = "#ff0000", }), de.substyle("green", { foreground_colour = "#00ff00", }), de.substyle("blue", { foreground_colour = "#0000ff", }), de.substyle("cyan", { foreground_colour = "#00ffff", }), de.substyle("magenta", { foreground_colour = "#ff00ff", }), de.substyle("yellow", { foreground_colour = "#ffff00", }), }) gr.refresh() notion-3+2012042300/contrib/styles/look_tiny_min_tabs.lua000066400000000000000000000057451174530661200231460ustar00rootroot00000000000000-- look_tiny.min_tabs.lua: an ion3 style for use with the min_tabs -- extension, based on: -- look_tiny.lua: an ion3 style for small screens...like mine :( -- based on look_minimalist.lua if not gr.select_engine("de") then return end de.reset() de.defstyle("*", { bar_inside_border = true, background_colour = "#000000", foreground_colour = "#ffffff", padding_colour = "#505050", padding_pixels = 0, highlight_pixels = 0, shadow_pixels = 0, border_style = "ridge", font = "-*-lucida-medium-r-normal-*-10-*-*-*-*-*-*-*", text_align = "left", transparent_background = true, }) de.defstyle("frame", { based_on = "*", spacing = 2, }) de.defstyle("frame-tiled", { based_on = "frame", border_style = "elevated", padding_pixels = 2, highlight_colour = "#000000", shadow_colour = "#000000", highlight_pixels = 1, shadow_pixels = 1, de.substyle("active", { padding_colour = "#5555cc", }) }) de.defstyle("frame-floating", { based_on = "frame", highlight_colour = "#888888", shadow_colour = "#333333", highlight_pixels = 1, shadow_pixels = 1, padding_pixels = 3, }) de.defstyle("tabstyle", { based_on = "*", background_colour = "#222222", de.substyle("active-unselected", { padding_colour = "#353577", background_colour = "#000044", }), de.substyle("inactive-selected", { padding_colour = "#707070", background_colour = "#505050", }), de.substyle("active-selected", { padding_colour = "#5555cc", background_colour = "#223399", }), }) de.defstyle("tab", { based_on = "tabstyle", spacing = 2, padding_pixels=1, }) de.defstyle("tab-menuentry", { based_on = "tabstyle", font = "-*-lucida-medium-r-normal-*-12-*-*-*-*-*-*-*", text_align = "left", }) de.defstyle("tab-menuentry-big", { based_on = "tab-menuentry", font = "-*-lucida-medium-r-normal-*-24-*-*-*-*-*-*-*", }) de.defstyle("input", { based_on = "*", padding_pixels = 1, font = "-*-lucida-medium-r-normal-*-14-*-*-*-*-*-*-*", de.substyle("*-cursor", { background_colour = "#00ff00", foreground_colour = "#000000", }), de.substyle("*-selection", { foreground_colour = "#5555cc", }), }) de.defstyle("stdisp", { based_on = "tab", background_colour = "#000000", padding_colour = "#000000", de.substyle("important", { foreground_colour = "#ffff00", }), de.substyle("critical", { foreground_colour = "#ff0000", }), de.substyle("gray", { foreground_colour = "#505050", }), de.substyle("red", { foreground_colour = "#ff0000", }), de.substyle("green", { foreground_colour = "#00ff00", }), de.substyle("blue", { foreground_colour = "#0000ff", }), de.substyle("cyan", { foreground_colour = "#00ffff", }), de.substyle("magenta", { foreground_colour = "#ff00ff", }), de.substyle("yellow", { foreground_colour = "#ffff00", }), }) gr.refresh() notion-3+2012042300/contrib/styles/look_whitecode.lua000066400000000000000000000070641174530661200222560ustar00rootroot00000000000000 if not gr.select_engine("de") then return end de.reset() de.defstyle("*", { foreground_colour = "#000000", background_colour = "#f0f0f0", border_style = "ridge", padding_colour = "#c0c0c0", highlight_colour = "#000000", shadow_colour = "#000000", padding_pixels = 1, highlight_pixels = 0, shadow_pixels = 0, spacing = 1, font = "-*-helvetica-medium-r-normal-*-12-*-*-*-*-*-*-*", text_align = "center", }) de.defstyle("frame", { based_on = "*", shadow_pixels = 0, highlight_pixels = 0, spacing = 1, padding_pixels = 1, }) de.defstyle("frame-tiled", { based_on = "frame", }) de.defstyle("frame-floating", { based_on = "frame", floatframe_tab_min_w = 10000, floatframe_bar_max_w_q = 1, padding_colour = "#c0c0c0", de.substyle("active", { padding_colour = "black", }) }) de.defstyle("tab", { based_on = "*", border_style = "elevated", padding_pixels = 1, highlight_pixels = 1, shadow_pixels = 1, spacing = 0, text_align = "left", }) de.defstyle("tab-frame", { based_on = "tab", de.substyle("active-selected", { background_colour = "#ffffff", padding_colour = "#ffffff", }), de.substyle("active-unselected", { background_colour = "#e0e0e0", padding_colour = "#e0e0e0", }), de.substyle("inactive-selected", { background_colour = "#d0d0d0", padding_colour = "#d0d0d0", foreground_colour = "#808080", shadow_colour = "#d0d0d0", highlight_colour = "#d0d0d0", }), de.substyle("inactive-unselected", { background_colour = "#c0c0c0", foreground_colour = "#808080", padding_colour = "#c0c0c0", shadow_colour = "#c0c0c0", highlight_colour = "#c0c0c0", }), }) de.defstyle("tab-frame-tiled", { based_on = "tab-frame", spacing = 1, }) de.defstyle("tab-menuentry", { de.substyle("active-selected", { background_colour = "#e0e0e0", background_colour = "#b0c0d0", background_colour = "#505050", padding_colour = "#e0e0e0", padding_colour = "#b0c0d0", padding_colour = "#505050", foreground_colour = "#f0f0f0", }), de.substyle("active-unselected", { background_colour = "#ffffff", padding_colour = "#ffffff", }), de.substyle("inactive-selected", { background_colour = "#c0c0c0", foreground_colour = "#808080", padding_colour = "#c0c0c0", }), de.substyle("inactive-unselected", { background_colour = "#d0d0d0", padding_colour = "#d0d0d0", foreground_colour = "#808080", }), based_on = "tab", text_align = "left", border_style = "inlaid", highlight_pixels = 0, shadow_pixels = 0, spacing = 1, padding_pixels = 2, }) de.defstyle("tab-menuentry-big", { based_on = "tab-menuentry", border_style = "inlaid", padding_pixels = 5, spacing = 1, }) de.defstyle("input", { based_on = "*", padding_pixels = 1, highlight_pixels = 0, shadow_pixels = 0, border_style = "elevated", de.substyle("*-cursor", { background_colour = "#000000", foreground_colour = "#ffffff", }), de.substyle("*-selection", { background_colour = "#c0c0c0", foreground_colour = "#000000", }), }) de.defstyle("input-menu", { based_on = "*", }) de.defstyle("stdisp", { based_on = "*", shadow_pixels = 0, highlight_pixels = 0, padding_pixels = 0, spacing = 0, background_colour = "#d0d0d0", }) gr.refresh() notion-3+2012042300/contrib/verify_index.pl000066400000000000000000000131111174530661200202470ustar00rootroot00000000000000#!/usr/bin/perl -w # # Script to verify that the index.html is in good shape. # This will verify that the entries: # 1) Are in order per section # 2) Exist on disk if in the index.html # 3) Are in the index.html if they exist on disk # # Run it from the ion-scripts-3/ directory. # # tyranix [ tyranix at gmail] # # # verify_index.pl is free software; you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation; either version 2.1 of the License, or # (at your option) any later version. # use strict; use warnings "all"; use diagnostics; sub report_error($); my $return_code = 0; # Darcs needs a return code for a test suite. my $line_count = 1; # Line count inside index.html { open(INDEX, "index.html") || die qq[Could not open "index.html": $!]; my %index_files = (); # Files found in "index.html" my %script_files = (); # Files found in the directory my %old_ignored_files = (); # Files marked for ion2 only my $section_counter = 1; # Section number (not same as line number) for (my $line = ; $line; $line = , $line_count++) { chomp($line); if ($line =~ m/^\s*
\s*([^<]+)<\/a>/) { my ($url, $name) = ($1, $2); my ($directory, $filename) = $url =~ m,(.+)/([^/]+),; if (!defined($directory) || !defined($filename)) { report_error("Malformed entry on line $line_count\n"); } if ($filename ne $name) { report_error(qq["$name" should be named "$filename"]); } # Reset the section_counter and setup the hashes. if (!exists($index_files{$directory})) { $section_counter = 1; $index_files{$directory} = {}; $script_files{$directory} = {}; # Add all of the actual files so we can compare the two hashes. opendir(FILES, $directory) || die qq[Could not open "$directory": $!]; while (my $entry = readdir(FILES)) { if ($entry !~ /^\./ && -f "$directory/$entry") { ${$script_files{$directory}}{$entry} = 1; } } closedir(FILES) || die qq[Could not close "$directory": $!]; } if (exists(${$index_files{$directory}}{$filename})) { report_error("Duplicate entry $directory/$filename"); } # Add this file to the hash of this directory for later use. ${$index_files{$directory}}{$filename} = $section_counter++; } elsif ($line =~ m/^\s*
\s*\s*([^<]+)<\/a>/) { my ($url, $name) = ($1, $2); my ($directory, $filename) = $url =~ m,(.+)/([^/]+),; if (!defined($directory) || !defined($filename)) { report_error("Malformed entry on line $line_count\n"); } if ($filename ne $name) { report_error(qq["$name" should be named "$filename"]); } if (!exists($old_ignored_files{$directory})) { $old_ignored_files{$directory} = {}; } if (exists(${$old_ignored_files{$directory}}{$filename})) { report_error("Duplicate entry $directory/$filename"); } ${$old_ignored_files{$directory}}{$filename} = 1; } elsif ($line =~ m/^\s*
/) { # This is a dt entry but it's invalid. report_error("Invalid line: $line"); } } close(INDEX) || die qq[Could not close "index.html": $!]; my @out_of_order = (); my @not_on_disk = (); # Compare the index.html entries to what's on disk. for my $dir (keys %index_files) { my $count = 1; for my $key (sort(keys %{$index_files{$dir}})) { if (!exists(${$script_files{$dir}}{$key})) { push(@not_on_disk, "$dir/$key"); } else { if ($count != ${$index_files{$dir}}{$key}) { push(@out_of_order, ["$dir/$key", $count, ${$index_files{$dir}}{$key}]); } # Delete it so we can go through it later and see what wasn't # in the html file. delete(${$script_files{$dir}}{$key}); } $count++; } } if ($#not_on_disk != -1) { $return_code = 1; print "Files in the index.html but not on disk:\n"; for my $key (@not_on_disk) { print "\t$key\n"; } } if ($#out_of_order != -1) { $return_code = 1; print "Files out of order in index.html:\n"; for my $key (@out_of_order) { my ($name, $correct_value, $value_saw) = @{$key}; print "\t$name \tshould be in spot $correct_value but found in $value_saw\n"; } } my $printed_header = undef; for my $dir (keys %script_files) { for my $key (sort { uc($a) cmp uc($b) } keys %{$script_files{$dir}}) { if (!exists(${$old_ignored_files{$dir}}{$key})) { if (!defined($printed_header)) { print "Files on the disk but not in index.html:\n"; $printed_header = 1; $return_code = 1; } print "\t$dir/$key\n"; } } } exit($return_code); } sub report_error($) { my ($error) = @_; print STDERR qq[$0: Error in "index.html" on line $line_count\n\t$error\n]; $return_code = 1; } notion-3+2012042300/de/000077500000000000000000000000001174530661200141525ustar00rootroot00000000000000notion-3+2012042300/de/Makefile000066400000000000000000000010371174530661200156130ustar00rootroot00000000000000## ## 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 precompose.c MODULE=de MAKE_EXPORTS=de ###################################### include $(TOPDIR)/build/rules.mk ###################################### _install: module_install notion-3+2012042300/de/brush.c000066400000000000000000000154641174530661200154530ustar00rootroot00000000000000/* * ion/de/brush.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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 */ #define MATCHES(S, A) (gr_stylespec_score(&(S), A)>0) #define MATCHES2(S, A1, A2) (gr_stylespec_score2(&(S), A1, A2)>0) #define ENSURE_INITSPEC(S, NM) \ if((S).attrs==NULL) gr_stylespec_load(&(S), NM); static GrStyleSpec tabframe_spec=GR_STYLESPEC_INIT; static GrStyleSpec tabinfo_spec=GR_STYLESPEC_INIT; static GrStyleSpec tabmenuentry_spec=GR_STYLESPEC_INIT; bool debrush_init(DEBrush *brush, Window win, const GrStyleSpec *spec, DEStyle *style) { GrStyleSpec tmp; brush->d=style; brush->extras_fn=NULL; brush->indicator_w=0; brush->win=win; brush->clip_set=FALSE; gr_stylespec_init(&brush->current_attr); style->usecount++; if(!grbrush_init(&(brush->grbrush))){ style->usecount--; return FALSE; } ENSURE_INITSPEC(tabframe_spec, "tab-frame"); ENSURE_INITSPEC(tabinfo_spec, "tab-info"); ENSURE_INITSPEC(tabmenuentry_spec, "tab-menuentry"); if(MATCHES(tabframe_spec, spec) || MATCHES(tabinfo_spec, spec)){ brush->extras_fn=debrush_tab_extras; if(!style->tabbrush_data_ok) destyle_create_tab_gcs(style); }else if(MATCHES(tabmenuentry_spec, spec)){ 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 GrStyleSpec *spec, DEStyle *style) { CREATEOBJ_IMPL(DEBrush, debrush, (p, win, spec, style)); } static DEBrush *do_get_brush(Window win, WRootWin *rootwin, const char *stylename, bool slave) { DEStyle *style; DEBrush *brush; GrStyleSpec spec; if(!gr_stylespec_load(&spec, stylename)) return NULL; style=de_get_style(rootwin, &spec); if(style==NULL){ gr_stylespec_unalloc(&spec); return NULL; } brush=create_debrush(win, &spec, style); gr_stylespec_unalloc(&spec); /* 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; gr_stylespec_unalloc(&brush->current_attr); grbrush_deinit(&(brush->grbrush)); } void debrush_release(DEBrush *brush) { destroy_obj((Obj*)brush); } /*}}}*/ /*{{{ Attributes */ void debrush_init_attr(DEBrush *brush, const GrStyleSpec *spec) { gr_stylespec_unalloc(&brush->current_attr); if(spec!=NULL) gr_stylespec_append(&brush->current_attr, spec); } void debrush_set_attr(DEBrush *brush, GrAttr attr) { gr_stylespec_set(&brush->current_attr, attr); } void debrush_unset_attr(DEBrush *brush, GrAttr attr) { gr_stylespec_unset(&brush->current_attr, attr); } GrStyleSpec *debrush_get_current_attr(DEBrush *brush) { return &brush->current_attr; } /*}}}*/ /*{{{ Border widths and extra information */ void debrush_get_border_widths(DEBrush *brush, GrBorderWidths *bdw) { DEStyle *style=brush->d; DEBorder *bd=&(style->border); uint tmp=0; uint tbf=1, lrf=1; uint pad=bd->pad; uint spc=style->spacing; switch(bd->sides){ case DEBORDER_TB: lrf=0; break; case DEBORDER_LR: tbf=0; break; } /* Ridge/groove styles use 'padding' for the spacing between the * 'highlight' and 'shadow' portions of the border, and 'spacing' * between the border and contents. Inlaid style also uses 'spacing' * between the contents and the border, and padding as its outer * component. Elevated style does not use spacing. */ switch(bd->style){ case DEBORDER_RIDGE: case DEBORDER_GROOVE: tmp=bd->sh+bd->hl+pad; bdw->top=tbf*tmp+spc; bdw->bottom=tbf*tmp+spc; bdw->left=lrf*tmp+spc; bdw->right=lrf*tmp+spc; break; case DEBORDER_INLAID: tmp=bd->sh+pad; bdw->top=tbf*tmp+spc; bdw->left=lrf*tmp+spc; tmp=bd->hl+pad; bdw->bottom=tbf*tmp+spc; bdw->right=lrf*tmp+spc; break; case DEBORDER_ELEVATED: default: tmp=bd->hl; bdw->top=tbf*tmp+pad; bdw->left=lrf*tmp+pad; tmp=bd->sh; bdw->bottom=tbf*tmp+pad; bdw->right=lrf*tmp+pad; break; } bdw->right+=brush->indicator_w; bdw->tb_ileft=bdw->left; bdw->tb_iright=bdw->right; bdw->spacing=style->spacing; } 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->extras_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}, {grbrush_init_attr, debrush_init_attr}, {grbrush_set_attr, debrush_set_attr}, {grbrush_unset_attr, debrush_unset_attr}, END_DYNFUNTAB }; IMPLCLASS(DEBrush, GrBrush, debrush_deinit, debrush_dynfuntab); /*}}}*/ notion-3+2012042300/de/brush.h000066400000000000000000000070441174530661200154530ustar00rootroot00000000000000/* * ion/de/brush.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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, const GrBorderWidths *bdw, const GrFontExtents *fnte, const GrStyleSpec *a1, const GrStyleSpec *a2, bool pre, int index); DECLCLASS(DEBrush){ GrBrush grbrush; DEStyle *d; DEBrushExtrasFn *extras_fn; int indicator_w; Window win; bool clip_set; GrStyleSpec current_attr; }; extern DEBrush *de_get_brush(Window win, WRootWin *rootwin, const char *style); extern DEBrush *create_debrush(Window win, const GrStyleSpec *spec, DEStyle *style); extern bool debrush_init(DEBrush *brush, Window win, const GrStyleSpec *spec, 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 GrStyleSpec *a1, const GrStyleSpec *a2); extern DEColourGroup *debrush_get_colour_group(DEBrush *brush, const GrStyleSpec *attr); extern DEColourGroup *debrush_get_current_colour_group(DEBrush *brush); /* Begin/end */ extern void debrush_begin(DEBrush *brush, const WRectangle *geom, int flags); extern void debrush_end(DEBrush *brush); extern void debrush_init_attr(DEBrush *brush, const GrStyleSpec *spec); extern void debrush_set_attr(DEBrush *brush, GrAttr attr); extern void debrush_unset_attr(DEBrush *brush, GrAttr attr); extern GrStyleSpec *debrush_get_current_attr(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); extern void debrush_draw_borderline(DEBrush *brush, const WRectangle *geom, GrBorderLine line); extern void debrush_draw_textbox(DEBrush *brush, const WRectangle *geom, const char *text, bool needfill); extern void debrush_draw_textboxes(DEBrush *brush, const WRectangle *geom, int n, const GrTextElem *elem, bool needfill); 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); extern void debrush_clear_area(DEBrush *brush, const WRectangle *geom); #endif /* ION_DE_BRUSH_H */ notion-3+2012042300/de/colour.c000066400000000000000000000024651174530661200156300ustar00rootroot00000000000000/* * ion/de/colour.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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); gr_stylespec_unalloc(&cg->spec); } void de_free_colour(WRootWin *rootwin, DEColour col) { DEColour pixels[1]; pixels[0]=col; XFreeColors(ioncore_g.dpy, rootwin->default_cmap, pixels, 1, 0); } notion-3+2012042300/de/colour.h000066400000000000000000000016121174530661200156260ustar00rootroot00000000000000/* * ion/de/colour.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #ifndef ION_DE_COLOUR_H #define ION_DE_COLOUR_H #include #include #include INTRSTRUCT(DEColourGroup); typedef unsigned long DEColour; DECLSTRUCT(DEColourGroup){ GrStyleSpec 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 */ notion-3+2012042300/de/draw.c000066400000000000000000000455451174530661200152700ustar00rootroot00000000000000/* * ion/de/draw.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #include #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 GrStyleSpec *a1, const GrStyleSpec *a2) { 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, a1, a2); if(score>maxscore){ maxg=&(style->extra_cgrps[i]); maxscore=score; } } style=style->based_on; } return maxg; } DEColourGroup *debrush_get_colour_group2(DEBrush *brush, const GrStyleSpec *a1, const GrStyleSpec *a2) { return destyle_get_colour_group2(brush->d, a1, a2); } DEColourGroup *debrush_get_colour_group(DEBrush *brush, const GrStyleSpec *attr) { return destyle_get_colour_group2(brush->d, attr, NULL); } DEColourGroup *debrush_get_current_colour_group(DEBrush *brush) { return debrush_get_colour_group(brush, debrush_get_current_attr(brush)); } /*}}}*/ /*{{{ 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; } 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 && tl>0){ XSetForeground(ioncore_g.dpy, gc, tlc); XSetBackground(ioncore_g.dpy, gc, tlc); XFillRectangle(ioncore_g.dpy, win, gc, geom->x, geom->y, tl, geom->h); geom->x+=tl; }else if(line==GR_BORDERLINE_TOP && geom->w>0 && tl>0){ XSetForeground(ioncore_g.dpy, gc, tlc); XSetBackground(ioncore_g.dpy, gc, tlc); XFillRectangle(ioncore_g.dpy, win, gc, geom->x, geom->y, geom->w, tl); geom->y+=tl; }else if(line==GR_BORDERLINE_RIGHT && geom->h>0 && br>0){ XSetForeground(ioncore_g.dpy, gc, brc); XSetBackground(ioncore_g.dpy, gc, brc); XFillRectangle(ioncore_g.dpy, win, gc, geom->x+geom->w-br, geom->y, br, geom->h); geom->w-=br; }else if(line==GR_BORDERLINE_BOTTOM && geom->w>0 && br>0){ XSetForeground(ioncore_g.dpy, gc, brc); XSetBackground(ioncore_g.dpy, gc, brc); XFillRectangle(ioncore_g.dpy, win, gc, geom->x, geom->y+geom->h-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_do_draw_padline(DEBrush *brush, WRectangle geom, DEColourGroup *cg, GrBorderLine line) { DEBorder *bd=&(brush->d->border); GC gc=brush->d->normal_gc; Window win=brush->win; draw_borderline(win, gc, &geom, bd->pad, bd->pad, cg->pad, cg->pad, line); } void debrush_draw_borderline(DEBrush *brush, const WRectangle *geom, GrBorderLine line) { DEColourGroup *cg=debrush_get_current_colour_group(brush); if(cg!=NULL) debrush_do_draw_borderline(brush, *geom, cg, line); } static void debrush_do_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_do_draw_border(DEBrush *brush, WRectangle geom, DEColourGroup *cg) { DEBorder *bd=&(brush->d->border); switch(bd->sides){ case DEBORDER_ALL: debrush_do_do_draw_border(brush, geom, cg); break; case DEBORDER_TB: debrush_do_draw_padline(brush, geom, cg, GR_BORDERLINE_LEFT); debrush_do_draw_padline(brush, geom, cg, GR_BORDERLINE_RIGHT); debrush_do_draw_borderline(brush, geom, cg, GR_BORDERLINE_TOP); debrush_do_draw_borderline(brush, geom, cg, GR_BORDERLINE_BOTTOM); break; case DEBORDER_LR: debrush_do_draw_padline(brush, geom, cg, GR_BORDERLINE_TOP); debrush_do_draw_padline(brush, geom, cg, GR_BORDERLINE_BOTTOM); debrush_do_draw_borderline(brush, geom, cg, GR_BORDERLINE_LEFT); debrush_do_draw_borderline(brush, geom, cg, GR_BORDERLINE_RIGHT); break; } } void debrush_draw_border(DEBrush *brush, const WRectangle *geom) { DEColourGroup *cg=debrush_get_current_colour_group(brush); if(cg!=NULL) debrush_do_draw_border(brush, *geom, cg); } /*}}}*/ /*{{{ 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); } #define ISSET(S, A) ((S)!=NULL && gr_stylespec_isset(S, A)) GR_DEFATTR(dragged); GR_DEFATTR(tagged); GR_DEFATTR(submenu); GR_DEFATTR(numbered); GR_DEFATTR(tabnumber); static void ensure_attrs() { GR_ALLOCATTR_BEGIN; GR_ALLOCATTR(dragged); GR_ALLOCATTR(tagged); GR_ALLOCATTR(submenu); GR_ALLOCATTR(numbered); GR_ALLOCATTR(tabnumber); GR_ALLOCATTR_END; } static int get_ty(const WRectangle *g, const GrBorderWidths *bdw, const GrFontExtents *fnte) { return (g->y+bdw->top+fnte->baseline +(g->h-bdw->top-bdw->bottom-fnte->max_height)/2); } void debrush_tab_extras(DEBrush *brush, const WRectangle *g, DEColourGroup *cg, const GrBorderWidths *bdw, const GrFontExtents *fnte, const GrStyleSpec *a1, const GrStyleSpec *a2, bool pre, int index) { 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; ensure_attrs(); if(pre){ if(ISSET(a2, GR_ATTR(dragged)) || ISSET(a1, GR_ATTR(dragged))){ 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((ISSET(a1, GR_ATTR(numbered)) || ISSET(a2, GR_ATTR(numbered))) && index>=0){ DEColourGroup *cg; GrStyleSpec tmp; gr_stylespec_init(&tmp); gr_stylespec_append(&tmp, a2); gr_stylespec_set(&tmp, GR_ATTR(tabnumber)); cg=debrush_get_colour_group2(brush, a1, &tmp); gr_stylespec_unalloc(&tmp); if(cg!=NULL){ char *s=NULL; libtu_asprintf(&s, "[%d]", index+1); if(s!=NULL){ int l=strlen(s); uint w=debrush_get_text_width(brush, s, l); if(w < g->w-bdw->right-bdw->left){ int ty=get_ty(g, bdw, fnte); int tx=(d->textalign==DEALIGN_RIGHT ? g->x+bdw->left : g->x+g->w-bdw->right-w); debrush_do_draw_string(brush, tx, ty, s, l, TRUE, cg); } free(s); } } } if(ISSET(a2, GR_ATTR(tagged)) || ISSET(a1, GR_ATTR(tagged))){ 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, const GrBorderWidths *bdw, const GrFontExtents *fnte, const GrStyleSpec *a1, const GrStyleSpec *a2, bool pre, int index) { int tx, ty; if(pre) return; ensure_attrs(); if(ISSET(a2, GR_ATTR(submenu)) || ISSET(a1, GR_ATTR(submenu))){ ty=get_ty(g, bdw, fnte); 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 GrStyleSpec *a1, const GrStyleSpec *a2, int index) { 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, index); 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=get_ty(geom, &bdw, &fnte); 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, index); } void debrush_draw_textbox(DEBrush *brush, const WRectangle *geom, const char *text, bool needfill) { GrStyleSpec *attr=debrush_get_current_attr(brush); DEColourGroup *cg=debrush_get_colour_group(brush, attr); if(cg!=NULL){ debrush_do_draw_textbox(brush, geom, text, cg, needfill, attr, NULL, -1); } } void debrush_draw_textboxes(DEBrush *brush, const WRectangle *geom, int n, const GrTextElem *elem, bool needfill) { GrStyleSpec *common_attrib; WRectangle g=*geom; DEColourGroup *cg; GrBorderWidths bdw; int i; common_attrib=debrush_get_current_attr(brush); grbrush_get_border_widths(&(brush->grbrush), &bdw); for(i=0; ; i++){ g.w=bdw.left+elem[i].iw+bdw.right; cg=debrush_get_colour_group2(brush, common_attrib, &elem[i].attr); if(cg!=NULL){ debrush_do_draw_textbox(brush, &g, elem[i].text, cg, needfill, common_attrib, &elem[i].attr, i); } if(i==n-1) break; g.x+=g.w; if(bdw.spacing>0 && 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(!ioncore_g.shape_extension) return; 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) { DEColourGroup *cg=debrush_get_current_colour_group(brush); 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_KEEP_ATTR)) debrush_init_attr(brush, NULL); 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); } /*}}}*/ notion-3+2012042300/de/font.c000066400000000000000000000224431174530661200152710ustar00rootroot00000000000000/* * ion/de/font.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #include #include #include #include "font.h" #include "fontset.h" #include "brush.h" #include "precompose.h" /*{{{ UTF-8 processing */ #define UTF_DATA 0x3F #define UTF_2_DATA 0x1F #define UTF_3_DATA 0x0F #define UTF_1 0x80 #define UTF_2 0xC0 #define UTF_3 0xE0 #define UTF_4 0xF0 #define UTF_5 0xF8 #define UTF_6 0xFC static void toucs(const char *str_, int len, XChar2b **str16, int *len16) { int i=0; const uchar *str=(const uchar*)str_; wchar_t prev=0; *str16=ALLOC_N(XChar2b, len); *len16=0; while(i=len) break; ch=((str[i] & UTF_3_DATA) << 12) | ((str[i+1] & UTF_DATA) << 6) | (str[i+2] & UTF_DATA); i+=3; }else if((str[i] & UTF_2) == UTF_2){ if(i+1>=len) break; ch = ((str[i] & UTF_2_DATA) << 6) | (str[i+1] & UTF_DATA); i+=2; }else if(str[i] < UTF_1){ ch = str[i]; i++; }else{ ch='?'; i++; } if(*len16>0){ wchar_t precomp=do_precomposition(prev, ch); if(precomp!=-1){ (*len16)--; ch=precomp; } } (*str16)[*len16].byte2=ch&0xff; (*str16)[*len16].byte1=(ch>>8)&0xff; (*len16)++; prev=ch; } } /*}}}*/ /*{{{ Load/free */ static DEFont *fonts=NULL; static bool iso10646_font(const char *fontname) { const char *iso; if(strchr(fontname, ',')!=NULL) return FALSE; /* fontset */ iso=strstr(fontname, "iso10646-1"); return (iso!=NULL && iso[10]=='\0'); } 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 && !(ioncore_g.enc_utf8 && iso10646_font(fontname))){ 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){ if(ioncore_g.enc_utf8){ XChar2b *str16; int len16=0; uint res; toucs(text, len, &str16, &len16); res=XTextWidth16(font->fontstruct, str16, len16); free(str16); return res; }else{ 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){ if(ioncore_g.enc_utf8){ XChar2b *str16; int len16=0; toucs(str, len, &str16, &len16); XDrawString16(ioncore_g.dpy, brush->win, gc, x, y, str16, len16); free(str16); }else{ 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){ if(ioncore_g.enc_utf8){ XChar2b *str16; int len16=0; toucs(str, len, &str16, &len16); XDrawImageString16(ioncore_g.dpy, brush->win, gc, x, y, str16, len16); free(str16); }else{ 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) { DEColourGroup *cg=debrush_get_current_colour_group(brush); if(cg!=NULL) debrush_do_draw_string(brush, x, y, str, len, needfill, cg); } /*}}}*/ notion-3+2012042300/de/font.h000066400000000000000000000032651174530661200152770ustar00rootroot00000000000000/* * ion/de/font.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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); 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 */ notion-3+2012042300/de/fontset.c000066400000000000000000000137531174530661200160110ustar00rootroot00000000000000/* * 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 * . * * However, the code that this file is based on, was taken from: * * Screen.cc for Blackbox - an X11 Window manager * Copyright (c) 2001 - 2002 Sean 'Shaleh' Perry * Copyright (c) 1997 - 2000 Brad Hughes (bhughes@tcac.net) * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #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; } notion-3+2012042300/de/fontset.h000066400000000000000000000004751174530661200160130ustar00rootroot00000000000000/* * ion/de/fontset.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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 */ notion-3+2012042300/de/init.c000066400000000000000000000273041174530661200152670ustar00rootroot00000000000000/* * ion/de/init.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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" /*{{{ Style specifications */ static bool get_spec(ExtlTab tab, const char *name, GrStyleSpec *spec, char **pat_ret) { char *str; bool res; if(!extl_table_gets_s(tab, name, &str)) return FALSE; res=gr_stylespec_load(spec, str); if(pat_ret==NULL) free(str); else *pat_ret=str; return res; } /*}}}*/ /*{{{ 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_sides(uint *ret, ExtlTab tab) { char *style=NULL; if(!extl_table_gets_s(tab, "border_sides", &style)) return; if(strcmp(style, "all")==0) *ret=DEBORDER_ALL; else if(strcmp(style, "tb")==0) *ret=DEBORDER_TB; else if(strcmp(style, "lr")==0) *ret=DEBORDER_LR; else warn(TR("Unknown border side configuration \"%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); de_get_border_sides(&(border->sides), tab); } /*}}}*/ /*{{{ Colours */ static bool de_get_colour_(WRootWin *rootwin, DEColour *ret, ExtlTab tab, const char *what, DEColour substitute, DEColour inherit) { char *name=NULL; bool set=FALSE; if(extl_table_gets_s(tab, what, &name)){ if(strcmp(name, "inherit")==0){ set=de_duplicate_colour(rootwin, inherit, ret); }else{ set=de_alloc_colour(rootwin, ret, name); if(!set) warn(TR("Unable to allocate colour \"%s\"."), name); } free(name); } if(!set) de_duplicate_colour(rootwin, substitute, ret); return set; } static bool de_get_colour(WRootWin *rootwin, DEColour *ret, ExtlTab tab, const char *what, DEColour substitute) { return de_get_colour_(rootwin, ret, tab, what, substitute, substitute); } void de_get_colour_group(WRootWin *rootwin, DEColourGroup *cg, ExtlTab tab, DEStyle *based_on) { bool bgset; DEColour padinh; de_get_colour(rootwin, &(cg->hl), tab, "highlight_colour", (based_on ? based_on->cgrp.hl : DE_WHITE(rootwin))); de_get_colour(rootwin, &(cg->sh), tab, "shadow_colour", (based_on ? based_on->cgrp.sh : DE_WHITE(rootwin))); de_get_colour(rootwin, &(cg->fg), tab, "foreground_colour", (based_on ? based_on->cgrp.fg : DE_WHITE(rootwin))); bgset=de_get_colour(rootwin, &(cg->bg), tab, "background_colour", (based_on ? based_on->cgrp.bg : DE_BLACK(rootwin))); padinh=(based_on ? based_on->cgrp.pad : DE_WHITE(rootwin)); de_get_colour_(rootwin, &(cg->pad), tab, "padding_colour", (bgset ? cg->bg : padinh), padinh); } 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].spec=spec; 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; } /*}}}*/ /*{{{ Extras filter/copy */ static const char * const known_values[]={ "based_on", "font", "shadow_pixels", "highlight_pixels", "padding_pixels", "border_style", "border_sides", "spacing", "foreground_colour", "background_colour", "shadow_colour", "highlight_colour", "padding_colour", "text_align", NULL }; static bool filter_extras_iter_fn(ExtlAny k, ExtlAny v, void *p) { ExtlTab *tgt=(ExtlTab*)p; const char *s; int i; if(k.type!='s' && k.type!='S') return TRUE; for(i=0; known_values[i]; i++){ if(strcmp(known_values[i], k.value.s)==0) return TRUE; } if(*tgt==extl_table_none()) *tgt=extl_create_table(); extl_table_set(*tgt, 'a', 'a', k, v); return TRUE; } static void filter_extras(ExtlTab *tgt, ExtlTab src) { /* Copy any unknown string-keyed values from src to tgt, * possibly creating tgt. */ extl_table_iter(src, filter_extras_iter_fn, tgt); } /*}}}*/ /*{{{ de_defstyle */ void de_get_nonfont(WRootWin *rootwin, DEStyle *style, ExtlTab tab) { DEStyle *based_on=style->based_on; 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, *based_on=NULL; int based_on_score=-1; char *fnt, *bss; uint n; if(name==NULL) return FALSE; style=de_create_style(rootwin, name); if(style==NULL) return FALSE; if(extl_table_gets_s(tab, "based_on", &bss)){ GrStyleSpec bs; gr_stylespec_load(&bs, bss); based_on=de_get_style(rootwin, &bs); gr_stylespec_unalloc(&bs); free(bss); }else{ based_on=de_get_style(rootwin, &style->spec); } if(based_on!=NULL){ style->based_on=based_on; based_on->usecount++; } 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); if(based_on!=NULL && gr_stylespec_equals(&based_on->spec, &style->spec)){ /* The new style replaces based_on, so it may be dumped. */ if(!based_on->is_fallback) destyle_dump(based_on); if(based_on->usecount==1){ uint nb=based_on->n_extra_cgrps; uint ns=style->n_extra_cgrps; /* Nothing else is using based_on: optimise and move * extra colour groups here, so that based_on can be freed. */ if(nb>0){ DEColourGroup *cgs=ALLOC_N(DEColourGroup, nb+ns); if(cgs!=NULL){ memcpy(cgs, based_on->extra_cgrps, sizeof(DEColourGroup)*nb); memcpy(cgs+nb, style->extra_cgrps, sizeof(DEColourGroup)*ns); free(style->extra_cgrps); style->extra_cgrps=cgs; style->n_extra_cgrps=nb+ns; free(based_on->extra_cgrps); based_on->extra_cgrps=NULL; based_on->n_extra_cgrps=0; } } /* style->extras_table should be none still */ style->extras_table=based_on->extras_table; based_on->extras_table=extl_table_none(); style->based_on=based_on->based_on; based_on->based_on=NULL; destyle_unref(based_on); } } filter_extras(&style->extras_table, tab); destyle_add(style); 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[]=NOTION_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(); } /*}}}*/ notion-3+2012042300/de/init.h000066400000000000000000000020651174530661200152710ustar00rootroot00000000000000/* * ion/de/init.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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 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 */ notion-3+2012042300/de/precompose.c000066400000000000000000000654551174530661200165110ustar00rootroot00000000000000/* * Canonical Compositions * * DO NOT EDIT BY HAND! This is generated by the script * unicode/make-precompose.sh */ #include "precompose.h" struct { int replacement; int base; int comb; } precompositions[] = { { 0x226E, 0x003C, 0x0338}, { 0x2260, 0x003D, 0x0338}, { 0x226F, 0x003E, 0x0338}, { 0x00C0, 0x0041, 0x0300}, { 0x00C1, 0x0041, 0x0301}, { 0x00C2, 0x0041, 0x0302}, { 0x00C3, 0x0041, 0x0303}, { 0x0100, 0x0041, 0x0304}, { 0x0102, 0x0041, 0x0306}, { 0x0226, 0x0041, 0x0307}, { 0x00C4, 0x0041, 0x0308}, { 0x1EA2, 0x0041, 0x0309}, { 0x00C5, 0x0041, 0x030A}, { 0x01CD, 0x0041, 0x030C}, { 0x0200, 0x0041, 0x030F}, { 0x0202, 0x0041, 0x0311}, { 0x1EA0, 0x0041, 0x0323}, { 0x1E00, 0x0041, 0x0325}, { 0x0104, 0x0041, 0x0328}, { 0x1E02, 0x0042, 0x0307}, { 0x1E04, 0x0042, 0x0323}, { 0x1E06, 0x0042, 0x0331}, { 0x0106, 0x0043, 0x0301}, { 0x0108, 0x0043, 0x0302}, { 0x010A, 0x0043, 0x0307}, { 0x010C, 0x0043, 0x030C}, { 0x00C7, 0x0043, 0x0327}, { 0x1E0A, 0x0044, 0x0307}, { 0x010E, 0x0044, 0x030C}, { 0x1E0C, 0x0044, 0x0323}, { 0x1E10, 0x0044, 0x0327}, { 0x1E12, 0x0044, 0x032D}, { 0x1E0E, 0x0044, 0x0331}, { 0x00C8, 0x0045, 0x0300}, { 0x00C9, 0x0045, 0x0301}, { 0x00CA, 0x0045, 0x0302}, { 0x1EBC, 0x0045, 0x0303}, { 0x0112, 0x0045, 0x0304}, { 0x0114, 0x0045, 0x0306}, { 0x0116, 0x0045, 0x0307}, { 0x00CB, 0x0045, 0x0308}, { 0x1EBA, 0x0045, 0x0309}, { 0x011A, 0x0045, 0x030C}, { 0x0204, 0x0045, 0x030F}, { 0x0206, 0x0045, 0x0311}, { 0x1EB8, 0x0045, 0x0323}, { 0x0228, 0x0045, 0x0327}, { 0x0118, 0x0045, 0x0328}, { 0x1E18, 0x0045, 0x032D}, { 0x1E1A, 0x0045, 0x0330}, { 0x1E1E, 0x0046, 0x0307}, { 0x01F4, 0x0047, 0x0301}, { 0x011C, 0x0047, 0x0302}, { 0x1E20, 0x0047, 0x0304}, { 0x011E, 0x0047, 0x0306}, { 0x0120, 0x0047, 0x0307}, { 0x01E6, 0x0047, 0x030C}, { 0x0122, 0x0047, 0x0327}, { 0x0124, 0x0048, 0x0302}, { 0x1E22, 0x0048, 0x0307}, { 0x1E26, 0x0048, 0x0308}, { 0x021E, 0x0048, 0x030C}, { 0x1E24, 0x0048, 0x0323}, { 0x1E28, 0x0048, 0x0327}, { 0x1E2A, 0x0048, 0x032E}, { 0x00CC, 0x0049, 0x0300}, { 0x00CD, 0x0049, 0x0301}, { 0x00CE, 0x0049, 0x0302}, { 0x0128, 0x0049, 0x0303}, { 0x012A, 0x0049, 0x0304}, { 0x012C, 0x0049, 0x0306}, { 0x0130, 0x0049, 0x0307}, { 0x00CF, 0x0049, 0x0308}, { 0x1EC8, 0x0049, 0x0309}, { 0x01CF, 0x0049, 0x030C}, { 0x0208, 0x0049, 0x030F}, { 0x020A, 0x0049, 0x0311}, { 0x1ECA, 0x0049, 0x0323}, { 0x012E, 0x0049, 0x0328}, { 0x1E2C, 0x0049, 0x0330}, { 0x0134, 0x004A, 0x0302}, { 0x1E30, 0x004B, 0x0301}, { 0x01E8, 0x004B, 0x030C}, { 0x1E32, 0x004B, 0x0323}, { 0x0136, 0x004B, 0x0327}, { 0x1E34, 0x004B, 0x0331}, { 0x0139, 0x004C, 0x0301}, { 0x013D, 0x004C, 0x030C}, { 0x1E36, 0x004C, 0x0323}, { 0x013B, 0x004C, 0x0327}, { 0x1E3C, 0x004C, 0x032D}, { 0x1E3A, 0x004C, 0x0331}, { 0x1E3E, 0x004D, 0x0301}, { 0x1E40, 0x004D, 0x0307}, { 0x1E42, 0x004D, 0x0323}, { 0x01F8, 0x004E, 0x0300}, { 0x0143, 0x004E, 0x0301}, { 0x00D1, 0x004E, 0x0303}, { 0x1E44, 0x004E, 0x0307}, { 0x0147, 0x004E, 0x030C}, { 0x1E46, 0x004E, 0x0323}, { 0x0145, 0x004E, 0x0327}, { 0x1E4A, 0x004E, 0x032D}, { 0x1E48, 0x004E, 0x0331}, { 0x00D2, 0x004F, 0x0300}, { 0x00D3, 0x004F, 0x0301}, { 0x00D4, 0x004F, 0x0302}, { 0x00D5, 0x004F, 0x0303}, { 0x014C, 0x004F, 0x0304}, { 0x014E, 0x004F, 0x0306}, { 0x022E, 0x004F, 0x0307}, { 0x00D6, 0x004F, 0x0308}, { 0x1ECE, 0x004F, 0x0309}, { 0x0150, 0x004F, 0x030B}, { 0x01D1, 0x004F, 0x030C}, { 0x020C, 0x004F, 0x030F}, { 0x020E, 0x004F, 0x0311}, { 0x01A0, 0x004F, 0x031B}, { 0x1ECC, 0x004F, 0x0323}, { 0x01EA, 0x004F, 0x0328}, { 0x1E54, 0x0050, 0x0301}, { 0x1E56, 0x0050, 0x0307}, { 0x0154, 0x0052, 0x0301}, { 0x1E58, 0x0052, 0x0307}, { 0x0158, 0x0052, 0x030C}, { 0x0210, 0x0052, 0x030F}, { 0x0212, 0x0052, 0x0311}, { 0x1E5A, 0x0052, 0x0323}, { 0x0156, 0x0052, 0x0327}, { 0x1E5E, 0x0052, 0x0331}, { 0x015A, 0x0053, 0x0301}, { 0x015C, 0x0053, 0x0302}, { 0x1E60, 0x0053, 0x0307}, { 0x0160, 0x0053, 0x030C}, { 0x1E62, 0x0053, 0x0323}, { 0x0218, 0x0053, 0x0326}, { 0x015E, 0x0053, 0x0327}, { 0x1E6A, 0x0054, 0x0307}, { 0x0164, 0x0054, 0x030C}, { 0x1E6C, 0x0054, 0x0323}, { 0x021A, 0x0054, 0x0326}, { 0x0162, 0x0054, 0x0327}, { 0x1E70, 0x0054, 0x032D}, { 0x1E6E, 0x0054, 0x0331}, { 0x00D9, 0x0055, 0x0300}, { 0x00DA, 0x0055, 0x0301}, { 0x00DB, 0x0055, 0x0302}, { 0x0168, 0x0055, 0x0303}, { 0x016A, 0x0055, 0x0304}, { 0x016C, 0x0055, 0x0306}, { 0x00DC, 0x0055, 0x0308}, { 0x1EE6, 0x0055, 0x0309}, { 0x016E, 0x0055, 0x030A}, { 0x0170, 0x0055, 0x030B}, { 0x01D3, 0x0055, 0x030C}, { 0x0214, 0x0055, 0x030F}, { 0x0216, 0x0055, 0x0311}, { 0x01AF, 0x0055, 0x031B}, { 0x1EE4, 0x0055, 0x0323}, { 0x1E72, 0x0055, 0x0324}, { 0x0172, 0x0055, 0x0328}, { 0x1E76, 0x0055, 0x032D}, { 0x1E74, 0x0055, 0x0330}, { 0x1E7C, 0x0056, 0x0303}, { 0x1E7E, 0x0056, 0x0323}, { 0x1E80, 0x0057, 0x0300}, { 0x1E82, 0x0057, 0x0301}, { 0x0174, 0x0057, 0x0302}, { 0x1E86, 0x0057, 0x0307}, { 0x1E84, 0x0057, 0x0308}, { 0x1E88, 0x0057, 0x0323}, { 0x1E8A, 0x0058, 0x0307}, { 0x1E8C, 0x0058, 0x0308}, { 0x1EF2, 0x0059, 0x0300}, { 0x00DD, 0x0059, 0x0301}, { 0x0176, 0x0059, 0x0302}, { 0x1EF8, 0x0059, 0x0303}, { 0x0232, 0x0059, 0x0304}, { 0x1E8E, 0x0059, 0x0307}, { 0x0178, 0x0059, 0x0308}, { 0x1EF6, 0x0059, 0x0309}, { 0x1EF4, 0x0059, 0x0323}, { 0x0179, 0x005A, 0x0301}, { 0x1E90, 0x005A, 0x0302}, { 0x017B, 0x005A, 0x0307}, { 0x017D, 0x005A, 0x030C}, { 0x1E92, 0x005A, 0x0323}, { 0x1E94, 0x005A, 0x0331}, { 0x00E0, 0x0061, 0x0300}, { 0x00E1, 0x0061, 0x0301}, { 0x00E2, 0x0061, 0x0302}, { 0x00E3, 0x0061, 0x0303}, { 0x0101, 0x0061, 0x0304}, { 0x0103, 0x0061, 0x0306}, { 0x0227, 0x0061, 0x0307}, { 0x00E4, 0x0061, 0x0308}, { 0x1EA3, 0x0061, 0x0309}, { 0x00E5, 0x0061, 0x030A}, { 0x01CE, 0x0061, 0x030C}, { 0x0201, 0x0061, 0x030F}, { 0x0203, 0x0061, 0x0311}, { 0x1EA1, 0x0061, 0x0323}, { 0x1E01, 0x0061, 0x0325}, { 0x0105, 0x0061, 0x0328}, { 0x1E03, 0x0062, 0x0307}, { 0x1E05, 0x0062, 0x0323}, { 0x1E07, 0x0062, 0x0331}, { 0x0107, 0x0063, 0x0301}, { 0x0109, 0x0063, 0x0302}, { 0x010B, 0x0063, 0x0307}, { 0x010D, 0x0063, 0x030C}, { 0x00E7, 0x0063, 0x0327}, { 0x1E0B, 0x0064, 0x0307}, { 0x010F, 0x0064, 0x030C}, { 0x1E0D, 0x0064, 0x0323}, { 0x1E11, 0x0064, 0x0327}, { 0x1E13, 0x0064, 0x032D}, { 0x1E0F, 0x0064, 0x0331}, { 0x00E8, 0x0065, 0x0300}, { 0x00E9, 0x0065, 0x0301}, { 0x00EA, 0x0065, 0x0302}, { 0x1EBD, 0x0065, 0x0303}, { 0x0113, 0x0065, 0x0304}, { 0x0115, 0x0065, 0x0306}, { 0x0117, 0x0065, 0x0307}, { 0x00EB, 0x0065, 0x0308}, { 0x1EBB, 0x0065, 0x0309}, { 0x011B, 0x0065, 0x030C}, { 0x0205, 0x0065, 0x030F}, { 0x0207, 0x0065, 0x0311}, { 0x1EB9, 0x0065, 0x0323}, { 0x0229, 0x0065, 0x0327}, { 0x0119, 0x0065, 0x0328}, { 0x1E19, 0x0065, 0x032D}, { 0x1E1B, 0x0065, 0x0330}, { 0x1E1F, 0x0066, 0x0307}, { 0x01F5, 0x0067, 0x0301}, { 0x011D, 0x0067, 0x0302}, { 0x1E21, 0x0067, 0x0304}, { 0x011F, 0x0067, 0x0306}, { 0x0121, 0x0067, 0x0307}, { 0x01E7, 0x0067, 0x030C}, { 0x0123, 0x0067, 0x0327}, { 0x0125, 0x0068, 0x0302}, { 0x1E23, 0x0068, 0x0307}, { 0x1E27, 0x0068, 0x0308}, { 0x021F, 0x0068, 0x030C}, { 0x1E25, 0x0068, 0x0323}, { 0x1E29, 0x0068, 0x0327}, { 0x1E2B, 0x0068, 0x032E}, { 0x1E96, 0x0068, 0x0331}, { 0x00EC, 0x0069, 0x0300}, { 0x00ED, 0x0069, 0x0301}, { 0x00EE, 0x0069, 0x0302}, { 0x0129, 0x0069, 0x0303}, { 0x012B, 0x0069, 0x0304}, { 0x012D, 0x0069, 0x0306}, { 0x00EF, 0x0069, 0x0308}, { 0x1EC9, 0x0069, 0x0309}, { 0x01D0, 0x0069, 0x030C}, { 0x0209, 0x0069, 0x030F}, { 0x020B, 0x0069, 0x0311}, { 0x1ECB, 0x0069, 0x0323}, { 0x012F, 0x0069, 0x0328}, { 0x1E2D, 0x0069, 0x0330}, { 0x0135, 0x006A, 0x0302}, { 0x01F0, 0x006A, 0x030C}, { 0x1E31, 0x006B, 0x0301}, { 0x01E9, 0x006B, 0x030C}, { 0x1E33, 0x006B, 0x0323}, { 0x0137, 0x006B, 0x0327}, { 0x1E35, 0x006B, 0x0331}, { 0x013A, 0x006C, 0x0301}, { 0x013E, 0x006C, 0x030C}, { 0x1E37, 0x006C, 0x0323}, { 0x013C, 0x006C, 0x0327}, { 0x1E3D, 0x006C, 0x032D}, { 0x1E3B, 0x006C, 0x0331}, { 0x1E3F, 0x006D, 0x0301}, { 0x1E41, 0x006D, 0x0307}, { 0x1E43, 0x006D, 0x0323}, { 0x01F9, 0x006E, 0x0300}, { 0x0144, 0x006E, 0x0301}, { 0x00F1, 0x006E, 0x0303}, { 0x1E45, 0x006E, 0x0307}, { 0x0148, 0x006E, 0x030C}, { 0x1E47, 0x006E, 0x0323}, { 0x0146, 0x006E, 0x0327}, { 0x1E4B, 0x006E, 0x032D}, { 0x1E49, 0x006E, 0x0331}, { 0x00F2, 0x006F, 0x0300}, { 0x00F3, 0x006F, 0x0301}, { 0x00F4, 0x006F, 0x0302}, { 0x00F5, 0x006F, 0x0303}, { 0x014D, 0x006F, 0x0304}, { 0x014F, 0x006F, 0x0306}, { 0x022F, 0x006F, 0x0307}, { 0x00F6, 0x006F, 0x0308}, { 0x1ECF, 0x006F, 0x0309}, { 0x0151, 0x006F, 0x030B}, { 0x01D2, 0x006F, 0x030C}, { 0x020D, 0x006F, 0x030F}, { 0x020F, 0x006F, 0x0311}, { 0x01A1, 0x006F, 0x031B}, { 0x1ECD, 0x006F, 0x0323}, { 0x01EB, 0x006F, 0x0328}, { 0x1E55, 0x0070, 0x0301}, { 0x1E57, 0x0070, 0x0307}, { 0x0155, 0x0072, 0x0301}, { 0x1E59, 0x0072, 0x0307}, { 0x0159, 0x0072, 0x030C}, { 0x0211, 0x0072, 0x030F}, { 0x0213, 0x0072, 0x0311}, { 0x1E5B, 0x0072, 0x0323}, { 0x0157, 0x0072, 0x0327}, { 0x1E5F, 0x0072, 0x0331}, { 0x015B, 0x0073, 0x0301}, { 0x015D, 0x0073, 0x0302}, { 0x1E61, 0x0073, 0x0307}, { 0x0161, 0x0073, 0x030C}, { 0x1E63, 0x0073, 0x0323}, { 0x0219, 0x0073, 0x0326}, { 0x015F, 0x0073, 0x0327}, { 0x1E6B, 0x0074, 0x0307}, { 0x1E97, 0x0074, 0x0308}, { 0x0165, 0x0074, 0x030C}, { 0x1E6D, 0x0074, 0x0323}, { 0x021B, 0x0074, 0x0326}, { 0x0163, 0x0074, 0x0327}, { 0x1E71, 0x0074, 0x032D}, { 0x1E6F, 0x0074, 0x0331}, { 0x00F9, 0x0075, 0x0300}, { 0x00FA, 0x0075, 0x0301}, { 0x00FB, 0x0075, 0x0302}, { 0x0169, 0x0075, 0x0303}, { 0x016B, 0x0075, 0x0304}, { 0x016D, 0x0075, 0x0306}, { 0x00FC, 0x0075, 0x0308}, { 0x1EE7, 0x0075, 0x0309}, { 0x016F, 0x0075, 0x030A}, { 0x0171, 0x0075, 0x030B}, { 0x01D4, 0x0075, 0x030C}, { 0x0215, 0x0075, 0x030F}, { 0x0217, 0x0075, 0x0311}, { 0x01B0, 0x0075, 0x031B}, { 0x1EE5, 0x0075, 0x0323}, { 0x1E73, 0x0075, 0x0324}, { 0x0173, 0x0075, 0x0328}, { 0x1E77, 0x0075, 0x032D}, { 0x1E75, 0x0075, 0x0330}, { 0x1E7D, 0x0076, 0x0303}, { 0x1E7F, 0x0076, 0x0323}, { 0x1E81, 0x0077, 0x0300}, { 0x1E83, 0x0077, 0x0301}, { 0x0175, 0x0077, 0x0302}, { 0x1E87, 0x0077, 0x0307}, { 0x1E85, 0x0077, 0x0308}, { 0x1E98, 0x0077, 0x030A}, { 0x1E89, 0x0077, 0x0323}, { 0x1E8B, 0x0078, 0x0307}, { 0x1E8D, 0x0078, 0x0308}, { 0x1EF3, 0x0079, 0x0300}, { 0x00FD, 0x0079, 0x0301}, { 0x0177, 0x0079, 0x0302}, { 0x1EF9, 0x0079, 0x0303}, { 0x0233, 0x0079, 0x0304}, { 0x1E8F, 0x0079, 0x0307}, { 0x00FF, 0x0079, 0x0308}, { 0x1EF7, 0x0079, 0x0309}, { 0x1E99, 0x0079, 0x030A}, { 0x1EF5, 0x0079, 0x0323}, { 0x017A, 0x007A, 0x0301}, { 0x1E91, 0x007A, 0x0302}, { 0x017C, 0x007A, 0x0307}, { 0x017E, 0x007A, 0x030C}, { 0x1E93, 0x007A, 0x0323}, { 0x1E95, 0x007A, 0x0331}, { 0x1FED, 0x00A8, 0x0300}, { 0x0385, 0x00A8, 0x0301}, { 0x1FC1, 0x00A8, 0x0342}, { 0x1EA6, 0x00C2, 0x0300}, { 0x1EA4, 0x00C2, 0x0301}, { 0x1EAA, 0x00C2, 0x0303}, { 0x1EA8, 0x00C2, 0x0309}, { 0x01DE, 0x00C4, 0x0304}, { 0x01FA, 0x00C5, 0x0301}, { 0x01FC, 0x00C6, 0x0301}, { 0x01E2, 0x00C6, 0x0304}, { 0x1E08, 0x00C7, 0x0301}, { 0x1EC0, 0x00CA, 0x0300}, { 0x1EBE, 0x00CA, 0x0301}, { 0x1EC4, 0x00CA, 0x0303}, { 0x1EC2, 0x00CA, 0x0309}, { 0x1E2E, 0x00CF, 0x0301}, { 0x1ED2, 0x00D4, 0x0300}, { 0x1ED0, 0x00D4, 0x0301}, { 0x1ED6, 0x00D4, 0x0303}, { 0x1ED4, 0x00D4, 0x0309}, { 0x1E4C, 0x00D5, 0x0301}, { 0x022C, 0x00D5, 0x0304}, { 0x1E4E, 0x00D5, 0x0308}, { 0x022A, 0x00D6, 0x0304}, { 0x01FE, 0x00D8, 0x0301}, { 0x01DB, 0x00DC, 0x0300}, { 0x01D7, 0x00DC, 0x0301}, { 0x01D5, 0x00DC, 0x0304}, { 0x01D9, 0x00DC, 0x030C}, { 0x1EA7, 0x00E2, 0x0300}, { 0x1EA5, 0x00E2, 0x0301}, { 0x1EAB, 0x00E2, 0x0303}, { 0x1EA9, 0x00E2, 0x0309}, { 0x01DF, 0x00E4, 0x0304}, { 0x01FB, 0x00E5, 0x0301}, { 0x01FD, 0x00E6, 0x0301}, { 0x01E3, 0x00E6, 0x0304}, { 0x1E09, 0x00E7, 0x0301}, { 0x1EC1, 0x00EA, 0x0300}, { 0x1EBF, 0x00EA, 0x0301}, { 0x1EC5, 0x00EA, 0x0303}, { 0x1EC3, 0x00EA, 0x0309}, { 0x1E2F, 0x00EF, 0x0301}, { 0x1ED3, 0x00F4, 0x0300}, { 0x1ED1, 0x00F4, 0x0301}, { 0x1ED7, 0x00F4, 0x0303}, { 0x1ED5, 0x00F4, 0x0309}, { 0x1E4D, 0x00F5, 0x0301}, { 0x022D, 0x00F5, 0x0304}, { 0x1E4F, 0x00F5, 0x0308}, { 0x022B, 0x00F6, 0x0304}, { 0x01FF, 0x00F8, 0x0301}, { 0x01DC, 0x00FC, 0x0300}, { 0x01D8, 0x00FC, 0x0301}, { 0x01D6, 0x00FC, 0x0304}, { 0x01DA, 0x00FC, 0x030C}, { 0x1EB0, 0x0102, 0x0300}, { 0x1EAE, 0x0102, 0x0301}, { 0x1EB4, 0x0102, 0x0303}, { 0x1EB2, 0x0102, 0x0309}, { 0x1EB1, 0x0103, 0x0300}, { 0x1EAF, 0x0103, 0x0301}, { 0x1EB5, 0x0103, 0x0303}, { 0x1EB3, 0x0103, 0x0309}, { 0x1E14, 0x0112, 0x0300}, { 0x1E16, 0x0112, 0x0301}, { 0x1E15, 0x0113, 0x0300}, { 0x1E17, 0x0113, 0x0301}, { 0x1E50, 0x014C, 0x0300}, { 0x1E52, 0x014C, 0x0301}, { 0x1E51, 0x014D, 0x0300}, { 0x1E53, 0x014D, 0x0301}, { 0x1E64, 0x015A, 0x0307}, { 0x1E65, 0x015B, 0x0307}, { 0x1E66, 0x0160, 0x0307}, { 0x1E67, 0x0161, 0x0307}, { 0x1E78, 0x0168, 0x0301}, { 0x1E79, 0x0169, 0x0301}, { 0x1E7A, 0x016A, 0x0308}, { 0x1E7B, 0x016B, 0x0308}, { 0x1E9B, 0x017F, 0x0307}, { 0x1EDC, 0x01A0, 0x0300}, { 0x1EDA, 0x01A0, 0x0301}, { 0x1EE0, 0x01A0, 0x0303}, { 0x1EDE, 0x01A0, 0x0309}, { 0x1EE2, 0x01A0, 0x0323}, { 0x1EDD, 0x01A1, 0x0300}, { 0x1EDB, 0x01A1, 0x0301}, { 0x1EE1, 0x01A1, 0x0303}, { 0x1EDF, 0x01A1, 0x0309}, { 0x1EE3, 0x01A1, 0x0323}, { 0x1EEA, 0x01AF, 0x0300}, { 0x1EE8, 0x01AF, 0x0301}, { 0x1EEE, 0x01AF, 0x0303}, { 0x1EEC, 0x01AF, 0x0309}, { 0x1EF0, 0x01AF, 0x0323}, { 0x1EEB, 0x01B0, 0x0300}, { 0x1EE9, 0x01B0, 0x0301}, { 0x1EEF, 0x01B0, 0x0303}, { 0x1EED, 0x01B0, 0x0309}, { 0x1EF1, 0x01B0, 0x0323}, { 0x01EE, 0x01B7, 0x030C}, { 0x01EC, 0x01EA, 0x0304}, { 0x01ED, 0x01EB, 0x0304}, { 0x01E0, 0x0226, 0x0304}, { 0x01E1, 0x0227, 0x0304}, { 0x1E1C, 0x0228, 0x0306}, { 0x1E1D, 0x0229, 0x0306}, { 0x0230, 0x022E, 0x0304}, { 0x0231, 0x022F, 0x0304}, { 0x01EF, 0x0292, 0x030C}, { 0x0344, 0x0308, 0x0301}, { 0x1FBA, 0x0391, 0x0300}, { 0x0386, 0x0391, 0x0301}, { 0x1FB9, 0x0391, 0x0304}, { 0x1FB8, 0x0391, 0x0306}, { 0x1F08, 0x0391, 0x0313}, { 0x1F09, 0x0391, 0x0314}, { 0x1FBC, 0x0391, 0x0345}, { 0x1FC8, 0x0395, 0x0300}, { 0x0388, 0x0395, 0x0301}, { 0x1F18, 0x0395, 0x0313}, { 0x1F19, 0x0395, 0x0314}, { 0x1FCA, 0x0397, 0x0300}, { 0x0389, 0x0397, 0x0301}, { 0x1F28, 0x0397, 0x0313}, { 0x1F29, 0x0397, 0x0314}, { 0x1FCC, 0x0397, 0x0345}, { 0x1FDA, 0x0399, 0x0300}, { 0x038A, 0x0399, 0x0301}, { 0x1FD9, 0x0399, 0x0304}, { 0x1FD8, 0x0399, 0x0306}, { 0x03AA, 0x0399, 0x0308}, { 0x1F38, 0x0399, 0x0313}, { 0x1F39, 0x0399, 0x0314}, { 0x1FF8, 0x039F, 0x0300}, { 0x038C, 0x039F, 0x0301}, { 0x1F48, 0x039F, 0x0313}, { 0x1F49, 0x039F, 0x0314}, { 0x1FEC, 0x03A1, 0x0314}, { 0x1FEA, 0x03A5, 0x0300}, { 0x038E, 0x03A5, 0x0301}, { 0x1FE9, 0x03A5, 0x0304}, { 0x1FE8, 0x03A5, 0x0306}, { 0x03AB, 0x03A5, 0x0308}, { 0x1F59, 0x03A5, 0x0314}, { 0x1FFA, 0x03A9, 0x0300}, { 0x038F, 0x03A9, 0x0301}, { 0x1F68, 0x03A9, 0x0313}, { 0x1F69, 0x03A9, 0x0314}, { 0x1FFC, 0x03A9, 0x0345}, { 0x1FB4, 0x03AC, 0x0345}, { 0x1FC4, 0x03AE, 0x0345}, { 0x1F70, 0x03B1, 0x0300}, { 0x03AC, 0x03B1, 0x0301}, { 0x1FB1, 0x03B1, 0x0304}, { 0x1FB0, 0x03B1, 0x0306}, { 0x1F00, 0x03B1, 0x0313}, { 0x1F01, 0x03B1, 0x0314}, { 0x1FB6, 0x03B1, 0x0342}, { 0x1FB3, 0x03B1, 0x0345}, { 0x1F72, 0x03B5, 0x0300}, { 0x03AD, 0x03B5, 0x0301}, { 0x1F10, 0x03B5, 0x0313}, { 0x1F11, 0x03B5, 0x0314}, { 0x1F74, 0x03B7, 0x0300}, { 0x03AE, 0x03B7, 0x0301}, { 0x1F20, 0x03B7, 0x0313}, { 0x1F21, 0x03B7, 0x0314}, { 0x1FC6, 0x03B7, 0x0342}, { 0x1FC3, 0x03B7, 0x0345}, { 0x1F76, 0x03B9, 0x0300}, { 0x03AF, 0x03B9, 0x0301}, { 0x1FD1, 0x03B9, 0x0304}, { 0x1FD0, 0x03B9, 0x0306}, { 0x03CA, 0x03B9, 0x0308}, { 0x1F30, 0x03B9, 0x0313}, { 0x1F31, 0x03B9, 0x0314}, { 0x1FD6, 0x03B9, 0x0342}, { 0x1F78, 0x03BF, 0x0300}, { 0x03CC, 0x03BF, 0x0301}, { 0x1F40, 0x03BF, 0x0313}, { 0x1F41, 0x03BF, 0x0314}, { 0x1FE4, 0x03C1, 0x0313}, { 0x1FE5, 0x03C1, 0x0314}, { 0x1F7A, 0x03C5, 0x0300}, { 0x03CD, 0x03C5, 0x0301}, { 0x1FE1, 0x03C5, 0x0304}, { 0x1FE0, 0x03C5, 0x0306}, { 0x03CB, 0x03C5, 0x0308}, { 0x1F50, 0x03C5, 0x0313}, { 0x1F51, 0x03C5, 0x0314}, { 0x1FE6, 0x03C5, 0x0342}, { 0x1F7C, 0x03C9, 0x0300}, { 0x03CE, 0x03C9, 0x0301}, { 0x1F60, 0x03C9, 0x0313}, { 0x1F61, 0x03C9, 0x0314}, { 0x1FF6, 0x03C9, 0x0342}, { 0x1FF3, 0x03C9, 0x0345}, { 0x1FD2, 0x03CA, 0x0300}, { 0x0390, 0x03CA, 0x0301}, { 0x1FD7, 0x03CA, 0x0342}, { 0x1FE2, 0x03CB, 0x0300}, { 0x03B0, 0x03CB, 0x0301}, { 0x1FE7, 0x03CB, 0x0342}, { 0x1FF4, 0x03CE, 0x0345}, { 0x03D3, 0x03D2, 0x0301}, { 0x03D4, 0x03D2, 0x0308}, { 0x0407, 0x0406, 0x0308}, { 0x04D0, 0x0410, 0x0306}, { 0x04D2, 0x0410, 0x0308}, { 0x0403, 0x0413, 0x0301}, { 0x0400, 0x0415, 0x0300}, { 0x04D6, 0x0415, 0x0306}, { 0x0401, 0x0415, 0x0308}, { 0x04C1, 0x0416, 0x0306}, { 0x04DC, 0x0416, 0x0308}, { 0x04DE, 0x0417, 0x0308}, { 0x040D, 0x0418, 0x0300}, { 0x04E2, 0x0418, 0x0304}, { 0x0419, 0x0418, 0x0306}, { 0x04E4, 0x0418, 0x0308}, { 0x040C, 0x041A, 0x0301}, { 0x04E6, 0x041E, 0x0308}, { 0x04EE, 0x0423, 0x0304}, { 0x040E, 0x0423, 0x0306}, { 0x04F0, 0x0423, 0x0308}, { 0x04F2, 0x0423, 0x030B}, { 0x04F4, 0x0427, 0x0308}, { 0x04F8, 0x042B, 0x0308}, { 0x04EC, 0x042D, 0x0308}, { 0x04D1, 0x0430, 0x0306}, { 0x04D3, 0x0430, 0x0308}, { 0x0453, 0x0433, 0x0301}, { 0x0450, 0x0435, 0x0300}, { 0x04D7, 0x0435, 0x0306}, { 0x0451, 0x0435, 0x0308}, { 0x04C2, 0x0436, 0x0306}, { 0x04DD, 0x0436, 0x0308}, { 0x04DF, 0x0437, 0x0308}, { 0x045D, 0x0438, 0x0300}, { 0x04E3, 0x0438, 0x0304}, { 0x0439, 0x0438, 0x0306}, { 0x04E5, 0x0438, 0x0308}, { 0x045C, 0x043A, 0x0301}, { 0x04E7, 0x043E, 0x0308}, { 0x04EF, 0x0443, 0x0304}, { 0x045E, 0x0443, 0x0306}, { 0x04F1, 0x0443, 0x0308}, { 0x04F3, 0x0443, 0x030B}, { 0x04F5, 0x0447, 0x0308}, { 0x04F9, 0x044B, 0x0308}, { 0x04ED, 0x044D, 0x0308}, { 0x0457, 0x0456, 0x0308}, { 0x0476, 0x0474, 0x030F}, { 0x0477, 0x0475, 0x030F}, { 0x04DA, 0x04D8, 0x0308}, { 0x04DB, 0x04D9, 0x0308}, { 0x04EA, 0x04E8, 0x0308}, { 0x04EB, 0x04E9, 0x0308}, { 0xFB2E, 0x05D0, 0x05B7}, { 0xFB2F, 0x05D0, 0x05B8}, { 0xFB30, 0x05D0, 0x05BC}, { 0xFB31, 0x05D1, 0x05BC}, { 0xFB4C, 0x05D1, 0x05BF}, { 0xFB32, 0x05D2, 0x05BC}, { 0xFB33, 0x05D3, 0x05BC}, { 0xFB34, 0x05D4, 0x05BC}, { 0xFB4B, 0x05D5, 0x05B9}, { 0xFB35, 0x05D5, 0x05BC}, { 0xFB36, 0x05D6, 0x05BC}, { 0xFB38, 0x05D8, 0x05BC}, { 0xFB1D, 0x05D9, 0x05B4}, { 0xFB39, 0x05D9, 0x05BC}, { 0xFB3A, 0x05DA, 0x05BC}, { 0xFB3B, 0x05DB, 0x05BC}, { 0xFB4D, 0x05DB, 0x05BF}, { 0xFB3C, 0x05DC, 0x05BC}, { 0xFB3E, 0x05DE, 0x05BC}, { 0xFB40, 0x05E0, 0x05BC}, { 0xFB41, 0x05E1, 0x05BC}, { 0xFB43, 0x05E3, 0x05BC}, { 0xFB44, 0x05E4, 0x05BC}, { 0xFB4E, 0x05E4, 0x05BF}, { 0xFB46, 0x05E6, 0x05BC}, { 0xFB47, 0x05E7, 0x05BC}, { 0xFB48, 0x05E8, 0x05BC}, { 0xFB49, 0x05E9, 0x05BC}, { 0xFB2A, 0x05E9, 0x05C1}, { 0xFB2B, 0x05E9, 0x05C2}, { 0xFB4A, 0x05EA, 0x05BC}, { 0xFB1F, 0x05F2, 0x05B7}, { 0x0622, 0x0627, 0x0653}, { 0x0623, 0x0627, 0x0654}, { 0x0625, 0x0627, 0x0655}, { 0x0624, 0x0648, 0x0654}, { 0x0626, 0x064A, 0x0654}, { 0x06C2, 0x06C1, 0x0654}, { 0x06D3, 0x06D2, 0x0654}, { 0x06C0, 0x06D5, 0x0654}, { 0x0958, 0x0915, 0x093C}, { 0x0959, 0x0916, 0x093C}, { 0x095A, 0x0917, 0x093C}, { 0x095B, 0x091C, 0x093C}, { 0x095C, 0x0921, 0x093C}, { 0x095D, 0x0922, 0x093C}, { 0x0929, 0x0928, 0x093C}, { 0x095E, 0x092B, 0x093C}, { 0x095F, 0x092F, 0x093C}, { 0x0931, 0x0930, 0x093C}, { 0x0934, 0x0933, 0x093C}, { 0x09DC, 0x09A1, 0x09BC}, { 0x09DD, 0x09A2, 0x09BC}, { 0x09DF, 0x09AF, 0x09BC}, { 0x09CB, 0x09C7, 0x09BE}, { 0x09CC, 0x09C7, 0x09D7}, { 0x0A59, 0x0A16, 0x0A3C}, { 0x0A5A, 0x0A17, 0x0A3C}, { 0x0A5B, 0x0A1C, 0x0A3C}, { 0x0A5E, 0x0A2B, 0x0A3C}, { 0x0A33, 0x0A32, 0x0A3C}, { 0x0A36, 0x0A38, 0x0A3C}, { 0x0B5C, 0x0B21, 0x0B3C}, { 0x0B5D, 0x0B22, 0x0B3C}, { 0x0B4B, 0x0B47, 0x0B3E}, { 0x0B48, 0x0B47, 0x0B56}, { 0x0B4C, 0x0B47, 0x0B57}, { 0x0B94, 0x0B92, 0x0BD7}, { 0x0BCA, 0x0BC6, 0x0BBE}, { 0x0BCC, 0x0BC6, 0x0BD7}, { 0x0BCB, 0x0BC7, 0x0BBE}, { 0x0C48, 0x0C46, 0x0C56}, { 0x0CC0, 0x0CBF, 0x0CD5}, { 0x0CCA, 0x0CC6, 0x0CC2}, { 0x0CC7, 0x0CC6, 0x0CD5}, { 0x0CC8, 0x0CC6, 0x0CD6}, { 0x0CCB, 0x0CCA, 0x0CD5}, { 0x0D4A, 0x0D46, 0x0D3E}, { 0x0D4C, 0x0D46, 0x0D57}, { 0x0D4B, 0x0D47, 0x0D3E}, { 0x0DDA, 0x0DD9, 0x0DCA}, { 0x0DDC, 0x0DD9, 0x0DCF}, { 0x0DDE, 0x0DD9, 0x0DDF}, { 0x0DDD, 0x0DDC, 0x0DCA}, { 0x0F69, 0x0F40, 0x0FB5}, { 0x0F43, 0x0F42, 0x0FB7}, { 0x0F4D, 0x0F4C, 0x0FB7}, { 0x0F52, 0x0F51, 0x0FB7}, { 0x0F57, 0x0F56, 0x0FB7}, { 0x0F5C, 0x0F5B, 0x0FB7}, { 0x0F73, 0x0F71, 0x0F72}, { 0x0F75, 0x0F71, 0x0F74}, { 0x0F81, 0x0F71, 0x0F80}, { 0x0FB9, 0x0F90, 0x0FB5}, { 0x0F93, 0x0F92, 0x0FB7}, { 0x0F9D, 0x0F9C, 0x0FB7}, { 0x0FA2, 0x0FA1, 0x0FB7}, { 0x0FA7, 0x0FA6, 0x0FB7}, { 0x0FAC, 0x0FAB, 0x0FB7}, { 0x0F76, 0x0FB2, 0x0F80}, { 0x0F78, 0x0FB3, 0x0F80}, { 0x1026, 0x1025, 0x102E}, { 0x1E38, 0x1E36, 0x0304}, { 0x1E39, 0x1E37, 0x0304}, { 0x1E5C, 0x1E5A, 0x0304}, { 0x1E5D, 0x1E5B, 0x0304}, { 0x1E68, 0x1E62, 0x0307}, { 0x1E69, 0x1E63, 0x0307}, { 0x1EAC, 0x1EA0, 0x0302}, { 0x1EB6, 0x1EA0, 0x0306}, { 0x1EAD, 0x1EA1, 0x0302}, { 0x1EB7, 0x1EA1, 0x0306}, { 0x1EC6, 0x1EB8, 0x0302}, { 0x1EC7, 0x1EB9, 0x0302}, { 0x1ED8, 0x1ECC, 0x0302}, { 0x1ED9, 0x1ECD, 0x0302}, { 0x1F02, 0x1F00, 0x0300}, { 0x1F04, 0x1F00, 0x0301}, { 0x1F06, 0x1F00, 0x0342}, { 0x1F80, 0x1F00, 0x0345}, { 0x1F03, 0x1F01, 0x0300}, { 0x1F05, 0x1F01, 0x0301}, { 0x1F07, 0x1F01, 0x0342}, { 0x1F81, 0x1F01, 0x0345}, { 0x1F82, 0x1F02, 0x0345}, { 0x1F83, 0x1F03, 0x0345}, { 0x1F84, 0x1F04, 0x0345}, { 0x1F85, 0x1F05, 0x0345}, { 0x1F86, 0x1F06, 0x0345}, { 0x1F87, 0x1F07, 0x0345}, { 0x1F0A, 0x1F08, 0x0300}, { 0x1F0C, 0x1F08, 0x0301}, { 0x1F0E, 0x1F08, 0x0342}, { 0x1F88, 0x1F08, 0x0345}, { 0x1F0B, 0x1F09, 0x0300}, { 0x1F0D, 0x1F09, 0x0301}, { 0x1F0F, 0x1F09, 0x0342}, { 0x1F89, 0x1F09, 0x0345}, { 0x1F8A, 0x1F0A, 0x0345}, { 0x1F8B, 0x1F0B, 0x0345}, { 0x1F8C, 0x1F0C, 0x0345}, { 0x1F8D, 0x1F0D, 0x0345}, { 0x1F8E, 0x1F0E, 0x0345}, { 0x1F8F, 0x1F0F, 0x0345}, { 0x1F12, 0x1F10, 0x0300}, { 0x1F14, 0x1F10, 0x0301}, { 0x1F13, 0x1F11, 0x0300}, { 0x1F15, 0x1F11, 0x0301}, { 0x1F1A, 0x1F18, 0x0300}, { 0x1F1C, 0x1F18, 0x0301}, { 0x1F1B, 0x1F19, 0x0300}, { 0x1F1D, 0x1F19, 0x0301}, { 0x1F22, 0x1F20, 0x0300}, { 0x1F24, 0x1F20, 0x0301}, { 0x1F26, 0x1F20, 0x0342}, { 0x1F90, 0x1F20, 0x0345}, { 0x1F23, 0x1F21, 0x0300}, { 0x1F25, 0x1F21, 0x0301}, { 0x1F27, 0x1F21, 0x0342}, { 0x1F91, 0x1F21, 0x0345}, { 0x1F92, 0x1F22, 0x0345}, { 0x1F93, 0x1F23, 0x0345}, { 0x1F94, 0x1F24, 0x0345}, { 0x1F95, 0x1F25, 0x0345}, { 0x1F96, 0x1F26, 0x0345}, { 0x1F97, 0x1F27, 0x0345}, { 0x1F2A, 0x1F28, 0x0300}, { 0x1F2C, 0x1F28, 0x0301}, { 0x1F2E, 0x1F28, 0x0342}, { 0x1F98, 0x1F28, 0x0345}, { 0x1F2B, 0x1F29, 0x0300}, { 0x1F2D, 0x1F29, 0x0301}, { 0x1F2F, 0x1F29, 0x0342}, { 0x1F99, 0x1F29, 0x0345}, { 0x1F9A, 0x1F2A, 0x0345}, { 0x1F9B, 0x1F2B, 0x0345}, { 0x1F9C, 0x1F2C, 0x0345}, { 0x1F9D, 0x1F2D, 0x0345}, { 0x1F9E, 0x1F2E, 0x0345}, { 0x1F9F, 0x1F2F, 0x0345}, { 0x1F32, 0x1F30, 0x0300}, { 0x1F34, 0x1F30, 0x0301}, { 0x1F36, 0x1F30, 0x0342}, { 0x1F33, 0x1F31, 0x0300}, { 0x1F35, 0x1F31, 0x0301}, { 0x1F37, 0x1F31, 0x0342}, { 0x1F3A, 0x1F38, 0x0300}, { 0x1F3C, 0x1F38, 0x0301}, { 0x1F3E, 0x1F38, 0x0342}, { 0x1F3B, 0x1F39, 0x0300}, { 0x1F3D, 0x1F39, 0x0301}, { 0x1F3F, 0x1F39, 0x0342}, { 0x1F42, 0x1F40, 0x0300}, { 0x1F44, 0x1F40, 0x0301}, { 0x1F43, 0x1F41, 0x0300}, { 0x1F45, 0x1F41, 0x0301}, { 0x1F4A, 0x1F48, 0x0300}, { 0x1F4C, 0x1F48, 0x0301}, { 0x1F4B, 0x1F49, 0x0300}, { 0x1F4D, 0x1F49, 0x0301}, { 0x1F52, 0x1F50, 0x0300}, { 0x1F54, 0x1F50, 0x0301}, { 0x1F56, 0x1F50, 0x0342}, { 0x1F53, 0x1F51, 0x0300}, { 0x1F55, 0x1F51, 0x0301}, { 0x1F57, 0x1F51, 0x0342}, { 0x1F5B, 0x1F59, 0x0300}, { 0x1F5D, 0x1F59, 0x0301}, { 0x1F5F, 0x1F59, 0x0342}, { 0x1F62, 0x1F60, 0x0300}, { 0x1F64, 0x1F60, 0x0301}, { 0x1F66, 0x1F60, 0x0342}, { 0x1FA0, 0x1F60, 0x0345}, { 0x1F63, 0x1F61, 0x0300}, { 0x1F65, 0x1F61, 0x0301}, { 0x1F67, 0x1F61, 0x0342}, { 0x1FA1, 0x1F61, 0x0345}, { 0x1FA2, 0x1F62, 0x0345}, { 0x1FA3, 0x1F63, 0x0345}, { 0x1FA4, 0x1F64, 0x0345}, { 0x1FA5, 0x1F65, 0x0345}, { 0x1FA6, 0x1F66, 0x0345}, { 0x1FA7, 0x1F67, 0x0345}, { 0x1F6A, 0x1F68, 0x0300}, { 0x1F6C, 0x1F68, 0x0301}, { 0x1F6E, 0x1F68, 0x0342}, { 0x1FA8, 0x1F68, 0x0345}, { 0x1F6B, 0x1F69, 0x0300}, { 0x1F6D, 0x1F69, 0x0301}, { 0x1F6F, 0x1F69, 0x0342}, { 0x1FA9, 0x1F69, 0x0345}, { 0x1FAA, 0x1F6A, 0x0345}, { 0x1FAB, 0x1F6B, 0x0345}, { 0x1FAC, 0x1F6C, 0x0345}, { 0x1FAD, 0x1F6D, 0x0345}, { 0x1FAE, 0x1F6E, 0x0345}, { 0x1FAF, 0x1F6F, 0x0345}, { 0x1FB2, 0x1F70, 0x0345}, { 0x1FC2, 0x1F74, 0x0345}, { 0x1FF2, 0x1F7C, 0x0345}, { 0x1FB7, 0x1FB6, 0x0345}, { 0x1FCD, 0x1FBF, 0x0300}, { 0x1FCE, 0x1FBF, 0x0301}, { 0x1FCF, 0x1FBF, 0x0342}, { 0x1FC7, 0x1FC6, 0x0345}, { 0x1FF7, 0x1FF6, 0x0345}, { 0x1FDD, 0x1FFE, 0x0300}, { 0x1FDE, 0x1FFE, 0x0301}, { 0x1FDF, 0x1FFE, 0x0342}, { 0x219A, 0x2190, 0x0338}, { 0x219B, 0x2192, 0x0338}, { 0x21AE, 0x2194, 0x0338}, { 0x21CD, 0x21D0, 0x0338}, { 0x21CF, 0x21D2, 0x0338}, { 0x21CE, 0x21D4, 0x0338}, { 0x2204, 0x2203, 0x0338}, { 0x2209, 0x2208, 0x0338}, { 0x220C, 0x220B, 0x0338}, { 0x2224, 0x2223, 0x0338}, { 0x2226, 0x2225, 0x0338}, { 0x2241, 0x223C, 0x0338}, { 0x2244, 0x2243, 0x0338}, { 0x2247, 0x2245, 0x0338}, { 0x2249, 0x2248, 0x0338}, { 0x226D, 0x224D, 0x0338}, { 0x2262, 0x2261, 0x0338}, { 0x2270, 0x2264, 0x0338}, { 0x2271, 0x2265, 0x0338}, { 0x2274, 0x2272, 0x0338}, { 0x2275, 0x2273, 0x0338}, { 0x2278, 0x2276, 0x0338}, { 0x2279, 0x2277, 0x0338}, { 0x2280, 0x227A, 0x0338}, { 0x2281, 0x227B, 0x0338}, { 0x22E0, 0x227C, 0x0338}, { 0x22E1, 0x227D, 0x0338}, { 0x2284, 0x2282, 0x0338}, { 0x2285, 0x2283, 0x0338}, { 0x2288, 0x2286, 0x0338}, { 0x2289, 0x2287, 0x0338}, { 0x22E2, 0x2291, 0x0338}, { 0x22E3, 0x2292, 0x0338}, { 0x22AC, 0x22A2, 0x0338}, { 0x22AD, 0x22A8, 0x0338}, { 0x22AE, 0x22A9, 0x0338}, { 0x22AF, 0x22AB, 0x0338}, { 0x22EA, 0x22B2, 0x0338}, { 0x22EB, 0x22B3, 0x0338}, { 0x22EC, 0x22B4, 0x0338}, { 0x22ED, 0x22B5, 0x0338}, { 0x3094, 0x3046, 0x3099}, { 0x304C, 0x304B, 0x3099}, { 0x304E, 0x304D, 0x3099}, { 0x3050, 0x304F, 0x3099}, { 0x3052, 0x3051, 0x3099}, { 0x3054, 0x3053, 0x3099}, { 0x3056, 0x3055, 0x3099}, { 0x3058, 0x3057, 0x3099}, { 0x305A, 0x3059, 0x3099}, { 0x305C, 0x305B, 0x3099}, { 0x305E, 0x305D, 0x3099}, { 0x3060, 0x305F, 0x3099}, { 0x3062, 0x3061, 0x3099}, { 0x3065, 0x3064, 0x3099}, { 0x3067, 0x3066, 0x3099}, { 0x3069, 0x3068, 0x3099}, { 0x3070, 0x306F, 0x3099}, { 0x3071, 0x306F, 0x309A}, { 0x3073, 0x3072, 0x3099}, { 0x3074, 0x3072, 0x309A}, { 0x3076, 0x3075, 0x3099}, { 0x3077, 0x3075, 0x309A}, { 0x3079, 0x3078, 0x3099}, { 0x307A, 0x3078, 0x309A}, { 0x307C, 0x307B, 0x3099}, { 0x307D, 0x307B, 0x309A}, { 0x309E, 0x309D, 0x3099}, { 0x30F4, 0x30A6, 0x3099}, { 0x30AC, 0x30AB, 0x3099}, { 0x30AE, 0x30AD, 0x3099}, { 0x30B0, 0x30AF, 0x3099}, { 0x30B2, 0x30B1, 0x3099}, { 0x30B4, 0x30B3, 0x3099}, { 0x30B6, 0x30B5, 0x3099}, { 0x30B8, 0x30B7, 0x3099}, { 0x30BA, 0x30B9, 0x3099}, { 0x30BC, 0x30BB, 0x3099}, { 0x30BE, 0x30BD, 0x3099}, { 0x30C0, 0x30BF, 0x3099}, { 0x30C2, 0x30C1, 0x3099}, { 0x30C5, 0x30C4, 0x3099}, { 0x30C7, 0x30C6, 0x3099}, { 0x30C9, 0x30C8, 0x3099}, { 0x30D0, 0x30CF, 0x3099}, { 0x30D1, 0x30CF, 0x309A}, { 0x30D3, 0x30D2, 0x3099}, { 0x30D4, 0x30D2, 0x309A}, { 0x30D6, 0x30D5, 0x3099}, { 0x30D7, 0x30D5, 0x309A}, { 0x30D9, 0x30D8, 0x3099}, { 0x30DA, 0x30D8, 0x309A}, { 0x30DC, 0x30DB, 0x3099}, { 0x30DD, 0x30DB, 0x309A}, { 0x30F7, 0x30EF, 0x3099}, { 0x30F8, 0x30F0, 0x3099}, { 0x30F9, 0x30F1, 0x3099}, { 0x30FA, 0x30F2, 0x3099}, { 0x30FE, 0x30FD, 0x3099}, { 0xFB2C, 0xFB49, 0x05C1}, { 0xFB2D, 0xFB49, 0x05C2}, }; int do_precomposition(int base, int comb) { int min = 0; int max = sizeof(precompositions) / sizeof(precompositions[0]) - 1; int mid; int sought = (base << 16) | comb, that; /* binary search */ while (max >= min) { mid = (min + max) / 2; that = (precompositions[mid].base << 16) | (precompositions[mid].comb); if (that < sought) { min = mid + 1; } else if (that > sought) { max = mid - 1; } else { return precompositions[mid].replacement; } } /* no match */ return -1; } notion-3+2012042300/de/precompose.h000066400000000000000000000002561174530661200165020ustar00rootroot00000000000000#ifndef PRECOMPOSE_H #define PRECOMPOSE_H int do_precomposition(int base, int comb); /* returns unicode value if a canonical composition exists, otherwise -1 */ #endif notion-3+2012042300/de/private.h000066400000000000000000000004001174530661200157670ustar00rootroot00000000000000/* * ion/de/private.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #ifndef ION_DE_PRIVATE_H #define ION_DE_PRIVATE_H #define DE_SUB_IND " ->" #define DE_SUB_IND_LEN 3 #endif /* ION_DE_PRIVATE_H */ notion-3+2012042300/de/style.c000066400000000000000000000152431174530661200154630ustar00rootroot00000000000000/* * ion/de/style.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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 GrStyleSpec *spec) { 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->spec, spec); 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); gr_stylespec_unalloc(&style->spec); 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->extras_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; } } void destyle_dump(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) { if(!gr_stylespec_load(&style->spec, name)) 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->border.sides=DEBORDER_ALL; style->spacing=0; style->textalign=DEALIGN_CENTER; style->cgrp_alloced=FALSE; 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); gr_stylespec_init(&style->cgrp.spec); style->font=NULL; style->transparency_mode=GR_TRANSPARENCY_NO; style->n_extra_cgrps=0; style->extra_cgrps=NULL; style->extras_table=extl_table_none(); create_normal_gc(style, rootwin); style->tabbrush_data_ok=FALSE; return TRUE; } DEStyle *de_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; } void destyle_add(DEStyle *style) { LINK_ITEM_FIRST(styles, style, next, prev); } /*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) destyle_dump(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 is still in use [%d] but the module " "is being unloaded!"), style->usecount); } destyle_dump(style); } } /*}}}*/ notion-3+2012042300/de/style.h000066400000000000000000000036231174530661200154670ustar00rootroot00000000000000/* * ion/de/style.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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{ DEBORDER_ALL=0, DEBORDER_TB, DEBORDER_LR }; enum{ DEALIGN_LEFT=0, DEALIGN_RIGHT=1, DEALIGN_CENTER=2 }; DECLSTRUCT(DEBorder){ uint sh, hl, pad; uint style; uint sides; }; DECLSTRUCT(DEStyle){ GrStyleSpec spec; 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 extras_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 GrStyleSpec *spec); extern void destyle_add(DEStyle *style); extern void destyle_dump(DEStyle *style); #endif /* ION_DE_STYLE_H */ notion-3+2012042300/de/unicode/000077500000000000000000000000001174530661200156005ustar00rootroot00000000000000notion-3+2012042300/de/unicode/README000066400000000000000000000014621174530661200164630ustar00rootroot00000000000000-- $XFree86: xc/programs/xterm/unicode/README,v 1.2 2000/06/13 02:28:44 dawes Exp $ -- Thomas E. Dickey These are some scripts and datafiles used for generating tables used in the experimental UTF-8 implementation in xterm. To run the convmap.pl script, you will need a copy of UnicodeData-Latest.txt which is currently available as ftp://ftp.unicode.org/Public/UNIDATA/UnicodeData-Latest.txt It is a large file (~460kb), so it is not included in this distribution. convmap.pl is used to generate ../keysym2ucs.c, e.g., ./convmap.plp >../keysym2ucs.c keysym.map is input data for convmap.pl The make-precompose.sh script makes the precompose.c file, which is used to handle canonical composition. This also needs UnicodeData-Latest.txt. It uses precompose.c.head and precompose.c.tail as templates. notion-3+2012042300/de/unicode/make-precompose.sh000077500000000000000000000002771174530661200212340ustar00rootroot00000000000000#!/bin/sh cat precompose.c.head cut UnicodeData-Latest.txt -d ";" -f 1,6 | \ grep ";[0-9,A-F]" | grep " " | \ sed -e "s/ /, 0x/;s/^/{ 0x/;s/;/, 0x/;s/$/},/" | sort +2 cat precompose.c.tail notion-3+2012042300/de/unicode/precompose.c.head000066400000000000000000000003411174530661200210160ustar00rootroot00000000000000/* * Canonical Compositions * * DO NOT EDIT BY HAND! This is generated by the script * unicode/make-precompose.sh */ #include struct { int replacement; int base; int comb; } precompositions[] = { notion-3+2012042300/de/unicode/precompose.c.tail000066400000000000000000000010301174530661200210420ustar00rootroot00000000000000}; int do_precomposition(int base, int comb) { int min = 0; int max = sizeof(precompositions) / sizeof(precompositions[0]) - 1; int mid; int sought = (base << 16) | comb, that; /* binary search */ while (max >= min) { mid = (min + max) / 2; that = (precompositions[mid].base << 16) | (precompositions[mid].comb); if (that < sought) { min = mid + 1; } else if (that > sought) { max = mid - 1; } else { return precompositions[mid].replacement; } } /* no match */ return -1; } notion-3+2012042300/etc/000077500000000000000000000000001174530661200143355ustar00rootroot00000000000000notion-3+2012042300/etc/Makefile000066400000000000000000000013411174530661200157740ustar00rootroot00000000000000## ## Notion etc Makefile ## # System-specific configuration is in system.mk TOPDIR=.. include $(TOPDIR)/build/system-inc.mk ###################################### ETC = cfg_notion.lua cfg_notioncore.lua cfg_kludges.lua cfg_defaults.lua \ cfg_tiling.lua cfg_query.lua cfg_menu.lua \ cfg_statusbar.lua cfg_dock.lua cfg_layouts.lua \ look.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 look_newviolet.lua \ look_greenlight.lua \ lookcommon_clean.lua lookcommon_emboss.lua ###################################### include $(TOPDIR)/build/rules.mk ###################################### _install: etc_install notion-3+2012042300/etc/cfg_defaults.lua000066400000000000000000000003151174530661200174650ustar00rootroot00000000000000-- -- Notion default settings -- dopath("cfg_notioncore") dopath("cfg_kludges") dopath("cfg_layouts") dopath("mod_query") dopath("mod_menu") dopath("mod_tiling") dopath("mod_statusbar") dopath("mod_sp") notion-3+2012042300/etc/cfg_dock.lua000066400000000000000000000027411174530661200166030ustar00rootroot00000000000000-- -- Notion 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 notion-3+2012042300/etc/cfg_kludges.lua000066400000000000000000000025351174530661200173220ustar00rootroot00000000000000-- -- Options to get some programs work more nicely (or at all) -- defwinprop{ class = "AcroRead", instance = "documentShell", acrobatic = true } defwinprop{ class = "Xpdf", instance = "openDialog_popup", ignore_cfgrq = true, } -- Put all dockapps in the statusbar's systray, also adding the missing -- size hints necessary for this to work. defwinprop{ is_dockapp = true, statusbar = "systray", max_size = { w = 64, h = 64}, min_size = { w = 64, h = 64}, } -- 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$<...") notion-3+2012042300/etc/cfg_layouts.lua000066400000000000000000000032441174530661200173620ustar00rootroot00000000000000-- -- Layouts for Notion -- -- -- Helper routines and structures -- -- Tiled frame template for the layouts below local a_frame = { type="WSplitRegion", regparams = { type = "WFrame", frame_style = "frame-tiled" } } -- Helper function for generating splits for layouts local function mksplit(dir, tl, br, float) return { type = (float and "WSplitFloat" or "WSplitSplit"), dir = dir, tls = 1, brs = 1, tl = tl, br = br, } end local function mktiling(split_tree) return { managed = { { type = "WTiling", bottom = true, -- Make it the bottom of the group split_tree = split_tree, } } } end -- -- The layouts -- -- Tiling with single 1:1 horizontal split local tmp=mktiling(mksplit("horizontal", a_frame, a_frame)) ioncore.deflayout("hsplit", tmp) ioncore.deflayout("default", tmp) -- Tiling with single 1:1 vertical split ioncore.deflayout("vsplit", mktiling(mksplit("vertical", a_frame, a_frame)) ) -- Tiling with single 1:1 floating horizontal split ioncore.deflayout("hfloat", mktiling(mksplit("horizontal", a_frame, a_frame, true)) ) -- Tiling with single 1:1 floating vertical split ioncore.deflayout("vfloat", mktiling(mksplit("vertical", a_frame, a_frame, true)) ) -- Tiling with horizontal and then vertical splits ioncore.deflayout("2x2", mktiling(mksplit("horizontal", mksplit("vertical", a_frame, a_frame), mksplit("vertical", a_frame, a_frame)) ) ) -- Tiling with single full screen frame ioncore.deflayout("full", mktiling(a_frame)) notion-3+2012042300/etc/cfg_menu.lua000066400000000000000000000017361174530661200166320ustar00rootroot00000000000000-- -- 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(_)"), }) notion-3+2012042300/etc/cfg_notion.lua000066400000000000000000000061631174530661200171730ustar00rootroot00000000000000-- -- Notion main configuration file -- -- This file only includes some settings that are rather frequently altered. -- The rest of the settings are in cfg_notioncore.lua and individual modules' -- configuration files (cfg_modulename.lua). -- -- When any binding and other customisations that you want are minor, it is -- recommended that you include them in a copy of this file in ~/.notion/. -- Simply create or copy the relevant settings at the end of this file (from -- the other files), recalling that a key can be unbound by passing 'nil' -- (without the quotes) as the callback. For more information, please see -- the Notion configuration manual available from the Notion Web page. -- -- 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, -- Switch frames to display newly mapped windows --switchto=true, -- Default index for windows in frames: one of 'last', 'next' (for -- after current), or 'next-act' (for after current and anything with -- activity right after it). --frame_default_index='next', -- Auto-unsqueeze transients/menus/queries. --unsqueeze=true, -- Display notification tooltips for activity on hidden workspace. --screen_notify=true, } -- Load default settings. The file cfg_defaults loads all the files -- commented out below, except mod_dock. If you do not want to load -- something, comment out this line, and uncomment the lines corresponding -- the the modules or configuration files that you want, below. -- The modules' configuration files correspond to the names of the -- modules with 'mod' replaced by 'cfg'. dopath("cfg_defaults") -- Load configuration of the Notion 'core'. Most bindings are here. --dopath("cfg_notioncore") -- Load some kludges to make apps behave better. --dopath("cfg_kludges") -- Define some layouts. --dopath("cfg_layouts") -- Load some modules. Bindings and other configuration specific to modules -- are in the files cfg_modulename.lua. --dopath("mod_query") --dopath("mod_menu") --dopath("mod_tiling") --dopath("mod_statusbar") --dopath("mod_dock") --dopath("mod_sp") -- -- Common customisations -- -- Uncommenting the following lines should get you plain-old-menus instead -- of query-menus. --defbindings("WScreen", { -- kpress(ALTMETA.."F12", "mod_menu.menu(_, _sub, 'mainmenu', {big=true})"), --}) -- --defbindings("WMPlex.toplevel", { -- kpress(META.."M", "mod_menu.menu(_, _sub, 'ctxmenu')"), --}) notion-3+2012042300/etc/cfg_notioncore.lua000066400000000000000000000332211174530661200200370ustar00rootroot00000000000000-- -- Notion 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", "mod_menu.grabmenu(_, _sub, 'focuslist')"), -- Alternative without (cyclable) menu --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.tagged_clear()"), }), 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(_, _sub, '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", "WGroup.set_fullscreen(_, '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. 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 Notion manual page."), kpress(META.."F1", "ioncore.exec_on(_, ':man notion')"), 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("Display context menu."), --kpress(META.."M", "mod_menu.menu(_, _sub, 'ctxmenu')"), kpress(META.."M", "mod_query.query_menu(_, _sub, 'ctxmenu', 'Context menu:')"), submap(META.."K", { bdoc("Detach (float) or reattach an object to its previous location."), -- By using _chld instead of _sub, we can detach/reattach queries -- attached to a group. The detach code checks if the parameter -- (_chld) is a group 'bottom' and detaches the whole group in that -- case. kpress("D", "ioncore.detach(_chld, 'toggle')", "_chld:non-nil"), }), }) -- WFrame context bindings -- -- These bindings are common to all types of frames. Some additional -- frame bindings are found in some 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("Query for a client window to attach."), kpress(META.."A", "mod_query.query_attachclient(_)"), submap(META.."K", { -- Display tab numbers when modifiers are released submap_wait("ioncore.tabnum.show(_)"), 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", "ioncore.tagged_attach(_)"), }), }) -- 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", { menuentry("Run...", "mod_query.query_exec(_)"), menuentry("Terminal", "ioncore.exec_on(_, XTERM or 'xterm')"), menuentry("Lock screen", "ioncore.exec_on(_, 'xlock')"), menuentry("Help", "mod_query.query_man(_)"), menuentry("About Notion", "mod_query.show_about_ion(_)"), submenu("Styles", "stylemenu"), submenu("Session", "sessionmenu"), }) -- 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 actions etc.) defctxmenu("WFrame", "Frame", { -- Note: this propagates the close to any subwindows; it does not -- destroy the frame itself, unless empty. An entry to destroy tiled -- frames is configured in cfg_tiling.lua. menuentry("Close", "WRegion.rqclose_propagate(_, _sub)"), -- Low-priority entries menuentry("Attach tagged", "ioncore.tagged_attach(_)", { priority = 0 }), menuentry("Clear tags", "ioncore.tagged_clear()", { priority = 0 }), menuentry("Window info", "mod_query.show_tree(_, _sub)", { priority = 0 }), }) -- Context menu for groups (workspaces, client windows) defctxmenu("WGroup", "Group", { menuentry("Toggle tag", "WRegion.set_tagged(_, 'toggle')"), menuentry("De/reattach", "ioncore.detach(_, 'toggle')"), }) -- Context menu for workspaces defctxmenu("WGroupWS", "Workspace", { menuentry("Close", "WRegion.rqclose(_)"), menuentry("Rename", "mod_query.query_renameworkspace(nil, _)"), menuentry("Attach tagged", "ioncore.tagged_attach(_)"), }) -- Context menu for client windows defctxmenu("WClientWin", "Client window", { menuentry("Kill", "WClientWin.kill(_)"), }) notion-3+2012042300/etc/cfg_query.lua000066400000000000000000000076071174530661200170360ustar00rootroot00000000000000-- -- 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, -- Case-insensitive completion? (Some queries only.) caseicompl=true, -- Sub-string completion? (Some queries only.) substrcompl=true, } --]] notion-3+2012042300/etc/cfg_statusbar.lua000066400000000000000000000056451174530661200177010ustar00rootroot00000000000000-- -- Notion 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 ] %filler%systray", --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={}, }, } notion-3+2012042300/etc/cfg_tiling.lua000066400000000000000000000046751174530661200171610ustar00rootroot00000000000000-- -- Notion 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.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)"), menuentry("Untile", "mod_tiling.untile(_)"), 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(_)"), }), }) -- Extra context menu extra entries for floatframes. defctxmenu("WFrame.floating", "Floating frame", { append=true, menuentry("New tiling", "mod_tiling.mkbottom(_)"), }) notion-3+2012042300/etc/look.lua000066400000000000000000000000311174530661200157760ustar00rootroot00000000000000dopath('look_newviolet') notion-3+2012042300/etc/look_brownsteel.lua000066400000000000000000000041451174530661200202540ustar00rootroot00000000000000-- look_brownsteel.lua drawing engine configuration file for Notion. 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("tab", { 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("input", { 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", { de.substyle("active", { shadow_colour = "#304050", highlight_colour = "#708090", background_colour = "#506070", foreground_colour = "#ffffff", }), }) dopath("lookcommon_emboss") gr.refresh() notion-3+2012042300/etc/look_clean.lua000066400000000000000000000034561174530661200171560ustar00rootroot00000000000000-- look_clean.lua drawing engine configuration file for Notion. 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("tab", { 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("input", { foreground_colour = "white", 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") de.defstyle("tab-menuentry-big", { padding_pixels = 7, font = "-misc-fixed-medium-r-*-*-18-*-*-*-*-*-*-*", }) gr.refresh() notion-3+2012042300/etc/look_cleanios.lua000066400000000000000000000032541174530661200176650ustar00rootroot00000000000000-- look_cleanios.lua drawing engine configuration file for Notion. 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("tab", { 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("input-edln", { de.substyle("*-cursor", { background_colour = "#000000", foreground_colour = "#d8d8d8", }), de.substyle("*-selection", { background_colour = "#f0c000", foreground_colour = "#000000", }), }) dopath("lookcommon_clean") gr.refresh() notion-3+2012042300/etc/look_cleanviolet.lua000066400000000000000000000032651174530661200203770ustar00rootroot00000000000000-- -- Look_cleanviolet for Notion'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("tab", { 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("input", { 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() notion-3+2012042300/etc/look_dusky.lua000066400000000000000000000040271174530661200172260ustar00rootroot00000000000000-- look_dusky.lua drawing engine configuration file for Notion. 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("tab", { 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("input", { 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", { de.substyle("active", { shadow_colour = "#452727", highlight_colour = "#866868", background_colour = "#664848", foreground_colour = "#ffffff", }), }) dopath("lookcommon_emboss") gr.refresh() notion-3+2012042300/etc/look_greenlight.lua000066400000000000000000000026121174530661200202150ustar00rootroot00000000000000-- -- look_greenlight for Notion's default drawing engine. -- if not gr.select_engine("de") then return end -- Clear existing styles from memory. de.reset() -- Base style de.defstyle("*", { highlight_colour = "#666666", shadow_colour = "#666666", background_colour = "#333333", foreground_colour = "#cccccc", shadow_pixels = 1, highlight_pixels = 1, padding_pixels = 1, spacing = 0, border_style = "elevated", border_sides = "tb", font = "-*-helvetica-medium-r-normal-*-14-*-*-*-*-*-*-*", text_align = "center", }) de.defstyle("frame", { background_colour = "#000000", transparent_background = false, }) de.defstyle("tab", { font = "-*-helvetica-medium-r-normal-*-10-*-*-*-*-*-*-*", spacing = 1, de.substyle("active-selected", { highlight_colour = "#8ac267", shadow_colour = "#8ac267", background_colour = "#354743", }), de.substyle("inactive-selected", { highlight_colour = "#6aa247", shadow_colour = "#6aa247", }), }) de.defstyle("input", { text_align = "left", spacing = 1, de.substyle("*-selection", { background_colour = "#354743", }), de.substyle("*-cursor", { background_colour = "#8ac267", foreground_colour = "#333333", }), }) dopath("lookcommon_clean") -- Refresh objects' brushes. gr.refresh() notion-3+2012042300/etc/look_greyviolet.lua000066400000000000000000000032311174530661200202540ustar00rootroot00000000000000-- look_greyviolet.lua drawing engine configuration file for Notion. 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("tab", { 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("input", { de.substyle("*-cursor", { background_colour = "#000000", foreground_colour = "#aaaaaa", }), de.substyle("*-selection", { background_colour = "#666699", foreground_colour = "black", }), }) dopath("lookcommon_emboss") gr.refresh() notion-3+2012042300/etc/look_ios.lua000066400000000000000000000034601174530661200166610ustar00rootroot00000000000000-- look_ios.lua drawing engine configuration file for Notion. 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("tab", { 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("input", { de.substyle("*-cursor", { background_colour = "#000000", foreground_colour = "#d8d8d8", }), de.substyle("*-selection", { background_colour = "#f0c000", foreground_colour = "#000000", }), }) dopath("lookcommon_emboss") de.defstyle("frame-tiled", { spacing = 0, }) de.defstyle("frame-tiled-alt", { spacing = 0, }) de.defstyle("tab-frame-tiled", { spacing = 0, }) gr.refresh() notion-3+2012042300/etc/look_newviolet.lua000066400000000000000000000032571174530661200201070ustar00rootroot00000000000000-- -- look_newviolet for Notion's default drawing engine. -- Based on look_cleanviolet -- if not gr.select_engine("de") then return end de.reset() de.defstyle("*", { highlight_colour = "#e7e7ff", shadow_colour = "#e7e7ff", background_colour = "#b8b8c8", foreground_colour = "#000000", shadow_pixels = 1, highlight_pixels = 2, padding_pixels = 1, spacing = 1, border_style = "elevated", border_sides = "tb", font = "-*-helvetica-medium-r-normal-*-14-*-*-*-*-*-*-*", text_align = "center", }) de.defstyle("tab", { font = "-*-helvetica-medium-r-normal-*-12-*-*-*-*-*-*-*", de.substyle("active-selected", { highlight_colour = "#aaaacc", shadow_colour = "#aaaacc", background_colour = "#666699", foreground_colour = "#eeeeee", }), de.substyle("inactive-selected", { highlight_colour = "#cfcfdf", shadow_colour = "#cfcfdf", background_colour = "#9999bb", foreground_colour = "#000000", }), }) de.defstyle("input", { text_align = "left", highlight_colour = "#eeeeff", shadow_colour = "#eeeeff", de.substyle("*-selection", { background_colour = "#666699", foreground_colour = "#000000", }), de.substyle("*-cursor", { background_colour = "#000000", foreground_colour = "#b8b8c8", }), }) de.defstyle("input-menu", { highlight_pixels = 0, shadow_pixels = 0, padding_pixels = 0, }) de.defstyle("frame", { shadow_pixels = 1, highlight_pixels = 1, padding_pixels = 0, border_sides = "all", }) dopath("lookcommon_clean") -- Refresh objects' brushes. gr.refresh() notion-3+2012042300/etc/look_simpleblue.lua000066400000000000000000000045271174530661200202350ustar00rootroot00000000000000-- look_simpleblue.lua drawing engine configuration file for Notion. 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", { 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", { font = "-*-helvetica-medium-r-normal-*-12-*-*-*-*-*-*-*", de.substyle("active-selected", { shadow_colour = "black", highlight_colour = "black", background_colour = "#3f3f3f", foreground_colour = "#bfbfbf", }), 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 = "#1f2f4f", foreground_colour = "#bfbfbf", }), de.substyle("inactive-unselected", { shadow_colour = "black", highlight_colour = "black", background_colour = "#0f1f4f", foreground_colour = "#9f9f9f", }), text_align = "center", }) de.defstyle("input", { 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", }), }) de.defstyle("input-menu", { padding_pixels=0, }) dopath("lookcommon_clean") gr.refresh() notion-3+2012042300/etc/lookcommon_clean.lua000066400000000000000000000040471174530661200203640ustar00rootroot00000000000000-- Common settings for the "clean" styles de.defstyle("frame", { background_colour = "#000000", de.substyle("quasiactive", { -- Something detached from the frame is active padding_colour = "#901010", }), de.substyle("userattr1", { -- For user scripts padding_colour = "#009010", }), padding_pixels = 1, }) de.defstyle("frame-tiled", { shadow_pixels = 0, highlight_pixels = 0, spacing = 1, }) --de.defstyle("frame-tiled-alt", { -- bar = "none", --}) de.defstyle("frame-floating", { --bar = "shaped", padding_pixels = 0, }) de.defstyle("frame-transient", { --bar = "none", padding_pixels = 0, }) de.defstyle("actnotify", { shadow_colour = "#c04040", highlight_colour = "#c04040", background_colour = "#901010", foreground_colour = "#eeeeee", }) de.defstyle("tab", { de.substyle("*-*-*-unselected-activity", { shadow_colour = "#c04040", highlight_colour = "#c04040", background_colour = "#901010", foreground_colour = "#eeeeee", }), de.substyle("*-*-*-selected-activity", { shadow_colour = "#c04040", highlight_colour = "#c04040", background_colour = "#b03030", foreground_colour = "#ffffff", }), de.substyle("*-*-*-tabnumber", { background_colour = "black", foreground_colour = "green", }), }) de.defstyle("tab-frame", { spacing = 1, }) de.defstyle("tab-frame-floating", { spacing = 0, }) de.defstyle("tab-menuentry", { text_align = "left", }) de.defstyle("tab-menuentry-big", { font = "-*-helvetica-medium-r-normal-*-17-*-*-*-*-*-*-*", padding_pixels = 7, }) de.defstyle("stdisp", { 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", }), }) notion-3+2012042300/etc/lookcommon_emboss.lua000066400000000000000000000044071174530661200205720ustar00rootroot00000000000000-- Common settings for the "emboss" styles de.defstyle("frame", { background_colour = "#000000", -- The special "inherit" value causes setting 'background_colour' -- above not to set padding_colour, but this colour being inherited. padding_colour = "inherit", de.substyle("quasiactive", { -- Something detached from the frame is active padding_colour = "#901010", }), de.substyle("userattr1", { -- For user scripts padding_colour = "#009010", }), border_style = "ridge", padding_pixels = 2, highlight_pixels = 1, shadow_pixels = 1, spacing = 1, }) de.defstyle("frame-tiled", { border_style = "inlaid", padding_pixels = 1, }) --de.defstyle("frame-tiled-alt", { -- bar = "none", --}) de.defstyle("frame-floating", { --bar = "shaped", spacing = 0, }) de.defstyle("frame-transient", { --bar = "none", spacing = 0, }) de.defstyle("actnotify", { shadow_colour = "#600808", highlight_colour = "#c04040", background_colour = "#b03030", foreground_colour = "#ffffff", }) de.defstyle("tab", { de.substyle("*-*-*-unselected-activity", { shadow_colour = "#600808", highlight_colour = "#c04040", background_colour = "#901010", foreground_colour = "#eeeeee", }), de.substyle("*-*-*-selected-activity", { shadow_colour = "#600808", highlight_colour = "#c04040", background_colour = "#b03030", foreground_colour = "#ffffff", }), de.substyle("*-*-*-tabnumber", { background_colour = "black", foreground_colour = "green", }), }) de.defstyle("tab-frame", { spacing = 1, }) de.defstyle("tab-frame-floating", { spacing = 0, }) de.defstyle("tab-frame-transient", { spacing = 0, }) de.defstyle("tab-menuentry", { text_align = "left", highlight_pixels = 0, shadow_pixels = 0, }) de.defstyle("tab-menuentry-big", { font = "-*-helvetica-medium-r-normal-*-17-*-*-*-*-*-*-*", padding_pixels = 7, }) de.defstyle("stdisp", { shadow_pixels = 0, highlight_pixels = 0, text_align = "left", de.substyle("important", { foreground_colour = "green", }), de.substyle("critical", { foreground_colour = "red", }), }) notion-3+2012042300/install-sh000077500000000000000000000127361174530661200155770ustar00rootroot00000000000000#!/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 notion-3+2012042300/ioncore/000077500000000000000000000000001174530661200152205ustar00rootroot00000000000000notion-3+2012042300/ioncore/Makefile000066400000000000000000000032631174530661200166640ustar00rootroot00000000000000## ## Notioncore 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 framedpholder.c \ return.c detach.c screen-notify.c frame-tabs-recalc.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 ioncore_tabnum.lua \ ioncore_quasiact.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 notion-3+2012042300/ioncore/activity.c000066400000000000000000000063671174530661200172340ustar00rootroot00000000000000/* * ion/ioncore/activity.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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, ioncore_g.notifies.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, ioncore_g.notifies.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, ioncore_g.notifies.activity); return nset; } /*EXTL_DOC * Set activity flag of \var{reg}. The \var{how} parameter must be * one of \codestr{set}, \codestr{unset} or \codestr{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 * Iterate over activity list until \var{iterfn} returns \code{false}. * The function is called in protected mode. * This routine returns \code{true} if it reaches the end of list * without this happening. */ EXTL_SAFE EXTL_EXPORT bool ioncore_activity_i(ExtlFn iterfn) { return extl_iter_objlist(iterfn, actlist); } /*EXTL_DOC * Returns first region on activity list. */ EXTL_SAFE EXTL_EXPORT WRegion *ioncore_activity_first() { if(actlist==NULL) return NULL; return (WRegion*)actlist->watch.obj; } ObjList *ioncore_activity_list() { return actlist; } /*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; } notion-3+2012042300/ioncore/activity.h000066400000000000000000000017151174530661200172310ustar00rootroot00000000000000/* * ion/ioncore/activity.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #ifndef ION_IONCORE_ACTIVITY_H #define ION_IONCORE_ACTIVITY_H #include #include #include #include "region.h" /** * Manipulate the 'activity' flag of this region. If the region is already * active the 'activity'-flag will remain off. * * @param sp SET, UNSET or TOGGLE * @returns the new value of the 'activity' flag */ 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 bool ioncore_activity_i(ExtlFn fn); extern WRegion *ioncore_activity_first(); extern bool ioncore_goto_activity(); extern ObjList *ioncore_activity_list(); #endif /* ION_IONCORE_ACTIVITY_H */ notion-3+2012042300/ioncore/attach.c000066400000000000000000000133271174530661200166360ustar00rootroot00000000000000/* * ion/ioncore/attach.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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" #include "focus.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; WRegion *disposeroot; if(!region_ancestor_check(mgr, reg)){ warn(TR("Attempt to make region %s manage its ancestor %s."), region_name(mgr), region_name(reg)); return NULL; } disposeroot=region_disposeroot(reg); if(disposeroot==NULL){ /* Region may not be reparented */ 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)){ WScreen *scr=region_screen_of(reg); warn(TR("Unexpected attach error: " "trying to recover by attaching to screen.")); if(scr!=NULL){ /* Try to attach to screen, to have `reg` attached at least * somewhere. For better recovery, we could try to get * a placeholder for `reg` before we detach it, but this * would add unnecessary overhead in the usual succesfull * case. (This failure is supposed to be _very_ rare!) * We intentionally also do not region_postdetach_dispose * on recovery. */ int flags=(region_may_control_focus(reg) ? MPLEX_ATTACH_SWITCHTO : 0); if(mplex_attach_simple(&scr->mplex, reg, flags)!=NULL) return NULL; } warn(TR("Failed recovery.")); return NULL; } region_postdetach_dispose(reg, disposeroot); return reg; } typedef struct{ ExtlTab tab; WPHolder **sm_ph_p; } WLP; static WRegion *wrap_load(WWindow *par, const WFitParams *fp, WLP *p) { return create_region_load(par, fp, p->tab, p->sm_ph_p); } WRegion *ioncore_newly_created=NULL; static WRegion *doit_load(WRegion *mgr, WWindow *par, const WFitParams *fp, WRegionDoAttachFn *cont, void *cont_param, ExtlTab tab, WPHolder **sm_ph) { WRegion *reg=NULL; if(extl_table_gets_o(tab, "reg", (Obj**)®)){ if(!OBJ_IS(reg, WRegion)) return FALSE; }/*else if(extl_table_is_bool_set(tab, "reg_use_new")){ reg=ioncore_newly_created; if(reg==NULL) return NULL; }*/ if(reg!=NULL){ return doit_reparent(mgr, par, fp, cont, cont_param, reg); }else{ WLP p; p.tab=tab; p.sm_ph_p=sm_ph; return doit_new(mgr, par, fp, cont, cont_param, (WRegionCreateFn*)wrap_load, &p); } } WRegion *region_attach_load_helper(WRegion *mgr, WWindow *par, const WFitParams *fp, WRegionDoAttachFn *fn, void *fn_param, ExtlTab tab, WPHolder **sm_ph) { return doit_load(mgr, par, fp, fn, fn_param, tab, sm_ph); } 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, NULL); }else if(data->type==REGION_ATTACH_REPARENT){ return doit_reparent(mgr, par, fp, fn, fn_param, data->u.reg); }else{ return NULL; } } /*}}}*/ /*{{{ Reparent check etc. */ bool region_ancestor_check(WRegion *dst, WRegion *reg) { WRegion *reg2; /* Check that reg is not a parent or manager of mgr */ for(reg2=dst; reg2!=NULL; reg2=REGION_MANAGER(reg2)){ if(reg2==reg) return FALSE; } for(reg2=REGION_PARENT_REG(dst); reg2!=NULL; reg2=REGION_PARENT_REG(reg2)){ if(reg2==reg) return FALSE; } return TRUE; } void region_postdetach_dispose(WRegion *reg, WRegion *disposeroot) { /* disposeroot should be destroyed (as empty and useless) unless it * still, in fact, is an ancestor of reg. */ if(disposeroot!=reg && region_ancestor_check(reg, disposeroot)) region_dispose(disposeroot); } /*}}}*/ notion-3+2012042300/ioncore/attach.h000066400000000000000000000033411174530661200166360ustar00rootroot00000000000000/* * ion/ioncore/attach.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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 WRegion *region_attach_load_helper(WRegion *mgr, WWindow *par, const WFitParams *fp, WRegionDoAttachFn *fn, void *fn_param, ExtlTab tab, WPHolder **sm_ph); extern bool region_ancestor_check(WRegion *dst, WRegion *reg); extern void region_postdetach_dispose(WRegion *reg, WRegion *disposeroot); #endif /* ION_IONCORE_ATTACH_H */ notion-3+2012042300/ioncore/basicpholder.c000066400000000000000000000036441174530661200200320ustar00rootroot00000000000000/* * ion/ioncore/basicpholder.c * * Copyright (c) Tuomo Valkonen 2005-2009. * * See the included file LICENSE for details. */ #include #include #include #include "basicpholder.h" /*{{{ Init/deinit */ 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, NULL)){ 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); /*}}}*/ notion-3+2012042300/ioncore/basicpholder.h000066400000000000000000000021311174530661200200250ustar00rootroot00000000000000/* * ion/ioncore/basicpholder.h * * Copyright (c) Tuomo Valkonen 2005-2009. * * See the included file LICENSE for details. */ #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 */ notion-3+2012042300/ioncore/binding.c000066400000000000000000000424641174530661200170100ustar00rootroot00000000000000/* * ion/ioncore/binding.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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_any(const WBinding *a, const WBinding *b) { int r=compare_bindings(a, b); if(r==0) r=CVAL(a, b, ksb); 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 || binding->area!=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 && binding->kcb!=0){ #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 || binding->area!=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_any(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_any, &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_IS_PSEUDO(act)){ /* No use trying anything else */ return binding; } if(binding==NULL){ tmp.state=AnyModifier; binding=search_binding(bindmap, &tmp); if(binding==NULL){ tmp.state=state; tmp.kcb=0; tmp.ksb=(act==BINDING_KEYPRESS ? AnyKey : AnyButton); binding=search_binding_any(bindmap, &tmp); if(binding==NULL){ tmp.state=AnyModifier; binding=search_binding_any(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 state&=KNOWN_MODIFIERS_MASK; 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 state&=KNOWN_MODIFIERS_MASK; 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 enum{ BINDING_KEYPRESS, BINDING_BUTTONPRESS, BINDING_BUTTONMOTION, BINDING_BUTTONCLICK, BINDING_BUTTONDBLCLICK, BINDING_SUBMAP_ENTER, BINDING_SUBMAP_RELEASEMOD /*BINDING_SUBMAP_LEAVE*/ }; #define BINDING_IS_PSEUDO(A) \ ((A)==BINDING_SUBMAP_ENTER || (A)==BINDING_SUBMAP_RELEASEMOD) #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 */ notion-3+2012042300/ioncore/bindmaps.c000066400000000000000000000135131174530661200171640ustar00rootroot00000000000000/* * ion/ioncore/bindmaps.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #include #include #include "common.h" #include "conf-bindings.h" #include "binding.h" #include "framep.h" #include "bindmaps.h" #include "global.h" #include "regbind.h" /* * This file contains higher-level bindmap management code */ WBindmap *ioncore_screen_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(screen, "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(screen, "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; } WBindmap *ioncore_create_cycle_bindmap(uint kcb, uint state, ExtlFn cycle, ExtlFn bcycle) { WBindmap *bindmap=create_bindmap(); WBinding b; if(bindmap==NULL) return NULL; b.ksb=XKeycodeToKeysym(ioncore_g.dpy, kcb, 0); b.kcb=kcb; b.state=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 NULL; } if((b.state&ShiftMask)==0 && bcycle!=extl_fn_none()){ b.func=extl_ref_fn(bcycle); b.state|=ShiftMask; bindmap_add_binding(bindmap, &b); } return bindmap; } WBindmap *region_add_cycle_bindmap(WRegion *reg, uint kcb, uint state, ExtlFn cycle, ExtlFn bcycle) { WBindmap *bindmap=ioncore_create_cycle_bindmap(kcb, state, cycle, bcycle); if(bindmap!=NULL){ if(!region_add_bindmap(reg, bindmap)){ bindmap_destroy(bindmap); return NULL; } } return bindmap; } notion-3+2012042300/ioncore/bindmaps.h000066400000000000000000000033001174530661200171620ustar00rootroot00000000000000/* * ion/ioncore/bindmaps.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #include #include "binding.h" #ifndef ION_IONCORE_BINDMAP_H #define ION_IONCORE_BINDMAP_H extern WBindmap *ioncore_screen_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(); extern WBindmap *ioncore_create_cycle_bindmap(uint kcb, uint state, ExtlFn cycle, ExtlFn bcycle); extern WBindmap *region_add_cycle_bindmap(WRegion *reg, uint kcb, uint state, ExtlFn cycle, ExtlFn bcycle); #endif /* ION_IONCORE_BINDMAP_H */ notion-3+2012042300/ioncore/classes.h000066400000000000000000000015631174530661200170330ustar00rootroot00000000000000/* * ion/ioncore/classes.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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); INTRCLASS(WFramedPHolder); INTRCLASS(WGroupPHolder); INTRCLASS(WGroupedPHolder); INTRSTRUCT(WStacking); INTRSTRUCT(WLListNode); INTRSTRUCT(WStackingIterTmp); INTRSTRUCT(WSubmapState); INTRSTRUCT(WRegionAttachData); INTRSTRUCT(WRQGeomParams); #endif /* ION_IONCORE_CLASSES_H */ notion-3+2012042300/ioncore/clientwin.c000066400000000000000000001166401174530661200173700ustar00rootroot00000000000000/* * ion/ioncore/clientwin.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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" #include "return.h" #include "conf.h" #include "group.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 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_RSZINC| \ CLIENTWIN_PROP_I_MAXSIZE| \ CLIENTWIN_PROP_I_MINSIZE| \ CLIENTWIN_PROP_I_ASPECT| \ CLIENTWIN_PROP_I_RSZINC) #define DO_SZH(NAME, FLAG, IFLAG, SZHFLAG, W, H, C) \ if(extl_table_is_bool_set(tab, "ignore_" NAME)){ \ cwin->flags|=IFLAG; \ }else if(extl_table_gets_t(tab, NAME, &tab2)){ \ if(extl_table_gets_i(tab2, "w", &i1) && \ extl_table_gets_i(tab2, "h", &i2)){ \ cwin->size_hints.W=i1; \ cwin->size_hints.H=i2; \ C \ cwin->size_hints.flags|=SZHFLAG; \ cwin->flags|=FLAG; \ } \ extl_unref_table(tab2); \ } static void clientwin_get_winprops(WClientWin *cwin) { ExtlTab tab, tab2; char *s; int i1, i2; tab=ioncore_get_winprop(cwin); 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_is_bool_set(tab, "lazy_resize")) cwin->flags|=CLIENTWIN_PROP_LAZY_RESIZE; DO_SZH("max_size", CLIENTWIN_PROP_MAXSIZE, CLIENTWIN_PROP_I_MAXSIZE, PMaxSize, max_width, max_height, { }); DO_SZH("min_size", CLIENTWIN_PROP_MINSIZE, CLIENTWIN_PROP_I_MINSIZE, PMinSize, min_width, min_height, { }); DO_SZH("resizeinc", CLIENTWIN_PROP_RSZINC, CLIENTWIN_PROP_I_RSZINC, PResizeInc, width_inc, height_inc, { }); DO_SZH("aspect", CLIENTWIN_PROP_ASPECT, CLIENTWIN_PROP_I_ASPECT, PAspect, min_aspect.x, min_aspect.y, { cwin->size_hints.max_aspect.x=i1; cwin->size_hints.max_aspect.y=i2; }); if(extl_table_is_bool_set(tab, "ignore_cfgrq")) cwin->flags|=CLIENTWIN_PROP_IGNORE_CFGRQ; if(extl_table_gets_s(tab, "orientation", &s)){ if(strcmp(s, "vertical")==0) cwin->flags|=CLIENTWIN_PROP_O_VERT; else if(strcmp(s, "horizontal")==0) cwin->flags|=CLIENTWIN_PROP_O_HORIZ; free(s); } } 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_I_MAXSIZE){ cwin->size_hints.flags&=~PMaxSize; }else 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_I_MINSIZE){ cwin->size_hints.flags&=~PMinSize; }else 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_I_ASPECT){ cwin->size_hints.flags&=~PAspect; }else 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_I_RSZINC){ cwin->size_hints.flags&=~PResizeInc; }else if(cwin->flags&CLIENTWIN_PROP_RSZINC){ cwin->size_hints.width_inc=tmp.width_inc; cwin->size_hints.height_inc=tmp.height_inc; 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->n_cmapwins=0; cwin->cmap=attr->colormap; cwin->cmaps=NULL; cwin->cmapwins=NULL; cwin->n_cmapwins=0; cwin->event_mask=IONCORE_EVENTMASK_CLIENTWIN; 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); netwm_update_allowed_actions(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)); } 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; void *mrshpm[2]; param.dockapp=FALSE; /* 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); if(!XGetWindowAttributes(ioncore_g.dpy, win, &attr)){ if(maprq) warn(TR("Window %#x disappeared."), win); goto fail2; } /* Is it a dockapp? */ hints=XGetWMHints(ioncore_g.dpy, win); if(hints!=NULL){ if(hints->flags&StateHint) init_state=hints->initial_state; if(!param.dockapp && init_state==WithdrawnState && hints->flags&IconWindowHint && hints->icon_window!=None){ Window icon_win=hints->icon_window; XWindowAttributes icon_attr; if(!XGetWindowAttributes(ioncore_g.dpy, icon_win, &icon_attr)){ if(maprq) warn(TR("Window %#x disappeared."), win); XFree((void*)hints); goto fail2; } if(!maprq){ if(attr.map_state==IsViewable){ /* The dockapp might be displaying its "main" window if no * wm that understands dockapps has been managing it. */ XUnmapWindow(ioncore_g.dpy, win); param.dockapp=TRUE; }else{ /* Main window is unmapped on initial scan, but icon window * is mapped. Let's hope it's a dockapp left by e.g. us. */ if(icon_attr.map_state==IsViewable) param.dockapp=TRUE; } }else{ param.dockapp=TRUE; } if(param.dockapp){ char **p=NULL; int n=0; xwindow_unmanaged_selectinput(win, 0); xwindow_unmanaged_selectinput(icon_win, StructureNotifyMask); /* Copy WM_CLASS as _ION_DOCKAPP_HACK */ p=xwindow_get_text_property(win, XA_WM_CLASS, &n); if(p!=NULL){ xwindow_set_text_property(icon_win, ioncore_g.atom_dockapp_hack, (const char **)p, n); XFreeStringList(p); }else{ const char *pdummy[2]={"unknowndockapp", "UnknownDockapp"}; xwindow_set_text_property(icon_win, ioncore_g.atom_dockapp_hack, pdummy, 2); } win=icon_win; attr=icon_attr; } } XFree((void*)hints); } /* Do we really want to manage it? */ if(!param.dockapp && (attr.override_redirect || (!maprq && attr.map_state!=IsViewable))){ goto fail2; } attr.width=maxof(attr.width, 1); attr.height=maxof(attr.height, 1); /* 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.jumpto=extl_table_is_bool_set(cwin->proptab, "jumpto"); param.switchto=(init_state!=IconicState && (param.jumpto || clientwin_get_switchto(cwin))); param.gravity=(cwin->size_hints.flags&PWinGravity ? cwin->size_hints.win_gravity : ForgetGravity); param.tfor=clientwin_get_transient_for(cwin); if(!extl_table_gets_b(cwin->proptab, "userpos", ¶m.userpos)) param.userpos=(cwin->size_hints.flags&USPosition); 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(maprq) netwm_check_manage_user_time(cwin, ¶m); mrshpm[0]=cwin; mrshpm[1]=¶m; if(!hook_call_alt(clientwin_do_manage_alt, &mrshpm, (WHookMarshall*)do_manage_mrsh, (WHookMarshallExtl*)do_manage_mrsh_extl)){ 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)){ /* Check for focus_next==NULL does not play nicely with * pointer_focus_hack. */ /*if(param.jumpto && ioncore_g.focus_next==NULL)*/ if(param.jumpto && !region_manager_is_focusnext((WRegion*)cwin)) 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){ region_pointer_focus_hack(&cwin->region); 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); 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) { cwin->flags|=CLIENTWIN_UNMAP_RQ; /* First try a graceful chain-dispose */ if(!region_rqdispose((WRegion*)cwin)){ /* But force dispose anyway */ region_dispose((WRegion*)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 \code{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); } void 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()); }else{ warn(TR("Client does not support the WM_DELETE protocol.")); } } /*}}}*/ /*{{{ 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) { region_pointer_focus_hack(&cwin->region); 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 postpone_resize(WClientWin *cwin) { return cwin->state == IconicState && cwin->flags&CLIENTWIN_PROP_LAZY_RESIZE; } 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); if(np==NULL && !changes) return TRUE; if(np!=NULL){ region_unset_parent((WRegion*)cwin); /** * update netwm properties before mapping, because some apps check the * netwm state directly when mapped. * * also, update netwm properties after setting the parent, because * the new state of _NET_WM_STATE_FULLSCREEN is determined based on * the parent of the cwin. */ region_set_parent((WRegion*)cwin, np); netwm_update_state(cwin); do_reparent_clientwin(cwin, np->win, geom.x, geom.y); sendconfig_clientwin(cwin); if(!REGION_IS_FULLSCREEN(cwin)) cwin->flags&=~CLIENTWIN_FS_RQ; } if (postpone_resize(cwin)) return TRUE; REGION_GEOM(cwin)=geom; 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(); region_finalise_focusing((WRegion*)cwin, cwin->win, warp, stmp); send_clientmsg(cwin->win, ioncore_g.atom_wm_take_focus, stmp); }else{ region_finalise_focusing((WRegion*)cwin, cwin->win, warp, CurrentTime); } 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); } } static int clientwin_orientation(WClientWin *cwin) { return (cwin->flags&CLIENTWIN_PROP_O_VERT ? REGION_ORIENTATION_VERTICAL : (cwin->flags&CLIENTWIN_PROP_O_HORIZ ? REGION_ORIENTATION_HORIZONTAL : REGION_ORIENTATION_NONE)); } /*}}}*/ /*{{{ 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, **p2=NULL, *wrole=NULL; int n=0, n2=0, n3=0, tmp=0; Window tforwin=None; ExtlTab tab; bool dockapp_hack=FALSE; p=xwindow_get_text_property(cwin->win, XA_WM_CLASS, &n); p2=xwindow_get_text_property(cwin->win, ioncore_g.atom_dockapp_hack, &n2); dockapp_hack=(n2>0); if(p==NULL){ /* Some dockapps do actually have WM_CLASS, so use it. */ p=p2; n=n2; p2=NULL; } wrole=xwindow_get_string_property(cwin->win, ioncore_g.atom_wm_window_role, &n3); 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(XGetTransientForHint(ioncore_g.dpy, cwin->win, &tforwin) && tforwin!=None){ extl_table_sets_b(tab, "is_transient", TRUE); } if(dockapp_hack) extl_table_sets_b(tab, "is_dockapp", TRUE); if(p!=NULL) XFreeStringList(p); if(p2!=NULL) XFreeStringList(p2); if(wrole!=NULL) free(wrole); return tab; } /*}}}*/ /*{{{ ConfigureRequest */ static bool check_fs_cfgrq(WClientWin *cwin, XConfigureRequestEvent *ev) { /* check full screen request */ if((ev->value_mask&(CWWidth|CWHeight))==(CWWidth|CWHeight)){ WRegion *grp=region_groupleader_of((WRegion*)cwin); WScreen *scr=clientwin_fullscreen_chkrq(cwin, ev->width, ev->height); if(scr!=NULL && REGION_MANAGER(grp)!=(WRegion*)scr){ bool sw=clientwin_fullscreen_may_switchto(cwin); cwin->flags|=CLIENTWIN_FS_RQ; if(!region_fullscreen_scr(grp, scr, sw)) cwin->flags&=~CLIENTWIN_FS_RQ; return TRUE; } } return FALSE; } WMPlex* find_mplexer(WRegion *cwin) { if (cwin == NULL) return NULL; if(obj_is((Obj*)cwin, &CLASSDESCR(WMPlex))) return (WMPlex*) cwin; return find_mplexer(cwin->manager); } /* Returns whether anything was actually changed. */ static bool check_normal_cfgrq(WClientWin *cwin, XConfigureRequestEvent *ev) { bool result = FALSE; 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; rq.flags&=~REGION_RQGEOM_WEAK_W; } if(ev->value_mask&CWY){ rq.geom.y=ev->y+gdy; rq.flags&=~REGION_RQGEOM_WEAK_Y; rq.flags&=~REGION_RQGEOM_WEAK_H; } region_rqgeom((WRegion*)cwin, &rq, NULL); result = TRUE; } if(ev->value_mask&CWStackMode){ switch(ev->detail){ case Above: region_set_activity((WRegion*) cwin, SETPARAM_SET); /* TODO we should be more conservative here - but what does/should * region_set_activity return? */ result = TRUE; break; case Below: case TopIf: case BottomIf: case Opposite: /* unimplemented */ break; } } return result; } void clientwin_handle_configure_request(WClientWin *cwin, XConfigureRequestEvent *ev) { if(ev->value_mask&CWBorderWidth) cwin->orig_bw=ev->border_width; cwin->flags|=CLIENTWIN_NEED_CFGNTFY; if(!(cwin->flags&CLIENTWIN_PROP_IGNORE_CFGRQ)){ if(!check_fs_cfgrq(cwin, ev)) check_normal_cfgrq(cwin, ev); } 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; } static void do_sm(ExtlTab tab) { SMAddCallback *add_cb; SMCfgCallback *cfg_cb; WPHolder *ph; ioncore_get_sm_callbacks(&add_cb, &cfg_cb); if(add_cb!=NULL){ ph=ioncore_get_load_pholder(); if(ph!=NULL){ if(!add_cb(ph, tab)) destroy_obj((Obj*)ph); } } } 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=FALSE; 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){ do_sm(tab); return NULL; } /* Found it! */ if(!XGetWindowAttributes(ioncore_g.dpy, win, &attr)){ warn(TR("Window %#x disappeared."), win); return NULL; } if(attr.root!=region_root_of((WRegion*)par)) 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; } 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_orientation, (DynFun*)clientwin_orientation}, {(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); /*}}}*/ notion-3+2012042300/ioncore/clientwin.h000066400000000000000000000064441174530661200173750ustar00rootroot00000000000000/* * ion/ioncore/clientwin.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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_TRANSPARENT 0x00020 #define CLIENTWIN_PROP_IGNORE_CFGRQ 0x00040 #define CLIENTWIN_PROP_LAZY_RESIZE 0x00080 #define CLIENTWIN_PROP_MINSIZE 0x00100 #define CLIENTWIN_PROP_MAXSIZE 0x00200 #define CLIENTWIN_PROP_ASPECT 0x00400 #define CLIENTWIN_PROP_RSZINC 0x00800 #define CLIENTWIN_PROP_I_MINSIZE 0x01000 #define CLIENTWIN_PROP_I_MAXSIZE 0x02000 #define CLIENTWIN_PROP_I_ASPECT 0x04000 #define CLIENTWIN_PROP_I_RSZINC 0x08000 #define CLIENTWIN_USE_NET_WM_NAME 0x10000 /* full screen mode has been requested by the client window itself */ #define CLIENTWIN_FS_RQ 0x20000 #define CLIENTWIN_UNMAP_RQ 0x40000 #define CLIENTWIN_NEED_CFGNTFY 0x80000 #define CLIENTWIN_PROP_O_VERT 0x100000 #define CLIENTWIN_PROP_O_HORIZ 0x200000 DECLCLASS(WClientWin){ WRegion region; int flags; int state; int event_mask; Window win; int orig_bw; Colormap cmap; Colormap *cmaps; Window *cmapwins; int n_cmapwins; XSizeHints size_hints; 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 void 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 */ notion-3+2012042300/ioncore/colormap.c000066400000000000000000000116241174530661200172040ustar00rootroot00000000000000/* * ion/ioncore/colormap.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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); } } /*}}}*/ notion-3+2012042300/ioncore/colormap.h000066400000000000000000000012131174530661200172020ustar00rootroot00000000000000/* * ion/ioncore/colormap.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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 */ notion-3+2012042300/ioncore/common.h000066400000000000000000000010021174530661200166520ustar00rootroot00000000000000/* * ion/ioncore/common.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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 */ notion-3+2012042300/ioncore/conf-bindings.c000066400000000000000000000270251174530661200201120ustar00rootroot00000000000000/* * ion/ioncore/conf-bindings.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #include #define XK_MISCELLANY #include #ifdef CF_SUN_F1X_REMAP #include #endif #include #include "common.h" #include "binding.h" #include #include "global.h" #include #include "conf-bindings.h" #include "bindmaps.h" #include "ioncore.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=SunXK_F36; else if(keysym==XK_F12) keysym=SunXK_F37; #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){ ioncore_warn_nolog("%s: %s", 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=0; 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}, {"submap_enter", BINDING_SUBMAP_ENTER}, {"submap_wait", BINDING_SUBMAP_RELEASEMOD}, /*{"submap_leave", BINDING_SUBMAP_LEAVE},*/ {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(!BINDING_IS_PSEUDO(action)){ 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; } /*}}}*/ notion-3+2012042300/ioncore/conf-bindings.h000066400000000000000000000011701174530661200201100ustar00rootroot00000000000000/* * ion/ioncore/conf-bindings.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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 */ notion-3+2012042300/ioncore/conf.c000066400000000000000000000242421174530661200163150ustar00rootroot00000000000000/* * ion/ioncore/conf.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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 }; static bool get_winprop_fn_set=FALSE; static ExtlFn get_winprop_fn; static bool get_layout_fn_set=FALSE; static ExtlFn get_layout_fn; /*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 * \codestr{last}, \codestr{next}, (for after current), * or \codestr{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{edge_resistance} & (integer) Resize edge resistance in pixels. \\ * \var{framed_transients} & (boolean) Put transients in nested frames. \\ * \var{float_placement_method} & (string) How to place floating frames. * One of \codestr{udlr} (up-down, then left-right), * \codestr{lrud} (left-right, then up-down), or * \codestr{random}. \\ * \var{float_placement_padding} & (integer) Pixels between frames when * \var{float_placement_method} is \codestr{udlr} or * \codestr{lrud}. \\ * \var{mousefocus} & (string) Mouse focus mode: * \codestr{disabled} or \codestr{sloppy}. \\ * \var{unsqueeze} & (boolean) Auto-unsqueeze transients/menus/queries/etc. \\ * \var{autoraise} & (boolean) Autoraise regions in groups on goto. \\ * \var{usertime_diff_current} & (integer) Controls switchto timeout. \\ * \var{usertime_diff_new} & (integer) Controls switchto timeout. \\ * \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; ExtlFn fn; 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)); extl_table_gets_b(tab, "unsqueeze", &(ioncore_g.unsqueeze_enabled)); extl_table_gets_b(tab, "autoraise", &(ioncore_g.autoraise)); 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_s(tab, "mousefocus", &tmp)){ if(strcmp(tmp, "disabled")==0) ioncore_g.no_mousefocus=TRUE; else if(strcmp(tmp, "sloppy")==0) ioncore_g.no_mousefocus=FALSE; free(tmp); } if(extl_table_gets_i(tab, "dblclick_delay", &dd)) ioncore_g.dblclick_delay=maxof(0, dd); if(extl_table_gets_i(tab, "usertime_diff_current", &dd)) ioncore_g.usertime_diff_current=maxof(0, dd); if(extl_table_gets_i(tab, "usertime_diff_new", &dd)) ioncore_g.usertime_diff_new=maxof(0, dd); ioncore_set_moveres_accel(tab); ioncore_groupws_set(tab); /* Internal -- therefore undocumented above */ if(extl_table_gets_f(tab, "_get_winprop", &fn)){ if(get_winprop_fn_set) extl_unref_fn(get_winprop_fn); get_winprop_fn=fn; get_winprop_fn_set=TRUE; } if(extl_table_gets_f(tab, "_get_layout", &fn)){ if(get_layout_fn_set) extl_unref_fn(get_layout_fn); get_layout_fn=fn; get_layout_fn_set=TRUE; } } /*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_b(tab, "framed_transients", ioncore_g.framed_transients); extl_table_sets_b(tab, "unsqueeze", ioncore_g.unsqueeze_enabled); extl_table_sets_b(tab, "autoraise", ioncore_g.autoraise); extl_table_sets_s(tab, "frame_default_index", stringintmap_key(frame_idxs, ioncore_g.frame_default_index, NULL)); extl_table_sets_s(tab, "mousefocus", (ioncore_g.no_mousefocus ? "disabled" : "sloppy")); ioncore_get_moveres_accel(tab); ioncore_groupws_get(tab); return tab; } ExtlTab ioncore_get_winprop(WClientWin *cwin) { ExtlTab tab=extl_table_none(); if(get_winprop_fn_set){ extl_protect(NULL); extl_call(get_winprop_fn, "o", "t", cwin, &tab); extl_unprotect(NULL); } return tab; } ExtlTab ioncore_get_layout(const char *layout) { ExtlTab tab=extl_table_none(); if(get_layout_fn_set){ extl_protect(NULL); extl_call(get_layout_fn, "s", "t", layout, &tab); extl_unprotect(NULL); } return tab; } /*EXTL_DOC * Get important directories (the fields \var{userdir}, * \var{sessiondir}, \var{searchpath} in the returned table). */ 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 (the fields \var{sessiondir}, \var{searchpath} * of \var{tab}). */ 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_notion"; ret=extl_read_config(cfgfile, ".", TRUE); unset+=(ioncore_screen_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); } notion-3+2012042300/ioncore/conf.h000066400000000000000000000005741174530661200163240ustar00rootroot00000000000000/* * ion/ioncore/conf.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #ifndef ION_IONCORE_CONF_H #define ION_IONCORE_CONF_H extern bool ioncore_read_main_config(const char *cfgfile); extern ExtlTab ioncore_get_winprop(WClientWin *cwin); extern ExtlTab ioncore_get_layout(const char *str); #endif /* ION_IONCORE_CONF_H */ notion-3+2012042300/ioncore/cursor.c000066400000000000000000000010551174530661200167020ustar00rootroot00000000000000/* * ion/ioncore/cursor.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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]; } notion-3+2012042300/ioncore/cursor.h000066400000000000000000000010471174530661200167100ustar00rootroot00000000000000/* * ion/ioncore/cursor.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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 */ notion-3+2012042300/ioncore/detach.c000066400000000000000000000140561174530661200166220ustar00rootroot00000000000000/* * ion/ioncore/detach.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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; } } static bool ioncore_do_detach(WRegion *reg, WGroup *grp, WFrameMode framemode, uint framelevel) { WGroupAttachParams ap=GROUPATTACHPARAMS_INIT; WRegionAttachData data; WPHolder *ph; bool newph=FALSE; bool ret; ap.switchto_set=TRUE; ap.switchto=region_may_control_focus(reg); data.type=REGION_ATTACH_REPARENT; data.u.reg=reg; ph=region_unset_get_return(reg); if(ph==NULL){ ph=region_make_return_pholder(reg); newph=TRUE; } if(framemode!=FRAME_MODE_UNKNOWN){ /* TODO: remove/obsolete this special case */ WFramedParam fpa=FRAMEDPARAM_INIT; fpa.mode=framemode; fpa.inner_geom_gravity_set=TRUE; fpa.gravity=ForgetGravity; ap.geom_weak_set=TRUE; ap.geom_weak=0; ap.level_set=TRUE; ap.level=framelevel+1; get_relative_geom(&fpa.inner_geom, reg, (WRegion*)grp); ret=(region_attach_framed((WRegion*)grp, &fpa, (WRegionAttachFn*)group_do_attach, &ap, &data)!=NULL); }else{ WStacking *st=ioncore_find_stacking(reg); ap.level_set=TRUE; ap.level=framelevel+1; if(st!=NULL){ ap.szplcy=st->szplcy; ap.szplcy_set=TRUE; /* Hack for modal detached queries, while transients become * non-modal detached. */ if(st->level>STACKING_LEVEL_MODAL1) ap.level=st->level; } ap.geom_set=TRUE; get_relative_geom(&ap.geom, reg, (WRegion*)grp); ret=(group_do_attach(grp, &ap, &data)!=NULL); } if(!ret && newph) destroy_obj((Obj*)ph); else if(!region_do_set_return(reg, ph)) destroy_obj((Obj*)ph); return ret; } static WRegion *check_mplex(WRegion *reg, WFrameMode *mode) { WMPlex *mplex=REGION_MANAGER_CHK(reg, WMPlex); if(OBJ_IS(reg, WWindow) || mplex==NULL){ *mode=FRAME_MODE_UNKNOWN; return reg; } *mode=FRAME_MODE_FLOATING; if(OBJ_IS(mplex, WFrame)){ WFrameMode mode2=frame_mode((WFrame*)mplex); if(framemode_unalt(mode2)==FRAME_MODE_TRANSIENT) *mode=mode2; } return (WRegion*)mplex; } static WGroup *find_group(WRegion *reg, uint *level) { WRegion *mgr=REGION_MANAGER(reg); while(mgr!=NULL){ reg=mgr; mgr=REGION_MANAGER(mgr); if(OBJ_IS(mgr, WGroup)){ WStacking *st=ioncore_find_stacking((WRegion*)reg); if(st!=NULL) *level=st->level; break; } } return (WGroup*)mgr; } bool ioncore_detach(WRegion *reg, int sp) { WFrameMode mode; WGroup *grp; bool set, nset; uint level=STACKING_LEVEL_NORMAL; reg=region_groupleader_of(reg); grp=find_group(check_mplex(reg, &mode), &level); /* reg is only considered detached if there's no higher-level group * to attach to, thus causing 'toggle' to cycle. */ set=(grp==NULL); nset=libtu_do_setparam(sp, set); if(!XOR(nset, set)) return set; if(!set){ return ioncore_do_detach(reg, grp, mode, level); }else{ WPHolder *ph=region_get_return(reg); if(ph!=NULL){ if(!pholder_attach_mcfgoto(ph, PHOLDER_ATTACH_SWITCHTO, reg)){ warn(TR("Failed to reattach.")); return TRUE; } region_unset_return(reg); } return FALSE; } } /*EXTL_DOC * Detach or reattach \var{reg} or any group it is the leader of * (see \fnref{WRegion.groupleader_of}), depending on whether \var{how} * is \codestr{set}, \codestr{unset} or \codestr{toggle}. If this * region is not a window, it is put into a frame. * * Detaching a region means having it managed by its nearest ancestor * \type{WGroup}. Reattaching means having it managed where it used * to be managed, if a ``return placeholder'' exists. * * Additionally, setting \var{how} to \codestr{forget}, can be used to * clear this return placeholder of the group leader of \var{reg}. */ EXTL_EXPORT_AS(ioncore, detach) bool ioncore_detach_extl(WRegion *reg, const char *how) { if(how==NULL) how="set"; if(strcmp(how, "forget")==0){ region_unset_return(region_groupleader_of(reg)); return FALSE; } return ioncore_detach(reg, libtu_string_to_setparam(how)); } void do_unsqueeze(WRegion *reg) { WSizeHints h; if(OBJ_IS(reg, WScreen)) return; region_size_hints(reg, &h); if(!h.min_set) return; if(h.min_width<=REGION_GEOM(reg).w && h.min_height<=REGION_GEOM(reg).h){ return; } ioncore_detach(reg, SETPARAM_SET); } /*EXTL_DOC * Try to detach \var{reg} if it fits poorly in its * current location. This function does not do anything, * unless \var{override} is set or the \var{unsqueeze} option * of \fnref{ioncore.set} is set. */ EXTL_EXPORT void ioncore_unsqueeze(WRegion *reg, bool override) { if(ioncore_g.unsqueeze_enabled || override) do_unsqueeze(reg); } notion-3+2012042300/ioncore/detach.h000066400000000000000000000005431174530661200166230ustar00rootroot00000000000000/* * ion/ioncore/detach.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #ifndef ION_IONCORE_DETACH_H #define ION_IONCORE_DETACH_H #include "region.h" extern bool ioncore_detach(WRegion *reg, int sp); extern void ioncore_unsqueeze(WRegion *reg, bool override); #endif /* ION_IONCORE_DETACH_H */ notion-3+2012042300/ioncore/dummywc.h000066400000000000000000000017351174530661200170640ustar00rootroot00000000000000/* * ion/ioncore/dummywc.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ /* 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 */ notion-3+2012042300/ioncore/event.c000066400000000000000000000116501174530661200165100ustar00rootroot00000000000000/* * ion/ioncore/event.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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; 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 > IONCORE_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_flushfocus() { WRegion *next; bool warp; if(ioncore_g.input_mode!=IONCORE_INPUTMODE_NORMAL) return; next=ioncore_g.focus_next; warp=ioncore_g.warp_next; if(next==NULL) return; 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. */ if(warp) skip_enterwindow(); } /*}}}*/ /*{{{ 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(); if(QLength(ioncore_g.dpy)==0){ XSync(ioncore_g.dpy, False); if(QLength(ioncore_g.dpy)==0){ ioncore_flushfocus(); XSync(ioncore_g.dpy, False); if(QLength(ioncore_g.dpy)==0){ mainloop_select(); continue; } } } ioncore_x_connection_handler(ioncore_g.conn, NULL); } } /*}}}*/ notion-3+2012042300/ioncore/event.h000066400000000000000000000036441174530661200165210ustar00rootroot00000000000000/* * ion/ioncore/event.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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) #define IONCORE_EVENTMASK_SCREEN (FocusChangeMask|EnterWindowMask| \ KeyPressMask|KeyReleaseMask| \ ButtonPressMask|ButtonReleaseMask) #define IONCORE_CLOCK_SKEW_MS 30000 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 */ notion-3+2012042300/ioncore/eventh.c000066400000000000000000000274511174530661200166660ustar00rootroot00000000000000/* * ion/ioncore/eventh.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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)); 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; default: return FALSE; } 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 */ void ioncore_handle_enter_window(XEvent *ev) { XEnterWindowEvent *eev=&(ev->xcrossing); WRegion *reg=NULL; if(ioncore_g.input_mode!=IONCORE_INPUTMODE_NORMAL || ioncore_g.no_mousefocus){ 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(ioncore_g.focus_next!=NULL && ioncore_g.focus_next_sourcewindow, 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; /* 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); if(ioncore_g.focus_next!=NULL && ioncore_g.focus_next_sourcedetail==NotifyPointerRoot || ev->detail==NotifyDetailNone) && ev->window==region_root_of(reg) /* OBJ_IS(reg, WRootWin) */){ /* Restore focus if it was returned to a root window and we don't * know of a pending focus change. */ if(pointer_in_root(ev->window)){ region_set_focus(reg); ioncore_g.focus_next_source=IONCORE_FOCUSNEXT_FALLBACK; } }else{ /* Something got the focus, don't use fallback. */ ioncore_g.focus_next=NULL; } } 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)); 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)); } /*}}}*/ notion-3+2012042300/ioncore/eventh.h000066400000000000000000000021231174530661200166600ustar00rootroot00000000000000/* * ion/ioncore/eventh.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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); 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 */ notion-3+2012042300/ioncore/exec.c000066400000000000000000000121611174530661200163110ustar00rootroot00000000000000/* * ion/ioncore/exec.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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_display(int xscr) { char *tmp, *ptr; char *display; /* Set up $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);*/ } void ioncore_setup_environ(const WExecP *p) { /* Set up $DISPLAY */ ioncore_setup_display(p->target!=NULL ? region_rootwin_of(p->target)->xscr : -1); /* Set up working directory */ if(p->wd!=NULL){ if(chdir(p->wd)!=0) warn_err_obj(p->wd); } } WHook *ioncore_exec_environ_hook=NULL; static void setup_exec(void *execp) { hook_call_p(ioncore_exec_environ_hook, execp, NULL); #ifndef CF_NO_SETPGID setpgid(0, 0); #endif ioncore_g.dpy=NULL; } EXTL_EXPORT int ioncore_do_exec_on(WRegion *reg, const char *cmd, const char *wd, ExtlFn errh) { WExecP p; p.target=reg; p.cmd=cmd; 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_on(NULL, cmd, NULL, extl_fn_none()); } /*EXTL_DOC * Run \var{cmd} in directory \var{wd} with a read pipe connected to its * stdout and stderr. * When data is received through one of these pipes, \var{h} or \var{errh} * is called with that data. When the pipe is closed, the handler is called * with \code{nil} argument. The PID of the new process is returned, or * -1 on error. */ EXTL_SAFE EXTL_EXPORT int ioncore_popen_bgread(const char *cmd, ExtlFn h, ExtlFn errh, const char *wd) { WExecP p; p.target=NULL; p.wd=wd; p.cmd=cmd; 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_display(-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(); } /*}}}*/ notion-3+2012042300/ioncore/exec.h000066400000000000000000000025571174530661200163260ustar00rootroot00000000000000/* * ion/ioncore/exec.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #ifndef ION_IONCORE_EXEC_H #define ION_IONCORE_EXEC_H #include #include #include "common.h" #include "rootwin.h" enum{ IONCORE_SM_RESIGN, IONCORE_SM_SHUTDOWN, IONCORE_SM_RESTART, IONCORE_SM_RESTART_OTHER, IONCORE_SM_SNAPSHOT }; INTRSTRUCT(WExecP); DECLSTRUCT(WExecP){ WRegion *target; const char *cmd; const char *wd; }; extern bool ioncore_exec(const char *cmd); extern int ioncore_do_exec_on(WRegion *reg, const char *cmd, const char *wd, ExtlFn errh); extern bool ioncore_popen_bgread(const char *cmd, ExtlFn h, ExtlFn errh, const char *wd); extern void ioncore_setup_environ(const WExecP *p); extern void ioncore_setup_display(int xscr); 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(); /* const WExecP* parameter */ extern WHook *ioncore_exec_environ_hook; #endif /* ION_IONCORE_EXEC_H */ notion-3+2012042300/ioncore/extlconv.c000066400000000000000000000051211174530661200172250ustar00rootroot00000000000000/* * ion/ioncore/extlconv.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #include #include #include "common.h" #include "extlconv.h" /*{{{ Object list */ bool extl_iter_objlist_(ExtlFn fn, ObjIterator *iter, void *st) { Obj *obj; while(1){ obj=iter(st); if(obj==NULL) break; if(!extl_iter_obj(fn, obj)) return FALSE; } return TRUE; } bool extl_iter_objlist(ExtlFn fn, ObjList *list) { ObjListIterTmp tmp; objlist_iter_init(&tmp, list); return extl_iter_objlist_(fn, (ObjIterator*)objlist_iter, &tmp); } bool extl_iter_obj(ExtlFn fn, Obj *obj) { bool ret1, ret2=FALSE; extl_protect(NULL); ret1=extl_call(fn, "o", "b", obj, &ret2); extl_unprotect(NULL); return (ret1 && ret2); } /*}}}*/ /*{{{ 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; } /*}}}*/ /*{{{ Size policy */ bool extl_table_gets_sizepolicy(ExtlTab tab, const char *nam, WSizePolicy *szplcy) { char *tmpstr; bool ret=FALSE; if(extl_table_gets_s(tab, nam, &tmpstr)){ ret=string2sizepolicy(tmpstr, szplcy); free(tmpstr); } return ret; } /*}}}*/ notion-3+2012042300/ioncore/extlconv.h000066400000000000000000000022261174530661200172350ustar00rootroot00000000000000/* * ion/ioncore/extlconv.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #ifndef ION_IONCORE_EXTLCONV_H #define ION_IONCORE_EXTLCONV_H #include #include #include #include #include "common.h" #include "region.h" #include "sizepolicy.h" extern bool extl_iter_obj(ExtlFn fn, Obj *obj); extern bool extl_iter_objlist_(ExtlFn fn, ObjIterator *iter, void *st); extern bool extl_iter_objlist(ExtlFn fn, ObjList *list); 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); extern bool extl_table_gets_sizepolicy(ExtlTab tab, const char *nam, WSizePolicy *szplcy); #endif /* ION_IONCORE_EXTLCONV_H */ notion-3+2012042300/ioncore/extlrx.c000066400000000000000000000012671174530661200167200ustar00rootroot00000000000000/* * ion/ioncore/extlrx.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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); } /*}}}*/ notion-3+2012042300/ioncore/float-placement.c000066400000000000000000000077361174530661200204540ustar00rootroot00000000000000/* * ion/ioncore/float-placement.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #include #include "common.h" #include "group.h" #include "float-placement.h" WFloatPlacement ioncore_placement_method=PLACEMENT_LRUD; int ioncore_placement_padding=1; 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); } static bool st_filt(WStacking *st, void *lvl) { uint level=*(uint*)lvl; return (st->reg!=NULL && REGION_IS_MAPPED(st->reg) && st->level==level); } #define FOR_ALL_STACKING_NODES(VAR, WS, LVL, TMP) \ for(stacking_iter_init(&(TMP), group_get_stacking(ws), \ st_filt, &LVL), \ VAR=stacking_iter_nodes(&(TMP)); \ VAR!=NULL; \ VAR=stacking_iter_nodes(&(TMP))) #define IGNORE_ST(ST, WS) ((ST)->reg==NULL || (ST)==(WS)->bottom) static WRegion* is_occupied(WGroup *ws, uint level, const WRectangle *r) { WStackingIterTmp tmp; WStacking *st; WRectangle p; FOR_ALL_STACKING_NODES(st, ws, level, tmp){ 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, uint level, int x) { WRectangle p; int retx=REGION_GEOM(ws).x+REGION_GEOM(ws).w; WStackingIterTmp tmp; WStacking *st; FOR_ALL_STACKING_NODES(st, ws, level, tmp){ 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, level, r.x)+ioncore_placement_padding; r.y=0; } } }else{ while(r.yx=r.x; g->y=r.y; return TRUE; }else{ r.y=next_least_y(ws, level, r.y)+ioncore_placement_padding; r.x=0; } } } return FALSE; } void group_calc_placement(WGroup *ws, uint level, WRectangle *geom) { if(ioncore_placement_method!=PLACEMENT_RANDOM){ if(tiling_placement(ws, level, geom)) return; } random_placement(REGION_GEOM(ws), geom); } notion-3+2012042300/ioncore/float-placement.h000066400000000000000000000011101174530661200204350ustar00rootroot00000000000000/* * ion/ioncore/float-placement.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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 int ioncore_placement_padding; extern void group_calc_placement(WGroup *ws, uint level, WRectangle *geom); #endif /* ION_IONCORE_FLOAT_PLACEMENT_H */ notion-3+2012042300/ioncore/focus.c000066400000000000000000000243111174530661200165040ustar00rootroot00000000000000/* * ion/ioncore/focus.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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; /*}}}*/ /*{{{ Focus list */ void region_focuslist_remove_with_mgrs(WRegion *reg) { WRegion *mgrp=region_manager_or_parent(reg); UNLINK_ITEM(ioncore_g.focus_current, reg, active_next, active_prev); if(mgrp!=NULL) region_focuslist_remove_with_mgrs(mgrp); } void region_focuslist_push(WRegion *reg) { region_focuslist_remove_with_mgrs(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_with_mgrs(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); UNLINK_ITEM(ioncore_g.focus_current, reg, active_next, active_prev); } /*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; } /*EXTL_DOC * Iterate over focus history until \var{iterfn} returns \code{false}. * The function is called in protected mode. * This routine returns \code{true} if it reaches the end of list * without this happening. */ EXTL_EXPORT bool ioncore_focushistory_i(ExtlFn iterfn) { WRegion *next; if(ioncore_g.focus_current==NULL) return FALSE; /* 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(!extl_iter_obj(iterfn, (Obj*)next)) return FALSE; } return TRUE; } /*}}}*/ /*{{{ 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_parent(WRegion *reg, WRegion *aw) { while(aw!=NULL){ if(aw==reg) return TRUE; aw=REGION_PARENT_REG(aw); } return FALSE; } static bool region_is_await(WRegion *reg) { return region_is_parent(reg, (WRegion*)await_watch.obj); } static bool region_is_focusnext(WRegion *reg) { return region_is_parent(reg, ioncore_g.focus_next); } /* 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); } WRegion *ioncore_await_focus() { return (WRegion*)(await_watch.obj); } /*}}}*/ /*{{{ Events */ void region_got_focus(WRegion *reg) { WRegion *par; 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; region_set_manager_pseudoactivity(reg); par=REGION_PARENT_REG(reg); if(par!=NULL){ par->active_sub=reg; region_update_owned_grabs(par); } region_activated(reg); region_notify_change(reg, ioncore_g.notifies.activated); }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); } void region_lost_focus(WRegion *reg) { WRegion *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_unset_manager_pseudoactivity(reg); region_inactivated(reg); region_notify_change(reg, ioncore_g.notifies.inactivated); } /*}}}*/ /*{{{ 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, bool pseudoact_ok) { return (REGION_IS_ACTIVE(reg) || (pseudoact_ok && REGION_IS_PSEUDOACTIVE(reg))); } bool region_manager_is_focusnext(WRegion *reg) { if(reg==NULL || ioncore_g.focus_next==NULL) return FALSE; if(reg==ioncore_g.focus_next) return TRUE; return region_manager_is_focusnext(REGION_MANAGER(reg)); } bool region_may_control_focus(WRegion *reg) { if(OBJ_IS_BEING_DESTROYED(reg)) return FALSE; if(REGION_IS_ACTIVE(reg) || REGION_IS_PSEUDOACTIVE(reg)) return TRUE; if(region_is_await(reg) || region_is_focusnext(reg)) return TRUE; if(region_manager_is_focusnext(reg)) return TRUE; return FALSE; } /*}}}*/ /*{{{ set_focus, warp */ /*Time ioncore_focus_time=CurrentTime;*/ void region_finalise_focusing(WRegion* reg, Window win, bool warp, Time time) { if(warp) region_do_warp(reg); if(REGION_IS_ACTIVE(reg) && ioncore_await_focus()==NULL) return; region_set_await_focus(reg); XSetInputFocus(ioncore_g.dpy, win, RevertToParent, time); } 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; } /*}}}*/ /*{{{ Pointer focus hack */ /* This ugly hack tries to prevent focus change, when the pointer is * in a window to be unmapped (or destroyed), and that does not have * the focus, or should not soon have it. */ void region_pointer_focus_hack(WRegion *reg) { WRegion *act; if(ioncore_g.opmode!=IONCORE_OPMODE_NORMAL) return; if(ioncore_g.focus_next!=NULL && ioncore_g.focus_next_source<=IONCORE_FOCUSNEXT_POINTERHACK){ return; } act=ioncore_await_focus(); if((REGION_IS_ACTIVE(reg) && act==NULL) || !region_is_fully_mapped(reg)) return; if(act==NULL) act=ioncore_g.focus_current; if(act==NULL || OBJ_IS_BEING_DESTROYED(act) || !region_is_fully_mapped(act) || region_skip_focus(act)){ return; } region_set_focus(act); ioncore_g.focus_next_source=IONCORE_FOCUSNEXT_POINTERHACK; } /*}}}*/ notion-3+2012042300/ioncore/focus.h000066400000000000000000000036001174530661200165070ustar00rootroot00000000000000/* * ion/ioncore/focus.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #ifndef ION_IONCORE_FOCUS_H #define ION_IONCORE_FOCUS_H #include #include "common.h" #include "window.h" #include "region.h" /* Delayed (until return to main loop) warp/focus */ extern void region_maybewarp(WRegion *reg, bool warp); /* warp/focus now; do not skip enter window events etc. in mainloop */ extern void region_maybewarp_now(WRegion *reg, bool warp); extern void region_warp(WRegion *reg); /* maybewarp TRUE */ extern void region_set_focus(WRegion *reg); /* maybewarp FALSE */ extern void region_finalise_focusing(WRegion* reg, Window win, bool warp, Time time); DYNFUN void region_do_set_focus(WRegion *reg, bool warp); extern void region_do_warp(WRegion *reg); extern bool region_do_warp_default(WRegion *reg); /* Awaiting focus state */ extern void region_set_await_focus(WRegion *reg); extern WRegion *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); extern bool region_manager_is_focusnext(WRegion *reg); /* Does reg have focus? */ extern bool region_is_active(WRegion *reg, bool pseudoact_ok); /* Focus history */ extern void region_focuslist_remove_with_mgrs(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 this hook should take WRegion* as parameter. */ extern WHook *region_do_warp_alt; /* Misc. */ extern bool region_skip_focus(WRegion *reg); WRegion *ioncore_current(); extern void region_pointer_focus_hack(WRegion *reg); #endif /* ION_IONCORE_FOCUS_H */ notion-3+2012042300/ioncore/frame-draw.c000066400000000000000000000266641174530661200174270ustar00rootroot00000000000000/* * ion/ioncore/frame-draw.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #include #include #include #include #include "common.h" #include "frame.h" #include "framep.h" #include "frame-draw.h" #include "strings.h" #include "activity.h" #include "names.h" #include "gr.h" #include "gr-util.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 /*{{{ Attributes */ GR_DEFATTR(active); GR_DEFATTR(inactive); GR_DEFATTR(selected); GR_DEFATTR(unselected); GR_DEFATTR(tagged); GR_DEFATTR(not_tagged); GR_DEFATTR(dragged); GR_DEFATTR(not_dragged); GR_DEFATTR(activity); GR_DEFATTR(no_activity); static void ensure_create_attrs() { GR_ALLOCATTR_BEGIN; GR_ALLOCATTR(active); GR_ALLOCATTR(inactive); GR_ALLOCATTR(selected); GR_ALLOCATTR(unselected); GR_ALLOCATTR(tagged); GR_ALLOCATTR(not_tagged); GR_ALLOCATTR(dragged); GR_ALLOCATTR(not_dragged); GR_ALLOCATTR(no_activity); GR_ALLOCATTR(activity); GR_ALLOCATTR_END; } void frame_update_attr(WFrame *frame, int i, WRegion *reg) { GrStyleSpec *spec; bool selected, tagged, dragged, activity; if(i>=frame->titles_n){ /* Might happen when deinitialising */ return; } ensure_create_attrs(); spec=&frame->titles[i].attr; selected=(reg==FRAME_CURRENT(frame)); tagged=(reg!=NULL && reg->flags®ION_TAGGED); dragged=(i==frame->tab_dragged_idx); activity=(reg!=NULL && region_is_activity_r(reg)); gr_stylespec_unalloc(spec); gr_stylespec_set(spec, selected ? GR_ATTR(selected) : GR_ATTR(unselected)); gr_stylespec_set(spec, tagged ? GR_ATTR(tagged) : GR_ATTR(not_tagged)); gr_stylespec_set(spec, dragged ? GR_ATTR(dragged) : GR_ATTR(not_dragged)); gr_stylespec_set(spec, activity ? GR_ATTR(activity) : GR_ATTR(no_activity)); } /*}}}*/ /*{{{ (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=0; /*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); } int frame_shaded_height(const WFrame *frame) { if(frame->barmode==FRAME_BAR_NONE){ return 0; }else if(!BAR_INSIDE_BORDER(frame)){ return frame->bar_h; }else { GrBorderWidths bdw; grbrush_get_border_widths(frame->brush, &bdw); return frame->bar_h+bdw.top+bdw.bottom; } } 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) { if(frame->brush!=NULL) grbrush_set_window_shape(frame->brush, TRUE, 0, NULL); } static void free_title(WFrame *frame, int i) { if(frame->titles[i].text!=NULL){ free(frame->titles[i].text); frame->titles[i].text=NULL; } } void frame_recalc_bar(WFrame *frame, bool complete) { int textw, i; WLListIterTmp tmp; WRegion *sub; char *title; bool set_shape; if(frame->bar_brush==NULL || frame->titles==NULL) return; set_shape=frame->tabs_params.alg(frame,complete); if(set_shape) { if(frame->barmode==FRAME_BAR_SHAPED) frame_set_shape(frame); else frame_clear_shape(frame); } i=0; if(FRAME_MCOUNT(frame)==0){ free_title(frame, i); textw=frame->titles[i].iw; if(textw>0){ title=grbrush_make_label(frame->bar_brush, TR(""), textw); frame->titles[i].text=title; } return; } FRAME_MX_FOR_ALL(sub, frame, tmp){ free_title(frame, i); textw=frame->titles[i].iw; if(textw>0){ 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; 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_init_attr(frame->bar_brush, &frame->baseattr); grbrush_draw_textboxes(frame->bar_brush, &geom, frame->titles_n, frame->titles, complete); grbrush_end(frame->bar_brush); } void frame_draw(const WFrame *frame, bool complete) { WRectangle geom; if(frame->brush==NULL) return; frame_border_geom(frame, &geom); grbrush_begin(frame->brush, &geom, (complete ? 0 : GRBRUSH_NO_CLEAR_OK)); grbrush_init_attr(frame->brush, &frame->baseattr); grbrush_draw_border(frame->brush, &geom); frame_draw_bar(frame, TRUE); grbrush_end(frame->brush); } void frame_brushes_updated(WFrame *frame) { WFrameBarMode barmode; 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_TILED || frame->mode==FRAME_MODE_UNKNOWN || frame->mode==FRAME_MODE_TRANSIENT_ALT){ barmode=FRAME_BAR_INSIDE; }else{ 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; } /* tabs and bar width calculation stuff */ frame_tabs_calc_brushes_updated(frame); } /*}}}*/ /*{{{ 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, TRUE); frame_set_background(frame, TRUE); } static StringIntMap frame_tab_styles[]={ {"tab-frame-unknown", FRAME_MODE_UNKNOWN}, {"tab-frame-unknown-alt", FRAME_MODE_UNKNOWN_ALT}, {"tab-frame-tiled", FRAME_MODE_TILED}, {"tab-frame-tiled-alt", FRAME_MODE_TILED_ALT}, {"tab-frame-floating", FRAME_MODE_FLOATING}, {"tab-frame-floating-alt", FRAME_MODE_FLOATING_ALT}, {"tab-frame-transient", FRAME_MODE_TRANSIENT}, {"tab-frame-transient-alt", FRAME_MODE_TRANSIENT_ALT}, 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; } void frame_setup_dragwin_style(WFrame *frame, GrStyleSpec *spec, int tab) { gr_stylespec_append(spec, &frame->baseattr); gr_stylespec_append(spec, &frame->titles[tab].attr); } /*}}}*/ /*{{{ Activated/inactivated */ void frame_inactivated(WFrame *frame) { ensure_create_attrs(); gr_stylespec_set(&frame->baseattr, GR_ATTR(inactive)); gr_stylespec_unset(&frame->baseattr, GR_ATTR(active)); window_draw((WWindow*)frame, FALSE); } void frame_activated(WFrame *frame) { ensure_create_attrs(); gr_stylespec_set(&frame->baseattr, GR_ATTR(active)); gr_stylespec_unset(&frame->baseattr, GR_ATTR(inactive)); window_draw((WWindow*)frame, FALSE); } /*}}}*/ notion-3+2012042300/ioncore/frame-draw.h000066400000000000000000000027501174530661200174220ustar00rootroot00000000000000/* * ion/ioncore/frame-draw.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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, bool complete); 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 int frame_shaded_height(const WFrame *frame); 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); extern void frame_update_attr(WFrame *frame, int i, WRegion *reg); extern void frame_setup_dragwin_style(WFrame *frame, GrStyleSpec *spec, int tab); extern void frame_inactivated(WFrame *frame); extern void frame_activated(WFrame *frame); #endif /* ION_IONCORE_FRAME_DRAW_H */ notion-3+2012042300/ioncore/frame-pointer.c000066400000000000000000000207261174530661200201430ustar00rootroot00000000000000/* * ion/ioncore/frame-pointer.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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(frame->barmode!=FRAME_BAR_NONE && 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_screen_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; frame_setup_dragwin_style(frame, infowin_stylespec(tabdrag_infowin), tab); 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 \codestr{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)); } */ mplex_switch_nth((WMPlex*)frame, p_tabnum); } /*}}}*/ notion-3+2012042300/ioncore/frame-pointer.h000066400000000000000000000011671174530661200201460ustar00rootroot00000000000000/* * ion/ioncore/frame-pointer.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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 */ notion-3+2012042300/ioncore/frame-tabs-recalc.c000066400000000000000000000266161174530661200206470ustar00rootroot00000000000000/* * notion/ioncore/frame-tabs-recalc.c * * Copyright (c) Tuomo Valkonen 1999-2009. * Copyright (c) Tomas Ebenlendr 2011. * * See the included file LICENSE for details. */ #include #include /* qsort */ #include "common.h" #include "names.h" #include "frame.h" #include "framep.h" #include "frame-draw.h" #include "frame-tabs-recalc.h" /*{{{ Common functions */ /* * Temporarily store actual title widths (without truncation) in * frame->titles[*].iw, when calculating tabs widths and bar width. After the * algorithm returns it has to set frame->titles[*].iw to the proper values. * * This function is generic for all algorithms. */ static void get_titles_text_width(WFrame *frame) { int i=0; WLListIterTmp itmp; WRegion *sub; const char *displayname; /* Assume frame->bar_brush != NULL. Assume FRAME_MCOUNT(frame) > 0 */ FRAME_MX_FOR_ALL(sub, frame, itmp){ displayname=region_displayname(sub); if(displayname==NULL) frame->titles[i].iw=0; else frame->titles[i].iw=grbrush_get_text_width(frame->bar_brush, displayname, strlen(displayname)); i++; } } /*}}}*/ /*{{{ Equal tab sizes algorithm * This gives tabs equal sizes (up to 1 pixel). */ static void frame_tabs_width_calculate_equal(WFrame *frame) { WRectangle bg; GrBorderWidths bdw=GR_BORDER_WIDTHS_INIT; int i,m=frame->titles_n; uint w; frame_bar_geom(frame, &bg); 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){ for(i=0;ititles[i].iw=0; }else{ for(i=0;ititles[i].iw=((i+1)*w)/m-(i*w)/m; } } /* Equal tab sizes algorithm. Calculate size of the bar of a shaped (i.e., floating) frame. */ static int frame_shaped_recalc_bar_size_equal(WFrame *frame) { int bar_w, textw=0, tmaxw=frame->tabs_params.tab_min_w, tmp=0; GrBorderWidths bdw; uint bdtotal; int i, m; m=FRAME_MCOUNT(frame); bar_w=frame->tabs_params.bar_max_width_q*REGION_GEOM(frame).w; if(m>0){ grbrush_get_border_widths(frame->bar_brush, &bdw); bdtotal=((m-1)*(bdw.tb_ileft+bdw.tb_iright+bdw.spacing) +bdw.right+bdw.left); get_titles_text_width(frame); for(i=0;ititles[i].iw>tmaxw) tmaxw=frame->titles[i].iw; if(bar_wtabs_params.tab_min_w && REGION_GEOM(frame).w>frame->tabs_params.tab_min_w) bar_w=frame->tabs_params.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>frame->tabs_params.requested_pad) tmp=frame->tabs_params.requested_pad; bar_w=(tmaxw+tmp*2)*m+bdtotal; }else{ /* Some labels must be truncated */ } }else{ if(bar_wtabs_params.tab_min_w) bar_w=frame->tabs_params.tab_min_w; } return bar_w; } static bool tab_sizes_equal(WFrame * frame, bool complete) { int bar_w; if(frame->barmode==FRAME_BAR_SHAPED){ bar_w=frame_shaped_recalc_bar_size_equal(frame); if((frame->bar_w!=bar_w)){ frame->bar_w=bar_w; complete=TRUE; } } frame_tabs_width_calculate_equal(frame); return complete; } /*}}}*/ /*{{{ Proportional/Elastic tab sizes algorithms * This gives tabs sizes proportional to the contained text. */ /* Failsafes to 'equal' algorithm if there are more tabs. * The result will be same in most cases of many tabs anyway. */ #define PROPOR_MAX_TABS 30 static int intcompare (const void *a, const void *b) { return *(int *)a-*(int *)b; } static bool tab_sizes_propor_elastic(WFrame * frame, bool complete, bool proportional) { int bar_w, titles_total=0; int titles_padded_total1=0, titles_padded_total2=0; WRectangle bg; GrBorderWidths bdw=GR_BORDER_WIDTHS_INIT; int i, m=frame->titles_n; int iw, min_w=0, max_w, w; int sorted_sizes[PROPOR_MAX_TABS]; uint bdtotal; if (m>PROPOR_MAX_TABS) return tab_sizes_equal(frame, complete); frame_bar_geom(frame, &bg); grbrush_get_border_widths(frame->bar_brush, &bdw); if (frame->barmode==FRAME_BAR_SHAPED) { bar_w=frame->tabs_params.bar_max_width_q*REGION_GEOM(frame).w; } else { bar_w=bg.w; frame->bar_w=bar_w; } get_titles_text_width(frame); /* Calculate thresholds. */ for (i=0;ititles[i].iw; titles_total+= (iwtabs_params.propor_tab_min_w ? frame->tabs_params.propor_tab_min_w : iw); titles_padded_total1+= (iw < frame->tabs_params.propor_tab_min_w-2*frame->tabs_params.requested_pad ? frame->tabs_params.propor_tab_min_w : iw+2*frame->tabs_params.requested_pad); titles_padded_total2+= (iw < frame->tabs_params.tab_min_w-2*frame->tabs_params.requested_pad ? frame->tabs_params.tab_min_w : iw+2*frame->tabs_params.requested_pad); } bdtotal=((m-1)*(bdw.tb_ileft+bdw.tb_iright+bdw.spacing) +bdw.right+bdw.left); w=bar_w-bdtotal; /* Do different things based on thresholds. */ if (m*frame->tabs_params.propor_tab_min_w>=w) { /*Equal sizes (tiny tabs). Base width is zero.*/ for(i=0;ititles[i].iw=0; } else if (titles_total>=w) { /*Truncate long tabs.*/ qsort(sorted_sizes, m, sizeof(int), intcompare); min_w=frame->tabs_params.propor_tab_min_w; max_w=sorted_sizes[m-1];titles_total=0; for(i=0;iframe->tabs_params.propor_tab_min_w) break; titles_total+=frame->tabs_params.propor_tab_min_w; } for(;i=w) { max_w=(w-titles_total)/(m-i); break; } titles_total+=sorted_sizes[i]; } for(i=0;ititles[i].iw>max_w) frame->titles[i].iw=max_w; } else if (titles_padded_total1>=w) { /*Just a little padding. *Pad equally, no tab shorter than tabs_params.propor_tab_min_w-1. */ max_w=frame->tabs_params.propor_tab_min_w; equal_pad: qsort(sorted_sizes, m, sizeof(int), intcompare); titles_total=m*max_w; i=m-1;min_w=0; while((i>=0) && (sorted_sizes[i]>=max_w)){ titles_total+=sorted_sizes[i]; i--; } while(i>=0){ /*Test padding by max_w-sorted_sizes[i].*/ if(titles_total-(m-i-1)*sorted_sizes[i]>=w){ min_w=(titles_total-w)/(m-i-1); break; } titles_total+=sorted_sizes[i]; i--; } /* After expanding to min_w: equal padding will extend short tabs to * required size (+- 1pixel). */ } else if (titles_padded_total2>=w) { /* Expand as many short tabs as possible to equal size, * they will be shorter than tabs_params.tab_min_w anyway. * Long tabs should be padded by 2*tabs_params.requested_pad. */ equal_tab: qsort(sorted_sizes, m, sizeof(int), intcompare); min_w=0;titles_total=2*m*frame->tabs_params.requested_pad; for(i=m-1;i>=0;i--){ if (sorted_sizes[i]*(i+1)+titles_total<=w){ min_w=(w-titles_total)/(i+1); break; } titles_total+=sorted_sizes[i]; } /* After expanding to min_w: it should remain * 2*m*tabs_params.requested_pad +- m */ } else if (frame->barmode==FRAME_BAR_SHAPED) { /* Shorter bar. */ w=titles_padded_total2; bar_w=w+bdtotal; min_w=frame->tabs_params.tab_min_w-2*frame->tabs_params.requested_pad; /* After expanding to min_w: it should remain * 2*m*tabs_params.requested_pad exactly */ } else { if(proportional){ /* Pad equally, no tab shorter than tabs_params.tab_min_w-1. */ max_w=frame->tabs_params.tab_min_w; goto equal_pad; }else{ /* Expand as many short tabs as possible to equal size, * Long tabs should be padded by 2*tabs_params.requested_pad. */ goto equal_tab; } } if (min_w>0) { for(i=0;ititles[i].iwtitles[i].iw=min_w; } /* Calculate remaining space */ titles_total=0; for(i=0;ititles[i].iw; w-=titles_total; /* Distribute remaining space equally up to 1 pixel */ for(i=0;ititles[i].iw+=((i+1)*w)/m-(i*w)/m; if((frame->bar_w!=bar_w)){ frame->bar_w=bar_w; return TRUE; } return complete; } static bool tab_sizes_proportional(WFrame * frame, bool complete) { return tab_sizes_propor_elastic(frame, complete, TRUE); } static bool tab_sizes_elastic(WFrame * frame, bool complete) { return tab_sizes_propor_elastic(frame, complete, FALSE); } /*}}}*/ DECLFUNPTRMAP(TabCalcPtr); static StringTabCalcPtrMap frame_tabs_width_algorithms[] = { { "equal", tab_sizes_equal }, { "elastic", tab_sizes_elastic }, { "proportional", tab_sizes_proportional }, END_STRINGPTRMAP }; static TabCalcPtr default_frame_tabs_width_algorithm=tab_sizes_equal; static void param_init(TabCalcParams *pars) { pars->tab_min_w=100; pars->propor_tab_min_w=50; pars->bar_max_width_q=0.95; pars->requested_pad=10; pars->alg=default_frame_tabs_width_algorithm; } void frame_tabs_calc_brushes_updated(WFrame *frame) { char *str; TabCalcPtr alg; TabCalcParams *pars=&(frame->tabs_params); param_init(pars); if(grbrush_get_extra(frame->brush, "floatframe_tab_min_w", 'i', &(pars->tab_min_w)) || grbrush_get_extra(frame->brush, "frame_tab_min_w", 'i', &(pars->tab_min_w))){ if(pars->tab_min_w<=0) pars->tab_min_w=1; } if(grbrush_get_extra(frame->brush, "frame_propor_tab_min_w", 'i', &(pars->propor_tab_min_w))){ if(pars->propor_tab_min_w<=0) pars->propor_tab_min_w=1; } if(grbrush_get_extra(frame->brush, "floatframe_bar_max_w_q", 'd', &(pars->bar_max_width_q))){ if(pars->bar_max_width_q<=0.0 || pars->bar_max_width_q>1.0) pars->bar_max_width_q=1.0; } if(grbrush_get_extra(frame->brush, "frame_tab_padding", 'i', &(pars->requested_pad))){ if(pars->requested_pad<=0) pars->requested_pad=1; } if(grbrush_get_extra(frame->brush, "frame_tab_width_alg", 's', &str)){ alg=STRINGFUNPTRMAP_VALUE(TabCalcPtr, frame_tabs_width_algorithms, str, NULL); if(alg!=NULL) frame->tabs_params.alg=alg; } } void frame_tabs_width_recalc_init(WFrame *frame) { param_init(&(frame->tabs_params)); } notion-3+2012042300/ioncore/frame-tabs-recalc.h000066400000000000000000000040151174530661200206410ustar00rootroot00000000000000/* * notion/ioncore/frame-tabs-recalc.h * * Copyright (c) Tomas Ebenlendr 2011. * * See the included file LICENSE for details. */ #ifndef NOTION_IONCORE_FRAME_TABS_RECALC_H #define NOTION_IONCORE_FRAME_TABS_RECALC_H #include "libtu/map.h" #include "libtu/obj.h" /* Compute inner widths of tabs and width of shaped bar. * There will be more algorithms to do this, thus here * is prototype of the called function. * * Requirements: * * If complete is set and frame->barmode==FRAME_BAR_SHAPED * then frame->bar_w has to be updated. * return TRUE if bar_w changed (i.e, new value != old value) * return the value of 'complete' on normal run. * frame_set_shape or frame_clear_shape is called when TRUE is returned. * * The function is called only if * frame->bar_brush != NULL && frame->titles != NULL */ typedef bool (*TabCalcPtr)(WFrame *frame, bool complete); /* Gets the identifier of the algorithm. */ const char *frame_get_tabs_sizes_algorithm(WFrame *frame); /* Sets the algorithm based on the identifier. * Returns -1 on failure, 1 on successfull change, 0 on success but no change * If 1 is returned, frame_bar_recalc and frame_bar_draw should be called. */ int frame_do_set_tabs_sizes_algorithm(WFrame *frame, const char *algstr); INTRSTRUCT(TabCalcParams); DECLSTRUCT(TabCalcParams){ TabCalcPtr alg; /* Maximum size of shaped bar. */ double bar_max_width_q; /* Minimum width of a tab in shaped frame. * For 'proportional' and 'elastic' algorithms also minimum width of a tab * provided that no title has to be truncated */ int tab_min_w; /* Requested empty space to be added before and after text. */ int requested_pad; /* Minimum width of a tab for 'proportional' and 'elastic' algorithms. * Long titles will be truncated instead of shortening a short tab below * this length. */ int propor_tab_min_w; }; void frame_tabs_calc_brushes_updated(WFrame *frame); void frame_tabs_width_recalc_init(WFrame *frame); #endif /* NOTION_IONCORE_FRAME_TABS_RECALC_H */ notion-3+2012042300/ioncore/frame.c000066400000000000000000000572241174530661200164700ustar00rootroot00000000000000/* * ion/ioncore/frame.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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 "llist.h" #include "framedpholder.h" #include "return.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 FORWARD_CWIN_RQGEOM(FRAME) framemode_is_floating(frame_mode(FRAME)) #define USE_MINMAX(FRAME) framemode_is_floating(frame_mode(FRAME)) #define DEST_EMPTY(FRAME) framemode_is_floating(frame_mode(FRAME)) WFrameMode framemode_unalt(WFrameMode mode) { if(mode==FRAME_MODE_UNKNOWN_ALT) return FRAME_MODE_UNKNOWN; else if(mode==FRAME_MODE_TILED_ALT) return FRAME_MODE_TILED; else if(mode==FRAME_MODE_FLOATING_ALT) return FRAME_MODE_FLOATING; else if(mode==FRAME_MODE_TRANSIENT_ALT) return FRAME_MODE_TRANSIENT; else return mode; } static WFrameMode framemode_is_floating(WFrameMode mode) { WFrameMode modea=framemode_unalt(mode); return (modea==FRAME_MODE_FLOATING || modea==FRAME_MODE_TRANSIENT); } /*{{{ 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_tabs_width_recalc_init(frame); gr_stylespec_init(&frame->baseattr); if(!mplex_init((WMPlex*)frame, parent, fp, "Notion WFrame")) return FALSE; frame_initialise_gr(frame); frame_initialise_titles(frame); region_add_bindmap((WRegion*)frame, ioncore_frame_bindmap); region_add_bindmap((WRegion*)frame, ioncore_mplex_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); gr_stylespec_unalloc(&frame->baseattr); mplex_deinit((WMPlex*)frame); } /*}}}*/ /*{{{ Mode switching */ static void frame_add_mode_bindmaps(WFrame *frame) { WFrameMode modea=framemode_unalt(frame->mode); if(modea==FRAME_MODE_FLOATING){ region_add_bindmap((WRegion*)frame, ioncore_mplex_toplevel_bindmap); region_add_bindmap((WRegion*)frame, ioncore_frame_toplevel_bindmap); region_add_bindmap((WRegion*)frame, ioncore_frame_floating_bindmap); }else if(modea==FRAME_MODE_TRANSIENT){ region_add_bindmap((WRegion*)frame, ioncore_frame_transient_bindmap); region_add_bindmap((WRegion*)frame, ioncore_frame_floating_bindmap); }else{ /* mode==FRAME_MODE_TILED || mode==FRAME_MODE_TILED_ALT || mode==FRAME_MODE_UNKNOWN */ region_add_bindmap((WRegion*)frame, ioncore_mplex_toplevel_bindmap); region_add_bindmap((WRegion*)frame, ioncore_frame_toplevel_bindmap); region_add_bindmap((WRegion*)frame, ioncore_frame_tiled_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_mplex_toplevel_bindmap); 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_updategr(frame); } WFrameMode frame_mode(WFrame *frame) { return frame->mode; } static StringIntMap frame_modes[]={ {"unknown", FRAME_MODE_UNKNOWN}, {"unknown-alt", FRAME_MODE_UNKNOWN_ALT}, {"tiled", FRAME_MODE_TILED}, {"tiled-alt", FRAME_MODE_TILED_ALT}, {"floating", FRAME_MODE_FLOATING}, {"floating-alt", FRAME_MODE_FLOATING_ALT}, {"transient", FRAME_MODE_TRANSIENT}, {"transient-alt", FRAME_MODE_TRANSIENT_ALT}, END_STRINGINTMAP }; /*EXTL_DOC * Get frame mode. */ EXTL_SAFE 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 (one of * \codestr{unknown}, \codestr{tiled}, \codestr{floating}, \codestr{transient}, * or any of these suffixed with \codestr{-alt}). */ 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 || xframe->titles_n){ fprintf(stderr,"WARNING: we should not be here, please contact notion developers\n"); return 0; } if(frame->titles[n].iw==0) return 0; /* Too small tab. */ if(frame->bar_brush!=NULL) grbrush_get_border_widths(frame->bar_brush, &bdw); return frame->titles[n].iw + (n==0 ? bdw.left : bdw.tb_ileft) + (n==frame->titles_n-1 ? bdw.right : bdw.tb_iright+bdw.spacing); } void frame_update_attr_nth(WFrame *frame, int i) { WRegion *reg; if(i<0 || i>=frame->titles_n) return; frame_update_attr(frame, i, mplex_mx_nth((WMPlex*)frame, i)); } static void frame_update_attrs(WFrame *frame) { int i=0; WRegion *sub; WLListIterTmp tmp; FRAME_MX_FOR_ALL(sub, frame, tmp){ frame_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); gr_stylespec_unalloc(&frame->titles[i].attr); } 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; gr_stylespec_init(&frame->titles[i].attr); frame_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, FALSE); 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); if(!window_fitrep(&(frame->mplex.win), par, fp)) return FALSE; 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); else sizehints_clear(hints_ret); FRAME_MX_FOR_ALL(sub, frame, tmp){ sizehints_adjust_for(hints_ret, sub); } 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;*/ } if(!hints_ret->min_set){ hints_ret->min_width=0; hints_ret->min_height=0; hints_ret->min_set=TRUE; } if(!hints_ret->base_set){ hints_ret->base_width=0; hints_ret->base_height=0; hints_ret->base_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; /* shaded */ { int f=frame->flags&(FRAME_SHADED|FRAME_SHADED_TOGGLE); if(f==FRAME_SHADED || f==FRAME_SHADED_TOGGLE){ int h=frame_shaded_height(frame); hints_ret->min_height=h; hints_ret->max_height=h; hints_ret->base_height=h; if(!hints_ret->max_set){ hints_ret->max_width=INT_MAX; hints_ret->max_set=TRUE; } } } } /*}}}*/ /*{{{ 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; } } } /*}}}*/ /*{{{ Frame recreate pholder stuff */ static WFramedPHolder *frame_make_recreate_pholder(WFrame *frame, WPHolder *rph) { WFramedParam fparam=FRAMEDPARAM_INIT; WFramedPHolder *fph=NULL; if(rph!=NULL){ fparam.mode=frame->mode; fph=create_framedpholder(rph, &fparam); if(fph==NULL) destroy_obj((Obj*)rph); } return fph; } static void mplex_migrate_phs_to_fph(WMPlex *mplex, WFramedPHolder *fph) { WMPlexPHolder *phs, *ph; phs=mplex->misc_phs; mplex->misc_phs=NULL; phs->recreate_pholder=fph; for(ph=phs; ph!=NULL; ph=ph->next) ph->mplex=NULL; } bool frame_rescue_clientwins(WFrame *frame, WRescueInfo *info) { bool ret; ret=mplex_rescue_clientwins(&frame->mplex, info); /* Now, do placeholders. * We can't currently be arsed to keep them ordered with regions... * First we check if we can simply recreate this frame, which takes * care of e.g. floating frames being destroyed when entering full * screen mode. If not, we check if the target would be a WMPlex, and * migrate there. This takes care of frames destroyed in tilings. */ mplex_flatten_phs(&frame->mplex); if(frame->mplex.misc_phs!=NULL){ WPHolder *ret_ph=region_make_return_pholder((WRegion*)frame); WFramedPHolder *fph=frame_make_recreate_pholder(frame, ret_ph); if(fph!=NULL){ mplex_migrate_phs_to_fph(&frame->mplex, fph); }else{ WPHolder *rescueph=rescueinfo_pholder(info); if(rescueph!=NULL){ WRegion *target=pholder_target(rescueph); WMPlex *other_mplex=OBJ_CAST(target, WMPlex); if(other_mplex!=NULL){ assert(other_mplex!=&frame->mplex); mplex_migrate_phs(&frame->mplex, other_mplex); } } } } return ret; } /*}}}*/ /*{{{ 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; rq.geom.h=frame_shaded_height(frame); } 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} * (\codestr{set}, \codestr{unset}, or \codestr{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); } /*EXTL_DOC * Is the attribute \var{attr} set on \var{frame}? */ EXTL_SAFE EXTL_EXPORT_MEMBER bool frame_is_grattr(WFrame *frame, const char *attr) { GrAttr a=stringstore_alloc(attr); bool set=gr_stylespec_isset(&frame->baseattr, a); stringstore_free(a); return set; } bool frame_set_grattr(WFrame *frame, GrAttr a, int sp) { bool set=gr_stylespec_isset(&frame->baseattr, a); bool nset=libtu_do_setparam(sp, set); if(XOR(set, nset)){ if(nset) gr_stylespec_set(&frame->baseattr, a); else gr_stylespec_unset(&frame->baseattr, a); window_draw((WWindow*)frame, TRUE); } return nset; } /*EXTL_DOC * Set extra drawing engine attributes for the frame. * The parameter \var{attr} is the attribute, and \var{how} is * one of \codestr{set}, \codestr{unset}, or \codestr{toggle}. */ EXTL_EXPORT_AS(WFrame, set_grattr) bool frame_set_grattr_extl(WFrame *frame, const char *attr, const char *how) { if(attr!=NULL){ GrAttr a=stringstore_alloc(attr); bool ret=frame_set_grattr(frame, a, libtu_string_to_setparam(how)); stringstore_free(a); return ret; }else{ return FALSE; } } void frame_managed_notify(WFrame *frame, WRegion *sub, WRegionNotify how) { bool complete; if(how==ioncore_g.notifies.activated || how==ioncore_g.notifies.inactivated || how==ioncore_g.notifies.name || how==ioncore_g.notifies.activity || how==ioncore_g.notifies.sub_activity || how==ioncore_g.notifies.tag){ complete=how==ioncore_g.notifies.name; frame_update_attrs(frame); frame_recalc_bar(frame, complete); frame_draw_bar(frame, complete); } } static void frame_size_changed_default(WFrame *frame, bool wchg, bool hchg) { int bar_w=frame->bar_w; if(wchg) frame_recalc_bar(frame, TRUE); 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 frame_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); } WRegion *frame_managed_disposeroot(WFrame *frame, WRegion *reg) { if(DEST_EMPTY(frame) && frame->mplex.mgd!=NULL && frame->mplex.mgd->reg==reg && frame->mplex.mgd->mgr_next==NULL){ WRegion *tmp=region_disposeroot((WRegion*)frame); return (tmp!=NULL ? tmp : reg); } return reg; } int frame_default_index(WFrame *frame) { return ioncore_g.frame_default_index; } /*}}}*/ /*{{{ prepare_manage_transient */ WPHolder *frame_prepare_manage_transient(WFrame *frame, const WClientWin *transient, const WManageParams *param, int unused) { /* Transient manager searches should not cross tiled frames * unless explicitly floated. */ if(framemode_is_floating(frame_mode(frame)) || extl_table_is_bool_set(transient->proptab, "float")){ return region_prepare_manage_transient_default((WRegion*)frame, transient, param, unused); }else{ return NULL; } } /*}}}*/ /*{{{ 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)){ char *tmp; if(extl_table_gets_s(tab, "mode", &tmp)){ mode=stringintmap_value(frame_modes, tmp, mode); free(tmp); } } frame=create_frame(par, fp, mode); if(frame!=NULL) frame_do_load(frame, tab); if(DEST_EMPTY(frame) && frame->mplex.mgd==NULL){ if(frame->mplex.misc_phs!=NULL){ /* Session management hack */ WFramedPHolder *fph; fph=frame_make_recreate_pholder(frame, ioncore_get_load_pholder()); if(fph!=NULL) mplex_migrate_phs_to_fph(&frame->mplex, fph); } destroy_obj((Obj*)frame); return NULL; } 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}, {(DynFun*)region_managed_disposeroot, (DynFun*)frame_managed_disposeroot}, {region_managed_rqgeom_absolute, frame_managed_rqgeom_absolute}, {(DynFun*)mplex_default_index, (DynFun*)frame_default_index}, {(DynFun*)region_prepare_manage_transient, (DynFun*)frame_prepare_manage_transient}, {(DynFun*)region_rescue_clientwins, (DynFun*)frame_rescue_clientwins}, END_DYNFUNTAB }; EXTL_EXPORT IMPLCLASS(WFrame, WMPlex, frame_deinit, frame_dynfuntab); /*}}}*/ notion-3+2012042300/ioncore/frame.h000066400000000000000000000070351174530661200164700ustar00rootroot00000000000000/* * ion/ioncore/frame.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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" #include "frame-tabs-recalc.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 */ typedef enum{ FRAME_MODE_UNKNOWN, FRAME_MODE_TILED, FRAME_MODE_TILED_ALT, FRAME_MODE_FLOATING, FRAME_MODE_TRANSIENT, FRAME_MODE_UNKNOWN_ALT, FRAME_MODE_FLOATING_ALT, FRAME_MODE_TRANSIENT_ALT } 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; void *quasiact_source; GrBrush *brush; GrBrush *bar_brush; GrStyleSpec baseattr; GrTransparency tr_mode; GrTextElem *titles; int titles_n; /* Bar stuff */ WFrameBarMode barmode; int bar_w, bar_h; /* Parameters to calculate tab sizes. */ TabCalcParams tabs_params; }; /* 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_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, WRegionNotify how); extern bool frame_managed_rqdispose(WFrame *frame, WRegion *reg); extern WPHolder *frame_prepare_manage_transient(WFrame *frame, const WClientWin *transient, const WManageParams *param, int unused); /* 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; extern WFrameMode framemode_unalt(WFrameMode mode); #endif /* ION_IONCORE_FRAME_H */ notion-3+2012042300/ioncore/framedpholder.c000066400000000000000000000136671174530661200202150ustar00rootroot00000000000000/* * ion/ioncore/framedpholder.c * * Copyright (c) Tuomo Valkonen 2005-2009. * * See the included file LICENSE for details. */ #include #include #include #include "frame.h" #include "framedpholder.h" #include "sizehint.h" #include "resize.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; watch_init(&ph->frame_watch); 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)); watch_reset(&ph->frame_watch); } /*}}}*/ /*{{{ Attach */ typedef struct{ WRegionAttachData *data; WFramedParam *param; WRegion *reg_ret; } AP; void frame_adjust_to_initial(WFrame *frame, const WFitParams *fp, const WFramedParam *param, WRegion *reg) { WRectangle rqg, mg; WSizeHints szh; int iw, ih; if(!(fp->mode&(REGION_FIT_BOUNDS|REGION_FIT_WHATEVER))) return; mplex_managed_geom((WMPlex*)frame, &mg); /* Adjust geometry */ if(!param->inner_geom_gravity_set){ iw=REGION_GEOM(reg).w; ih=REGION_GEOM(reg).h; rqg.x=REGION_GEOM(frame).x; rqg.y=REGION_GEOM(frame).y; }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); iw=param->inner_geom.w; ih=param->inner_geom.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)); } /* Some apps seem to request geometries inconsistent with their size hints, * so correct for that here. * Because WGroup(CW) sets no_constrain on the size hints, we have * to set override_no_constrain to force the frame to have the size * of the 'bottom' of the group. */ region_size_hints(reg, &szh); sizehints_correct(&szh, &iw, &ih, TRUE, TRUE); rqg.w=maxof(1, iw+(REGION_GEOM(frame).w-mg.w)); rqg.h=maxof(1, ih+(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); } WRegion *framed_handler(WWindow *par, const WFitParams *fp, void *ap_) { AP *ap=(AP*)ap_; WMPlexAttachParams mp=MPLEXATTACHPARAMS_INIT; WFramedParam *param=ap->param; WFrame *frame; WRegion *reg; frame=create_frame(par, fp, param->mode); 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); ap->reg_ret=reg; if(reg==NULL){ destroy_obj((Obj*)frame); return NULL; } frame_adjust_to_initial(frame, fp, param, reg); 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; ap.reg_ret=NULL; return fn(reg, fn_param, &data2); } WRegion *framedpholder_do_attach(WFramedPHolder *ph, int flags, WRegionAttachData *data) { WRegionAttachData data2; WFrame *frame; AP ap; frame=(WFrame*)ph->frame_watch.obj; if(frame!=NULL){ WMPlexAttachParams mp=MPLEXATTACHPARAMS_INIT; return mplex_do_attach(&frame->mplex, &mp, data); } 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; ap.reg_ret=NULL; frame=(WFrame*)pholder_do_attach(ph->cont, flags, &data2); if(frame!=NULL){ assert(OBJ_IS(frame, WFrame)); watch_setup(&ph->frame_watch, (Obj*)frame, NULL); } return (flags&PHOLDER_ATTACH_RETURN_CREATEROOT ? (WRegion*)frame : ap.reg_ret); } /*}}}*/ /*{{{ Other dynfuns */ bool framedpholder_do_goto(WFramedPHolder *ph) { WRegion *frame=(WRegion*)ph->frame_watch.obj; if(frame!=NULL) return region_goto((WRegion*)frame); else if(ph->cont!=NULL) return pholder_goto(ph->cont); else return FALSE; } WRegion *framedpholder_do_target(WFramedPHolder *ph) { WRegion *frame=(WRegion*)ph->frame_watch.obj; return (frame!=NULL ? frame : (ph->cont!=NULL ? pholder_target(ph->cont) : NULL)); } bool framedpholder_stale(WFramedPHolder *ph) { WRegion *frame=(WRegion*)ph->frame_watch.obj; return (frame==NULL && (ph->cont==NULL || pholder_stale(ph->cont))); } /*}}}*/ /*{{{ 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}, {(DynFun*)pholder_stale, (DynFun*)framedpholder_stale}, END_DYNFUNTAB }; IMPLCLASS(WFramedPHolder, WPHolder, framedpholder_deinit, framedpholder_dynfuntab); /*}}}*/ notion-3+2012042300/ioncore/framedpholder.h000066400000000000000000000032601174530661200202060ustar00rootroot00000000000000/* * ion/ioncore/framedpholder.h * * Copyright (c) Tuomo Valkonen 2005-2009. * * See the included file LICENSE for details. */ #ifndef ION_IONCORE_FRAMEDPHOLDER_H #define ION_IONCORE_FRAMEDPHOLDER_H #include "common.h" #include "pholder.h" #include "attach.h" #include "frame.h" #define FRAMEDPARAM_INIT {0, 0, {0, 0, 0, 0}, FRAME_MODE_FLOATING /*, NULL*/} INTRSTRUCT(WFramedParam); DECLSTRUCT(WFramedParam){ uint inner_geom_gravity_set:1; int gravity; WRectangle inner_geom; WFrameMode mode; /*WRegionSimpleCreateFn *mkframe;*/ }; DECLCLASS(WFramedPHolder){ WPHolder ph; WPHolder *cont; WFramedParam param; Watch frame_watch; }; 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 bool framedpholder_stale(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); extern void frame_adjust_to_initial(WFrame *frame, const WFitParams *fp, const WFramedParam *param, WRegion *reg); #endif /* ION_IONCORE_FRAMEDPHOLDER_H */ notion-3+2012042300/ioncore/framep.h000066400000000000000000000020231174530661200166400ustar00rootroot00000000000000/* * ion/ioncore/framep.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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 */ notion-3+2012042300/ioncore/fullscreen.c000066400000000000000000000073241174530661200175340ustar00rootroot00000000000000/* * ion/ioncore/fullscreen.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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.h" #include "return.h" /*{{{ Generic full screen mode code */ bool region_fullscreen_scr(WRegion *reg, WScreen *scr, bool switchto) { int rootx, rooty; bool wasfs=TRUE; int swf=(switchto ? MPLEX_ATTACH_SWITCHTO : 0); WPHolder *ph=NULL; bool newph=FALSE, ret; ph=region_unset_get_return(reg); if(ph==NULL){ ph=region_make_return_pholder(reg); newph=TRUE; } ret=(mplex_attach_simple((WMPlex*)scr, reg, swf)!=NULL); if(!ret) warn(TR("Failed to enter full screen mode.")); if(!ret && newph) destroy_obj((Obj*)ph); else if(!region_do_set_return(reg, ph)) destroy_obj((Obj*)ph); return TRUE; } bool region_enter_fullscreen(WRegion *reg, bool switchto) { WScreen *scr=region_screen_of(reg); if(scr==NULL){ scr=rootwin_current_scr(region_rootwin_of(reg)); if(scr==NULL) return FALSE; } return region_fullscreen_scr(reg, scr, switchto); } bool region_leave_fullscreen(WRegion *reg, bool switchto) { int swf=(switchto ? PHOLDER_ATTACH_SWITCHTO : 0); WPHolder *ph=region_unset_get_return(reg); if(ph==NULL) return FALSE; if(!pholder_attach_mcfgoto(ph, swf, reg)){ warn(TR("Failed to return from full screen mode; remaining manager " "or parent from previous location refused to manage us.")); return FALSE; } destroy_obj((Obj*)ph); return TRUE; } /*#undef REGION_IS_FULLSCREEN #define REGION_IS_FULLSCREEN(REG) (region_get_return((WRegion*)REG)!=NULL)*/ static bool region_set_fullscreen(WRegion *reg, int sp) { bool set=REGION_IS_FULLSCREEN(reg); bool nset=libtu_do_setparam(sp, set); if(!XOR(nset, set)) return set; if(nset) region_enter_fullscreen(reg, TRUE); else region_leave_fullscreen(reg, TRUE); return REGION_IS_FULLSCREEN(reg); } /*}}}*/ /*{{{ Client window requests */ bool clientwin_fullscreen_may_switchto(WClientWin *cwin) { return (region_may_control_focus((WRegion*)cwin) || !REGION_IS_ACTIVE(region_screen_of((WRegion*)cwin))); } WScreen *clientwin_fullscreen_chkrq(WClientWin *cwin, int w, int h) { WScreen *scr; WMwmHints *mwm; WRectangle *rwgeom; mwm=xwindow_get_mwmhints(cwin->win); if(mwm==NULL || !(mwm->flags&MWM_HINTS_DECORATIONS) || mwm->decorations!=0) return NULL; FOR_ALL_SCREENS(scr){ if(!region_same_rootwin((WRegion*)scr, (WRegion*)cwin)) continue; /* Only Mplayer supports single Xinerama region FS to my knowledge, * and doesn't set position, so we also don't check position here, * and instead take the first screen with matching size. */ if(REGION_GEOM(scr).w==w && REGION_GEOM(scr).h==h) return scr; } return NULL; } /*}}}*/ /*{{{ Group exports */ /*EXTL_DOC * Set client window \var{reg} full screen state according to the * parameter \var{how} (one of \codestr{set}, \codestr{unset}, or * \codestr{toggle}). Resulting state is returned, which may not be * what was requested. */ EXTL_EXPORT_AS(WGroup, set_fullscreen) bool group_set_fullscreen_extl(WGroup *grp, const char *how) { return region_set_fullscreen((WRegion*)grp, libtu_string_to_setparam(how)); } /*}}}*/ notion-3+2012042300/ioncore/fullscreen.h000066400000000000000000000013611174530661200175340ustar00rootroot00000000000000/* * ion/ioncore/fullscreen.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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 WScreen *clientwin_fullscreen_chkrq(WClientWin *cwin, int w, int h); extern bool clientwin_fullscreen_may_switchto(WClientWin *cwin); extern bool region_fullscreen_scr(WRegion *reg, WScreen *vp, bool switchto); extern bool region_enter_fullscreen(WRegion *reg, bool switchto); extern bool region_leave_fullscreen(WRegion *reg, bool switchto); #endif /* ION_IONCORE_FULLSCREEN_H */ notion-3+2012042300/ioncore/global.h000066400000000000000000000052711174530661200166360ustar00rootroot00000000000000/* * ion/ioncore/global.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #ifndef ION_IONCORE_GLOBAL_H #define ION_IONCORE_GLOBAL_H #include "common.h" #include #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 }; enum{ IONCORE_FOCUSNEXT_OTHER, IONCORE_FOCUSNEXT_POINTERHACK, IONCORE_FOCUSNEXT_ENTERWINDOW, IONCORE_FOCUSNEXT_FALLBACK }; 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; Atom atom_dockapp_hack; WRootWin *rootwins; WScreen *screens; WRegion *focus_next; bool warp_next; int focus_next_source; /* We could have a display WRegion but the screen-link could impose * some problems so these are handled as a special case. * * This is a doubly-linked list with links 'active_next' and 'active_prev' */ 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 no_mousefocus; bool unsqueeze_enabled; bool autoraise; Time usertime_diff_current; Time usertime_diff_new; 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; struct{ StringId activated, inactivated, activity, sub_activity, name, unset_manager, set_manager, tag, set_return, unset_return, pseudoactivated, pseudoinactivated, deinit, map, unmap; } notifies; /** XShape extension presence */ bool shape_extension; int shape_event_basep; int shape_error_basep; }; extern WGlobal ioncore_g; #endif /* ION_IONCORE_GLOBAL_H */ notion-3+2012042300/ioncore/gr-util.h000066400000000000000000000007731174530661200167630ustar00rootroot00000000000000/* * ion/ioncore/gr-util.h * * Copyright (c) Tuomo Valkonen 2007-2009. * * See the included file LICENSE for details. */ #ifndef ION_IONCORE_GR_UTIL_H #define ION_IONCORE_GR_UTIL_H #include "gr.h" #define GR_ATTR(X) grattr_##X #define GR_DEFATTR(X) static GrAttr GR_ATTR(X) = STRINGID_NONE #define GR_ALLOCATTR_BEGIN static bool alloced=FALSE; if(alloced) return #define GR_ALLOCATTR_END alloced=TRUE #define GR_ALLOCATTR(X) GR_ATTR(X) = stringstore_alloc(#X) #endif /* ION_IONCORE_GR_UTIL_H */ notion-3+2012042300/ioncore/gr.c000066400000000000000000000313761174530661200160060ustar00rootroot00000000000000/* * ion/ioncore/gr.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #include #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 */ static GrAttr star_id=STRINGID_NONE; static int cmp(const void *a_, const void *b_) { StringId a=*(const StringId*)a_; StringId b=((const GrAttrScore*)b_)->attr; return (a < b ? -1 : ((a == b) ? 0 : 1)); } static uint scorefind(const GrStyleSpec *attr, const GrAttrScore *spec) { GrAttrScore *res; if(attr->attrs==NULL) return 0; if(star_id==STRINGID_NONE) star_id=stringstore_alloc("*"); if(spec->attr==star_id){ /* Since every item occurs only once on the list, with a score, * return the score of the star in the spec, instead of one. */ return spec->score; } res=bsearch(&spec->attr, attr->attrs, attr->n, sizeof(GrAttrScore), cmp); return (res==NULL ? 0 : 2*res->score); } uint gr_stylespec_score2(const GrStyleSpec *spec, const GrStyleSpec *attr1, const GrStyleSpec *attr2) { uint score=0; uint i; for(i=0; in; i++){ int sc=scorefind(attr1, &spec->attrs[i]); if(attr2!=NULL) sc=maxof(sc, scorefind(attr2, &spec->attrs[i])); if(sc==0){ score=0; break; } score+=sc; } return score; } uint gr_stylespec_score(const GrStyleSpec *spec, const GrStyleSpec *attr) { return gr_stylespec_score2(spec, attr, NULL); } static uint count_dashes(const char *str) { uint n=0; if(str!=NULL){ while(1){ const char *p=strchr(str, '-'); if(p==NULL) break; n++; str=p+1; } } return n; } bool gr_stylespec_load_(GrStyleSpec *spec, const char *str, bool no_order_score) { uint score=(no_order_score ? 1 : count_dashes(str)+1); gr_stylespec_init(spec); while(str!=NULL){ GrAttr a; const char *p=strchr(str, '-'); if(p==NULL){ a=stringstore_alloc(str); str=p; }else{ a=stringstore_alloc_n(str, p-str); str=p+1; } if(a==STRINGID_NONE) goto fail; if(!gr_stylespec_add(spec, a, score)) goto fail; stringstore_free(a); if(!no_order_score) score--; } return TRUE; fail: gr_stylespec_unalloc(spec); return FALSE; } bool gr_stylespec_load(GrStyleSpec *spec, const char *str) { return gr_stylespec_load_(spec, str, FALSE); } void gr_stylespec_unalloc(GrStyleSpec *spec) { uint i; for(i=0; in; i++) stringstore_free(spec->attrs[i].attr); if(spec->attrs!=NULL){ free(spec->attrs); spec->attrs=NULL; } spec->n=0; } void gr_stylespec_init(GrStyleSpec *spec) { spec->attrs=NULL; spec->n=0; } static bool gr_stylespec_find_(const GrStyleSpec *spec, GrAttr a, int *idx_ge) { bool found=FALSE; uint i; for(i=0; in; i++){ if(spec->attrs[i].attr>=a){ found=(spec->attrs[i].attr==a); break; } } *idx_ge=i; return found; } bool gr_stylespec_isset(const GrStyleSpec *spec, GrAttr a) { int idx_ge; return gr_stylespec_find_(spec, a, &idx_ge); } bool gr_stylespec_add(GrStyleSpec *spec, GrAttr a, uint score) { static const uint sz=sizeof(GrAttrScore); GrAttrScore *idsn; int idx_ge; if(a==GRATTR_NONE || score==0) return TRUE; if(gr_stylespec_find_(spec, a, &idx_ge)){ spec->attrs[idx_ge].score+=score; return TRUE; } idsn=(GrAttrScore*)realloc(spec->attrs, (spec->n+1)*sz); if(idsn==NULL) return FALSE; stringstore_ref(a); memmove(idsn+idx_ge+1, idsn+idx_ge, (spec->n-idx_ge)*sz); idsn[idx_ge].attr=a; idsn[idx_ge].score=score; spec->attrs=idsn; spec->n++; return TRUE; } bool gr_stylespec_set(GrStyleSpec *spec, GrAttr a) { return gr_stylespec_add(spec, a, 1); } void gr_stylespec_unset(GrStyleSpec *spec, GrAttr a) { static const uint sz=sizeof(GrAttrScore); GrAttrScore *idsn; int idx_ge; if(a==GRATTR_NONE) return; if(!gr_stylespec_find_(spec, a, &idx_ge)) return; stringstore_free(spec->attrs[idx_ge].attr); memmove(spec->attrs+idx_ge, spec->attrs+idx_ge+1, (spec->n-idx_ge-1)*sz); spec->n--; idsn=(GrAttrScore*)realloc(spec->attrs, (spec->n)*sz); if(idsn!=NULL || spec->n==0) spec->attrs=idsn; } static bool gr_stylespec_do_init_from(GrStyleSpec *dst, const GrStyleSpec *src) { uint i; if(src->n==0) return TRUE; dst->attrs=ALLOC_N(GrAttrScore, src->n); if(dst->attrs==NULL) return FALSE; for(i=0; in; i++){ dst->attrs[i]=src->attrs[i]; stringstore_ref(dst->attrs[i].attr); } dst->n=src->n; return TRUE; } bool gr_stylespec_append(GrStyleSpec *dst, const GrStyleSpec *src) { uint i; bool ok=TRUE; if(dst->attrs==NULL){ ok=gr_stylespec_do_init_from(dst, src); }else{ for(i=0; in; i++){ if(!gr_stylespec_add(dst, src->attrs[i].attr, src->attrs[i].score)) ok=FALSE; } } return ok; } bool gr_stylespec_equals(const GrStyleSpec *s1, const GrStyleSpec *s2) { uint i; if(s1->n!=s2->n) return FALSE; for(i=0; in; i++){ if(s1->attrs[i].attr!=s2->attrs[i].attr) return FALSE; } return TRUE; } /*}}}*/ /*{{{ 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) { CALL_DYN(grbrush_draw_border, brush, (brush, geom)); } void grbrush_draw_borderline(GrBrush *brush, const WRectangle *geom, GrBorderLine line) { CALL_DYN(grbrush_draw_borderline, brush, (brush, geom, line)); } /*}}}*/ /*{{{ Dynfuns/Strings */ void grbrush_draw_string(GrBrush *brush, int x, int y, const char *str, int len, bool needfill) { CALL_DYN(grbrush_draw_string, brush, (brush, x, y, str, len, needfill)); } 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, bool needfill) { CALL_DYN(grbrush_draw_textbox, brush, (brush, geom, text, needfill)); } void grbrush_draw_textboxes(GrBrush *brush, const WRectangle *geom, int n, const GrTextElem *elem, bool needfill) { CALL_DYN(grbrush_draw_textboxes, brush, (brush, geom, n, elem, needfill)); } /*}}}*/ /*{{{ 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) { CALL_DYN(grbrush_fill_area, brush, (brush, geom)); } void grbrush_clear_area(GrBrush *brush, const WRectangle *geom) { CALL_DYN(grbrush_clear_area, brush, (brush, geom)); } void grbrush_init_attr(GrBrush *brush, const GrStyleSpec *spec) { CALL_DYN(grbrush_init_attr, brush, (brush, spec)); } void grbrush_set_attr(GrBrush *brush, GrAttr attr) { CALL_DYN(grbrush_set_attr, brush, (brush, attr)); } void grbrush_unset_attr(GrBrush *brush, GrAttr attr) { CALL_DYN(grbrush_unset_attr, brush, (brush, attr)); } /*}}}*/ /*{{{ ioncore_read_config/refresh */ /*EXTL_DOC * Read drawing engine configuration file \file{look.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); /*}}}*/ notion-3+2012042300/ioncore/gr.h000066400000000000000000000126671174530661200160150ustar00rootroot00000000000000/* * ion/ioncore/gr.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #ifndef ION_IONCORE_GR_H #define ION_IONCORE_GR_H #include #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 StringId GrAttr; #define GRATTR_NONE STRINGID_NONE #define GR_STYLESPEC_INIT {0, NULL} typedef struct{ GrAttr attr; uint score; } GrAttrScore; typedef struct{ uint n; GrAttrScore *attrs; } GrStyleSpec; #define GR_TEXTELEM_INIT {NULL, 0, GR_STYLESPEC_INIT} typedef struct{ char *text; int iw; GrStyleSpec 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 */ #define GRBRUSH_KEEP_ATTR 0x0010 /* 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(); /* Every star ('*') element of 'spec' increases score by one. * Every other element of 'spec' found in 'attr' increases the score by the * number set in attr times two. Any element of 'spec' (other than star), * not found in 'attr', forces score to zero. */ extern uint gr_stylespec_score(const GrStyleSpec *spec, const GrStyleSpec *attr); extern uint gr_stylespec_score2(const GrStyleSpec *spec, const GrStyleSpec *attr, const GrStyleSpec *attr2); extern void gr_stylespec_init(GrStyleSpec *spec); extern bool gr_stylespec_set(GrStyleSpec *spec, GrAttr a); extern bool gr_stylespec_isset(const GrStyleSpec *spec, GrAttr a); extern void gr_stylespec_unset(GrStyleSpec *spec, GrAttr a); extern bool gr_stylespec_add(GrStyleSpec *spec, GrAttr a, uint score); extern bool gr_stylespec_append(GrStyleSpec *dst, const GrStyleSpec *src); extern void gr_stylespec_unalloc(GrStyleSpec *spec); extern bool gr_stylespec_equals(const GrStyleSpec *s1, const GrStyleSpec *s2); extern bool gr_stylespec_load(GrStyleSpec *spec, const char *str); extern bool gr_stylespec_load_(GrStyleSpec *spec, const char *str, bool no_order_score); /* GrBrush */ extern GrBrush *gr_get_brush(Window win, WRootWin *rootwin, const char *style); DYNFUN 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); DYNFUN void grbrush_begin(GrBrush *brush, const WRectangle *geom, int flags); DYNFUN void grbrush_end(GrBrush *brush); /* Attributes */ DYNFUN void grbrush_init_attr(GrBrush *brush, const GrStyleSpec *spec); DYNFUN void grbrush_set_attr(GrBrush *brush, GrAttr attr); DYNFUN void grbrush_unset_attr(GrBrush *brush, GrAttr attr); /* Border drawing */ DYNFUN void grbrush_get_border_widths(GrBrush *brush, GrBorderWidths *bdi); DYNFUN void grbrush_draw_border(GrBrush *brush, const WRectangle *geom); DYNFUN void grbrush_draw_borderline(GrBrush *brush, const WRectangle *geom, 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); /* Textbox drawing */ DYNFUN void grbrush_draw_textbox(GrBrush *brush, const WRectangle *geom, const char *text, bool needfill); DYNFUN void grbrush_draw_textboxes(GrBrush *brush, const WRectangle *geom, int n, const GrTextElem *elem, bool needfill); /* 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); 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 */ notion-3+2012042300/ioncore/grab.c000066400000000000000000000142401174530661200163000ustar00rootroot00000000000000/* * ion/ioncore/grab.c * * Copyright (c) Lukas Schroeder 2002, * Tuomo Valkonen 2003-2009. * * See the included file LICENSE for details. * * Alternatively, you may apply the Clarified Artistic License to this file, * since Lukas' contributions were originally under that. */ #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=None; /*region_root_of(reg);*/ current_grab->sqid=last_sqid++; watch_init(¤t_grab->watch); 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; } /*}}}*/ notion-3+2012042300/ioncore/grab.h000066400000000000000000000026461174530661200163140ustar00rootroot00000000000000/* * ion/ioncore/grab.h * * Copyright (c) Lukas Schroeder 2002, * Tuomo Valkonen 2003-2009. * * See the included file LICENSE for details. * * Alternatively, you may apply the Clarified Artistic License to this file, * since Lukas' contributions were originally under that. */ #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 */ notion-3+2012042300/ioncore/group-cw.c000066400000000000000000000206561174530661200171400ustar00rootroot00000000000000/* * ion/ioncore/group-cw.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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" #include "return.h" #include "saveload.h" #define DFLT_SZPLCY SIZEPOLICY_FREE_GLUE__SOUTH /*{{{ Add/remove managed */ static WPHolder *groupcw_transient_pholder(WGroupCW *cwg, const WClientWin *cwin, const WManageParams *mp) { WGroupAttachParams param=GROUPATTACHPARAMS_INIT; WFramedParam fp=FRAMEDPARAM_INIT; WPHolder *ph; groupattachparams_get(¶m, cwin->proptab, "attach_params"); if(!param.level_set){ param.level_set=1; param.level=STACKING_LEVEL_MODAL1; } if(!param.szplcy_set){ param.szplcy_set=1; param.szplcy=cwg->transient_szplcy; } if(!param.geom_weak_set){ param.geom_weak_set=1; param.geom_weak=REGION_RQGEOM_WEAK_ALL; } param.switchto_set=1; param.switchto=mp->switchto; 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.mode=FRAME_MODE_TRANSIENT; 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 priority) { if(!MANAGE_PRIORITY_OK(priority, MANAGE_PRIORITY_GROUP)) 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; } static WRegion *groupcw_managed_disposeroot(WGroupCW *ws, WRegion *reg) { WGroupIterTmp tmp; WStacking *st; WRegion *tmpr; FOR_ALL_NODES_IN_GROUP(&ws->grp, st, tmp){ if(st!=ws->grp.managed_stdisp && st->reg!=reg){ return reg; } } tmpr=region_disposeroot((WRegion*)ws); return (tmpr!=NULL ? tmpr : reg); } /*}}}*/ /*{{{ 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.mode=0; 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, WRegionNotify how) { if(group_bottom(&cwg->grp)==reg && how==ioncore_g.notifies.name){ /* Title has changed */ region_notify_change((WRegion*)cwg, how); } group_managed_notify(&cwg->grp, reg, how); } void groupcw_bottom_set(WGroupCW *cwg) { region_notify_change((WRegion*)cwg, ioncore_g.notifies.name); } /*}}}*/ /*{{{ Rescue */ static void group_migrate_phs_to_ph(WGroup *group, WPHolder *rph) { WGroupPHolder *phs, *ph; phs=group->phs; group->phs=NULL; phs->recreate_pholder=rph; for(ph=phs; ph!=NULL; ph=ph->next) ph->group=NULL; } bool groupcw_rescue_clientwins(WGroupCW *cwg, WRescueInfo *info) { bool ret=group_rescue_clientwins(&cwg->grp, info); /* If this group can be recreated, arrange remaining placeholders * to do so. This takes care of e.g. recreating client window groups * when recreating layout delayedly under a session manager. */ if(cwg->grp.phs!=NULL){ WPHolder *rph=region_make_return_pholder((WRegion*)cwg); if(rph!=NULL) group_migrate_phs_to_ph(&cwg->grp, rph); } return ret; } /*}}}*/ /*{{{ WGroupCW class */ bool groupcw_init(WGroupCW *cwg, WWindow *parent, const WFitParams *fp) { cwg->transient_szplcy=DFLT_SZPLCY; if(!group_init(&(cwg->grp), parent, fp)) return FALSE; 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 *cwg; ExtlTab substab, subtab; int i, n; cwg=create_groupcw(par, fp); if(cwg==NULL) return NULL; group_do_load(&cwg->grp, tab); if(cwg->grp.managed_list==NULL){ if(cwg->grp.phs!=NULL){ /* Session management hack */ WPHolder *ph=ioncore_get_load_pholder(); if(ph!=NULL) group_migrate_phs_to_ph(&cwg->grp, ph); } destroy_obj((Obj*)cwg); return NULL; } return (WRegion*)cwg; } 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_managed_disposeroot, (DynFun*)groupcw_managed_disposeroot}, {(DynFun*)region_displayname, (DynFun*)groupcw_displayname}, {region_managed_notify, groupcw_managed_notify}, {group_bottom_set, groupcw_bottom_set}, {(DynFun*)region_rescue_clientwins, (DynFun*)groupcw_rescue_clientwins}, END_DYNFUNTAB }; EXTL_EXPORT IMPLCLASS(WGroupCW, WGroup, groupcw_deinit, groupcw_dynfuntab); /*}}}*/ notion-3+2012042300/ioncore/group-cw.h000066400000000000000000000023251174530661200171360ustar00rootroot00000000000000/* * ion/ioncore/group-cw.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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); #endif /* ION_IONCORE_GROUPCW_H */ notion-3+2012042300/ioncore/group-ws.c000066400000000000000000000221701174530661200171510ustar00rootroot00000000000000/* * ion/ioncore/group-ws.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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 "framedpholder.h" #include "float-placement.h" #include "resize.h" #include "conf.h" /*{{{ Settings */ void ioncore_groupws_set(ExtlTab tab) { char *method=NULL; int fpp; 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_i(tab, "float_placement_padding", &fpp)) ioncore_placement_padding=maxof(0, fpp); } 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"))); extl_table_sets_i(t, "float_placement_padding", ioncore_placement_padding); } /*}}}*/ /*{{{ 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 frp=FRAMEDPARAM_INIT; if(reg==NULL) return FALSE; groupattachparams_get(&ap, t, NULL); /* Sensible size is given in framedparams */ if(ap.geom_set){ ap.geom_set=0; ap.geom_weak_set=1; ap.geom_weak=0; frp.inner_geom_gravity_set=1; frp.inner_geom=ap.geom; frp.gravity=NorthWestGravity; extl_table_gets_i(t, "gravity", &frp.gravity); } return groupws_attach_framed(ws, &ap, &frp, reg); } /*}}}*/ /*{{{ groupws_prepare_manage */ static WPHolder *groupws_do_prepare_manage(WGroupWS *ws, const WClientWin *cwin, const WManageParams *param) { WGroupAttachParams ap=GROUPATTACHPARAMS_INIT; WFramedParam fp=FRAMEDPARAM_INIT; WPHolder *ph; fp.inner_geom_gravity_set=TRUE; fp.inner_geom=param->geom; fp.gravity=param->gravity; groupattachparams_get(&ap, cwin->proptab, "attach_params"); if(param->userpos || !param->maprq || ioncore_g.opmode==IONCORE_OPMODE_INIT){ ap.geom_weak_set=1; ap.geom_weak=0; }else if(!ap.geom_weak_set){ ap.geom_weak_set=1; ap.geom_weak=REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_Y; } ph=(WPHolder*)create_grouppholder(&ws->grp, NULL, &ap); if(ph!=NULL) ph=pholder_either((WPHolder*)create_framedpholder(ph, &fp), ph); if(ph!=NULL){ WGroupPHolder *gph; WGroupAttachParams gp=GROUPATTACHPARAMS_INIT; gp.switchto_set=1; gp.switchto=1; gp.bottom=1; gph=create_grouppholder(NULL, NULL, &gp); if(gph!=NULL){ gph->recreate_pholder=ph; return (WPHolder*)gph; } } return ph; } WPHolder *groupws_prepare_manage(WGroupWS *ws, const WClientWin *cwin, const WManageParams *param, int priority) { int cpriority=MANAGE_PRIORITY_SUB(priority, MANAGE_PRIORITY_GROUP); int bpriority=MANAGE_PRIORITY_SUBX(priority, MANAGE_PRIORITY_GROUP); 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 use_bottom; 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, bpriority); if(ph==NULL){ /* Check current */ WRegion *r=(ws->grp.current_managed!=NULL ? ws->grp.current_managed->reg : NULL); if(r!=NULL && r!=b) ph=region_prepare_manage(r, cwin, param, cpriority); } if(ph==NULL && MANAGE_PRIORITY_OK(priority, MANAGE_PRIORITY_GROUP)) ph=groupws_do_prepare_manage(ws, cwin, param); if(ph==NULL && b!=NULL && !use_bottom) ph=region_prepare_manage(b, cwin, param, cpriority); 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; bool tmp; 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.mode=FRAME_MODE_TRANSIENT; groupattachparams_get(&ap, cwin->proptab, "attach_params"); if(!ap.geom_weak_set || param->userpos || !param->maprq || ioncore_g.opmode==IONCORE_OPMODE_INIT){ 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); } static bool group_empty_for_bottom_stdisp(WGroup *ws) { WGroupIterTmp tmp; WStacking *st; FOR_ALL_NODES_IN_GROUP(ws, st, tmp){ if(st!=ws->bottom && st!=ws->managed_stdisp) return FALSE; } return TRUE; } static WRegion *groupws_managed_disposeroot(WGroupWS *ws, WRegion *reg) { if(group_bottom(&ws->grp)==reg){ if(group_empty_for_bottom_stdisp(&ws->grp)) return region_disposeroot((WRegion*)ws); } return reg; } /*}}}*/ /*{{{ 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; } static DynFunTab groupws_dynfuntab[]={ {(DynFun*)region_prepare_manage, (DynFun*)groupws_prepare_manage}, {(DynFun*)region_prepare_manage_transient, (DynFun*)groupws_prepare_manage_transient}, {(DynFun*)region_managed_disposeroot, (DynFun*)groupws_managed_disposeroot}, {(DynFun*)region_handle_drop, (DynFun*)groupws_handle_drop}, {region_manage_stdisp, group_manage_stdisp}, END_DYNFUNTAB }; EXTL_EXPORT IMPLCLASS(WGroupWS, WGroup, groupws_deinit, groupws_dynfuntab); /*}}}*/ notion-3+2012042300/ioncore/group-ws.h000066400000000000000000000025371174530661200171630ustar00rootroot00000000000000/* * ion/ioncore/groupws.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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(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 */ notion-3+2012042300/ioncore/group.c000066400000000000000000001054751174530661200165340ustar00rootroot00000000000000/* * ion/ioncore/group.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #include #include #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" #include "return.h" static void group_place_stdisp(WGroup *ws, WWindow *parent, int pos, WRegion *stdisp); static void group_remanage_stdisp(WGroup *ws); static void group_do_set_bottom(WGroup *grp, WStacking *st); /*{{{ 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 void group_do_set_focus(WGroup *ws, bool warp) { WStacking *st=find_to_focus(ws, ws->current_managed, FALSE); if(st!=NULL && st->reg!=NULL) region_do_set_focus(st->reg, warp); else region_finalise_focusing((WRegion*)ws, ws->dummywin, warp, CurrentTime); } 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{ if(!region_prepare_focus((WRegion*)ws, flags, res)) return FALSE; st=find_to_focus(ws, st, FALSE); if(st==NULL) return FALSE; if(ioncore_g.autoraise && !(flags®ION_GOTO_ENTERWINDOW) && st->level>STACKING_LEVEL_BOTTOM){ WStacking **stackingp=group_get_stackingp(ws); stacking_restack(stackingp, st, None, NULL, NULL, FALSE); } res->reg=st->reg; res->flags=flags; return (res->reg==reg); } } void group_managed_remove(WGroup *ws, WRegion *reg) { bool mcf=region_may_control_focus((WRegion*)ws); WStacking *st, *next_st=NULL; bool was_stdisp=FALSE, was_bottom=FALSE; bool was_current=FALSE; st=group_find_stacking(ws, reg); if(st!=NULL){ if(st==ws->bottom){ was_bottom=TRUE; group_do_set_bottom(ws, NULL); } if(st==ws->managed_stdisp){ ws->managed_stdisp=NULL; was_stdisp=TRUE; } if(st==ws->current_managed){ ws->current_managed=NULL; was_current=TRUE; } next_st=stacking_unstack(REGION_PARENT(ws), st); UNLINK_ITEM(ws->managed_list, st, mgr_next, mgr_prev); stacking_unassoc(st); stacking_free(st); } region_unset_manager(reg, (WRegion*)ws); if(!OBJ_IS_BEING_DESTROYED(ws) && was_current){ /* This may still potentially cause problems when focus * change is pending. Perhaps we should use region_await_focus, * if it is pointing to our child (and region_may_control_focus * fail if it is pointing somewhere else). */ WStacking *stf=find_to_focus(ws, next_st, TRUE); if(stf!=NULL && mcf){ region_maybewarp_now(stf->reg, FALSE); }else{ ws->current_managed=stf; } } } void group_managed_notify(WGroup *ws, WRegion *reg, WRegionNotify how) { if(how==ioncore_g.notifies.activated || how==ioncore_g.notifies.pseudoactivated){ ws->current_managed=group_find_stacking(ws, reg); } } /*}}}*/ /*{{{ Create/destroy */ bool group_init(WGroup *ws, WWindow *par, const WFitParams *fp) { const char *p[1]; ws->current_managed=NULL; ws->managed_stdisp=NULL; ws->bottom=NULL; ws->managed_list=NULL; ws->phs=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; p[0] = "WGroup"; xwindow_set_text_property(ws->dummywin, XA_WM_NAME, p, 1); 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; while(ws->phs!=NULL) grouppholder_do_unlink(ws->phs); region_deinit(&ws->reg); } bool group_rescue_clientwins(WGroup *ws, WRescueInfo *info) { WGroupIterTmp tmp; group_iter_init_nostdisp(&tmp, ws); return region_rescue_some_clientwins((WRegion*)ws, info, (WRegionIterator*)group_iter, &tmp); } WPHolder *group_get_rescue_pholder_for(WGroup *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; if(REGION_PARENT(forwhat)==REGION_PARENT(ws)){ ap.geom.x-=REGION_GEOM(ws).x; ap.geom.y-=REGION_GEOM(ws).y; }else{ ap.geom_weak=REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_Y; } /* frame mode */ /*{ WFrame *frame=OBJ_CAST(forwhat, WFrame); if(frame!=NULL) fp.mode=frame->mode; }*/ ph=(WPHolder*)create_grouppholder(ws, NULL, &ap); return pholder_either((WPHolder*)create_framedpholder(ph, &fp), ph); } /*}}}*/ /*{{{ Bottom */ void group_bottom_set(WGroup *grp) { CALL_DYN(group_bottom_set, grp, (grp)); } static void group_do_set_bottom(WGroup *grp, WStacking *st) { WStacking *was=grp->bottom; WStacking *std=grp->managed_stdisp; grp->bottom=st; if(!OBJ_IS_BEING_DESTROYED(grp)){ bool noremanage=((was==st) || (was==NULL && std==NULL) || (st!=NULL && st==std) || (st==NULL && was==std)); if(!noremanage && (st==NULL || HAS_DYN(st->reg, region_manage_stdisp))){ group_remanage_stdisp(grp); } group_bottom_set(grp); } } /*EXTL_DOC * Sets the `bottom' of \var{ws}. The region \var{reg} must already * be managed by \var{ws}, unless \code{nil}. */ EXTL_EXPORT_MEMBER bool group_set_bottom(WGroup *ws, WRegion *reg) { WStacking *st=NULL; if(reg!=NULL){ st=group_find_stacking(ws, reg); if(st==NULL) return FALSE; } group_do_set_bottom(ws, st); return TRUE; } /*EXTL_DOC * Returns the `bottom' of \var{ws}. */ EXTL_SAFE EXTL_EXPORT_MEMBER WRegion *group_bottom(WGroup *ws) { return (ws->bottom!=NULL ? ws->bottom->reg : NULL); } /*}}}*/ /*{{{ 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){ if(framemode_unalt(frame_mode(frame))==FRAME_MODE_TILED) 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); } static int group_must_focus(WGroup *ws, WStacking *st) { WStacking *stacking=group_get_stacking(ws); return (stacking!=NULL && stacking_must_focus(stacking, st)); } 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; /* Stacking */ 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)); /* Fit */ szplcy=(param->szplcy_set ? param->szplcy : (param->bottom ? SIZEPOLICY_FULL_EXACT : SIZEPOLICY_VISIBILITY_CONSTRAINED)); if(!param->whatever){ 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.x>=REGION_GEOM(ws).x+REGION_GEOM(ws).w)){ weak|=REGION_RQGEOM_WEAK_X; } if((g.y+g.h<=REGION_GEOM(ws).y) || (g.y>=REGION_GEOM(ws).y+REGION_GEOM(ws).h)){ weak|=REGION_RQGEOM_WEAK_Y; } if(weak&(REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_Y) && (szplcy==SIZEPOLICY_UNCONSTRAINED || szplcy==SIZEPOLICY_VISIBILITY_CONSTRAINED || szplcy==SIZEPOLICY_FREE || szplcy==SIZEPOLICY_FREE_GLUE /* without flags */)){ /* TODO: use 'weak'? */ group_calc_placement(ws, level, &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); } /* Add */ st=group_do_add_managed(ws, reg, level, szplcy); if(st==NULL) return FALSE; if(stabove!=NULL) st->above=stabove; if(param->bottom) group_do_set_bottom(ws, st); /* Focus */ sw=((param->switchto_set ? param->switchto : ioncore_g.switchto_new) ? st==find_to_focus(ws, st, FALSE) : group_must_focus(ws, st)); if(sw){ if(region_may_control_focus((WRegion*)ws)) region_set_focus(st->reg); else ws->current_managed=st; }else if(region_is_fully_mapped(reg)){ region_pointer_focus_hack(reg); } return TRUE; } static void group_attach_fp(WGroup *ws, const WGroupAttachParams *param, WFitParams *fp) { 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; } } WRegion *group_do_attach(WGroup *ws, /*const*/ WGroupAttachParams *param, WRegionAttachData *data) { WFitParams fp; WRegion *reg; if(ws->bottom!=NULL && param->bottom){ warn(TR("'bottom' already set.")); return NULL; } group_attach_fp(ws, param, &fp); return region_attach_helper((WRegion*) ws, REGION_PARENT(ws), &fp, (WRegionDoAttachFn*)group_do_attach_final, /*(const WRegionAttachParams*)*/param, data); /* ^^^^ doesn't seem to work. */ } void groupattachparams_get(WGroupAttachParams *par, ExtlTab tab, const char *sub) { int tmp; bool tmpb; char *tmps; ExtlTab g; par->switchto_set=0; par->level_set=0; par->szplcy_set=0; par->geom_set=0; par->bottom=0; if(sub){ ExtlTab s; if(extl_table_gets_t(tab, sub, &s)){ groupattachparams_get(par, s, NULL); extl_unref_table(s); } return; } if(extl_table_is_bool_set(tab, "bottom")){ par->level=STACKING_LEVEL_BOTTOM; par->level_set=1; par->bottom=1; } if(extl_table_gets_i(tab, "level", &tmp)){ if(tmp>=0){ par->level_set=1; par->level=tmp; } } if(!par->level_set && extl_table_is_bool_set(tab, "modal")){ par->level=STACKING_LEVEL_MODAL1; par->level_set=1; } if(extl_table_gets_b(tab, "switchto", &tmpb)){ par->switchto=(tmpb!=0); par->switchto_set=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); } if(extl_table_gets_b(tab, "auto_placement", &tmpb)){ par->geom_weak_set=1; par->geom_weak=(tmpb ? REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_Y : 0); } } /*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; groupattachparams_get(&par, param, NULL); 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} & (string) Class of the object to be created. Mandatory. \\ * \var{name} & (string) Name of the object to be created. \\ * \var{switchto} & (boolean) Should the region be switched to? \\ * \var{level} & (integer) Stacking level; default is 1. \\ * \var{modal} & (boolean) Make object modal; ignored if level is set. \\ * \var{sizepolicy} & (string) Size policy; see Section \ref{sec:sizepolicies}. \\ * \var{bottom} & (boolean) Mark the attached region as the * ``bottom'' of \var{ws}. \\ * \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; groupattachparams_get(&par, param, NULL); 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; int policy=0, gravity=0; if(di->fullsize){ if(region_orientation(stdisp)==REGION_ORIENTATION_VERTICAL){ if(pos==MPLEX_STDISP_TL || pos==MPLEX_STDISP_BL) policy=SIZEPOLICY_STRETCH_LEFT; else policy=SIZEPOLICY_STRETCH_RIGHT; }else{ if(pos==MPLEX_STDISP_TL || pos==MPLEX_STDISP_TR) policy=SIZEPOLICY_STRETCH_TOP; else policy=SIZEPOLICY_STRETCH_BOTTOM; } }else{ policy=SIZEPOLICY_GRAVITY; } if(pos==MPLEX_STDISP_TL) gravity=SIZEPOLICY_VERT_TOP|SIZEPOLICY_HORIZ_LEFT; else if(pos==MPLEX_STDISP_BL) gravity=SIZEPOLICY_VERT_BOTTOM|SIZEPOLICY_HORIZ_LEFT; else if(pos==MPLEX_STDISP_TR) gravity=SIZEPOLICY_VERT_TOP|SIZEPOLICY_HORIZ_RIGHT; else /*if(pos=MPLEX_STDISP_BR)*/ gravity=SIZEPOLICY_VERT_BOTTOM|SIZEPOLICY_HORIZ_RIGHT; return (policy|gravity); } 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); } stdisp->flags|=REGION_SKIP_FOCUS; fp.g=REGION_GEOM(ws); fp.mode=0; sizepolicy(&ws->managed_stdisp->szplcy, stdisp, NULL, 0, &fp); region_fitrep(stdisp, NULL, &fp); } static void group_remanage_stdisp(WGroup *ws) { 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); } } /*}}}*/ /*{{{ Geometry requests */ 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); fp.mode=0; 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 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_mapped(stacking); 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 sti; 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) { 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 * Iterate over managed regions of \var{ws} until \var{iterfn} returns * \code{false}. * The function is called in protected mode. * This routine returns \code{true} if it reaches the end of list * without this happening. */ EXTL_SAFE EXTL_EXPORT_MEMBER bool group_managed_i(WGroup *ws, ExtlFn iterfn) { WGroupIterTmp tmp; group_iter_init(&tmp, ws); return extl_iter_objlist_(iterfn, (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; } /*EXTL_DOC * Returns the group of \var{reg}, if it is managed by one, * and \var{reg} itself otherwise. */ /*EXTL_EXPORT_MEMBER WRegion *region_group_of(WRegion *reg) { WRegion *mgr=REGION_MANAGER(reg); return (OBJ_IS(mgr, WGroup) ? mgr : reg); }*/ /*EXTL_DOC * Returns the group of \var{reg}, if \var{reg} is its bottom, * and \var{reg} itself otherwise. */ EXTL_EXPORT_MEMBER WRegion *region_groupleader_of(WRegion *reg) { WGroup *grp=REGION_MANAGER_CHK(reg, WGroup); return ((grp!=NULL && group_bottom(grp)==reg) ? (WRegion*)grp : reg); } /*}}}*/ /*{{{ 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_s(subtab, "sizepolicy", sizepolicy2string(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)){ WGroupAttachParams par=GROUPATTACHPARAMS_INIT; WRegionAttachData data; WFitParams fp; WPHolder *ph; groupattachparams_get(&par, subtab, NULL); group_attach_fp(ws, &par, &fp); ph=(WPHolder*)create_grouppholder(ws, NULL, &par); region_attach_load_helper((WRegion*)ws, REGION_PARENT(ws), &fp, (WRegionDoAttachFn*)group_do_attach_final, (void*)&par, subtab, &ph); if(ph!=NULL) destroy_obj((Obj*)ph); extl_unref_table(subtab); } } extl_unref_table(substab); } } 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_notify, group_managed_notify}, {region_managed_remove, group_managed_remove}, {(DynFun*)region_get_configuration, (DynFun*)group_get_configuration}, {(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}, {(DynFun*)region_get_rescue_pholder_for, (DynFun*)group_get_rescue_pholder_for}, END_DYNFUNTAB }; EXTL_EXPORT IMPLCLASS(WGroup, WRegion, group_deinit, group_dynfuntab); /*}}}*/ notion-3+2012042300/ioncore/group.h000066400000000000000000000101531174530661200165250ustar00rootroot00000000000000/* * ion/ioncore/group.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #ifndef ION_IONCORE_GROUP_H #define ION_IONCORE_GROUP_H #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 bottom:1; uint whatever: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; WGroupPHolder *phs; }; 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 void groupattachparams_get(WGroupAttachParams *par, ExtlTab tab, const char *sub); 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 void group_managed_notify(WGroup *ws, WRegion *reg, WRegionNotify how); extern WRegion *group_bottom(WGroup *ws); extern bool group_set_bottom(WGroup *ws, WRegion *reg); DYNFUN void group_bottom_set(WGroup *grp); extern bool group_rescue_clientwins(WGroup *ws, WRescueInfo *info); 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); extern WRegion *region_groupleader_of(WRegion *reg); /*extern WRegion *region_group_of(WRegion *reg);*/ 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 */ notion-3+2012042300/ioncore/grouppholder.c000066400000000000000000000165071174530661200201070ustar00rootroot00000000000000/* * ion/ioncore/grouppholder.c * * Copyright (c) Tuomo Valkonen 2005-2009. * * See the included file LICENSE for details. */ #include #include #include #include #include #include "group.h" #include "group-cw.h" #include "grouppholder.h" static void group_watch_handler(Watch *watch, Obj *ws); /*{{{ Primitives */ void grouppholder_do_link(WGroupPHolder *ph, WGroup *group, WRegion *stack_above) { ph->group=group; if(group!=NULL){ LINK_ITEM_FIRST(group->phs, ph, next, prev); }else{ /* This seems very crucial for detached pholders... */ ph->next=NULL; ph->prev=ph; } /* We must move stack_above pointer into a Watch. */ if(stack_above!=NULL) watch_setup(&(ph->stack_above_watch), (Obj*)stack_above, NULL); } static WGroupPHolder *get_head(WGroupPHolder *ph) { while(1){ /* ph->prev==NULL should not happen.. */ if(ph->prev==NULL || ph->prev->next==NULL) break; ph=ph->prev; } return ph; } void grouppholder_do_unlink(WGroupPHolder *ph) { WGroup *group=ph->group; watch_reset(&(ph->stack_above_watch)); if(ph->recreate_pholder!=NULL){ if(ph->next!=NULL){ ph->next->recreate_pholder=ph->recreate_pholder; }else{ /* It might be in use in attach chain! So defer. */ mainloop_defer_destroy((Obj*)ph->recreate_pholder); } ph->recreate_pholder=NULL; } if(group!=NULL){ UNLINK_ITEM(group->phs, ph, next, prev); }else if(ph->prev!=NULL){ WGroupPHolder *next=ph->next; ph->prev->next=next; if(next==NULL){ next=get_head(ph); assert(next->prev==ph); } next->prev=ph->prev; }else{ /* ph should not be on a list, if prev pointer is NULL (whereas * next alone can be NULL in our semi-doubly-linked lists). */ assert(ph->next==NULL); } ph->group=NULL; ph->next=NULL; ph->prev=NULL; } /*}}}*/ /*{{{ Init/deinit */ static WGroupAttachParams dummy_param=GROUPATTACHPARAMS_INIT; bool grouppholder_init(WGroupPHolder *ph, WGroup *ws, const WStacking *st, const WGroupAttachParams *param) { WRegion *stack_above=NULL; pholder_init(&(ph->ph)); watch_init(&(ph->stack_above_watch)); ph->next=NULL; ph->prev=NULL; ph->group=NULL; ph->recreate_pholder=NULL; ph->param=(param==NULL ? dummy_param : *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.bottom=(st==ws->bottom); } ph->param.switchto_set=FALSE; stack_above=ph->param.stack_above; ph->param.stack_above=NULL; grouppholder_do_link(ph, ws, stack_above); 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) { grouppholder_do_unlink(ph); pholder_deinit(&(ph->ph)); } /*}}}*/ /*{{{ Dynfuns */ static WPHolder *get_recreate_ph(WGroupPHolder *ph) { return get_head(ph)->recreate_pholder; } typedef struct{ WGroupPHolder *ph, *ph_head; WRegionAttachData *data; WRegion *reg_ret; } RP; static WRegion *recreate_handler(WWindow *par, const WFitParams *fp, void *rp_) { WGroupPHolder *phtmp; RP *rp=(RP*)rp_; WGroup *grp; grp=(WGroup*)create_groupcw(par, fp); if(grp==NULL) return NULL; rp->ph->param.whatever=(fp->mode®ION_FIT_WHATEVER ? 1 : 0); rp->reg_ret=group_do_attach(grp, &rp->ph->param, rp->data); rp->ph->param.whatever=0; if(rp->reg_ret==NULL){ destroy_obj((Obj*)grp); return NULL; }else{ grp->phs=rp->ph_head; for(phtmp=grp->phs; phtmp!=NULL; phtmp=phtmp->next) phtmp->group=grp; } if(fp->mode®ION_FIT_WHATEVER) REGION_GEOM(grp)=REGION_GEOM(rp->reg_ret); return (WRegion*)grp; } static WRegion *grouppholder_attach_recreate(WGroupPHolder *ph, int flags, WRegionAttachData *data) { WRegionAttachData data2; WPHolder *root, *rph; WRegion *res; RP rp; rp.ph_head=get_head(ph); assert(rp.ph_head!=NULL); rph=rp.ph_head->recreate_pholder; if(rph==NULL) return NULL; rp.ph=ph; rp.data=data; rp.reg_ret=NULL; data2.type=REGION_ATTACH_NEW; data2.u.n.fn=recreate_handler; data2.u.n.param=&rp; res=pholder_do_attach(rph, flags, &data2); if(res!=NULL){ rp.ph_head->recreate_pholder=NULL; /* It might be in use in attach chain! So defer. */ mainloop_defer_destroy((Obj*)rph); } return (flags&PHOLDER_ATTACH_RETURN_CREATEROOT ? (WRegion*)res : rp.reg_ret); } WRegion *grouppholder_do_attach(WGroupPHolder *ph, int flags, WRegionAttachData *data) { WGroup *ws=ph->group; WRegion *reg; if(ws==NULL) return grouppholder_attach_recreate(ph, flags, data); 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) { return (ph->group!=NULL ? region_goto((WRegion*)ph->group) : (ph->recreate_pholder!=NULL ? pholder_do_goto(ph->recreate_pholder) : FALSE)); } WRegion *grouppholder_do_target(WGroupPHolder *ph) { return (ph->group!=NULL ? (WRegion*)ph->group : (ph->recreate_pholder!=NULL ? pholder_do_target(ph->recreate_pholder) : NULL)); } /*}}}*/ /*{{{ 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); /*}}}*/ notion-3+2012042300/ioncore/grouppholder.h000066400000000000000000000030201174530661200200760ustar00rootroot00000000000000/* * ion/ioncore/grouppholder.h * * Copyright (c) Tuomo Valkonen 2005-2009. * * See the included file LICENSE for details. */ #ifndef ION_IONCORE_GROUPPHOLDER_H #define ION_IONCORE_GROUPPHOLDER_H #include #include #include "group.h" DECLCLASS(WGroupPHolder){ WPHolder ph; WGroup *group; Watch stack_above_watch; WGroupAttachParams param; WGroupPHolder *next, *prev; WPHolder *recreate_pholder; }; 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); extern void grouppholder_do_unlink(WGroupPHolder *ph); extern void grouppholder_do_link(WGroupPHolder *ph, WGroup *group, WRegion *stack_above); #endif /* ION_IONCORE_GROUPPHOLDER_H */ notion-3+2012042300/ioncore/infowin.c000066400000000000000000000117231174530661200170410ustar00rootroot00000000000000/* * ion/ioncore/infowin.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #include #include #include "common.h" #include "global.h" #include "window.h" #include "infowin.h" #include "resize.h" #include "gr.h" #include "event.h" #include "strings.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, "WInfoWin")) 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; gr_stylespec_init(&p->attr); infowin_updategr(p); if(p->brush==NULL) goto fail3; /* 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: gr_stylespec_unalloc(&p->attr); 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->style!=NULL){ free(p->style); p->style=NULL; } if(p->brush!=NULL){ grbrush_release(p->brush); p->brush=NULL; } gr_stylespec_unalloc(&p->attr); 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_init_attr(p->brush, &p->attr); grbrush_draw_textbox(p->brush, &g, p->buffer, 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 */ GrStyleSpec *infowin_stylespec(WInfoWin *p) { return &p->attr; } 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, int maxw) { bool set=FALSE; if(str==NULL){ INFOWIN_BUFFER(p)[0]='\0'; }else{ if(maxw>0 && p->brush!=NULL){ char *tmp=grbrush_make_label(p->brush, str, maxw); if(tmp!=NULL){ infowin_do_set_text(p, tmp); free(tmp); set=TRUE; } } if(!set) 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); /*}}}*/ notion-3+2012042300/ioncore/infowin.h000066400000000000000000000021051174530661200170400ustar00rootroot00000000000000/* * ion/ioncore/infowin.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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 *style; GrStyleSpec attr; }; #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, int maxw); extern GrStyleSpec *infowin_stylespec(WInfoWin *p); extern WRegion *infowin_load(WWindow *par, const WFitParams *fp, ExtlTab tab); extern void infowin_updategr(WInfoWin *p); #endif /* ION_IONCORE_INFOWIN_H */ notion-3+2012042300/ioncore/ioncore.c000066400000000000000000000412651174530661200170320ustar00rootroot00000000000000/* * ion/ioncore/ioncore.c * * Copyright (c) The Notion Team 2011. * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #include #include #include #include #include #include #include #include #include #ifndef CF_NO_LOCALE #include #include #endif #ifndef CF_NO_GETTEXT #include #endif #include #include #include #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 "exec.h" #include "screen-notify.h" #include "key.h" #include "../version.h" #include "exports.h" /*{{{ Variables */ WGlobal ioncore_g; static const char *progname="notion"; static const char ioncore_copy[]= "Notion " NOTION_VERSION ", see the README for copyright details."; static const char ioncore_license[]=DUMMY_TR( "This software is licensed under the GNU Lesser General Public License\n" "(LGPL), version 2.1, extended with terms applying to the use of the\n" "former name of the project, Ion(tm), unless otherwise indicated in\n" "components taken from elsewhere. For details, see the file LICENSE\n" "that you should have received with this software.\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.\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, ...) { va_list args; va_start(args, str); if(ioncore_g.opmode==IONCORE_OPMODE_INIT){ fprintf(stderr, "%s: ", libtu_progname()); vfprintf(stderr, str, args); fprintf(stderr, "\n"); }else{ warn_v(str, args); } va_end(args); } /*}}}*/ /*{{{ 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(TR("Statefull encodings are unsupported.")); return FALSE; } ioncore_g.enc_sb=FALSE; ioncore_g.use_mb=TRUE; return TRUE; integr_err: warn(TR("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; } #endif #ifndef CF_NO_GETTEXT #define TEXTDOMAIN "notion" 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 ADD_HOOK_(NM, FN) \ if(!hook_add(NM, (void (*)())FN)) return FALSE #define INIT_HOOK(NM, DFLT) INIT_HOOK_(NM); ADD_HOOK_(NM, DFLT) 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_(clientwin_mapped_hook); INIT_HOOK_(clientwin_unmapped_hook); INIT_HOOK_(clientwin_property_change_hook); INIT_HOOK_(ioncore_submap_ungrab_hook); INIT_HOOK_(region_notify_hook); ADD_HOOK_(region_notify_hook, ioncore_screen_activity_notify); ADD_HOOK_(region_notify_hook, ioncore_region_notify); 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); INIT_HOOK(ioncore_exec_environ_hook, ioncore_setup_environ); 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; } #define INITSTR(NM) \ ioncore_g.notifies.NM=stringstore_alloc(#NM); \ if(ioncore_g.notifies.NM==STRINGID_NONE) return FALSE; static bool 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_next_source=IONCORE_FOCUSNEXT_OTHER; 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.usertime_diff_current=CF_USERTIME_DIFF_CURRENT; ioncore_g.usertime_diff_new=CF_USERTIME_DIFF_NEW; ioncore_g.opaque_resize=0; ioncore_g.warp_enabled=TRUE; ioncore_g.switchto_new=TRUE; ioncore_g.no_mousefocus=FALSE; ioncore_g.unsqueeze_enabled=TRUE; ioncore_g.autoraise=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; ioncore_g.shape_extension=FALSE; ioncore_g.shape_event_basep=0; ioncore_g.shape_error_basep=0; INITSTR(activated); INITSTR(inactivated); INITSTR(activity); INITSTR(sub_activity); INITSTR(name); INITSTR(unset_manager); INITSTR(set_manager); INITSTR(unset_return); INITSTR(set_return); INITSTR(pseudoactivated); INITSTR(pseudoinactivated); INITSTR(tag); INITSTR(deinit); INITSTR(map); INITSTR(unmap); return TRUE; } bool ioncore_init(const char *prog, int argc, char *argv[], const char *localedir) { if(!init_global()) return FALSE; progname=prog; ioncore_g.argc=argc; ioncore_g.argv=argv; #ifndef CF_NO_LOCALE init_locale(); #endif #ifndef CF_NO_GETTEXT 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); if(XShapeQueryExtension(ioncore_g.dpy, &ioncore_g.shape_event_basep, &ioncore_g.shape_error_basep)) ioncore_g.shape_extension=TRUE; else XMissingExtension(ioncore_g.dpy, "SHAPE"); 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_dockapp_hack=XInternAtom(dpy, "_ION_DOCKAPP_HACK", 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 Notion 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; } /*}}}*/ notion-3+2012042300/ioncore/ioncore.h000066400000000000000000000015421174530661200170310ustar00rootroot00000000000000/* * ion/ioncore/focus.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #ifndef ION_IONCORE_IONCORE_H #define ION_IONCORE_IONCORE_H #include #include "common.h" #define IONCORE_STARTUP_ONEROOT 0x0001 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 */ notion-3+2012042300/ioncore/ioncore_bindings.lua000066400000000000000000000165041174530661200212440ustar00rootroot00000000000000-- -- ion/share/ioncore-bindings.lua -- -- Copyright (c) Tuomo Valkonen 2004-2009. -- -- See the included file LICENSE for details. -- 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 gerr 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 fn(_, _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(keyspec, list) if not list then return function(lst) return submap(keyspec, lst) end end return {action = "kpress", kcb = keyspec, 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{cmd}. -- The \var{guard} controls when the binding can be called. -- For more informationp 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{ioncore.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 --DOC -- Submap enter event for bindings. function ioncore.submap_enter(cmd, guard) return putcmd(cmd, guard, {action = "submap_enter"}) end --DOC -- Submap modifier release event for bindings. function ioncore.submap_wait(cmd, guard) return putcmd(cmd, guard, {action = "submap_wait"}) end -- DOC -- Submap leave event for bindings. --function ioncore.submap_leave(cmd, guard) -- return putcmd(cmd, guard, {action = "submap_leave"}) --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{cmd}. -- 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{ioncore.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{ioncore.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 notion-3+2012042300/ioncore/ioncore_efbb.lua000066400000000000000000000014521174530661200203410ustar00rootroot00000000000000-- -- ion/share/ioncore_efbb.lua -- Minimal emergency fallback bindings. -- -- Copyright (c) Tuomo Valkonen 2004-2009. -- -- See the included file LICENSE for details. -- 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), }) }) notion-3+2012042300/ioncore/ioncore_ext.lua000066400000000000000000000044041174530661200202430ustar00rootroot00000000000000-- -- ion/share/ioncore_ext.lua -- Ioncore Lua library -- -- Copyright (c) Tuomo Valkonen 2004-2009. -- -- See the included file LICENSE for details. -- -- 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') dopath('ioncore_tabnum') dopath('ioncore_quasiact') -- 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) notioncore = ioncore -- Export some important functions into global namespace. export(ioncore, notioncore, "submap", "submap_enter", "submap_wait", "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 notion-3+2012042300/ioncore/ioncore_luaext.lua000066400000000000000000000035011174530661200207420ustar00rootroot00000000000000-- -- ion/share/ioncore_luaext.lua -- -- Copyright (c) Tuomo Valkonen 2004-2009. -- -- See the included file LICENSE for details. -- --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({...}) do _G[v]=lib[v] end end notion-3+2012042300/ioncore/ioncore_menudb.lua000066400000000000000000000246301174530661200207200ustar00rootroot00000000000000-- -- ion/ioncore/ioncore_menudb.lua -- Routines for defining menus. -- -- Copyright (c) Tuomo Valkonen 2004-2009. -- -- See the included file LICENSE for details. -- 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 for k, v in ipairs(tab) do table.insert(menus[name], v) end 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 local a1, a2 = ... if a2 and type(a1)=="string" then tab=a2 tab.label=ioncore.gettext(a1) else tab=a1 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, ...) if type(menu)=="string" then return ioncore.evalmenu(menus[menu], ...) elseif type(menu)=="function" then return menu(...) 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. The -- parameter \var{cmd} and \var{guard_or_opts} (when string) are similar -- to those of \fnref{ioncore.defbindings}. If \var{guard_or_opts} is -- a table, it may contains the \var{guard} field, and the \var{priority} -- field, for controlling positioning of entries in context menus. -- (The default priority is 1 for most entries, and -1 for auto-generated -- submenus.) function ioncore.menuentry(name, cmd, guard_or_opts) local guard local opts if type(guard_or_opts)=="string" then guard=guard_or_opts elseif type(guard_or_opts)=="table" then opts=guard_or_opts guard=opts.guard end local fn, gfn=ioncore.compile_cmd(cmd, guard) if fn then return table.append({ name=ioncore.gettext(name), func=fn, guard_func=gfn, }, opts or {}) 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) return table.append({ name=ioncore.gettext(name), submenu_fn=function() return ioncore.evalmenu (sub_or_name) end, }, options or {}) end -- }}} -- Workspace and window lists {{{ local function addto(list) return function(tgt, attr) local e=menuentry(tgt:name(), function() tgt:goto() end) e.attr=attr; table.insert(list, e) return true end end local function sort(entries) table.sort(entries, function(a, b) return a.name < b.name end) return entries end function menus.windowlist() local entries={} ioncore.clientwin_i(addto(entries)) return sort(entries) end function menus.workspacelist() local entries={} local iter_=addto(entries) local function iter(obj) return (not obj_is(obj, "WGroupWS") or iter_(obj)) end ioncore.region_i(iter) return sort(entries) end local function focuslist(do_act) local entries={} local seen={} local iter_=addto(entries) local function iter(obj, attr) if obj_is(obj, "WClientWin") then iter_(obj, attr) seen[obj]=true end return true end local function iter_act(obj) return iter(obj, "activity") end local function iter_foc(obj) return (seen[obj] or iter(obj)) end if do_act then -- Windows with activity first ioncore.activity_i(iter_act) end -- The ones that have been focused in their lifetime ioncore.focushistory_i(iter_foc) -- And then the rest ioncore.clientwin_i(iter_foc) return entries end menus.focuslist=function() return focuslist(true) end menus.focuslist_=function() return focuslist(false) 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.gmatch(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) 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 m=table.icat(m, cp(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 local function sortmenu(m) local v=1/2 for _, e in ipairs(m) do e.priority=(e.priority or 1)+v v=v/2 end table.sort(m, function(e1, e2) return e1.priority > e2.priority end) return m end function menus.ctxmenu(reg, sub) local m, r, s if obj_is(sub, "WGroup") then sub=(sub:bottom() or sub) end -- First, stuff between reg (inclusive) and sub_or_chld (inclusive) -- at the top level in the menu. r=(sub or reg) while r and s~=reg do local mm=get_ctxmenu(r, s) m=((m and table.icat(mm, m)) or mm) s=r r=r:manager() end m=(m or {}) -- Then stuff below reg (exclusive) as submenus while r do local mm = get_ctxmenu(r, s) if #mm>0 then local nm=mm.label or obj_typename(reg) local tmp=ioncore.submenu(nm, sortmenu(mm), {priority=-1}) table.insert(m, tmp) end s=r r=r:manager() end return sortmenu(m) end -- }}} ioncore.refresh_stylelist() notion-3+2012042300/ioncore/ioncore_misc.lua000066400000000000000000000050221174530661200203730ustar00rootroot00000000000000-- -- ion/share/ioncore_misc.lua -- -- Copyright (c) Tuomo Valkonen 2004-2009. -- -- See the included file LICENSE for details. -- local group_tmpl = { type="WGroupWS" } local default_tmpl = { switchto=true } local empty = { type="WGroupWS", managed={} } local layouts={ empty = empty, default = empty, } --DOC -- Define a new workspace layout with name \var{name}, and -- attach/creation parameters given in \var{tab}. The layout -- "empty" may not be defined. function ioncore.deflayout(name, tab) assert(name ~= "empty") if name=="default" and not tab then layouts[name] = empty else layouts[name] = table.join(tab, group_tmpl) end end --DOC -- Get named layout (or all of the latter parameter is set, -- but this is for internal use only). function ioncore.getlayout(name, all) if all then return layouts else return layouts[name] end end ioncore.set{_get_layout=ioncore.getlayout} --DOC -- Create new workspace on screen \var{scr}. The table \var{tmpl} -- may be used to override parts of the layout named with \code{layout}. -- If no \var{layout} is given, "default" is used. function ioncore.create_ws(scr, tmpl, layout) local lo=ioncore.getlayout(layout or "default") assert(lo, TR("Unknown layout")) return scr:attach_new(table.join(tmpl or default_tmpl, lo)) 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), ...) end --DOC -- Attach tagged regions to \var{reg}. The method of attach -- depends on the types of attached regions and whether \var{reg} -- implements \code{attach_framed} and \code{attach}. If \var{param} -- is not set, the default of \verb!{switchto=true}! is used. -- The function returns \code{true} if all tagged regions were -- succesfully attached, and \code{false} otherwisse. function ioncore.tagged_attach(reg, param) local errors=false if not param then param={switchto=true} end local tagged=function() return ioncore.tagged_first(true) end for r in tagged do local fn=((not obj_is(r, "WWindow") and reg.attach_framed) or reg.attach) if not (fn and fn(reg, r, param)) then errors=true end end return not errors end notion-3+2012042300/ioncore/ioncore_quasiact.lua000066400000000000000000000033501174530661200212540ustar00rootroot00000000000000-- -- ion/share/ioncore_quasiact.lua -- Frame quasiactivation support -- -- Copyright (c) Tuomo Valkonen 2007-2009. -- -- See the included file LICENSE for details. -- local qa_source={} local qa_target={} local function quasi_activated(frame, src) local old=qa_source[frame] if old then qa_target[old]=nil end qa_source[frame]=src qa_target[src]=frame ioncore.defer(function() frame:set_grattr("quasiactive", "set") end) end local function quasi_inactivated(frame, src) qa_source[frame]=nil qa_target[src]=nil ioncore.defer(function() frame:set_grattr("quasiactive", "unset") end) end local function activated(src) local tgt=src:__return_target() if obj_is(tgt, "WFrame") then quasi_activated(tgt, src) elseif obj_is(tgt, "WGroup") then local mgr=tgt:manager() if obj_is(mgr, "WFrame") then quasi_activated(mgr, src) end end end local function inactivated(src) local tgt=qa_target[src] if tgt then quasi_inactivated(tgt, src) return true end end local function deinit(tgt) local src=qa_source[tgt] if src then qa_target[src]=nil qa_source[tgt]=nil end end local function quasiact_notify(reg, how) if how=="activated" or how=="pseudoactivated" then activated(reg) elseif how=="inactivated" or how=="pseudoinactivated" then inactivated(reg) elseif how=="set_return" then if reg:is_active(true--[[pseudoact--]]) then activated(reg) end elseif how=="unset_return" then inactivated(reg) elseif how=="deinit" then inactivated(reg) deinit(reg) end end ioncore.get_hook("region_notify_hook"):add(quasiact_notify) notion-3+2012042300/ioncore/ioncore_tabnum.lua000066400000000000000000000024521174530661200207320ustar00rootroot00000000000000-- -- ion/share/ioncore_tabnum.lua -- Ioncore tab numbering support -- -- Copyright (c) Tuomo Valkonen 2007-2009. -- -- See the included file LICENSE for details. -- ioncore.tabnum={} local framestate={} local function do_show(frame) if obj_exists(frame) then frame:set_grattr('numbered', 'set') framestate[frame]='set' else framestate[frame]=nil end end --DOC -- Show tab numbers on \var{frame}, clearing them when submap -- grab is released the next time. If \var{delay} is given, in -- milliseconds, the numbers are not actually displayed until this -- time has passed. function ioncore.tabnum.show(frame, delay) if delay and delay>0 then local tmr=ioncore.create_timer() framestate[frame]=tmr tmr:set(delay, function() do_show(frame) end) else do_show(frame) end end --DOC -- Clear all tab numbers set by \fnref{ioncore.tabnum.show}. function ioncore.tabnum.clear() local st=framestate framestate={} for f, s in pairs(st) do if s=='set' then if obj_exists(f) then f:set_grattr('numbered', 'unset') end elseif obj_is(s, "WTimer") then s:reset() end end end ioncore.get_hook("ioncore_submap_ungrab_hook") :add(ioncore.tabnum.clear) notion-3+2012042300/ioncore/ioncore_wd.lua000066400000000000000000000071761174530661200200660ustar00rootroot00000000000000-- -- ion/share/ioncore_wd.lua -- -- Copyright (c) Tuomo Valkonen 2004-2009. -- -- See the included file LICENSE for details. -- local savefile="saved_wd" local dirs={} local lfs if pcall(function() return require('lfs') end) then lfs=_G["lfs"] end local function checkdir(d) if not lfs then return true else local t, err=lfs.attributes(d, "mode") if not t then return nil, err elseif t=="directory" then return true else return nil, 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(dir==nil or 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 rx="exec "..rx 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_on(reg, 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 local nm=obj_exists(r) and r:name() if nm then t[nm]=d end 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() notion-3+2012042300/ioncore/ioncore_winprops.lua000066400000000000000000000064111174530661200213240ustar00rootroot00000000000000-- -- ion/share/ioncore_winprops.lua -- -- Copyright (c) Tuomo Valkonen 2004-2009. -- -- See the included file LICENSE for details. -- local ioncore=_G.ioncore local winprops={} local function ifnil(...) local arg={...} 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, id) then if prop.oneshot then table.remove(props, idx) end return prop end end end end end end ioncore.set{_get_winprop=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, 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_dflt(prop, cwin, id) local function chkf(p, i) if p==nil then return true else return (p==(i and true or false)) -- hack for nil i end end if not chkf(prop.is_transient, id.is_transient) then return false end if not chkf(prop.is_dockapp, id.is_dockapp) then return false end if prop.name then local nm=cwin:name() if nm then local st, en=string.find(nm, prop.name) return (st and en) else return false end end return true 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_dflt end do_add_winprop(class, role, instance, list2) end notion-3+2012042300/ioncore/kbresize.c000066400000000000000000000222611174530661200172050ustar00rootroot00000000000000/* * ion/ioncore/kbresize.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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; /* Here to not have to write other set callback for resize code... */ int ioncore_edge_resistance=CF_EDGE_RESISTANCE; 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, er; 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); if(extl_table_gets_i(tab, "edge_resistance", &er)) ioncore_edge_resistance=maxof(0, er); } 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); extl_table_sets_i(tab, "edge_resistance", ioncore_edge_resistance); } 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_rqgeom_extl, (ExtlExportedFn*)&moveresmode_geom, (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 * Request exact geometry in move/resize mode. For details on parameters, * see \fnref{WRegion.rqgeom}. */ EXTL_EXPORT_AS(WMoveresMode, rqgeom) ExtlTab moveresmode_rqgeom_extl(WMoveresMode *mode, ExtlTab g) { WRQGeomParams rq=RQGEOMPARAMS_INIT; WRectangle res; rqgeomparams_from_table(&rq, &mode->geom, g); moveresmode_rqgeom(mode, &rq, &res); return extl_table_from_rectangle(&res); } /*EXTL_DOC * Returns current geometry. */ EXTL_EXPORT_MEMBER ExtlTab moveresmode_geom(WMoveresMode *mode) { return extl_table_from_rectangle(&mode->geom); } /*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; } /*}}}*/ notion-3+2012042300/ioncore/kbresize.h000066400000000000000000000020411174530661200172040ustar00rootroot00000000000000/* * ion/ioncore/kbresize.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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 ExtlTab moveresmode_geom(WMoveresMode *mode); extern ExtlTab moveresmode_rqgeom_extl(WMoveresMode *mode, ExtlTab g); extern void moveresmode_accel(WMoveresMode *mode, int *wu, int *hu, int accel_mode); #endif /* ION_IONCORE_KBRESIZE_H */ notion-3+2012042300/ioncore/key.c000066400000000000000000000237361174530661200161670ustar00rootroot00000000000000/* * ion/ioncore/key.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #include #include #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 "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) { return (ioncore_unmod(ev->xkey.state, ev->xkey.keycode)==0); } 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_sub(WSubmapState *p) { /*extl_unref_fn(p->leave); watch_reset(&p->leave_reg); */ free(p); } void region_free_submapstat(WRegion *reg) { while(reg->submapstat!=NULL){ WSubmapState *p=reg->submapstat; reg->submapstat=p->next; free_sub(p); } } WHook *ioncore_submap_ungrab_hook=NULL; static void call_submap_ungrab_hook() { hook_call_v(ioncore_submap_ungrab_hook); } static void clear_subs(WRegion *reg) { region_free_submapstat(reg); mainloop_defer_action(NULL, (WDeferredAction*)call_submap_ungrab_hook); /* while(reg!=NULL && reg->submapstat!=NULL){ WSubmapState *p=reg->submapstat; reg->submapstat=p->next; if(p->leave!=extl_fn_none() && p->leave_reg.obj!=NULL){ Watch regw=WATCH_INIT; watch_setup(®w, (Obj*)reg, NULL); extl_call(p->leave, "o", NULL, p->leave_reg.obj); reg=(WRegion*)regw.obj; watch_reset(®w); } free_sub(p); } */ } static WSubmapState *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 NULL; s->key=key; s->state=state; /*s->leave=extl_fn_none(); watch_init(&s->leave_reg);*/ *p=s; return s; } static XKeyEvent *current_key_event=NULL; static uint current_kcb, current_state; static bool current_submap; /* Note: state set to AnyModifier for submaps */ bool ioncore_current_key(uint *kcb, uint *state, bool *sub) { if(current_kcb==0) return FALSE; *kcb=current_kcb; *state=current_state; *sub=current_submap; return TRUE; } enum{GRAB_NONE, GRAB_NONE_SUBMAP, GRAB_SUBMAP, GRAB_WAITRELEASE}; static WBinding *lookup_binding_(WRegion *reg, int act, uint state, uint kcb, WSubmapState *st, WRegion **binding_owner, WRegion **subreg) { WBinding *binding; *subreg=NULL; do{ binding=region_lookup_keybinding(reg, act, state, kcb, st, binding_owner); if(binding!=NULL) break; if(OBJ_IS(reg, WRootWin)) break; *subreg=reg; reg=REGION_PARENT_REG(reg); }while(reg!=NULL); return binding; } static WBinding *lookup_binding(WRegion *oreg, int act, uint state, uint kcb, WRegion **binding_owner, WRegion **subreg) { WRegion *reg=oreg; /* Find the deepest nested active window grabbing this key. */ while(reg->active_sub!=NULL) reg=reg->active_sub; return lookup_binding_(reg, act, state, kcb, oreg->submapstat, binding_owner, subreg); } static void do_call_binding(WBinding *binding, WRegion *reg, WRegion *subreg) { WRegion *mgd=region_managed_within(reg, subreg); /* TODO: having to pass both mgd and subreg for some handlers * to work is ugly and complex. */ extl_call(binding->func, "ooo", NULL, reg, mgd, subreg); } static int do_key(WRegion *oreg, XKeyEvent *ev) { WBinding *binding=NULL; WRegion *binding_owner=NULL, *subreg=NULL; bool grabbed=(oreg->flags®ION_BINDINGS_ARE_GRABBED); int ret=GRAB_NONE; if(grabbed){ binding=lookup_binding(oreg, BINDING_KEYPRESS, ev->state, ev->keycode, &binding_owner, &subreg); }else{ binding=region_lookup_keybinding(oreg, BINDING_KEYPRESS, ev->state, ev->keycode, oreg->submapstat, &binding_owner); } if(binding!=NULL){ bool subs=(oreg->submapstat!=NULL); WBinding *call=NULL; if(binding->submap!=NULL){ WSubmapState *s=add_sub(oreg, ev->keycode, ev->state); if(s!=NULL){ /*WRegion *own2, *subreg2; call=lookup_binding(binding_owner, BINDING_SUBMAP_LEAVE, 0, 0, oreg->submapstat, &own2, &subreg2); if(call!=NULL){ s->leave=extl_ref_fn(call->func); watch_setup(&s->leave_reg, (Obj*)own2, NULL); }*/ call=lookup_binding_(binding_owner, BINDING_SUBMAP_ENTER, 0, 0, oreg->submapstat, &binding_owner, &subreg); ret=(grabbed ? GRAB_SUBMAP : GRAB_NONE_SUBMAP); } }else{ call=binding; if(grabbed) XUngrabKeyboard(ioncore_g.dpy, CurrentTime); if(ev->state!=0 && !subs && binding->wait) ret=GRAB_WAITRELEASE; } if(call!=NULL){ current_kcb=ev->keycode; current_state=ev->state; current_submap=subs; do_call_binding(call, binding_owner, subreg); current_kcb=0; } }else if(oreg->submapstat==NULL && OBJ_IS(oreg, WWindow)){ insstr((WWindow*)oreg, ev); } return ret; } static bool submapgrab_handler(WRegion* reg, XEvent *xev) { XKeyEvent *ev=&xev->xkey; if(ev->type!=KeyPress){ if(ioncore_unmod(ev->state, ev->keycode)==0){ WBinding *binding; WRegion *binding_owner, *subreg; binding=lookup_binding(reg, BINDING_SUBMAP_RELEASEMOD, 0, 0, &binding_owner, &subreg); if(binding!=NULL) do_call_binding(binding, binding_owner, subreg); } return FALSE; } if(ioncore_ismod(ev->keycode)) return FALSE; if(do_key(reg, ev)!=GRAB_SUBMAP){ clear_subs(reg); return TRUE; }else{ return FALSE; } } 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){ Watch w=WATCH_INIT; int grab; /* reg might be destroyed by binding handlers */ watch_setup(&w, (Obj*)reg, NULL); grab=do_key(reg, ev); reg=(WRegion*)w.obj; if(reg!=NULL){ if(grab==GRAB_SUBMAP) submapgrab(reg); else if(grab==GRAB_WAITRELEASE) waitrelease(reg); else if(grab==GRAB_NONE_SUBMAP) /* nothing */; else if(grab==GRAB_NONE && reg->submapstat!=NULL) clear_subs(reg); } watch_reset(&w); } } notion-3+2012042300/ioncore/key.h000066400000000000000000000011031174530661200161540ustar00rootroot00000000000000/* * ion/ioncore/key.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #ifndef ION_IONCORE_KEY_H #define ION_IONCORE_KEY_H #include #include #include "common.h" #include "clientwin.h" extern void ioncore_do_handle_keypress(XKeyEvent *ev); extern void clientwin_quote_next(WClientWin *cwin); extern bool ioncore_current_key(uint *kcb, uint *state, bool *sub); extern void region_free_submapstat(WRegion *reg); extern WHook *ioncore_submap_ungrab_hook; #endif /* ION_IONCORE_KEY_H */ notion-3+2012042300/ioncore/llist.c000066400000000000000000000037751174530661200165270ustar00rootroot00000000000000/* * ion/ioncore/llist.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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); } /*}}}*/ notion-3+2012042300/ioncore/llist.h000066400000000000000000000030011174530661200165120ustar00rootroot00000000000000/* * ion/ioncore/llist.h * * Copyright (c) Tuomo Valkonen 2005-2009. * * See the included file LICENSE for details. */ #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 */ notion-3+2012042300/ioncore/manage.c000066400000000000000000000275761174530661200166350ustar00rootroot00000000000000/* * ion/ioncore/manage.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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" #include "return.h" #include "conf.h" #include "detach.h" #include "group-ws.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; } /*extern WRegion *ioncore_newly_created;*/ static WPHolder *try_target(WClientWin *cwin, const WManageParams *param, const char *target) { WRegion *r=ioncore_lookup_region(target, NULL); if(r==NULL) return NULL; if(!region_same_rootwin(r, (WRegion*)cwin)) return NULL; return region_prepare_manage(r, cwin, param, MANAGE_PRIORITY_NONE); } static bool handle_target_winprops(WClientWin *cwin, const WManageParams *param, WScreen *scr, WPHolder **ph_ret) { char *layout=NULL, *target=NULL; WPHolder *ph=NULL; bool mgd=FALSE; if(extl_table_gets_s(cwin->proptab, "target", &target)) ph=try_target(cwin, param, target); if(ph==NULL && extl_table_gets_s(cwin->proptab, "new_group", &layout)){ ExtlTab lo=ioncore_get_layout(layout); free(layout); if(lo!=extl_table_none()){ WMPlexAttachParams par=MPLEXATTACHPARAMS_INIT; int mask=MPLEX_ATTACH_SWITCHTO; WRegion *reg; if(param->switchto) par.flags|=MPLEX_ATTACH_SWITCHTO; /*ioncore_newly_created=(WRegion*)cwin;*/ reg=mplex_attach_new_(&scr->mplex, &par, mask, lo); extl_unref_table(lo); /*ioncore_newly_created=NULL;*/ mgd=(region_manager((WRegion*)cwin)!=NULL); if(reg!=NULL && !mgd){ if(target!=NULL) ph=try_target(cwin, param, target); if(ph==NULL){ ph=region_prepare_manage(reg, cwin, param, MANAGE_PRIORITY_NONE); if(ph==NULL) destroy_obj((Obj*)reg); } } } } if(target!=NULL) free(target); *ph_ret=ph; return mgd; } static bool try_fullscreen(WClientWin *cwin, WScreen *dflt, const WManageParams *param) { WScreen *fs_scr=NULL; bool fs=FALSE, tmp; /* Check fullscreen mode. (This is intentionally not done * for transients and windows with target winprops.) */ if(extl_table_gets_b(cwin->proptab, "fullscreen", &tmp)){ if(!tmp) return FALSE; fs_scr=dflt; } if(fs_scr==NULL && netwm_check_initial_fullscreen(cwin)) fs_scr=dflt; if(fs_scr==NULL) fs_scr=clientwin_fullscreen_chkrq(cwin, param->geom.w, param->geom.h); if(fs_scr!=NULL){ WPHolder *fs_ph=region_prepare_manage((WRegion*)fs_scr, cwin, param, MANAGE_PRIORITY_NOREDIR); if(fs_ph!=NULL){ int swf=(param->switchto ? PHOLDER_ATTACH_SWITCHTO : 0); cwin->flags|=CLIENTWIN_FS_RQ; fs=pholder_attach(fs_ph, swf, (WRegion*)cwin); if(!fs) cwin->flags&=~CLIENTWIN_FS_RQ; destroy_obj((Obj*)fs_ph); } } return fs; } bool clientwin_do_manage_default(WClientWin *cwin, const WManageParams *param) { WScreen *scr=NULL; WPHolder *ph=NULL; int swf=(param->switchto ? PHOLDER_ATTACH_SWITCHTO : 0); bool ok, uq=FALSE; WRegion *createroot=NULL; /* 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(handle_target_winprops(cwin, param, scr, &ph)) return TRUE; /* Check if param->tfor or any of its managers want to manage cwin. */ if(ph==NULL && param->tfor!=NULL){ assert(param->tfor!=cwin); ph=region_prepare_manage_transient((WRegion*)param->tfor, cwin, param, 0); uq=TRUE; } if(ph==NULL){ /* Find a placeholder for non-fullscreen state */ ph=region_prepare_manage((WRegion*)scr, cwin, param, MANAGE_PRIORITY_NONE); if(try_fullscreen(cwin, scr, param)){ if(pholder_target(ph)!=(WRegion*)region_screen_of((WRegion*)cwin)){ WRegion *grp=region_groupleader_of((WRegion*)cwin); if(region_do_set_return(grp, ph)) return TRUE; } destroy_obj((Obj*)ph); return TRUE; } } if(ph==NULL) return FALSE; /* Not in full-screen mode; use the placeholder to attach. */ { WRegionAttachData data; data.type=REGION_ATTACH_REPARENT; data.u.reg=(WRegion*)cwin; createroot=pholder_do_attach(ph, swf|PHOLDER_ATTACH_RETURN_CREATEROOT, &data); } destroy_obj((Obj*)ph); if(uq && createroot!=NULL) ioncore_unsqueeze(createroot, FALSE); return (createroot!=NULL); } /*}}}*/ /*{{{ region_prepare_manage/region_manage_clientwin/etc. */ WPHolder *region_prepare_manage(WRegion *reg, const WClientWin *cwin, const WManageParams *param, int priority) { WPHolder *ret=NULL; CALL_DYN_RET(ret, WPHolder*, region_prepare_manage, reg, (reg, cwin, param, priority)); return ret; } WPHolder *region_prepare_manage_default(WRegion *reg, const WClientWin *cwin, const WManageParams *param, int priority) { int cpriority=MANAGE_PRIORITY_SUB(priority, MANAGE_PRIORITY_NONE); WRegion *curr=region_current(reg); if(curr==NULL) return NULL; return region_prepare_manage(curr, cwin, param, cpriority); } 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 priority) { bool ret; WPHolder *ph=region_prepare_manage(reg, cwin, par, priority); 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 */ DECLSTRUCT(WRescueInfo){ WPHolder *ph; WRegion *get_rescue; bool failed_get; bool test; int flags; }; 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, WRescueInfo *info) { WRegion *next=reg->children; return region_rescue_some_clientwins(reg, info, iter_children, &next); } WPHolder *rescueinfo_pholder(WRescueInfo *info) { if(info->test) return NULL; if(info->ph==NULL){ info->ph=region_get_rescue_pholder(info->get_rescue); if(info->ph==NULL){ info->failed_get=TRUE; return NULL; } } return info->ph; } /* Bah, unsplitissä oikestaan pitäisi tehä non-deep rescue */ bool region_do_rescue_this(WRegion *tosave_, WRescueInfo *info, int ph_flags) { WClientWin *cwin=OBJ_CAST(tosave_, WClientWin); WRegion *tosave=NULL; if(cwin!=NULL){ if(cwin->flags&CLIENTWIN_UNMAP_RQ) return TRUE; tosave=(WRegion*)cwin; }else if(info->flags®ION_RESCUE_NODEEP){ tosave=tosave_; }else{ /* Try to rescue whole groups. */ /*tosave=(WRegion*)OBJ_CAST(tosave_, WGroupCW);*/ } if(tosave==NULL){ return region_rescue_clientwins(tosave_, info); }else{ int phf=(info->flags®ION_RESCUE_PHFLAGS_OK ? ph_flags : 0); WPHolder *ph=rescueinfo_pholder(info); return (ph==NULL ? FALSE : pholder_attach(info->ph, phf, tosave)); } } bool region_rescue_some_clientwins(WRegion *reg, WRescueInfo *info, WRegionIterator *iter, void *st) { bool res=FALSE; int fails=0; if(info->failed_get) return FALSE; reg->flags|=REGION_CWINS_BEING_RESCUED; while(TRUE){ WRegion *tosave=iter(st); if(tosave==NULL) break; if(!region_do_rescue_this(tosave, info, 0)){ fails++; if(info->failed_get) break; } } reg->flags&=~REGION_CWINS_BEING_RESCUED; return (fails==0 && !info->failed_get); } bool region_rescue_clientwins(WRegion *reg, WRescueInfo *info) { bool ret=FALSE; CALL_DYN_RET(ret, bool, region_rescue_clientwins, reg, (reg, info)); return ret; } bool region_rescue(WRegion *reg, WPHolder *ph, int flags) { WRescueInfo info; bool ret; info.ph=ph; info.flags=flags; info.test=FALSE; info.get_rescue=reg; info.failed_get=FALSE; ret=region_rescue_clientwins(reg, &info); if(info.ph!=ph) destroy_obj((Obj*)info.ph); return ret; } bool region_rescue_needed(WRegion *reg) { WRescueInfo info; info.ph=NULL; info.flags=0; info.test=TRUE; info.get_rescue=reg; info.failed_get=FALSE; return !region_rescue_clientwins(reg, &info); } /*}}}*/ /*{{{ 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; } /*}}}*/ notion-3+2012042300/ioncore/manage.h000066400000000000000000000072771174530661200166360ustar00rootroot00000000000000/* * ion/ioncore/manage.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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_PRIORITY_NONE, MANAGE_PRIORITY_LOW, MANAGE_PRIORITY_NORMAL, MANAGE_PRIORITY_GROUP, MANAGE_PRIORITY_NO, /* Special */ MANAGE_PRIORITY_NOREDIR }; #define MANAGE_PRIORITY_OK(PRIORITY, OUR) \ ((PRIORITY) <= (OUR) || (PRIORITY)==MANAGE_PRIORITY_NOREDIR) #define MANAGE_PRIORITY_SUB(PRIORITY, OUR) \ ((PRIORITY)==MANAGE_PRIORITY_NOREDIR \ ? MANAGE_PRIORITY_NO \ : (PRIORITY) < (OUR) ? (OUR) : (PRIORITY)) #define MANAGE_PRIORITY_SUBX(PRIORITY, OUR) \ ((PRIORITY)==MANAGE_PRIORITY_NOREDIR || (OUR) < (PRIORITY) \ ? MANAGE_PRIORITY_NO \ : MANAGE_PRIORITY_NONE) 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 */ #define REGION_RESCUE_PHFLAGS_OK 0x01 #define REGION_RESCUE_NODEEP 0x02 INTRSTRUCT(WRescueInfo); extern WPHolder *rescueinfo_pholder(WRescueInfo *info); /* if ph is given, it is used, otherwise one is looked for when needed */ extern bool region_rescue(WRegion *reg, WPHolder *ph, int flags); extern bool region_rescue_needed(WRegion *reg); extern bool region_rescue_clientwins(WRegion *reg, WRescueInfo *info); extern bool region_rescue_child_clientwins(WRegion *reg, WRescueInfo *info); extern bool region_rescue_some_clientwins(WRegion *reg, WRescueInfo *info, WRegionIterator *iter, void *st); extern bool region_do_rescue_this(WRegion *tosave, WRescueInfo *info, int ph_flags); #endif /* ION_IONCORE_MANAGE_H */ notion-3+2012042300/ioncore/modules.c000066400000000000000000000162111174530661200170350ustar00rootroot00000000000000/* * ion/ioncore/modules.c * * Copyright (c) Arnout Engelen 2011 * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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 void (*get_module_fptr(dlhandle handle, const char *modulename, const char *name))(void **) { /* This is not valid ISO C. However, it is 'tried and tested'. The * workaround originally recommended[1] is not alias-safe[2]. The approach * we chose, while not valid ISO C, *is* valid under POSIX 2008[3]. Newer * versions of GCC should not warn about it anymore[4]. * * [1] http://pubs.opengroup.org/onlinepubs/009695399/functions/dlsym.html#tag_03_112_06) * [2] http://gcc.gnu.org/bugzilla/show_bug.cgi?id=45289#c1 * [3] http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_12_03 * [4] http://gcc.gnu.org/bugzilla/show_bug.cgi?id=45289#c10 */ return (void (*)(void**)) get_module_symbol(handle, modulename, name); } static char *get_version(dlhandle handle, const char *modulename) { return (char*)get_module_symbol(handle, modulename, "_ion_api_version"); } static bool check_has_version(dlhandle handle, const char *modulename) { return get_version(handle, modulename) != NULL; } static bool check_version(dlhandle handle, const char *modulename) { char *versionstr=get_version(handle, modulename); if(versionstr==NULL) return FALSE; return (strcmp(versionstr, NOTION_API_VERSION)==0); } static bool call_init(dlhandle handle, const char *modulename) { bool (*initfn)(void); initfn=(bool (*)())get_module_fptr(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_fptr(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_has_version(handle, name)){ warn_obj(file, TR("Module version information for %s not found. " "Refusing to use."), name); goto err3; } if(!check_version(handle, name)){ warn_obj(file, TR("Module version mismatch: expected '%s', found '%s'." " Refusing to use."), NOTION_API_VERSION, get_version(handle, name)); 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; const char *extension = "so"; retval=extl_try_config(modname, NULL, (ExtlTryConfigFn*)try_load, NULL, extension, NULL); if(retval==EXTL_TRYCONFIG_NOTFOUND) warn(TR("Unable to find '%s.%s' on search path."), modname, extension); 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); } /*}}}*/ notion-3+2012042300/ioncore/modules.h000066400000000000000000000007621174530661200170460ustar00rootroot00000000000000/* * ion/ioncore/modules.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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 */ notion-3+2012042300/ioncore/mplex.c000066400000000000000000001566111174530661200165230ustar00rootroot00000000000000/* * ion/ioncore/mplex.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #include #include #include #include #include #include #include #include "names.h" #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 "grouppholder.h" #include "llist.h" #include "names.h" #include "sizepolicy.h" #include "stacking.h" #include "group.h" #include "navi.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, const WFitParams *fp, Window win, const char *name) { mplex->flags=0; mplex->mx_list=NULL; mplex->mx_current=NULL; mplex->misc_phs=NULL; mplex->mx_count=0; mplex->mgd=NULL; watch_init(&(mplex->stdispwatch)); mplex->stdispinfo.pos=MPLEX_STDISP_BL; mplex->stdispinfo.fullsize=FALSE; if(!window_do_init((WWindow*)mplex, parent, fp, win, name)) return FALSE; mplex->win.region.flags|=REGION_BINDINGS_ARE_GRABBED; window_select_input(&(mplex->win), IONCORE_EVENTMASK_CWINMGR); region_register((WRegion*)mplex); region_set_name((WRegion*)mplex, name); /* 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, const char *name) { return mplex_do_init(mplex, parent, fp, None, name); } WMPlex *create_mplex(WWindow *parent, const WFitParams *fp, const char *name) { CREATEOBJ_IMPL(WMPlex, mplex, (p, parent, fp, name)); } 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->misc_phs!=NULL){ assert(mplexpholder_move(mplex->misc_phs, NULL, NULL, NULL)); } window_deinit((WWindow*)mplex); } /*}}}*/ /*{{{ 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 on the mutually exclusive * list of \var{mplex}. */ 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 * Iterate over numbered/mutually exclusive region list of \var{mplex} * until \var{iterfn} returns \code{false}. * The function is called in protected mode. * This routine returns \code{true} if it reaches the end of list * without this happening. */ EXTL_SAFE EXTL_EXPORT_MEMBER bool mplex_mx_i(WMPlex *mplex, ExtlFn iterfn) { WLListIterTmp tmp; llist_iter_init(&tmp, mplex->mx_list); return extl_iter_objlist_(iterfn, (ObjIterator*)llist_iter_regions, &tmp); } /*EXTL_DOC * Iterate over managed regions of \var{mplex} until \var{iterfn} returns * \code{false}. * The function is called in protected mode. * This routine returns \code{true} if it reaches the end of list * without this happening. */ EXTL_SAFE EXTL_EXPORT_MEMBER bool mplex_managed_i(WMPlex *mplex, ExtlFn iterfn) { WMPlexIterTmp tmp; mplex_iter_init(&tmp, mplex); return extl_iter_objlist_(iterfn, (ObjIterator*)mplex_iter, &tmp); } /*EXTL_DOC * Set index of \var{reg} to \var{index} within the mutually exclusive * list of \var{mplex}. Special values for \var{index} are: * \begin{tabularx}{\linewidth}{lX} * $-1$ & Last. \\ * $-2$ & After \fnref{WMPlex.mx_current}. \\ * \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); } 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} on the mutually exclusive list of \var{mplex}. * The indices begin from zero.. If \var{reg} is not on the list, * -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} ``left'' 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); if(!window_fitrep(&(mplex->win), par, fp)) return FALSE; 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); fp.mode=0; 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); } void mplex_set_szplcy(WMPlex *mplex, WRegion *sub, WSizePolicy szplcy) { WStacking *node; node=mplex_find_stacking(mplex, sub); if(node!=NULL) node->szplcy=szplcy; } WSizePolicy mplex_get_szplcy(WMPlex *mplex, WRegion *sub) { WStacking *node; node=mplex_find_stacking(mplex, sub); return (node==NULL ? SIZEPOLICY_DEFAULT : node->szplcy); } /*}}}*/ /*{{{ Focus */ typedef struct{ WMPlex *mplex; WStacking *to_try; WStacking *group_st; PtrList **hidelist; bool try_hard; } FiltData; static WRegion *manager_within(WMPlex *mplex, WStacking *st) { return region_managed_within((WRegion*)mplex, st->reg); } static WStacking *stacking_within(WMPlex *mplex, WStacking *st) { WRegion *reg=manager_within(mplex, st); return (reg==NULL ? NULL : (reg==st->reg ? st : ioncore_find_stacking(reg))); } /* Mutually exclusive regions can't be pseudomodal */ #define IS_PSEUDOMODAL(ST) ((ST)->lnode==NULL && (ST)->pseudomodal) static bool mapped_pseudomodal_include_filt(WStacking *st, void *data_) { FiltData *data=(FiltData*)data_; WStacking *stw; if(st->reg==NULL || !REGION_IS_MAPPED(st->reg)) return FALSE; if(!data->hidelist || (data->to_try==NULL && data->group_st==NULL) || st->levelmplex, st); /* This should not happen */ if(stw==NULL || stw->reg==NULL) return FALSE; /* The node is within the same group, so it can not be hidden. * Latter case should not happen. */ if(stw==data->group_st || stw==data->to_try) return TRUE; if(IS_PSEUDOMODAL(stw)){ /* Don't insert multiple times. */ return !ptrlist_reinsert_first(data->hidelist, stw); } return TRUE; } static bool mgr_pseudomodal_approve_filt(WStacking *st, void *data_) { FiltData *data=(FiltData*)data_; return (data->group_st==NULL || st==data->group_st || manager_within(data->mplex, st)==data->group_st->reg); } WStacking *mplex_find_to_focus(WMPlex *mplex, WStacking *to_try, WStacking *group_st, PtrList **hidelist) { WStackingFilter *fi=mapped_pseudomodal_include_filt; WStackingFilter *fa=mgr_pseudomodal_approve_filt; WStacking *stacking=mplex_get_stacking(mplex); FiltData data; WStacking *st; if(stacking==NULL) return NULL; if(to_try!=NULL && (to_try->reg==NULL || !REGION_IS_MAPPED(to_try->reg))) to_try=NULL; data.mplex=mplex; data.to_try=to_try; data.group_st=group_st; data.hidelist=hidelist; st=stacking_find_to_focus(stacking, to_try, fi, fa, &data); if(st==NULL && hidelist!=NULL) ptrlist_clear(hidelist); return st; } static WStacking *mplex_do_to_focus_on(WMPlex *mplex, WStacking *node, WStacking *to_try, PtrList **hidelist, bool *within) { WGroup *grp=OBJ_CAST(node->reg, WGroup); WStacking *st; if(grp!=NULL){ if(to_try==NULL) to_try=grp->current_managed; /* Only will return stuff within 'node' */ st=mplex_find_to_focus(mplex, to_try, node, hidelist); if(st!=NULL){ if(within!=NULL) *within=TRUE; return st; } } st=mplex_find_to_focus(mplex, node, NULL, hidelist); /* If 'node' points to a group, it isn't actually on the stacking list. * Give it the focus, if there's nothing "proper" that could be focussed. */ if(st==NULL && grp!=NULL && REGION_IS_MAPPED(grp)) st=node; if(st==node && within!=NULL) *within=TRUE; return st; } static WStacking *maybe_focusable(WRegion *reg) { if(reg==NULL || !REGION_IS_MAPPED(reg)) return NULL; return ioncore_find_stacking(reg); } static WStacking *has_stacking_within(WMPlex *mplex, WRegion *reg) { while(reg!=NULL && REGION_MANAGER(reg)!=(WRegion*)mplex) reg=REGION_MANAGER(reg); return maybe_focusable(reg); } /* 1. Try keep focus in REGION_ACTIVE_SUB. * 2. Choose something else, attempting previous in focus history. */ static WStacking *mplex_to_focus(WMPlex *mplex) { WStacking *foc=NULL, *fallback=NULL; WRegion *reg=NULL; foc=maybe_focusable(REGION_ACTIVE_SUB(mplex)); if(foc==NULL){ /* Search focus history if no specific attempt set.*/ for(reg=ioncore_g.focus_current; reg!=NULL; reg=reg->active_next){ foc=has_stacking_within(mplex, reg); if(foc!=NULL) break; } } if(foc!=NULL){ /* In the history search case, 'foc' might point to a group, * since we don't properly try to find a stacking within it... */ return mplex_do_to_focus_on(mplex, foc, NULL, NULL, NULL); }else{ return mplex_find_to_focus(mplex, NULL, NULL, NULL); } } void mplex_do_set_focus(WMPlex *mplex, bool warp) { if(!MPLEX_MGD_UNVIEWABLE(mplex)){ WStacking *st=mplex_to_focus(mplex); if(st==NULL){ st=(mplex->mx_current!=NULL ? mplex->mx_current->st : NULL); } if(st!=NULL){ region_do_set_focus(st->reg, warp); return; } } window_do_set_focus((WWindow*)mplex, warp); } static void mplex_refocus(WMPlex *mplex, WStacking *node, bool warp) { bool within=FALSE; WStacking *foc=NULL; if(node!=NULL) foc=mplex_do_to_focus_on(mplex, node, NULL, NULL, &within); if(foc==NULL || !within) foc=mplex_to_focus(mplex); if(foc!=NULL) region_maybewarp(foc->reg, 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 *omgr=REGION_MANAGER(stdisp); if(omgr!=sub && omgr!=NULL){ if(CAN_MANAGE_STDISP(omgr)) region_unmanage_stdisp(omgr, FALSE, FALSE); region_detach_manager(stdisp); } region_manage_stdisp(sub, stdisp, &(mplex->stdispinfo)); }else{ region_unmanage_stdisp(sub, TRUE, FALSE); } } } 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; WFitParams fp; 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); /* the mplex might have been resized while this window was invisible, * and the client window might have had lazy resizing enabled. */ fp.mode=REGION_FIT_EXACT; mplex_managed_geom(mplex, &(fp.g)); sizepolicy(&node->szplcy, node->reg, NULL, 0, &fp); region_fitrep(node->reg, NULL, &fp); 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. */ { WGroup *grp=(WGroup*)OBJ_CAST(sub, WGroupCW); if(grp!=NULL){ WRegion *bottom=group_bottom(grp); if(bottom!=NULL){ region_managed_rqorder((WRegion*)grp, bottom, REGION_ORDER_BACK); } } } if(call_changed) mplex_managed_changed(mplex, MPLEX_CHANGE_SWITCHONLY, TRUE, sub); } } bool mplex_do_prepare_focus(WMPlex *mplex, WStacking *node, WStacking *sub, int flags, WPrepareFocusResult *res) { bool ew=(flags®ION_GOTO_ENTERWINDOW); PtrList *hidelist=NULL; PtrList **hidelistp=(ew ? NULL : &hidelist); WStacking *foc; /*bool within=FALSE;*/ if(sub==NULL && node==NULL) return FALSE; /* Display the node in any case */ if(node!=NULL && !ew) mplex_do_node_display(mplex, node, TRUE); if(!region_prepare_focus((WRegion*)mplex, flags, res)) return FALSE; foc=mplex_do_to_focus_on(mplex, node, sub, hidelistp, NULL /*&within*/); if(foc!=NULL){ while(hidelist!=NULL){ WStacking *st=(WStacking*)ptrlist_take_first(&hidelist); st->hidden=TRUE; region_unmap(st->reg); } if(ioncore_g.autoraise && !(flags®ION_GOTO_ENTERWINDOW) && foc->level>STACKING_LEVEL_BOTTOM){ WStacking **stackingp=mplex_get_stackingp(mplex); stacking_restack(stackingp, foc, None, NULL, NULL, FALSE); } res->reg=foc->reg; res->flags=flags; return (foc==sub || (sub==NULL && foc==node)); }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)); } /*EXTL_DOC * Have \var{mplex} display the given child window already added to the mplex */ EXTL_EXPORT_MEMBER void mplex_switch_to(WMPlex *mplex, WRegion *reg) { WPrepareFocusResult result; mplex_managed_prepare_focus(mplex, reg->manager, 0, &result); } 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} * (one of \codestr{set}, \codestr{unset}, or \codestr{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, bool wrap) { WStacking *st, *stacking; uint min_level=0; 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 && res!=st->reg) return res; }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; WRegion *res=NULL; if(lst!=NULL){ if(nh==REGION_NAVI_ANY){ /* ? */ } if(nh==REGION_NAVI_ANY || nh==REGION_NAVI_END || nh==REGION_NAVI_BOTTOM || nh==REGION_NAVI_RIGHT){ res=do_navi(mplex, lst, mplex_prv, data, TRUE, TRUE); }else{ res=do_navi(mplex, lst->mgr_prev, mplex_nxt, data, TRUE, TRUE); } } return region_navi_cont((WRegion*)mplex, res, data); } WRegion *mplex_navi_next(WMPlex *mplex, WRegion *rel, WRegionNavi nh, WRegionNaviData *data) { WStacking *st; WRegion *res; 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){ res=do_navi(mplex, st, mplex_nxt, data, FALSE, FALSE); }else{ res=do_navi(mplex, st, mplex_prv, data, FALSE, FALSE); } return region_navi_cont((WRegion*)mplex, res, data); } /*}}}*/ /*{{{ 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; 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; WSizePolicy szplcy; uint level; mcf=region_may_control_focus((WRegion*)mplex); mx_was_empty=(mplex->mx_list==NULL); szplcy=((param->flags&MPLEX_ATTACH_SIZEPOLICY && param->szplcy!=SIZEPOLICY_DEFAULT) ? param->szplcy : (param->flags&MPLEX_ATTACH_UNNUMBERED ? SIZEPOLICY_FULL_BOUNDS : SIZEPOLICY_FULL_EXACT)); modal=(param->flags&MPLEX_ATTACH_LEVEL && param->level>=STACKING_LEVEL_MODAL1); level=(param->flags&MPLEX_ATTACH_LEVEL ? param->level : (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 ? FALSE : (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=szplcy; node->level=level; node->pseudomodal=(param->flags&MPLEX_ATTACH_PSEUDOMODAL ? 1 : 0); if(lnode!=NULL){ WMPlexPHolder *ph2, *phn, *php; llist_link_after(&(mplex->mx_list), (ph!=NULL ? ph->after : NULL), lnode); mplex->mx_count++; /* Move placeholders after new node */ for(php=NULL, ph2=ph; ph2!=NULL; php=ph2, ph2=phn){ phn=ph2->next; mplexpholder_move(ph2, mplex, php, 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(param->flags&MPLEX_ATTACH_PASSIVE) reg->flags|=REGION_SKIP_FOCUS; if(!(param->flags&MPLEX_ATTACH_WHATEVER)){ WFitParams fp; fp.mode=0; mplex_managed_geom(mplex, &(fp.g)); sizepolicy(&node->szplcy, reg, (param->flags&MPLEX_ATTACH_GEOM ? &(param->geom) : NULL), 0, &fp); if(rectangle_compare(&fp.g, ®ION_GEOM(reg))!=RECTANGLE_SAME) region_fitrep(reg, NULL, &fp); } if(!hidden) mplex_do_node_display(mplex, node, FALSE); else region_unmap(reg); if(mcf){ if(sw){ mplex_refocus(mplex, node, FALSE); }else if(!hidden && (level>=STACKING_LEVEL_MODAL1 || OBJ_IS(reg, WGroup))){ /* New modal regions may require focusing, so try to * give focus back to currently active object. * (There seems to be some problem with uncontained * client windows still..) */ mplex_refocus(mplex, NULL, FALSE); }else if(!hidden){ region_pointer_focus_hack(reg); } }else if(!hidden){ region_pointer_focus_hack(reg); } if(lnode!=NULL) mplex_managed_changed(mplex, MPLEX_CHANGE_ADD, sw, reg); return TRUE; } static void mplex_attach_fp(WMPlex *mplex, const WMPlexAttachParams *param, WFitParams *fp) { if(param->flags&MPLEX_ATTACH_GEOM) fp->g=param->geom; else mplex_managed_geom(mplex, &(fp->g)); fp->mode=REGION_FIT_WHATEVER|REGION_FIT_BOUNDS; } WRegion *mplex_do_attach_pholder(WMPlex *mplex, WMPlexPHolder *ph, WRegionAttachData *data) { WFitParams fp; mplex_attach_fp(mplex, &ph->param, &fp); return region_attach_helper((WRegion*)mplex, (WWindow*)mplex, &fp, (WRegionDoAttachFn*)mplex_do_attach_final, (void*)ph, data); } 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, int mask, WMPlexAttachParams *par) { int tmp; char *tmpstr; int ok=~mask; if(ok&MPLEX_ATTACH_LEVEL){ 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->level=maxof(par->level, STACKING_LEVEL_MODAL1); } if(extl_table_is_bool_set(tab, "unnumbered")) par->flags|=MPLEX_ATTACH_UNNUMBERED&ok; if(extl_table_is_bool_set(tab, "switchto")) par->flags|=MPLEX_ATTACH_SWITCHTO&ok; if(extl_table_is_bool_set(tab, "hidden")) par->flags|=MPLEX_ATTACH_HIDDEN&ok; if(extl_table_is_bool_set(tab, "passive")) par->flags|=MPLEX_ATTACH_PASSIVE&ok; if(extl_table_is_bool_set(tab, "pseudomodal")) par->flags|=MPLEX_ATTACH_PSEUDOMODAL&ok; if(extl_table_gets_i(tab, "index", &(par->index))) par->flags|=MPLEX_ATTACH_INDEX&ok; if(ok&MPLEX_ATTACH_SIZEPOLICY){ if(extl_table_gets_sizepolicy(tab, "sizepolicy", &par->szplcy)){ par->flags|=MPLEX_ATTACH_SIZEPOLICY; }else if(extl_table_gets_i(tab, "sizepolicy", &tmp)){ /* Backwards compat. numeric version */ par->flags|=MPLEX_ATTACH_SIZEPOLICY; par->szplcy=tmp; } } if(extl_table_gets_rectangle(tab, "geom", &par->geom)) par->flags|=MPLEX_ATTACH_GEOM&ok; } /*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=MPLEXATTACHPARAMS_INIT; WRegionAttachData data; if(reg==NULL) return NULL; get_params(mplex, param, 0, &par); data.type=REGION_ATTACH_REPARENT; data.u.reg=reg; return mplex_do_attach(mplex, &par, &data); } WRegion *mplex_attach_new_(WMPlex *mplex, WMPlexAttachParams *par, int mask, ExtlTab param) { WRegionAttachData data; get_params(mplex, param, mask, par); data.type=REGION_ATTACH_LOAD; data.u.tab=param; 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{passive} & (boolean) Skip in certain focusing operations. \\ * \var{pseudomodal} & (boolean) The attached region is ``pseudomodal'' * if the stacking level dictates it to be modal. * This means that the region may be hidden to display * regions with lesser stacking levels. \\ * \var{sizepolicy} & (string) Size policy; see Section \ref{sec:sizepolicies}. \\ * \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=MPLEXATTACHPARAMS_INIT; return mplex_attach_new_(mplex, &par, 0, param); } 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 priority) { int cpriority=MANAGE_PRIORITY_SUB(priority, MANAGE_PRIORITY_NORMAL); WMPlexAttachParams ap; WPHolder *ph=NULL; WMPlexPHolder *mph; WLListNode *after; /* Check current */ { WStacking *cur=mplex_current_node(mplex); if(cur!=NULL){ ph=region_prepare_manage(cur->reg, cwin, param, cpriority); 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, cpriority); if(ph!=NULL) return ph; } } if(!MANAGE_PRIORITY_OK(priority, MANAGE_PRIORITY_NORMAL)) return NULL; ap.flags=((param->switchto ? MPLEX_ATTACH_SWITCHTO : 0) |MPLEX_ATTACH_SIZEPOLICY); ap.szplcy=SIZEPOLICY_FULL_EXACT; mph=create_mplexpholder(mplex, NULL, &ap); if(mph!=NULL){ WGroupPHolder *gph; WGroupAttachParams gp=GROUPATTACHPARAMS_INIT; gp.switchto_set=1; gp.switchto=1; gp.bottom=1; gph=create_grouppholder(NULL, NULL, &gp); if(gph!=NULL){ gph->recreate_pholder=(WPHolder*)mph; 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); } 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); } } /*}}}*/ /*{{{ Rescue */ bool mplex_rescue_clientwins(WMPlex *mplex, WRescueInfo *info) { bool ret1, ret2; WMPlexIterTmp tmp; WLListIterTmp ltmp; WLListNode *lnode, *was_current=mplex->mx_current; /* First all mx stuff to move them nicely to another mplex (when that * is the case), switching to the current region in the target if * allowed by ph_flags_mask region_rescue. */ FOR_ALL_NODES_ON_LLIST(lnode, mplex->mx_list, ltmp){ int sw=(lnode==was_current ? PHOLDER_ATTACH_SWITCHTO : 0); region_do_rescue_this(lnode->st->reg, info, sw); } /* Then the rest (possibly retrying failed mx stuff). */ mplex_iter_init(&tmp, mplex); ret1=region_rescue_some_clientwins((WRegion*)mplex, info, (WRegionIterator*)mplex_iter, &tmp); ret2=region_rescue_child_clientwins((WRegion*)mplex, info); return (ret1 && ret2); } /*}}}*/ /*{{{ Status display support */ 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(din!=NULL) mplex->stdispinfo=*din; if(reg==NULL){ watch_reset(&(mplex->stdispwatch)); if(mgr!=NULL){ region_unmanage_stdisp(mgr, TRUE, FALSE); if(oldstdisp!=NULL) region_detach_manager(oldstdisp); } }else{ watch_setup(&(mplex->stdispwatch), (Obj*)reg, NULL); mplex_remanage_stdisp(mplex); } if(oldstdisp!=NULL && oldstdisp!=reg) mainloop_defer_destroy((Obj*)oldstdisp); 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} & (string) The corner of the screen to place the status * display in: one of \codestr{tl}, \codestr{tr}, \codestr{bl} * or \codestr{br}. \\ * \var{fullsize} & (boolean) Waste all available space. \\ * \var{action} & (string) If this field is set to \codestr{keep}, * \var{pos} and \var{fullsize} are changed for the existing * status display. If this field is set to \codestr{remove}, * the existing status display is removed. If this * field is not set or is set to \codestr{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()){ if(mplex->mx_current!=NULL && node==mplex->mx_current->st) extl_table_sets_b(st, "switchto", TRUE); extl_table_sets_s(st, "sizepolicy", sizepolicy2string(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(STACKING_IS_PSEUDOMODAL(node)) extl_table_sets_b(st, "pseudomodal", 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, "managed", 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; } 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, "managed", &substab) || 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)){ WMPlexAttachParams par=MPLEXATTACHPARAMS_INIT; WFitParams fp; WPHolder *ph; get_params(mplex, subtab, 0, &par); mplex_attach_fp(mplex, &par, &fp); par.flags|=MPLEX_ATTACH_INDEX; par.index=LLIST_INDEX_LAST; ph=(WPHolder*)create_mplexpholder(mplex, NULL, &par); if(ph!=NULL){ region_attach_load_helper((WRegion*)mplex, (WWindow*)mplex, &fp, (WRegionDoAttachFn*)mplex_do_attach_final, (void*)ph, subtab, &ph); if(ph!=NULL) destroy_obj((Obj*)ph); } extl_unref_table(subtab); } } extl_unref_table(substab); } } WRegion *mplex_load(WWindow *par, const WFitParams *fp, ExtlTab tab, const char *name) { WMPlex *mplex=create_mplex(par, fp, name); 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_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); /*}}}*/ notion-3+2012042300/ioncore/mplex.h000066400000000000000000000171441174530661200165250ustar00rootroot00000000000000/* * ion/ioncore/mplex.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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_PSEUDOMODAL 0x0008 /* pseudomodal (if modal) */ #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 */ #define MPLEX_ATTACH_PASSIVE 0x0200 /* sets SKIP_FOCUS */ 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 *misc_phs; Watch stdispwatch; WMPlexSTDispInfo stdispinfo; }; /* Create/destroy */ extern WMPlex *create_mplex(WWindow *parent, const WFitParams *fp, const char *name); extern bool mplex_init(WMPlex *mplex, WWindow *parent, const WFitParams *fp, const char *name); extern bool mplex_do_init(WMPlex *mplex, WWindow *parent, const WFitParams *fp, Window win, const char *name); 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, WMPlexAttachParams *partmpl, int mask, 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_managed_remove(WMPlex *mplex, WRegion *reg); extern void mplex_child_removed(WMPlex *mplex, WRegion *sub); extern bool mplex_rescue_clientwins(WMPlex *mplex, WRescueInfo *info); 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 void mplex_switch_to(WMPlex *mplex, WRegion *reg); 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_managed_i(WMPlex *mplex, ExtlFn iterfn); extern int mplex_mx_count(WMPlex *mplex); extern WRegion *mplex_mx_nth(WMPlex *mplex, uint n); extern bool mplex_mx_i(WMPlex *mplex, ExtlFn iterfn); extern WRegion *mplex_mx_current(WMPlex *mplex); extern void mplex_call_changed_hook(WMPlex *mplex, WHook *hook, int mode, bool sw, WRegion *reg); extern void mplex_remanage_stdisp(WMPlex *mplex); /* Note: only the size policy field is changed; actual geometry is not * yet changed. */ extern void mplex_set_szplcy(WMPlex *mplex, WRegion *sub, WSizePolicy szplcy); extern WSizePolicy mplex_get_szplcy(WMPlex *mplex, WRegion *sub); /* 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, const char *name); 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 */ notion-3+2012042300/ioncore/mplexpholder.c000066400000000000000000000304641174530661200200760ustar00rootroot00000000000000/* * ion/ioncore/mplexpholder.c * * Copyright (c) Tuomo Valkonen 2005-2009. * * See the included file LICENSE for details. */ #include #include #include #include #include "common.h" #include "mplex.h" #include "mplexpholder.h" #include "llist.h" #include "framedpholder.h" #include "basicpholder.h" /*{{{ 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==ph->mplex && 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->misc_phs, after)); LINK_ITEM_AFTER(mplex->misc_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->misc_phs, ph, next, prev); ph->after=NULL; } } static WMPlexPHolder *get_head(WMPlexPHolder *ph) { while(1){ /* ph->prev==NULL should not happen.. */ if(ph->prev==NULL || ph->prev->next==NULL) break; ph=ph->prev; } return ph; } void mplexpholder_do_unlink(WMPlexPHolder *ph, WMPlex *mplex) { if(ph->recreate_pholder!=NULL){ if(ph->next!=NULL){ ph->next->recreate_pholder=ph->recreate_pholder; }else{ /* It might be in use in attach chain! So defer. */ mainloop_defer_destroy((Obj*)ph->recreate_pholder); } ph->recreate_pholder=NULL; } if(ph->after!=NULL){ UNLINK_ITEM(ph->after->phs, ph, next, prev); }else if(mplex!=NULL && on_ph_list(mplex->misc_phs, ph)){ UNLINK_ITEM(mplex->misc_phs, ph, next, prev); }else if(ph->prev!=NULL){ WMPlexPHolder *next=ph->next; ph->prev->next=next; if(next==NULL){ next=get_head(ph); assert(next->prev==ph); } next->prev=ph->prev; }else{ /* ph should not be on a list, if prev pointer is NULL (whereas * next alone can be NULL in our semi-doubly-linked lists). */ assert(ph->next==NULL); } ph->after=NULL; ph->next=NULL; ph->prev=NULL; } /*}}}*/ /*{{{ Init/deinit */ static void mplex_get_attach_params(WMPlex *mplex, WStacking *st, WMPlexAttachParams *param) { param->flags=(MPLEX_ATTACH_SIZEPOLICY| MPLEX_ATTACH_GEOM| MPLEX_ATTACH_LEVEL| (st->hidden ? MPLEX_ATTACH_HIDDEN : 0)| (st->lnode==NULL ? MPLEX_ATTACH_UNNUMBERED : 0)); param->szplcy=st->szplcy; param->geom=REGION_GEOM(st->reg); param->level=st->level; } bool mplexpholder_init(WMPlexPHolder *ph, WMPlex *mplex, WStacking *st, WMPlexAttachParams *param) { WLListNode *or_after=NULL; WMPlexPHolder *after=NULL; pholder_init(&(ph->ph)); ph->mplex=mplex; ph->after=NULL; ph->next=NULL; ph->prev=NULL; ph->param.flags=0; ph->recreate_pholder=NULL; if(st!=NULL){ mplex_get_attach_params(mplex, st, &ph->param); if(st->lnode!=NULL){ after=LIST_LAST(st->lnode->phs, next, prev); or_after=st->lnode; } }else{ static WMPlexAttachParams dummy_param={0, 0, {0, 0, 0, 0}, 0, 0}; if(param==NULL) param=&dummy_param; ph->param=*param; if(!(param->flags&MPLEX_ATTACH_UNNUMBERED)){ int index=(param->flags&MPLEX_ATTACH_INDEX ? param->index : mplex_default_index(mplex)); or_after=llist_index_to_after(mplex->mx_list, mplex->mx_current, index); after=(index==LLIST_INDEX_LAST ? (or_after!=NULL ? LIST_LAST(or_after->phs, next, prev) : LIST_LAST(mplex->misc_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, ph->mplex); pholder_deinit(&(ph->ph)); } /*}}}*/ /*{{{ Move, attach */ typedef struct{ WMPlexPHolder *ph, *ph_head; WRegionAttachData *data; WFramedParam *param; WRegion *reg_ret; } RP; static WRegion *recreate_handler(WWindow *par, const WFitParams *fp, void *rp_) { RP *rp=(RP*)rp_; WMPlexPHolder *ph=rp->ph, *ph_head=rp->ph_head, *phtmp; WFramedParam *param=rp->param; WFrame *frame; frame=create_frame(par, fp, param->mode); if(frame==NULL) return NULL; /* Move pholders to frame */ frame->mplex.misc_phs=ph_head; for(phtmp=frame->mplex.misc_phs; phtmp!=NULL; phtmp=phtmp->next) phtmp->mplex=&frame->mplex; /* Attach */ if(fp->mode&(REGION_FIT_BOUNDS|REGION_FIT_WHATEVER)) ph->param.flags|=MPLEX_ATTACH_WHATEVER; rp->reg_ret=mplex_do_attach_pholder(&frame->mplex, ph, rp->data); ph->param.flags&=~MPLEX_ATTACH_WHATEVER; if(rp->reg_ret==NULL){ /* Try to recover */ for(phtmp=frame->mplex.misc_phs; phtmp!=NULL; phtmp=phtmp->next) phtmp->mplex=NULL; frame->mplex.misc_phs=NULL; destroy_obj((Obj*)frame); return NULL; }else{ frame_adjust_to_initial(frame, fp, param, rp->reg_ret); return (WRegion*)frame; } } static WFramedPHolder *get_recreate_ph(WMPlexPHolder *ph) { return get_head(ph)->recreate_pholder; } static WRegion *mplexpholder_attach_recreate(WMPlexPHolder *ph, int flags, WRegionAttachData *data) { WRegionAttachData data2; WFramedPHolder *fph; WPHolder *root; WRegion *res; RP rp; rp.ph_head=get_head(ph); assert(rp.ph_head!=NULL); fph=rp.ph_head->recreate_pholder; if(fph==NULL) return NULL; rp.ph=ph; rp.data=data; rp.param=&fph->param; rp.reg_ret=NULL; data2.type=REGION_ATTACH_NEW; data2.u.n.fn=recreate_handler; data2.u.n.param=&rp; res=pholder_do_attach(fph->cont, flags, &data2); if(res!=NULL){ rp.ph_head->recreate_pholder=NULL; /* It might be in use in attach chain! So defer. */ mainloop_defer_destroy((Obj*)fph); } return (flags&PHOLDER_ATTACH_RETURN_CREATEROOT ? (WRegion*)res : rp.reg_ret); } WRegion *mplexpholder_do_attach(WMPlexPHolder *ph, int flags, WRegionAttachData *data) { WMPlex *mplex=ph->mplex; if(mplex==NULL) return mplexpholder_attach_recreate(ph, flags, data); 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, ph->mplex); ph->mplex=mplex; if(mplex==NULL) return TRUE; mplexpholder_do_link(ph, mplex, after, or_after); return TRUE; } bool mplexpholder_do_goto(WMPlexPHolder *ph) { WRegion *reg=(WRegion*)ph->mplex; if(reg!=NULL){ return region_goto(reg); }else{ WFramedPHolder *fph=get_recreate_ph(ph); return (fph!=NULL ? pholder_do_goto((WPHolder*)fph) : FALSE); } } WRegion *mplexpholder_do_target(WMPlexPHolder *ph) { WRegion *reg=(WRegion*)ph->mplex; if(reg!=NULL){ return reg; }else{ WFramedPHolder *fph=get_recreate_ph(ph); return (fph!=NULL ? pholder_do_target((WPHolder*)fph) : NULL); } } bool mplexpholder_stale(WMPlexPHolder *ph) { WRegion *reg=(WRegion*)ph->mplex; if(reg!=NULL){ return FALSE; }else{ WFramedPHolder *fph=get_recreate_ph(ph); return (fph==NULL || pholder_stale((WPHolder*)fph)); } } /*}}}*/ /*{{{ 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->misc_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); } void mplex_flatten_phs(WMPlex *mplex) { WLListNode *node; WLListIterTmp tmp; FOR_ALL_NODES_ON_LLIST(node, mplex->mx_list, tmp){ WMPlexPHolder *last=(mplex->misc_phs==NULL ? NULL : mplex->misc_phs->prev); mplex_move_phs(mplex, node, last, NULL); } } void mplex_migrate_phs(WMPlex *src, WMPlex *dst) { WLListNode *or_after=LIST_LAST(dst->mx_list, next, prev); WMPlexPHolder *after=(or_after!=NULL ? LIST_LAST(or_after->phs, next, prev) : LIST_LAST(dst->misc_phs, next, prev)); while(src->misc_phs!=NULL){ WMPlexPHolder *ph=src->misc_phs; mplexpholder_move(ph, dst, after, or_after); after=ph; } } /*}}}*/ /*{{ Rescue */ WRegion *mplex_rescue_attach(WMPlex *mplex, int flags, WRegionAttachData *data) { WMPlexAttachParams param; param.flags=0; /* Improved attach parametrisation hack for WMPlex source */ if(data->type==REGION_ATTACH_REPARENT){ WRegion *reg=data->u.reg; WMPlex *src_mplex=REGION_MANAGER_CHK(reg, WMPlex); if(src_mplex!=NULL){ WStacking *st=ioncore_find_stacking(reg); if(st!=NULL) mplex_get_attach_params(src_mplex, st, ¶m); } } param.flags|=(MPLEX_ATTACH_INDEX| (flags&PHOLDER_ATTACH_SWITCHTO ? MPLEX_ATTACH_SWITCHTO : 0)); param.index=LLIST_INDEX_LAST; return mplex_do_attach(mplex, ¶m, data); } WPHolder *mplex_get_rescue_pholder_for(WMPlex *mplex, WRegion *mgd) { #if 0 WStacking *st=mplex_find_stacking(mplex, mgd); WMPlexAttachParams param; param.flags=MPLEX_ATTACH_INDEX; param.index=LLIST_INDEX_LAST; return create_mplexpholder(mplex, st, ¶m); #else return (WPHolder*)create_basicpholder((WRegion*)mplex, (WBasicPHolderHandler*)mplex_rescue_attach); #endif } /*}}}*/ /*{{{ 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}, {(DynFun*)pholder_stale, (DynFun*)mplexpholder_stale}, END_DYNFUNTAB }; IMPLCLASS(WMPlexPHolder, WPHolder, mplexpholder_deinit, mplexpholder_dynfuntab); /*}}}*/ notion-3+2012042300/ioncore/mplexpholder.h000066400000000000000000000042651174530661200201030ustar00rootroot00000000000000/* * ion/ioncore/mplexpholder.h * * Copyright (c) Tuomo Valkonen 2005-2009. * * See the included file LICENSE for details. */ #ifndef ION_IONCORE_MPLEXPHOLDER_H #define ION_IONCORE_MPLEXPHOLDER_H #include "common.h" #include "pholder.h" #include "mplex.h" #include "attach.h" #include "framedpholder.h" DECLCLASS(WMPlexPHolder){ WPHolder ph; WMPlex *mplex; WFramedPHolder *recreate_pholder; /* only on first of list */ WLListNode *after; WMPlexPHolder *next, *prev; WMPlexAttachParams param; }; /* If 'either_st' is set, it is used, otherwise 'or_param', is used. */ 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 WRegion *mplexpholder_do_attach(WMPlexPHolder *ph, int flags, WRegionAttachData *data); extern bool mplexpholder_do_goto(WMPlexPHolder *ph); extern bool mplexpholder_stale(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 void mplex_migrate_phs(WMPlex *src, WMPlex *dst); extern void mplex_flatten_phs(WMPlex *mplex); extern WMPlexPHolder *mplex_managed_get_pholder(WMPlex *mplex, WRegion *mgd); extern WPHolder *mplex_get_rescue_pholder_for(WMPlex *mplex, WRegion *mgd); #endif /* ION_IONCORE_MPLEXPHOLDER_H */ notion-3+2012042300/ioncore/mwmhints.c000066400000000000000000000023371174530661200172370ustar00rootroot00000000000000/* * ion/ioncore/mwmhints.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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); } notion-3+2012042300/ioncore/mwmhints.h000066400000000000000000000027271174530661200172470ustar00rootroot00000000000000/* * ion/ioncore/mwmhints.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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 */ notion-3+2012042300/ioncore/names.c000066400000000000000000000322001174530661200164640ustar00rootroot00000000000000/* * ion/ioncore/names.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #include #include #include #include #include #include #include "common.h" #include "global.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, ioncore_g.notifies.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 \codestr{} 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 \codestr{} 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; if(typenam!=NULL && !obj_is_str((Obj*)node->v.val, typenam)) 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 bool do_list(ExtlFn fn, WNamespace *ns, const char *typenam) { Rb_node node; int n=0; if(!ns->initialised) return FALSE; 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_iter_obj(fn, (Obj*)reg)) return FALSE; } return TRUE; } /*EXTL_DOC * Iterate over all non-client window regions with (inherited) class * \var{typenam} until \var{iterfn} returns \code{false}. * The function is called in protected mode. * This routine returns \code{true} if it reaches the end of list * without this happening. */ EXTL_SAFE EXTL_EXPORT bool ioncore_region_i(ExtlFn fn, const char *typenam) { return do_list(fn, &ioncore_internal_ns, typenam); } /*EXTL_DOC * Iterate over client windows until \var{iterfn} returns \code{false}. * The function is called in protected mode. * This routine returns \code{true} if it reaches the end of list * without this happening. */ EXTL_SAFE EXTL_EXPORT bool ioncore_clientwin_i(ExtlFn fn) { return do_list(fn, &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); } /*}}}*/ notion-3+2012042300/ioncore/names.h000066400000000000000000000031451174530661200164770ustar00rootroot00000000000000/* * ion/ioncore/names.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #ifndef ION_IONCORE_NAMES_H #define ION_IONCORE_NAMES_H #include "region.h" #include "clientwin.h" #include "gr.h" #include typedef struct{ /** Names, keyed by WRegionNameInfo's */ struct rb_node *rb; bool initialised; } WNamespace; extern WNamespace ioncore_internal_ns; extern WNamespace ioncore_clientwin_ns; /** Register a region (but not a clientwin) to the naming registry */ 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 bool ioncore_region_i(ExtlFn fn, const char *typenam); extern bool ioncore_clientwin_i(ExtlFn fn); /** * Look up a region (internal windows, not client windows) by name and type * name. * * As region names are unique, the 'typename' parameter is only used to filter * out regions that do not have the expected type. */ extern WRegion *ioncore_lookup_region(const char *cname, const char *typenam); extern WClientWin *ioncore_lookup_clientwin(const char *cname); #endif /* ION_IONCORE_NAMES_H */ notion-3+2012042300/ioncore/navi.c000066400000000000000000000201341174530661200163210ustar00rootroot00000000000000/* * ion/ioncore/navi.c * * Copyright (c) Tuomo Valkonen 2006-2009. * * See the included file LICENSE for details. */ #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{ /* Set to TRUE for cycling out of nested workspaces etc. */ 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{ /* Set to TRUE for cycling into nested workspaces etc. */ 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} * (\codestr{up}, \codestr{down}, \codestr{left}, \codestr{right}, * \codestr{next}, \codestr{prev}, or \codestr{any}). The table \var{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 boolean functions \var{ascend_filter} and \var{descend_filter} * on \var{WRegion} pairs (\var{to}, \var{from}), are used to decide when * to descend or ascend into another region. */ 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}. * 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) region_goto(res); return res; } /*EXTL_DOC * Go to region next from \var{reg} in direction \var{dirstr}. * 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}. * 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)); } notion-3+2012042300/ioncore/navi.h000066400000000000000000000027221174530661200163310ustar00rootroot00000000000000/* * ion/ioncore/navi.h * * Copyright (c) Tuomo Valkonen 2006-2009. * * See the included file LICENSE for details. */ #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 */ notion-3+2012042300/ioncore/netwm.c000066400000000000000000000262271174530661200165270ustar00rootroot00000000000000/* * ion/ioncore/netwm.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #include #include #include #include "common.h" #include "global.h" #include "fullscreen.h" #include "clientwin.h" #include "netwm.h" #include "property.h" #include "activity.h" #include "focus.h" #include "xwindow.h" #include "extlconv.h" #include "group.h" #include "event.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_wm_state_demands_attention=0; static Atom atom_net_supporting_wm_check=0; static Atom atom_net_virtual_roots=0; static Atom atom_net_active_window=0; static Atom atom_net_wm_user_time=0; static Atom atom_net_wm_allowed_actions=0; static Atom atom_net_wm_moveresize=0; #define N_NETWM 9 static Atom atom_net_supported=0; #define SOURCE_UNKNOWN 0 #define SOURCE_APPLICATION 1 #define SOURCE_PAGER 2 /*}}}*/ /*{{{ 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_wm_state_demands_attention=XInternAtom(ioncore_g.dpy, "_NET_WM_STATE_DEMANDS_ATTENTION", 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); atom_net_wm_user_time=XInternAtom(ioncore_g.dpy, "_NET_WM_USER_TIME", False); atom_net_wm_allowed_actions=XInternAtom(ioncore_g.dpy, "_NET_WM_ALLOWED_ACTIONS", False); atom_net_wm_moveresize=XInternAtom(ioncore_g.dpy, "_NET_WM_MOVERESIZE", 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_wm_state_demands_attention; atoms[4]=atom_net_supporting_wm_check; atoms[5]=atom_net_virtual_roots; atoms[6]=atom_net_active_window; atoms[7]=atom_net_wm_allowed_actions; atoms[8]=atom_net_wm_moveresize; 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, rw->dummy_win, 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); p[0]=libtu_progbasename(); /** * Unfortunately we cannot determine the charset of libtu_progbasename() * so we'll just have to guess it makes sense in the current locale charset */ xwindow_set_utf8_property(rw->dummy_win, atom_net_wm_name, p, 1); } /*}}}*/ /*{{{ _NET_WM_STATE */ bool netwm_check_initial_fullscreen(WClientWin *cwin) { 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 FALSE; for(i=0; iregion))) data[n++]=atom_net_wm_state_demands_attention; XChangeProperty(ioncore_g.dpy, cwin->win, atom_net_wm_state, XA_ATOM, 32, PropModeReplace, (uchar*)data, n); } void netwm_update_allowed_actions(WClientWin *cwin) { CARD32 data[1]; int n=0; /* TODO add support for 'resize' and list it here */ /* TODO add support for 'minimize' and list it here */ /* TODO add support for 'maximize_horz' and list it here */ /* TODO add support for 'maximize_vert' and list it here */ /* TODO add support for 'fullscreen' and list it here */ /* TODO add support for 'change desktop' and list it here */ /* TODO add support for 'close' and list it here */ /* TODO add support for 'above' and list it here */ /* TODO add support for 'below' and list it here */ XChangeProperty(ioncore_g.dpy, cwin->win, atom_net_wm_allowed_actions, 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){ WRegion *grp=region_groupleader_of((WRegion*)cwin); bool sw=clientwin_fullscreen_may_switchto(cwin); cwin->flags|=CLIENTWIN_FS_RQ; if(!region_enter_fullscreen(grp, 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){ WRegion *grp=region_groupleader_of((WRegion*)cwin); bool sw=clientwin_fullscreen_may_switchto(cwin); cwin->flags&=~CLIENTWIN_FS_RQ; region_leave_fullscreen(grp, 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) { long source=ev->data.l[0]; /** * By default we ignore non-pager activity requests, as they're known to * steal focus from newly-created windows :( */ bool ignore=source!=SOURCE_PAGER; extl_table_gets_b(cwin->proptab, "ignore_net_active_window", &ignore); if(!ignore) region_goto((WRegion*)cwin); else region_set_activity((WRegion*)cwin, SETPARAM_SET); } /*}}}*/ /*{{{ _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; } /*}}}*/ /*{{{ user time */ /** When a new window is mapped, look at the netwm user time to find out * whether the new window should be switched to and get the focus. * * It is unclear what the desired behavior would be, and how we should takee * into consideration ioncore_g.usertime_diff_new and IONCORE_CLOCK_SKEW_MS, * so for now we deny raising the new window only in the special case where * its user time is set to 0, specifically preventing it from being raised. */ void netwm_check_manage_user_time(WClientWin *cwin, WManageParams *param) { /* the currently focussed window */ WClientWin *cur=OBJ_CAST(ioncore_g.focus_current, WClientWin); /* the new window */ Window win=region_xwindow((WRegion*)cwin); Time now=ioncore_get_timestamp(); /* TODO: should really use the event.. */ /* user time, current window user time */ CARD32 ut=0, cut=0; /* whether the new (got) and current (gotcut) windows had their usertime * set */ bool got=FALSE, gotcut=FALSE; bool nofocus=FALSE; if(cur!=NULL){ Window curwin; CARD32 cut; if(param->tfor==cur) return; curwin=region_xwindow((WRegion*)cur); gotcut=xwindow_get_cardinal_property(curwin, atom_net_wm_user_time, &cut); } got=xwindow_get_cardinal_property(win, atom_net_wm_user_time, &ut); /* The special value of zero on a newly mapped window can be used to * request that the window not be initially focused when it is mapped */ if (got && ut == 0) nofocus = TRUE; /* there was some other logic here, but it was not clear how it was meant * to work and prevented newly created windows from receiving the focus * in some cases * (https://sourceforge.net/tracker/?func=detail&aid=3109576&group_id=314802&atid=1324528) * Stripped until we decide how this is supposed to behave. */ if(nofocus){ param->switchto=FALSE; param->jumpto=FALSE; } } /*}}}*/ /*{{{ _NET_WM_VIRTUAL_ROOTS */ int count_screens() { int result = 0; WScreen *scr; FOR_ALL_SCREENS(scr){ result++; } return result; } /*EXTL_DOC * refresh \_NET\_WM\_VIRTUAL\_ROOTS */ EXTL_SAFE EXTL_EXPORT void ioncore_screens_updated(WRootWin *rw) { int current_screen = 0; int n_screens; CARD32 *virtualroots; WScreen *scr; n_screens = count_screens(); virtualroots = (CARD32*)malloc(n_screens * sizeof(CARD32)); FOR_ALL_SCREENS(scr){ virtualroots[current_screen] = region_xwindow((WRegion *)scr); current_screen++; } XChangeProperty(ioncore_g.dpy, WROOTWIN_ROOT(rw), atom_net_virtual_roots, XA_WINDOW, 32, PropModeReplace, (uchar*)virtualroots, n_screens); free(virtualroots); } /*}}}*/ notion-3+2012042300/ioncore/netwm.h000066400000000000000000000022001174530661200165150ustar00rootroot00000000000000/* * ion/ioncore/netwm.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #ifndef ION_IONCORE_NETWM_H #define ION_IONCORE_NETWM_H #include "common.h" #include "rootwin.h" #include "screen.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 bool netwm_check_initial_fullscreen(WClientWin *cwin); extern void netwm_update_state(WClientWin *cwin); extern void netwm_update_allowed_actions(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); extern void netwm_check_manage_user_time(WClientWin *cwin, WManageParams *param); extern void ioncore_screens_updated(WRootWin *rw); #endif /* ION_IONCORE_NETWM_H */ notion-3+2012042300/ioncore/pholder.c000066400000000000000000000065771174530661200170400ustar00rootroot00000000000000/* * ion/ioncore/pholder.c * * Copyright (c) Tuomo Valkonen 2005-2009. * * See the included file LICENSE for details. */ #include #include "common.h" #include "attach.h" #include "pholder.h" #include "focus.h" bool pholder_init(WPHolder *ph) { return TRUE; } void pholder_deinit(WPHolder *ph) { } 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; } bool pholder_attach(WPHolder *ph, int flags, WRegion *reg) { WRegionAttachData data; data.type=REGION_ATTACH_REPARENT; data.u.reg=reg; return (pholder_do_attach(ph, flags, &data)!=NULL); } bool pholder_attach_mcfgoto(WPHolder *ph, int flags, WRegion *reg) { bool cf=region_may_control_focus(reg); if(!pholder_attach(ph, flags, reg)) return FALSE; if(cf) region_goto(reg); return TRUE; } 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) { return pholder_do_target(ph); } static bool pholder_do_check_reparent_default(WPHolder *ph, WRegion *reg) { WRegion *target=pholder_do_target(ph); return (target==NULL ? FALSE : region_ancestor_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) { 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) { return pholder_do_goto(ph); } bool pholder_stale_default(WPHolder *ph) { return (pholder_target(ph)==NULL); } bool pholder_stale(WPHolder *ph) { bool ret=TRUE; CALL_DYN_RET(ret, bool, pholder_stale, ph, (ph)); return ret; } 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}, {(DynFun*)pholder_stale, (DynFun*)pholder_stale_default}, END_DYNFUNTAB }; IMPLCLASS(WPHolder, Obj, pholder_deinit, pholder_dynfuntab); notion-3+2012042300/ioncore/pholder.h000066400000000000000000000030401174530661200170230ustar00rootroot00000000000000/* * ion/ioncore/pholder.h * * Copyright (c) Tuomo Valkonen 2005-2009. * * See the included file LICENSE for details. */ #ifndef ION_IONCORE_PHOLDER_H #define ION_IONCORE_PHOLDER_H #include "common.h" #include "attach.h" #include "extlconv.h" #define PHOLDER_ATTACH_SWITCHTO 0x0001 #define PHOLDER_ATTACH_RETURN_CREATEROOT 0x0002 /* Note: PHolders should be destroyed by their acquirer. */ DECLCLASS(WPHolder){ Obj obj; }; 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); extern bool pholder_attach_mcfgoto(WPHolder *ph, int flags, WRegion *reg); DYNFUN WRegion *pholder_do_target(WPHolder *ph); extern WRegion *pholder_target(WPHolder *ph); DYNFUN bool pholder_stale(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 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 */ notion-3+2012042300/ioncore/pointer.c000066400000000000000000000210241174530661200170430ustar00rootroot00000000000000/* * ion/ioncore/pointer.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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, "ooo", NULL, p_reg, p_subreg, (p_reg!=NULL ? p_reg->active_sub : NULL)); 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(); } static bool listens_to(WRegion *reg, uint state, uint button, int area) { static const int acts[]={BINDING_BUTTONMOTION, BINDING_BUTTONCLICK, BINDING_BUTTONDBLCLICK}; static const int n_acts=3; int i; for(i=0; istate; button=ev->button; reg=(WRegion*)XWINDOW_REGION_OF_T(ev->window, WWindow); if(reg==NULL) return FALSE; dblclick=(p_clickcnt==1 && time_in_threshold(ev->time) && p_button==button && p_state==state); if(dblclick && p_reg!=reg){ if(sub) return FALSE; dblclick=FALSE; } subreg=region_current(reg); area=window_press((WWindow*)reg, ev, &subreg); if(dblclick){ pressbind=region_lookup_binding(reg, BINDING_BUTTONDBLCLICK, state, button, area); } if(pressbind==NULL){ pressbind=region_lookup_binding(reg, BINDING_BUTTONPRESS, state, button, area); } if(pressbind==NULL && sub){ /* If subwindow doesn't listen to state/button(/area) at all, return and * let the parent that has the event grabbed, handle it. Otherwise we * fully block the parent. */ if(!dblclick && !listens_to(reg, state, button, area)) return FALSE; } 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; p_area=area; watch_setup(&p_regwatch, (Obj*)reg, NULL); if(subreg!=NULL) watch_setup(&p_subregwatch, (Obj*)subreg, NULL); ioncore_grab_establish(reg, handle_key, pointer_grab_killed, 0); p_grabstate=ST_HELD; if(pressbind!=NULL) call_button(pressbind, ev); return TRUE; } bool ioncore_do_handle_buttonpress(XButtonEvent *ev) { /* Only one level of subwindows is supported... more would require * searching through the trees thanks to grabbed events being reported * relative to the outermost grabbing window. */ if(ev->subwindow!=None && ev->state!=0){ XButtonEvent ev2=*ev; ev2.window=ev->subwindow; ev2.subwindow=None; if(XTranslateCoordinates(ioncore_g.dpy, ev->window, ev2.window, ev->x, ev->y, &(ev2.x), &(ev2.y), &(ev2.subwindow))){ if(ioncore_dodo_handle_buttonpress(&ev2, TRUE)) return TRUE; } } return ioncore_dodo_handle_buttonpress(ev, FALSE); } 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); if(binding!=NULL) 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); } } /*}}}*/ notion-3+2012042300/ioncore/pointer.h000066400000000000000000000020621174530661200170510ustar00rootroot00000000000000/* * ion/ioncore/pointer.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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 */ notion-3+2012042300/ioncore/presize.c000066400000000000000000000075341174530661200170560ustar00rootroot00000000000000/* * ion/ioncore/presize.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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); } /*}}}*/ notion-3+2012042300/ioncore/presize.h000066400000000000000000000006471174530661200170610ustar00rootroot00000000000000/* * ion/ioncore/presize.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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 */ notion-3+2012042300/ioncore/property.c000066400000000000000000000263351174530661200172610ustar00rootroot00000000000000/* * notion/ioncore/property.c * * Copyright (c) The Notion Team 2011. * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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; bool ok; 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){ Status st=XTextPropertyToStringList(&prop, &list, &n); ok=(st!=0); }else{ int st=XmbTextPropertyToTextList(ioncore_g.dpy, &prop, &list, &n); ok=(st>=0); } XFree(prop.value); if(!ok || 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; bool ok; if(ioncore_g.use_mb){ int st; #ifdef X_HAVE_UTF8_STRING if (ioncore_g.enc_utf8) st=Xutf8TextListToTextProperty(ioncore_g.dpy, (char **)ptr, n, XUTF8StringStyle, &prop); else #endif st=XmbTextListToTextProperty(ioncore_g.dpy, (char **)ptr, n, XTextStyle, &prop); ok=(st>=0); }else{ Status st=XStringListToTextProperty((char **)ptr, n, &prop); ok=(st!=0); } if(!ok) return; XSetTextProperty(ioncore_g.dpy, win, &prop, a); XFree(prop.value); } void xwindow_set_utf8_property(Window win, Atom a, const char **ptr, int n) { #ifndef X_HAVE_UTF8_STRING xwindow_set_text_property(win, a, ptr, n); #else XTextProperty prop; bool ok; int st=XmbTextListToTextProperty(ioncore_g.dpy, (char **)ptr, n, XUTF8StringStyle, &prop); ok=(st>=0); if(!ok) return; XSetTextProperty(ioncore_g.dpy, win, &prop, a); XFree(prop.value); #endif } /*}}}*/ /*{{{ 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) { char *xatomname, *atomname; xatomname = XGetAtomName(ioncore_g.dpy, atom); atomname = scopy(xatomname); XFree(xatomname); return atomname; } #define CP(TYPE) \ { \ TYPE *d=(TYPE*)p; \ for(i=0; i0 && p!=NULL){ *vret=*p; XFree((void*)p); return TRUE; } return FALSE; } /*}}}*/ notion-3+2012042300/ioncore/property.h000066400000000000000000000036111174530661200172560ustar00rootroot00000000000000/* * ion/ioncore/property.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #ifndef ION_IONCORE_PROPERTY_H #define ION_IONCORE_PROPERTY_H #include #include #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); /** * Set a text property. The type of the property (STRING, COMPOUND_STRING, * UTF8_STRING or even any custom multibyte encoding) is determined * automatically based on the string and the current locale. * * This may be used for any property of type 'TEXT' (not 'STRING') in * http://tronche.com/gui/x/icccm/sec-2.html#s-2.6.2 * * @param p null-terminated list of input strings, in the current locale * encoding */ extern void xwindow_set_text_property(Window win, Atom a, const char **p, int n); extern bool xwindow_get_cardinal_property(Window win, Atom a, CARD32 *vret); /** * Set a property as UTF8_STRING. To read UTF8_STRING properties, the normal * xwindow_get_text_property can be used. * * @param p null-terminated list of input strings, in the current locale * encoding */ extern void xwindow_set_utf8_property(Window win, Atom a, const char **p, int n); #endif /* ION_IONCORE_PROPERTY_H */ notion-3+2012042300/ioncore/rectangle.c000066400000000000000000000021431174530661200173300ustar00rootroot00000000000000/* * ion/ioncore/rectangle.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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)); } notion-3+2012042300/ioncore/rectangle.h000066400000000000000000000016551174530661200173440ustar00rootroot00000000000000/* * ion/ioncore/rectangle.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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 */ notion-3+2012042300/ioncore/regbind.c000066400000000000000000000203741174530661200170040ustar00rootroot00000000000000/* * ion/regbind.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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, int act, uint state, uint kcb, 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, act, state, kcb); 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); } } /*}}}*/ notion-3+2012042300/ioncore/regbind.h000066400000000000000000000027371174530661200170140ustar00rootroot00000000000000/* * ion/ioncore/regbind.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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; /* ExtlFn leave; */ /* Watch leave_reg; */ }; 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, int act, uint state, uint kcb, 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 */ notion-3+2012042300/ioncore/reginfo.c000066400000000000000000000030401174530661200170120ustar00rootroot00000000000000/* * ion/ioncore/reginfo.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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; } /*}}}*/ notion-3+2012042300/ioncore/reginfo.h000066400000000000000000000020051174530661200170170ustar00rootroot00000000000000/* * ion/ioncore/reginfo.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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 */ notion-3+2012042300/ioncore/region-iter.h000066400000000000000000000026721174530661200176240ustar00rootroot00000000000000/* * ion/ioncore/region-iter.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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 */ notion-3+2012042300/ioncore/region.c000066400000000000000000000502431174530661200166530ustar00rootroot00000000000000/* * ion/ioncore/region.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #include #include #include #include #include #include "property.h" #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" #include "return.h" #include "key.h" #define D2(X) WHook *region_notify_hook=NULL; static void region_notify_change_(WRegion *reg, WRegionNotify how); /*{{{ 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)); } } 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) { region_notify_change(reg, ioncore_g.notifies.deinit); destroy_children(reg); if(ioncore_g.focus_next==reg){ D(warn("Region to be focused next destroyed[1].")); ioncore_g.focus_next=NULL; } assert(reg->submapstat==NULL); /*region_free_submapstat(reg);*/ region_detach_manager(reg); region_unset_return(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)); region_notify_change_(reg, ioncore_g.notifies.map); } void region_unmap(WRegion *reg) { CALL_DYN(region_unmap, reg, (reg)); region_notify_change_(reg, ioncore_g.notifies.unmap); } 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 */ 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, WRegionNotify 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) { if(TRUE /* !REGION_IS_ACTIVE(reg) || !REGION_IS_MAPPED(reg) || ioncore_g.focus_next!=NULL*/){ WRegion *mgr=REGION_MANAGER(reg); WRegion *par=REGION_PARENT_REG(reg); 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. */ }else if(!REGION_IS_MAPPED(reg)){ region_map(reg); } } 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 void region_rqclose_default(WRegion *reg, bool relocate) { if(relocate || region_may_dispose(reg)) region_defer_rqdispose(reg); } /*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}). The region will not be destroyed when * this function returns. To find out if and when it is destroyed, * use the \codestr{deinit} notification. 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 void region_rqclose(WRegion *reg, bool relocate) { CALL_DYN(region_rqclose, reg, (reg, relocate)); } 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); }else{ region_rqclose(reg, FALSE); return reg; } } /*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. For further details, 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_dispose_default(WRegion *reg) { bool res=region_rescue_needed(reg); if(res){ const char *name=region_name(reg); warn(TR("Can not destroy %s: contains client windows."), (name!=NULL ? name : TR("(unknown)"))); } return !res; } bool region_may_dispose(WRegion *reg) { bool ret=TRUE; CALL_DYN_RET(ret, bool, region_may_dispose, reg, (reg)); return ret; } static WRegion *region_managed_disposeroot_default(WRegion *mgr, WRegion *reg) { return reg; } WRegion *region_managed_disposeroot(WRegion *mgr, WRegion *reg) { WRegion *ret=NULL; CALL_DYN_RET(ret, WRegion*, region_managed_disposeroot, mgr, (mgr, reg)); return ret; } WRegion *region_disposeroot(WRegion *reg) { WRegion *mgr=REGION_MANAGER(reg); return (mgr!=NULL ? region_managed_disposeroot(mgr, reg) : reg); } bool region_rqdispose(WRegion *reg) { WRegion *root; if(!region_may_dispose(reg)) return FALSE; root=region_disposeroot(reg); if(root==NULL) return FALSE; return region_dispose(root); } bool region_dispose_(WRegion *reg, bool not_simple) { bool rescue=not_simple; bool was_mcf=(not_simple && region_may_control_focus(reg)); WPHolder *ph=NULL; if(rescue){ if(!region_rescue(reg, NULL, 0)){ warn(TR("Failed to rescue some client windows - not closing.")); return FALSE; } } if(was_mcf) ph=region_unset_get_return(reg); destroy_obj((Obj*)reg); if(ph!=NULL){ pholder_goto(ph); destroy_obj((Obj*)ph); } return TRUE; } bool region_dispose(WRegion *reg) { return region_dispose_(reg, TRUE); } void region_defer_rqdispose(WRegion *reg) { mainloop_defer_action((Obj*)reg, (WDeferredAction*)region_rqdispose); } /*}}}*/ /*{{{ Manager/parent stuff */ /* Routine to call to unmanage a region */ void region_detach_manager(WRegion *reg) { WRegion *mgr=REGION_MANAGER(reg); if(mgr==NULL) return; region_set_activity(reg, SETPARAM_UNSET); region_managed_remove(mgr, reg); assert(REGION_MANAGER(reg)==NULL); } void region_unset_manager_pseudoactivity(WRegion *reg) { WRegion *mgr=reg->manager, *par=REGION_PARENT_REG(reg); if(mgr==NULL || mgr==par || !REGION_IS_PSEUDOACTIVE(mgr)) return; mgr->flags&=~REGION_PSEUDOACTIVE; region_notify_change(mgr, ioncore_g.notifies.pseudoinactivated); region_unset_manager_pseudoactivity(mgr); } void region_set_manager_pseudoactivity(WRegion *reg) { WRegion *mgr=reg->manager, *par=REGION_PARENT_REG(reg); if(!REGION_IS_ACTIVE(reg) && !REGION_IS_PSEUDOACTIVE(reg)) return; if(mgr==NULL || mgr==par || REGION_IS_PSEUDOACTIVE(mgr)) return; mgr->flags|=REGION_PSEUDOACTIVE; region_notify_change(mgr, ioncore_g.notifies.pseudoactivated); region_set_manager_pseudoactivity(mgr); } /* 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; region_notify_change_(reg, ioncore_g.notifies.unset_manager); region_unset_manager_pseudoactivity(reg); reg->manager=NULL; /* Reset status, as it is set by manager */ reg->flags&=~REGION_SKIP_FOCUS; if(region_is_activity_r(reg)) region_clear_mgd_activity(mgr); region_unset_return(reg); } /* 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; region_set_manager_pseudoactivity(reg); if(region_is_activity_r(reg)) region_mark_mgd_activity(mgr); region_notify_change_(reg, ioncore_g.notifies.set_manager); } 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 \codestr{front} and \codestr{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; } typedef struct{ WRegion *reg; WRegionNotify how; } MRSHP; static bool mrsh_notify_change(WHookDummy *fn, void *p_) { MRSHP *p=(MRSHP*)p_; fn(p->reg, p->how); return TRUE; } static bool mrshe_notify_change(ExtlFn fn, void *p_) { MRSHP *p=(MRSHP*)p_; extl_call(fn, "os", NULL, p->reg, stringstore_get(p->how)); return TRUE; } static void region_notify_change_(WRegion *reg, WRegionNotify how) { MRSHP p; p.reg=reg; p.how=how; extl_protect(NULL); hook_call(region_notify_hook, &p, mrsh_notify_change, mrshe_notify_change), extl_unprotect(NULL); } void region_notify_change(WRegion *reg, WRegionNotify how) { WRegion *mgr=REGION_MANAGER(reg); if(mgr!=NULL) region_managed_notify(mgr, reg, how); region_notify_change_(reg, how); } /*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; } void ioncore_region_notify(WRegion *reg, WRegionNotify how) { const char *p[1]; if(how==ioncore_g.notifies.name && obj_is((Obj*)reg, &CLASSDESCR(WWindow))){ p[0] = region_name(reg); xwindow_set_text_property(((WWindow*)reg)->win, XA_WM_NAME, p, 1); } } /*}}}*/ /*{{{ 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_may_dispose, (DynFun*)region_may_dispose_default}, {(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_managed_disposeroot, (DynFun*)region_managed_disposeroot_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); /*}}}*/ notion-3+2012042300/ioncore/region.h000066400000000000000000000147471174530661200166710ustar00rootroot00000000000000/* * ion/ioncore/region.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #ifndef ION_IONCORE_REGION_H #define ION_IONCORE_REGION_H #include #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_PSEUDOACTIVE 0x0010 #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_PSEUDOACTIVE(R) (((WRegion*)(R))->flags®ION_PSEUDOACTIVE) #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 StringId WRegionNotify; 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 void 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_dispose(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_notify(WRegion *reg, WRegion *sub, WRegionNotify how); DYNFUN WRegion *region_managed_disposeroot(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, WRegionNotify 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 WRegion *region_disposeroot(WRegion *reg); extern bool region_dispose(WRegion *reg); extern bool region_rqdispose(WRegion *reg); extern void region_defer_rqdispose(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 WRegion *region_managed_within(WRegion *reg, WRegion *mgd); extern void region_set_manager_pseudoactivity(WRegion *reg); extern void region_unset_manager_pseudoactivity(WRegion *reg); extern WHook *region_notify_hook; void ioncore_region_notify(WRegion *reg, WRegionNotify how); #endif /* ION_IONCORE_REGION_H */ notion-3+2012042300/ioncore/resize.c000066400000000000000000000506701174530661200166750ustar00rootroot00000000000000/* * ion/ioncore/resize.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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) extern int ioncore_edge_resistance; /*{{{ 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.base_set){ w=maxof(0, w-mode->hints.base_width); h=maxof(0, h-mode->hints.base_height); } if(mode->hints.inc_set){ w/=maxof(1, mode->hints.width_inc); h/=maxof(1, 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;*/ } 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_setorig(WMoveresMode *mode) { mode->dx1=0; mode->dx2=0; mode->dy1=0; mode->dy2=0; mode->origgeom=mode->geom; } static void moveresmode_do_newgeom(WMoveresMode *mode, WRQGeomParams *rq) { if(XOR_RESIZE) moveres_draw_rubberband(mode); if(mode->reg!=NULL){ rq->flags|=mode->rqflags; region_rqgeom(mode->reg, rq, &mode->geom); } moveres_draw_infowin(mode); if(XOR_RESIZE) moveres_draw_rubberband(mode); } static int clamp_up(int t, int low, int high) { return (t < high && t > low ? high : t); } 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 er=ioncore_edge_resistance; 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; 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=maxof(1, mode->origgeom.w-realdx1+realdx2); h=maxof(1, mode->origgeom.h-realdy1+realdy2); if(mode->snap_enabled && mode->hints.base_set){ w=clamp_up(w, mode->hints.base_width-er, mode->hints.base_width); h=clamp_up(h, mode->hints.base_height-er, mode->hints.base_height); } /* Correct size */ 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; } moveresmode_do_newgeom(mode, &rq); if(!mode->resize_cumulative) moveresmode_setorig(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); } void moveresmode_rqgeom(WMoveresMode *mode, WRQGeomParams *rq, WRectangle *rret) { mode->mode=MOVERES_SIZE; moveresmode_do_newgeom(mode, rq); moveresmode_setorig(mode); } /* 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); } } void rqgeomparams_from_table(WRQGeomParams *rq, const WRectangle *origg, ExtlTab g) { rq->geom=*origg; 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); } /*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) { WRQGeomParams rq=RQGEOMPARAMS_INIT; WRectangle res; rqgeomparams_from_table(&rq, ®ION_GEOM(reg), g); region_rqgeom(reg, &rq, &res); return extl_table_from_rectangle(&res); } 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_SHADED) frame->flags|=FRAME_SHADED_TOGGLE; if(frame->flags&FRAME_SAVED_VERT) rqh(frame, frame->saved_y, frame->saved_h); frame->flags&=~(FRAME_MAXED_VERT|FRAME_SAVED_VERT|FRAME_SHADED_TOGGLE); 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; } } /*}}}*/ notion-3+2012042300/ioncore/resize.h000066400000000000000000000124631174530661200167000ustar00rootroot00000000000000/* * ion/ioncore/resize.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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 void moveresmode_rqgeom(WMoveresMode *mode, WRQGeomParams *rq, 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); extern void rqgeomparams_from_table(WRQGeomParams *rq, const WRectangle *origg, ExtlTab g); #endif /* ION_IONCORE_RESIZE_H */ notion-3+2012042300/ioncore/return.c000066400000000000000000000047721174530661200167150ustar00rootroot00000000000000/* * ion/ioncore/return.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #include #include #include "common.h" #include "global.h" #include "region.h" #include "pholder.h" #include "return.h" /*{{{ Storage tree */ static Rb_node retrb=NULL; /*}}}*/ /*{{{ Set */ bool region_do_set_return(WRegion *reg, WPHolder *ph) { Rb_node node; int found=0; assert(!OBJ_IS_BEING_DESTROYED(reg)); region_unset_return(reg); if(retrb==NULL){ retrb=make_rb(); if(retrb==NULL) return FALSE; } node=rb_insertp(retrb, reg, ph); region_notify_change(reg, ioncore_g.notifies.set_return); return (node!=NULL); } WPHolder *region_make_return_pholder(WRegion *reg) { WRegion *mgr=region_manager(reg); if(mgr==NULL) return NULL; return region_managed_get_pholder(mgr, reg); } /* extern WPHolder *region_set_return(WRegion *reg) { WPHolder *ph=region_make_return_pholder(reg); if(ph!=NULL){ if(region_do_set_return(reg, ph)) return ph; destroy_obj((Obj*)ph); } return NULL; } */ /*}}}*/ /*{{{ Get */ Rb_node do_find(WRegion *reg) { int found=0; Rb_node node; if(retrb==NULL) return NULL; node=rb_find_pkey_n(retrb, reg, &found); return (found ? node : NULL); } WPHolder *region_do_get_return(WRegion *reg) { Rb_node node=do_find(reg); return (node!=NULL ? (WPHolder*)node->v.val : NULL); } WPHolder *region_get_return(WRegion *reg) { /* Should managers be scanned? */ return region_do_get_return(reg); } /*}}}*/ /*{{{ Unset */ static WPHolder *do_remove_node(Rb_node node) { WPHolder *ph=(WPHolder*)node->v.val; rb_delete_node(node); return ph; } WPHolder *region_unset_get_return(WRegion *reg) { Rb_node node; node=do_find(reg); if(node!=NULL){ region_notify_change(reg, ioncore_g.notifies.unset_return); return do_remove_node(node); }else{ return NULL; } } void region_unset_return(WRegion *reg) { WPHolder *ph=region_unset_get_return(reg); if(ph!=NULL) mainloop_defer_destroy((Obj*)ph); } /*}}}*/ /*{{{ Internal Lua exports */ EXTL_SAFE EXTL_EXPORT_MEMBER WRegion *region___return_target(WRegion *reg) { WPHolder *ph=region_get_return(reg); return (ph!=NULL ? pholder_target(ph) : NULL); } /*}}}*/ notion-3+2012042300/ioncore/return.h000066400000000000000000000014621174530661200167130ustar00rootroot00000000000000/* * ion/ioncore/return.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #ifndef ION_IONCORE_RETURN_H #define ION_IONCORE_RETURN_H #include "region.h" #include "pholder.h" /* Note: 'ph' is under the control of this 'return' module after succesfully * (true return value) having been passed to this function and must no * longer be destroyed by other code unless removed with unset_get. */ extern bool region_do_set_return(WRegion *reg, WPHolder *ph); /*extern WPHolder *region_set_return(WRegion *reg);*/ extern WPHolder *region_get_return(WRegion *reg); extern void region_unset_return(WRegion *reg); extern WPHolder *region_unset_get_return(WRegion *reg); extern WPHolder *region_make_return_pholder(WRegion *reg); #endif /* ION_IONCORE_RETURN_H */ notion-3+2012042300/ioncore/rootwin.c000066400000000000000000000230571174530661200170740ustar00rootroot00000000000000/* * ion/ioncore/rootwin.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #include #include #include #include #include #include #include #include #include #include #include #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 "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. * * Apparently XGetWindowProperty can return BadMatch on a race * condition where the server is already reusing the XID for a * non-window drawable, so let's just ignore BadMatch entirely... */ 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); p[0] = "WRootWin"; xwindow_set_text_property(rootwin->dummy_win, XA_WM_NAME, p, 1); 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 Atom net_virtual_roots=None; static bool rootwin_init(WRootWin *rootwin, int xscr) { Display *dpy=ioncore_g.dpy; WFitParams fp; Window root; WScreen *scr; /* 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." "Maybe another window manager is running?"), xscr); return FALSE; } 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, &fp, root, "WRootWin")){ free(rootwin); return FALSE; } ((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_screen_bindmap); scr=create_screen(rootwin, &fp, xscr); if(scr==NULL){ free(rootwin); return FALSE; } region_set_manager((WRegion*)scr, (WRegion*)rootwin); region_map((WRegion*)scr); LINK_ITEM(*(WRegion**)&ioncore_g.rootwins, (WRegion*)rootwin, p_next, p_prev); ioncore_screens_updated(rootwin); xwindow_set_cursor(root, IONCORE_CURSOR_DEFAULT); return TRUE; } WRootWin *create_rootwin(int xscr) { CREATEOBJ_IMPL(WRootWin, rootwin, (p, xscr)); } void rootwin_deinit(WRootWin *rw) { WScreen *scr, *next; FOR_ALL_SCREENS_W_NEXT(scr, next){ if(REGION_MANAGER(scr)==(WRegion*)rw) destroy_obj((Obj*)scr); } UNLINK_ITEM(*(WRegion**)&ioncore_g.rootwins, (WRegion*)rw, p_next, p_prev); XSelectInput(ioncore_g.dpy, WROOTWIN_ROOT(rw), 0); XFreeGC(ioncore_g.dpy, rw->xor_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; } /*EXTL_DOC * Returns the first WRootWin */ EXTL_SAFE EXTL_EXPORT WRootWin *ioncore_rootwin() { return ioncore_g.rootwins; } /*}}}*/ /*{{{ 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); /*}}}*/ notion-3+2012042300/ioncore/rootwin.h000066400000000000000000000016741174530661200171020ustar00rootroot00000000000000/* * ion/ioncore/rootwin.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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" #include "screen.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 *create_rootwin(int xscr); #endif /* ION_IONCORE_ROOTWIN_H */ notion-3+2012042300/ioncore/saveload.c000066400000000000000000000143771174530661200171760ustar00rootroot00000000000000/* * ion/ioncore/saveload.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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; 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; } /*}}}*/ /*{{{ Load support functions */ static WPHolder **current_ph_p=NULL; WPHolder *ioncore_get_load_pholder() { if(current_ph_p==NULL){ return NULL; }else{ WPHolder *ph=*current_ph_p; *current_ph_p=NULL; return ph; } } WRegion *create_region_load(WWindow *par, const WFitParams *fp, ExtlTab tab, WPHolder **sm_ph_p) { char *objclass=NULL, *name=NULL; WRegionLoadCreateFn* fn=NULL; WRegClassInfo *info=NULL; WRegion *reg=NULL; bool grouped=FALSE; char *grouped_name=NULL; WPHolder **old_ph_p; if(!extl_table_gets_s(tab, "type", &objclass)) return NULL; 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); old_ph_p=current_ph_p; current_ph_p=sm_ph_p; reg=fn(par, fp, tab); current_ph_p=old_ph_p; if(reg!=NULL){ if(!OBJ_IS(reg, WClientWin)){ if(extl_table_gets_s(tab, "name", &name)){ region_set_name(reg, name); free(name); } } } 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; } static bool get_config_clientwins=TRUE; ExtlTab region_get_configuration(WRegion *reg) { ExtlTab tab=extl_table_none(); if(get_config_clientwins || !OBJ_IS(reg, WClientWin)){ CALL_DYN_RET(tab, ExtlTab, region_get_configuration, reg, (reg)); } return tab; } /*EXTL_DOC * Get configuration tree. If \var{clientwins} is unset, client windows * are filtered out. */ EXTL_EXPORT_AS(WRegion, get_configuration) ExtlTab region_get_configuration_extl(WRegion *reg, bool clientwins) { ExtlTab tab; get_config_clientwins=clientwins; tab=region_get_configuration(reg); get_config_clientwins=TRUE; 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; /* Potential Xinerama or such support should set the screen ID * of the root window to less than zero, and number its own * fake screens up from 0. */ if(screen_id(scr)<0) continue; 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; /* See note above */ if(screen_id(scr)<0) continue; 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; } /*}}}*/ notion-3+2012042300/ioncore/saveload.h000066400000000000000000000021051174530661200171650ustar00rootroot00000000000000/* * ion/ioncore/saveload.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #ifndef ION_IONCORE_SAVELOAD_H #define ION_IONCORE_SAVELOAD_H #include #include "common.h" #include "region.h" #include "screen.h" #include "pholder.h" #include "attach.h" extern WRegion *create_region_load(WWindow *par, const WFitParams *fp, ExtlTab tab, WPHolder **sm_ph_p); 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); extern void ioncore_set_sm_callbacks(SMAddCallback *add, SMCfgCallback *cfg); extern void ioncore_get_sm_callbacks(SMAddCallback **add, SMCfgCallback **cfg); extern WPHolder *ioncore_get_load_pholder(); #endif /* ION_IONCORE_SAVELOAD_H */ notion-3+2012042300/ioncore/screen-notify.c000066400000000000000000000207151174530661200201560ustar00rootroot00000000000000/* * ion/ioncore/screen-notify.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #include #include #include #include "common.h" #include "global.h" #include "infowin.h" #include "activity.h" #include "tags.h" #include "gr.h" #include "gr-util.h" #include "stacking.h" #include "names.h" #include "screen.h" #include "screen-notify.h" #include "strings.h" /*{{{ Generic stuff */ static WInfoWin *do_get_notifywin(WScreen *scr, Watch *watch, uint pos, char *style) { 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| MPLEX_ATTACH_PASSIVE); param.level=STACKING_LEVEL_ON_TOP; param.geom.x=0; param.geom.y=0; param.geom.w=1; param.geom.h=1; switch(pos){ case MPLEX_STDISP_TL: param.szplcy=SIZEPOLICY_GRAVITY_NORTHWEST; param.geom.x=0; break; case MPLEX_STDISP_TR: param.szplcy=SIZEPOLICY_GRAVITY_NORTHEAST; param.geom.x=REGION_GEOM(scr).w-1; break; case MPLEX_STDISP_BL: param.szplcy=SIZEPOLICY_GRAVITY_SOUTHWEST; param.geom.x=0; param.geom.y=REGION_GEOM(scr).h-1; break; case MPLEX_STDISP_BR: param.szplcy=SIZEPOLICY_GRAVITY_SOUTHEAST; param.geom.x=REGION_GEOM(scr).w-1; param.geom.y=REGION_GEOM(scr).h-1; break; } iw=(WInfoWin*)mplex_do_attach_new(&scr->mplex, ¶m, (WRegionCreateFn*)create_infowin, style); if(iw!=NULL) watch_setup(watch, (Obj*)iw, NULL); } return iw; } static void do_unnotify(Watch *watch) { Obj *iw=watch->obj; if(iw!=NULL){ watch_reset(watch); mainloop_defer_destroy((Obj*)iw); } } /*}}}*/ /*{{{ Notifywin */ static WInfoWin *get_notifywin(WScreen *scr) { WRegion *stdisp=NULL; WMPlexSTDispInfo info; uint pos=MPLEX_STDISP_TL; mplex_get_stdisp(&scr->mplex, &stdisp, &info); if(stdisp!=NULL) pos=info.pos; return do_get_notifywin(scr, &scr->notifywin_watch, pos, "actnotify"); } void screen_notify(WScreen *scr, const char *str) { WInfoWin *iw=get_notifywin(scr); if(iw!=NULL){ int maxw=REGION_GEOM(scr).w/3; infowin_set_text(iw, str, maxw); } } void screen_unnotify(WScreen *scr) { do_unnotify(&scr->notifywin_watch); } static bool ws_mapped(WScreen *scr, WRegion *reg) { while(1){ WRegion *mgr=REGION_MANAGER(reg); if(mgr==NULL) return FALSE; if(mgr==(WRegion*)scr) return REGION_IS_MAPPED(reg); reg=mgr; } } static void screen_managed_activity(WScreen *scr) { char *notstr=NULL; WRegion *reg; ObjListIterTmp tmp; PtrListIterTmp tmp2; ObjList *actlist=ioncore_activity_list(); WInfoWin *iw=NULL; PtrList *found=NULL; int nfound=0, nadded=0; int w=0, maxw=REGION_GEOM(scr).w/4; /* Lisäksi minimipituus (10ex tms.), ja sen yli menevät jätetään * pois (+ n) */ FOR_ALL_ON_OBJLIST(WRegion*, reg, actlist, tmp){ if(region_screen_of(reg)!=scr || ws_mapped(scr, reg)) continue; if(region_name(reg)==NULL) continue; if(ptrlist_insert_last(&found, reg)) nfound++; } if(found==NULL) goto unnotify; iw=get_notifywin(scr); if(iw==NULL) return; if(iw->brush==NULL) goto unnotify; notstr=scopy(TR("act: ")); if(notstr==NULL) goto unnotify; FOR_ALL_ON_PTRLIST(WRegion*, reg, found, tmp2){ const char *nm=region_name(reg); char *nstr=NULL, *label=NULL; w=grbrush_get_text_width(iw->brush, notstr, strlen(notstr)); if(w>=maxw) break; label=grbrush_make_label(iw->brush, nm, maxw-w); if(label!=NULL){ nstr=(nadded>0 ? scat3(notstr, ", ", label) : scat(notstr, label)); if(nstr!=NULL){ free(notstr); notstr=nstr; nadded++; } free(label); } } if(nfound > nadded){ char *nstr=NULL; libtu_asprintf(&nstr, "%s +%d", notstr, nfound-nadded); if(nstr!=NULL){ free(notstr); notstr=nstr; } } ptrlist_clear(&found); infowin_set_text(iw, notstr, 0); free(notstr); return; unnotify: screen_unnotify(scr); } static void screen_do_update_notifywin(WScreen *scr) { if(ioncore_g.screen_notify) screen_managed_activity(scr); else screen_unnotify(scr); } /*}}}*/ /*{{{ Infowin */ static WInfoWin *get_infowin(WScreen *scr) { WRegion *stdisp=NULL; WMPlexSTDispInfo info; uint pos=MPLEX_STDISP_TR; mplex_get_stdisp(&scr->mplex, &stdisp, &info); if(stdisp!=NULL && info.pos==MPLEX_STDISP_TR) pos=MPLEX_STDISP_BR; return do_get_notifywin(scr, &scr->infowin_watch, pos, "tab-info"); } void screen_windowinfo(WScreen *scr, const char *str) { WInfoWin *iw=get_infowin(scr); if(iw!=NULL){ int maxw=REGION_GEOM(scr).w/3; infowin_set_text(iw, str, maxw); } } void screen_nowindowinfo(WScreen *scr) { do_unnotify(&scr->infowin_watch); } GR_DEFATTR(active); GR_DEFATTR(inactive); GR_DEFATTR(selected); GR_DEFATTR(tagged); GR_DEFATTR(not_tagged); GR_DEFATTR(not_dragged); GR_DEFATTR(activity); GR_DEFATTR(no_activity); static void init_attr() { GR_ALLOCATTR_BEGIN; GR_ALLOCATTR(active); GR_ALLOCATTR(inactive); GR_ALLOCATTR(selected); GR_ALLOCATTR(tagged); GR_ALLOCATTR(not_tagged); GR_ALLOCATTR(not_dragged); GR_ALLOCATTR(no_activity); GR_ALLOCATTR(activity); GR_ALLOCATTR_END; } static void screen_do_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) && !REGION_IS_ACTIVE(scr)); bool sac=REGION_IS_ACTIVE(scr); if(tag || act){ const char *n=region_displayname(reg); WInfoWin *iw; screen_windowinfo(scr, n); iw=(WInfoWin*)scr->infowin_watch.obj; if(iw!=NULL){ GrStyleSpec *spec=infowin_stylespec(iw); init_attr(); gr_stylespec_unalloc(spec); gr_stylespec_set(spec, GR_ATTR(selected)); gr_stylespec_set(spec, GR_ATTR(not_dragged)); gr_stylespec_set(spec, sac ? GR_ATTR(active) : GR_ATTR(inactive)); gr_stylespec_set(spec, tag ? GR_ATTR(tagged) : GR_ATTR(not_tagged)); gr_stylespec_set(spec, act ? GR_ATTR(activity) : GR_ATTR(no_activity)); } }else{ screen_nowindowinfo(scr); } } /*}}}*/ /*{{{ Notification callbacks and deferred updates*/ void screen_update_infowin(WScreen *scr) { mainloop_defer_action((Obj*)scr, (WDeferredAction*)screen_do_update_infowin); } void screen_update_notifywin(WScreen *scr) { mainloop_defer_action((Obj*)scr, (WDeferredAction*)screen_do_update_notifywin); } void screen_managed_notify(WScreen *scr, WRegion *reg, WRegionNotify how) { if(how==ioncore_g.notifies.tag) screen_update_infowin(scr); } void ioncore_screen_activity_notify(WRegion *reg, WRegionNotify how) { WScreen *scr=region_screen_of(reg); if(scr!=NULL){ if(how==ioncore_g.notifies.activity){ screen_update_notifywin(region_screen_of(reg)); }else if(how==ioncore_g.notifies.name){ if(region_is_activity(reg)) screen_update_notifywin(scr); if((WRegion*)scr==REGION_MANAGER(reg)) screen_do_update_infowin(scr); } } } /*}}}*/ notion-3+2012042300/ioncore/screen-notify.h000066400000000000000000000010501174530661200201520ustar00rootroot00000000000000/* * ion/ioncore/screen-notify.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #ifndef ION_IONCORE_SCREEN_NOTIFY_H #define ION_IONCORE_SCREEN_NOTIFY_H #include "common.h" #include "region.h" #include "screen.h" void screen_managed_notify(WScreen *scr, WRegion *reg, WRegionNotify how); void screen_update_infowin(WScreen *scr); void screen_update_notifywin(WScreen *scr); extern void ioncore_screen_activity_notify(WRegion *reg, WRegionNotify how); #endif /* ION_IONCORE_SCREEN_NOTIFY_H */ notion-3+2012042300/ioncore/screen.c000066400000000000000000000253301174530661200166460ustar00rootroot00000000000000/* * ion/ioncore/screen.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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 "extlconv.h" #include "llist.h" #include "group-ws.h" #include "mplex.h" #include "conf.h" #include "activity.h" #include "screen-notify.h" WHook *screen_managed_changed_hook=NULL; /*{{{ Init/deinit */ bool screen_init(WScreen *scr, WRootWin *parent, const WFitParams *fp, int id) { Window win; XSetWindowAttributes attr; ulong attrflags=0; scr->id=id; scr->atom_workspace=None; 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; watch_init(&(scr->notifywin_watch)); watch_init(&(scr->infowin_watch)); attr.background_pixmap=ParentRelative; attrflags=CWBackPixmap; win=XCreateWindow(ioncore_g.dpy, WROOTWIN_ROOT(parent), fp->g.x, fp->g.y, fp->g.w, fp->g.h, 0, DefaultDepth(ioncore_g.dpy, parent->xscr), InputOutput, DefaultVisual(ioncore_g.dpy, parent->xscr), attrflags, &attr); if(win==None) return FALSE; if(!mplex_do_init((WMPlex*)scr, (WWindow*)parent, fp, win, "WScreen")){ XDestroyWindow(ioncore_g.dpy, win); 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; scr->mplex.win.region.flags|=REGION_MAPPED; window_select_input((WWindow*)scr, IONCORE_EVENTMASK_SCREEN); 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 all the needed bindings here; mplex does nothing so that * frames don't have to remove extra bindings. */ region_add_bindmap((WRegion*)scr, ioncore_screen_bindmap); region_add_bindmap((WRegion*)scr, ioncore_mplex_bindmap); region_add_bindmap((WRegion*)scr, ioncore_mplex_toplevel_bindmap); LINK_ITEM(ioncore_g.screens, scr, next_scr, prev_scr); return TRUE; } WScreen *create_screen(WRootWin *parent, const WFitParams *fp, int id) { CREATEOBJ_IMPL(WScreen, screen, (p, parent, fp, id)); } void screen_deinit(WScreen *scr) { UNLINK_ITEM(ioncore_g.screens, scr, next_scr, prev_scr); 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 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); } if(region_is_activity_r((WRegion*)scr)) screen_update_notifywin(scr); screen_update_infowin(scr); mplex_call_changed_hook((WMPlex*)scr, screen_managed_changed_hook, mode, sw, reg_); } static void screen_map(WScreen *scr) { mplex_map((WMPlex*)scr); } static void screen_unmap(WScreen *scr) { mplex_unmap((WMPlex*)scr); } void screen_inactivated(WScreen *scr) { screen_update_infowin(scr); } void screen_activated(WScreen *scr) { screen_update_infowin(scr); } /*}}}*/ /*{{{ Misc. */ /*EXTL_DOC * Find the screen with numerical id \var{id}. */ 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 WRegion *screen_managed_disposeroot(WScreen *scr, WRegion *reg) { bool onmxlist=FALSE, others=FALSE; WLListNode *lnode; WLListIterTmp tmp; if(OBJ_IS(reg, WGroupWS)){ FOR_ALL_NODES_ON_LLIST(lnode, scr->mplex.mx_list, tmp){ if(lnode->st->reg==reg){ onmxlist=TRUE; }else if(OBJ_IS(lnode->st->reg, WGroupWS)){ others=TRUE; break; } } if(onmxlist && !others){ warn(TR("Only workspace may not be destroyed/detached.")); return NULL; } } return reg; } static bool screen_may_dispose(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; } /*}}}*/ /*{{{ 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=MPLEXATTACHPARAMS_INIT; ExtlTab lo=ioncore_get_layout("default"); if(lo==extl_table_none()){ reg=mplex_do_attach_new(&scr->mplex, &par, (WRegionCreateFn*)create_groupws, NULL); }else{ reg=mplex_attach_new_(&scr->mplex, &par, 0, lo); extl_unref_table(lo); } 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_disposeroot, (DynFun*)screen_managed_disposeroot}, {(DynFun*)region_may_dispose, (DynFun*)screen_may_dispose}, {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}, END_DYNFUNTAB }; EXTL_EXPORT IMPLCLASS(WScreen, WMPlex, screen_deinit, screen_dynfuntab); /*}}}*/ notion-3+2012042300/ioncore/screen.h000066400000000000000000000035011174530661200166470ustar00rootroot00000000000000/* * ion/ioncore/screen.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #ifndef ION_IONCORE_SCREEN_H #define ION_IONCORE_SCREEN_H #include #include #include "common.h" #include "mplex.h" #include "rectangle.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; /** Deprecated field, but kept to keep our ABI stable */ bool dep; WRectangle managed_off; WScreen *next_scr, *prev_scr; Watch notifywin_watch; Watch infowin_watch; }; extern bool screen_init(WScreen *scr, WRootWin *parent, const WFitParams *fp, int id); extern WScreen *create_screen(WRootWin *parent, const WFitParams *fp, int id); extern void screen_deinit(WScreen *scr); 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 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 */ notion-3+2012042300/ioncore/selection.c000066400000000000000000000105531174530661200173550ustar00rootroot00000000000000/* * ion/ioncore/selection.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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; #define CLIPATOM(X) XA_PRIMARY static Atom XA_COMPOUND_TEXT(Display *unused) { static Atom a=None; if(a==None) a=XInternAtom(ioncore_g.dpy, "COMPOUND_TEXT", False); return a; } void ioncore_handle_selection_request(XSelectionRequestEvent *ev) { XSelectionEvent sev; XTextProperty prop; const char *p[1]; bool ok=FALSE; sev.property=None; if(selection_data==NULL || ev->property==None) goto refuse; p[0]=selection_data; if(!ioncore_g.use_mb && ev->target==XA_STRING){ Status st=XStringListToTextProperty((char **)p, 1, &prop); ok=(st!=0); }else if(ioncore_g.use_mb){ XICCEncodingStyle style; if(ev->target==XA_STRING){ style=XStringStyle; ok=TRUE; }else if(ev->target==XA_COMPOUND_TEXT(ioncore_g.dpy)){ style=XCompoundTextStyle; ok=TRUE; } if(ok){ int st=XmbTextListToTextProperty(ioncore_g.dpy, (char **)p, 1, style, &prop); ok=(st>=0); } } if(ok){ XSetTextProperty(ioncore_g.dpy, ev->requestor, &prop, ev->property); sev.property=ev->property; XFree(prop.value); } refuse: sev.type=SelectionNotify; sev.requestor=ev->requestor; sev.selection=ev->selection; sev.target=ev->target; sev.time=ev->time; 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); } } void ioncore_handle_selection(XSelectionEvent *ev) { Atom prop=ev->property; Window win=ev->requestor; WWindow *wwin; if(prop!=None){ 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; XSetSelectionOwner(ioncore_g.dpy, CLIPATOM(ioncore_g.dpy), 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) a=XA_COMPOUND_TEXT(ioncore_g.dpy); XConvertSelection(ioncore_g.dpy, CLIPATOM(ioncore_g.dpy), 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; } notion-3+2012042300/ioncore/selection.h000066400000000000000000000010451174530661200173560ustar00rootroot00000000000000/* * ion/ioncore/selection.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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 */ notion-3+2012042300/ioncore/sizehint.c000066400000000000000000000166541174530661200172350ustar00rootroot00000000000000/* * ion/ioncore/sizehint.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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, tw, bw=(hints->base_set ? hints->base_width : 0); int h=*hp, th, bh=(hints->base_set ? hints->base_height : 0); int bs=0; if(min && hints->min_set){ w=maxof(w, hints->min_width); h=maxof(h, hints->min_height); } if(hints->no_constrain && !override_no_constrain){ *wp=w; *hp=h; return; } tw=w-bw; th=h-bh; if(tw>=0 && th>=0) correct_aspect(tw, th, hints, &tw, &th); if(hints->inc_set){ if(tw>0) tw=(tw/hints->width_inc)*hints->width_inc; if(th>0) th=(th/hints->height_inc)*hints->height_inc; } w=tw+bw; h=th+bh; if(hints->max_set){ w=minof(w, hints->max_width); h=minof(h, hints->max_height); } *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; } } hints->min_width=maxof(hints->min_width, 0); hints->min_height=maxof(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){ hints->max_width=maxof(hints->max_width, hints->min_width); hints->max_height=maxof(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; } /*}}}*/ notion-3+2012042300/ioncore/sizehint.h000066400000000000000000000024141174530661200172270ustar00rootroot00000000000000/* * ion/ioncore/sizehint.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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 */ notion-3+2012042300/ioncore/sizepolicy.c000066400000000000000000000267531174530661200175730ustar00rootroot00000000000000/* * ion/ioncore/sizepolicy.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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; /* ignore out-of-bound values for 'x' entirely */ if(!(rq_flags®ION_RQGEOM_WEAK_X) && rq_geom->x > max_geom.w){ rq_flags|=REGION_RQGEOM_WEAK_X; rq_geom->x = reg->geom.x; } /* ignore out-of-bound values for 'y' entirely */ if(!(rq_flags®ION_RQGEOM_WEAK_Y) && rq_geom->y > max_geom.h){ rq_flags|=REGION_RQGEOM_WEAK_Y; rq_geom->y = reg->geom.y; } 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)); } static WSizePolicy org(WSizePolicy base, WSizePolicy g) { if((base&SIZEPOLICY_VERT_MASK)==0) base|=g&SIZEPOLICY_VERT_MASK; if((base&SIZEPOLICY_HORIZ_MASK)==0) base|=g&SIZEPOLICY_HORIZ_MASK; return base; } 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(org(*szplcy, SIZEPOLICY_HORIZ_LEFT|SIZEPOLICY_VERT_CENTER), reg, &tmp, fp, FALSE, TRUE); break; case SIZEPOLICY_STRETCH_RIGHT: gravity_stretch_policy(org(*szplcy, SIZEPOLICY_HORIZ_RIGHT|SIZEPOLICY_VERT_CENTER), reg, &tmp, fp, FALSE, TRUE); break; case SIZEPOLICY_STRETCH_TOP: gravity_stretch_policy(org(*szplcy, SIZEPOLICY_VERT_TOP|SIZEPOLICY_HORIZ_CENTER), reg, &tmp, fp, TRUE, FALSE); break; case SIZEPOLICY_STRETCH_BOTTOM: gravity_stretch_policy(org(*szplcy, SIZEPOLICY_VERT_BOTTOM|SIZEPOLICY_HORIZ_CENTER), reg, &tmp, fp, TRUE, FALSE); break; case SIZEPOLICY_FULL_EXACT: gravity_stretch_policy(org(*szplcy, SIZEPOLICY_VERT_CENTER|SIZEPOLICY_HORIZ_CENTER), reg, &tmp, fp, TRUE, TRUE); break; case SIZEPOLICY_FREE: if(reg!=NULL) region_size_hints_correct(reg, &tmp.w, &tmp.h, FALSE); rectangle_constrain(&tmp, &(fp->g)); fp->g=tmp; break; case SIZEPOLICY_VISIBILITY_CONSTRAINED: if(reg!=NULL) region_size_hints_correct(reg, &tmp.w, &tmp.h, FALSE); { /* Constraint such that at least min(size, CF_VISIBILITY_CONSTRAINT) * much is visible at each border. */ int dx=maxof(0, tmp.w-CF_VISIBILITY_CONSTRAINT); int dy=maxof(0, tmp.h-CF_VISIBILITY_CONSTRAINT); tmp.x=maxof(fp->g.x-dx, minof(tmp.x, fp->g.x+fp->g.w+dx-tmp.w)); tmp.y=maxof(fp->g.y-dy, minof(tmp.y, fp->g.y+fp->g.h+dy-tmp.h)); } 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; } } /* translation table for sizepolicy specifications */ static StringIntMap 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}, {"visibility_constrained", SIZEPOLICY_VISIBILITY_CONSTRAINED}, {"unconstrained", SIZEPOLICY_UNCONSTRAINED}, { NULL, SIZEPOLICY_DEFAULT} /* end marker */ }; bool string2sizepolicy(const char *szplcy, WSizePolicy *value) { int tmp; tmp=stringintmap_value(szplcy_specs, szplcy, -1); if(tmp==-1){ *value=SIZEPOLICY_DEFAULT; return FALSE; }else{ *value=tmp; return TRUE; } } const char *sizepolicy2string(WSizePolicy szplcy) { const char* str=stringintmap_key(szplcy_specs, szplcy, NULL); if(str==NULL){ /* fall back on policy without modifiers if full name not found * * Without this, the scratchpad sometimes became impossible to resize * after reboots. * http://lists.berlios.de/pipermail/ion-general/2009-December/001775.html * http://article.gmane.org/gmane.comp.window-managers.ion.general/8897/match=scratchpad */ str=stringintmap_key(szplcy_specs, szplcy&0xff, NULL); } return str; } notion-3+2012042300/ioncore/sizepolicy.h000066400000000000000000000067561174530661200176010ustar00rootroot00000000000000/* * ion/ioncore/sizepolicy.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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_VISIBILITY_CONSTRAINED 0x11 #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); extern bool string2sizepolicy(const char *szplcy, WSizePolicy *value); extern const char *sizepolicy2string(WSizePolicy szplcy); #endif /* ION_IONCORE_SIZEPOLICY_H */ notion-3+2012042300/ioncore/stacking.c000066400000000000000000000313101174530661200171650ustar00rootroot00000000000000/* * ion/ioncore/stacking.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #include #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; st->pseudomodal=FALSE; } 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) { uint min_level=STACKING_LEVEL_BOTTOM; WStacking *st=NULL; 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) { uint min_level=STACKING_LEVEL_BOTTOM; WStacking *st=NULL, *found=NULL; if(stacking==NULL) return NULL; st=stacking; do{ st=st->prev; if(st->reg==NULL) continue; if(st!=to_try && (st->reg->flags®ION_SKIP_FOCUS || !cf(include_filt, filt_data, st))){ /* skip */ continue; } if(st->levellevel>=STACKING_LEVEL_MODAL1) min_level=maxof(min_level, st->level); }while(st!=stacking); return found; } static bool mapped_filt(WStacking *st, void *unused) { return (st->reg!=NULL && REGION_IS_MAPPED(st->reg)); } static bool mapped_filt_neq(WStacking *st, void *st_neq) { return (st!=(WStacking*)st_neq && mapped_filt(st, NULL)); } 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); } bool stacking_must_focus(WStacking *stacking, WStacking *st) { WStacking *stf=stacking_find_to_focus(stacking, NULL, mapped_filt_neq, NULL, st); return (stf==NULL || (st->level>stf->level && st->level>=STACKING_LEVEL_MODAL1)); } /*}}}*/ notion-3+2012042300/ioncore/stacking.h000066400000000000000000000060421174530661200171760ustar00rootroot00000000000000/* * ion/ioncore/stacking.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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) #define STACKING_IS_PSEUDOMODAL(ST) ((ST)->pseudomodal) 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; uint pseudomodal: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); bool stacking_must_focus(WStacking *stacking, WStacking *st); WStacking *ioncore_find_stacking(WRegion *reg); void stacking_unassoc(WStacking *stacking); bool stacking_assoc(WStacking *stacking, WRegion *reg); #endif /* ION_IONCORE_STACKING_H */ notion-3+2012042300/ioncore/strings.c000066400000000000000000000241721174530661200170630ustar00rootroot00000000000000/* * ion/ioncore/strings.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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; p += 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(""); } /*}}}*/ notion-3+2012042300/ioncore/strings.h000066400000000000000000000014451174530661200170660ustar00rootroot00000000000000/* * ion/ioncore/strings.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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 */ notion-3+2012042300/ioncore/tags.c000066400000000000000000000044331174530661200163260ustar00rootroot00000000000000/* * ion/ioncore/tags.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #include #include #include "global.h" #include "region.h" #include "tags.h" #include "extlconv.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, ioncore_g.notifies.tag); } return nset; } /*EXTL_DOC * Change tagging state of \var{reg} as defined by \var{how} * (one of \codestr{set}, \codestr{unset}, or \codestr{toggle}). * The 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_tagged_clear() { while(ioncore_tagged_first(TRUE)!=NULL) /* nothing */; } /*}}}*/ /*{{{ Iteration */ /*EXTL_DOC * Returns first tagged object, untagging it as well if \var{untag} is set. */ EXTL_SAFE EXTL_EXPORT WRegion *ioncore_tagged_first(bool untag) { WRegion *reg; if(!untag){ reg=(WRegion*)OBJLIST_FIRST(WRegion*, taglist); }else{ reg=(WRegion*)objlist_take_first(&taglist); if(reg!=NULL){ reg->flags&=~REGION_TAGGED; region_notify_change(reg, ioncore_g.notifies.tag); } } return reg; } /*EXTL_DOC * Iterate over tagged regions until \var{iterfn} returns \code{false}. * The function is called in protected mode. * This routine returns \code{true} if it reaches the end of list * without this happening. */ EXTL_SAFE EXTL_EXPORT bool ioncore_tagged_i(ExtlFn iterfn) { return extl_iter_objlist(iterfn, taglist); } /*}}}*/ notion-3+2012042300/ioncore/tags.h000066400000000000000000000007541174530661200163350ustar00rootroot00000000000000/* * ion/ioncore/tags.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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_tagged_clear(); extern WRegion *ioncore_tagged_first(bool untag); extern bool ioncore_tagged_i(ExtlFn iterfn); #endif /* ION_IONCORE_TAGS_H */ notion-3+2012042300/ioncore/window.c000066400000000000000000000116161174530661200167000ustar00rootroot00000000000000/* * ion/ioncore/window.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #include #include #include "names.h" #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, const WFitParams *fp, Window win, const char *name) { if(win==None){ assert(par!=NULL); win=create_xwindow(region_rootwin_of((WRegion*)par), par->win, &(fp->g), name); if(win==None) return FALSE; } wwin->win=win; wwin->xic=NULL; wwin->event_mask=0; wwin->stacking=NULL; region_init(&(wwin->region), par, fp); XSaveContext(ioncore_g.dpy, win, ioncore_g.win_context, (XPointer)wwin); return TRUE; } bool window_init(WWindow *wwin, WWindow *par, const WFitParams *fp, const char *name) { return window_do_init(wwin, par, fp, None, name); } void window_deinit(WWindow *wwin) { region_deinit((WRegion*)wwin); region_pointer_focus_hack(&wwin->region); if(wwin->xic!=NULL) XDestroyIC(wwin->xic); if(wwin->win!=None){ XDeleteContext(ioncore_g.dpy, wwin->win, ioncore_g.win_context); /* Probably should not try destroy if root window... */ 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) { region_pointer_focus_hack(&wwin->region); 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, CurrentTime); } 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); /*}}}*/ notion-3+2012042300/ioncore/window.h000066400000000000000000000034131174530661200167010ustar00rootroot00000000000000/* * ion/ioncore/window.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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, /*@notnull@*/ WWindow *parent, const WFitParams *fp, const char *name); /** * @param parent required when 'win' is 'None'. * @param win the window to initialize. A new window is created when 'win' * is 'None'. * @param name the name of the newly created Window */ extern bool window_do_init(WWindow *p, WWindow *parent, const WFitParams *fp, Window win, const char *name); 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 */ notion-3+2012042300/ioncore/xic.c000066400000000000000000000037311174530661200161530ustar00rootroot00000000000000/* * ion/ioncore/xic.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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); } notion-3+2012042300/ioncore/xic.h000066400000000000000000000005721174530661200161600ustar00rootroot00000000000000/* * ion/ioncore/xic.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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 */ notion-3+2012042300/ioncore/xwindow.c000066400000000000000000000046131174530661200170670ustar00rootroot00000000000000/* * ion/ioncore/xwindow.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #include #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, const char *name) { int w=maxof(1, geom->w); int h=maxof(1, geom->h); const char *p[1]; Window window; window = XCreateSimpleWindow(ioncore_g.dpy, par, geom->x, geom->y, w, h, 0, 0, BlackPixel(ioncore_g.dpy, rw->xscr)); p[0] = name; xwindow_set_text_property(window, XA_WM_NAME, p, 1); return window; } /*}}}*/ /*{{{ 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 rx=0, ry=0; uint mask=0; return XQueryPointer(ioncore_g.dpy, rel, &realroot, &win, &rx, &ry, px, py, &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); } /*}}}*/ notion-3+2012042300/ioncore/xwindow.h000066400000000000000000000017131174530661200170720ustar00rootroot00000000000000/* * ion/ioncore/xwindow.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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, const char *name); 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 */ notion-3+2012042300/libextl/000077500000000000000000000000001174530661200152255ustar00rootroot00000000000000notion-3+2012042300/libextl/LICENSE000066400000000000000000000636501174530661200162440ustar00rootroot00000000000000 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! notion-3+2012042300/libextl/Makefile000066400000000000000000000020521174530661200166640ustar00rootroot00000000000000## ## Extl Makefile ## ifeq ($(MAKELEVEL),0) TOPDIR=. else TOPDIR=.. endif # System-specific configuration include $(TOPDIR)/system.mk # Internal library CFLAGS/INCLUDES include $(TOPDIR)/build/libs.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 .PHONY : libextl-mkexports ###################################### include $(TOPDIR)/build/rules.mk ###################################### libextl.a: $(OBJS) $(AR) $(ARFLAGS) $@ $+ $(RANLIB) $@ libextl-mkexports: libextl-mkexports.in sed "1s:LUA50:$(LUA):" $< > $@ install: $(INSTALLDIR) $(DESTDIR)$(BINDIR) $(INSTALL) -m $(BIN_MODE) libextl-mkexports $(DESTDIR)$(BINDIR) $(INSTALLDIR) $(DESTDIR)$(LIBDIR) $(INSTALL) -m $(DATA_MODE) libextl.a $(DESTDIR)$(LIBDIR) $(INSTALLDIR) $(DESTDIR)$(INCDIR) for h in $(HEADERS); do \ $(INSTALL) -m $(DATA_MODE) $$h $(DESTDIR)$(INCDIR); \ done notion-3+2012042300/libextl/README000066400000000000000000000101121174530661200161000ustar00rootroot00000000000000 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). notion-3+2012042300/libextl/build/000077500000000000000000000000001174530661200163245ustar00rootroot00000000000000notion-3+2012042300/libextl/build/libs.mk000066400000000000000000000004551174530661200176120ustar00rootroot00000000000000ifeq ($(wildcard $(TOPDIR)/libtu/obj.h),) #External libtu, feel free to edit LIBTU_DIR = $(TOPDIR)/../libtu LIBTU_INCLUDES = -I$(TOPDIR)/.. LIBTU_LIBS = -ltu else #In-tree libtu LIBS_SUBDIRS += libtu LIBTU_DIR = $(TOPDIR)/libtu LIBTU_INCLUDES = -I$(TOPDIR) LIBTU_LIBS = -L$(LIBTU_DIR) -ltu endif notion-3+2012042300/libextl/build/system-inc.mk000066400000000000000000000000751174530661200207520ustar00rootroot00000000000000TOPDIR := $(TOPDIR)/.. include $(TOPDIR)/build/system-inc.mk notion-3+2012042300/libextl/exact-version000066400000000000000000000117641174530661200177500ustar00rootroot00000000000000commit ad87a500852c3bd3683b932a72365b7580995725 Author: Arnout Engelen Date: Sat Apr 21 10:53:02 2012 +0200 Revert "Insist that $(LUA) exists when building libextl-mkexports" Breaks with dynamic values for $(LUA) :/ This reverts commit 2da1278d46f2a42b70ada5cc5ae914d094996545. commit 2da1278d46f2a42b70ada5cc5ae914d094996545 Author: Arnout Engelen Date: Sat Apr 21 01:35:10 2012 +0200 Insist that $(LUA) exists when building libextl-mkexports commit 4bcb5dba0fa14e0fcde3f99410ccb144bdd94ee8 Author: Arnout Date: Thu Oct 20 18:16:55 2011 +0200 Support $(DESTDIR), thanks to Josef 'Jeff' Sipek commit f74ce60ccfac0fa57c951115019e46fa3fc714c6 Merge: 6089f0c 865f73d Author: Arnout Date: Thu Oct 20 10:29:22 2011 +0200 Merge branch 'master' of ssh://notion.git.sourceforge.net/gitroot/notion/libextl commit 6089f0c28751227faf2eb44c973d23e4d45782f7 Author: Arnout Date: Thu Oct 20 10:27:09 2011 +0200 Do not hard-code the program name commit 865f73d5542ecf8b4d3039d9208d9c10af5316df Author: Arnout Engelen Date: Fri Sep 30 19:56:23 2011 +0200 Include a build/libs.mk to use when building outside the notion tree. commit 9950687593f2b1846adc9b26b51b1aec09fa2c75 Author: Arnout Engelen Date: Fri Sep 30 19:53:13 2011 +0200 Revert "Oops, libextl doesn't have build/libs.mk." .. but it does when building from inside the notion tree. This reverts commit 4c0ad112333590ab74c0e21fdbcfa0f225ed2b31. commit 8f98e9e61f6cb2c3c085f2874b0e45caa6ad109e Author: Arnout Date: Sat Jun 11 12:42:36 2011 +0200 Improved error messages: calls to unsafe functions are ignored in restricted mode commit 83535a8618d2185904a6c6a89a1738a182a2f6fa Author: Arnout Date: Sat Jun 11 11:47:08 2011 +0200 When a config file could not be found, also log each filename tried. commit 637ad432b911cee20754166e33c97efbc89a9c40 Author: Arnout Date: Sat Jun 11 11:00:59 2011 +0200 Log possible extensions of filenames of files not found on search path commit c6408184b07ef6a3ab21e8049e1102132a23b6e5 Author: Arnout Date: Sat Jun 11 10:54:40 2011 +0200 Lua autodetection: use full path to lua interpreter '#!' requires the full path to the interpreter, disregards $PATH commit 4c0ad112333590ab74c0e21fdbcfa0f225ed2b31 Author: Etan Reisner Date: Thu May 19 23:36:15 2011 -0400 Oops, libextl doesn't have build/libs.mk. commit 15a558f9f648a859c301fff5178935909c09811b Author: Etan Reisner Date: Thu May 19 23:20:40 2011 -0400 Remove system-inc.mk and reference to system-ac.mk Support for generating system-ac.mk was killed off long ago so system-inc.mk has essentially been a static file with two include lines since has. So, in the interest of having fewer "useless" files around pull those two includes into each Makefile. commit 1477f3ae7257cb9679bf229b472c61af8bdac0b6 Author: Etan Reisner Date: Thu May 12 00:21:02 2011 -0400 .gitignore commit f65571fd499c977f78cc1a3af0f9bdc831a55e89 Author: Arnout Engelen Date: Sun May 8 22:08:45 2011 +0200 Remove spurious '/bin' from LUA_DIR commit a7da08e2c3f98662c8582471e313f9cc82c42fc9 Author: Arnout Engelen Date: Sun May 8 22:01:47 2011 +0200 Determine LUA_DIR based on `which lua`, so both prefixes /usr and /usr/local are detected. commit a6191de02c8252d28b8b4dd109f1be8ea083c0d1 Author: Arnout Engelen Date: Sun May 8 21:52:24 2011 +0200 Port lua autodetection improvement from notion to libextl commit 6e205d110b37bc88a75448cbcdd2bafeb1d6d7ad Author: Etan Reisner Date: Fri May 6 01:48:35 2011 -0400 Use $(MAKELEVEL) instead of SUBMODULE. GNU Make increments MAKELEVEL for every recursive invocation of make, so use that to detect the notion subdirectory case from the stand-alone case. commit cd9e70fa857aeee00b76b26527425525ef9964c1 Author: Arnout Engelen Date: Mon Apr 25 18:53:16 2011 +0200 Autodetect which lua to use commit 84f21ee7e50be5f02c913a870b62ee1cab49a760 Author: Tomáš Ebenlendr Date: Tue Apr 12 22:28:36 2011 +0200 Prepare for being git submodule Mimic release behaviour when 'SUBMODULE=1' is passed to make. commit 6aab741532e058daaa3e4d51f639641dec57a39d Author: Arnout Engelen Date: Sat Feb 19 02:50:41 2011 +0100 Rebuild libextrl-mkexports every time, to make it pick up changes in system.mk A 'make clean; make' will rebuild everything already, but not libextrl-mkexports since it doesn't depend on a cleaned artifact. commit 014ecaf69aea3343227f7e1c9d407821045b3e6e Author: M Rawash Date: Tue Apr 6 19:10:28 2010 +0200 init notion-3+2012042300/libextl/extl.h000066400000000000000000000011631174530661200163530ustar00rootroot00000000000000/* * 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 */ notion-3+2012042300/libextl/install-sh000066400000000000000000000127361174530661200172370ustar00rootroot00000000000000#!/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 notion-3+2012042300/libextl/libextl-mkexports.in000066400000000000000000000464461174530661200212700ustar00rootroot00000000000000#!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", ["a"]="any value", } -- }}} -- 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 -- }}} notion-3+2012042300/libextl/luaextl.c000066400000000000000000001564171174530661200170650ustar00rootroot00000000000000/* * libextl/luaextl.c * * Copyright (c) The Notion Team 2011 * 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 #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 the application? */ 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, 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=='b'){ if(valret) *((bool*)valret)=lua_toboolean(st, pos); return TRUE; } switch(lua_type(st, pos)){ case LUA_TNUMBER: if(type!='i' && type!='d' && type!='a') return FALSE; d=lua_tonumber(st, pos); if(type=='i'){ if(d-floor(d)!=0) return FALSE; if(valret) *((int*)valret)=d; }else if(type=='a'){ if(valret){ ((ExtlAny*)valret)->type='d'; ((ExtlAny*)valret)->value.d=d; } }else{ if(valret) *((double*)valret)=d; } return TRUE; case LUA_TNIL: case LUA_TNONE: if(type=='a'){ if(valret) ((ExtlAny*)valret)->type='v'; }else 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; case LUA_TSTRING: if(type!='s' && type!='S' && type!='a') return FALSE; if(valret){ str=lua_tostring(st, pos); if(str!=NULL && copystring){ str=extl_scopy(str); if(str==NULL) return FALSE; } if(type=='a'){ ((ExtlAny*)valret)->type=(copystring ? 's' : 'S'); ((ExtlAny*)valret)->value.s=str; }else{ *((const char**)valret)=str; } } return TRUE; case LUA_TFUNCTION: if(type!='f' && type!='a') return FALSE; if(valret){ lua_pushvalue(st, pos); if(type=='a'){ ((ExtlAny*)valret)->type='f'; ((ExtlAny*)valret)->value.f=lua_ref(st, 1); }else{ *((int*)valret)=lua_ref(st, 1); } } return TRUE; case LUA_TTABLE: if(type!='t' && type!='a') return FALSE; if(valret){ lua_pushvalue(st, pos); if(type=='a'){ ((ExtlAny*)valret)->type='t'; ((ExtlAny*)valret)->value.f=lua_ref(st, 1); }else{ *((int*)valret)=lua_ref(st, 1); } } return TRUE; case LUA_TUSERDATA: if(type=='o'|| type=='a'){ bool invalid=FALSE, dead=FALSE; Obj *obj=extl_get_obj(st, pos, &invalid, &dead); if(wasdeadobject!=NULL) *wasdeadobject=dead; if(valret){ if(type=='a'){ ((ExtlAny*)valret)->type='o'; ((ExtlAny*)valret)->value.o=obj; }else{ *((Obj**)valret)=obj; } } return !invalid; } } return FALSE; } static void extl_to_any(ExtlAny *a, char type, void *ptr) { if(type=='a'){ *a=*(ExtlAny*)ptr; return; } a->type=type; switch(type){ case 'i': a->value.i=*(int*)ptr; break; case 'd': a->value.d=*(double*)ptr; break; case 'b': a->value.b=*(bool*)ptr; break; case 'o': a->value.o=*(Obj**)ptr; break; case 's': case 'S': a->value.s=*(char**)ptr; break; case 't': a->value.t=*(ExtlTab*)ptr; break; case 'f': a->value.f=*(ExtlFn*)ptr; break; } } static void extl_to_any_vararg(ExtlAny *a, char type, va_list *argsp) { if(type=='a'){ *a=va_arg(*argsp, ExtlAny); return; } a->type=type; switch(type){ case 'i': a->value.i=va_arg(*argsp, int); break; case 'd': a->value.d=va_arg(*argsp, double); break; case 'b': a->value.b=va_arg(*argsp, bool); break; case 'o': a->value.o=va_arg(*argsp, Obj*); break; case 's': case 'S': a->value.s=va_arg(*argsp, char*); break; case 't': a->value.t=va_arg(*argsp, ExtlTab); break; case 'f': a->value.f=va_arg(*argsp, ExtlFn); break; } } static void extl_stack_pusha(lua_State *st, ExtlAny *a) { switch(a->type){ case 'i': lua_pushnumber(st, a->value.i); break; case 'd': lua_pushnumber(st, a->value.d); break; case 'b': lua_pushboolean(st, a->value.b); break; case 'o': extl_push_obj(st, a->value.o); break; case 's': case 'S': lua_pushstring(st, a->value.s); break; case 't': lua_rawgeti(st, LUA_REGISTRYINDEX, a->value.t); break; case 'f': lua_rawgeti(st, LUA_REGISTRYINDEX, a->value.f); break; default: lua_pushnil(st); } } static void extl_stack_push(lua_State *st, char spec, void *ptr) { ExtlAny a; extl_to_any(&a, spec, ptr); extl_stack_pusha(st, &a); } static bool extl_stack_push_vararg(lua_State *st, char spec, va_list *argsp) { ExtlAny a; extl_to_any_vararg(&a, spec, argsp); extl_stack_pusha(st, &a); return TRUE; } /*}}}*/ /*{{{ Free */ enum{STRINGS_NONE, STRINGS_NONCONST, STRINGS_ALL}; static void extl_any_free(ExtlAny *a, int strings) { if((a->type=='s' && strings!=STRINGS_NONE) || (a->type=='S' && strings==STRINGS_ALL)){ if(a->value.s!=NULL) free((char*)a->value.s); }else if(a->type=='t'){ extl_unref_table(a->value.t); }else if(a->type=='f'){ extl_unref_fn(a->value.f); } } static void extl_free(void *ptr, char spec, int strings) { ExtlAny a; extl_to_any(&a, spec, ptr); extl_any_free(&a, strings); } /*}}}*/ /*{{{ 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_a(ExtlTab ref, const char *entry, ExtlAny *ret) { return extl_table_do_gets(ref, entry, 'a', (void*)ret); } 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_a(ExtlTab ref, int entry, ExtlAny *ret) { return extl_table_do_geti(ref, entry, 'a', (void*)ret); } 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_a(ExtlTab ref, const char *entry, const ExtlAny *val) { return extl_table_set(ref, 's', 'a', entry, val); } 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_a(ExtlTab ref, int entry, const ExtlAny *val) { return extl_table_set(ref, 'i', 'a', 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); } /*}}}*/ /*{{{ Table iteration */ typedef struct{ ExtlTab ref; ExtlIterFn *fn; void *d; } IterP; int extl_table_iter_do(lua_State *st, IterP *par) { lua_rawgeti(st, LUA_REGISTRYINDEX, par->ref); lua_pushnil(st); while(lua_next(st, -2)!=0){ ExtlAny k, v; if(extl_stack_get(st, -2, 'a', FALSE, NULL, &k)){ bool ret=TRUE; if(extl_stack_get(st, -1, 'a', FALSE, NULL, &v)){ ret=par->fn(k, v, par->d); extl_any_free(&v, STRINGS_NONE); } extl_any_free(&k, STRINGS_NONE); if(!ret) return 0; } lua_pop(st, 1); } return 0; } void extl_table_iter(ExtlTab ref, ExtlIterFn *fn, void *d) { IterP par; par.ref=ref; par.fn=fn; par.d=d; extl_cpcall(l_st, (ExtlCPCallFn*)extl_table_iter_do, &par); } /*}}}*/ /*{{{ 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->registered){ 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("Ignoring call to 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); lua_pushlightuserdata(st, spec); 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; int ind=LUA_GLOBALSINDEX; 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 %s. Do not edit.\n"), libtu_progname()); fprintf(d.f, "return "); ret=extl_cpcall(l_st, (ExtlCPCallFn*)extl_do_serialise, &d); fprintf(d.f, "\n\n"); fclose(d.f); return ret; } /*}}}*/ notion-3+2012042300/libextl/luaextl.h000066400000000000000000000143031174530661200170550ustar00rootroot00000000000000/* * 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 * a: ExtlAny */ 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 struct{ char type; ExtlL2Param value; } ExtlAny; 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; bool registered; } 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_a(ExtlTab ref, const char *entry, ExtlAny *ret); 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_a(ExtlTab ref, int entry, ExtlAny *ret); 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_a(ExtlTab ref, const char *entry, const ExtlAny *ret); 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_a(ExtlTab ref, int entry, const ExtlAny *ret); 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); /* Table/iterate */ typedef bool ExtlIterFn(ExtlAny k, ExtlAny v, void *d); extern void extl_table_iter(ExtlTab ref, ExtlIterFn *fn, void *d); /* 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 */ notion-3+2012042300/libextl/misc.c000066400000000000000000000011561174530661200163270ustar00rootroot00000000000000/* * 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; } notion-3+2012042300/libextl/private.h000066400000000000000000000053601174530661200170540ustar00rootroot00000000000000/* * 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, NULL); \ (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 */ notion-3+2012042300/libextl/readconfig.c000066400000000000000000000251541174530661200175010ustar00rootroot00000000000000/* * 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; } static int warn_notfound(const char *file, void *param) { warn(TR("Tried: '%s'"), file); return EXTL_TRYCONFIG_NOTFOUND; } 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.%s' or '%s.%s' on search path..."), file, EXTL_COMPILED_EXTENSION, file, EXTL_EXTENSION); extl_try_config(file, sp, (ExtlTryConfigFn*)warn_notfound, NULL, EXTL_COMPILED_EXTENSION, EXTL_EXTENSION); } 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; } /*}}}*/ notion-3+2012042300/libextl/readconfig.h000066400000000000000000000031321174530661200174760ustar00rootroot00000000000000/* * 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 */ notion-3+2012042300/libextl/types.h000066400000000000000000000010771174530661200165470ustar00rootroot00000000000000/* * 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 */ notion-3+2012042300/libmainloop/000077500000000000000000000000001174530661200160675ustar00rootroot00000000000000notion-3+2012042300/libmainloop/Makefile000066400000000000000000000010701174530661200175250ustar00rootroot00000000000000## ## Libmainloop Makefile ## # System-specific configuration is in system.mk TOPDIR=.. include $(TOPDIR)/build/system-inc.mk ###################################### INCLUDES += $(LIBTU_INCLUDES) $(LIBEXTL_INCLUDES) -I.. CFLAGS += $(POSIX_SOURCE) $(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: notion-3+2012042300/libmainloop/defer.c000066400000000000000000000100331174530661200173150ustar00rootroot00000000000000/* * ion/libmainloop/defer.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ /* 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 #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); D(warn(TR("Object destroyed while deferred actions are still pending."))); } static bool already_deferred(Obj *obj, WDeferredAction *action, WDeferred *list) { WDeferred *d; for(d=list; d!=NULL; d=d->next){ if(d->action==action && d->watch.obj==obj) return TRUE; } return FALSE; } bool mainloop_defer_action_on_list(Obj *obj, WDeferredAction *action, WDeferred **list) { WDeferred *d; if(already_deferred(obj, action, *list)) return TRUE; d=alloc_defer(); if(d==NULL){ warn_err(); return FALSE; } d->action=action; d->list=list; d->fn=extl_fn_none(); watch_init(&(d->watch)); 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){ /* The deferral should not be on the list, if there * was an object, and it got destroyed. */ /*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); } notion-3+2012042300/libmainloop/defer.h000066400000000000000000000015171174530661200173310ustar00rootroot00000000000000/* * ion/libmainloop/defer.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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 */ notion-3+2012042300/libmainloop/exec.c000066400000000000000000000126611174530661200171650ustar00rootroot00000000000000/* * ion/mainloop/exec.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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); } /*}}}*/ notion-3+2012042300/libmainloop/exec.h000066400000000000000000000020471174530661200171670ustar00rootroot00000000000000/* * ion/libmainloop/exec.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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 */ notion-3+2012042300/libmainloop/hooks.c000066400000000000000000000157051174530661200173660ustar00rootroot00000000000000/* * ion/mainloop/hooks.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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(name==NULL) return NULL; 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); } /*}}}*/ notion-3+2012042300/libmainloop/hooks.h000066400000000000000000000035051174530661200173660ustar00rootroot00000000000000/* * ion/mainloop/hooks.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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 */ notion-3+2012042300/libmainloop/rx.mk000066400000000000000000000005131174530661200170500ustar00rootroot00000000000000## ## 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_EXTRA_DEPS += $(MAINLOOP_SOURCES) MKEXPORTS_EXTRAS += -reexport mainloop $(MAINLOOP_SOURCES) notion-3+2012042300/libmainloop/select.c000066400000000000000000000057621174530661200175240ustar00rootroot00000000000000/* * libmainloop/select.c * * Partly based on a contributed code. * * See the included file LICENSE for details. */ #include #include #include #include #include #include #include #include "select.h" #include "signal.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; int ret=0; #if _POSIX_SELECT || _POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600 { sigset_t oldmask; FD_ZERO(&rfds); set_input_fds(&rfds, &nfds); mainloop_block_signals(&oldmask); if(!mainloop_unhandled_signals()) ret=pselect(nfds+1, &rfds, NULL, NULL, NULL, &oldmask); sigprocmask(SIG_SETMASK, &oldmask, NULL); } #else #warning "pselect() unavailable -- using dirty hacks" { struct timeval tv={0, 0}; bool to; /* If there are timers, make sure we return from select with * some delay, if the timer signal happens right before * entering select(). Race conditions with other signals * we'll just have to ignore without pselect(). */ do{ bool to; FD_ZERO(&rfds); set_input_fds(&rfds, &nfds); to=libmainloop_get_timeout(&tv); if(mainloop_unhandled_signals()){ ret=0; break; } ret=select(nfds+1, &rfds, NULL, NULL, to ? &tv : NULL); }while(ret<0 && errno==EINTR && !mainloop_unhandled_signals()); } #endif if(ret>0) check_input_fds(&rfds); } /*}}}*/ notion-3+2012042300/libmainloop/select.h000066400000000000000000000013151174530661200175170ustar00rootroot00000000000000/* * ion/mainloop/select.h * * Based on a contributed readfds code. * * See the included file LICENSE for details. */ #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 */ notion-3+2012042300/libmainloop/signal.c000066400000000000000000000304221174530661200175110ustar00rootroot00000000000000/* * ion/libmainloop/signal.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #include #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; static sigset_t special_sigs; /*{{{ Timers */ static WTimer *queue=NULL; int mainloop_gettime(struct timeval *val) { #if defined(_POSIX_MONOTONIC_CLOCK) && (_POSIX_MONOTONIC_CLOCK>=0) struct timespec spec; int ret; static int checked=0; if(checked>=0){ ret=clock_gettime(CLOCK_MONOTONIC, &spec); if(ret==-1 && errno==EINVAL && checked==0){ checked=-1; }else{ checked=1; val->tv_sec=spec.tv_sec; val->tv_usec=spec.tv_nsec/1000; return ret; } } #else #warning "Monotonic clock unavailable; please fix your operating system." #endif return gettimeofday(val, 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 bool libmainloop_get_timeout(struct timeval *tv) { if(queue==NULL) return FALSE; /* Subtract queue time from current time, don't go below zero */ mainloop_gettime(tv); if(TIMEVAL_LATER((queue)->when, (*tv))){ if(queue->when.tv_usectv_usec){ tv->tv_usec=(queue->when.tv_usec+USECS_IN_SEC)-tv->tv_usec; /* TIMEVAL_LATER ensures >= 0 */ tv->tv_sec=(queue->when.tv_sec-1)-tv->tv_sec; }else{ tv->tv_usec=queue->when.tv_usec-tv->tv_usec; tv->tv_sec=queue->when.tv_sec-tv->tv_sec; } /* 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. */ tv->tv_sec+=tv->tv_usec/USECS_IN_SEC; tv->tv_usec%=USECS_IN_SEC; }else{ had_tmr=TRUE; return FALSE; } return TRUE; } static void do_timer_set() { struct itimerval val={{0, 0}, {0, 0}}; if(libmainloop_get_timeout(&val.it_value)){ val.it_interval.tv_usec=0; val.it_interval.tv_sec=0; if((setitimer(ITIMER_REAL, &val, NULL))) had_tmr=TRUE; }else if(!had_tmr){ setitimer(ITIMER_REAL, &val, NULL); } } 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){ had_tmr=FALSE; if(queue==NULL) break; mainloop_gettime(¤t_time); 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; } void mainloop_block_signals(sigset_t *oldmask) { sigprocmask(SIG_BLOCK, &special_sigs, oldmask); } bool mainloop_unhandled_signals() { return (usr2_sig || wait_sig || kill_sig || had_tmr); } static void add_to_current_time(struct timeval *when, uint msecs) { long tmp_usec; mainloop_gettime(when); 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(&special_sigs); 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); sigaddset(&special_sigs, SIGALRM); } IFTRAP(SIGCHLD){ sa.sa_handler=chld_handler; sa.sa_flags=SA_NOCLDSTOP|SA_RESTART; sigaction(SIGCHLD, &sa, NULL); sigaddset(&special_sigs, SIGCHLD); } IFTRAP(SIGUSR2){ sa.sa_handler=usr2_handler; sa.sa_flags=SA_RESTART; sigaction(SIGUSR2, &sa, NULL); sigaddset(&special_sigs, SIGUSR2); } IFTRAP(SIGTERM){ sa.sa_handler=exit_handler; sa.sa_flags=SA_RESTART; sigaction(SIGTERM, &sa, NULL); sigaddset(&special_sigs, SIGTERM); } 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 /*}}}*/ notion-3+2012042300/libmainloop/signal.h000066400000000000000000000027171174530661200175240ustar00rootroot00000000000000/* * ion/mainloop/signal.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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 void mainloop_block_signals(sigset_t *oldmask); extern bool mainloop_unhandled_signals(); extern bool libmainloop_get_timeout(struct timeval *tv); extern WHook *mainloop_sigchld_hook; extern WHook *mainloop_sigusr2_hook; /* Returns monotonic time if possible */ extern int mainloop_gettime(struct timeval *val); #endif /* ION_LIBMAINLOOP_SIGNAL_H */ notion-3+2012042300/libtu/000077500000000000000000000000001174530661200147015ustar00rootroot00000000000000notion-3+2012042300/libtu/Makefile000066400000000000000000000022451174530661200163440ustar00rootroot00000000000000## ## Tu Makefile ## # System-specific configuration is in system.mk ifeq ($(MAKELEVEL),0) TOPDIR=. else TOPDIR=.. endif # System-specific configuration include $(TOPDIR)/system.mk ###################################### #INCLUDES += $(LIBTU_INCLUDES) $(LUA_INCLUDES) CFLAGS += $(C98_SOURCE) $(POSIX_SOURCE) $(WARN) SOURCES=iterable.c map.c misc.c obj.c objlist.c optparser.c output.c parser.c prefix.c ptrlist.c rb.c setparam.c stringstore.c tokenizer.c util.c errorlog.c HEADERS=debug.h errorlog.h locale.h minmax.h obj.h objp.h output.h pointer.h private.h rb.h stringstore.h types.h dlist.h iterable.h map.h misc.h objlist.h optparser.h parser.h prefix.h ptrlist.h setparam.h tokenizer.h util.h TARGETS=libtu.a ###################################### include $(TOPDIR)/build/rules.mk ###################################### libtu.a: $(OBJS) $(AR) $(ARFLAGS) $@ $+ $(RANLIB) $@ install: $(INSTALLDIR) $(DESTDIR)$(LIBDIR) $(INSTALL) -m $(DATA_MODE) libtu.a $(DESTDIR)$(LIBDIR) $(INSTALLDIR) $(DESTDIR)$(INCDIR)/libtu for h in $(HEADERS); do \ $(INSTALL) -m $(DATA_MODE) $$h $(DESTDIR)$(INCDIR)/libtu; \ done notion-3+2012042300/libtu/build/000077500000000000000000000000001174530661200160005ustar00rootroot00000000000000notion-3+2012042300/libtu/build/system-inc.mk000066400000000000000000000000751174530661200204260ustar00rootroot00000000000000TOPDIR := $(TOPDIR)/.. include $(TOPDIR)/build/system-inc.mk notion-3+2012042300/libtu/debug.h000066400000000000000000000005311174530661200161370ustar00rootroot00000000000000/* * 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 */ notion-3+2012042300/libtu/dlist.h000066400000000000000000000102211174530661200161650ustar00rootroot00000000000000/* * 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 */ notion-3+2012042300/libtu/errorlog.c000066400000000000000000000052431174530661200167040ustar00rootroot00000000000000/* * 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", libtu_progname(), 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; } notion-3+2012042300/libtu/errorlog.h000066400000000000000000000015661174530661200167150ustar00rootroot00000000000000/* * 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 */ notion-3+2012042300/libtu/exact-version000066400000000000000000000065221174530661200174200ustar00rootroot00000000000000commit 1982dcfdc17a946dff0aa6086118b6e266c79b7e Author: Arnout Date: Thu Oct 20 18:18:11 2011 +0200 Support $(DESTDIR), thanks to Josef 'Jeff' Sipek commit 3cd7df0d2a7ae110abe35e095dd31bcc172316f5 Merge: bd240fd 9a09482 Author: Arnout Date: Sun Aug 7 14:53:53 2011 +0200 Merge branch 'master' of ssh://notion.git.sourceforge.net/gitroot/notion/libtu commit bd240fd93db418cb468cfd647fc4e78d81007b93 Author: Arnout Date: Sun Aug 7 14:53:02 2011 +0200 Document (lack of) character encoding rules in the API commit 9a0948249463732d7c00276c212b5f503db3a140 Author: Etan Reisner Date: Thu May 19 23:34:47 2011 -0400 Oops, libtu doesn't have build/libs.mk. commit 86fbf7c7d727b37280f2c8fe0a6ab3c64433b84e Author: Etan Reisner Date: Thu May 19 23:19:32 2011 -0400 Remove system-inc.mk and reference to system-ac.mk Support for generating system-ac.mk was killed off long ago so system-inc.mk has essentially been a static file with two include lines since has. So, in the interest of having fewer "useless" files around pull those two includes into each Makefile. commit 10bbff51c3784f107447cc1358857c71864a1d10 Author: Etan Reisner Date: Thu May 12 00:18:26 2011 -0400 .gitignore commit acb9925b84f00758fb225f74db93bf06f88b9e4e Author: Etan Reisner Date: Tue May 10 09:54:18 2011 -0400 We don't need to include system.mk twice. $(TOPDIR)/build/system-inc.mk includes $(TOPDIR)/system.mk for us, we don't need to include it ourselves a second time. commit 9a44af6b25adb6da7078e646cfb828a70eabf895 Author: Etan Reisner Date: Fri May 6 01:46:38 2011 -0400 Use $(MAKELEVEL) instead of SUBMODULE. GNU Make increments MAKELEVEL for every recursive invocation of make, so use that to detect the notion subdirectory case from the stand-alone case. commit 643550b941e6071b016a77ae8300e73b0fcd1a6f Author: Tomáš Ebenlendr Date: Tue Apr 12 22:17:36 2011 +0200 Prepare for being git submodule. Mimic release behaviour when 'SUBMODULE=1' is passed to make. commit c27630d44c29bcc4c590695461f1200b0a097a1d Author: Arnout Date: Sat Feb 19 13:38:49 2011 +0100 Build layout more consistent with libextl and notion commit 7dcbb5dad0f27ee61d6e9602fcc5d854a4b75af0 Author: Tomáš Ebenlendr Date: Sat Feb 5 23:35:48 2011 +0100 Add StringFunPtrMap. Adds map between function pointers and strings. This should allow named alternative implementation of a function. commit f25a03099b7eadfd7393901a5394b98f9dc665eb Author: Arnout Date: Sat Jan 22 03:12:34 2011 +0100 Check HAS_SYSTEM_ASPRINTF value instead of by definedness commit 10729e6468b1285b12b80d0a42aad442f7a813a5 Author: Arnout Date: Fri Sep 3 21:07:03 2010 +0200 add missing files from previous ion release commit 2f07f5e15e1c8572cd61863e0e9dadf00fe19b25 Author: Arnout Date: Fri Sep 3 21:06:09 2010 +0200 Add build system (Makefile and rules.mk) commit ffe387ea0170b4f4b696f1a3fefa0157107beb8c Author: M Rawash Date: Tue Apr 6 19:11:45 2010 +0200 init notion-3+2012042300/libtu/install-sh000066400000000000000000000127361174530661200167130ustar00rootroot00000000000000#!/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 notion-3+2012042300/libtu/iterable.c000066400000000000000000000015761174530661200166450ustar00rootroot00000000000000/* * 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; } } notion-3+2012042300/libtu/iterable.h000066400000000000000000000014701174530661200166430ustar00rootroot00000000000000/* * 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 */ notion-3+2012042300/libtu/locale.h000066400000000000000000000006651174530661200163200ustar00rootroot00000000000000/* * 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_GETTEXT #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 */ notion-3+2012042300/libtu/map.c000066400000000000000000000031051174530661200156210ustar00rootroot00000000000000/* * 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; } int stringfunptrmap_ndx(const StringFunPtrMap *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; } FunPtr stringfunptrmap_value(const StringFunPtrMap *map, const char *str, FunPtr dflt) { int i=stringfunptrmap_ndx(map, str); return (i==-1 ? dflt : map[i].value); } const char *stringfunptrmap_key(const StringFunPtrMap *map, FunPtr 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; } notion-3+2012042300/libtu/map.h000066400000000000000000000032641174530661200156340ustar00rootroot00000000000000/* * 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); typedef void (*FunPtr)(void); #define DECLFUNPTRMAP(NAME) \ typedef struct _String##NAME##Map{\ const char *string;\ NAME value;\ } String##NAME##Map DECLFUNPTRMAP(FunPtr); #define END_STRINGPTRMAP {NULL, NULL} /* Return the index of str in map or -1 if not found. */ extern int stringfunptrmap_ndx(const StringFunPtrMap *map, const char *str); extern FunPtr stringfunptrmap_value(const StringFunPtrMap *map, const char *str, FunPtr dflt); extern const char *stringfunptrmap_key(const StringFunPtrMap *map, FunPtr value, const char *dflt); #define STRINGFUNPTRMAP_NDX(MAP,STR) stringfunptrmap_ndx((StringFunPtrMap *)MAP, STR) #define STRINGFUNPTRMAP_VALUE(TYPE,MAP,STR,DFLT) (TYPE)stringfunptrmap_value((StringFunPtrMap *)MAP, STR, (FunPtr)DFLT) #define STRINGFUNPTRMAP_KEY(MAP,VALUE,DFLT) stringfunptrmap_key((StringFunPtrMap *)MAP, (FunPtr)VALUE, DFLT) #endif /* LIBTU_MAP_H */ notion-3+2012042300/libtu/minmax.h000066400000000000000000000006501174530661200163440ustar00rootroot00000000000000/* * 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 */ notion-3+2012042300/libtu/misc.c000066400000000000000000000061721174530661200160060ustar00rootroot00000000000000/* * 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; } notion-3+2012042300/libtu/misc.h000066400000000000000000000024531174530661200160110ustar00rootroot00000000000000/* * 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 */ notion-3+2012042300/libtu/np/000077500000000000000000000000001174530661200153165ustar00rootroot00000000000000notion-3+2012042300/libtu/np/np-conv.h000066400000000000000000000137271174530661200170610ustar00rootroot00000000000000/* * 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 notion-3+2012042300/libtu/np/numparser2.h000066400000000000000000000122671174530661200175750ustar00rootroot00000000000000/* * 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; } notion-3+2012042300/libtu/obj.c000066400000000000000000000123331174530661200156210ustar00rootroot00000000000000/* * 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; } /*}}}*/ notion-3+2012042300/libtu/obj.h000066400000000000000000000032571174530661200156330ustar00rootroot00000000000000/* * 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 */ notion-3+2012042300/libtu/objlist.c000066400000000000000000000130221174530661200165110ustar00rootroot00000000000000/* * 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_contains(ObjList *objlist, Obj *obj) { return (objlist_find_node(objlist, obj)!=NULL); } 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 objlist_insert_last(objlist, obj); 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 objlist_insert_first(objlist, obj); 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; } notion-3+2012042300/libtu/objlist.h000066400000000000000000000037361174530661200165310ustar00rootroot00000000000000/* * 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 bool objlist_contains(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 */ notion-3+2012042300/libtu/objp.h000066400000000000000000000046071174530661200160130ustar00rootroot00000000000000/* * 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 */ notion-3+2012042300/libtu/optparser.c000066400000000000000000000225451174530661200170740ustar00rootroot00000000000000/* * 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); } notion-3+2012042300/libtu/optparser.h000066400000000000000000000041121174530661200170670ustar00rootroot00000000000000/* * 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 */ notion-3+2012042300/libtu/output.c000066400000000000000000000161231174530661200164100ustar00rootroot00000000000000/* * 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 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 !HAS_SYSTEM_ASPRINTF #include "snprintf_2.2/snprintf.c" #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=libtu_progname(); 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; } notion-3+2012042300/libtu/output.h000066400000000000000000000047411174530661200164200ustar00rootroot00000000000000/* * 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 */ notion-3+2012042300/libtu/parser.c000066400000000000000000000364621174530661200163540ustar00rootroot00000000000000/* * 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); } notion-3+2012042300/libtu/parser.h000066400000000000000000000031231174530661200163450ustar00rootroot00000000000000/* * 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 */ notion-3+2012042300/libtu/pointer.h000066400000000000000000000006471174530661200165410ustar00rootroot00000000000000/* * 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 */ notion-3+2012042300/libtu/prefix.c000066400000000000000000000021561174530661200163460ustar00rootroot00000000000000/* * libtu/prefix.c * * Copyright (c) Tuomo Valkonen 1999-2007. * * 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 "locale.h" #include "output.h" static char *the_prefix=NULL; void prefix_set(const char *binloc, const char *dflt) { int i=strlen(binloc); int j=strlen(dflt); if(binloc[0]!='/') die(TR("This relocatable binary should be started with an absolute path.")); while(i>0 && j>0){ if(binloc[i-1]!=dflt[j-1]) break; i--; j--; } the_prefix=scopyn(binloc, i); } char *prefix_add(const char *s) { if(the_prefix==NULL) return scopy(s); else return scat3(the_prefix, "/", s); } bool prefix_wrap_simple(bool (*fn)(const char *s), const char *s) { bool ret=FALSE; if(the_prefix==NULL){ ret=fn(s); }else{ char *s2=prefix_add(s); if(s2!=NULL){ ret=fn(s2); free(s2); } } return ret; } notion-3+2012042300/libtu/prefix.h000066400000000000000000000007331174530661200163520ustar00rootroot00000000000000/* * libtu/prefix.h * * Copyright (c) Tuomo Valkonen 1999-2007. * * 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_PREFIX_H #define _LIBTU_PREFIX_H extern void prefix_set(const char *binloc, const char *dflt); extern char *prefix_add(const char *s); extern bool prefix_wrap_simple(bool (*fn)(const char *s), const char *s); #endif /* _LIBTU_PREFIX_H */ notion-3+2012042300/libtu/private.h000066400000000000000000000006611174530661200165270ustar00rootroot00000000000000/* * 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_GETTEXT #include #define TR(X) dgettext("libtu", X) #else #define TR(X) (X) #endif #define DUMMY_TR(X) X #endif /* LIBTU_PRIVATE_H */ notion-3+2012042300/libtu/ptrlist.c000066400000000000000000000066611174530661200165570ustar00rootroot00000000000000/* * 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_contains(PtrList *ptrlist, void *ptr) { return (ptrlist_find_node(ptrlist, ptr)!=NULL); } 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 ptrlist_insert_last(ptrlist, ptr); 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 ptrlist_insert_first(ptrlist, ptr); 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; } notion-3+2012042300/libtu/ptrlist.h000066400000000000000000000035331174530661200165570ustar00rootroot00000000000000/* * 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 bool ptrlist_contains(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 */ notion-3+2012042300/libtu/rb-test.c000066400000000000000000000051411174530661200164260ustar00rootroot00000000000000/* 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); } notion-3+2012042300/libtu/rb.c000066400000000000000000000352051174530661200154550ustar00rootroot00000000000000/* 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); } notion-3+2012042300/libtu/rb.h000066400000000000000000000104721174530661200154610ustar00rootroot00000000000000/* 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 */ notion-3+2012042300/libtu/setparam.c000066400000000000000000000023101174530661200166550ustar00rootroot00000000000000/* * 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; } } notion-3+2012042300/libtu/setparam.h000066400000000000000000000011501174530661200166630ustar00rootroot00000000000000/* * 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 */ notion-3+2012042300/libtu/snprintf_2.2/000077500000000000000000000000001174530661200171255ustar00rootroot00000000000000notion-3+2012042300/libtu/snprintf_2.2/INSTALL000066400000000000000000000012661174530661200201630ustar00rootroot00000000000000HOW 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. notion-3+2012042300/libtu/snprintf_2.2/LICENSE.txt000066400000000000000000000123621174530661200207540ustar00rootroot00000000000000The 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 notion-3+2012042300/libtu/snprintf_2.2/Makefile.unused000066400000000000000000000023321174530661200220670ustar00rootroot00000000000000# 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 notion-3+2012042300/libtu/snprintf_2.2/README000066400000000000000000000336071174530661200200160ustar00rootroot00000000000000 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! notion-3+2012042300/libtu/snprintf_2.2/README.html000066400000000000000000000377551174530661200207710ustar00rootroot00000000000000 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! notion-3+2012042300/libtu/snprintf_2.2/snprintf-orig.c000066400000000000000000001216671174530661200221070ustar00rootroot00000000000000/* * 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 notion-3+2012042300/libtu/snprintf_2.2/snprintf.c000066400000000000000000001221071174530661200211370ustar00rootroot00000000000000#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 notion-3+2012042300/libtu/snprintf_2.2/snprintf.h000066400000000000000000000016521174530661200211450ustar00rootroot00000000000000#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 notion-3+2012042300/libtu/snprintf_2.2/test.c000066400000000000000000000636001174530661200202550ustar00rootroot00000000000000/* * 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); } notion-3+2012042300/libtu/stringstore.c000066400000000000000000000046431174530661200174370ustar00rootroot00000000000000/* * libtu/stringstore.c * * Copyright (c) Tuomo Valkonen 2004-2007. * * 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 "misc.h" #include "output.h" #include "rb.h" #include "stringstore.h" static Rb_node stringstore=NULL; const char *stringstore_get(StringId id) { return (id==STRINGID_NONE ? NULL : (const char*)(((Rb_node)id)->k.key)); } typedef struct{ const char *key; uint len; } D; static int cmp(const void *d_, const char *nodekey) { D *d=(D*)d_; int res=strncmp(d->key, nodekey, d->len); return (res!=0 ? res : (nodekey[d->len]=='\0' ? 0 : -1)); } StringId stringstore_find_n(const char *str, uint l) { Rb_node node; int found=0; D d; if(stringstore==NULL) return STRINGID_NONE; d.key=str; d.len=l; node=rb_find_gkey_n(stringstore, &d, (Rb_compfn*)cmp, &found); if(!found) return STRINGID_NONE; return (StringId)node; } StringId stringstore_find(const char *str) { return stringstore_find_n(str, strlen(str)); } StringId stringstore_alloc_n(const char *str, uint l) { Rb_node node=(Rb_node)stringstore_find_n(str, l); char *s; if(node!=NULL){ node->v.ival++; return node; } if(stringstore==NULL){ stringstore=make_rb(); if(stringstore==NULL) return STRINGID_NONE; } s=scopyn(str, l); 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; } StringId stringstore_alloc(const char *str) { if(str==NULL) return STRINGID_NONE; return stringstore_alloc_n(str, strlen(str)); } void stringstore_free(StringId id) { Rb_node node=(Rb_node)id; if(node==NULL) 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); } } void stringstore_ref(StringId id) { Rb_node node=(Rb_node)id; if(node!=NULL) node->v.ival++; } notion-3+2012042300/libtu/stringstore.h000066400000000000000000000013431174530661200174360ustar00rootroot00000000000000/* * libtu/stringstore.h * * Copyright (c) Tuomo Valkonen 2004-2007. * * 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 StringId stringstore_find_n(const char *str, uint l); extern StringId stringstore_alloc_n(const char *str, uint l); extern void stringstore_free(StringId id); extern void stringstore_ref(StringId id); #endif /* LIBTU_STRINGSTORE_H */ notion-3+2012042300/libtu/tester.c000066400000000000000000000024511174530661200163550ustar00rootroot00000000000000/* * 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; } notion-3+2012042300/libtu/tester2.c000066400000000000000000000026311174530661200164370ustar00rootroot00000000000000/* * 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; } notion-3+2012042300/libtu/tester3.c000066400000000000000000000033541174530661200164430ustar00rootroot00000000000000/* * 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; } notion-3+2012042300/libtu/tokenizer.c000066400000000000000000000452241174530661200170660ustar00rootroot00000000000000/* * 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)); } notion-3+2012042300/libtu/tokenizer.h000066400000000000000000000141111174530661200170620ustar00rootroot00000000000000/* * 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 */ notion-3+2012042300/libtu/types.h000066400000000000000000000025451174530661200162240ustar00rootroot00000000000000/* * 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 */ notion-3+2012042300/libtu/util.c000066400000000000000000000013001174530661200160140ustar00rootroot00000000000000/* * 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_GETTEXT textdomain(simple_basename(argv0)); #endif } const char *libtu_progname() { return progname; } const char *libtu_progbasename() { const char *s=strrchr(progname, '/'); return (s==NULL ? progname : s+1); } notion-3+2012042300/libtu/util.h000066400000000000000000000021031174530661200160230ustar00rootroot00000000000000/* * 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" /** * @parame argv0 The program name used to invoke the current program, with * path (if specified). Unfortunately it is generally not easy to determine * the encoding of this string, so we don't require a specific one here. * * @see http://stackoverflow.com/questions/5408730/what-is-the-encoding-of-argv */ extern void libtu_init(const char *argv0); /** * The program name used to invoke the current program, with path (if * supplied). Unfortunately the encoding is undefined. */ extern const char *libtu_progname(); /** * The program name used to invoke the current program, without path. * Unfortunately the encoding is undefined. */ extern const char *libtu_progbasename(); #endif /* LIBTU_UTIL_H */ notion-3+2012042300/man/000077500000000000000000000000001174530661200143355ustar00rootroot00000000000000notion-3+2012042300/man/Makefile000066400000000000000000000040201174530661200157710ustar00rootroot00000000000000## ## Notion manual page Makefile ## # System-specific configuration is in system.mk TOPDIR=.. include $(TOPDIR)/build/system-inc.mk ###################################### TRANSLATIONS=fi cs WELCOME_TARGETS=\ welcome.txt \ $(foreach tr, $(TRANSLATIONS), welcome.$(tr).txt) TARGETS=notion.1 $(foreach tr, $(TRANSLATIONS), notion.$(tr).1) \ pwm3.1 $(foreach tr, $(TRANSLATIONS), pwm3.$(tr).1) \ $(WELCOME_TARGETS) MKMAN=$(LUA) ../build/mkman.lua $(MKMAN_DEFS) MKMAN_DEFS=-D ETCDIR $(REL)$(ETCDIR) -D DOCDIR $(REL)$(DOCDIR) ifeq ($(RELOCATABLE),1) REL="/..." endif 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_notioncore.lua \ ../etc/cfg_tiling.lua \ ../etc/cfg_query.lua \ ../etc/cfg_menu.lua # TODO: PWM configuration file is undocumented PWM_CONFIGS=\ ../etc/cfg_notioncore.lua \ ../etc/cfg_menu.lua \ ../pwm/cfg_pwm.lua ###################################### include $(TOPDIR)/build/rules.mk ###################################### notion.1: notion.in $(CONFIGS) $(MKMAN) -i $< -o $@ $(CONFIGS) pwm3.1: pwm3.in $(PWM_CONFIGS) $(MKMAN) -i $< -o $@ $(PWM_CONFIGS) notion.%.1: notion.%.in $(CONFIGS) ../po/%.po $(MKMAN) -po ../po/$*.po -i $< -o $@ $(CONFIGS) pwm3.%.1: pwm3.%.in $(PWM_CONFIGS) ../po/%.po $(MKMAN) -po ../po/$*.po -i $< -o $@ $(PWM_CONFIGS) welcome%txt: welcome%head notion%1 (cat welcome$*head; \ $(NROFF) notion$*1 | $(FILTERCRAP)) > $@ _install: $(INSTALLDIR) $(DESTDIR)$(MANDIR)/man1 $(INSTALL) -m $(DATA_MODE) notion.1 $(DESTDIR)$(MANDIR)/man1 $(INSTALL) -m $(DATA_MODE) pwm3.1 $(DESTDIR)$(MANDIR)/man1 for tr in $(TRANSLATIONS); do \ $(INSTALLDIR) $(DESTDIR)$(MANDIR)/$$tr/man1 ; \ $(INSTALL) -m $(DATA_MODE) notion.$$tr.1 $(DESTDIR)$(MANDIR)/$$tr/man1/notion.1 ; \ $(INSTALL) -m $(DATA_MODE) pwm3.$$tr.1 $(DESTDIR)$(MANDIR)/$$tr/man1/pwm3.1 ; \ done $(INSTALLDIR) $(DESTDIR)$(SHAREDIR) for i in $(WELCOME_TARGETS); do \ $(INSTALL) -m $(DATA_MODE) $$i $(DESTDIR)$(SHAREDIR) ; \ done notion-3+2012042300/man/notion.cs.in000066400000000000000000000106621174530661200166040ustar00rootroot00000000000000.TH NOTION 1 .SH NÁZEV Notion - správce oken systému X11 .SH PØEHLED .B notion .I "[volby]" .SH "POPIS" Notion 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 ~/.notion/session_name. .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, autor, copyright) .SH Základní pojetí Tato sekce pøiná¹í pøehled typù objektù, které se objevují na X displeji spravovaném Notionem. 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 Notionu. 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í Notionu - 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 Notion 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 rámy a obrazovkami BINDINGS:WMPlex .SS Pøiøazení pro práci se v¹emi rámy BINDINGS:WFrame .SS Pøiøazení pro práci s nejvy¹¹ími rámy (ne s doèasnými) a obrazovkami BINDINGS:WMPlex.toplevel .SS Pøiøazení pro práci s nejvy¹¹ími rámy (ne s doèasnými) BINDINGS:WFrame.toplevel .SS Pøiøazení pro práci s plovoucími/odpojenými rámy BINDINGS:WFrame.floating .SS Pøiøazení pro dla¾dicové plochy a rámy [mod_tiling] BINDINGS:WTiling .\" BINDINGS:WFrame.tiled .SS Pøiøazení pro klientská okna BINDINGS:WClientWin BINDINGS:WGroupCW .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_notion.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 ~/.notion/ U¾ivatelské konfiguraèní soubory .TP .B ~/.notion/cfg_notion.lua Hlavní u¾ivatelský konfiguraèní soubor (pøepisuje systémové nastavení) .SH VIZ TAKÉ Domovská stránka Notionu \fIhttp://notion.sf.net/\fP .PP Dokument "Configuring and extending Notion with Lua" k nalezení tamté¾. .PP .I DOCDIR/ .PP \fIX(7x)\fP, \fIpwm3(1)\fP, \fIjoe(1)\fP .SH AUTOR Notion napsal Notion Team Ion napsal Tuomo Valkonen . .SH PØEKLAD Do èe¹tiny pøelo¾il Miroslav Kuøe . notion-3+2012042300/man/notion.fi.in000066400000000000000000000111701174530661200165700ustar00rootroot00000000000000.TH NOTION 1 .SH NIMI Notion - X11 ikkunanhallintaohjelma .SH SELOSTE .B notion .I "[valinnat]" .SH "KUVAUS" Notion 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 \-help Näytä komentorivin ohje. .TP .B \-version Näytä Notionin versio. .TP .B \-about Näytä tietoja Notionista (versio, tekijä, lisenssi). .SH PERUSKÄSITTEET Tämä kappale on katsaus erilaisiin niin kutsuttuihin \fBkappaleisiin\fP, joita ilmenee Notionin 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 Notionin seittisivulta saatavaa yksityiskohtaisempaa dokumentaatiota. Ylimmän tason kappaleista, 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ä kappaleet 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 kappaleita, mutta tässä tapauksessa lähinnä asiakasikkuna\fBryhmiä\fP. Kehykset ovat suurimman osan ajasta ainut asia, minkä näet Notionista. Niillä voi olla reunakoristukset, ja lisäksi kehyksissä on \fBvälilehti\fP jokaiselle lomitetulle kappaleelle. \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ä ja kaikissa kehyksissä toimivat sidonnat BINDINGS:WMPlex .SS Kaikissa kehyksissä toimivat sidonnat BINDINGS:WFrame .SS Näytöillä ja ylimmän tason kehyksissä (ei ns. transientit) toimivat sidonnat BINDINGS:WMPlex.toplevel .SS Ylimmän tason kehyksissä (ei ns. transientit) toimivat sidonnat BINDINGS:WFrame.toplevel .SS Kelluvissa/irroitetuissa kehyksissä toimivat sidonnat BINDINGS:WFrame.floating .SS Laatoituksien ja laatoitettujen kehysten sidonnat [mod_tiling] BINDINGS:WTiling .\" BINDINGS:WFrame.tiled .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_notion.lua Järjestelmän oletusarvoinen pääasetustiedosto. .TP .B ETCDIR/cfg_*.lua Muita asetustiedostoja. .TP .B ETCDIR/look_*.lua Ulkonäön asetustiedostoja. .TP .B ~/.notion/ Käyttäjän asetustiedostot. .TP .B ~/.notion/cfg_notion.lua Käyttäjän oletusarvoinen pääasetustiedosto. .SH KATSO MYÖS Notionin kotisivu, \fIhttp://iki.fi/tuomov/ion/\fP (englanniksi). .PP Dokumentti "Configuring and extending Notion with Lua" (englanniksi) nähtävissä Notionin kotisivulla. .PP .I DOCDIR/ .PP \fIX(7x)\fP, \fIpwm3(1)\fP, \fIjoe(1)\fP .SH TEKIJÄ Notionin on kirjoittanut Notion Team Ionin on kirjoittanut Tuomo Valkonen . notion-3+2012042300/man/notion.in000066400000000000000000000107011174530661200161720ustar00rootroot00000000000000.TH NOTION 1 .SH NAME Notion - an X11 window manager .SH SYNOPSIS .B notion .I "[options]" .SH "DESCRIPTION" Notion 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 (~/.notion/session_name if option set). .TP .B \-help Show help on command line options .TP .B \-version Show version .TP .B \-about Show about text (version, author, license) .SH BASIC CONCEPTS This section is an overview of the types objects that appear on an X display managed by Notion 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 Notion 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. A workspace containing no tilings and only detached/floating windows is also called a \fBfloating workspace\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 Notion 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 whose configuration file defines these bindings. .SS Globally available bindings BINDINGS:WScreen .SS Bindings operating on all frames and screens BINDINGS:WMPlex .SS Bindings operating on all frames BINDINGS:WFrame .SS Bindings operating on top-level (non-transient) frames and screens BINDINGS:WMPlex.toplevel .SS Bindings operating on top-level (non-transient) frames BINDINGS:WFrame.toplevel .SS Bindings for floating/detached frames BINDINGS:WFrame.floating .SS Bindings for tilings and tiled frames [mod_tiling] BINDINGS:WTiling .\" BINDINGS:WFrame.tiled .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_notion.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 ~/.notion/ User configuration files .TP .B ~/.notion/cfg_notion.lua User default main configuration file (overrides system default) .SH SEE ALSO The Notion home page, \fIhttp://notion.sf.net/\fP .PP The document "Configuring and extending Notion with Lua" found on the Notion home page. .PP .I DOCDIR/ .PP \fIX(7x)\fP, \fIpwm3(1)\fP, \fIjoe(1)\fP .SH AUTHOR Notion was written by the Notion team, based on Ion which was written by Tuomo Valkonen . notion-3+2012042300/man/pwm3.cs.in000066400000000000000000000052041174530661200161600ustar00rootroot00000000000000.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 \-session 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 \-help Zobrazí nápovìdu k pøíkazovým parametrùm .TP .B \-version Zobrazí verzi .TP .B \-about Zobrazí informace o programu (verze, autor, copyright) .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 rámy, obrazovkami a jejich potomky BINDINGS:WMPlex .SS Pøiøazení pro práci s rámy a jejich potomky BINDINGS:WFrame BINDINGS:WFrame.floating .SS Pøiøazení pro práci s nejvy¹¹ími (ne s doèasnými) rámy a obrazovkami BINDINGS:WMPlex.toplevel .SS Pøiøazení pro práci s nejvy¹¹ími (ne s doèasnými) rámy BINDINGS:WFrame.toplevel .SS Pøiøazení pro práci s klientskými okny BINDINGS:WClientWin BINDINGS:WGroupCW .SS Pøiøazení pro pøesun/zmìnu velikosti BINDINGS:WMoveresMode .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 \fInotion(1)\fP. .SH AUTOR Programy PWM a Ion napsal Tuomo Valkonen . .SH PØEKLAD Do èe¹tiny pøelo¾il Miroslav Kuøe . notion-3+2012042300/man/pwm3.fi.in000066400000000000000000000047201174530661200161530ustar00rootroot00000000000000.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 \-session session_name Istunnon nimi. Tämä vaikuttaa talletustiedostojen sijaintiin. .TP .B \-help Näytä komentorivin ohje. .TP .B \-version Näytä Ionin versio. .TP .B \-about Näytä tietoja PWM:sta (versio, tekijä, lisenssi). .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ä ja kaikissa kehyksissä toimivat sidonnat BINDINGS:WMPlex .SS Kaikissa kehyksissä toimivat sidonnat BINDINGS:WFrame BINDINGS:WFrame.floating .SS Näytöillä ja ylimmän tason kehyksissä (ei ns. transientit) toimivat sidonnat BINDINGS:WMPlex.toplevel .SS Ylimmän tason kehyksissä (ei ns. transientit) toimivat sidonnat BINDINGS:WFrame.toplevel .SS Asiakasikkunoiden sidonnat BINDINGS:WClientWin BINDINGS:WGroupCW .SS Siirto- ja koonmuutostilan sidonnat BINDINGS:WMoveresMode .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 ~/.notion/ Käyttäjän asetustiedostot. .TP .B ~/.notion/cfg_pwm.lua Käyttäjän oletusarvoinen pääasetustiedosto. .SH KATSO MYÖS Katso \fInotion(1)\fP. .SH TEKIJÄ PWM:n sekä Ionin on kirjoittanut Tuomo Valkonen . notion-3+2012042300/man/pwm3.in000066400000000000000000000050371174530661200155600ustar00rootroot00000000000000.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 \-session session_name Set session name. This option affects where workspace and other save files are put (~/.pwm3/session_name if option set). .TP .B \-help Show help on command line options .TP .B \-version Show version .TP .B \-about Show about text (version, author, license) .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 all frames and screens and their children BINDINGS:WMPlex .SS Bindings operating on all frames and their children BINDINGS:WFrame BINDINGS:WFrame.floating .SS Bindings operating on top-level (non-transient) frames as well as screens BINDINGS:WMPlex.toplevel .SS Bindings operating on top-level (non-transient) frames BINDINGS:WFrame.toplevel .SS Bindings for client windows BINDINGS:WClientWin BINDINGS:WGroupCW .SS Move/resize mode bindings BINDINGS:WMoveresMode .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 \fInotion(1)\fP. .SH AUTHOR Both PWM and Ion have been written by Tuomo Valkonen . notion-3+2012042300/man/welcome.cs.head000066400000000000000000000020241174530661200172150ustar00rootroot00000000000000 Vítejte v Notionu! Pokud jste je¹tì nikdy Notion 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 notion'. (Pøedpokládáme, ¾e jste Notion 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 Notionu. Pøedtím ale doporuèujeme Notion nìjakou dobu pou¾ívat, abyste pochytili základní koncepty nutné pro porozumìní konfiguraèních souborù. ---- Následuje manuálová stránka ---- notion-3+2012042300/man/welcome.fi.head000066400000000000000000000022231174530661200172070ustar00rootroot00000000000000 Tervetuloa käyttämään Notionia! Jos et ole käyttänyt Notionia aiemmin, on hyvin suotiteltavaa, että opiskelet Notionin 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 notion' komentoriviltä. (Tämä toimii sillä oletuksella, että olet asentanut Notionin järjestelmässäsi sellaiseen vakiosijaintin, mistä sen työkalut osaavat hakea ohjesivuja.) Jos olet innokas kokeilemaan Notionia 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 Notionin peruskäsitteet ja toiminnot, haluat varmaan muokata sen asetukset itsellesi sopivammiksi. Tähän löydät apua (englanniksi) Notionin asetus- ja ohjelmointimanuaalista. Se löytyy ainakin Notionin seittisivulta, johon on linkki alla olevan ohjesivun lopussa. Huomautettakoon vielä, että Notionin käytön ja peruskäsitteiden opettelu on hyvin suositeltvaa ennen asetustiedostojen pariin hyppäämistä. ---- Ohjesivu seuraa ---- notion-3+2012042300/man/welcome.head000066400000000000000000000021421174530661200166120ustar00rootroot00000000000000 Welcome to Notion! If you have never used Notion before, it is highly recommended that you study the Notion 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 notion' in a terminal. All this supposing that you have installed Notion in a standard location so that the system commands can find the manual page. If you are too eager to try out Notion 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 Notion webpage listed at the end of the user manual below. It may, however, be beneficial to become well acquainted with Notion before delving into this so that you have grasped the basic concepts necessary to understand the binding configuration files. ---- Manual page follows ---- notion-3+2012042300/mod_dock/000077500000000000000000000000001174530661200153415ustar00rootroot00000000000000notion-3+2012042300/mod_dock/Makefile000066400000000000000000000011161174530661200170000ustar00rootroot00000000000000## ## Notion dock 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=dock.c DOCS=LICENSE MAKE_EXPORTS=mod_dock MODULE=mod_dock ###################################### include $(TOPDIR)/build/rules.mk ###################################### _install: module_install ###################################### .PHONY: tags tags: exuberant-ctags -R . $(TOPDIR) notion-3+2012042300/mod_dock/dock.c000066400000000000000000001240171174530661200164320ustar00rootroot00000000000000/* * Ion dock module * Copyright (C) 2003 Tom Payne * Copyright (C) 2003 Per Olofsson * Copyright (C) 2004-2009 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 "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[]=NOTION_API_VERSION; 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(!ioncore_g.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 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.x=REGION_GEOM(dock).x; border_dock_geom.y=REGION_GEOM(dock).y; 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; /* 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(ioncore_g.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); } 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); } } 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 mplexszplcy(int pos, WSizePolicy *szplcy) { int hp=pos&DOCK_HPOS_MASK, vp=pos&DOCK_VPOS_MASK; WSizePolicy p; p=(vp!=DOCK_VPOS_MIDDLE ? (vp==DOCK_VPOS_TOP ? (hp!=DOCK_HPOS_CENTER ? (hp==DOCK_HPOS_RIGHT ? SIZEPOLICY_GRAVITY_NORTHEAST : SIZEPOLICY_GRAVITY_NORTHWEST) : SIZEPOLICY_GRAVITY_NORTH) : (hp!=DOCK_HPOS_CENTER ? (hp==DOCK_HPOS_RIGHT ? SIZEPOLICY_GRAVITY_SOUTHEAST : SIZEPOLICY_GRAVITY_SOUTHWEST) : SIZEPOLICY_GRAVITY_SOUTH)) : (hp!=DOCK_HPOS_CENTER ? (hp==DOCK_HPOS_RIGHT ? SIZEPOLICY_GRAVITY_EAST : SIZEPOLICY_GRAVITY_WEST) : SIZEPOLICY_GRAVITY_CENTER)); *szplcy=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); }else if((WRegion*)par==REGION_MANAGER(dock)){ WSizePolicy szplcy; mplexszplcy(dock->pos, &szplcy); mplex_set_szplcy(par, (WRegion*)dock, szplcy); } } 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, "WDock")) return FALSE; region_add_bindmap((WRegion*)dock, dock_bindmap); 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); } 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; WFitParams fp; 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 */ 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); /* Calculate min/max size */ dock_managed_rqgeom_(dock, NULL, 0, NULL, NULL, TRUE); /* Final setup */ if(floating){ const WRectangle *pg=®ION_GEOM(screen); WMPlexAttachParams par=MPLEXATTACHPARAMS_INIT; WRegionAttachData data; par.flags=(MPLEX_ATTACH_UNNUMBERED |MPLEX_ATTACH_SIZEPOLICY |MPLEX_ATTACH_GEOM |MPLEX_ATTACH_PASSIVE); par.geom.w=dock->min_w; par.geom.h=dock->min_h; par.geom.x=0; par.geom.y=0; mplexszplcy(dock->pos, &par.szplcy); if(extl_table_is_bool_set(tab, "floating_hidden")) par.flags|=MPLEX_ATTACH_HIDDEN; data.type=REGION_ATTACH_REPARENT; data.u.reg=(WRegion*)dock; if(mplex_do_attach((WMPlex*)screen, &par, &data)) 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 priority) { if(!MANAGE_PRIORITY_OK(priority, MANAGE_PRIORITY_LOW)) 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_PRIORITY_NONE); } /*}}}*/ /*{{{ Module init/deinit */ bool mod_dock_init() { 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_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); /*}}}*/ notion-3+2012042300/mod_menu/000077500000000000000000000000001174530661200153655ustar00rootroot00000000000000notion-3+2012042300/mod_menu/Makefile000066400000000000000000000010171174530661200170240ustar00rootroot00000000000000## ## 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 notion-3+2012042300/mod_menu/grabmenu.c000066400000000000000000000054261174530661200173400ustar00rootroot00000000000000/* * ion/mod_menu/grabmenu.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #include #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(ev->keycode==menu->gm_kcb){ if(menu->gm_state==ev->state) menu_select_next(menu); else if((menu->gm_state|ShiftMask)==ev->state) menu_select_prev(menu); else if(menu->gm_state==AnyModifier) menu_select_next(menu); } return FALSE; } static void grabkilled_handler(WRegion *reg) { destroy_obj((Obj*)reg); } /*--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; uint state, kcb; bool sub; if(!ioncore_current_key(&kcb, &state, &sub)) return NULL; if(state==0){ WMenu *menu=mod_menu_do_menu(mplex, handler, tab, param); /* if(menu!=NULL && cycle!=extl_fn_none()){ uint kcb, state; menu->cycle_bindmap=region_add_cycle_bindmap((WRegion*)menu, kcb, state, ???, ???); }*/ return menu; } 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_LEVEL| MPLEX_ATTACH_UNNUMBERED| MPLEX_ATTACH_SIZEPOLICY); par.szplcy=SIZEPOLICY_FULL_BOUNDS; par.level=STACKING_LEVEL_MODAL1+2; menu=(WMenu*)mplex_do_attach_new(mplex, &par, (WRegionCreateFn*)create_menu, (void*)&fnp); if(menu==NULL) return NULL; menu->gm_kcb=kcb; menu->gm_state=state; ioncore_grab_establish((WRegion*)menu, grabmenu_handler, grabkilled_handler, 0); return menu; } notion-3+2012042300/mod_menu/main.c000066400000000000000000000017721174530661200164640ustar00rootroot00000000000000/* * ion/mod_menu/main.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #include #include #include #include "menu.h" #include "exports.h" /*{{{ Module information */ #include "../version.h" char mod_menu_ion_api_version[]=NOTION_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; } /*}}}*/ notion-3+2012042300/mod_menu/main.h000066400000000000000000000005361174530661200164660ustar00rootroot00000000000000/* * ion/mod_menu/main.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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 */ notion-3+2012042300/mod_menu/menu.c000066400000000000000000001002571174530661200165020ustar00rootroot00000000000000/* * ion/mod_menu/menu.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #include #include #include #include #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); } } GR_DEFATTR(active); GR_DEFATTR(inactive); GR_DEFATTR(selected); GR_DEFATTR(unselected); GR_DEFATTR(normal); GR_DEFATTR(submenu); static void init_attr() { GR_ALLOCATTR_BEGIN; GR_ALLOCATTR(active); GR_ALLOCATTR(inactive); GR_ALLOCATTR(selected); GR_ALLOCATTR(unselected); GR_ALLOCATTR(normal); GR_ALLOCATTR(submenu); GR_ALLOCATTR_END; } static void menu_draw_entry(WMenu *menu, int i, const WRectangle *igeom, bool complete) { WRectangle geom; GrAttr sa, aa; aa=(REGION_IS_ACTIVE(menu) ? GR_ATTR(active) : GR_ATTR(inactive)); sa=(menu->selected_entry==i ? GR_ATTR(selected) : GR_ATTR(unselected)); 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); grbrush_begin(menu->entry_brush, &geom, GRBRUSH_AMEND|GRBRUSH_KEEP_ATTR); grbrush_init_attr(menu->entry_brush, &menu->entries[i].attr); grbrush_set_attr(menu->entry_brush, aa); grbrush_set_attr(menu->entry_brush, sa); grbrush_draw_textbox(menu->entry_brush, &geom, menu->entries[i].title, 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_set_attr(menu->brush, aa); grbrush_draw_border(menu->brush, &geom); 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; } void menu_size_hints(WMenu *menu, WSizeHints *hints_ret) { int n=menu->n_entries; int w=menu->max_entry_w; int h=menu->entry_h*n + menu->entry_spacing*maxof(0, n-1); if(menu->brush!=NULL){ GrBorderWidths bdw; grbrush_get_border_widths(menu->brush, &bdw); w+=bdw.left+bdw.right; h+=bdw.top+bdw.bottom; } hints_ret->min_set=TRUE; hints_ret->min_width=w; hints_ret->min_height=h; } /*}}}*/ /*{{{ 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) { WMenuEntry *entries; ExtlTab entry; 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; init_attr(); /* Initialise entries and check submenus */ for(i=1; i<=n; i++){ WMenuEntry *ent=&entries[i-1]; ent->title=NULL; ent->flags=0; gr_stylespec_init(&ent->attr); if(extl_table_geti_t(tab, i, &entry)){ char *attr; ExtlTab sub; ExtlFn fn; if(extl_table_gets_s(entry, "attr", &attr)){ gr_stylespec_load_(&ent->attr, attr, TRUE); free(attr); } if(extl_table_gets_f(entry, "submenu_fn", &fn)){ ent->flags|=WMENUENTRY_SUBMENU; extl_unref_fn(fn); }else if(extl_table_gets_t(entry, "submenu", &sub)){ ent->flags|=WMENUENTRY_SUBMENU; extl_unref_table(sub); } if(ent->flags&WMENUENTRY_SUBMENU) gr_stylespec_set(&ent->attr, GR_ATTR(submenu)); extl_unref_table(entry); } } return entries; } static void deinit_entries(WMenu *menu); 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->cycle_bindmap=NULL;*/ 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, "WMenu")) goto fail; win=menu->win.win; if(!menu_init_gr(menu, region_rootwin_of((WRegion*)par), win)) goto fail2; init_attr(); 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); deinit_entries(menu); return FALSE; } WMenu *create_menu(WWindow *par, const WFitParams *fp, const WMenuCreateParams *params) { CREATEOBJ_IMPL(WMenu, menu, (p, par, fp, params)); } static void deinit_entries(WMenu *menu) { int i; for(i=0; in_entries; i++){ gr_stylespec_unalloc(&menu->entries[i].attr); if(menu->entries[i].title!=NULL) free(menu->entries[i].title); } free(menu->entries); } void menu_deinit(WMenu *menu) { menu_typeahead_clear(menu); if(menu->submenu!=NULL) destroy_obj((Obj*)menu->submenu); /*if(menu->cycle_bindmap!=NULL) bindmap_destroy(menu->cycle_bindmap);*/ extl_unref_table(menu->tab); extl_unref_fn(menu->handler); deinit_entries(menu); 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_rqdispose((WRegion*)head)){ 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) { region_defer_rqdispose((WRegion*)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 in * 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}, {region_size_hints, menu_size_hints}, END_DYNFUNTAB }; EXTL_EXPORT IMPLCLASS(WMenu, WWindow, menu_deinit, menu_dynfuntab); /*}}}*/ notion-3+2012042300/mod_menu/menu.h000066400000000000000000000043441174530661200165070ustar00rootroot00000000000000/* * ion/mod_menu/menu.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #ifndef ION_MOD_MENU_MENU_H #define ION_MOD_MENU_MENU_H #include #include #include #include #include INTRCLASS(WMenu); INTRSTRUCT(WMenuEntry); #define WMENUENTRY_SUBMENU 0x0001 DECLSTRUCT(WMenuEntry){ char *title; int flags; GrStyleSpec attr; }; 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; /*WBindmap *cycle_bindmap;*/ }; 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 */ notion-3+2012042300/mod_menu/mkmenu.c000066400000000000000000000070061174530661200170300ustar00rootroot00000000000000/* * ion/mod_menu/mkmenu.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #include #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_LEVEL| MPLEX_ATTACH_UNNUMBERED| MPLEX_ATTACH_SIZEPOLICY); par.szplcy=SIZEPOLICY_FULL_BOUNDS; par.level=STACKING_LEVEL_MODAL1+2; 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; } notion-3+2012042300/mod_menu/mkmenu.h000066400000000000000000000007641174530661200170410ustar00rootroot00000000000000/* * ion/mod_menu/mkmenu.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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 */ notion-3+2012042300/mod_menu/mod_menu.lua000066400000000000000000000063261174530661200177020ustar00rootroot00000000000000-- -- ion/mod_menu/mod_menu.lua -- Menu opening helper routines. -- -- Copyright (c) Tuomo Valkonen 2004-2009. -- -- See the included file LICENSE for details. -- -- 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 ok=reg:managed_i(function(r) return not obj_is(r, "WMenu") end) if not ok then return 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 ioncore.unsqueeze(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 param={big=true, initial=initial} return mod_menu.menu(mplex, sub, menu_or_name, param) 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) notion-3+2012042300/mod_query/000077500000000000000000000000001174530661200155665ustar00rootroot00000000000000notion-3+2012042300/mod_query/Makefile000066400000000000000000000012171174530661200172270ustar00rootroot00000000000000## ## 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 notion-3+2012042300/mod_query/complete.c000066400000000000000000000102611174530661200175420ustar00rootroot00000000000000/* * ion/mod_query/complete.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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); /*}}}*/ notion-3+2012042300/mod_query/complete.h000066400000000000000000000016651174530661200175570ustar00rootroot00000000000000/* * ion/mod_query/complete.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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 */ notion-3+2012042300/mod_query/edln.c000066400000000000000000000331611174530661200166600ustar00rootroot00000000000000/* * ion/mod_query/edln.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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, FALSE); free(tmpstr); }else{ e=mod_query_history_search(edln->context, from, bwd, FALSE); } 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); } /*}}}*/ notion-3+2012042300/mod_query/edln.h000066400000000000000000000034371174530661200166700ustar00rootroot00000000000000/* * ion/mod_query/edln.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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 */ notion-3+2012042300/mod_query/fwarn.c000066400000000000000000000025411174530661200170510ustar00rootroot00000000000000/* * ion/mod_query/query.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #include #include #include #include #include #include #include "wmessage.h" #include "fwarn.h" /*(internal) EXTL_DOC * Display an error message box in the multiplexer \var{mplex}. */ EXTL_EXPORT WMessage *mod_query_do_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_do_message(mplex, p2); free(p2); return wmsg; } /*(internal) EXTL_DOC * Display a message in the \var{mplex}. */ EXTL_EXPORT WMessage *mod_query_do_message(WMPlex *mplex, const char *p) { WMPlexAttachParams par; if(p==NULL) return NULL; par.flags=(MPLEX_ATTACH_SWITCHTO| MPLEX_ATTACH_LEVEL| MPLEX_ATTACH_UNNUMBERED| MPLEX_ATTACH_SIZEPOLICY); par.szplcy=SIZEPOLICY_FULL_BOUNDS; par.level=STACKING_LEVEL_MODAL1+2; return (WMessage*)mplex_do_attach_new(mplex, &par, (WRegionCreateFn*)create_wmsg, (void*)p); } notion-3+2012042300/mod_query/fwarn.h000066400000000000000000000006341174530661200170570ustar00rootroot00000000000000/* * ion/mod_query/fwarn.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #ifndef ION_MOD_QUERY_FWARN_H #define ION_MOD_QUERY_FWARN_H #include #include "wmessage.h" extern WMessage *mod_query_do_message(WMPlex *mplex, const char *p); extern WMessage *mod_query_do_fwarn(WMPlex *mplex, const char *p); #endif /* ION_MOD_QUERY_FWARN_H */ notion-3+2012042300/mod_query/history.c000066400000000000000000000072551174530661200174440ustar00rootroot00000000000000/* * ion/mod_query/history.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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, TRUE); if(ndx==0){ free(str); 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, bool exact); extern uint mod_query_history_complete(const char *s, char ***h_ret); extern ExtlTab mod_query_history_table(); #endif /* ION_MOD_QUERY_HISTORY_H */ notion-3+2012042300/mod_query/input.c000066400000000000000000000100011174530661200170610ustar00rootroot00000000000000/* * ion/mod_query/input.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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); } bool input_fitrep(WInput *input, WWindow *par, const WFitParams *fp) { if(par!=NULL && !region_same_rootwin((WRegion*)input, (WRegion*)par)) return FALSE; input->last_fp=*fp; input_do_refit(input, par); return TRUE; } 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, "WInput")) 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) { region_defer_rqdispose((WRegion*)input); } /*}}}*/ /*{{{ Focus */ static void input_inactivated(WInput *input) { window_draw((WWindow*)input, FALSE); } static void input_activated(WInput *input) { window_draw((WWindow*)input, FALSE); } /*}}}*/ /*{{{{ Misc */ void mod_query_get_minimum_extents(GrBrush *brush, bool with_spacing, int *w, int *h) { GrBorderWidths bdw; GrFontExtents fnte; int spc; grbrush_get_border_widths(brush, &bdw); grbrush_get_font_extents(brush, &fnte); spc=(with_spacing ? bdw.spacing : 0); *h=(fnte.max_height+bdw.top+bdw.bottom+spc); *w=(bdw.left+bdw.right+spc); } /*}}}*/ /*{{{ 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); /*}}}*/ notion-3+2012042300/mod_query/input.h000066400000000000000000000021621174530661200170770ustar00rootroot00000000000000/* * ion/mod_query/input.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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 bool 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); extern void mod_query_get_minimum_extents(GrBrush *brush, bool with_spacing, int *w, int *h); #endif /* ION_MOD_QUERY_INPUT_H */ notion-3+2012042300/mod_query/inputp.h000066400000000000000000000011151174530661200172540ustar00rootroot00000000000000/* * ion/mod_query/inputp.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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 */ notion-3+2012042300/mod_query/listing.c000066400000000000000000000255541174530661200174160ustar00rootroot00000000000000/* * ion/mod_query/listing.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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) { int i, l; if(iinf==NULL){ grbrush_draw_string(brush, x, y, str, strlen(str), TRUE); 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); for(i=1; in_parts; i++){ grbrush_draw_string(brush, x+maxw-wrapw, y, "\\", 1, TRUE); 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); } } 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, GrAttr selattr, int mode) { 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; if(mode>=0 || l->selected_str==i || LISTING_DRAW_GET_SELECTED(mode)==i){ if(i==l->selected_str) grbrush_set_attr(brush, selattr); draw_multirow(brush, geom->x+x, y, l->itemh, l->strs[i], (l->iteminfos!=NULL ? &(l->iteminfos[i]) : NULL), geom->w-x, ciw, wrapw); if(i==l->selected_str) grbrush_unset_attr(brush, selattr); } y+=l->itemh*ITEMROWS(l, i); r+=ITEMROWS(l, i); i++; } x+=l->itemw; c++; } } static int prevsel=-1; static bool filteridx_sel(WListing *l, int i) { return (i==prevsel || i==l->selected_str); } void draw_listing(GrBrush *brush, const WRectangle *geom, WListing *l, int mode, GrAttr selattr) { WRectangle geom2; GrBorderWidths bdw; grbrush_begin(brush, geom, GRBRUSH_AMEND|GRBRUSH_KEEP_ATTR |GRBRUSH_NEED_CLIP); if(mode==LISTING_DRAW_COMPLETE) grbrush_clear_area(brush, geom); grbrush_draw_border(brush, geom); 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, selattr, mode); 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; } int listing_select(WListing *l, int i) { int irow, frow, lrow; int redraw; redraw=LISTING_DRAW_SELECTED(l->selected_str); if(i<0){ l->selected_str=-1; return redraw; } 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--; redraw=LISTING_DRAW_COMPLETE; } irow+=ITEMROWS(l, i)-1; lrow=frow+l->visrow-1; while(irow>lrow){ one_row_down(l, &(l->firstitem), &(l->firstoff)); lrow++; redraw=LISTING_DRAW_COMPLETE; } return redraw; } notion-3+2012042300/mod_query/listing.h000066400000000000000000000025401174530661200174110ustar00rootroot00000000000000/* * ion/mod_query/listing.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #ifndef ION_MOD_QUERY_LISTING_H #define ION_MOD_QUERY_LISTING_H #include #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; }; #define LISTING_DRAW_COMPLETE 1 #define LISTING_DRAW_ALL 0 #define LISTING_DRAW_SELECTED(X) minof(-1, -(X)-2) #define LISTING_DRAW_GET_SELECTED(X) (-(X)-2) 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, GrAttr selattr); 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 */ notion-3+2012042300/mod_query/main.c000066400000000000000000000076511174530661200166670ustar00rootroot00000000000000/* * ion/mod_query/main.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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[]=NOTION_API_VERSION; /*}}}*/ /*{{{ Bindmaps */ WBindmap *mod_query_input_bindmap=NULL; WBindmap *mod_query_wedln_bindmap=NULL; /*}}}*/ /*{{{ Set & get */ ModQueryConfig mod_query_config={ 250, TRUE, FALSE, 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). \\ * \var{caseicompl} & (boolean) Turn some completions case-insensitive * (default: false). \\ * \var{substrcompl} & (boolean) Complete on sub-strings in some cases * (default: ftrue). \\ * \end{tabularx} */ EXTL_EXPORT void mod_query_set(ExtlTab tab) { ModQueryConfig *c=&mod_query_config; extl_table_gets_b(tab, "autoshowcompl", &c->autoshowcompl); extl_table_gets_b(tab, "caseicompl", &c->caseicompl); extl_table_gets_b(tab, "substrcompl", &c->substrcompl); 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_SAFE 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); extl_table_sets_b(tab, "caseicompl", c->caseicompl); extl_table_sets_b(tab, "substrcompl", c->substrcompl); 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; } /*}}}*/ notion-3+2012042300/mod_query/main.h000066400000000000000000000007431174530661200166670ustar00rootroot00000000000000/* * ion/mod_query/main.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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; bool caseicompl; bool substrcompl; }; extern ModQueryConfig mod_query_config; #endif /* ION_MOD_QUERY_MAIN_H */ notion-3+2012042300/mod_query/mod_query.lua000066400000000000000000001052531174530661200203030ustar00rootroot00000000000000-- -- ion/query/mod_query.lua -- Some common queries for Ion -- -- Copyright (c) Tuomo Valkonen 2004-2009. -- -- See the included file LICENSE for details. -- -- 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 {{{ --DOC -- Display an error message box in the multiplexer \var{mplex}. function mod_query.warn(mplex, str) ioncore.unsqueeze(mod_query.do_warn(mplex, str)) end --DOC -- Display a message in \var{mplex}. function mod_query.message(mplex, str) ioncore.unsqueeze(mod_query.do_message(mplex, str)) 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 local function bcycle(wedln) wedln:complete('prev', 'normal') end -- Check that no other queries or message boxes are open in the mplex. local ok=mplex:managed_i(function(r) return not (obj_is(r, "WEdln") or obj_is(r, "WMessage")) end) if not ok then return end local wedln=mod_query.do_query(mplex, prompt, initvalue, handle_it, completor, cycle, bcycle) if wedln then ioncore.unsqueeze(wedln) if context then wedln:set_context(context) end 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), nil, wd) end end local function mk_completion_test(str, sub_ok, casei_ok) local settings=mod_query.get() if not str then return function(s) return true end end local function mk(str, sub_ok) if sub_ok then return function(s) return string.find(s, str, 1, true) end else local len=string.len(str) return function(s) return string.sub(s, 1, len)==str end end end casei_ok=(casei_ok and settings.caseicompl) sub_ok=(sub_ok and settings.substrcompl) if not casei_ok then return mk(str, sub_ok) else local fn=mk(string.lower(str), sub_ok) return function(s) return fn(string.lower(s)) end end end local function mk_completion_add(entries, str, sub_ok, casei_ok) local tst=mk_completion_test(str, sub_ok, casei_ok) return function(s) if s and tst(s) then table.insert(entries, s) end end end function mod_query.complete_keys(list, str, sub_ok, casei_ok) local results={} local test_add=mk_completion_add(results, str, sub_ok, casei_ok) for m, _ in pairs(list) do test_add(m) end return results end function mod_query.complete_name(str, iter) local sub_ok_first=true local casei_ok=true local entries={} local tst_add=mk_completion_add(entries, str, sub_ok_first, casei_ok) iter(function(reg) tst_add(reg:name()) return true end) if #entries==0 and not sub_ok_first then local tst_add2=mk_completion_add(entries, str, true, casei_ok) iter(function(reg) tst_add2(reg:name()) return true end) end return entries end function mod_query.make_completor(completefn) local function completor(cp, str, point) cp:set_completions(completefn(str, point)) end return completor 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_clientwin(str) return mod_query.complete_name(str, ioncore.clientwin_i) end function mod_query.complete_workspace(str) local function iter(fn) return ioncore.region_i(function(obj) return (not obj_is(obj, "WGroupWS") or fn(obj)) end) end return mod_query.complete_name(str, iter) end function mod_query.complete_region(str) return mod_query.complete_name(str, ioncore.region_i) 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:groupleader_of() local function attach() frame:attach(reg, { switchto = true }) 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() else local function create_handler(mplex_, layout) if not layout or layout=="" then layout="default" end if not ioncore.getlayout(layout) then mod_query.warn(mplex_, TR("Unknown layout")) else local scr=mplex:screen_of() local function mkws() local tmpl={ name=(name~="" and name), switchto=true } if not ioncore.create_ws(scr, tmpl, layout) then error(TR("Unknown error")) end end mod_query.call_warn(mplex, mkws) end end local function compl_layout(str) local los=ioncore.getlayout(nil, true) return mod_query.complete_keys(los, str, true, true) end mod_query.query(mplex, TR("New workspace layout (default):"), nil, create_handler, mod_query.make_completor(compl_layout), "workspacelayout") end 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_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 attaches -- it to the frame the query was opened in. 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 Notion/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 Notion (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 \var{ws}, -- or the one on which \var{mplex} resides, if it is not set. -- If \var{mplex} is not set, one is looked for. function mod_query.query_renameworkspace(mplex, ws) if not mplex then assert(ws) mplex=ioncore.find_manager(ws, "WMPlex") elseif not ws then assert(mplex) ws=ioncore.find_manager(mplex, "WGroupWS") end assert(mplex and ws) 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_(wd, cp, 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(cp, ic..wp..string.shell_safe(s_compl), set_fn, filter_fn, wd) end end function mod_query.exec_completor(...) mod_query.exec_completor_(nil, ...) 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) local function compl(...) local wd=ioncore.get_dir_for(mplex) mod_query.exec_completor_(wd, ...) end mod_query.query(mplex, TR("Run:"), nil, mod_query.exec_handler, compl, "run") end -- }}} -- SSH {{{ mod_query.known_hosts={} mod_query.hostnicks={} mod_query.ssh_completions={} 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 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.gmatch(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 = {} local tst = mk_completion_test(host, true, true) for _, v in ipairs(mod_query.ssh_completions) do if tst(v) then table.insert(res, user .. v) end end return res end --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(cp, str) local mc=ioncore.lookup_script("ion-completeman") local icase=(mod_query.get().caseicompl and " -icase" or "") local mid="" if mc then mod_query.popen_completions(cp, (mc..icase..mid.." -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 arg={...} 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.gmatch(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, sub, themenu, prompt) if type(sub)=="string" then -- Backwards compat. shift prompt=themenu themenu=sub sub=nil end 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]+", "-")) 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) while t[n] or t[n..'/'] do n=n.."'" end if is_submenu then 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) return mod_query.complete_keys(ntab, str, true, true) 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_i then local first=true reg:managed_i(function(sub) if first then s=s .. "\n" .. indent .. "---" first=false end s=s .. "\n" .. get_info(sub, indent, d+1) return true 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) notion-3+2012042300/mod_query/mod_query_chdir.lua000066400000000000000000000024371174530661200214540ustar00rootroot00000000000000-- -- ion/query/mod_query_chdir.lua -- -- Copyright (c) Tuomo Valkonen 2004-2009. -- -- See the included file LICENSE for details. -- local function simplify_path(path) local npath=string.gsub(path, "([^/]+)/+%.%./+", "") if npath~=path then return simplify_path(npath) else return string.gsub(string.gsub(path, "([^/]+)/+%.%.$", ""), "/+", "/") end end local function relative_path(path) return not string.find(path, "^/") end local function empty_path(path) return (not path or path=="") end local function ws_chdir(mplex, params) local nwd=params[1] ws=assert(ioncore.find_manager(mplex, "WGroupWS")) if not empty_path(nwd) and relative_path(nwd) then local owd=ioncore.get_dir_for(ws) if empty_path(owd) then owd=os.getenv("PWD") end if owd then nwd=owd.."/"..nwd end end local ok, err=ioncore.chdir_for(ws, nwd and simplify_path(nwd)) if not ok then mod_query.warn(mplex, err) end end local function ws_showdir(mplex, params) local dir=ioncore.get_dir_for(mplex) if empty_path(dir) then dir=os.getenv("PWD") end mod_query.message(mplex, dir or "(?)") end mod_query.defcmd("cd", ws_chdir) mod_query.defcmd("pwd", ws_showdir) notion-3+2012042300/mod_query/query.c000066400000000000000000000041261174530661200171020ustar00rootroot00000000000000/* * ion/mod_query/query.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #include #include #include #include #include #include #include #include #include "query.h" #include "wedln.h" /*--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, ExtlFn bcycle) { WRectangle geom; WEdlnCreateParams fnp; WMPlexAttachParams par; WEdln *wedln; fnp.prompt=prompt; fnp.dflt=dflt; fnp.handler=handler; fnp.completor=completor; par.flags=(MPLEX_ATTACH_SWITCHTO| MPLEX_ATTACH_LEVEL| MPLEX_ATTACH_UNNUMBERED| MPLEX_ATTACH_SIZEPOLICY); par.szplcy=SIZEPOLICY_FULL_BOUNDS; par.level=STACKING_LEVEL_MODAL1+2; wedln=(WEdln*)mplex_do_attach_new(mplex, &par, (WRegionCreateFn*)create_wedln, (void*)&fnp); if(wedln!=NULL && cycle!=extl_fn_none()){ uint kcb, state; bool sub; if(ioncore_current_key(&kcb, &state, &sub) && !sub){ wedln->cycle_bindmap=region_add_cycle_bindmap((WRegion*)wedln, kcb, state, cycle, bcycle); } } return wedln; } notion-3+2012042300/mod_query/query.h000066400000000000000000000011031174530661200170770ustar00rootroot00000000000000/* * ion/mod_query/query.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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, ExtlFn bcycle); #endif /* ION_MOD_QUERY_QUERY_H */ notion-3+2012042300/mod_query/wedln-wrappers.c000066400000000000000000000120601174530661200207030ustar00rootroot00000000000000/* * ion/mod_query/wedln-wrappers.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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; } notion-3+2012042300/mod_query/wedln.c000066400000000000000000000623361174530661200170550ustar00rootroot00000000000000/* * ion/mod_query/wedln.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #include #include #include #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, GrAttr a) { if(len==0) return 0; grbrush_set_attr(WEDLN_BRUSH(wedln), a); grbrush_draw_string(WEDLN_BRUSH(wedln), x, y, str, len, TRUE); grbrush_unset_attr(WEDLN_BRUSH(wedln), a); 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, A) \ if(LEN>0){ \ tx+=wedln_draw_strsect(wedln, geom, geom->x+tx, ty, \ str, LEN, GR_ATTR(A)); \ str+=LEN; len-=LEN; \ } GR_DEFATTR(active); GR_DEFATTR(inactive); GR_DEFATTR(normal); GR_DEFATTR(selection); GR_DEFATTR(cursor); GR_DEFATTR(prompt); GR_DEFATTR(info); static void init_attr() { GR_ALLOCATTR_BEGIN; GR_ALLOCATTR(active); GR_ALLOCATTR(inactive); GR_ALLOCATTR(normal); GR_ALLOCATTR(selection); GR_ALLOCATTR(cursor); GR_ALLOCATTR(prompt); GR_ALLOCATTR(info); GR_ALLOCATTR_END; } 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; ty=calc_text_y(wedln, geom); if(mark<=cursor){ if(mark>=0){ DSTRSECT(mark, normal); DSTRSECT(cursor-mark, selection); }else{ DSTRSECT(cursor, normal); } if(len==0){ tx+=wedln_draw_strsect(wedln, geom, geom->x+tx, ty, " ", 1, GR_ATTR(cursor)); }else{ ll=str_nextoff(str, 0); DSTRSECT(ll, cursor); } }else{ DSTRSECT(cursor, normal); ll=str_nextoff(str, 0); DSTRSECT(ll, cursor); DSTRSECT(mark-cursor-ll, selection); } DSTRSECT(len, normal); 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_KEEP_ATTR|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) { int w=1, h=1; if(WEDLN_BRUSH(wedln)!=NULL) mod_query_get_minimum_extents(WEDLN_BRUSH(wedln), with_spacing, &w, &h); return h; } 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); } void wedln_size_hints(WEdln *wedln, WSizeHints *hints_ret) { int w=1, h=1; if(WEDLN_BRUSH(wedln)!=NULL){ mod_query_get_minimum_extents(WEDLN_BRUSH(wedln), FALSE, &w, &h); w+=wedln->prompt_w+wedln->info_w; w+=grbrush_get_text_width(WEDLN_BRUSH(wedln), "xxxxxxxxxx", 10); } hints_ret->min_set=TRUE; hints_ret->min_width=w; hints_ret->min_height=h; } /*}}}*/ /*{{{ Draw */ void wedln_draw_completions(WEdln *wedln, int mode) { WRectangle geom; if(wedln->compl_list.strs!=NULL && WEDLN_BRUSH(wedln)!=NULL){ get_completions_geom(wedln, G_CURRENT, &geom); draw_listing(WEDLN_BRUSH(wedln), &geom, &(wedln->compl_list), mode, GR_ATTR(selection)); } } void wedln_draw_textarea(WEdln *wedln) { WRectangle geom; int ty; 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); get_inner_geom(wedln, G_CURRENT, &geom); ty=calc_text_y(wedln, &geom); grbrush_set_attr(WEDLN_BRUSH(wedln), GR_ATTR(prompt)); if(wedln->prompt!=NULL){ grbrush_draw_string(WEDLN_BRUSH(wedln), geom.x, ty, wedln->prompt, wedln->prompt_len, TRUE); } if(wedln->info!=NULL){ int x=geom.x+geom.w-wedln->info_w; grbrush_set_attr(WEDLN_BRUSH(wedln), GR_ATTR(info)); grbrush_draw_string(WEDLN_BRUSH(wedln), x, ty, wedln->info, wedln->info_len, TRUE); grbrush_unset_attr(WEDLN_BRUSH(wedln), GR_ATTR(info)); } grbrush_unset_attr(WEDLN_BRUSH(wedln), GR_ATTR(prompt)); 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));*/ } static void wedln_draw_(WEdln *wedln, bool complete, bool completions) { 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); grbrush_set_attr(WEDLN_BRUSH(wedln), REGION_IS_ACTIVE(wedln) ? GR_ATTR(active) : GR_ATTR(inactive)); if(completions) wedln_draw_completions(wedln, LISTING_DRAW_ALL); wedln_draw_textarea(wedln); grbrush_end(WEDLN_BRUSH(wedln)); } void wedln_draw(WEdln *wedln, bool complete) { wedln_draw_(wedln, complete, TRUE); } /*}}} */ /*{{{ 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_(wedln, FALSE, FALSE); } /*}}}*/ /*{{{ 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, LISTING_DRAW_COMPLETE); } 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, LISTING_DRAW_COMPLETE); } void wedln_scrolldown_completions(WEdln *wedln) { if(wedln->compl_list.strs==NULL) return; if(scrolldown_listing(&(wedln->compl_list))) wedln_draw_completions(wedln, LISTING_DRAW_COMPLETE); } static int update_nocompl=0; static void free_completions(char **ptr, int i) { while(i>0){ i--; if(ptr[i]!=NULL) 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; icompl_list), n); wedln_draw_completions(wedln, redraw); 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 \codestr{next} or * \codestr{prev}, respectively. * The \var{mode} may be \codestr{history} or \codestr{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 \codestr{next-always} and \codestr{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; init_attr(); 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)); region_rqdispose((WRegion*)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}, {region_size_hints, wedln_size_hints}, END_DYNFUNTAB }; EXTL_EXPORT IMPLCLASS(WEdln, WInput, wedln_deinit, wedln_dynfuntab); /*}}}*/ notion-3+2012042300/mod_query/wedln.h000066400000000000000000000032671174530661200170600ustar00rootroot00000000000000/* * ion/mod_query/wedln.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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 */ notion-3+2012042300/mod_query/wmessage.c000066400000000000000000000116101174530661200175440ustar00rootroot00000000000000/* * ion/mod_query/wmessage.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #include #include #include #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; } void wmsg_size_hints(WMessage *wmsg, WSizeHints *hints_ret) { int w=1, h=1; if(WMSG_BRUSH(wmsg)!=NULL){ mod_query_get_minimum_extents(WMSG_BRUSH(wmsg), FALSE, &w, &h); w+=grbrush_get_text_width(WMSG_BRUSH(wmsg), "xxxxx", 5); } hints_ret->min_set=TRUE; hints_ret->min_width=w; hints_ret->min_height=h; } /*}}}*/ /*{{{ Draw */ GR_DEFATTR(active); GR_DEFATTR(inactive); static void init_attr() { GR_ALLOCATTR_BEGIN; GR_ALLOCATTR(active); GR_ALLOCATTR(inactive); GR_ALLOCATTR_END; } static void wmsg_draw(WMessage *wmsg, bool complete) { 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)); grbrush_set_attr(WMSG_BRUSH(wmsg), REGION_IS_ACTIVE(wmsg) ? GR_ATTR(active) : GR_ATTR(inactive)); draw_listing(WMSG_BRUSH(wmsg), &geom, &(wmsg->listing), FALSE, GRATTR_NONE); 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_attr(); 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}, {region_size_hints, wmsg_size_hints}, END_DYNFUNTAB }; EXTL_EXPORT IMPLCLASS(WMessage, WInput, wmsg_deinit, wmsg_dynfuntab); /*}}}*/ notion-3+2012042300/mod_query/wmessage.h000066400000000000000000000011141174530661200175470ustar00rootroot00000000000000/* * ion/mod_query/wmessage.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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 */ notion-3+2012042300/mod_sm/000077500000000000000000000000001174530661200150405ustar00rootroot00000000000000notion-3+2012042300/mod_sm/Makefile000066400000000000000000000010451174530661200165000ustar00rootroot00000000000000## ## 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 notion-3+2012042300/mod_sm/sm.c000066400000000000000000000054261174530661200156320ustar00rootroot00000000000000/* * ion/mod_sm/sm.c * * Copyright (c) Tuomo Valkonen 2004-2009. * * See the included file LICENSE for details. */ #include #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[]=NOTION_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=scat3(smdir, "/", libtu_progbasename()); }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; } /*}}}*/ notion-3+2012042300/mod_sm/sm_matchwin.c000066400000000000000000000174121174530661200175220ustar00rootroot00000000000000/* * ion/mod_sm/sm_mathcwin.c * * Copyright (c) Tuomo Valkonen 2004-2009. * * Based on the code of the 'sm' module for Ion1 by an unknown contributor. * * This is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser 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); } } notion-3+2012042300/mod_sm/sm_matchwin.h000066400000000000000000000024331174530661200175240ustar00rootroot00000000000000/* * ion/mod_sm/sm_mathcwin.c * * Copyright (c) Tuomo Valkonen 2004-2009. * * Based on the code of the 'sm' module for Ion1 by an unknown contributor. * * This is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser 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 */ notion-3+2012042300/mod_sm/sm_session.c000066400000000000000000000255341174530661200173770ustar00rootroot00000000000000/* * ion/mod_sm/sm_session.c * * Copyright (c) Tuomo Valkonen 2004-2009. * * Based on the code of the 'sm' module for Ion1 by an unknown contributor. * * This is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser 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; } } notion-3+2012042300/mod_sm/sm_session.h000066400000000000000000000012741174530661200173770ustar00rootroot00000000000000/* * ion/mod_sm/sm_session.h * * Copyright (c) Tuomo Valkonen 2004-2009. * * Based on the code of the 'sm' module for Ion1 by an unknown contributor. * * This is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser 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 */ notion-3+2012042300/mod_sp/000077500000000000000000000000001174530661200150435ustar00rootroot00000000000000notion-3+2012042300/mod_sp/Makefile000066400000000000000000000007701174530661200165070ustar00rootroot00000000000000## ## 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 notion-3+2012042300/mod_sp/cfg_sp.lua000066400000000000000000000014031174530661200170050ustar00rootroot00000000000000-- -- 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')"), }) notion-3+2012042300/mod_sp/main.c000066400000000000000000000122121174530661200161310ustar00rootroot00000000000000/* * ion/mod_sp/main.c * * Copyright (c) Tuomo Valkonen 2004-2009. * * See the included file LICENSE for details. */ #include #include #include #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[]=NOTION_API_VERSION; /*}}}*/ /*{{{ Bindmaps, config, etc. */ #define SP_NAME "*scratchpad*" #define SPWS_NAME "*scratchws*" /*}}}*/ /*{{{ Exports */ static WRegion *create_frame_scratchpad(WWindow *parent, const WFitParams *fp, void *unused) { return (WRegion*)create_frame(parent, fp, FRAME_MODE_UNKNOWN); } static WRegion *create_scratchws(WWindow *parent, const WFitParams *fp, void *unused) { WRegion *reg; WRegionAttachData data; WGroupAttachParams par=GROUPATTACHPARAMS_INIT; WGroupWS *ws; ws=create_groupws(parent, fp); if(ws==NULL) return NULL; region_set_name((WRegion*)ws, SPWS_NAME); data.type=REGION_ATTACH_NEW; data.u.n.fn=create_frame_scratchpad; data.u.n.param=NULL; par.szplcy_set=TRUE; par.szplcy=SIZEPOLICY_FREE_GLUE; par.geom_set=TRUE; par.geom.w=minof(fp->g.w, CF_SCRATCHPAD_DEFAULT_W); par.geom.h=minof(fp->g.h, CF_SCRATCHPAD_DEFAULT_H); par.geom.x=(fp->g.w-par.geom.w)/2; par.geom.y=(fp->g.h-par.geom.h)/2; par.level_set=TRUE; par.level=STACKING_LEVEL_MODAL1+1; par.bottom=TRUE; reg=group_do_attach(&ws->grp, &par, &data); if(reg==NULL){ destroy_obj((Obj*)ws); return NULL; } region_set_name((WRegion*)reg, SP_NAME); return (WRegion*)ws; } static WRegion *create(WMPlex *mplex, int flags) { WRegion *sp; WMPlexAttachParams par=MPLEXATTACHPARAMS_INIT; par.flags=(flags |MPLEX_ATTACH_UNNUMBERED |MPLEX_ATTACH_SIZEPOLICY |MPLEX_ATTACH_PSEUDOMODAL); par.szplcy=SIZEPOLICY_FULL_EXACT; sp=mplex_do_attach_new(mplex, &par, create_scratchws, NULL); if(sp==NULL) warn(TR("Unable to create scratchpad.")); 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; return (inst_off<0 ? (strcmp(nm, SP_NAME)==0 || strcmp(nm, SPWS_NAME)==0) : (strncmp(nm, SP_NAME, inst_off)==0 || strncmp(nm, SPWS_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 * \codestr{set}, \codestr{unset}, or \codestr{toggle}. * The resulting status is returned. */ 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, res=FALSE; FOR_ALL_MANAGED_BY_MPLEX(mplex, reg, tmp){ if(is_scratchpad(reg)){ res=!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); res=found; } return res; } /*EXTL_DOC * Toggle displayed status of \var{sp}. * The parameter \var{how} is one of * \codestr{set}, \codestr{unset}, or \codestr{toggle}. * The resulting status is returned. */ 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; } /*}}}*/ notion-3+2012042300/mod_sp/main.h000066400000000000000000000006351174530661200161440ustar00rootroot00000000000000/* * ion/mod_sp/main.h * * Copyright (c) Tuomo Valkonen 2004-2009. * * See the included file LICENSE for details. */ #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 */ notion-3+2012042300/mod_statusbar/000077500000000000000000000000001174530661200164315ustar00rootroot00000000000000notion-3+2012042300/mod_statusbar/Makefile000066400000000000000000000010721174530661200200710ustar00rootroot00000000000000## ## Notion 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 statusd-launch.c MAKE_EXPORTS = mod_statusbar MODULE = mod_statusbar MODULE_STUB = mod_statusbar.lua ###################################### include $(TOPDIR)/build/rules.mk ###################################### _install: module_install notion-3+2012042300/mod_statusbar/draw.c000066400000000000000000000077331174530661200175440ustar00rootroot00000000000000/* * ion/mod_statusbar/draw.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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, 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_set_attr(brush, elems->attr); grbrush_set_attr(brush, elems->meter); grbrush_draw_string(brush, elems->x, ty, s, strlen(s), needfill); grbrush_unset_attr(brush, elems->meter); grbrush_unset_attr(brush, elems->attr); 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); 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, complete); grbrush_end(sb->brush); } notion-3+2012042300/mod_statusbar/draw.h000066400000000000000000000006171174530661200175430ustar00rootroot00000000000000/* * ion/mod_statusbar/draw.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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 */ notion-3+2012042300/mod_statusbar/main.c000066400000000000000000000061671174530661200175330ustar00rootroot00000000000000/* * ion/mod_statusbar/main.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #include #include #include #include #include #include #include "statusbar.h" #include "exports.h" /*{{{ Module information */ #include "../version.h" char mod_statusbar_ion_api_version[]=NOTION_API_VERSION; /*}}}*/ /*{{{ Bindmaps */ WBindmap *mod_statusbar_statusbar_bindmap=NULL; /*}}}*/ /*{{{ 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_PRIORITY_NONE); } /*}}}*/ /*{{{ 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; } /*}}}*/ notion-3+2012042300/mod_statusbar/main.h000066400000000000000000000006061174530661200175300ustar00rootroot00000000000000/* * ion/mod_statusbar/main.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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 */ notion-3+2012042300/mod_statusbar/mod_statusbar.lua000066400000000000000000000231711174530661200220070ustar00rootroot00000000000000-- -- ion/mod_statusbar/mod_statusbar.lua -- -- Copyright (c) Tuomo Valkonen 2004-2009. -- -- See the included file LICENSE for details. -- -- 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 function mod_statusbar.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 \file{ion-statusd} with configuration -- table \var{cfg}. The options for each \file{ion-statusd} monitor -- script should be contained in the corresponding sub-table of \var{cfg}. function mod_statusbar.launch_statusd(cfg) if statusd_pid>0 then return end -- Launch tried, don't do it automatically after reading -- configuration. mod_statusbar.no_autolaunch=true local mods=mod_statusbar.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_script="ion-statusd" local statusd=ioncore.lookup_script(statusd_script) if not statusd then ioncore.warn(TR("Could not find %s", statusd_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.." -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. The possible parameters in the -- table \var{param} are: -- -- \begin{tabularx}{\linewidth}{llX} -- Variable & Type & Description \\ -- \var{template} & string & The template; see -- Section \ref{sec:statusbar}. \\ -- \var{pos} & string & Position: \codestr{tl}, \codestr{tr}, -- \codestr{bl} or \codestr{br} -- (for the obvious combinations of -- top/left/bottom/right). \\ -- \var{screen} & integer & Screen number to create the statusbar on. \\ -- \var{fullsize} & boolean & If set, the statusbar will waste -- space instead of adapting to layout. \\ -- \var{systray} & boolaen & Swallow (KDE protocol) systray icons. \\ -- \end{tabularx} -- 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 not mod_statusbar.no_autolaunch then mod_statusbar.launch_statusd() end notion-3+2012042300/mod_statusbar/statusbar.c000066400000000000000000000611711174530661200206130ustar00rootroot00000000000000/* * ion/mod_statusbar/statusbar.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #include #include #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, "WStatusBar")) 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); 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->text_w=0; el->text=NULL; el->max_w=0; el->tmpl=NULL; el->meter=STRINGID_NONE; el->attr=STRINGID_NONE; el->stretch=0; el->align=WSBELEM_ALIGN_CENTER; el->zeropad=0; el->x=0; el->traywins=NULL; } static bool gets_stringstore(ExtlTab t, const char *str, StringId *id) { char *s; if(extl_table_gets_s(t, str, &s)){ *id=stringstore_alloc(s); free(s); return (*id!=STRINGID_NONE); } return FALSE; } 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=INT_MAX;/*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++){ const char *meter; if(sb->elems[i].type!=WSBELEM_SYSTRAY) continue; meter=stringstore_get(sb->elems[i].meter); if(meter==NULL){ fbel=&sb->elems[i]; continue; } if(name!=NULL && strcmp(meter, name)==0){ el=&sb->elems[i]; break; } if(strcmp(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 priority) { if(!MANAGE_PRIORITY_OK(priority, MANAGE_PRIORITY_LOW)) 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); if(!window_fitrep(&(sb->wwin), par, fp)) return FALSE; 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_PRIORITY_NONE); 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", stringstore_get(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++){ const char *meter; el=&(sb->elems[i]); if(el->type!=WSBELEM_METER) continue; if(el->text!=NULL){ free(el->text); el->text=NULL; } if(el->attr!=GRATTR_NONE){ stringstore_free(el->attr); el->attr=GRATTR_NONE; } meter=stringstore_get(el->meter); if(meter!=NULL){ const char *str; char *attrnm; extl_table_gets_s(t, 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(meter, "_hint"); if(attrnm!=NULL){ char *s; if(extl_table_gets_s(t, attrnm, &s)){ el->attr=stringstore_alloc(s); free(s); } 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 * \codestr{set}, \codestr{unset}, or \codestr{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_AS(WStatusBar, is_systray) 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); /*}}}*/ notion-3+2012042300/mod_statusbar/statusbar.h000066400000000000000000000041631174530661200206160ustar00rootroot00000000000000/* * ion/mod_statusbar/statusbar.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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; int max_w; char *tmpl; StringId meter; StringId 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 */ notion-3+2012042300/mod_statusbar/statusd-launch.c000066400000000000000000000104131174530661200215330ustar00rootroot00000000000000/* * ion/mod_statusbar/statusd-launch.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "statusbar.h" #define CF_STATUSD_TIMEOUT_SEC 3 #define BL 1024 #define USEC 1000000 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; } 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; if(mainloop_gettime(&endtime)!=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(mainloop_gettime(&now)!=0){ warn_err(); return FALSE; } } return TRUE; timeout: /* Just complain to stderr, not startup error log, and do not fail. * The system might just be a bit slow. We can continue, but without * initial values for the meters, geometry adjustments may be necessary * when we finally get that information. */ ioncore_warn_nolog(TR("ion-statusd timed out.")); return TRUE; } 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; } notion-3+2012042300/mod_tiling/000077500000000000000000000000001174530661200157075ustar00rootroot00000000000000notion-3+2012042300/mod_tiling/Makefile000066400000000000000000000011301174530661200173420ustar00rootroot00000000000000## ## Notion 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 MODULE_STUB=mod_tiling.lua ###################################### include $(TOPDIR)/build/rules.mk ###################################### _install: module_install notion-3+2012042300/mod_tiling/main.c000066400000000000000000000050321174530661200167770ustar00rootroot00000000000000/* * ion/mod_tiling/main.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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[]=NOTION_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; } /*}}}*/ notion-3+2012042300/mod_tiling/main.h000066400000000000000000000007311174530661200170050ustar00rootroot00000000000000/* * ion/mod_tiling/main.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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 */ notion-3+2012042300/mod_tiling/mod_tiling.lua000066400000000000000000000017551174530661200205470ustar00rootroot00000000000000-- -- ion/mod_menu/mod_tiling.lua -- Tiling module stub loader -- -- Copyright (c) Tuomo Valkonen 2004-2009. -- -- 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_tiling"] then return end if not ioncore.load_module("mod_tiling") then return end -- Change default layout if ioncore.getlayout("default")==ioncore.getlayout("empty") then ioncore.deflayout("default", { managed = { { type = "WTiling", bottom = true, } } }) end -- Mark ourselves loaded. package.loaded["mod_tiling"]=true notion-3+2012042300/mod_tiling/ops.c000066400000000000000000000073351174530661200166640ustar00rootroot00000000000000/* * ion/mod_tiling/ops.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #include #include #include #include #include #include #include "tiling.h" /*{{{ mkbottom */ static WRegion *mkbottom_fn(WWindow *parent, const WFitParams *fp, void *param) { WRegion *reg=(WRegion*)param, *res; WRegionAttachData data; WTiling *tiling; WFitParams fp2; fp2.mode=REGION_FIT_EXACT; fp2.g=fp->g; tiling=create_tiling(parent, &fp2, NULL, FALSE); if(tiling==NULL) return NULL; data.type=REGION_ATTACH_REPARENT; data.u.reg=reg; /* Warning! Potentially dangerous call to remove a `reg` from the same * group we're being attached to, and from the attach routine of which * this function is called from! */ res=region_attach_helper((WRegion*)tiling, parent, &fp2, (WRegionDoAttachFn*)tiling_do_attach_initial, NULL, &data); if(res==NULL){ destroy_obj((Obj*)tiling); return NULL; } return (WRegion*)tiling; } /*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; if(grp==NULL){ warn(TR("Not member of a group")); return FALSE; } if(group_bottom(grp)!=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; /* See the "Warning!" above. */ return (group_do_attach(grp, &ap, &data)!=NULL); } /*}}}*/ /*{{{ untile */ /*EXTL_DOC * If \var{tiling} is managed by some group, float the frames in * the tiling in that group, and dispose of \var{tiling}. */ EXTL_EXPORT bool mod_tiling_untile(WTiling *tiling) { WGroup *grp=REGION_MANAGER_CHK(tiling, WGroup); WGroupAttachParams param=GROUPATTACHPARAMS_INIT; WTilingIterTmp tmp; WRegion *reg, *reg2; if(grp==NULL){ warn(TR("Not member of a group")); return FALSE; } if(group_bottom(grp)==(WRegion*)tiling) group_set_bottom(grp, NULL); /* Setting `batchop` will stop `tiling_managed_remove` from * resizing remaining frames into freed space. It will also * stop the tiling from being destroyed by actions of * `tiling_managed_disposeroot`. */ tiling->batchop=TRUE; FOR_ALL_MANAGED_BY_TILING(reg, tiling, tmp){ WRegionAttachData data; /* Don't bother with the status display */ if(reg==TILING_STDISP_OF(tiling)) continue; /* Don't bother with regions containing no client windows. */ if(!region_rescue_needed(reg)) continue; data.type=REGION_ATTACH_REPARENT; data.u.reg=reg; param.geom_set=TRUE; param.geom=REGION_GEOM(reg); reg2=group_do_attach(grp, ¶m, &data); if(reg2==NULL) warn(TR("Unable to move a region from tiling to group.")); } tiling->batchop=FALSE; region_dispose((WRegion*)tiling); return TRUE; } /*}}}*/ notion-3+2012042300/mod_tiling/panehandle.c000066400000000000000000000050441174530661200201550ustar00rootroot00000000000000/* * ion/mod_tiling/panehandle.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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, "WPanelHandle")) return FALSE; 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, 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); /*}}}*/ notion-3+2012042300/mod_tiling/panehandle.h000066400000000000000000000012461174530661200201620ustar00rootroot00000000000000/* * ion/mod_tiling/panehandle.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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 */ notion-3+2012042300/mod_tiling/placement.c000066400000000000000000000046651174530661200200360ustar00rootroot00000000000000/* * ion/mod_tiling/placement.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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 priority) { int cpriority=MANAGE_PRIORITY_SUBX(priority, MANAGE_PRIORITY_NORMAL); WRegion *target=NULL; WTilingPlacementParams param; WPHolder *ph; bool ret; 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, cpriority); 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, cpriority); } notion-3+2012042300/mod_tiling/placement.h000066400000000000000000000014261174530661200200330ustar00rootroot00000000000000/* * ion/mod_tiling/placement.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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 WTilingPlacementParams* 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 */ notion-3+2012042300/mod_tiling/split-stdisp.c000066400000000000000000000426051174530661200205210ustar00rootroot00000000000000/* * ion/mod_tiling/split-stdisp.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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; } /*}}}*/ notion-3+2012042300/mod_tiling/split-stdisp.h000066400000000000000000000011261174530661200205170ustar00rootroot00000000000000/* * ion/mod_tiling/split-stdisp.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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 */ notion-3+2012042300/mod_tiling/split.c000066400000000000000000001355011174530661200172130ustar00rootroot00000000000000/* * ion/mod_tiling/split.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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 WSplitST *splittree_scan_stdisp(WSplit *node_, bool set_saw) { WSplitST *r=NULL; WSplitSplit *node=OBJ_CAST(node_, WSplitSplit); if(node==NULL) return NULL; r=OBJ_CAST(node->tl, WSplitST); if(r==NULL) r=OBJ_CAST(node->br, WSplitST); if(r!=NULL){ if(set_saw) saw_stdisp=r; return r; } r=splittree_scan_stdisp(node->tl, set_saw); if(r==NULL) r=splittree_scan_stdisp(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 *dodge_stdisp(WSplit *node, bool keep_within) { WSplitST *stdisp; WSplitSplit *stdispp; stdisp=splittree_scan_stdisp(node, TRUE); if(stdisp==NULL) return node; stdispp=OBJ_CAST(((WSplit*)stdisp)->parent, WSplitSplit); if(stdispp==NULL) return node; if((WSplit*)stdispp==node){ /* Node itself immediately contains stdisp. Due to the way * try_unsink works, stdisp this will not change, so another * node must be used, if we want to fully dodge stdisp. */ return (keep_within ? node : (stdispp->tl==(WSplit*)stdisp ? stdispp->br : stdispp->tl)); } do{ if(!split_try_unsink_stdisp(stdispp, FALSE, TRUE)){ warn(TR("Unable to move the status display out of way.")); return NULL; } }while(stdispp->tl!=node && stdispp->br!=node); return node; } /*}}}*/ /*{{{ 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); splittree_begin_resize(); node=dodge_stdisp(node, FALSE); if(node==NULL) return NULL; if(OBJ_IS(node, WSplitST)){ warn(TR("Splitting the status display is not allowed.")); 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; int hprimn=PRIMN_ANY, vprimn=PRIMN_ANY; assert(node->tl==child || node->br==child); if(node->tl==child){ other=node->br; if(node->dir==SPLIT_VERTICAL) vprimn=PRIMN_TL; else hprimn=PRIMN_TL; }else{ other=node->tl; if(node->dir==SPLIT_VERTICAL) vprimn=PRIMN_BR; else hprimn=PRIMN_BR; } 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), hprimn, vprimn); 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; } /* Note: both hprimn and vprimn are inverted when descending. Therefore * one should be either PRIMN_NONE or PRIMN_ANY for sensible geometric * navigation. (Both are PRIMN_TL or PRIMN_BR for pseudo-linear * next/previous navigation.) */ 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; else if(node->br==child && (primn==PRIMN_TL || primn==PRIMN_ANY)) split=node->tl; if(split!=NULL){ nnode=split_current_todir(split, primn_none2any(primn_invert(hprimn)), primn_none2any(primn_invert(vprimn)), filter); } if(nnode==NULL) nnode=split_nextto((WSplit*)node, 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{split}. */ EXTL_EXPORT_MEMBER void splitsplit_flip(WSplitSplit *split) { splittree_begin_resize(); split=OBJ_CAST(dodge_stdisp((WSplit*)split, FALSE), WSplitSplit); if(split==NULL) 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=dodge_stdisp(node, TRUE); if(node==NULL || node2!=node) return FALSE; split_update_bounds(node, TRUE); split_do_rqgeom_(node, geom, PRIMN_ANY, PRIMN_ANY, &rg, FALSE); split_do_resize(node, &rg, PRIMN_ANY, PRIMN_ANY, trans); if(flip!=FLIP_NONE) splittree_flip_dir(node, 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 \codestr{vertical} or * \codestr{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); /*}}}*/ notion-3+2012042300/mod_tiling/split.h000066400000000000000000000140621174530661200172160ustar00rootroot00000000000000/* * ion/mod_tiling/split.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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 */ notion-3+2012042300/mod_tiling/splitfloat.c000066400000000000000000000642261174530661200202460ustar00rootroot00000000000000/* * ion/mod_tiling/splitext.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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; } ((WRegion*)split->brpwin)->flags|=REGION_SKIP_FOCUS; ((WRegion*)split->tlpwin)->flags|=REGION_SKIP_FOCUS; 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); /*}}}*/ notion-3+2012042300/mod_tiling/splitfloat.h000066400000000000000000000032411174530661200202410ustar00rootroot00000000000000/* * ion/mod_tiling/splitfloat.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #ifndef ION_MOD_TILING_SPLITFLOAT_H #define ION_MOD_TILING_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_MOD_TILING_SPLITFLOAT_H */ notion-3+2012042300/mod_tiling/tiling.c000066400000000000000000001226131174530661200173460ustar00rootroot00000000000000/* * ion/mod_tiling/tiling.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #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 "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 */ 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, CurrentTime); } 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; } /*}}}*/ /*{{{ 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; } stdisp->flags|=REGION_SKIP_FOCUS; 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(TILING_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){ if(framemode_unalt(frame_mode(frame))!=FRAME_MODE_TILED) 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; } bool tiling_do_attach_initial(WTiling *ws, WRegion *reg) { assert(ws->split_tree==NULL); ws->split_tree=(WSplit*)create_splitregion(®ION_GEOM(reg), reg); if(ws->split_tree==NULL) return FALSE; ws->split_tree->ws_if_root=ws; if(!tiling_managed_add(ws, reg)){ destroy_obj((Obj*)ws->split_tree); ws->split_tree=NULL; return FALSE; } return TRUE; } 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) { const char *p[1]; 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->batchop=FALSE; 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; p[0] = "Notion WTiling dummy window"; xwindow_set_text_property(ws->dummywin, XA_WM_NAME, p, 1); region_init(&(ws->reg), parent, fp); ws->reg.flags|=(REGION_GRAB_ON_PARENT| REGION_PLEASE_WARP); if(ci){ WRegionAttachData data; WRegion *res; data.type=REGION_ATTACH_NEW; data.u.n.fn=(WRegionCreateFn*)ws->create_frame_fn; data.u.n.param=NULL; res=region_attach_helper((WRegion*)ws, parent, fp, (WRegionDoAttachFn*)tiling_do_attach_initial, NULL, &data); if(res==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)); } WRegion *tiling_managed_disposeroot(WTiling *ws, WRegion *reg) { WTilingIterTmp tmp; WRegion *mgd; if(ws->batchop) return reg; FOR_ALL_MANAGED_BY_TILING(mgd, ws, tmp){ if(mgd!=TILING_STDISP_OF(ws) && mgd!=reg) return reg; } return region_disposeroot((WRegion*)ws); } bool tiling_rescue_clientwins(WTiling *ws, WRescueInfo *info) { WTilingIterTmp tmp; ptrlist_iter_init(&tmp, ws->managed_list); return region_rescue_some_clientwins((WRegion*)ws, info, (WRegionIterator*)ptrlist_iter, &tmp); } void tiling_do_managed_remove(WTiling *ws, WRegion *reg) { if(TILING_STDISP_OF(ws)==reg){ ws->stdispnode->regnode.reg=NULL; }else{ ptrlist_remove(&(ws->managed_list), reg); } region_unset_manager(reg, (WRegion*)ws); splittree_set_node_of(reg, NULL); } static bool nostdispfilter(WSplit *node) { return (OBJ_IS(node, WSplitRegion) && !OBJ_IS(node, WSplitST)); } void tiling_managed_remove(WTiling *ws, WRegion *reg) { bool act=REGION_IS_ACTIVE(reg); bool mcf=region_may_control_focus((WRegion*)ws); WSplitRegion *node=get_node_check(ws, reg); bool norestore=(OBJ_IS_BEING_DESTROYED(ws) || ws->batchop); WRegion *other=NULL; if(!norestore) 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){ bool reused=FALSE; if(other==NULL && !norestore){ WWindow *par=REGION_PARENT(ws); WFitParams fp; assert(par!=NULL); fp.g=node->split.geom; fp.mode=REGION_FIT_EXACT; other=(ws->create_frame_fn)(par, &fp); if(other!=NULL){ node->reg=other; splittree_set_node_of(other, node); tiling_managed_add(ws, other); reused=TRUE; }else{ warn(TR("Tiling in useless state.")); } } if(!reused) splittree_remove((WSplit*)node, (!norestore && other!=NULL)); } if(!norestore && other!=NULL && 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; } return newframe; } /*EXTL_DOC * Create a new frame on \var{ws} \codestr{above}, \codestr{below} * \codestr{left} of, or \codestr{right} of \var{node} as indicated * by \var{dirstr}. If \var{dirstr} is prefixed with * \codestr{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 \codestr{left}, \codestr{right}, \codestr{top} or * \codestr{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 \codestr{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{reg} to another frame * and, if possible, destroy it. */ EXTL_EXPORT_MEMBER void tiling_unsplit_at(WTiling *ws, WRegion *reg) { WPHolder *ph; if(reg==NULL || REGION_MANAGER(reg)!=(WRegion*)ws) return; ph=region_get_rescue_pholder_for((WRegion*)ws, reg); if(ph!=NULL){ region_rescue(reg, ph, REGION_RESCUE_NODEEP|REGION_RESCUE_PHFLAGS_OK); destroy_obj((Obj*)ph); } region_defer_rqdispose(reg); } /*}}}*/ /*{{{ 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 * Iterate over managed regions of \var{ws} until \var{iterfn} returns * \code{false}. * The function is called in protected mode. * This routine returns \code{true} if it reaches the end of list * without this happening. */ EXTL_SAFE EXTL_EXPORT_MEMBER bool tiling_managed_i(WTiling *ws, ExtlFn iterfn) { PtrListIterTmp tmp; ptrlist_iter_init(&tmp, ws->managed_list); return extl_iter_objlist_(iterfn, (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} (\codestr{left}, \codestr{right}, \codestr{up}, * or \codestr{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} * (\codestr{left}, \codestr{right}, \codestr{up}, or \codestr{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} (\codestr{set}, \codestr{unset}, or \codestr{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} (\codestr{set}, \codestr{unset}, or * \codestr{toggle}) and \var{dirstr} (\codestr{left}, \codestr{right}, * \codestr{up}, or \codestr{down}). 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_disposeroot, (DynFun*)tiling_managed_disposeroot}, {(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}, END_DYNFUNTAB }; EXTL_EXPORT IMPLCLASS(WTiling, WRegion, tiling_deinit, tiling_dynfuntab); /*}}}*/ notion-3+2012042300/mod_tiling/tiling.h000066400000000000000000000115641174530661200173550ustar00rootroot00000000000000/* * ion/mod_tiling/tiling.h * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #ifndef ION_MOD_TILING_TILING_H #define ION_MOD_TILING_TILING_H #include #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; bool batchop; }; 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 bool tiling_do_attach_initial(WTiling *tiling, WRegion *reg); 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 void tiling_unsplit_at(WTiling *ws, WRegion *reg); 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 bool tiling_rescue_clientwins(WTiling *ws, WRescueInfo *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 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) /* Misc. */ #define TILING_STDISP_OF(WS) \ ((WS)->stdispnode!=NULL ? (WS)->stdispnode->regnode.reg : NULL) #endif /* ION_MOD_TILING_TILING_H */ notion-3+2012042300/mod_xinerama/000077500000000000000000000000001174530661200162255ustar00rootroot00000000000000notion-3+2012042300/mod_xinerama/LICENSE000066400000000000000000000634761174530661200172520ustar00rootroot00000000000000 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! notion-3+2012042300/mod_xinerama/Makefile000066400000000000000000000022151174530661200176650ustar00rootroot00000000000000## ## Ion xinerama module Makefile ## ## # System specific configuration is in system.mk TOPDIR=../ include $(TOPDIR)/build/system-inc.mk ###################################### INCLUDES += $(LIBTU_INCLUDES) $(LIBEXTL_INCLUDES) $(X11_INCLUDES) -I$(TOPDIR) CFLAGS += $(XOPEN_SOURCE) $(C99_SOURCE) DOCS=LICENSE README SOURCES=mod_xinerama.c MAKE_EXPORTS=mod_xinerama LIBS = $(X11_LIBS) -lXinerama MODULE=mod_xinerama MODULE_STUB=mod_xinerama.lua ###################################### include $(TOPDIR)/build/rules.mk ###################################### _install: module_install $(INSTALLDIR) $(DESTDIR)$(ETCDIR) for i in $(ETC); do \ $(INSTALL) -m $(DATA_MODE) $$i $(DESTDIR)$(ETCDIR); \ done ###################################### .PHONY: tarball tarball: $(SOURCES) $(ETC) $(DOCS) $(MODULE_STUB) Makefile sh -c 'BASENAME=ion-devel-$(MODULE)-`date -r \`ls -t $+ | head -n 1\` +%Y%m%d`; \ mkdir $$BASENAME; \ cp $+ $$BASENAME; \ tar -cvjf $$BASENAME.tar.bz2 $$BASENAME/*; \ rm -Rf $$BASENAME' .PHONY: test test: $(SOURCES) lua test_xinerama.lua ###################################### .PHONY: tags tags: exuberant-ctags -R . $(TOPDIR) notion-3+2012042300/mod_xinerama/README000066400000000000000000000063071174530661200171130ustar00rootroot00000000000000Notion Xinerama module * By Tomáš Ebenlendr and Arnout Engelen * Based on work by Thomas Themel * Based on the mod_xrandr skeleton INTRODUCTION This module provides multi-head support to Notion. It uses the Xinerama API, and thus can be used with any X Server that exposes functionality through this API, notably RandR (used by both ATI and nVidia) and TwinView (used by nVidia). (it is based on the module that gave ion3 its Xinerama support back that was killed on 20070117) INSTALLATION 1. Edit Makefile to ensure TOPDIR points to your top-level notion source directory with a system.mk that matches the version of notion installed on your system. 2. Run make. 3. Either run (as root) # make install or (as yourself), $ mkdir -p ~/.notion/lib $ cp .libs/mod_xinerama.{so,lc} ~/.notion/lib 4. Add dopath("mod_xinerama") to ~/.notion/cfg_notion.lua. See below for possible status bar issues. 5. (Re)start Ion. CONFIGURATION If you don't like current behaviour, you can set up screens differently, by placing following code in cfg_xinerama.lua: local screens = mod_xinerama.query_screens() if (screens) then -- -- now you can edit the 'screens' table in lua, -- -- e.g.: -- local merged_screens = mod_xinerama.merge_contained_screens(screens) -- -- or (current behaviour): -- local merged_screens = mod_xinerama.merge_overlapping_screens(screens) -- -- and finally setup the screens: mod_xinerama.setup_screens(merged_screens) end REFRESHING When the screen topology changes, the locations, sizes and mergings of windows may be recalculated. The Xinerama API does not support notifications of topology changes, but these might be received through other means, such as mod_xrandr. If you're satisfied with the default merge algorithm, you may simply call mod_xinerama.refresh(). If you want to use your own merging algorithm, you may call mod_xinerama.setup_screens(merged_screens) again. LIMITATIONS For some reason, loading the statusbar module _BEFORE_ the Xinerama module hides the status bar. To work around this, load mod_xinerama before loading the statusbar module. This does not contain the Sun Xinerama support that was in the original ion3 because I don't have a machine running Solaris ready. Adding it should be rather trivial with access to the original ion code and a Solaris machine. WRAPPING goto_next_scr/goto_prev_scr Without altering the notion source, it doesn't seem possible to get goto_next_scr/goto_prev_scr to properly wrap around on the last/first screen. This can be worked around in lua, though: function next_wrap() scr = ioncore.goto_next_screen() if obj_is(scr, "WRootWin") then ioncore.goto_nth_screen(0) end end function prev_wrap() scr = ioncore.goto_prev_screen() if obj_is(scr, "WRootWin") then ioncore.goto_nth_screen(-1) end end defbindings("WScreen", { bdoc("Go to next/previous screen on multihead setup."), kpress(META.."Shift+comma", "prev_wrap()"), kpress(META.."Shift+period", "next_wrap()"), }) notion-3+2012042300/mod_xinerama/exact-version000066400000000000000000000245641174530661200207520ustar00rootroot00000000000000commit 2ce512189cf5d9fb09cfe8bf644b4d980b6c9923 Author: Arnout Engelen Date: Sat Apr 21 00:52:10 2012 +0200 Update the list of virtual roots after refreshing the Xinerama screens https://sourceforge.net/tracker/index.php?func=detail&aid=3162974&group_id=314802&atid=1324528 commit b0c1f3df293cf70e948835edee974f9fad49bca7 Author: Arnout Engelen Date: Fri Apr 20 22:20:15 2012 +0200 Warnings about load order of xinerama and docks/statusbar no longer needed commit a17bf06ecf3e874cb1fb8eff1edeb1949ebd4b96 Author: Arnout Engelen Date: Fri Apr 20 21:24:27 2012 +0200 Remove hacks, because notion was fixed and screens can be properly resized now. Many thanks to AopicieR/Philipp Hartwig commit b8f25271766a46b93b8c0987653078ea7fabc861 Author: Arnout Engelen Date: Fri Apr 20 18:43:37 2012 +0200 'Ion'->'Notion' commit 4f6d465cf86a44cb10f5738aa9306d5443e74b19 Author: Arnout Engelen Date: Thu Nov 17 21:53:45 2011 +0100 Warn when loading mod_xinerama after mod_dock or mod_statusbar, which currently does not work well commit 19b8cc6783ac841ec31536df5c48e254eedf675a Author: Arnout Date: Thu Oct 20 18:20:10 2011 +0200 Support $(DESTDIR), thanks to Josef 'Jeff' Sipek commit ad5f3a13cf1bd6c0573e68b009a8541bac0274c4 Author: Arnout Date: Sun Sep 18 14:18:58 2011 +0200 Fit the mplex directly instead of using region_fit, due to #3349390 When we ever get to refactor Notion to make WRootWin not subclass Screen/WMPlex (so we can allow screens to be resized again), this code will no longer work, but yield an easy-to-fix compile-time error. commit 96b5ddacde5fad655a82d0a96fb023e49f544da7 Author: Arnout Date: Sun Aug 14 18:53:42 2011 +0200 Document refreshing the layout commit b58a22cdc7af96d6913fd1932634adb8baf5d4ef Author: Arnout Date: Sun Aug 14 18:43:53 2011 +0200 typo commit c0a0e47f5296b7171b0e3abba2e83cff1cff09e0 Author: Arnout Date: Sun Aug 14 18:40:31 2011 +0200 .gitignore commit 125cc8b41ec9322ed3dff13d8b6c575205847b54 Author: Arnout Date: Sun Aug 14 18:39:39 2011 +0200 Update documentation commit 089e89d7a7296ee229b73e8f49600d7d98c350d3 Author: Arnout Engelen Date: Thu May 5 22:03:03 2011 +0200 Default TOPDIR is ../notion commit e4d20d9109d129a347e8238ccaab66713ab9e08d Author: Arnout Engelen Date: Thu May 5 22:02:41 2011 +0200 'test'-target commit bbd439bd50ec9d2a3bd3e1f7012e1b1a42dba9ce Author: Arnout Date: Thu May 5 11:32:56 2011 +0200 Use 'region_fit' instead of 'region_fitrep', as we're not reparenting anyway commit ca27abeb2de89dea5ea681d7c19faf10624ed6c8 Author: Arnout Date: Thu May 5 11:29:37 2011 +0200 Move invisible WScreen's outside the visible area commit 6549d2631e630ab5575308759b437163094ec5e1 Author: Arnout Date: Wed May 4 00:30:35 2011 +0200 Create and then resize the WScreens This works around the fact that WMPlexes, like WRootWin, will try to resize their children to be of the same size as themselves. Perhaps WRootWin should not be a WMPlex? commit 9d831ab5d27939cb71dc247838f9a2a2649cda40 Author: Arnout Date: Mon May 2 23:29:06 2011 +0200 allow mod_xinerama layouting to be called multiple times. - Existing WScreens should be updated to the new desired dimensions - New WScreens should be created when extra screens are added - The usecase where screens disappear is not yet handled It appears mod_xinerama_update_screen is called with the right parameters, but my WScreens aren't actually resizing yet :( commit bc3d399c29747794e713364dee2112febd4bb602 Author: Arnout Date: Mon May 2 22:07:38 2011 +0200 Port mod_xinerama_setup_screens to lua commit 6365e885e00d3b4f6e066fc3ed21c2324474bf07 Author: Arnout Date: Mon May 2 21:49:06 2011 +0200 Refactor to reduce function size and expose mod_xinerama.setup_new_screen to lua commit 13dfe84bae98199bc5e59b4994339c108fef566e Author: Arnout Date: Mon May 2 21:36:30 2011 +0200 convert mod_xinerama.setup_screens_once to lua commit b0b01c59a15744d98fff23437e875db8952c3d1b Author: Tomáš Ebenlendr Date: Sun Apr 17 20:16:45 2011 +0200 Rewrite of the merging algorithms. * Better readability of all three algorithms. * Move merge_overlapping_screens to merge_overlapping_screens_alternate, as WScreens with default layout won't behave nice with this algorithm. * New merge_overlapping_screens, that outputs set of rectangles which do not overlap. * Rewrite of test: specify what we exactly expect to output. commit fcd1251cd84eb744a6500d27ccd31c5381f7e31d Author: Tomáš Ebenlendr Date: Sun Apr 17 12:29:45 2011 +0200 Fix merge_overlapping_screens. Bug was introduced by stabilising order in the merge. commit 75ac185adaae877e044c7081364728a5cee01417 Author: Tomáš Ebenlendr Date: Sun Apr 17 12:28:06 2011 +0200 Accept tables with hiles in numbering. Use logic that is consitent with other parts of notion. commit 21f5aab424d89c79fa6c6902862d11565fe4ad97 Author: Arnout Engelen Date: Sat Apr 16 16:26:02 2011 +0200 regressiontests for the xinerama lua logic currently shows a crash :) commit 3fe1c89af0ec14fdb9504ff74a8c0104433fa47f Author: Tomáš Ebenlendr Date: Mon Apr 11 22:18:18 2011 +0200 Documentation of lua function. (I have not tried to generate the documentation for the module yet, just documented the lua functions.) commit 6131eab9ce15555b90b10cb9add819d7f7f88d0c Author: Tomáš Ebenlendr Date: Mon Apr 11 07:59:38 2011 +0200 Stabilize order when merging overlapping screens. The order of overlapping screens is now defined by the first screen in the merged set. commit d4858d56b257c5168f95c2ffa5a9a987b9e64bce Author: Tomáš Ebenlendr Date: Sun Apr 10 21:08:12 2011 +0200 Documentation fix commit bd6b4f5f0a9bcf023881755b8cbdcd0823ee962e Author: Tomáš Ebenlendr Date: Sun Apr 10 20:55:18 2011 +0200 Overlapping screens patch (re)implemented in Lua. commit f3e3dced9eb5ced88cc1aeebe21f9ef1094bba8c Author: Tomáš Ebenlendr Date: Sun Apr 10 18:57:15 2011 +0200 Fix numbering of tables passed from and to lua. commit 8f76b932acb4781d14aced47ff611066cd98e983 Author: Tomáš Ebenlendr Date: Sun Apr 10 17:39:29 2011 +0200 ion names for coordinates in rectangles commit 98b24d51054288274f7f7a89a0670b6d31408380 Author: Tomáš Ebenlendr Date: Sun Apr 10 17:32:58 2011 +0200 removed partially resolved FIXME commit e6da9d6df11b9bb0c3fed528d5b2866a27980931 Author: Tomáš Ebenlendr Date: Sun Apr 10 16:17:09 2011 +0200 Funcionality split, callable by lua. The user may setup screens as he wants from lua. This version can setup screens only once, moreover it has to be done before mod_xrandr loads. commit d07e03bbd3fb79cea90462ed94127b3f6e6d5c8e Author: Tomáš Ebenlendr Date: Sun Apr 10 15:11:50 2011 +0200 Makefile tarball fix Let the documentation be included in tarball. Also prepare makefile for MODULE_STUB - lua part of the module. commit 3d51a65219c60e4f916f1666acfd80979c58dd39 Author: Thomas Themel Date: Tue Sep 23 09:38:49 2008 +0200 better-lua-wrap darcs-hash:20080923073849-06ad4-9ee6e62c8f054c7f1a6f185a1849216bbf462d09.gz commit fb10caaeba1bedfe25eae49bbec622512fbd1612 Author: Thomas Themel Date: Tue Sep 23 09:14:20 2008 +0200 merge-crashfix-patch darcs-hash:20080923071420-06ad4-983a2bd3bbb57be9063748c15e3f8175b0741171.gz commit 887cbe92fe8971c6c354be008f93a5061f501b16 Author: Thomas Themel Date: Tue Sep 23 09:09:07 2008 +0200 fix-wrapping darcs-hash:20080923070907-06ad4-ace85dc1725594551d01f1e737ba0f5912655164.gz commit e22f353e05b02075562d70fc2dbdd8c00dfef420 Author: talex5 Date: Thu Jul 19 18:56:04 2007 +0200 Don't fail to start if Xinerama isn't available If the Xinerama module was used when Xinerama wasn't available then it prevented Ion from starting correctly. This is a problem for e.g. a laptop which sometimes has an external monitor attached. With this patch, Ion still starts correctly. darcs-hash:20070719165604-072e0-17e1a0cebad1c70d52c517ca8ebceda55b72f12c.gz commit d4aa0d36222aece5fee6e7ea165cd7a8f8c5c15f Author: Thomas Themel Date: Wed Jun 27 09:15:42 2007 +0200 fix-wrap-doc darcs-hash:20070627071542-06ad4-c0e691a8e072816045da6bf7ebcd60d4e12b1e79.gz commit a0742d1cdd8f9482a48336049802fa8571b23380 Author: Thomas Themel Date: Wed Jun 27 09:11:50 2007 +0200 fix-screen-wrap Make ioncore.goto_next_screen and ioncore.goto_prev_screen wrap around (by removing the pseudo screen that tuomov creates from ioncore_g.screens and only putting Xinerama screens there) darcs-hash:20070627071150-06ad4-b611278d066f418f2fdb827bcbe3b4da1882eaac.gz commit 59e263391868c452992d2d07daf4747be5045a07 Author: Thomas Themel Date: Mon Apr 30 21:16:59 2007 +0200 email-typo-try2 darcs-hash:20070430191659-3d077-35c02285593353b4e5f701bcd4ac9d9de9fab0a8.gz commit af5421f8c1c932829f44f09472ecfd538cfb71ab Author: Thomas Themel Date: Mon Apr 30 20:43:40 2007 +0200 readme-typo-email darcs-hash:20070430184340-06ad4-f58e65c01b0876c27c9fa93e42c11377f0d8437a.gz commit 46e065f400ce4f7e62dd464dc629a23ae4836fb7 Author: Thomas Themel Date: Mon Apr 9 23:12:03 2007 +0200 doc-fix-statusbar darcs-hash:20070409211203-06ad4-54d52551dbedc9de549d4c8b54b0f71b8357409e.gz commit a5ed02849e955a495639f717e7170c017a7da9be Author: Thomas Themel Date: Mon Apr 9 22:06:24 2007 +0200 init-all darcs-hash:20070409200624-06ad4-d044bc705e73d8d9284a0d9cbac8a783c604e2f7.gz notion-3+2012042300/mod_xinerama/mod_xinerama.c000066400000000000000000000114601174530661200210360ustar00rootroot00000000000000/* * Notion xinerama module * based on mod_xrandr by Ragnar Rova and Tuomo Valkonen * * by Thomas Themel * * splitted to read and setup part callable by lua * by Tomas Ebenlendr * * 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 * */ #include #include #include #include #include #include #include #include #include "exports.h" #ifdef MOD_XINERAMA_DEBUG #include #endif char mod_xinerama_ion_api_version[]=ION_API_VERSION; /* {{{ the actual xinerama query */ static int xinerama_event_base; static int xinerama_error_base; static bool xinerama_ready = FALSE; /*EXTL_DOC * Queries the Xinerama extension for screen configuration. * * Example output: \{\{x=0,y=0,w=1024,h=768\},\{x=1024,y=0,w=1280,h=1024\}\} */ EXTL_SAFE EXTL_EXPORT ExtlTab mod_xinerama_query_screens() { XineramaScreenInfo* sInfo; int nRects,i; if (xinerama_ready) { ExtlTab ret = extl_create_table(); sInfo = XineramaQueryScreens(ioncore_g.dpy, &nRects); if(!sInfo) return ret; for(i = 0 ; i < nRects ; ++i) { ExtlTab rect = extl_create_table(); extl_table_sets_i(rect,"x",sInfo[i].x_org); extl_table_sets_i(rect,"y",sInfo[i].y_org); extl_table_sets_i(rect,"w",sInfo[i].width); extl_table_sets_i(rect,"h",sInfo[i].height); extl_table_seti_t(ret,i+1,rect); } XFree(sInfo); return ret; } return extl_table_none(); } /*EXTL_DOC * Queries Notion for screen location and dimensions. * * Example output: \{x=0,y=0,w=1024,h=768\} */ EXTL_SAFE EXTL_EXPORT ExtlTab mod_xinerama_get_screen_dimensions(WScreen *screen) { ExtlTab rect = extl_create_table(); extl_table_sets_i(rect,"x",REGION_GEOM(screen).x); extl_table_sets_i(rect,"y",REGION_GEOM(screen).y); extl_table_sets_i(rect,"w",REGION_GEOM(screen).w); extl_table_sets_i(rect,"h",REGION_GEOM(screen).h); return rect; } /* }}} */ /* {{{ Controlling notion screens from lua */ /* * Updates WFitParams based on the lua parameters * * @param screen dimensions (x/y/w/h) */ static void convert_parameters(ExtlTab screen, WFitParams *fp) { WRectangle *g = &(fp->g); extl_table_gets_i(screen,"x",&(g->x)); extl_table_gets_i(screen,"y",&(g->y)); extl_table_gets_i(screen,"w",&(g->w)); extl_table_gets_i(screen,"h",&(g->h)); fp->mode=REGION_FIT_EXACT; fp->gravity=ForgetGravity; } /* Set up one new screen * @param screen the screen to update * @param dimensions the new dimensions (x/y/w/h) */ EXTL_EXPORT bool mod_xinerama_update_screen(WScreen *screen, ExtlTab dimensions) { WFitParams fp; convert_parameters(dimensions, &fp); #ifdef MOD_XINERAMA_DEBUG printf("Updating rectangle #%d: x=%d y=%d width=%u height=%u\n", screen->id, fp.g.x, fp.g.y, fp.g.w, fp.g.h); #endif region_fitrep((WRegion*)screen, NULL, &fp); return TRUE; } /* Set up one new screen * @param screen dimensions (x/y/w/h) * @returns true on success, false on failure */ EXTL_EXPORT bool mod_xinerama_setup_new_screen(int screen_id, ExtlTab screen) { WRootWin* rootWin = ioncore_g.rootwins; WScreen* newScreen; WFitParams fp; convert_parameters(screen, &fp); newScreen = create_screen(rootWin, &fp, screen_id); if(newScreen == NULL) { warn(TR("Unable to create Xinerama workspace %d."), screen_id); return FALSE; } region_set_manager((WRegion*)newScreen, (WRegion*)rootWin); region_map((WRegion*)newScreen); return mod_xinerama_update_screen(newScreen, screen); } /* }}} */ /* {{{ Module init and deinit */ bool mod_xinerama_init() { xinerama_ready = XineramaQueryExtension(ioncore_g.dpy,&xinerama_event_base, &xinerama_error_base); if (!xinerama_ready) warn(TR("No Xinerama support detected, mod_xinerama won't do anything.")); return mod_xinerama_register_exports(); } void mod_xinerama_deinit() { mod_xinerama_unregister_exports(); } /* }}} */ notion-3+2012042300/mod_xinerama/mod_xinerama.lua000066400000000000000000000276341174530661200214070ustar00rootroot00000000000000-- Ion xinerama module - lua setup -- -- by Tomas Ebenlendr -- -- 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 -- -- 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_xinerama"] then return end if not ioncore.load_module("mod_xinerama") then return end local mod_xinerama=_G["mod_xinerama"] assert(mod_xinerama) -- Helper functions {{{ local function max(one, other) if one == nil then return other end if other == nil then return one end return (one > other) and one or other end -- creates new table, converts {x,y,w,h} representation to {x,y,xmax,ymax} local function to_max_representation(screen) return { x = screen.x, y = screen.y, xmax = screen.x + screen.w, ymax = screen.y + screen.h } end -- edits passed table, converts representation {x,y,xmax,ymax} to {x,y,w,h}, -- and sorts table of indices (entry screen.ids) local function fix_representation(screen) screen.w = screen.xmax - screen.x screen.h = screen.ymax - screen.y screen.xmax = nil screen.ymax = nil table.sort(screen.ids) end local function fix_representations(screens) for _k, screen in pairs(screens) do fix_representation(screen) end end -- }}} -- Contained screens {{{ -- true if [from1, to1] contains [from2, to2] local function contains(from1, to1, from2, to2) return (from1 <= from2) and (to1 >= to2) end -- true if scr1 contains scr2 local function screen_contains(scr1, scr2) local x_in = contains(scr1.x, scr1.xmax, scr2.x, scr2.xmax) local y_in = contains(scr1.y, scr1.ymax, scr2.y, scr2.ymax) return x_in and y_in end --DOC -- Filters out fully contained screens. I.e. it merges two screens -- if one is fully contained in the other screen (this contains the -- case that both screens are of the same geometry). -- The output screens also contain field ids containing the numbers -- of merged screens. The order of the screens is defined by the -- first screen in the merged set. (I.e., having big B, and big C, -- showing different parts of desktop and small A (primary) showing -- part of C, then the order will be C,B and not B,C as someone -- may expect. -- -- Example input format: \{\{x=0,y=0,w=1024,h=768\},\{x=0,y=0,w=1280,h=1024\}\} function mod_xinerama.merge_contained_screens(screens) local ret = {} for newnum, _newscreen in ipairs(screens) do newscreen = to_max_representation(_newscreen) local merged = false for prevnum, prevscreen in pairs(ret) do if screen_contains(prevscreen, newscreen) then table.insert(prevscreen.ids,newnum) merged = true elseif screen_contains(newscreen, prevscreen) then prevscreen.x = newscreen.x prevscreen.y = newscreen.y prevscreen.xmax = newscreen.xmax prevscreen.ymax = newscreen.ymax table.insert(prevscreen.ids,newnum) merged = true end if merged then break end end if not merged then newscreen.ids = { newnum } table.insert(ret, newscreen) end end fix_representations(ret) return ret end -- }}} --- {{{ Overlapping screens -- true if [from1, to1] overlaps [from2, to2] local function overlaps (from1, to1, from2, to2) return (from1 < to2) and (from2 < to1) end -- true if scr1 overlaps scr2 local function screen_overlaps(scr1, scr2) local x_in = overlaps(scr1.x, scr1.xmax, scr2.x, scr2.xmax) local y_in = overlaps(scr1.y, scr1.ymax, scr2.y, scr2.ymax) return x_in and y_in end --DOC -- Merges overlapping screens. I.e. it finds set of smallest rectangles, -- such that these rectangles do not overlap and such that they contain -- all screens. -- -- Example input format: \{\{x=0,y=0,w=1024,h=768\},\{x=0,y=0,w=1280,h=1024\}\} function mod_xinerama.merge_overlapping_screens(screens) local ret = {} for _newnum, _newscreen in ipairs(screens) do local newscreen = to_max_representation(_newscreen) newscreen.ids = { _newnum } local overlaps = true local pos while overlaps do overlaps = false for prevpos, prevscreen in pairs(ret) do if screen_overlaps(prevscreen, newscreen) then -- stabilise ordering if (not pos) or (prevpos < pos) then pos = prevpos end -- merge with the previous screen newscreen.x = math.min(newscreen.x, prevscreen.x) newscreen.y = math.min(newscreen.y, prevscreen.y) newscreen.xmax = math.max(newscreen.xmax, prevscreen.xmax) newscreen.ymax = math.max(newscreen.ymax, prevscreen.ymax) -- merge the indices for _k, _v in ipairs(prevscreen.ids) do table.insert(newscreen.ids, _v) end -- delete the merged previous screen table.remove(ret, prevpos) -- restart from beginning overlaps = true break end end end if not pos then pos = table.maxn(ret)+1 end table.insert(ret, pos, newscreen) end fix_representations(ret) return ret end --DOC -- Merges overlapping screens. I.e. it merges two screens -- if they overlap. It merges two screens if and only if there -- is a path between them using only overlapping screens. -- one is fully contained in the other screen (this contains the -- case that both screens are of the same geometry). -- The output screens also contain field ids containing the numbers -- of merged screens. The order of the screens is defined by the -- first screen in the merged set. (I.e., having big B, and big C, -- showing different parts of desktop and small A (primary) showing -- part of C, then the order will be C,B and not B,C as someone -- may expect. -- -- This function may output overlapping regions, AB and C on the example: -- *-------* -- *-----* | C | -- | | *-------* -- | A | -- | +-+-------* -- *---+-* | -- | B | -- | | -- +---------* -- Notion's WScreen implementation will (partially) hide C when AB is focused. -- Thus this algorithm is not what you want by default. -- -- Example input format: \{\{x=0,y=0,w=1024,h=768\},\{x=0,y=0,w=1280,h=1024\}\} -- See test_xinerama.lua for example input/output function mod_xinerama.merge_overlapping_screens_alternative(screens) -- Group overlapping screens into sets for merging. -- *-------* -- *-----* | C | -- | | *-------* -- | A | -- | +-+-------* -- *---+-* | -- | B | -- | | -- +---------* -- -- Our algorithm merges A with B, but it does not -- merge C to 'AB'. This is due to we only identify -- overlapping screen sets in first phase. -- -- -- *-----* *-----* -- | A +-+-----+-+ B | -- *---+-+ C +-+---+ -- +---------+ -- -- Our algoritm merges all three screens even if -- it first decides that A and B does not overlap, -- and then takes screen C. -- local screensets = {} for _newnum, _newscreen in ipairs(screens) do newscreen = to_max_representation(_newscreen) newscreen.id = _newnum --Find all screensets to merge with: --if there is a screen in a screenset that overlaps --with current screen, then we mark the set in 'mergekeys' local mergekeys = {} -- We use ipairs here, because we rely on the order. for setkey, screenset in ipairs(screensets) do -- find any screen of 'screenset' that overlaps 'newscreen' for _k, prevscreen in pairs(screenset) do if screen_overlaps(newscreen, prevscreen) then -- Found. 'setkey' contains indices of overlapping -- 'screenset's, sorted decreasingly table.insert(mergekeys, 1, setkey) break end end end -- Here we merge all marked screensets to one new screenset. -- We also delete the merged screensets from the 'screensets' table. local mergedset = {newscreen} local pos -- we use ipairs here, because we rely on the order. for _k, setkey in ipairs(mergekeys) do -- copy contents of 'screensets[setkey]' to 'mergedset' for _k2, prevscreen in pairs(screensets[setkey]) do table.insert(mergedset, prevscreen) end -- remove 'screensets[setkey]' table.remove(screensets, setkey) pos = setkey end -- pos keeps index of first set that we merged in this loop, -- we want to insert the product of this merge to pos. if not pos then pos = table.maxn(screensets)+1 end table.insert(screensets, pos, mergedset) end -- Now we have the screenset that contains the screens to be merged local ret = {} for _k, screenset in ipairs(screensets) do local newscreen = { x = screenset[1].x, xmax = screenset[1].xmax, y = screenset[1].y, ymax = screenset[1].ymax, ids = {} } for _k2, screen in pairs(screenset) do newscreen.x = math.min(newscreen.x, screen.x) newscreen.y = math.min(newscreen.y, screen.y) newscreen.xmax = math.max(newscreen.xmax, screen.xmax) newscreen.ymax = math.max(newscreen.ymax, screen.ymax) table.insert(newscreen.ids, screen.id) end table.insert(ret, newscreen) end fix_representations(ret) return ret end -- }}} --- {{{ Setup ion's screens */ --DOC -- Move a WScreen off the visible virtual screen. function mod_xinerama.move_offscreen(screen, max_right) local dimensions = mod_xinerama.get_screen_dimensions(screen) dimensions.x = max_right + 1 mod_xinerama.update_screen(screen, dimensions) end --DOC -- Perform the setup of ion screens. -- -- The first call sets up the screens of ion, subsequent calls update the -- current screens -- -- Returns true on success, false on failure -- -- Example input: {{x=0,y=0,w=1024,h=768},{x=1024,y=0,w=1280,h=1024}} function mod_xinerama.setup_screens(screens) local max_screen_id = 0 local max_right for screen_index, screen in ipairs(screens) do local screen_id = screen_index - 1 max_screen_id = max(max_screen_id, screen_id) max_right = max(max_right, screen.x + screen.w) local existing_screen = ioncore.find_screen_id(screen_id) if existing_screen ~= nil then mod_xinerama.update_screen(existing_screen, screen) else mod_xinerama.setup_new_screen(screen_id, screen) end end -- TODO what to do when the number of screens is lower than last time -- this function was called? Remove the screen and store its contents -- somewhere else? -- for now move the screen to a location outside the virtual screen, so -- it can't be accidentally focussed and obscure the proper screens local invisible_screen_id = max_screen_id + 1 local invisible_screen = ioncore.find_screen_id(invisible_screen_id) while invisible_screen do mod_xinerama.move_offscreen(invisible_screen, max_right) invisible_screen_id = invisible_screen_id + 1 invisible_screen = ioncore.find_screen_id(invisible_screen_id) end end -- }}} -- Mark ourselves loaded. package.loaded["mod_xinerama"]=true -- Load configuration file dopath('cfg_xinerama', true) --DOC -- Queries Xinerama for the screen dimensions and updates ion screens -- accordingly function mod_xinerama.refresh() local screens = mod_xinerama.query_screens() if screens then local merged_screens = mod_xinerama.merge_overlapping_screens(screens) mod_xinerama.setup_screens(merged_screens) end notioncore.screens_updated(notioncore.rootwin()); end mod_xinerama.refresh() notion-3+2012042300/mod_xinerama/test_xinerama.lua000066400000000000000000000076351174530661200216060ustar00rootroot00000000000000-- {{{ Table printing function table_tostring (tt) if type(tt) == "table" then local sb = {} local first = true table.insert(sb, "{ "); for key, value in pairs (tt) do if first then first = false else table.insert(sb, ", ") end if "number" == type (key) then table.insert(sb, table_tostring (value)) else table.insert(sb, key) table.insert(sb, "=") table.insert(sb, table_tostring (value)) end end table.insert(sb, " }"); return table.concat(sb) elseif type (tt) == "number" then return tostring(tt) else return '"' .. tostring(tt) .. '"' end end function print_tables(str,t) print(str .. ":") for key, value in ipairs(t) do print(" " .. key .. ": " .. table_tostring(value)) end end -- }}} -- {{{ Table compare -- Second table tree may contain some indices that the first does not contain. function table_compare(table, super) if not ( type(table) == type(super) ) then return false end if not ( type(table) == "table" ) then return table == super end for key, value in pairs(table) do if not table_compare(value, super[key]) then return false end end return true end -- }}} -- {{{ mock notion context ioncore = { load_module = function() return 1 end } function dopath() end _G["mod_xinerama"] = { query_screens = function() end } -- }}} -- load xinerama module lua code dofile('mod_xinerama.lua') function do_test(fn_name, input, output) print("\nTesting:" .. fn_name) print_tables("Input", input) local ret = mod_xinerama[fn_name](input) print_tables("Output", ret) assert(table_compare(output, ret)) end -- now perform some tests: -- {{{ merge_contained_screens do_test("merge_contained_screens",{ {x=0,y=0,w=800,h=600} },{ {x=0,y=0,w=800,h=600} }) do_test("merge_contained_screens",{ {x=0,y=0,w=800,h=600}, {x=0,y=0,w=800,h=600}, },{ {x=0,y=0,w=800,h=600} }) do_test("merge_contained_screens",{ {x=0,y=0,w=800,h=600}, {x=100,y=100,w=600,h=400}, },{ {x=0,y=0,w=800,h=600} }) do_test("merge_contained_screens",{ {x=0,y=0,w=800,h=600}, {x=100,y=100,w=800,h=600}, },{ {x=0,y=0,w=800,h=600}, {x=100,y=100,w=800,h=600}, }) -- }}} -- {{{ merge_overlapping_screens do_test("merge_overlapping_screens",{ {x=0,y=0,w=800,h=600} },{ {x=0,y=0,w=800,h=600} }) do_test("merge_overlapping_screens",{ {x=0,y=0,w=800,h=600}, {x=0,y=0,w=800,h=600}, },{ {x=0,y=0,w=800,h=600} }) do_test("merge_overlapping_screens",{ {x=0,y=0,w=800,h=600}, {x=100,y=100,w=600,h=400}, },{ {x=0,y=0,w=800,h=600} }) do_test("merge_overlapping_screens",{ {x=0,y=0,w=800,h=600}, {x=100,y=100,w=800,h=600}, },{ {x=0,y=0,w=900,h=700} }) --[[ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 0 ____________________ 1 | | | | 2 | | | | 3 | ~~~~~|~~~~ 4 | _______|________ 5 | | | | 6 ~~~~~~~~~~~~~~~~ | 7 | | 8 | | 9 | | 0 ~~~~~~~~~~~~~~~~ ]]-- do_test("merge_overlapping_screens",{ {x=0,y=0,w=800,h=600}, {x=400,y=400,w=800,h=600}, {x=500,y=0,w=500,h=300} },{ {x=0,y=0,w=1200,h=1000} }) --[[ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 0 ________________ ______ 1 | | | | 2 | | | | 3 | | ~~~~~~ 4 | _______|________ 5 | | | | 6 ~~~~~~~~~~~~~~~~ | 7 | | 8 | | 9 | | 0 ~~~~~~~~~~~~~~~~ ]]-- do_test("merge_overlapping_screens",{ {x=0,y=0,w=800,h=600}, {x=900,y=100,w=300,h=300}, {x=400,y=400,w=800,h=600} },{ {x=0,y=0,w=1200,h=1000} }) do_test("merge_overlapping_screens_alternative",{ {x=0,y=0,w=800,h=600}, {x=400,y=400,w=800,h=600}, {x=900,y=100,w=300,h=300} },{ {x=0,y=0,w=1200,h=1000}, {x=900,y=100,w=300,h=300} }) notion-3+2012042300/mod_xkbevents/000077500000000000000000000000001174530661200164325ustar00rootroot00000000000000notion-3+2012042300/mod_xkbevents/Makefile000066400000000000000000000010301174530661200200640ustar00rootroot00000000000000## ## frame_xkb module Makefile ## # System-specific configuration is in system.mk TOPDIR=../ include $(TOPDIR)/build/system-inc.mk ###################################### INCLUDES += $(X11_INCLUDES) $(LIBTU_INCLUDES) $(LIBEXTL_INCLUDES) -I$(TOPDIR) CFLAGS += $(XOPEN_SOURCE) $(C99_SOURCE) -Wall LDFLAGS += $(X11_LIBS) SOURCES=mod_xkbevents.c MAKE_EXPORTS=mod_xkbevents MODULE=mod_xkbevents ###################################### include $(TOPDIR)/build/rules.mk ###################################### _install: module_install notion-3+2012042300/mod_xkbevents/exact-version000066400000000000000000000053401174530661200211460ustar00rootroot00000000000000commit cdb4444d1ff89b179f02ddd89fcd6bac0ee8a3c2 Author: Arnout Date: Wed Sep 14 18:26:54 2011 +0200 Merge mod_xkb functionality in here, thanks to Sergej Pupykin for the patch. commit c7e9a45f70b8807b434c6413ab8fc11eabf79965 Author: Arnout Date: Sat Jun 11 11:52:53 2011 +0200 load 'mod_xkbevents', not 'xkbevents' commit 0554797ec9d51061b9bf91ebda4cf984f4b25dbe Author: Arnout Date: Sat Jun 11 11:48:40 2011 +0200 set 'TOPDIR' more consistently commit 4f1135c1e0a3bddf92e74f65e42414e647721723 Author: Etan Reisner Date: Mon May 9 01:21:42 2011 -0400 Statusbar script to display the current xkb group. Known issues: - Doesn't display anything on startup (only after first group switch). - Only displays group number not name. commit 96f937a611d765a0f58a605fae348c7261b471c8 Author: Etan Reisner Date: Mon May 9 01:20:37 2011 -0400 More ignored files. commit 4c452f5f13c7fb564aa8791cda40af1422abfb35 Author: Etan Reisner Date: Mon May 9 01:19:07 2011 -0400 Correct the horribly broken logic test here. commit f4a3497c3dd343876db3ce925c7add4252e20d8a Author: Etan Reisner Date: Mon May 9 00:54:41 2011 -0400 Last xkbevents -> mod_xkbevents change. commit 134f946a76d177b1971f990ca682c4a4d163a339 Author: Etan Reisner Date: Mon May 9 00:52:49 2011 -0400 s/xkbevents/mod_xkbevents/ commit 0c85916b397811585149d42ed4a96f1828ca1173 Author: Etan Reisner Date: Mon May 9 00:36:28 2011 -0400 Undef the macros when we are done with them. commit 2b249388e782d7e4cb1268126d41499e605e6132 Author: Etan Reisner Date: Mon May 9 00:36:04 2011 -0400 .gitignore commit b8541dbd30f3938519562edb12abc0073ca41df9 Author: Etan Reisner Date: Mon May 9 00:35:15 2011 -0400 Use the right name. commit d8f079b3212fcaa9acbb0492bbf9d6eff2a48c4d Author: Etan Reisner Date: Sun May 8 23:21:41 2011 -0400 Change how we fail when mod_xkbevents can't be found. commit 86dd4298fee22d25bbbb2ef8b3f93c8c272afb4a Author: Arnout Engelen Date: Sun May 8 20:52:31 2011 +0200 minor syntax/style/typo errors commit 31f69300e5dc69f5e1b20811feab69e0ded378b4 Author: Etan Reisner Date: Fri May 6 02:25:16 2011 -0400 Add xkbbell.lua script. commit 2234c7a2d9134bbd7741db8517d057df60aa68cc Author: Etan Reisner Date: Fri May 6 02:24:19 2011 -0400 Initial commit of xkbevents module. notion-3+2012042300/mod_xkbevents/mod_xkbevents.c000066400000000000000000000161271174530661200214550ustar00rootroot00000000000000/* * Ion xkb events * * Copyright (c) Sergey Redin 2006. * Copyright (c) Etan Reisner 2011. * * Released under the MIT License. */ #include #include #include "ioncore/event.h" #include "ioncore/global.h" #include "ioncore/xwindow.h" #include "exports.h" static int xkb_event_code, xkb_error_code; WHook *xkb_group_event=NULL, *xkb_bell_event=NULL; INTRSTRUCT(WAnyParams); DECLSTRUCT(WAnyParams){ bool send_event; /* True => synthetically generated */ Time time; /* server time when event generated */ unsigned int device; /* Xkb device ID, will not be XkbUseCoreKbd */ }; INTRSTRUCT(WGroupParams); DECLSTRUCT(WGroupParams){ WAnyParams any; int group; int base_group; int latched_group; int locked_group; }; INTRSTRUCT(WBellParams); DECLSTRUCT(WBellParams){ WAnyParams any; int percent; int pitch; int duration; unsigned int bell_class; unsigned int bell_id; char *name; WClientWin *window; bool event_only; }; /*{{{ Module information */ #include "version.h" char mod_xkbevents_ion_api_version[]=ION_API_VERSION; /*}}} Module information */ static char *get_atom_name(Atom atom) { char *xatomname, *atomname; xatomname = XGetAtomName(ioncore_g.dpy, atom); atomname = scopy(xatomname); XFree(xatomname); return atomname; } static bool docall(ExtlFn fn, ExtlTab t) { bool ret; extl_protect(NULL); ret=extl_call(fn, "t", NULL, t); extl_unprotect(NULL); extl_unref_table(t); return ret; } #define MRSH_ANY(PRM,TAB) \ extl_table_sets_b(TAB, "send_event", PRM->any.send_event); \ extl_table_sets_i(TAB, "time", PRM->any.time); \ extl_table_sets_i(TAB, "device", PRM->any.device) static bool mrsh_group_extl(ExtlFn fn, WGroupParams *param) { ExtlTab t=extl_create_table(); MRSH_ANY(param,t); if(param->group!=-1) extl_table_sets_i(t, "group", param->group + 1); if(param->base_group!=-1) extl_table_sets_i(t, "base", param->base_group + 1); if(param->latched_group!=-1) extl_table_sets_i(t, "latched", param->latched_group + 1); if(param->locked_group!=-1) extl_table_sets_i(t, "locked", param->locked_group + 1); return docall(fn, t); } static bool mrsh_bell_extl(ExtlFn fn, WBellParams *param) { ExtlTab t=extl_create_table(); MRSH_ANY(param,t); extl_table_sets_i(t, "percent", param->percent); extl_table_sets_i(t, "pitch", param->pitch); extl_table_sets_i(t, "duration", param->duration); extl_table_sets_i(t, "bell_class", param->bell_class); extl_table_sets_i(t, "bell_id", param->bell_id); if(param->name){ extl_table_sets_s(t, "name", param->name); free(param->name); } if(param->window) extl_table_sets_o(t, "window", (Obj*)param->window); extl_table_sets_b(t, "event_only", param->event_only); return docall(fn, t); } #define PARAM_ANY(PRM,NM) \ PRM.any.send_event=kev->NM.send_event; \ PRM.any.time=kev->NM.time; \ PRM.any.device=kev->NM.device #define CHANGED(NM,FLD) (kev->state.changed&XkbGroup##NM##Mask)?kev->state.FLD:-1 bool handle_xkb_event(XEvent *ev) { void *p=NULL; WHook *hook=NULL; XkbEvent *kev=NULL; WHookMarshallExtl *fn=NULL; if(ev->type!=xkb_event_code) return FALSE; kev=(XkbEvent*)ev; switch(kev->any.xkb_type){ case XkbStateNotify: { WGroupParams p2; p=&p2; hook=xkb_group_event; fn=(WHookMarshallExtl*)mrsh_group_extl; PARAM_ANY(p2,state); p2.group=CHANGED(State,group); p2.base_group=CHANGED(Base,base_group); p2.latched_group=CHANGED(Latch,latched_group); p2.locked_group=CHANGED(Lock,locked_group); } break; case XkbBellNotify: { WBellParams p2; p=&p2; hook=xkb_bell_event; fn=(WHookMarshallExtl*)mrsh_bell_extl; PARAM_ANY(p2,bell); p2.percent=kev->bell.percent; p2.pitch=kev->bell.pitch; p2.duration=kev->bell.duration; p2.bell_class=kev->bell.bell_class; p2.bell_id=kev->bell.bell_id; p2.name=NULL; if(kev->bell.name!=None) p2.name=get_atom_name(kev->bell.name); p2.window=NULL; if(kev->bell.window!=None) p2.window=XWINDOW_REGION_OF_T(kev->bell.window, WClientWin); p2.event_only=kev->bell.event_only; } break; } if(hook && p && fn) hook_call_p(hook, p, fn); return FALSE; } #undef CHANGED #undef PARAM_ANY /*EXTL_DOC * Set the current XKB group. See \code{XkbLockGroup}(3) manual page * for details. See xkbion.lua for example use. */ EXTL_EXPORT int mod_xkbevents_lock_group(int state) { return XkbLockGroup(ioncore_g.dpy, XkbUseCoreKbd, state); } /*EXTL_DOC * Latch modifiers. See \code{XkbLatchModifiers}(3) manual page * for details. */ EXTL_EXPORT int mod_xkbevents_lock_modifiers(int affect, int values) { return XkbLockModifiers(ioncore_g.dpy, XkbUseCoreKbd, affect, values); } /*{{{ Init & deinit */ /* ion never does this though it looks to me like that leaks (though I suppose * that doesn't matter if modules can't ever be unloaded at runtime. void deinit_hooks() { } */ #define INIT_HOOK_(NM) \ NM=mainloop_register_hook(#NM, create_hook()); \ if(NM==NULL) return FALSE; static bool init_hooks() { INIT_HOOK_(xkb_group_event); INIT_HOOK_(xkb_bell_event); return TRUE; } #undef INIT_HOOK_ void mod_xkbevents_deinit() { mod_xkbevents_unregister_exports(); } bool mod_xkbevents_init() { int opcode; int major=XkbMajorVersion; int minor=XkbMinorVersion; if(!XkbLibraryVersion(&major,&minor)){ warn(TR("X library built with XKB version %d.%02d but mod_xkbevents was built with XKB version %d.%02d. Going to try to work anyway."), major, minor, XkbMajorVersion, XkbMinorVersion); } if(!XkbQueryExtension(ioncore_g.dpy,&opcode,&xkb_event_code,&xkb_error_code,&major,&minor)>0){ if ((major!=0)||(minor!=0)) warn(TR("Server supports incompatible XKB version %d.%02d. Going to try to work anyway."), major, minor); else warn(TR("XkbQueryExtension failed. Going to try to work anyway.")); } if(!init_hooks()) return FALSE; if(!mod_xkbevents_register_exports()) return FALSE; if(!hook_add(ioncore_handle_event_alt, (void (*)())handle_xkb_event)) return FALSE; /* Select for the specific XkbState events we care about. */ XkbSelectEventDetails(ioncore_g.dpy, XkbUseCoreKbd, XkbStateNotify, XkbGroupStateMask|XkbGroupBaseMask|XkbGroupLockMask, XkbGroupStateMask|XkbGroupBaseMask|XkbGroupLockMask); /* Select for all XkbBell events (we can't select for less). */ XkbSelectEvents(ioncore_g.dpy, XkbUseCoreKbd, XkbBellNotifyMask, XkbBellNotifyMask); return TRUE; } /*}}} Init & deinit */ notion-3+2012042300/mod_xkbevents/statusbar_xkbgroup.lua000066400000000000000000000012051174530661200230640ustar00rootroot00000000000000--[[ Author: Etan Reisner Email: deryni@unreliablesource.net Summary: Adds an xkbgroup statusbar meter. License: MIT Last Updated: 2011-05-09 Copyright (c) Etan Reisner 2011 --]] local group_hook = ioncore.get_hook("xkb_group_event") if not group_hook then pcall(dopath, "mod_xkbevents") group_hook = ioncore.get_hook("xkb_group_event") if not group_hook then warn("Could not load mod_xkbevents.") return end end local function group_event(tab) if tab.group then mod_statusbar.inform("xkbgroup", tostring(tab.group)) end ioncore.defer(mod_statusbar.update) end group_hook:add(group_event) notion-3+2012042300/mod_xkbevents/xkbbell.lua000066400000000000000000000063541174530661200205700ustar00rootroot00000000000000--[[ Author: Etan Reisner Email: deryni@unreliablesource.net Summary: Displays an WInfoWin on XkbBell events, also sets xkbbell grattr:s on the frame containing the window that triggered the event. License: MIT Last Updated: 2011-05-05 Copyright (c) Etan Reisner 2011 --]] local bell_hook = ioncore.get_hook("xkb_bell_event") if not bell_hook then pcall(dopath, "mod_xkbevents") bell_hook = ioncore.get_hook("xkb_bell_event") if not bell_hook then warn("Could not load mod_xkbevents.") return end end xkbbell = xkbbell or {} if not xkbbell.timeout then xkbbell.timeout = 10000 end if not xkbbell.low_threshold then xkbbell.low_threshold = 50 end if not xkbbell.medium_threshold then xkbbell.medium_threshold = 75 end if not xkbbell.high_threshold then xkbbell.high_threshold = 100 end local timer = ioncore.create_timer() local screen_iws = setmetatable({}, {__mode="kv"}) local iw_hide_funs = setmetatable({}, {__mode="kv"}) local active_frames = setmetatable({}, {__mode="kv"}) local function set_hidden(iw, scr, state) scr = scr or ioncore.find_manager(iw, "WScreen") scr:set_hidden(iw, state or "set") end local function unset_grattrs(frame) frame:set_grattr("xkbbell", "unset") frame:set_grattr("xkbbell-low", "unset") frame:set_grattr("xkbbell-medium", "unset") frame:set_grattr("xkbbell-high", "unset") end local function hide_fun(iw, scr) return function() set_hidden(iw, scr) end end local function region_notify(reg, how) if how ~= "activated" then return end local frame = ioncore.find_manager(reg, "WFrame") if frame and active_frames[frame] then active_frames[frame] = nil unset_grattrs(frame) end end local function bell_info(tab) local style = "xkbbell" if tab.percent <= xkbbell.low_threshold then style = "xkbbell-low" elseif tab.percent <= xkbbell.medium_threshold then style = "xkbbell-medium" elseif tab.percent <= xkbbell.high_threshold then style = "xkbbell-high" end if tab.window then local frame = ioncore.find_manager(tab.window, "WFrame") if frame then local cframe = ioncore.find_manager(ioncore.current(), "WFrame") if frame ~= cframe then active_frames[frame] = true unset_grattrs(frame) frame:set_grattr(style, "set") end return end -- Fall through to setting a screen iw for this bell. end local scr = ioncore.current():screen_of() local iw = screen_iws[scr] ioncore.defer(function() if not iw then iw = scr:attach_new{ name="xkbbell"..scr:id(), style=style, type="WInfoWin", hidden=true, unnumbered=true, sizepolicy="free", geom={x=0,y=0,w=1,h=1}, } screen_iws[scr] = iw iw:set_text("Bell!", -1) iw_hide_funs[scr] = hide_fun(iw, scr) end set_hidden(iw, scr, "unset") timer:set(xkbbell.timeout, iw_hide_funs[scr]) end) end bell_hook:add(bell_info) local hook = ioncore.get_hook("region_notify") if hook then hook:add() end notion-3+2012042300/mod_xkbevents/xkbion.lua000066400000000000000000000144571174530661200204420ustar00rootroot00000000000000-- xkbion.lua -- TODO: make xkbion_set understand some simple presets -- (c) Sergey Redin -- Thanks: -- smersh at users.sf.net (author of xkbind) for the original idea --[[ -- This script allows you to use independent keyboard layouts for different windows in Anion3. -- It uses a window property to store the XKB groups, so you can restart Ion without losing -- settings for each window. -- Example usage. This is what I have in my cfg_ion.lua: dopath("mod_xkbevents") dopath("xkbion.lua") xkbion_set { {name="EN", hint="", action = function() mod_xkbevents.lock_group(0) end}, {name="RU", hint="important", action = function() mod_xkbevents.lock_group(1) end}, key="Caps_Lock", statusname = "xkbion", } xkbion_set { {name="NUM", command="numlockx on"}, {name="---", command="numlockx off"}, key="Num_Lock", statusname="num", atomname="XKBION_NUM", } xkbion_set { {name="----", hint="", action = function() mod_xkbevents.lock_modifiers(2, 0) end}, {name="CAPS", hint="critical", action = function() mod_xkbevents.lock_modifiers(2, 2) end}, key="Caps_Lock", statusname = "caps", atomname="XKBION_CAPS", } -- Edit this to suit your needs. -- Please note, if you want to use Caps_Lock key to change the keyboard groups like I do, -- do not forget to add "grp:caps_toggle" to your XKB settings, just to prevent X from using -- this key also for swiching keyboard registers. -- At least one group definition must be present. -- "name" is only neseccary if you want to use mod_statusbar to indicate current XKB group. -- "hint" is only necessary if you want to highlight your XKB group in statusbar, possible -- values are standard values provided by the mod_statusbar: important, normal, critical -- "command" and "action" are also unneseccary but xkbion.lua is not particulary useful -- without them. :) The same thing for "key". -- The last thing to say about xkbion_set() parameters is that if you call xkbion_set -- more than once (like I do it for XKB groups and NumLock state) you must choose different -- "atomname" values. The default for atomname is XKBION_GROUP. -- The second xkbion_set() call (numlock section) is here mostly for the example. Most users -- will need only one call, for changing XKB group. Please also note that you can define more -- than two groups in call to xkbion_set(). -- You can use this line in cfg_statusbar.lua to indicate the current XKB group: -- template="... %xkbion ...", -- If your Ion does not have mod_xkb, you may try the following: -- xkbion_set { -- {name="EN", command="setxkbmap us -option grp:caps_toggle"}, -- {name="RU", command="setxkbmap ru winkeys -option grp:caps_toggle"}, -- key="Caps_Lock", -- statusname = "xkbion", -- } ]] function xkbion_set (groups) -- the only global created by xkbion.lua if not groups or type(groups) ~= "table" then error("bad args") end if not groups[1] or type(groups[1]) ~= "table" then error("default group is undefined") end -- window_group_prop(w) - get XKBION_GROUP integer property of window `w' (set it to 1 if it's not yet defined) -- window_group_prop(w, group) - set XKBION_GROUP property of window `w' to integer `group' -- "XKBION_GROUP" is just the default name local window_group_prop do local XA_INTEGER = 19 local atom = notioncore.x_intern_atom( tostring( groups.atomname or "XKBION_GROUP" ) ) if not atom or type(atom) ~= "number" then error("Cannot intern atom " .. atomname) end window_group_prop = function(w, gnum) if not w or type(w) ~= "userdata" or not w.xid or type(w.xid) ~= "function" then return 1 end local xid = tonumber( w:xid() ) if gnum == nil then local t = notioncore.x_get_window_property( xid, atom, XA_INTEGER, 1, true ) if t and type(t) == "table" and t[1] ~= nil then do return tonumber(t[1]) end else gnum = 1 end else gnum = tonumber(gnum) end -- we're here if the second argument is set or if the window does not have our property yet notioncore.defer( function() notioncore.x_change_property( xid, atom, XA_INTEGER, 32, "replace", {gnum} ) end ) return gnum end end local set_group do local current_gnum = 1 local first_time = true local statusname = groups.statusname if statusname and type(statusname) ~= "string" then statusname = nil end set_group = function(w, do_increment) local gnum if w then gnum = window_group_prop(w) else gnum = 1 end if do_increment then gnum = gnum + 1 end local g = groups[gnum] if not g then gnum, g = 1, groups[1] end if not g then return end -- error in settings, groups[1] not defined if first_time then first_time = false elseif gnum == current_gnum then return end window_group_prop(w, gnum) -- it's OK to call it even it `w' is nil if g.command then notioncore.exec(g.command) end if g.action then notioncore.defer(g.action) end current_gnum = gnum local group_name = g.name local hint_name = g.hint if statusname and group_name and type(group_name) == "string" then mod_statusbar.inform(statusname, group_name) mod_statusbar.inform(statusname.."_hint", hint_name) notioncore.defer(mod_statusbar.update) end end end notioncore.get_hook("region_notify_hook"):add( function(reg, action) if (reg ~= nil) and (tostring(reg.__typename) == "WClientWin") then if (action == "activated") or (action == "pseudoactivated") then set_group(reg) end end end ) local key = groups.key if key and type(key) == "string" then defbindings("WClientWin", { kpress(key, function (_, _sub) set_group(_, true) end) }) end set_group() -- initialize end -- xkbion_set() notion-3+2012042300/mod_xrandr/000077500000000000000000000000001174530661200157175ustar00rootroot00000000000000notion-3+2012042300/mod_xrandr/LICENSE000066400000000000000000000634761174530661200167440ustar00rootroot00000000000000 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! notion-3+2012042300/mod_xrandr/Makefile000066400000000000000000000020201174530661200173510ustar00rootroot00000000000000## ## Ion xrandr module Makefile ## ## # System specific configuration is in system.mk TOPDIR=../ include $(TOPDIR)/build/system-inc.mk ###################################### INCLUDES += $(LIBTU_INCLUDES) $(LIBEXTL_INCLUDES) $(X11_INCLUDES) -I$(TOPDIR) CFLAGS += $(XOPEN_SOURCE) $(C99_SOURCE) SOURCES=mod_xrandr.c MAKE_EXPORTS=xrandr_module LIBS = $(X11_LIBS) -lXrandr MODULE=mod_xrandr ###################################### include $(TOPDIR)/build/rules.mk ###################################### _install: module_install $(INSTALLDIR) $(DESTDIR)$(ETCDIR) for i in $(ETC); do \ $(INSTALL) -m $(DATA_MODE) $$i $(DESTDIR)$(ETCDIR); \ done ###################################### .PHONY: tarball tarball: $(SOURCES) $(ETC) $(DOCS) Makefile sh -c 'BASENAME=ion-devel-$(MODULE)-`date -r \`ls -t $+ | head -n 1\` +%Y%m%d`; \ mkdir $$BASENAME; \ cp $+ $$BASENAME; \ tar -cvjf $$BASENAME.tar.bz2 $$BASENAME/*; \ rm -Rf $$BASENAME' ###################################### .PHONY: tags tags: exuberant-ctags -R . $(TOPDIR) notion-3+2012042300/mod_xrandr/README000066400000000000000000000014601174530661200166000ustar00rootroot00000000000000Ion xrandr module Copyright (c) Ragnar Rova 2004 Tuomo Valkonen 2005-2007 by Ragnar Rova INTRODUCTION This module gives ion3 XrandR support. It updates ions screen size when a screen size change event is received. INSTALLATION 1. Edit Makefile to ensure TOPDIR points to your top-level ion source directory with a system.mk that matches the version of ion installed on your system. 2. Run make. 3. Either run (as root) # make install or (as yourself), $ mkdir -p ~/.ion3/lib $ cp .libs/mod_xrandr.* ~/.ion3/lib 4. Add dopath("mod_xrandr") to ~/.ion3/cfg_ion.lua. 5. (Re)start Ion. LIMITATIONS The module only supports screen size changes. Nothing else. notion-3+2012042300/mod_xrandr/exact-version000066400000000000000000000017521174530661200204360ustar00rootroot00000000000000commit 75b4dc1f9fbd178db0689dae810d1b5c08bff086 Author: Arnout Date: Thu Oct 20 18:21:06 2011 +0200 Support $(DESTDIR), thanks to Josef 'Jeff' Sipek commit 118bca12203c5fc45b4def1302187d1d3a2f6a26 Merge: ff9acca f7f5544 Author: Arnout Date: Sat Apr 9 14:09:51 2011 +0200 Merge branch 'master' into expose commit f7f55440d8afef1a3078e515489692cfc664a6ea Author: Arnout Date: Sat Apr 9 14:08:42 2011 +0200 use XRRRootToScreen to relate notion screens to RandR screen id's commit ff9acca8eb4d0425dc94576173ead57a687a64da Author: Arnout Date: Sat Apr 9 13:22:05 2011 +0200 Introduce a randr_screen_change_notify hook commit 6e7544fef7702b243c0229c81042d632a6fdc000 Author: Arnout Date: Sat Apr 9 12:39:36 2011 +0200 ion-3 -> notion commit 6fad13eff6e14adac2307ba686917778ffcc4e49 Author: M Rawash Date: Fri Aug 20 16:55:19 2010 +0200 init notion-3+2012042300/mod_xrandr/mod_xrandr.c000066400000000000000000000115161174530661200202240ustar00rootroot00000000000000/* * Ion xrandr module * Copyright (C) 2004 Ragnar Rova * 2005-2007 Tuomo Valkonen * * by Ragnar Rova * * 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 * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include char mod_xrandr_ion_api_version[]=ION_API_VERSION; WHook *randr_screen_change_notify=NULL; static bool hasXrandR=FALSE; static int xrr_event_base; static int xrr_error_base; Rb_node rotations=NULL; static int rr2scrrot(int rr) { switch(rr){ case RR_Rotate_0: return SCREEN_ROTATION_0; case RR_Rotate_90: return SCREEN_ROTATION_90; case RR_Rotate_180: return SCREEN_ROTATION_180; case RR_Rotate_270: return SCREEN_ROTATION_270; default: return SCREEN_ROTATION_0; } } static void insrot(int id, int r) { Rb_node node; node=rb_inserti(rotations, id, NULL); if(node!=NULL) node->v.ival=r; } bool handle_xrandr_event(XEvent *ev) { if(hasXrandR && ev->type == xrr_event_base + RRScreenChangeNotify) { XRRScreenChangeNotifyEvent *rev=(XRRScreenChangeNotifyEvent *)ev; WFitParams fp; WScreen *screen; bool pivot=FALSE; screen=XWINDOW_REGION_OF_T(rev->root, WScreen); if(screen!=NULL){ int r; Rb_node node; int found; r=rr2scrrot(rev->rotation); fp.g.x=REGION_GEOM(screen).x; fp.g.y=REGION_GEOM(screen).y; if(rev->rotation==RR_Rotate_90 || rev->rotation==RR_Rotate_270){ fp.g.w=rev->height; fp.g.h=rev->width; }else{ fp.g.w=rev->width; fp.g.h=rev->height; } fp.mode=REGION_FIT_EXACT; node=rb_find_ikey_n(rotations, screen->id, &found); if(!found){ insrot(screen->id, r); }else if(r!=node->v.ival){ int or=node->v.ival; fp.mode|=REGION_FIT_ROTATE; fp.rotation=(r>or ? SCREEN_ROTATION_0+r-or : (SCREEN_ROTATION_270+1)+r-or); node->v.ival=r; } REGION_GEOM(screen)=fp.g; mplex_managed_geom((WMPlex*)screen, &(fp.g)); mplex_do_fit_managed((WMPlex*)screen, &fp); } hook_call_v(randr_screen_change_notify); return TRUE; } return FALSE; } static bool check_pivots() { WScreen *scr; XRRScreenConfiguration *cfg; rotations=make_rb(); if(rotations==NULL) return FALSE; FOR_ALL_SCREENS(scr){ Rotation rot=RR_Rotate_90; int randr_screen_id = XRRRootToScreen(ioncore_g.dpy, ((WMPlex*) scr)->win.win); if (randr_screen_id != -1) XRRRotations(ioncore_g.dpy, randr_screen_id, &rot); insrot(scr->id, rr2scrrot(rot)); } return TRUE; } #define INIT_HOOK_(NM) \ NM=mainloop_register_hook(#NM, create_hook()); \ if(NM==NULL) return FALSE bool mod_xrandr_init() { hasXrandR= XRRQueryExtension(ioncore_g.dpy,&xrr_event_base,&xrr_error_base); if(!check_pivots()) return FALSE; if(hasXrandR){ XRRSelectInput(ioncore_g.dpy,ioncore_g.rootwins->dummy_win, RRScreenChangeNotifyMask); }else{ warn_obj("mod_xrandr","XRandR is not supported on this display"); } hook_add(ioncore_handle_event_alt,(WHookDummy *)handle_xrandr_event); INIT_HOOK_(randr_screen_change_notify); return TRUE; } bool mod_xrandr_deinit() { hook_remove(ioncore_handle_event_alt, (WHookDummy *)handle_xrandr_event); return TRUE; } notion-3+2012042300/modulelist.mk000066400000000000000000000004131174530661200162720ustar00rootroot00000000000000## ## List of modules to build ## MODULE_LIST = mod_tiling mod_query mod_menu \ mod_dock mod_sp mod_sm mod_statusbar \ de mod_xinerama mod_xrandr mod_xkbevents # Modules to -dlpreload into pwm if statically linking. PWM_MODULE_LIST := $(MODULE_LIST) notion-3+2012042300/notion/000077500000000000000000000000001174530661200150705ustar00rootroot00000000000000notion-3+2012042300/notion/Makefile000066400000000000000000000017351174530661200165360ustar00rootroot00000000000000## ## Notion 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 ###################################### EXECUTABLE = notion SOURCES = notion.c INCLUDES += $(X11_INCLUDES) INCLUDES += $(LIBMAINLOOP_INCLUDES) $(LIBTU_INCLUDES) $(LIBEXTL_INCLUDES) INCLUDES += -I.. LIBS += $(X11_LIBS) -lSM -lICE LIBS += $(WHOLEA) $(LIBMAINLOOP_LIBS) $(LIBEXTL_LIBS) $(LIBTU_LIBS) $(NO_WHOLEA) LIBS += $(LUA_LIBS) $(DL_LIBS) LIBS += -lm MODULE_PATH = $(TOPDIR) 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 ###################################### _install: executable_install notion-3+2012042300/notion/notion.c000066400000000000000000000162671174530661200165560ustar00rootroot00000000000000/* * ion/ion/ion.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #include #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 screen only")}, {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"), libtu_progname()); 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_notion"; 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; char *localedir; libtu_init(argv[0]); #ifdef CF_RELOCATABLE_BIN_LOCATION prefix_set(argv[0], CF_RELOCATABLE_BIN_LOCATION); #endif localedir=prefix_add(LOCALEDIR); if(!ioncore_init(CF_EXECUTABLE, argc, argv, localedir)) return EXIT_FAILURE; if(localedir!=NULL) free(localedir); prefix_wrap_simple(extl_add_searchdir, EXTRABINDIR); /* ion-completefile */ prefix_wrap_simple(extl_add_searchdir, MODULEDIR); prefix_wrap_simple(extl_add_searchdir, ETCDIR); prefix_wrap_simple(extl_add_searchdir, SHAREDIR); prefix_wrap_simple(extl_add_searchdir, LCDIR); extl_set_userdirs(CF_EXECUTABLE); 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('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("Notion 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_display(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; } notion-3+2012042300/po/000077500000000000000000000000001174530661200142005ustar00rootroot00000000000000notion-3+2012042300/po/Makefile000066400000000000000000000037461174530661200156520ustar00rootroot00000000000000## ## Notion 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 fr de 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 \ ../notion \ ../pwm \ ../etc \ $(LIBEXTL_DIR) EXTRA_POTFILES_LUA=../build/mkman.lua POTFILE=notion.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) $(DESTDIR)$(LOCALEDIR)/$$i/LC_MESSAGES ; \ $(INSTALL) -m $(DATA_MODE) $$i.mo $(DESTDIR)$(LOCALEDIR)/$$i/LC_MESSAGES/notion.mo ; \ done update_fi: pot $(MSGMERGE) -U fi.po $(POTFILE) update_cs: pot $(MSGMERGE) -U cs.po $(POTFILE) update_fr: pot $(MSGMERGE) -U fr.po $(POTFILE) update_de: pot $(MSGMERGE) -U de.po $(POTFILE) notion-3+2012042300/po/README000066400000000000000000000011041174530661200150540ustar00rootroot00000000000000 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/`. notion-3+2012042300/po/cs.po000066400000000000000000001275721174530661200151630ustar00rootroot00000000000000# # Czech language translations for Ion3. # # Copyright (c) Miroslav Kure 2004,2005,2006,2007. # # 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: 2007-07-03 12:00+0300\n" "PO-Revision-Date: 2007-07-02 19:12+0200\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:93 msgid "Insane key combination." msgstr "©ílená klávesová kombinace." #: ../ioncore/conf-bindings.c:97 msgid "Could not convert keysym to keycode." msgstr "Nemohu pøevést keysym na keycode." #: ../ioncore/conf-bindings.c:108 #, c-format msgid "Unknown button \"%s\"." msgstr "Neznámé tlaèítko \"%s\"." #: ../ioncore/conf-bindings.c:113 msgid "Insane button combination." msgstr "©ílená kombinace tlaèítek." #: ../ioncore/conf-bindings.c:120 ../ioncore/conf-bindings.c:127 msgid "Insane modifier combination." msgstr "©ílená kombinace modifikátorù." #: ../ioncore/conf-bindings.c:165 #, 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:183 #, c-format msgid "Unable to add binding %s." msgstr "Nemohu pøidat vazbu %s." #: ../ioncore/conf-bindings.c:188 #, c-format msgid "Unable to remove binding %s." msgstr "Nemohu odstranit vazbu %s." #: ../ioncore/conf-bindings.c:228 #, c-format msgid "Unable to add submap for binding %s." msgstr "Nemohu pøidat **** pro vazbu %s." # #: ../ioncore/conf-bindings.c:261 msgid "Binding type not set." msgstr "Typ vazby není nastaven." #: ../ioncore/conf-bindings.c:271 #, c-format msgid "Unknown binding type \"%s\"." msgstr "Neznámý typ vazby \"%s\"." #: ../ioncore/conf-bindings.c:295 #, c-format msgid "Unknown area \"%s\" for binding %s." msgstr "Neznámá oblast \"%s\" pro vazbu %s." #: ../ioncore/conf-bindings.c:336 #, c-format msgid "Unable to get bindmap entry %d." msgstr "Nemohu získat záznam o vazbì %d." #: ../ioncore/conf-bindings.c:378 msgid "Unable to convert keysym to string." msgstr "Nemohu pøevést keysym na øetìzec." #: ../ioncore/conf-bindings.c:392 msgid "Unable to convert button to string." msgstr "Nemohu pøevést tlaèítko na øetìzec." # #: ../ioncore/event.c:110 msgid "Time request from X server failed." msgstr "Èasový po¾adavek od X serveru selhal." # #: ../ioncore/exec.c:185 msgid "Not saving state: running under session manager." msgstr "Neukládám stav: bì¾ím pod správcem sezení." #: ../ioncore/strings.c:104 ../ioncore/strings.c:140 ../ioncore/strings.c:173 msgid "Invalid multibyte string." msgstr "Neplatný vícebajtový øetìzec." #: ../ioncore/strings.c:264 #, c-format msgid "Error compiling regular expression: %s" msgstr "Chyba pøi kompilaci regulárního výrazu: %s" #: ../ioncore/modules.c:155 msgid "Invalid module name." msgstr "Neplatné jméno modulu." # #: ../ioncore/modules.c:167 msgid "The module is already loaded." msgstr "Modul je ji¾ zaveden." #: ../ioncore/modules.c:182 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:193 #, c-format msgid "Unable to initialise module %s." msgstr "Nemohu inicializovat modul %s." #: ../ioncore/modules.c:217 ../../libextl-3/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:288 msgid "Unknown module." msgstr "Neznámý modul." #: ../ioncore/modules.c:296 msgid "Unable to initialise module." msgstr "Nemohu inicializovat modul." #: ../ioncore/modules.c:341 msgid "No module to load given." msgstr "Nebyl zadán modul pro zavedení." #: ../ioncore/property.c:350 ../ioncore/property.c:359 msgid "Invalid arguments." msgstr "Neplatné argumenty." #: ../ioncore/screen.c:382 msgid "Only workspace may not be destroyed/detached." msgstr "Jediná pracovní plocha nemù¾e být zru¹ena." #: ../ioncore/screen.c:393 msgid "Screens may not be destroyed." msgstr "Obrazovky nesmí být znièeny." #: ../ioncore/screen.c:429 msgid "Invalid offset." msgstr "Neplatné odsazení" #: ../ioncore/screen.c:468 #, c-format msgid "Unable to create a workspace on screen %d." msgstr "Nemohu vytvoøit pracovní plochu na obrazovce %d." #: ../ioncore/sizehint.c:146 msgid "Invalid client-supplied width/height increment." msgstr "Klient zadal neplatný pøírùstek ¹íøky/vý¹ky." #: ../ioncore/sizehint.c:154 msgid "Invalid client-supplied aspect-ratio." msgstr "Klient zadal neplatný pomìr stran." #: ../ioncore/ioncore.c:79 msgid "" "This software is essentially licensed under the GNU Lesser General\n" "Public License (LGPL), version 2.1, unless otherwise indicated in\n" "components taken from elsewhere. Additional terms apply to the use\n" "of the name of the project, Ion(tm). For details, see the file\n" "LICENSE that you should have received with this software.\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.\n" msgstr "" "Tento program je v podstatì licencovaný pod licencí GNU Lesser\n" "General Public Licence (LGPL) verze 2.1, s mo¾nou výjimkou\n" "komponent získaných jinde. Na pou¾ití jména projektu, Ion(tm),\n" "se vztahují dal¹í podmínky. Podrobnosti naleznete v souboru\n" "LICENSE, který byste mìli obdr¾et spolu s tímto softwarem.\n" "\n" "Tento program je distribuovaný v nadìji, ¾e bude u¾iteèný, ale\n" "BEZ JAKÉKOLIV ZÁRUKY.\n" #: ../ioncore/ioncore.c:160 msgid "No encoding given in LC_CTYPE." msgstr "V LC_CTYPE není zadáno kódování." #: ../ioncore/ioncore.c:478 #, c-format msgid "Could not connect to X display '%s'" msgstr "Nemohu se pøipojit na X displej \"%s\"" #: ../ioncore/ioncore.c:531 msgid "Could not find a screen to manage." msgstr "Nemohu najít obrazovku, kterou bych mohl spravovat." #: ../ioncore/xic.c:35 msgid "Failed to open input method." msgstr "Selhalo otevøení vstupní metody." #: ../ioncore/xic.c:40 msgid "Input method doesn't support any style." msgstr "Vstupní metoda nepodporuje ¾ádný styl." #: ../ioncore/xic.c:55 msgid "input method doesn't support my preedit type." msgstr "Vstupní metoda nepodporuje mùj pøedpøipravený typ." #: ../ioncore/xic.c:83 msgid "Failed to create input context." msgstr "Selhalo vytvoøení vstupního kontextu." #: ../ioncore/clientwin.c:376 #, 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:380 #, 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:385 #, 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:405 ../ioncore/clientwin.c:512 #: ../ioncore/clientwin.c:1327 #, c-format msgid "Window %#x disappeared." msgstr "Okno %#x zmizelo." #: ../ioncore/clientwin.c:532 msgid "Unable to find a matching root window!" msgstr "Nemohu najít odpovídající koøenové okno!" #: ../ioncore/clientwin.c:571 #, c-format msgid "Unable to manage client window %#x." msgstr "Nemohu spravovat klientské okno %#x." #: ../ioncore/clientwin.c:620 msgid "Changes is WM_TRANSIENT_FOR property are unsupported." msgstr "Zmìny vlastnosti WM_TRANSIENT_FOR nejsou podporovány." # #: ../ioncore/clientwin.c:792 msgid "Client does not support the WM_DELETE protocol." msgstr "Klient nepodporuje protokol WM_DELETE." #: ../ioncore/clientwin.c:1333 msgid "Saved client window does not want to be managed." msgstr "Ulo¾ené klientské okno nechce být spravováno." # #: ../ioncore/colormap.c:93 msgid "Unable to store colourmap watch info." msgstr "Nemohu ulo¾it informace o barevné mapì." #: ../ioncore/region.c:45 msgid "Creating region with negative width or height!" msgstr "Vytváøím oblast se zápornou ¹íøkou nebo vý¹kou!" #: ../ioncore/region.c:93 #, 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:434 #, c-format msgid "Can not destroy %s: contains client windows." msgstr "Nemohu znièit %s: obsahuje klientská okna." #: ../ioncore/region.c:435 msgid "(unknown)" msgstr "(neznámý)" #: ../ioncore/region.c:498 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:55 ../ioncore/frame-pointer.c:278 #, c-format msgid "Attempt to make region %s manage its ancestor %s." msgstr "Pokus, aby oblast %s spravovala svého pøedka %s." #: ../ioncore/attach.c:80 msgid "Unable to reparent." msgstr "Nemohu zmìnit rodièe." #: ../ioncore/attach.c:89 msgid "Unexpected attach error: trying to recover by attaching to screen." msgstr "Neoèekávaná chyba pøipojení: zkou¹ím obnovit pøipojením k obrazovce." #: ../ioncore/attach.c:108 msgid "Failed recovery." msgstr "Obnovení selhalo." #: ../ioncore/manage.c:190 msgid "Unable to find a screen for a new client window." msgstr "Nemohu najít obrazovku pro nové klientské okno." #: ../ioncore/rootwin.c:215 #, 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/names.c:88 #, c-format msgid "Corrupt instance number %s." msgstr "Poru¹ená instance èíslo %s." #: ../ioncore/saveload.c:95 #, c-format msgid "Unknown class \"%s\", cannot create region." msgstr "Neznámá tøída \"%s\", nemohu vytvoøit oblast." #: ../ioncore/saveload.c:199 #, 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:250 msgid "Unable to get file for layout backup." msgstr "Nemohu získat soubor pro zálohu rozlo¾ení." #: ../ioncore/saveload.c:254 #, c-format msgid "Backup file %s already exists." msgstr "Zálo¾ní soubor %s ji¾ existuje." #: ../ioncore/saveload.c:260 msgid "Failed backup." msgstr "Selhala záloha." # #: ../ioncore/saveload.c:265 msgid "Unable to initialise layout on any screen." msgstr "Na ¾ádné obrazovce nemohu inicializovat rozlo¾ení." #: ../ioncore/saveload.c:292 #, c-format msgid "Unable to get configuration for screen %d." msgstr "Nemohu získat nastavení pro obrazovku %d." #: ../ioncore/saveload.c:305 msgid "Unable to save layout." msgstr "Nemohu ulo¾it rozlo¾ení." #: ../ioncore/conf.c:237 msgid "User directory can not be set." msgstr "U¾ivatelský adresáø nemù¾e být nastaven." #: ../ioncore/conf.c:311 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:46 msgid "Failed to enter full screen mode." msgstr "Selhal pøechod do celoobrazovkového re¾imu." #: ../ioncore/fullscreen.c:80 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:1797 msgid "Invalid position setting." msgstr "Neplatné nastavení pozice." # #: ../ioncore/mplex.c:1837 msgid "Invalid action setting." msgstr "Neplatné nastavení akce." #: ../ioncore/gr.c:117 #, c-format msgid "Drawing engine %s is not registered!" msgstr "Vykreslovací jednotka %s není registrována!" #: ../ioncore/gr.c:136 #, c-format msgid "Unable to find brush for style '%s'." msgstr "Nemohu najít ¹tìtec pro styl \"%s\"." #: ../ioncore/gr.c:652 msgid "No drawing engines loaded, trying \"de\"." msgstr "Nebyly nahrány ¾ádné vykreslovací jednotky, zkou¹ím \"%s\"." #: ../ioncore/frame-draw.c:311 msgid "" msgstr "" #: ../ioncore/group.c:183 ../mod_tiling/tiling.c:89 #, c-format msgid "Error reparenting %s." msgstr "Chyba zmìny rodièe %s." #: ../ioncore/group.c:708 msgid "'bottom' already set." msgstr "'bottom' je ji¾ nastaven." #: ../ioncore/navi.c:42 msgid "Invalid parameter." msgstr "Neplatný parametr" #: ../ioncore/navi.c:69 msgid "Invalid direction parameter." msgstr "Neplatný parametr smìru." #: ../ioncore/group-ws.c:48 #, c-format msgid "Unknown placement method \"%s\"." msgstr "Neznámý zpùsob umístìní \"%s\"." #: ../ioncore/detach.c:174 msgid "Failed to reattach." msgstr "Znovupøipojení selhalo." #: ../ioncore/screen-notify.c:187 msgid "act: " msgstr "akt: " # #: ../mod_tiling/tiling.c:70 msgid "Split not on workspace." msgstr "Rozdìlení není na pracovní plo¹e." #: ../mod_tiling/tiling.c:345 msgid "Unable to create a node for status display." msgstr "Nemohu vytvoøit uzel pro stavový øádek." #: ../mod_tiling/tiling.c:358 msgid "Unable to create new split for status display." msgstr "Nemohu vytvoøit nové rozdìlení pro stavový øádek." #: ../mod_tiling/tiling.c:709 msgid "Tiling in useless state." msgstr "Dl¾dice v neu¾iteèném stavu." #: ../mod_tiling/tiling.c:923 msgid "Invalid direction" msgstr "Neplatný smìr" # #: ../mod_tiling/tiling.c:956 ../mod_tiling/split.c:1030 msgid "Invalid node." msgstr "Neplatný uzel." # #: ../mod_tiling/tiling.c:975 msgid "Unable to split." msgstr "Nemohu rozdìlit." #: ../mod_tiling/tiling.c:1190 msgid "Nil parameter." msgstr "Prázdný parametr." #: ../mod_tiling/tiling.c:1195 msgid "Manager doesn't match." msgstr "Správce se neshoduje." #: ../mod_tiling/tiling.c:1232 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:1323 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:1387 msgid "No suitable split here." msgstr "®ádné vhodné rozdìlení." # #: ../mod_tiling/tiling.c:1423 msgid "Could not get split tree." msgstr "Nemohu získat strom rozdìlení" #: ../mod_tiling/tiling.c:1444 msgid "Workspace already has a status display node." msgstr "Pracovní plocha ji¾ obsahuje stavový displej." # #: ../mod_tiling/tiling.c:1482 msgid "Missing region parameters." msgstr "Chybìjící parametry oblasti." #: ../mod_tiling/tiling.c:1526 ../mod_tiling/splitfloat.c:777 msgid "Invalid direction." msgstr "Neplatný smìr." # #: ../mod_tiling/tiling.c:1601 msgid "No split type given." msgstr "Nebyl zadán typ rozdìlení." #: ../mod_tiling/tiling.c:1614 msgid "Unknown split type." msgstr "Neznámý typ rozdìlení." # # #: ../mod_tiling/tiling.c:1654 msgid "The workspace is empty." msgstr "Pracovní plocha je prázdná." #: ../mod_tiling/placement.c:101 #, 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:536 msgid "Unable to move the status display out of way." msgstr "Nemohu pøesunout stavový øádek tak, aby nezavazel." #: ../mod_tiling/split.c:949 msgid "REGION_RQGEOM_TRYONLY unsupported for status display." msgstr "REGION_RQGEOM_TRYONLY není stavovým displejem podporován." # #: ../mod_tiling/split.c:1102 msgid "Splitting the status display is not allowed." msgstr "Rozdìlení stavového displeje není povoleno." #: ../mod_tiling/split.c:1128 ../mod_tiling/splitfloat.c:900 msgid "Unable to split: not enough free space." msgstr "Nelze rozdìlit: nedostatek volného místa." #: ../mod_tiling/split.c:1881 #, c-format msgid "Unable to get configuration for %s." msgstr "Nemohu získat nastavení pro %s." #: ../mod_tiling/split-stdisp.c:599 ../mod_tiling/split-stdisp.c:624 msgid "Status display in bad split configuration." msgstr "Stavový øádek se nachází v chybné konfiguraci rozdìlení." #: ../mod_tiling/split-stdisp.c:664 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:69 ../mod_tiling/ops.c:117 msgid "Not member of a group" msgstr "Není èlenem skupiny" #: ../mod_tiling/ops.c:74 msgid "Manager group already has bottom" msgstr "Øídící skupina ji¾ má dno" #: ../mod_tiling/ops.c:151 msgid "Unable to move a region from tiling to group." msgstr "Nemohu pøesunout regiion z dlá¾dìní do skupiny." #: ../mod_query/wedln.c:811 msgid "history" msgstr "historie" #: ../mod_query/fwarn.c:32 msgid "Error:\n" msgstr "Chyba:\n" # #: ../mod_menu/menu.c:598 msgid "Empty menu." msgstr "Prázdné menu." # #: ../mod_sm/sm.c:108 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:124 msgid "Unable to create scratchpad." msgstr "Nemohu vytvoøit poznámkový blok." #: ../mod_statusbar/main.c:74 msgid "reading a pipe" msgstr "ètu rouru" #: ../mod_statusbar/main.c:163 msgid "ion-statusd timed out." msgstr "Èas ion-statusd vypr¹el." #: ../mod_statusbar/statusbar.c:1079 #, c-format msgid "[ %date || load: %load ] %filler%systray" msgstr "[ %date || vytí¾ení: %load ] %filler%systray" #: ../de/init.c:65 #, c-format msgid "Border attribute %s sanity check failed." msgstr "Selhala kontrola atributu okraje %s." #: ../de/init.c:88 #, c-format msgid "Unknown border style \"%s\"." msgstr "Neznámý styl okraje \"%s\"." #: ../de/init.c:108 #, c-format msgid "Unknown border side configuration \"%s\"." msgstr "Neznámé nastavení okraje \"%s\"." #: ../de/init.c:144 #, c-format msgid "Unable to allocate colour \"%s\"." msgstr "Nemohu alokovat barvu \"%s\"." #: ../de/init.c:220 #, c-format msgid "Corrupt substyle table %d." msgstr "Poru¹ená tabulka stylù %d." #: ../de/init.c:253 #, c-format msgid "Unknown text alignment \"%s\"." msgstr "Neznámý textový prvek \"%s\"." #: ../de/font.c:44 #, 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:56 #, c-format msgid "Could not load font \"%s\", trying \"%s\"" msgstr "Nemohu nahrát font \"%s\", zkou¹ím \"%s\"." #: ../de/font.c:60 msgid "Failed to load fallback font." msgstr "Nahrání zálo¾ního fontu selhalo." #: ../de/style.c:291 #, c-format msgid "Style is still in use [%d] but the module is being unloaded!" msgstr "Styl se stále pou¾ívá [%d], ale modul se ji¾ ru¹í z pamìti!" #: ../ion/ion.c:39 ../pwm/pwm.c:39 msgid "X display to use" msgstr "X displej, který se má pou¾ít" #: ../ion/ion.c:42 ../pwm/pwm.c:42 msgid "Configuration file" msgstr "Konfiguraèní soubor" #: ../ion/ion.c:45 ../pwm/pwm.c:45 msgid "Add directory to search path" msgstr "Pøidá adresáø do prohledávané cesty" #: ../ion/ion.c:48 ../pwm/pwm.c:48 msgid "Manage default screen only" msgstr "Spravuje pouze výchozí obrazovku" #: ../ion/ion.c:51 ../pwm/pwm.c:51 msgid "Name of session (affects savefiles)" msgstr "Jméno sezení (ovlivní místo ulo¾ení)" #: ../ion/ion.c:54 ../pwm/pwm.c:54 msgid "Session manager client ID" msgstr "Klientské ID správce sezení" #: ../ion/ion.c:57 ../pwm/pwm.c:57 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:61 ../pwm/pwm.c:61 msgid "Show this help" msgstr "Zobrazí tuto nápovìdu" #: ../ion/ion.c:64 ../pwm/pwm.c:64 msgid "Show program version" msgstr "Zobrazí verzi programu" #: ../ion/ion.c:67 ../pwm/pwm.c:67 msgid "Show about text" msgstr "Zobrazí nìco o programu" #: ../ion/ion.c:82 msgid "Could not get user configuration file directory." msgstr "Nemohu získat u¾ivatelùv adresáø s nastavením." #: ../ion/ion.c:96 #, c-format msgid "%s/welcome.txt" msgstr "%s/welcome.cs.txt" #: ../ion/ion.c:129 ../pwm/pwm.c:76 #, c-format msgid "" "Usage: %s [options]\n" "\n" msgstr "" "Pou¾ití: %s [volby]\n" "\n" # #: ../ion/ion.c:197 ../pwm/pwm.c:147 msgid "Invalid command line." msgstr "Neplatná pøíkazová øádka." #: ../ion/ion.c:219 msgid "Ion startup error log:\n" msgstr "Záznam chyb pøi startu Ionu:\n" #: ../ion/ion.c:230 ../pwm/pwm.c:180 msgid "Refusing to start due to encountered errors." msgstr "Odmítám se spustit kvùli zaznamenaným chybám." #: ../pwm/pwm.c:169 msgid "PWM startup error log:\n" msgstr "Záznam chyb pøi startu PWM:\n" #: ../../libextl-3/readconfig.c:86 msgid "$HOME not set" msgstr "Promìnná $HOME není nastavená" #: ../../libextl-3/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-3/readconfig.c:254 #, c-format msgid "Falling back to %s." msgstr "Nouzovì pou¾ívám %s." #: ../../libextl-3/readconfig.c:474 #, c-format msgid "Unable to create session directory \"%s\"." msgstr "Nemohu vytvoøit adresáø pro sezení \"%s\"." #: ../../libextl-3/luaextl.c:117 msgid "Lua stack full." msgstr "Lua zásobník je plný." # #: ../../libextl-3/luaextl.c:143 msgid "Unknown Lua error." msgstr "Neznámá Lua chyba." #: ../../libextl-3/luaextl.c:490 msgid "Stack trace:" msgstr "Výpis zásobníku:" #: ../../libextl-3/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-3/luaextl.c:515 msgid "" "\n" " [Skipping unnamed C functions.]" msgstr "" "\n" " [Pøeskakuji nepojmenované C funkce.]" #: ../../libextl-3/luaextl.c:566 msgid "Internal error." msgstr "Interní chyba." #: ../../libextl-3/luaextl.c:585 msgid "Unable to initialize Lua." msgstr "Nemohu inicializovat Lua." #: ../../libextl-3/luaextl.c:1469 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-3/luaextl.c:1489 msgid "Returned dead object." msgstr "Vrácen mrtvý objekt." #: ../../libextl-3/luaextl.c:1492 #, 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-3/luaextl.c:1528 ../../libextl-3/luaextl.c:1883 msgid "Stack full." msgstr "Zásobník je plný." #: ../../libextl-3/luaextl.c:1894 #, c-format msgid "Argument %d to %s is a dead object." msgstr "Argument %d pro %s je mrtvý objekt." #: ../../libextl-3/luaextl.c:1897 #, 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-3/luaextl.c:1960 msgid "L1 call handler upvalues corrupt." msgstr "Obsluha volání L1 je poru¹ená." #: ../../libextl-3/luaextl.c:1965 msgid "Called function has been unregistered." msgstr "Volaná funkce byla byla odregistrována." #: ../../libextl-3/luaextl.c:1976 #, 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-3/luaextl.c:2089 #, 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-3/luaextl.c:2480 msgid "Maximal serialisation depth reached." msgstr "Dosa¾ena maximální hloubka serializace." #: ../../libextl-3/luaextl.c:2501 #, c-format msgid "Unable to serialise type %s." msgstr "Nemohu serializovat typ %s." #: ../../libextl-3/luaextl.c:2532 msgid "-- This file has been generated by %s. Do not edit.\n" msgstr "-- Tento soubor byl vytvoøen %sem. Neupravujte jej.\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 "" "Kontrola typu selhala ve druhé úrovni obsluhy volání pro parametr %d " "(obdr¾el %s, oèekával %s)." msgid "Scroll the message or completions up/down." msgstr "Roluje zprávu nebo seznam dokonèení nahoru/dolù." 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 "Close the query and execute bound action." msgstr "Zavøe dotaz a spustí svázanou akci." msgid "Complete from history" msgstr "Doplní z historie." msgid "Try to complete the entered text or cycle through completions." msgstr "Zkusí doplnit zadaný text nebo bude cyklovat mezi mo¾nostmi." # msgid "Clear mark/cancel selection." msgstr "Sma¾e znaèku/zru¹í výbìr." # msgid "Copy selection." msgstr "Zkopíruje výbìr." # msgid "Cut selection." msgstr "Vyjme výbìr." # msgid "Set mark/begin selection." msgstr "Nastaví znaèku/zaèátek výbìru." msgid "Paste from the clipboard." msgstr "Vlo¾í ze schránky." msgid "Select next/previous (matching) history entry." msgstr "Vybere dal¹í/pøedchozí (odpovídající) polo¾ku historie." msgid "Transpose characters." msgstr "Transponuje znaky." msgid "Delete the whole line." msgstr "Sma¾e celý øádek." msgid "Delete to end of line." msgstr "Sma¾e text do konce øádky." msgid "Delete one word forward/backward." msgstr "Sma¾e slovo pøed/za kurzorem." msgid "Delete previous character." msgstr "Sma¾e pøedchozí znak." msgid "Delete next character." msgstr "Sma¾e následující znak." msgid "Skip one word forward/backward." msgstr "Pøeskoèí slovo dopøedu/dozadu." msgid "Go to end/beginning." msgstr "Skoèí na konec/zaèátek." msgid "Move one character forward/backward." msgstr "Posune se o znak vpøed/vzad." msgid "Kill" msgstr "Ukonèit" msgid "Attach tagged" msgstr "Pøipojit oznaèené" msgid "Rename" msgstr "Pøejmenovat" msgid "Close" msgstr "Zavøít" msgid "De/reattach" msgstr "Od/pøipojit" msgid "Toggle tag" msgstr "(Od)znaèit" msgid "Window info" msgstr "Informace o oknì" msgid "Clear tags" msgstr "Vyèistit znaèky" msgid "Exit" msgstr "Ukonèit" msgid "Restart TWM" msgstr "Restartovat TWM" msgid "Restart" msgstr "Restartovat" msgid "Save" msgstr "Ulo¾it" msgid "Session" msgstr "Sezení" msgid "Styles" msgstr "Styly" msgid "About Ion" msgstr "O Ionu" msgid "Help" msgstr "Nápovìda" msgid "Lock screen" msgstr "Zamknout obrazovku" msgid "Terminal" msgstr "Terminál" # msgid "Run..." msgstr "Spustit..." # msgid "Move in specified direction." msgstr "Posune se v zadaném smìru." msgid "Shrink in specified direction." msgstr "Zmen¹uje v zadaném smìru." # msgid "Grow in specified direction." msgstr "Roste v zadaném smìru." msgid "End the resize mode." msgstr "Ukonèí re¾im zmìny velikosti." msgid "Cancel the resize mode." msgstr "Zru¹í re¾im zmìny velikosti." msgid "Move the frame." msgstr "Pøesune rám." msgid "Lower the frame." msgstr "Pøesune rám do pozadí" # msgid "Raise the frame." msgstr "Pøesune rám do popøedí." msgid "Toggle shade mode" msgstr "Pøepne stínový re¾im" msgid "Attach tagged objects to this frame." msgstr "Pøipojí oznaèené objekty k tomuto rámu." msgid "Maximize the frame horizontally/vertically." msgstr "Maximalizuje rám horizontálnì/vertikálnì." msgid "Move current object within the frame left/right." msgstr "Pøesune objekt v rámu vlevo/vpravo." msgid "Switch to next/previous object within the frame." msgstr "Pøepne se na dal¹í/pøedchozí 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 "Query for a client window to attach." msgstr "Dotá¾e se na klientské okno, které má pøipojit." 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 "Resize the frame." msgstr "Zmìní velikost rámu." 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 "Begin move/resize mode." msgstr "Zahájí re¾im pøesunu/zmìny velikosti." msgid "Display context menu." msgstr "Zobrazí kontextové menu." # 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 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 file to view." msgstr "Zeptá se na soubor, který chcete zobrazit." msgid "Query for file to edit." msgstr "Zeptá se na soubor, který chcete upravit." 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 Lua code to execute." msgstr "Zeptá se na lua kód, který má vykonat." msgid "Query for command line to execute." msgstr "Zeptá se na pøíkaz, který má spustit." # msgid "Run a terminal emulator." msgstr "Spustí emulátor terminálu." msgid "Show the Ion manual page." msgstr "Zobrazí manuálovou stránku Ionu." msgid "Query for manual page to be displayed." msgstr "Zeptá se na manuálovou stánku, kterou má zobrazit." msgid "Toggle tag of current object." msgstr "Pøepne oznaèení aktuálního objektu." msgid "Detach (float) or reattach an object to its previous location." msgstr "Odpojí nebo znovu pøipojí objekt z/do pùvodního umístìní." msgid "Close current object." msgstr "Zavøe aktuální objekt." msgid "Toggle client window group full-screen mode" msgstr "Pøepne skupinu klientského okna do celoobrazovkového re¾imu." # 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 "Kill client owning the client window." msgstr "Ukonèí klienta vlastnícího klientské okno." # 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 "Raise focused object, if possible." msgstr "Pokud je to mo¾né, pøesune zamìøený objekt nahoru." msgid "Backward-circulate focus." msgstr "Cykluje vzad." msgid "Forward-circulate focus." msgstr "Cykluje vpøed." msgid "Display the window list menu." msgstr "Zobrazí menu se seznamem oken." msgid "Display the main menu." msgstr "Zobrazí hlavní menu." # # msgid "Create a new workspace of chosen default type." msgstr "Vytvoøí novou pracovní plochu vybraného typu." # msgid "Go to next/previous screen on multihead setup." msgstr "Ve víceobrazovkovém nastavení pøejde na dal¹í/pøedchozí obrazovku." # msgid "Go to n:th screen on multihead setup." msgstr "U víceobrazovkového nastavení pøejde na n-tou obrazovku." # msgid "Clear all tags." msgstr "Vyèistí v¹echny znaèky." 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 "Switch to next/previous object within current screen." msgstr "Pøepne se na dal¹í/pøedchozí objekt v aktuální obrazovce." # 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 "List" msgstr "Seznam" msgid "New" msgstr "Nový" msgid "Dillo" msgstr "Dillo" msgid "Konqueror" msgstr "Konqueror" msgid "Links" msgstr "Links" msgid "Opera" msgstr "Opera" msgid "Rxvt" msgstr "Rxvt" msgid "W3M" msgstr "W3M" msgid "XTerm" msgstr "XTerm" # msgid "Workspaces" msgstr "Pracovní plochy" msgid "Programs" msgstr "Programy" msgid "Show the PWM manual page." msgstr "Zobrazí manuálovou stránku PWM." msgid "Toggle scratchpad." msgstr "Zapne poznámkový blok." 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 "No entry '%s'" msgstr "®ádný záznam '%s'" msgid "%s:" msgstr "%s:" msgid "Missing submenu " msgstr "Chybìjící podmenu" msgid "Unknown menu %s." msgstr "Neznámé menu %s." msgid "Lua code:" msgstr "Lua kód:" msgid "Manual page (%s):" msgstr "Manuálová stránka (%s):" msgid "SSH to:" msgstr "SSH na:" msgid "Failed to open ~/.ssh/config" msgstr "Selhalo otevøení ~/.ssh/config" msgid "Failed to open ~/.ssh/known_hosts" msgstr "Selhalo otevøení ~/.ssh/known_hosts" msgid "Run:" msgstr "Spustit:" msgid "View file:" msgstr "Prohlédnout soubor:" msgid "Edit file:" msgstr "Upravit soubor:" msgid "Workspace name:" msgstr "Název pracovní plochy:" msgid "Frame name:" msgstr "Název rámu:" msgid "Restart Notion (y/n)?" msgstr "Restartovat Notion (y/n)?" msgid "Exit Notion/Shutdown session (y/n)?" msgstr "Ukonèit Notion/ukonèit sezení (y/n)?" msgid "Go to or create workspace:" msgstr "Vytvoøit nebo pøejít na pracovní plochu:" msgid "Attach window:" msgstr "Pøipojit okno:" msgid "Go to window:" msgstr "Jít do okna:" msgid "New workspace layout (default):" msgstr "Rozlo¾ení nové pracovní plochy (default):" msgid "Unknown error" msgstr "Neznámá chyba" msgid "Unknown layout" msgstr "Neznámé rozlo¾ení." msgid "Cannot attach: different root windows." msgstr "Nemohu pøipojit: rùzná koøenová okna." msgid "Could not find client window %s." msgstr "Nemohu najít klientské okno %s." msgid "Too much result data" msgstr "Pøíli¹ mnoho výsledkù" msgid "Save look selection in %s?" msgstr "Ulo¾it nastavení vzhledu do %s?" msgid "Cannot save selection." msgstr "Nemohu ulo¾it výbìr." msgid "Unable to append to non-table menu" msgstr "Nelze pøidat do netabulkového menu" 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 "Recursive table - unable to deepcopy" msgstr "Rekurzivní tabulka - nemohu provést hlubokou kopii" msgid "Frame" msgstr "Rám" msgid "Screen" msgstr "Obrazovka" # msgid "Workspace" msgstr "Pracovní plocha" msgid "Tiling" msgstr "Dla¾dice" msgid "Tiled frame" msgstr "Dla¾dicový rám" msgid "Floating frame" msgstr "Plovoucí rám" msgid "Context menu:" msgstr "Kontextové menu:" msgid "Main menu:" msgstr "Hlavní menu:" msgid "Invalid guard %s." msgstr "Neplatná ochrana %s." msgid "Error compiling guard: %s" msgstr "Chyba pøi sestavení strá¾ce: %s" msgid "Error in command string: " msgstr "Chyba v pøíkazu: " msgid "Invalid command" msgstr "Neplatný pøíkaz" msgid "Not a directory." msgstr "Není adresáøem." msgid "Could not find %s" msgstr "Nemohu najít %s" msgid "ion-statusd quit." msgstr "ion-statusd konèí." msgid "Errors starting ion-statusd:\n" msgstr "Chyba pøi startu ion-statusd:\n" msgid "Failed to start ion-statusd." msgstr "Selhalo spu¹tìní ion-statusd." msgid "Screen not found." msgstr "Obrazovka nebyla nalezna." msgid "Screen already has an stdisp. Refusing to create a statusbar." msgstr "Obrazovka ji¾ obsahuje stdisp. Odmítám vytvoøit stavový øádek." msgid "Failed to create statusbar." msgstr "Selhalo vytvoøení stavového øádku." msgid "Split current frame vertically." msgstr "Rozpùlí rám vertiká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 horizontally." msgstr "Rozpùlí rám horizontálnì." msgid "Destroy current frame." msgstr "Znièí aktuální 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 "Destroy frame" msgstr "Znièit rám" msgid "Split vertically" msgstr "Rozpùlit vertikálnì" msgid "Split horizontally" msgstr "Rozpùlit horizontálnì" msgid "Flip" msgstr "Obrátit" msgid "Transpose" msgstr "Transponovat" msgid "Untile" msgstr "Zru¹it dla¾dice" msgid "Float split" msgstr "Plovoucí okraj" msgid "At left" msgstr "Vlevo" msgid "At right" msgstr "Vpravo" msgid "Above" msgstr "Nahoøe" msgid "Below" msgstr "Dole" msgid "At root" msgstr "Na koøenu" msgid "New tiling" msgstr "Vytvoøit dla¾dice" msgid "Close the menu." msgstr "Zavøe menu." # # msgid "Activate current menu entry." msgstr "Aktivuje vybranou polo¾ku v menu." msgid "Select next/previous menu entry." msgstr "V menu vybere dal¹í/pøedchozí polo¾ku." msgid "Clear the menu's typeahead find buffer." msgstr "Vyma¾e buffer pro dopøedné hledání v menu." msgid "Toggle floating dock." msgstr "Zapne plovoucí dok." msgid "Pos-TL" msgstr "Pozice LH" msgid "Pos-TR" msgstr "Pozice PH" msgid "Pos-BL" msgstr "Pozice LD" msgid "Pos-BR" msgstr "Pozice PD" msgid "Grow-L" msgstr "Rùst vlevo" msgid "Grow-R" msgstr "Rùst vpravo" msgid "Grow-U" msgstr "Rùst nahoru" msgid "Grow-D" msgstr "Rùst 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 unsplit: Could not move client windows elsewhere within the " #~ "tiling." #~ msgstr "Nemohu slouèit: Nemohu pøesunout klientské okno nìkam do dla¾dic." #~ msgid "'based_on' for %s points back to the style itself." #~ msgstr "Èást 'based_on' stylu %s ukazuje sama na sebe." #~ msgid "Unknown base style. \"%s\"" #~ msgstr "Neznámý základní styl. \"%s\"" #~ msgid "Tag current object within the frame." #~ msgstr "Oznaèí aktuální objekt v rámu." #~ msgid "Xinerama sanity check failed; overlapping screens detected." #~ msgstr "Kontrola Xineramy selhala: byly nalezeny pøekrývající se obrazovky." #~ msgid "Xinerama sanity check failed; zero size detected." #~ msgstr "Kontrola Xineramy selhala: byla nalezena nulová velikost." #~ 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." #~ msgid "Error retrieving Xinerama information." #~ msgstr "Chyba pøi získávání informaci z Xineramy." #~ msgid "Unable to setup Xinerama screen %d." #~ msgstr "Nemohu nastavit obrazovku Xineramy %d." #~ msgid "Unable to setup X screen %d." #~ msgstr "Nemohu nastavit X obrazovku %d." #~ msgid "Refusing to destroy - not empty." #~ msgstr "Odmítám zru¹it - není prázdné." #~ msgid "Workspace not empty - refusing to destroy." #~ msgstr "Pracovní plocha není prázdná - odmítám zru¹it." #~ msgid "Nil frame." #~ msgstr "Prázdný rám." #~ msgid "The frame is not managed by the workspace." #~ msgstr "Rám není spravován pracovní plochou." #~ msgid "Already detached" #~ msgstr "Ji¾ je odpojen" #~ msgid "ion-statusd launch timeout." #~ msgstr "Èas spou¹tìní ion-statusd vypr¹el." #~ msgid "Use Xinerama screen information (default: 1/yes)" #~ msgstr "Pou¾ít Xineramu (implicitnì: 1/ano)" #~ msgid "Ignored: not compiled with Xinerama support" #~ msgstr "Ignorováno: program je sestaven bez podpory Xineramy" #~ msgid "Invalid parameter to -xinerama." #~ msgstr "Neplatný parametr pro -xinerama." #~ msgid "Use Xinerama screen information (default: 0/no)" #~ msgstr "Pou¾ít Xineramu (implicitnì: 0/ne) " #~ msgid "Detach window from tiled frame" #~ msgstr "Odpojí okno od dla¾dicového rámu" # #~ 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 "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" notion-3+2012042300/po/de.po000066400000000000000000001174661174530661200151470ustar00rootroot00000000000000# -*- encoding: utf-8 -*- # # German translations for Ion3 # # Copyright (C) Nicolas Schier 20008. # This file is distributed under the same license as the Ion3 package. # Nicolas Schier , 2008. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: Ion3\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2008-02-20 11:21+0100\n" "PO-Revision-Date: 2008-02-20 11:55+0100\n" "Last-Translator: Nicolas Schier " msgstr "" #: ../ioncore/group.c:185 ../mod_tiling/tiling.c:89 #, c-format msgid "Error reparenting %s." msgstr "Fehler bei Adoption von %s." #: ../ioncore/group.c:729 msgid "'bottom' already set." msgstr "'unten' ist bereits gesetzt." #: ../ioncore/navi.c:42 msgid "Invalid parameter." msgstr "Ungültiger Parameter." #: ../ioncore/navi.c:69 msgid "Invalid direction parameter." msgstr "Ungültiger Richtungsparameter." #: ../ioncore/group-ws.c:47 #, c-format msgid "Unknown placement method \"%s\"." msgstr "Unbekannte Plazierungsmethode \"%s\"." #: ../ioncore/detach.c:183 msgid "Failed to reattach." msgstr "Wiederanhängen schlug fehl." #: ../ioncore/screen-notify.c:188 msgid "act: " msgstr "Achtung: " #: ../mod_tiling/tiling.c:70 msgid "Split not on workspace." msgstr "Keine Spaltung der Arbeitsfläche." #: ../mod_tiling/tiling.c:345 msgid "Unable to create a node for status display." msgstr "Knoten für Statuszeile kann nicht erstellt werden." #: ../mod_tiling/tiling.c:358 msgid "Unable to create new split for status display." msgstr "Statuszeile kann nicht neu gespalten werden." #: ../mod_tiling/tiling.c:710 msgid "Tiling in useless state." msgstr "Teilung in unbenutzbarem Zustand." #: ../mod_tiling/tiling.c:924 msgid "Invalid direction" msgstr "Ungültige Richtung" #: ../mod_tiling/tiling.c:957 ../mod_tiling/split.c:1030 msgid "Invalid node." msgstr "Ungültiger Knoten." #: ../mod_tiling/tiling.c:976 msgid "Unable to split." msgstr "Spaltung nicht möglich." #: ../mod_tiling/tiling.c:1188 msgid "Nil parameter." msgstr "Nil Parameter." #: ../mod_tiling/tiling.c:1193 msgid "Manager doesn't match." msgstr "Verwalter passt nicht." #: ../mod_tiling/tiling.c:1230 msgid "The status display is not a valid parameter for this routine." msgstr "Die Statusanzeige ist kein gültiger Parameter für diese Routine." #: ../mod_tiling/tiling.c:1321 msgid "Refusing to float split directly containing the status display." msgstr "Verweigere fließende Spaltung die direkt die Statusanzeige beinhaltet." #: ../mod_tiling/tiling.c:1385 msgid "No suitable split here." msgstr "Keine passende Spaltung." #: ../mod_tiling/tiling.c:1421 msgid "Could not get split tree." msgstr "Spaltungsbaum nicht erreichbar." #: ../mod_tiling/tiling.c:1442 msgid "Workspace already has a status display node." msgstr "Arbeitsfläche hat bereits einen Statusanzeigeknoten." #: ../mod_tiling/tiling.c:1480 msgid "Missing region parameters." msgstr "Vermisse Region Parameter." #: ../mod_tiling/tiling.c:1524 ../mod_tiling/splitfloat.c:780 msgid "Invalid direction." msgstr "Ungültige Richtung." #: ../mod_tiling/tiling.c:1599 msgid "No split type given." msgstr "Kein Spaltungstyp angegeben." #: ../mod_tiling/tiling.c:1612 msgid "Unknown split type." msgstr "Unbekannter Spaltungstyp." #: ../mod_tiling/tiling.c:1652 msgid "The workspace is empty." msgstr "Die Arbeitsfläche ist leer." #: ../mod_tiling/placement.c:101 #, c-format msgid "" "Ooops... could not find a region to attach client window to on workspace %s." msgstr "Huch... konnte keine Region auf der Arbeitsfläche %s finden um ein Anwendungsfenster anzuhängen." #: ../mod_tiling/split.c:536 msgid "Unable to move the status display out of way." msgstr "Statusanzeige kann nicht weggeschoben werden." #: ../mod_tiling/split.c:949 msgid "REGION_RQGEOM_TRYONLY unsupported for status display." msgstr "REGION_RQGEOM_TRYONLY wird für die Statusanzeige nicht unterstützt." #: ../mod_tiling/split.c:1102 msgid "Splitting the status display is not allowed." msgstr "Spaltung der Statusanzeige ist nicht erlaubt." #: ../mod_tiling/split.c:1128 ../mod_tiling/splitfloat.c:903 msgid "Unable to split: not enough free space." msgstr "Kann nicht spalten: nicht genug Platz." #: ../mod_tiling/split.c:1891 #, c-format msgid "Unable to get configuration for %s." msgstr "Konfiguration für %s nicht erreichbar." #: ../mod_tiling/split-stdisp.c:599 ../mod_tiling/split-stdisp.c:624 msgid "Status display in bad split configuration." msgstr "Statusanzeige hat eine schlechte Spaltungskonfiguration." #: ../mod_tiling/split-stdisp.c:664 msgid "Status display badly located in split tree." msgstr "Statusanzeige im Spaltungsbaum fehlerhaft plaziert." #: ../mod_tiling/ops.c:69 ../mod_tiling/ops.c:117 msgid "Not member of a group" msgstr "Kein Mitglied einer Gruppe" #: ../mod_tiling/ops.c:74 msgid "Manager group already has bottom" msgstr "Verwaltergruppe hat bereits 'unten'" #: ../mod_tiling/ops.c:151 msgid "Unable to move a region from tiling to group." msgstr "Regionen können nicht von einer Teilung in eine Gruppe verschoben werden." #: ../mod_query/wedln.c:811 msgid "history" msgstr "Geschichte" #: ../mod_query/fwarn.c:32 msgid "Error:\n" msgstr "Fehler:\n" #: ../mod_menu/menu.c:598 msgid "Empty menu." msgstr "Leeres Menü." #: ../mod_sm/sm.c:108 msgid "Failed to set session directory." msgstr "Setzen des Sitzungsverzeichnisses schlug fehl." #: ../mod_sm/sm_session.c:86 msgid "Too many ICE connections." msgstr "Zu viele ICE Verbindungen." #: ../mod_sm/sm_session.c:228 msgid "Failed to save session state" msgstr "Speichern des Sitzungsstatus schlug fehl" #: ../mod_sm/sm_session.c:247 msgid "Failed to request save-yourself-phase2 from session manager." msgstr "Anforderung von Save-yourself-phase2 vom Sitzungsmanager schlug fehl." #: ../mod_sm/sm_session.c:296 msgid "SESSION_MANAGER environment variable not set." msgstr "Umgebungsvariable SESSION_MANAGER ist nicht gesetzt." #: ../mod_sm/sm_session.c:301 msgid "Session Manager: IceAddConnectionWatch failed." msgstr "Sitzungsmanager: IceAddConnectionWatch schlug fehl." #: ../mod_sm/sm_session.c:326 msgid "Unable to connect to the session manager." msgstr "Kann nicht mit dem Sitzungsmanager verbinden." #: ../mod_sp/main.c:124 msgid "Unable to create scratchpad." msgstr "Notizblock kann nicht erstellt werden." #: ../mod_statusbar/statusbar.c:1078 #, c-format msgid "[ %date || load: %load ] %filler%systray" msgstr "[ %date || load: %load ] %filler%systray" #: ../mod_statusbar/statusd-launch.c:51 msgid "reading a pipe" msgstr "lese eine Pipe" #: ../mod_statusbar/statusd-launch.c:137 msgid "ion-statusd timed out." msgstr "ion-statusd: Zeitüberschreitung." #: ../de/init.c:65 #, c-format msgid "Border attribute %s sanity check failed." msgstr "Randattribut %s Überprüfung schlug fehl." #: ../de/init.c:88 #, c-format msgid "Unknown border style \"%s\"." msgstr "Unbekannter Randstil \"%s\"." #: ../de/init.c:108 #, c-format msgid "Unknown border side configuration \"%s\"." msgstr "Unbekannte Randseitenkonfiguration \"%s\"." #: ../de/init.c:144 #, c-format msgid "Unable to allocate colour \"%s\"." msgstr "Kann Farbe \"%s\" nicht allokieren." #: ../de/init.c:220 #, c-format msgid "Corrupt substyle table %d." msgstr "Korrupte Unterstiltabelle." #: ../de/init.c:253 #, c-format msgid "Unknown text alignment \"%s\"." msgstr "Unbekannte Textausrichtung \"%s\"." #: ../de/font.c:44 #, c-format msgid "" "Fontset for font pattern '%s' implements context dependent drawing, which is " "unsupported. Expect clutter." msgstr "" "Schriftsatz für das Schriftartenpattern '%s' implementiert nicht unterstützte " "kontextabhängige Grafiken. Erwarte Cluster." #: ../de/font.c:56 #, c-format msgid "Could not load font \"%s\", trying \"%s\"" msgstr "Schriftart \"%s\" konnte nicht geladen werden, versuche \"%s\"" #: ../de/font.c:60 msgid "Failed to load fallback font." msgstr "Notfall-Schriftart konnte nicht geladen werden." #: ../de/style.c:291 #, c-format msgid "Style is still in use [%d] but the module is being unloaded!" msgstr "Stil wird benutzt [%d] aber das Modul wird entfernt!" #: ../ion/ion.c:40 ../pwm/pwm.c:40 msgid "X display to use" msgstr "Zu benutzendes X-Display" #: ../ion/ion.c:43 ../pwm/pwm.c:43 msgid "Configuration file" msgstr "Konfigurationsdatei" #: ../ion/ion.c:46 ../pwm/pwm.c:46 msgid "Add directory to search path" msgstr "Füge Verzeichnis zum Suchpfad hinzu" #: ../ion/ion.c:49 ../pwm/pwm.c:49 msgid "Manage default screen only" msgstr "Nur Standardbildschirm verwalten" #: ../ion/ion.c:52 ../pwm/pwm.c:52 msgid "Name of session (affects savefiles)" msgstr "Sitzungsname (beeinflußt Sitzungsdateien)" #: ../ion/ion.c:55 ../pwm/pwm.c:55 msgid "Session manager client ID" msgstr "Sitzungsmanager-Anwendungs-ID" #: ../ion/ion.c:58 ../pwm/pwm.c:58 msgid "Do not create startup error log and display it with xmessage." msgstr "Erstelle keine Fehler-beim-Starten-Liste" #: ../ion/ion.c:62 ../pwm/pwm.c:62 msgid "Show this help" msgstr "Zeige diese Hilfe" #: ../ion/ion.c:65 ../pwm/pwm.c:65 msgid "Show program version" msgstr "Zeige Programmversion" #: ../ion/ion.c:68 ../pwm/pwm.c:68 msgid "Show about text" msgstr "Zeige Informationenstext" #: ../ion/ion.c:83 msgid "Could not get user configuration file directory." msgstr "Benutzer-Konfigurationsverzeichnis unbekannt." #: ../ion/ion.c:97 #, c-format msgid "%s/welcome.txt" msgstr "%s/welcome.de.txt" #: ../ion/ion.c:130 ../pwm/pwm.c:77 #, c-format msgid "" "Usage: %s [options]\n" "\n" msgstr "" "Syntax: %s [Optionen]\n" "\n" #: ../ion/ion.c:208 ../pwm/pwm.c:158 msgid "Invalid command line." msgstr "Ungültige Kommandozeile." #: ../ion/ion.c:230 msgid "Ion startup error log:\n" msgstr "Ion Fehler-beim-Starten-Liste:\n" #: ../ion/ion.c:241 ../pwm/pwm.c:191 msgid "Refusing to start due to encountered errors." msgstr "Verweigere zu starten, da Fehler auftraten." #: ../pwm/pwm.c:180 msgid "PWM startup error log:\n" msgstr "PWM Fehlerlog:\n" #: ../libextl/readconfig.c:86 msgid "$HOME not set" msgstr "$HOME nicht gesetzt" #: ../libextl/readconfig.c:113 msgid "User directory not set. Unable to set session directory." msgstr "Benutzerverzeichnis ist nicht gesetzt. Sitzungsverzeichnis kann nicht gesetzt werden." #: ../libextl/readconfig.c:254 #, c-format msgid "Falling back to %s." msgstr "Falle zurück auf %s." #: ../libextl/readconfig.c:474 #, c-format msgid "Unable to create session directory \"%s\"." msgstr "Sitzungsverzeichnis \"%s\" konnte nicht erstellt werden." #: ../libextl/luaextl.c:117 msgid "Lua stack full." msgstr "Lua stack ist voll." #: ../libextl/luaextl.c:143 msgid "Unknown Lua error." msgstr "Unbekannte Lua Fehler." #: ../libextl/luaextl.c:490 msgid "Stack trace:" msgstr "Stack Trace:" #: ../libextl/luaextl.c:497 #, c-format msgid "" "\n" "(Unable to get debug info for level %d)" msgstr "" "\n" "(Debuginformationen für Level %d nicht verfügbar)" #: ../libextl/luaextl.c:515 msgid "" "\n" " [Skipping unnamed C functions.]" msgstr "" "\n" " [Überspringe unbenannte C-Funktionen.]" #: ../libextl/luaextl.c:566 msgid "Internal error." msgstr "Interner Fehler." #: ../libextl/luaextl.c:585 msgid "Unable to initialize Lua." msgstr "Lua konnte nicht initialisiert werden." #: ../libextl/luaextl.c:1469 msgid "" "Too many return values. Use a C compiler that has va_copy to support more." msgstr "" "Zu viele Rückgabewerte. Benutze einen C-Compiler mit va_copy-Unterstützung." #: ../libextl/luaextl.c:1489 msgid "Returned dead object." msgstr "Totes Objekt zurückgegeben." #: ../libextl/luaextl.c:1492 #, c-format msgid "Invalid return value (expected '%c', got lua type \"%s\")." msgstr "Ungültiger Rückgabewert (erwartete '%c', bekam Lua-Typ \"%s\")." #: ../libextl/luaextl.c:1528 ../libextl/luaextl.c:1883 msgid "Stack full." msgstr "Stack voll." #: ../libextl/luaextl.c:1894 #, c-format msgid "Argument %d to %s is a dead object." msgstr "Argument %d für/an %s ist ein totes Objekt." #: ../libextl/luaextl.c:1897 #, c-format msgid "" "Argument %d to %s is of invalid type. (Argument template is '%s', got lua " "type %s)." msgstr "" "Argument %d an/für ist ungültien Typs. (Argumentvorlage ist '%s', bekam " "Lua-Typ %s)." #: ../libextl/luaextl.c:1960 msgid "L1 call handler upvalues corrupt." msgstr "L1-Aufrufhändler korrupt aufgewertet." #: ../libextl/luaextl.c:1965 msgid "Called function has been unregistered." msgstr "Aufgerufene Funktion wurde de-registriert." #: ../libextl/luaextl.c:1976 #, c-format msgid "Attempt to call an unsafe function \"%s\" in restricted mode." msgstr "Versuch, die unsichere Funktion \"%s\" im beschränkten Modus aufzurufen." #: ../libextl/luaextl.c:2089 #, c-format msgid "" "Function '%s' has more parameters than the level 1 call handler can handle" msgstr "" "Funktion '%s' hat mehr Parameter als der Ebene-1-Aufrufhändler händeln kann" #: ../libextl/luaextl.c:2480 msgid "Maximal serialisation depth reached." msgstr "Maximale Serialisierungstiefe erreicht." #: ../libextl/luaextl.c:2501 #, c-format msgid "Unable to serialise type %s." msgstr "Typ %s kann nicht serialisiert werden." #: ../libextl/luaextl.c:2532 msgid "-- This file has been generated by %s. Do not edit.\n" msgstr "-- Diese Datei wurde durch %s generiert. Nicht editieren.\n" #: ../libextl/misc.c:17 #, c-format msgid "" "Type checking failed in level 2 call handler for parameter %d (got %s, " "expected %s)." msgstr "" "Typprüfung schlug fehl im Ebene-2-Aufrufhändler für Parameter %d " "(bekam %s, erwartete %s)." msgid "Scroll the message or completions up/down." msgstr "Nachricht oder Vervollständigung nach oben/unten scrollen." msgid "Close the query/message box, not executing bound actions." msgstr "Schließen der Abfrage-/Nachrichtenkiste, nicht-Ausführen von gebundenen Aktionen." msgid "Close the query and execute bound action." msgstr "Abfrage schließen und gebundene Aktion ausführen." msgid "Complete from history" msgstr "Aus der Geschichte vervollständigen." msgid "Try to complete the entered text or cycle through completions." msgstr "Versuche den eingegebenen Text zu vervollständigen oder rotiere durch Vervollständigungen." msgid "Clear mark/cancel selection." msgstr "Markierung entfernen/Selektion abbrechen." msgid "Copy selection." msgstr "Markiertes kopieren" msgid "Cut selection." msgstr "Markiertes ausschneiden." msgid "Set mark/begin selection." msgstr "Markieren/Selektion anfangen." msgid "Paste from the clipboard." msgstr "Aus der Zwischenablage einfügen." msgid "Select next/previous (matching) history entry." msgstr "Nächsten/letzten (passenden) Geschichtseintrag auswählen." msgid "Transpose characters." msgstr "Zeichen austauschen." msgid "Delete the whole line." msgstr "Ganze Zeile löschen." msgid "Delete to end of line." msgstr "Bis zum Ende der Zeile löschen." msgid "Delete one word forward/backward." msgstr "Lösche ein Wort vorwärts/rückwärts." msgid "Delete previous character." msgstr "Vorhergehendes Zeichen löschen." msgid "Delete next character." msgstr "Nächstes Zeichen löschen." msgid "Skip one word forward/backward." msgstr "Ein Wort vorwärts/rückwärts überspringen." msgid "Go to end/beginning." msgstr "Gehe an den Anfang/ans Ende." msgid "Move one character forward/backward." msgstr "Ein Zeichen vorwärts/rückwärts." msgid "Kill" msgstr "Zerstören" msgid "Attach tagged" msgstr "Markierte anhängen" msgid "Rename" msgstr "Umbenennen" msgid "Close" msgstr "Schließen" msgid "De/reattach" msgstr "Aus- oder wieder anhängen" msgid "Toggle tag" msgstr "(De)markieren" msgid "Window info" msgstr "Fensterinformationen" msgid "Clear tags" msgstr "Alle Markierungen entfernen" msgid "Exit" msgstr "Beenden" msgid "Restart TWM" msgstr "Neustart TWM" msgid "Restart" msgstr "Neustart" msgid "Save" msgstr "Speichern" msgid "Session" msgstr "Sitzung" msgid "Styles" msgstr "Layout" msgid "About Ion" msgstr "Ion Informationstext" msgid "Help" msgstr "Hilfe" msgid "Lock screen" msgstr "Sperren des Bildschirms" msgid "Terminal" msgstr "Terminal" msgid "Run..." msgstr "Ausführen..." msgid "Move in specified direction." msgstr "Verschiebe in angegebene Richtung." msgid "Shrink in specified direction." msgstr "Verkleinere in angegebener Richtung." msgid "Grow in specified direction." msgstr "Vergrößere in angegebener Richtung." msgid "End the resize mode." msgstr "Ende des Größenänderungsmodus." msgid "Cancel the resize mode." msgstr "Größenänderungsmodus abbrechen." msgid "Move the frame." msgstr "Rahmen verschieben." msgid "Lower the frame." msgstr "Rahmen absenken." msgid "Raise the frame." msgstr "Rahmen anheben." msgid "Toggle shade mode" msgstr "Aus/in Schattenmodus wechseln." msgid "Attach tagged objects to this frame." msgstr "Markierte Objekte an diesen Rahmen anfügen." msgid "Maximize the frame horizontally/vertically." msgstr "Rahmen horizontal/vertikal maximieren." msgid "Move current object within the frame left/right." msgstr "Aktuelles Objekt innerhalb des Rahmen nach links/rechts bewegen." msgid "Switch to next/previous object within the frame." msgstr "Wechsel zum nächsten/vorhergehenden Objekt innerhalb dieses Rahmens." msgid "Switch to n:th object within the frame." msgstr "Wechsel zum n. Objekt innerhalb des Rahmens." msgid "Query for a client window to attach." msgstr "Abfrage nach einem Anwendungsfenster das angefügt werden soll." msgid "Move objects between frames by dragging and dropping the tab." msgstr "Verschiebe Objekte zwischen Rahmen durch Anheben und Fallenlassen des Tabs." msgid "Resize the frame." msgstr "Rahmengröße ändern." msgid "Switch the frame to display the object indicated by the tab." msgstr "Rahmen wechseln, damit das vom Tab indizierte Objekt angezeigt wird." msgid "Begin move/resize mode." msgstr "Verschiebe-/Größenänderungsmodus starten." msgid "Display context menu." msgstr "Kontextmenü anzeigen." msgid "Detach (float) or reattach an object to its previous location." msgstr "Aushängen (fließen) oder Wiedereinhängen eines Objektes in seine frühere Lokation." msgid "Query for a client window to go to." msgstr "Nach einem Anwendungsfenster fragen und dort hinspringen." msgid "Query for workspace to go to or create a new one." msgstr "Nach einer Arbeitsfläche fragen und dort hinspringen oder eine neue erstellen." msgid "Query for file to view." msgstr "Nach einer Datei zum Anzeigen fragen." msgid "Query for file to edit." msgstr "Nach einer Datei zum Editieren fragen." msgid "Query for host to connect to with SSH." msgstr "Nach einem SSH-Server zum Verbinden fragen." msgid "Query for Lua code to execute." msgstr "Nach Lua-Code zum Ausführen fragen." msgid "Query for command line to execute." msgstr "Nach einer Kommandozeile zum Ausführen fragen." msgid "Run a terminal emulator." msgstr "Terminalemulator starten." msgid "Show the Ion manual page." msgstr "Ion Man-Page anzeigen." msgid "Query for manual page to be displayed." msgstr "Nach einer Man-Page fragen." msgid "Toggle tag of current object." msgstr "Aktuelles Objekt (de-)markieren." msgid "Close current object." msgstr "Aktuelles Objekt schließen." msgid "Toggle client window group full-screen mode" msgstr "Anwendungsfenster in/aus Vollbildmodus darstellen" msgid "Send next key press to the client window. Some programs may not allow this by default." msgstr "Sende nächsten Tastendruck an das Anwendungsfenster. Manche Programme unterstützen dies nicht standardmäßig." msgid "Kill client owning the client window." msgstr "Anwendung dieses Anwendungsfensters abschießen." msgid "Nudge the client window. This might help with some programs' resizing problems." msgstr "Fenster anstupsen; hilft bei manchen Darstellungsproblemen." msgid "Raise focused object, if possible." msgstr "Fokussiertes Objekt anheben, falls möglich." msgid "Backward-circulate focus." msgstr "Rückwärts-zirkulierender Fokus." msgid "Forward-circulate focus." msgstr "Vorwärts-zirkulierender Fokus." msgid "Display the window list menu." msgstr "Fensterliste anzeigen." msgid "Display the main menu." msgstr "Hauptmenü anzeigen." msgid "Create a new workspace of chosen default type." msgstr "Neue Arbeitsfläche des ausgewählten Typs erstellen." msgid "Go to next/previous screen on multihead setup." msgstr "Gehe zum nächsten/vorhergehen Bildschirm (Multihead Setup)." msgid "Go to n:th screen on multihead setup." msgstr "Gehe zum n.ten Bildschirm (Multihead Setup)." msgid "Clear all tags." msgstr "Alle Markierungen entfernen." msgid "Go to first region demanding attention or previously active one." msgstr "Gehe zur ersten aufmerksamkeitsdefizitären Region oder zur letzten aktiven." msgid "Switch to next/previous object within current screen." msgstr "Wechsle zum nächsten/vorhergehenden Objekt auf dem aktuellen Bildschirm." msgid "Switch to n:th object (workspace, full screen client window) within current screen." msgstr "Wechsle zum n. Objekt (Arbeitsfläche, Vollbildanwendungsfenster) innerhalb des aktuellen Bildschirms." msgid "List" msgstr "Liste" msgid "New" msgstr "Neu" msgid "Dillo" msgstr "Dillo" msgid "Konqueror" msgstr "Konqueror" msgid "Links" msgstr "Links" msgid "Opera" msgstr "Opera" msgid "Rxvt" msgstr "Rxvt" msgid "W3M" msgstr "W3M" msgid "XTerm" msgstr "XTerm" msgid "Workspaces" msgstr "Arbeitsflächen" msgid "Programs" msgstr "Programme" msgid "Show the PWM manual page." msgstr "Zeige die PWM Man-page." msgid "Toggle scratchpad." msgstr "Schmierblock ein-/ausblenden." msgid "\n%sClass: %s\n%sRole: %s\n%sInstance: %s\n%sXID: 0x%x" msgstr "\n%sClass: %s\n%sRole: %s\n%sInstance: %s\n%sXID: 0x%x" msgid "No entry '%s'" msgstr "Kein Eintrag '%s'" msgid "%s:" msgstr "%s:" msgid "Missing submenu " msgstr "Vermisse das Untermenü " msgid "Unknown menu %s." msgstr "Unbekanntes Menü %s." msgid "Lua code:" msgstr "Lua Quellcode:" msgid "Manual page (%s):" msgstr "Man-page (%s):" msgid "SSH to:" msgstr "SSH:" msgid "Failed to open ~/.ssh/config" msgstr "Öffnen von ~/.ssh/config schlug fehl" msgid "Failed to open ~/.ssh/known_hosts" msgstr "Öffnen von ~/.ssh/known_hosts schlug fehl" msgid "Run:" msgstr "Ausführen:" msgid "View file:" msgstr "Zeige Datei:" msgid "Edit file:" msgstr "Editiere Datei:" msgid "Workspace name:" msgstr "Neuer Arbeitsflächenname:" msgid "Frame name:" msgstr "Rahmenname:" msgid "Restart Notion (y/n)?" msgstr "Notion neustarten (y[a]/n[ein])/?" msgid "Exit Notion/Shutdown session (y/n)?" msgstr "Notion beenden/Sitzung herunterfahren (y/n)?" msgid "Go to or create workspace:" msgstr "Gehe zu oder erstelle Arbeitsfläche:" msgid "Attach window:" msgstr "Fenster einhängen:" msgid "Go to window:" msgstr "Gehe zu Fenter:" msgid "New workspace layout (default):" msgstr "Layout der neuen Arbeitsfläche (default):" msgid "Unknown error" msgstr "Unbekannter Fehler" msgid "Unknown layout" msgstr "Unbekanntes Layout" msgid "Cannot attach: different root windows." msgstr "Kann nicht anhängen: verschiedene Root Fenster." msgid "Could not find client window %s." msgstr "Anwendungsfenster %s nicht gefunden." msgid "Too much result data" msgstr "Zu viele Ergebniswerte" msgid "Could not find %s" msgstr "%s nicht gefunden" msgid "Not a directory." msgstr "Kein Verzeichnis." msgid "Invalid command" msgstr "Ungültiges Kommando" msgid "Error in command string: " msgstr "Fehler in Kommandozeichenkette: " msgid "Error compiling guard: %s" msgstr "Fehler beim Kompilieren eines Wächters: %s" msgid "Invalid guard %s." msgstr "Ungültiger Wächter %s." msgid "Main menu:" msgstr "Programmmenü:" msgid "Context menu:" msgstr "Kontextmenü:" msgid "Floating frame" msgstr "Fließender Rahmen" msgid "Tiled frame" msgstr "Geteilter Rahmen" msgid "Tiling" msgstr "Teilung" msgid "Workspace" msgstr "Arbeitsfläche" msgid "Screen" msgstr "Bildschirm" msgid "Frame" msgstr "Rahmen" msgid "Recursive table - unable to deepcopy" msgstr "Rekursive Tabelle - Tiefenkopie nicht möglich" 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 "Folgende minimale Notfalltastenbelegungen werden gemacht:\n F2 -> xterm\n F11 -> ion-Neustart\n F12 -> ion beenden\n Mod1+C -> Schließen\n Mod1+K P/N -> WFrame.switch_next/switch_prev\n" msgid "Unable to append to non-table menu" msgstr "Anfügen an Nicht-Tabellenmenü ist nicht möglich" msgid "Cannot save selection." msgstr "Kann Auswahl nicht speichern." msgid "Save look selection in %s?" msgstr "Speichern der Layoutauswahl in %s?" msgid "ion-statusd quit." msgstr "ion-statusd beenden." msgid "Errors starting ion-statusd:\n" msgstr "Fehler beim Starten von ion-statusd:\n" msgid "Failed to start ion-statusd." msgstr "Start von ion-statusd schlug fehl." msgid "Screen not found." msgstr "Bildschirm nicht gefunden." msgid "Screen already has an stdisp. Refusing to create a statusbar." msgstr "Bildschirm hat bereits ein stdisp. Verweigere die Erstellung einer Statuszeile." msgid "Failed to create statusbar." msgstr "Statuszeile konnte nicht erstellt werden." msgid "Split current frame vertically." msgstr "Aktuellen Rahmen vertikal spalten." msgid "Go to frame above/below/right/left of current frame." msgstr "Gehe zu Rahmen über/unter/rechts/links vom aktuellen Rahmen." msgid "Split current frame horizontally." msgstr "Aktuellen Rahmen horizontal spalten." msgid "Destroy current frame." msgstr "Aktuellen Rahmen zerstören." msgid "Tile frame, if no tiling exists on the workspace" msgstr "Rahmen teilen wenn aud der Arbeitsfläche keine Teilung existiert" msgid "Destroy frame" msgstr "Rahmen zerstören" msgid "Split vertically" msgstr "Spalten (vertikal)" msgid "Split horizontally" msgstr "Spalten (horizontal)" msgid "Flip" msgstr "Spiegeln" msgid "Transpose" msgstr "Austauschen" msgid "Untile" msgstr "Wiedervereinen" msgid "Float split" msgstr "Fließende Spaltung" msgid "At left" msgstr "Links" msgid "At right" msgstr "Rechts" msgid "Above" msgstr "Über" msgid "Below" msgstr "Unter" msgid "At root" msgstr "An der Wurzel" msgid "New tiling" msgstr "Neue Teilung" msgid "Close the menu." msgstr "Menü schließen." msgid "Activate current menu entry." msgstr "Aktuellen Menüeintrag aktivieren" msgid "Select next/previous menu entry." msgstr "Nächsten/vorhergehenden Menüeintrag auswählen." msgid "Clear the menu's typeahead find buffer." msgstr "Leeren des Vervollständigungssuchpuffers für das Menü." msgid "Toggle floating dock." msgstr "Fließendes Dock an/ausschalten" msgid "Pos-TL" msgstr "Position-Oben/links" msgid "Pos-TR" msgstr "Position-Oben/rechts" msgid "Pos-BL" msgstr "Position-Unten/links" msgid "Pos-BR" msgstr "Position-Unten/rechts" msgid "Grow-L" msgstr "Ausbreitungsrichtung: nach links" msgid "Grow-R" msgstr "Ausbreitungsrichtung: nach rechts" msgid "Grow-U" msgstr "Ausbreitungsrichtung: nach oben" msgid "Grow-D" msgstr "Ausbreitungsrichtung: nach unten" msgid "press" msgstr "Drücken" msgid "click" msgstr "Klick(en)" msgid "drag" msgstr "Ziehen" msgid "double click" msgstr "Doppelklick(en)" msgid "%s %s" msgstr "%s %s" msgid "%s %s at %s" msgstr "%s %s bei/auf/an %s" # vim: fileencoding=utf-8: notion-3+2012042300/po/fi.po000066400000000000000000001323511174530661200151430ustar00rootroot00000000000000# -*- 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: 2007-07-03 12:00+0300\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:93 msgid "Insane key combination." msgstr "Järjetön näppäinyhdistelmä." #: ../ioncore/conf-bindings.c:97 msgid "Could not convert keysym to keycode." msgstr "Näppäinsymbolin muunto näppäinkoodiksi epäonnistui." #: ../ioncore/conf-bindings.c:108 #, c-format msgid "Unknown button \"%s\"." msgstr "Tuntematon nappi \"%s\"." #: ../ioncore/conf-bindings.c:113 msgid "Insane button combination." msgstr "Järjetön nappiyhdistelmä." #: ../ioncore/conf-bindings.c:120 ../ioncore/conf-bindings.c:127 msgid "Insane modifier combination." msgstr "Järjestön näppäinmuunninyhdistelmä." #: ../ioncore/conf-bindings.c:165 #, 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:183 #, c-format msgid "Unable to add binding %s." msgstr "Ei voitu lisätä sidontaa %s." #: ../ioncore/conf-bindings.c:188 #, c-format msgid "Unable to remove binding %s." msgstr "Ei voitu poistaa sidontaa %s." #: ../ioncore/conf-bindings.c:228 #, c-format msgid "Unable to add submap for binding %s." msgstr "Ei voida lisätä alikarttaa sidonnalle %s." # #: ../ioncore/conf-bindings.c:261 msgid "Binding type not set." msgstr "Sidonnan tyyppiä ei ole asetettu." #: ../ioncore/conf-bindings.c:271 #, c-format msgid "Unknown binding type \"%s\"." msgstr "Sidonnalle annettu tyyppi \"%s\" on tuntematon." #: ../ioncore/conf-bindings.c:295 #, c-format msgid "Unknown area \"%s\" for binding %s." msgstr "Sidonnalle %s asetettu tuntematon alue \"%s.\"" #: ../ioncore/conf-bindings.c:336 #, c-format msgid "Unable to get bindmap entry %d." msgstr "Sidontakartan alkion %d haussa epäonnistuttiin." #: ../ioncore/conf-bindings.c:378 msgid "Unable to convert keysym to string." msgstr "Näppäinsymbolia muunto merkkijonoksi epäonnistui." #: ../ioncore/conf-bindings.c:392 msgid "Unable to convert button to string." msgstr "Napin tunnistetteen muunto merkkijonoksi epäonnistui." # #: ../ioncore/event.c:110 msgid "Time request from X server failed." msgstr "Ajan pyyntö X-palvelimelta epäonnistui." # #: ../ioncore/exec.c:185 msgid "Not saving state: running under session manager." msgstr "Tilaa ei talleteta, koska olemme istunnonhallinnan alaisia." #: ../ioncore/strings.c:104 ../ioncore/strings.c:140 ../ioncore/strings.c:173 msgid "Invalid multibyte string." msgstr "Virheellinen multibyte-merkkijono." #: ../ioncore/strings.c:264 #, c-format msgid "Error compiling regular expression: %s" msgstr "Virhe säännölisen lausekkeen käännöksessä: %s" #: ../ioncore/modules.c:155 msgid "Invalid module name." msgstr "Moduulin nimi on tuntematon." # #: ../ioncore/modules.c:167 msgid "The module is already loaded." msgstr "Moduuli on jo ladattu." #: ../ioncore/modules.c:182 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:193 #, c-format msgid "Unable to initialise module %s." msgstr "Virhe moduulin %s alustuksessa." #: ../ioncore/modules.c:217 ../../libextl-3/readconfig.c:388 #, c-format msgid "Unable to find '%s' on search path." msgstr "Hakupolulta ei löytynyt '%s':ää." #: ../ioncore/modules.c:288 msgid "Unknown module." msgstr "Tuntematon moduuli." #: ../ioncore/modules.c:296 msgid "Unable to initialise module." msgstr "Moduulin alustus epäonnistui." #: ../ioncore/modules.c:341 msgid "No module to load given." msgstr "Ladattava moduulia ei ole annettu." # #: ../ioncore/property.c:350 ../ioncore/property.c:359 msgid "Invalid arguments." msgstr "Virheelliset parametrit." # #: ../ioncore/screen.c:382 msgid "Only workspace may not be destroyed/detached." msgstr "Ainoata työpöytää ei voi tuhota/irroitaa." # #: ../ioncore/screen.c:393 msgid "Screens may not be destroyed." msgstr "Näyttöjä ei voi tuhota." #: ../ioncore/screen.c:429 msgid "Invalid offset." msgstr "Virheellinen poikkeama." #: ../ioncore/screen.c:468 #, c-format msgid "Unable to create a workspace on screen %d." msgstr "Työpöydän luonti ruudulle %d epäonnistui." #: ../ioncore/sizehint.c:146 msgid "Invalid client-supplied width/height increment." msgstr "Asiakkaan ilmoittama pysty/vaakalisäys koolle on virheellinen." #: ../ioncore/sizehint.c:154 msgid "Invalid client-supplied aspect-ratio." msgstr "Asiakkaan ilmoittama sivusuhde on virheellinen." #: ../ioncore/ioncore.c:79 msgid "" "This software is essentially licensed under the GNU Lesser General\n" "Public License (LGPL), version 2.1, unless otherwise indicated in\n" "components taken from elsewhere. Additional terms apply to the use\n" "of the name of the project, Ion(tm). For details, see the file\n" "LICENSE that you should have received with this software.\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.\n" msgstr "" "Tämä ohjelma on olennaisesti GNU vähäisemmän yleisen lisenssin (LGPL)\n" "version 2.1 alla, ellei komponenteissa muutoin mainita. Projektin\n" "nimen Ion(tm) käyttöön liittyy lisäehtoja. Yksityiskohdat löydät\n" "tiedostosta LICENSE, joka sinun olisi pitänyt saada tämän ohjelman\n" "mukana.\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" #: ../ioncore/ioncore.c:160 msgid "No encoding given in LC_CTYPE." msgstr "LC_CTYPE ei kerro enkoodausta." #: ../ioncore/ioncore.c:478 #, c-format msgid "Could not connect to X display '%s'" msgstr "Yhteydenotto X-palvelimeen '%s' epäonnistui." #: ../ioncore/ioncore.c:531 msgid "Could not find a screen to manage." msgstr "Yhtään hallittavissa olevaa juuri-ikkunaa ei löytynyt." #: ../ioncore/xic.c:35 msgid "Failed to open input method." msgstr "Syöttömenetelmän avaaminen epäonnistui." #: ../ioncore/xic.c:40 msgid "Input method doesn't support any style." msgstr "Syöttömenetelmä ei tue yhtään syöttötapaa." #: ../ioncore/xic.c:55 msgid "input method doesn't support my preedit type." msgstr "Syöttömenetelmä ei tue kaivattua esimuokkaustyyliä." #: ../ioncore/xic.c:83 msgid "Failed to create input context." msgstr "Syöttökontekstin luonti epäonnistui." #: ../ioncore/clientwin.c:376 #, c-format msgid "The transient_for hint for \"%s\" points to itself." msgstr "Ikkunan \"%s\" transient_for neuvo osoittaa itseensä." #: ../ioncore/clientwin.c:380 #, 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:385 #, 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:405 ../ioncore/clientwin.c:512 #: ../ioncore/clientwin.c:1327 #, c-format msgid "Window %#x disappeared." msgstr "Ikkuna %#x katosi." #: ../ioncore/clientwin.c:532 msgid "Unable to find a matching root window!" msgstr "Vastaavaa juuri-ikkunaa ei löytynyt." #: ../ioncore/clientwin.c:571 #, c-format msgid "Unable to manage client window %#x." msgstr "Asiakasikkunan %x hallittavaksi saattaminen epäonnistui." #: ../ioncore/clientwin.c:620 msgid "Changes is WM_TRANSIENT_FOR property are unsupported." msgstr "Muutoksia WM_TRANSIENT_FOR ominaisuudessa ei tueta." # #: ../ioncore/clientwin.c:792 msgid "Client does not support the WM_DELETE protocol." msgstr "Asiakasikkuna ei tue WM_DELETE-protokollaa." #: ../ioncore/clientwin.c:1333 msgid "Saved client window does not want to be managed." msgstr "Talletettu asiakasikkuna ei halua sitä hallittavan." # #: ../ioncore/colormap.c:93 msgid "Unable to store colourmap watch info." msgstr "Värikartan valvonnan alustaminen epäonnistui." #: ../ioncore/region.c:45 msgid "Creating region with negative width or height!" msgstr "Yritys luoda korkeudeltaan tai leveydeltään negatiivinen alue." #: ../ioncore/region.c:93 #, c-format msgid "Destroying object \"%s\" with client windows as children." msgstr "Tuhotaan kappale \"%s\", jolla on vielä asiakasikkunoita." #: ../ioncore/region.c:434 #, c-format msgid "Can not destroy %s: contains client windows." msgstr "Kappaletta %s ei voida tuhota, sillä se sisältää asiakasikkunoita." #: ../ioncore/region.c:435 msgid "(unknown)" msgstr "(tuntematon)" # #: ../ioncore/region.c:498 msgid "Failed to rescue some client windows - not closing." msgstr "Joidenkin asiakasikkunoiden pelastaminen epäonnistui - ei suljeta." #: ../ioncore/attach.c:55 ../ioncore/frame-pointer.c:278 #, c-format msgid "Attempt to make region %s manage its ancestor %s." msgstr "Yritys saattaa alue %s hallitsemaan sen esivanhempaa %s." #: ../ioncore/attach.c:80 msgid "Unable to reparent." msgstr "Vanhemman vaihto epäonnistui." #: ../ioncore/attach.c:89 msgid "Unexpected attach error: trying to recover by attaching to screen." msgstr "Tuntematon liitäntävirhe: yritetään toipua liittämällä näyttöön." #: ../ioncore/attach.c:108 msgid "Failed recovery." msgstr "Toipuminen epäonnistui." #: ../ioncore/manage.c:190 msgid "Unable to find a screen for a new client window." msgstr "Uudelle asiakasikkunalle ei löytynyt näyttöä." #: ../ioncore/rootwin.c:215 #, c-format msgid "Unable to redirect root window events for screen %d." msgstr "Juuri-ikkunan %d viestien uudelleenohjaus epäonnistui." #: ../ioncore/names.c:88 #, c-format msgid "Corrupt instance number %s." msgstr "Rikkinäinen instanssinumero %s." #: ../ioncore/saveload.c:95 #, c-format msgid "Unknown class \"%s\", cannot create region." msgstr "Luokka \"%s\" on tuntematon. Aluetta ei voida luoda." #: ../ioncore/saveload.c:199 #, 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:250 msgid "Unable to get file for layout backup." msgstr "Sijoittelun varmuuskopiolle ei voitu muodostaa tiedostonimeä." #: ../ioncore/saveload.c:254 #, c-format msgid "Backup file %s already exists." msgstr "Varmuuskopio %s on jo olemassa." #: ../ioncore/saveload.c:260 msgid "Failed backup." msgstr "Varmuuskopionti epäonnistui." # #: ../ioncore/saveload.c:265 msgid "Unable to initialise layout on any screen." msgstr "Sijoittelun alustus epäonnistui kaikilla näytöillä." #: ../ioncore/saveload.c:292 #, c-format msgid "Unable to get configuration for screen %d." msgstr "Ruudun %d asetukset puuttuvat." #: ../ioncore/saveload.c:305 msgid "Unable to save layout." msgstr "Sijoittelun talletus epäonnistui." #: ../ioncore/conf.c:237 msgid "User directory can not be set." msgstr "Käyttäjän hakemistoa ei pystytä asettamaan." #: ../ioncore/conf.c:311 msgid "Some bindmaps were empty, loading ioncore_efbb." msgstr "Jotkut sidontakartat olivat tyhjiä. Ladataan ioncore_efbb." #: ../ioncore/fullscreen.c:46 msgid "Failed to enter full screen mode." msgstr "Vaihto kokoruudun tilaan epäonnistui." #: ../ioncore/fullscreen.c:80 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:1797 msgid "Invalid position setting." msgstr "Virheellinen paikka-asetus." # #: ../ioncore/mplex.c:1837 msgid "Invalid action setting." msgstr "Virheellinen toiminto-asetus." #: ../ioncore/gr.c:117 #, c-format msgid "Drawing engine %s is not registered!" msgstr "Piirtomoottoria %s ei ole rekisteröity!" #: ../ioncore/gr.c:136 #, c-format msgid "Unable to find brush for style '%s'." msgstr "Tyylille '%s' ei löytynyt pensseliä." #: ../ioncore/gr.c:652 msgid "No drawing engines loaded, trying \"de\"." msgstr "Yhtään piirtomoottoria ei ole ladattu. Yritetään oletusta \"de\"." # #: ../ioncore/frame-draw.c:311 msgid "" msgstr "" #: ../ioncore/group.c:183 ../mod_tiling/tiling.c:89 #, c-format msgid "Error reparenting %s." msgstr "Virhe alueen %s vanhemman vaihdossa." #: ../ioncore/group.c:708 msgid "'bottom' already set." msgstr "'pohja' on jo asetettu." # #: ../ioncore/navi.c:42 msgid "Invalid parameter." msgstr "Virheellinen parametri." #: ../ioncore/navi.c:69 msgid "Invalid direction parameter." msgstr "Virheellinen suunta." #: ../ioncore/group-ws.c:48 #, c-format msgid "Unknown placement method \"%s\"." msgstr "Tuntematon sijoittelumenetelmä \"%s\"." #: ../ioncore/detach.c:174 msgid "Failed to reattach." msgstr "Uudelleenliittäminen epäonnistui" #: ../ioncore/screen-notify.c:187 msgid "act: " msgstr "act: " # # #: ../mod_tiling/tiling.c:70 msgid "Split not on workspace." msgstr "Jako ei ole tällä työpöydällä." #: ../mod_tiling/tiling.c:345 msgid "Unable to create a node for status display." msgstr "Solmun luonti tilanäytölle epäonnistui." #: ../mod_tiling/tiling.c:358 msgid "Unable to create new split for status display." msgstr "Uuden jaon tekeminen tilanäytölle epäonnistui." #: ../mod_tiling/tiling.c:709 msgid "Tiling in useless state." msgstr "Laatoitus kelvottomassa tilassa." #: ../mod_tiling/tiling.c:923 msgid "Invalid direction" msgstr "Virheellinen suunta." # #: ../mod_tiling/tiling.c:956 ../mod_tiling/split.c:1030 msgid "Invalid node." msgstr "Epäkelpo solmu." # #: ../mod_tiling/tiling.c:975 msgid "Unable to split." msgstr "Halkaisu ei ole mahdollista." #: ../mod_tiling/tiling.c:1190 msgid "Nil parameter." msgstr "Parametri on asettamatta." #: ../mod_tiling/tiling.c:1195 msgid "Manager doesn't match." msgstr "Hallitsija on väärä." #: ../mod_tiling/tiling.c:1232 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:1323 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:1387 msgid "No suitable split here." msgstr "Ei sopivaa jakoa tässä." # #: ../mod_tiling/tiling.c:1423 msgid "Could not get split tree." msgstr "Halkaisupuu puuttuu." #: ../mod_tiling/tiling.c:1444 msgid "Workspace already has a status display node." msgstr "Työpöydällä on jo solmu tilanäytölle." # #: ../mod_tiling/tiling.c:1482 msgid "Missing region parameters." msgstr "Alueen parameterit puuttuu." #: ../mod_tiling/tiling.c:1526 ../mod_tiling/splitfloat.c:777 msgid "Invalid direction." msgstr "Virheellinen suunta." # #: ../mod_tiling/tiling.c:1601 msgid "No split type given." msgstr "Halkaisun tyyppiä ei ole asetettu." #: ../mod_tiling/tiling.c:1614 msgid "Unknown split type." msgstr "Tuntematon halkaisutyyppi." # # #: ../mod_tiling/tiling.c:1654 msgid "The workspace is empty." msgstr "Työpöytä on tyhjä." #: ../mod_tiling/placement.c:101 #, 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:536 msgid "Unable to move the status display out of way." msgstr "Tilanäyttöä ei voida siirtää pois tieltä." #: ../mod_tiling/split.c:949 msgid "REGION_RQGEOM_TRYONLY unsupported for status display." msgstr "REGION_RQGEOM_TRYONLY:ä ei tueta tilanäytölle." # #: ../mod_tiling/split.c:1102 msgid "Splitting the status display is not allowed." msgstr "Tilanäytön halkaisu ei ole sallittu." #: ../mod_tiling/split.c:1128 ../mod_tiling/splitfloat.c:900 msgid "Unable to split: not enough free space." msgstr "Ei voida halkaista: liian vähän tilaa." #: ../mod_tiling/split.c:1881 #, c-format msgid "Unable to get configuration for %s." msgstr "Alueelle \"%s\" ei saatu asetuksia." #: ../mod_tiling/split-stdisp.c:599 ../mod_tiling/split-stdisp.c:624 msgid "Status display in bad split configuration." msgstr "Tilanäyttö huonossa halkaisuasetelmassa." #: ../mod_tiling/split-stdisp.c:664 msgid "Status display badly located in split tree." msgstr "Tilanäyttö on huonosti sijoittuneena halkaisupuuhun." #: ../mod_tiling/ops.c:69 ../mod_tiling/ops.c:117 msgid "Not member of a group" msgstr "Ei jäsenenä missään ryhmässä" #: ../mod_tiling/ops.c:74 msgid "Manager group already has bottom" msgstr "Managerilla on jo 'pohja'" #: ../mod_tiling/ops.c:151 msgid "Unable to move a region from tiling to group." msgstr "Ei voitu siirtää kappaletta laatoituksesta ryhmään." #: ../mod_query/wedln.c:811 msgid "history" msgstr "hist.täyd." #: ../mod_query/fwarn.c:32 msgid "Error:\n" msgstr "Virhe:\n" # #: ../mod_menu/menu.c:598 msgid "Empty menu." msgstr "Tyhjä valikko." # #: ../mod_sm/sm.c:108 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:124 msgid "Unable to create scratchpad." msgstr "Suttausalueen luonti epäonnistui." #: ../mod_statusbar/main.c:74 msgid "reading a pipe" msgstr "putken luku" #: ../mod_statusbar/main.c:163 msgid "ion-statusd timed out." msgstr "ion-statusd:n käynnistyksen aikaraja umpeutui." #: ../mod_statusbar/statusbar.c:1079 #, c-format msgid "[ %date || load: %load ] %filler%systray" msgstr "" #: ../de/init.c:65 #, c-format msgid "Border attribute %s sanity check failed." msgstr "Reunan arvon '%s' järkevyystarkistus epäonnistui." #: ../de/init.c:88 #, c-format msgid "Unknown border style \"%s\"." msgstr "Reunan tyyli \"%s\" on tuntematon." #: ../de/init.c:108 #, c-format msgid "Unknown border side configuration \"%s\"." msgstr "Reunan sivuasetus \"%s\" on tuntematon." #: ../de/init.c:144 #, c-format msgid "Unable to allocate colour \"%s\"." msgstr "Värin \"%s\" varaaminen epäonnistui." #: ../de/init.c:220 #, c-format msgid "Corrupt substyle table %d." msgstr "Alityylin %d taulu on rikki." #: ../de/init.c:253 #, c-format msgid "Unknown text alignment \"%s\"." msgstr "Tuntematon tekstin tasaus \"%s\"." #: ../de/font.c:44 #, 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:56 #, c-format msgid "Could not load font \"%s\", trying \"%s\"" msgstr "Kirjaisimen \"%s\" lataaminen epäonnistui. Yritetään \"%s\":ää." #: ../de/font.c:60 msgid "Failed to load fallback font." msgstr "Hätävarakirjaisimen lataus epäonnistui." #: ../de/style.c:291 msgid "Style is 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:39 ../pwm/pwm.c:39 msgid "X display to use" msgstr "Käytettävä X-palvelin/näyttö" #: ../ion/ion.c:42 ../pwm/pwm.c:42 msgid "Configuration file" msgstr "Ensisijainen asetustiedosto" #: ../ion/ion.c:45 ../pwm/pwm.c:45 msgid "Add directory to search path" msgstr "Lisää hakemisto hakupolulle" #: ../ion/ion.c:48 ../pwm/pwm.c:48 msgid "Manage default screen only" msgstr "Hallitse vain oletusarvoista juuri-ikkunaa." #: ../ion/ion.c:51 ../pwm/pwm.c:51 msgid "Name of session (affects savefiles)" msgstr "Istunnon nimi (vaikuttaa talletustiedostoihin)" #: ../ion/ion.c:54 ../pwm/pwm.c:54 msgid "Session manager client ID" msgstr "Istunnonhallinnan asiakastunniste" #: ../ion/ion.c:57 ../pwm/pwm.c:57 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:61 ../pwm/pwm.c:61 msgid "Show this help" msgstr "Näytä tämä aputeksti" #: ../ion/ion.c:64 ../pwm/pwm.c:64 msgid "Show program version" msgstr "Näytä ohjelman versio" #: ../ion/ion.c:67 ../pwm/pwm.c:67 msgid "Show about text" msgstr "Näytä tietoja ohjelmasta" #: ../ion/ion.c:82 msgid "Could not get user configuration file directory." msgstr "Käyttäjän asetustiedostohakemisto puuttuu, eikä sitä voitu luoda." #: ../ion/ion.c:96 #, c-format msgid "%s/welcome.txt" msgstr "%s/welcome.fi.txt" #: ../ion/ion.c:129 ../pwm/pwm.c:76 #, c-format msgid "" "Usage: %s [options]\n" "\n" msgstr "" "Käyttö: %s [valintoja]\n" "\n" # #: ../ion/ion.c:197 ../pwm/pwm.c:147 msgid "Invalid command line." msgstr "Virheellinen komentorivi." #: ../ion/ion.c:219 msgid "Ion startup error log:\n" msgstr "Ionin käynnistysvirheloki:\n" #: ../ion/ion.c:230 ../pwm/pwm.c:180 msgid "Refusing to start due to encountered errors." msgstr "Ohjelma kieltäytyy käynnistymästä tavattujen virheiden johdosta." #: ../pwm/pwm.c:169 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:1469 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:1489 msgid "Returned dead object." msgstr "Kuollut kappale palautettu." #: ../../libextl-3/luaextl.c:1492 #, 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:1528 ../../libextl-3/luaextl.c:1883 msgid "Stack full." msgstr "Pino täysi." #: ../../libextl-3/luaextl.c:1894 #, c-format msgid "Argument %d to %s is a dead object." msgstr "Parametri %d %s:lle on kuollut olio." #: ../../libextl-3/luaextl.c:1897 #, 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:1960 msgid "L1 call handler upvalues corrupt." msgstr "Tason 1 kutsunkäsittelijän \"upvalue\":t rikki." #: ../../libextl-3/luaextl.c:1965 msgid "Called function has been unregistered." msgstr "Kutsuttu funktio on poistettu." #: ../../libextl-3/luaextl.c:1976 #, 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:2089 #, 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:2480 msgid "Maximal serialisation depth reached." msgstr "Suurin mahdollinen talletuksen rekursiosyvyys saavutettu." #: ../../libextl-3/luaextl.c:2501 #, c-format msgid "Unable to serialise type %s." msgstr "Tyyppiä %s ei voida tallettaa." #: ../../libextl-3/luaextl.c:2532 msgid "-- This file has been generated by %s. Do not edit.\n" msgstr "-- Tämä tiedosto on %sin 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 "Scroll the message or completions up/down." msgstr "Vieritä viestiä tai täydennyksiä ylös/alas." msgid "Close the query/message box, not executing bound actions." msgstr "Sulje kysely/viesti suorittamatta sidottuja toimintoja." msgid "Close the query and execute bound action." msgstr "Sulje kysely ja suorita sidottu toiminta." msgid "Complete from history" msgstr "Täydennä historiasta" msgid "Try to complete the entered text or cycle through completions." msgstr "Yritä täydentää syötetty teksti tai selaa täydennyksiä." # msgid "Clear mark/cancel selection." msgstr "Lopeta tekstin valinta." # msgid "Copy selection." msgstr "Kopioi valittu teksti." # msgid "Cut selection." msgstr "Leikkaa valittu teksti." # msgid "Set mark/begin selection." msgstr "Aloita tekstin valinta." msgid "Paste from the clipboard." msgstr "Liimaa teksti leikelaudalta." # msgid "Select next/previous (matching) history entry." msgstr "Näytä seuraava/edellinen vastaus muistista." msgid "Transpose characters." msgstr "Transponoi merkit." msgid "Delete the whole line." msgstr "Tuhoa koko rivi." msgid "Delete to end of line." msgstr "Tuhoa merkit rivin loppuun saakka." msgid "Delete one word forward/backward." msgstr "Tuhoa yksi sana eteen/taakse." msgid "Delete previous character." msgstr "Tuhoa edellinen merkki." msgid "Delete next character." msgstr "Poista seuraava merkki." msgid "Skip one word forward/backward." msgstr "Ohita yksi sana eteen/taaksepäin" msgid "Go to end/beginning." msgstr "Mene rivin alkuun/loppuun." msgid "Move one character forward/backward." msgstr "Siirry yksi merkki eteen/taakse." msgid "Kill" msgstr "Tapa" msgid "Attach tagged" msgstr "Liitä merkityt" msgid "Rename" msgstr "Uudelleennimeä" msgid "Close" msgstr "Sulje" msgid "De/reattach" msgstr "Irroita/liitä" msgid "Toggle tag" msgstr "Muuta merkintää" msgid "Window info" msgstr "Ikkunan tiedot" msgid "Clear tags" msgstr "Poista merkinnät" msgid "Exit" msgstr "Poistu" msgid "Restart TWM" msgstr "Käynnistä TWM" msgid "Restart" msgstr "Uudelleenkäynnistä" msgid "Save" msgstr "Talleta" msgid "Session" msgstr "Istunto" msgid "Styles" msgstr "Tyylit" msgid "About Ion" msgstr "Tietoja Ionista" msgid "Help" msgstr "Ohjeet" msgid "Lock screen" msgstr "Lukitse näyttö" msgid "Terminal" msgstr "Pääteohjelma" # msgid "Run..." msgstr "Suorita..." # msgid "Move in specified direction." msgstr "Liiku vastaavaan suuntaan." msgid "Shrink in specified direction." msgstr "Kutista vastaavaan suuntaan." # msgid "Grow in specified direction." msgstr "Kasvata vastaavaan suuntaan." msgid "End the resize mode." msgstr "Siirry tilasta pois." msgid "Cancel the resize mode." msgstr "Peruuta tilasta." msgid "Move the frame." msgstr "Siirrä kehystä." msgid "Lower the frame." msgstr "Alenna kehystä." # msgid "Raise the frame." msgstr "Nosta kehys." msgid "Toggle shade mode" msgstr "Kytke litistys päälle/pois." msgid "Attach tagged objects to this frame." msgstr "Liitä merkityt kappaleet tähän kehykseen." msgid "Maximize the frame horizontally/vertically." msgstr "Maksimoi kehys vaaka/pystysuunnassa." msgid "Move current object within the frame left/right." msgstr "" "Siirrä kehyksessä tällä hetkellä näytettävää kappaletta vasemmalle/oikealle." msgid "Switch to next/previous object within the frame." msgstr "Siirry seuraavaan/edelliseen kehyksen jakavaan kappaleeseen." msgid "Switch to n:th object within the frame." msgstr "Siirry n:teen kehyksen jakavaan kappaleeseen." # msgid "Query for a client window to attach." msgstr "Kysy liitettävää asiakasikkunaa." msgid "Move objects between frames by dragging and dropping the tab." msgstr "" "Siirrä kappaletta kehysten välillä raahaamalla ja pudottamalla välilehti." msgid "Resize the frame." msgstr "Muuta kehyksen kokoa." msgid "Switch the frame to display the object indicated by the tab." msgstr "Vaihda kehys näyttämään välilehden ilmoittama kappale." msgid "Begin move/resize mode." msgstr "Aloita siirto ja koonmuutostila." msgid "Display context menu." msgstr "Näytä kontekstivalikko." # msgid "Query for a client window to go to." msgstr "Kysy asiakasikkunaa, johon siirtyä." 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 file to view." msgstr "Kysy tiedostoa näytettäväksi." msgid "Query for file to edit." msgstr "Kysy tiedostoa muokattavaksi." msgid "Query for host to connect to with SSH." msgstr "Ota SSH-yhteys kyseltävään koneeseen." msgid "Query for Lua code to execute." msgstr "Kysy Lua-koodia ajettavaksi." msgid "Query for command line to execute." msgstr "Kysy komentoriviä suoritettavaksi." # msgid "Run a terminal emulator." msgstr "Käynnistä pääte-emulaattori." msgid "Show the Ion manual page." msgstr "Näytä Ionin ohjesivu." msgid "Query for manual page to be displayed." msgstr "Kysy ohjesivua näytettäväksi." msgid "Toggle tag of current object." msgstr "Merkitse aktiivinen kappale." msgid "Detach (float) or reattach an object to its previous location." msgstr "" "Irroita (kelluta) tai uudelleenliitä kappale sen edelliseen sijaintiin." msgid "Close current object." msgstr "Sulje aktiivinen kappale." msgid "Toggle client window group full-screen mode" msgstr "Muuta asiakasikkunaryhmän kokoruudun tilaa" # 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 "Kill client owning the client window." msgstr "Tapa aktiivisen asiakasikkunan omistava ohjelma." # 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 "Raise focused object, if possible." msgstr "Nosta aktiivista kappaletta, jos mahdollista." msgid "Backward-circulate focus." msgstr "Kierrätä fokusta taaksepäin" msgid "Forward-circulate focus." msgstr "Kierrätä fokusta eteenpäin." msgid "Display the window list menu." msgstr "Näytä ikkunavalikko." msgid "Display the main menu." msgstr "Näytä päävalikko." # # msgid "Create a new workspace of chosen default type." msgstr "Luo uusi työpöytä ennalta asetettua tyyppiä." # msgid "Go to next/previous screen on multihead setup." msgstr "Mene seuraavalle/edelliselle näytölle usean näytön järjestelmässä." # msgid "Go to n:th screen on multihead setup." msgstr "Siirry näytölle n usean näytön järjestelmässä." # msgid "Clear all tags." msgstr "Poista kaikki merkinnät." msgid "Go to first region demanding attention or previously active one." msgstr "" "Mene ensimmäiseen huomiota vaativaan alueeseen, tai edelliseen aktiiviseen." # msgid "Switch to next/previous object within current screen." msgstr "" "Siirry seuraavaan/edelliseen tämänhetkisen näytön lomittamaan kappaleeseen." # 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 kappaleeseen (työpöytä, " "kokoruudun asiakasikkuna)." msgid "List" msgstr "Lista" msgid "New" msgstr "Uusi" msgid "Dillo" msgstr "" msgid "Konqueror" msgstr "" msgid "Links" msgstr "" msgid "Opera" msgstr "" msgid "Rxvt" msgstr "" msgid "W3M" msgstr "" msgid "XTerm" msgstr "" # msgid "Workspaces" msgstr "Työpöydät" msgid "Programs" msgstr "Ohjelmat" msgid "Show the PWM manual page." msgstr "Näytä PWM:n ohjesivu." msgid "Toggle scratchpad." msgstr "Kytke suttausalue päälle/pois" 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 "No entry '%s'" msgstr "Tuntematon valinta '%s'." msgid "%s:" msgstr "" msgid "Missing submenu " msgstr "Puuttuva alivalikko " msgid "Unknown menu %s." msgstr "Tuntematon valikko %s." msgid "Lua code:" msgstr "Lua-koodia:" msgid "Manual page (%s):" msgstr "Ohjesivu (%s):" msgid "SSH to:" msgstr "Avaa SSH-yhteys:" msgid "Failed to open ~/.ssh/config" msgstr "Tiedoston ~/.ssh/config avaaminen epäonnistui." msgid "Failed to open ~/.ssh/known_hosts" msgstr "Tiedostoa ~/.ssh/known_hosts avaaminen epäonnistui." msgid "Run:" msgstr "Suorita:" msgid "View file:" msgstr "Näytä tiedosto:" msgid "Edit file:" msgstr "Muokkaa tiedostoa:" msgid "Workspace name:" msgstr "Työpöydän nimi:" msgid "Frame name:" msgstr "Kehyksen nimi:" msgid "Restart Notion (y/n)?" msgstr "Uudelleenkäynnistä Notion (kyllä: y, ei: n)?" msgid "Exit Notion/Shutdown session (y/n)?" msgstr "Poistu Notionista/Lopeta istunto (kyllä: y, ei: n)?" msgid "Go to or create workspace:" msgstr "Mene tai luo työpöytä:" msgid "Attach window:" msgstr "Liitä ikkuna:" msgid "Go to window:" msgstr "Mene ikkunaan:" msgid "New workspace layout (default):" msgstr "Uuden työpöydän sijoitelma (default):" msgid "Unknown error" msgstr "Tuntematon virhe" msgid "Unknown layout" msgstr "Tuntematon sijoittelu." msgid "Cannot attach: different root windows." msgstr "Ei voida liittää: eri juuri-ikkunat." msgid "Could not find client window %s." msgstr "Asiakasikkunaa %s ei löytynyt." msgid "Too much result data" msgstr "Liian suuri vastaus" msgid "Save look selection in %s?" msgstr "Talletaanko ulkonäköasetus tiedostoon %s?" msgid "Cannot save selection." msgstr "Ei voida tallettaa valintaa." msgid "Unable to append to non-table menu" msgstr "Ei voida lisätä valikkoon, koska sitä ei ole määritelty tauluna " 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 "Recursive table - unable to deepcopy" msgstr "Rekursiivinen taulu - ei voida syväkopioida." msgid "Frame" msgstr "Kehys" msgid "Screen" msgstr "Näyttö" # msgid "Workspace" msgstr "Työpöytä" msgid "Tiling" msgstr "Laatoitus" msgid "Tiled frame" msgstr "Laatoitettu kehys" msgid "Floating frame" msgstr "Kelluva kehys" msgid "Context menu:" msgstr "Kontekstivalikko:" msgid "Main menu:" msgstr "Päävalikko:" msgid "Invalid guard %s." msgstr "Virheellinen vahti %s." msgid "Error compiling guard: %s" msgstr "Virhe vahdin käännössä. %s" msgid "Error in command string: " msgstr "Virhe komentojonossa:" msgid "Invalid command" msgstr "Virheellinen komento" msgid "Not a directory." msgstr "Ei hakemisto." msgid "Could not find %s" msgstr "Ei löydettyä %s:ää." msgid "ion-statusd quit." msgstr "ion-statusd päätti suorituksen." msgid "Errors starting ion-statusd:\n" msgstr "Ion-statusd:n käynnistysvirheet:\n" msgid "Failed to start ion-statusd." msgstr "Ion-statusd:n käynnistys eopäonnistui." msgid "Screen not found." msgstr "Ruutua ei löytynyt." msgid "Screen already has an stdisp. Refusing to create a statusbar." msgstr "Ruudulla on jo tilanäyttö. Kieltäydytään luomasta tilariviä." msgid "Failed to create statusbar." msgstr "Epäonnistuttiin tilarivin luonnissa" msgid "Split current frame vertically." msgstr "Halkaise tämänhetkinen kehys pystysuunnassa." 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 horizontally." msgstr "Halkaise tämänhetkinen kehys vaakasuunnassa." msgid "Destroy current frame." msgstr "Tuhoa tämänhetkinen kehys." msgid "Tile frame, if no tiling exists on the workspace" msgstr "Laatoita kehys, jos työpöydällä ei ole kehystä" # msgid "Destroy frame" msgstr "Tuhoa kehys" # # msgid "Split vertically" msgstr "Jaa pystysuunnassa" # # msgid "Split horizontally" msgstr "Jaa vaakasuunnassa" # msgid "Flip" msgstr "Peilaa" # msgid "Transpose" msgstr "Käännä" msgid "Untile" msgstr "Hajoita laatoitus" msgid "Float split" msgstr "Kelluta" msgid "At left" msgstr "Vasemmalla" msgid "At right" msgstr "Oikealla" msgid "Above" msgstr "Yläpuolella" msgid "Below" msgstr "Alapuolella" # msgid "At root" msgstr "Juuressa" msgid "New tiling" msgstr "Uusi laatoitus" msgid "Close the menu." msgstr "Sulje valikko." # # msgid "Activate current menu entry." msgstr "Suorita valinta." msgid "Select next/previous menu entry." msgstr "Siirry seuraavaan/edelliseen valintaan." msgid "Clear the menu's typeahead find buffer." msgstr "Tyhjää valikon hakupuskuri." msgid "Toggle floating dock." msgstr "Näytä/piiloita kelluva laituri (dock)." msgid "Pos-TL" msgstr "Paikka: ylävasen" msgid "Pos-TR" msgstr "Paikka: yläoikea" msgid "Pos-BL" msgstr "Paikka: alavasen" msgid "Pos-BR" msgstr "Paikka: alaoikea" msgid "Grow-L" msgstr "Kasvu: vasemmalle" msgid "Grow-R" msgstr "Kasvu: oikealle" msgid "Grow-U" msgstr "Kasvu: ylös" msgid "Grow-D" msgstr "Kasvu: 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 "" #~ "Unable to unsplit: Could not move client windows elsewhere within the " #~ "tiling." #~ msgstr "" #~ "Ei voida poistaa halkaisua: asiakasikkunoita ei voitu siirtää muualle " #~ "laatoituksessa." #~ msgid "'based_on' for %s points back to the style itself." #~ msgstr "Tyylin %s 'based_on' -asetus osoittaa itseensä." #~ msgid "Unknown base style. \"%s\"" #~ msgstr "Tuntematon pohjatyyli \"%s\"." #~ msgid "Tag current object within the frame." #~ msgstr "Merkitse kehyksen tällä hetkellä näyttämä kappale." #~ msgid "Xinerama sanity check failed; overlapping screens detected." #~ msgstr "Xinerama-tiedon järkevyystarkastus havaitsi päällekkäisiä ruutuja." #~ msgid "Xinerama sanity check failed; zero size detected." #~ msgstr "Xinerama-tiedon järkevyystarkastus havaitsi päällekkäisiä ruutuja." #~ msgid "" #~ "Don't know how to get Xinerama information for multiple X root windows." #~ msgstr "Xinerama-tietoa ei osata käyttää usealle juuri-ikkunalle." #~ msgid "Error retrieving Xinerama information." #~ msgstr "Xinerama-tietoja ei saatu." #~ msgid "Unable to setup Xinerama screen %d." #~ msgstr "Xinerama-ruudun %d hallittavaksi saattaminen epäonnistui." #~ msgid "Unable to setup X screen %d." #~ msgstr "X-ruudun %d hallittavaksi saattaminen epäonnistui." # #~ msgid "Refusing to destroy - not empty." #~ msgstr "Tuhoamisesta kieltäydytään - ei tyhjä." # #~ msgid "Workspace not empty - refusing to destroy." #~ msgstr "Työpöytää ei voida tuhota, koska se ei ole tyhjä." #~ msgid "Nil frame." #~ msgstr "Kehys on asettamatta." #~ msgid "The frame is not managed by the workspace." #~ msgstr "Kehys ei ole tämän työpöydän hallinnoima." #~ msgid "Already detached" #~ msgstr "Irroitettu jo" #~ msgid "Use Xinerama screen information (default: 1/yes)" #~ msgstr "Hyödynnä Xineramaa (oletus: 1/kyllä)" #~ msgid "Ignored: not compiled with Xinerama support" #~ msgstr "Jätetään huomiotta: ohjelmaa ei ole käännetty Xinerama-tuella" #~ msgid "Invalid parameter to -xinerama." #~ msgstr "Virheellinen parametri -xinerama:lle" #~ msgid "Use Xinerama screen information (default: 0/no)" #~ msgstr "Hyödynnä Xineramaa (oletus: 0/ei)" #~ msgid "Detach window from tiled frame" #~ msgstr "Irroita ikkuna laatoitetusta kehyksestä" # #~ 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 "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" notion-3+2012042300/po/fr.po000066400000000000000000001213561174530661200151570ustar00rootroot00000000000000# translation of ion3.po to # # french language translations for Ion3. # # Copyright (c) Pierre-Henri RAMBOZ 2007. # # This file is distributed under the same license as the Ion3 package. # #cf ligne 770 pour welcome.fr.txt msgid "" msgstr "" "Project-Id-Version: ion3\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2007-04-22 15:41+0200\n" "PO-Revision-Date: 2007-05-21 14:12+0200\n" "Last-Translator: Pierre-Henri RAMBOZ DidouPh@gmail.com\n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: KBabel 1.11.4\n" #: ../ioncore/conf-bindings.c:96 msgid "Insane key combination." msgstr "Combinaison de touche démente" #: ../ioncore/conf-bindings.c:100 msgid "Could not convert keysym to keycode." msgstr "Ne peut convertir keysym en keycode." #: ../ioncore/conf-bindings.c:111 #, c-format msgid "Unknown button \"%s\"." msgstr "Bouton inconnu \"%s\"." #: ../ioncore/conf-bindings.c:116 msgid "Insane button combination." msgstr "Combinaison de boutons démente." #: ../ioncore/conf-bindings.c:123 ../ioncore/conf-bindings.c:130 msgid "Insane modifier combination." msgstr "Combinaison de modificateur démente." #: ../ioncore/conf-bindings.c:168 #, c-format msgid "Can not wait on modifiers when no modifiers set in \"%s\"." msgstr "Ne peut attendre les modificateurs quand aucun modificateur définis dans \"%s\"." #: ../ioncore/conf-bindings.c:186 #, c-format msgid "Unable to add binding %s." msgstr "Ne peut ajouter de liaison %s." #: ../ioncore/conf-bindings.c:191 #, c-format msgid "Unable to remove binding %s." msgstr "Ne peut enlever une liaison %s." #: ../ioncore/conf-bindings.c:230 #, c-format msgid "Unable to add submap for binding %s." msgstr "Ne peut ajouter de sousmappage pour la liaison %s." # #: ../ioncore/conf-bindings.c:260 msgid "Binding type not set." msgstr "Type de liaison indéfinis." #: ../ioncore/conf-bindings.c:270 #, c-format msgid "Unknown binding type \"%s\"." msgstr "Type de liaison inconnu \"%s\"." #: ../ioncore/conf-bindings.c:292 #, c-format msgid "Unknown area \"%s\" for binding %s." msgstr "Zone inconnue \"%s\" pour la liaison %s." #: ../ioncore/conf-bindings.c:333 #, c-format msgid "Unable to get bindmap entry %d." msgstr "Incapable d'obtenir la liaison de mappage pour %d." #: ../ioncore/conf-bindings.c:375 msgid "Unable to convert keysym to string." msgstr "Incapable de convertir keysym en string." #: ../ioncore/conf-bindings.c:389 msgid "Unable to convert button to string." msgstr "Incapable de convertir le bouton en string." # #: ../ioncore/event.c:113 msgid "Time request from X server failed." msgstr "Requète temporelle pour le serveur X échouée." # #: ../ioncore/exec.c:186 msgid "Not saving state: running under session manager." msgstr "Ne sauvegarde pas l'état : s'exécute dans le gestionnaire de session" #: ../ioncore/strings.c:107 ../ioncore/strings.c:143 ../ioncore/strings.c:176 msgid "Invalid multibyte string." msgstr "String multibyte invalide." #: ../ioncore/strings.c:267 #, c-format msgid "Error compiling regular expression: %s" msgstr "Erreur enc compilant l'expression régulière: %s" #: ../ioncore/modules.c:158 msgid "Invalid module name." msgstr "Nom de module invalide." # #: ../ioncore/modules.c:170 msgid "The module is already loaded." msgstr "Le module est déjà chargé." #: ../ioncore/modules.c:185 msgid "Module version information not found or version mismatch. Refusing to use." msgstr "Information de version de module non trouvée ou erreur de version. Refus d'utilisation." #: ../ioncore/modules.c:196 #, c-format msgid "Unable to initialise module %s." msgstr "Incapable d'initialiser le module %s." #: ../ioncore/modules.c:220 ../libextl/readconfig.c:388 #, c-format msgid "Unable to find '%s' on search path." msgstr "Incapable de trouver '%s' dans le chemin de recherche." #: ../ioncore/modules.c:291 msgid "Unknown module." msgstr "Module inconnu." #: ../ioncore/modules.c:299 msgid "Unable to initialise module." msgstr "Incapable d'initialiser le module." #: ../ioncore/modules.c:344 msgid "No module to load given." msgstr "Pas de module à charger spécifié" #: ../ioncore/property.c:355 ../ioncore/property.c:364 msgid "Invalid arguments." msgstr "Argument invalide." #: ../ioncore/screen.c:385 msgid "Only workspace may not be destroyed/detached." msgstr "Seuls les espace de travail ne peuvent être détruits/détachés" #: ../ioncore/screen.c:396 msgid "Screens may not be destroyed." msgstr "Les écrans ne seront pas détruits." #: ../ioncore/screen.c:432 msgid "Invalid offset." msgstr "Offset invalide" #: ../ioncore/screen.c:471 #, c-format msgid "Unable to create a workspace on screen %d." msgstr "Incapable de créer un espace de travail sur l'écran %d." #: ../ioncore/sizehint.c:157 msgid "Invalid client-supplied width/height increment." msgstr "Incrémentation de hauteur/largeur envoyée par le client invalide." #: ../ioncore/sizehint.c:165 msgid "Invalid client-supplied aspect-ratio." msgstr "Rapport d'aspect envoyé par le client invalide." #: ../ioncore/ioncore.c:78 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 "" "Ce programme est un logiciel libre; vous pouvez le redistribuer et/ou\n" "le modifier sous les termes de la GNU Lesser General Public\n" "Licence comme publiée par la Free Software Foundation; soit\n" "en version 2.1 de la licence, ou (a votre convenance) une version ulterrieure.\n" "\n" "Ce programme est distribué dans l'espoir qu'il sera utile\n" "Mais sans aucune garantie; sans même la garantie implicite que\n" "celui-ci soit commercialisable ou adapté a un usage quelconque. référez vous\n" "à la GNU Lesser General Public Licence pour plus de détails.\n" #: ../ioncore/ioncore.c:159 msgid "No encoding given in LC_CTYPE." msgstr "Pas d'encodage donné dans LC_CTYPE" #: ../ioncore/ioncore.c:470 #, c-format msgid "Could not connect to X display '%s'" msgstr "Incapable de se connecter a l'affichage X \"%s\"" #: ../ioncore/ioncore.c:522 msgid "Could not find a screen to manage." msgstr "N'a pas trouvé d'écran à gérer." #: ../ioncore/xic.c:38 msgid "Failed to open input method." msgstr "Echec d'ouverture d'une méthode d'entrée." #: ../ioncore/xic.c:43 msgid "Input method doesn't support any style." msgstr "La méthode d'entrée ne suporte aucun style." #: ../ioncore/xic.c:58 msgid "input method doesn't support my preedit type." msgstr "La méthode d'entrée ne suporte pas mes types preedit." #: ../ioncore/xic.c:86 msgid "Failed to create input context." msgstr "Echec de création du contexte d'entrée." #: ../ioncore/clientwin.c:379 #, c-format msgid "The transient_for hint for \"%s\" points to itself." msgstr "Le transcient_for hint vers \"%s\" pointe vers lui-même." #: ../ioncore/clientwin.c:383 #, c-format msgid "" "Client window \"%s\" has broken transient_for hint. (\"Extended WM hints\" " "multi-parent brain damage?)" msgstr "" "La fenêtre de client \"%s\" a rompu le transient_for hint. (\"WM hints étendus\" " "domages cérébraux multi parents?)" #: ../ioncore/clientwin.c:388 #, c-format msgid "The transient_for window for \"%s\" is not on the same screen." msgstr "La fenêtre transcient_for pour \"%s\" n'est pas sur le même écran." #: ../ioncore/clientwin.c:408 ../ioncore/clientwin.c:496 #: ../ioncore/clientwin.c:1292 #, c-format msgid "Window %#x disappeared." msgstr "la fenêtre %#x a disparue." #: ../ioncore/clientwin.c:516 msgid "Unable to find a matching root window!" msgstr "Incapable de trouver une fenêtre racine correspondante!" #: ../ioncore/clientwin.c:555 #, c-format msgid "Unable to manage client window %#x." msgstr "incapable de gérer la fenêtre du client %#x." #: ../ioncore/clientwin.c:604 msgid "Changes is WM_TRANSIENT_FOR property are unsupported." msgstr "Le changement est WM_TRANSCIENT_FOR propriété non supportée." # #: ../ioncore/clientwin.c:776 msgid "Client does not support the WM_DELETE protocol." msgstr "Le client ne supporte pas le protocole WM_DELETE." #: ../ioncore/clientwin.c:1298 msgid "Saved client window does not want to be managed." msgstr "La fenêtre de client sauvegardée ne veut pas être gérée." # #: ../ioncore/colormap.c:96 msgid "Unable to store colourmap watch info." msgstr "Incapable de stocker les infos de la palette de couleur" #: ../ioncore/region.c:47 msgid "Creating region with negative width or height!" msgstr "Création de région avec une largeur ou hauteur négative!" #: ../ioncore/region.c:95 #, c-format msgid "Destroying object \"%s\" with client windows as children." msgstr "Destruction de l'objet \"%s\" avec une fenêtre de client comme fille." #: ../ioncore/region.c:434 #, c-format msgid "Can not destroy %s: contains client windows." msgstr "Ne peut détruire %s: contiend des fenêtres client." #: ../ioncore/region.c:435 msgid "(unknown)" msgstr "(inconnu)" #: ../ioncore/region.c:498 msgid "Failed to rescue some client windows - not closing." msgstr "Echec de sauvetage de quelques fenêtres de client - ne ferme pas." #: ../ioncore/attach.c:58 ../ioncore/frame-pointer.c:280 #, c-format msgid "Attempt to make region %s manage its ancestor %s." msgstr "Temptative de faire que la région %s gère ses ancètres %s." #: ../ioncore/attach.c:83 msgid "Unable to reparent." msgstr "Incapable de réaparenter." #: ../ioncore/attach.c:92 msgid "Unexpected attach error: trying to recover by attaching to screen." msgstr "Erreur d'atachement inattendue: essai de récupération par attachement à l'écran." #: ../ioncore/attach.c:111 msgid "Failed recovery." msgstr "Récupération échouée." #: ../ioncore/manage.c:193 msgid "Unable to find a screen for a new client window." msgstr "Incapable de trouver un écran pour une nouvelle fenêtre de client." #: ../ioncore/rootwin.c:218 #, c-format msgid "Unable to redirect root window events for screen %d." msgstr "Incapable de rediriger les évènement de la fenêtre racine pour l'écran %d." #: ../ioncore/names.c:91 #, c-format msgid "Corrupt instance number %s." msgstr "Numéro d'instance corrompu %s." #: ../ioncore/saveload.c:98 #, c-format msgid "Unknown class \"%s\", cannot create region." msgstr "classe inconnue \"%s\", ne peut créer de région." #: ../ioncore/saveload.c:202 #, 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 "" "Il y a eu des erreurs en changeant la mise en place. Sauvegarde de la \n" "mise en place courrante en tant que savefile %s.\n" "Si vous _n'utilisez_ pas de gestionnaire de session et voulez restaurer\n" "votre ancienne mise en place, copiez ce fichier de sauvegarde sur le\n" "savefile situé dans le même répertoire pendant que Ion ne fonctionne pas\n" "et après avoir fixé vos autresfichiers de configuration causant des\n" "problèmes. (peut-être un module manquant" #: ../ioncore/saveload.c:253 msgid "Unable to get file for layout backup." msgstr "Incapable d'obtenir le fichier de sauvegarde de la mise en place." #: ../ioncore/saveload.c:257 #, c-format msgid "Backup file %s already exists." msgstr "Le fichier de sauvegarde %s existe déjà." #: ../ioncore/saveload.c:263 msgid "Failed backup." msgstr "Sauvegarde échouée." # #: ../ioncore/saveload.c:268 msgid "Unable to initialise layout on any screen." msgstr "Incapable d'initialiser la mise en place sur aucun écran." #: ../ioncore/saveload.c:295 #, c-format msgid "Unable to get configuration for screen %d." msgstr "Incapable d'obtenir la configuration pour l'écran %d." #: ../ioncore/saveload.c:308 msgid "Unable to save layout." msgstr "Incapable de sauvegarder la mise en place" #: ../ioncore/conf.c:235 msgid "User directory can not be set." msgstr "Le répertoire d'utilisateur ne peut être définis." #: ../ioncore/conf.c:309 msgid "Some bindmaps were empty, loading ioncore_efbb." msgstr "Certaines bindmaps étaient vides, chargement de ioncore_efbb." #: ../ioncore/fullscreen.c:49 msgid "Failed to enter full screen mode." msgstr "Echec d'entrée dans le mode plein écran." #: ../ioncore/fullscreen.c:83 msgid "" "Failed to return from full screen mode; remaining manager or parent from " "previous location refused to manage us." msgstr "" "Echec de retour du mode plein écran; reliquat de gestionaire ou parent de " "la position précédente ayant refusé de nous gérer." # #: ../ioncore/mplex.c:1685 msgid "Invalid position setting." msgstr "Réglages de position invalides." # #: ../ioncore/mplex.c:1725 msgid "Invalid action setting." msgstr "Réglage d'action invalide." #: ../ioncore/gr.c:120 #, c-format msgid "Drawing engine %s is not registered!" msgstr "Gestionnaire de rendu %s non enregistré!" #: ../ioncore/gr.c:139 #, c-format msgid "Unable to find brush for style '%s'." msgstr "Incapable de trouver les pinceaux pour le style \"%s\"." #: ../ioncore/gr.c:646 msgid "No drawing engines loaded, trying \"de\"." msgstr "Pas de moteur de rendu chagé, éssai avec \"de\"." #: ../ioncore/frame-draw.c:314 msgid "" msgstr "" #: ../ioncore/group.c:186 ../mod_tiling/tiling.c:92 #, c-format msgid "Error reparenting %s." msgstr "Erreur de réapparissage %s." #: ../ioncore/group.c:711 msgid "'bottom' already set." msgstr "'bottom' déjà définis." #: ../ioncore/navi.c:45 msgid "Invalid parameter." msgstr "paramètre invalide" #: ../ioncore/navi.c:72 msgid "Invalid direction parameter." msgstr "Paramètre de direction invalide." #: ../ioncore/group-ws.c:51 #, c-format msgid "Unknown placement method \"%s\"." msgstr "Méthode de placement inconnue \"%s\"." #: ../ioncore/detach.c:176 msgid "Failed to reattach." msgstr "Incapable de réattacher." #: ../ioncore/screen-notify.c:190 msgid "act: " msgstr "acte: " # #: ../mod_tiling/tiling.c:73 msgid "Split not on workspace." msgstr "Division hors de l'espace de travail." #: ../mod_tiling/tiling.c:348 msgid "Unable to create a node for status display." msgstr "Incapable de créer un noeud pour l'affichage du status." #: ../mod_tiling/tiling.c:361 msgid "Unable to create new split for status display." msgstr "Incapable de créer de nouvelles divisions pour l'affichage de status" #: ../mod_tiling/tiling.c:710 msgid "Tiling in useless state." msgstr "Empilement en mode inutile." #: ../mod_tiling/tiling.c:924 msgid "Invalid direction" msgstr "Direction invalide" # #: ../mod_tiling/tiling.c:957 ../mod_tiling/split.c:1018 msgid "Invalid node." msgstr "Noeud invalide." # #: ../mod_tiling/tiling.c:976 msgid "Unable to split." msgstr "Incapable de diviser." #: ../mod_tiling/tiling.c:1090 msgid "Unable to unsplit: Could not move client windows elsewhere within the tiling." msgstr "Incapable de diviser : N'a pas pu déplacer la fenêtre cliente ailleur dans l'empilement." #: ../mod_tiling/tiling.c:1207 msgid "Nil parameter." msgstr "Paramètre Nul." #: ../mod_tiling/tiling.c:1212 msgid "Manager doesn't match." msgstr "Les gestionnaires ne correspondent pas" #: ../mod_tiling/tiling.c:1249 msgid "The status display is not a valid parameter for this routine." msgstr "L'affichage du status n'est pas un paramètre valide pour cette routine." #: ../mod_tiling/tiling.c:1340 msgid "Refusing to float split directly containing the status display." msgstr "Refus de rendre directement flotant la division contenant l'affichage du status." #: ../mod_tiling/tiling.c:1403 msgid "No suitable split here." msgstr "Pas d'arbre de division acceptable ici." # #: ../mod_tiling/tiling.c:1439 msgid "Could not get split tree." msgstr "N'a pus obtenir l'arbre de divisions" #: ../mod_tiling/tiling.c:1460 msgid "Workspace already has a status display node." msgstr "L'Espace de travail a déja un noeud d'affichage de status." # #: ../mod_tiling/tiling.c:1498 msgid "Missing region parameters." msgstr "Paramètre de région invalide." #: ../mod_tiling/tiling.c:1542 ../mod_tiling/splitfloat.c:780 msgid "Invalid direction." msgstr "Direction invalide." # #: ../mod_tiling/tiling.c:1617 msgid "No split type given." msgstr "Pas de typde de division donné." #: ../mod_tiling/tiling.c:1630 msgid "Unknown split type." msgstr "Type de division inconnue." # # #: ../mod_tiling/tiling.c:1670 msgid "The workspace is empty." msgstr "L'espace de travail est vide" #: ../mod_tiling/placement.c:104 #, c-format msgid "Ooops... could not find a region to attach client window to on workspace %s." msgstr "" "Ooops... n'a pas trouvé de région pour atacher la fenêtre cliente sur l'espace" "de travail %s." # #: ../mod_tiling/split.c:524 msgid "Unable to move the status display out of way." msgstr "Incapable d'enlever l'affichage du status du passage." #: ../mod_tiling/split.c:937 msgid "REGION_RQGEOM_TRYONLY unsupported for status display." msgstr "REGION_RQGEOM_TRYONLY non suporté pour l'affichage de status." # #: ../mod_tiling/split.c:1083 msgid "Splitting the status display is not allowed." msgstr "Diviser l'affichage de status n'est pas autorisé." #: ../mod_tiling/split.c:1114 ../mod_tiling/splitfloat.c:903 msgid "Unable to split: not enough free space." msgstr "Incapable de diviser: pas assez d'espace libre." #: ../mod_tiling/split.c:1865 #, c-format msgid "Unable to get configuration for %s." msgstr "Incapable d'obtenir la configuration pour %s." #: ../mod_tiling/split-stdisp.c:602 ../mod_tiling/split-stdisp.c:627 msgid "Status display in bad split configuration." msgstr "Affichage de status dans une configuration divisée défectueuse" #: ../mod_tiling/split-stdisp.c:667 msgid "Status display badly located in split tree." msgstr "Affichage de status mal situé dans l'arbre divisé" #: ../mod_tiling/ops.c:72 ../mod_tiling/ops.c:120 msgid "Not member of a group" msgstr "N'est pas membre d'un groupe" #: ../mod_tiling/ops.c:77 msgid "Manager group already has bottom" msgstr "Le groupe de gestion est déjà déscendu" #: ../mod_tiling/ops.c:154 msgid "Unable to move a region from tiling to group." msgstr "Incapable de déplacer une région de l'empilement vers un groupe." #: ../mod_query/wedln.c:813 msgid "history" msgstr "historique" #: ../mod_query/fwarn.c:35 msgid "Error:\n" msgstr "Erreur:\n" # #: ../mod_menu/menu.c:601 msgid "Empty menu." msgstr "Menu vide." # #: ../mod_sm/sm.c:111 msgid "Failed to set session directory." msgstr "Echec de définition du répertoire de sessions" #: ../mod_sm/sm_session.c:86 msgid "Too many ICE connections." msgstr "Trop de connexions ICE." #: ../mod_sm/sm_session.c:228 msgid "Failed to save session state" msgstr "Echec de sauvegarde l'état de la session" #: ../mod_sm/sm_session.c:247 msgid "Failed to request save-yourself-phase2 from session manager." msgstr "Echec de requète save-yourself-phase2 depuis le gestionnaire de sessions" # #: ../mod_sm/sm_session.c:296 msgid "SESSION_MANAGER environment variable not set." msgstr "Variable d'environnement SESSION_MANAGER non définie." #: ../mod_sm/sm_session.c:301 msgid "Session Manager: IceAddConnectionWatch failed." msgstr "Gestionnaire de session: IceAddConnectionWatch échoué." # #: ../mod_sm/sm_session.c:326 msgid "Unable to connect to the session manager." msgstr "Incapable de se connecter au gestionnaire de sessions" #: ../mod_sp/main.c:126 msgid "Unable to create scratchpad." msgstr "Incapable de créer le bloc note." #: ../mod_statusbar/main.c:75 msgid "reading a pipe" msgstr "lecture du tunnel (pipe)" #: ../mod_statusbar/main.c:159 msgid "ion-statusd timed out." msgstr "ion-statusd n'a pas répondu à temps." #: ../mod_statusbar/statusbar.c:1081 #, c-format msgid "[ %date || load: %load ] %filler%systray" msgstr "[ %date || charge: %load ] %filler%systray" #: ../de/init.c:68 #, c-format msgid "Border attribute %s sanity check failed." msgstr "Le test de salubrité de l'attribut de bordure %s a échoué." #: ../de/init.c:91 #, c-format msgid "Unknown border style \"%s\"." msgstr "Style de bordure inconnu \"%s\"." #: ../de/init.c:111 #, c-format msgid "Unknown border side configuration \"%s\"." msgstr "Configuration des côtés de bordure inconnue \"%s\"." #: ../de/init.c:144 #, c-format msgid "Unable to allocate colour \"%s\"." msgstr "Incapable d'allouer la couleur \"%s\"." #: ../de/init.c:210 #, c-format msgid "Corrupt substyle table %d." msgstr "Table de soustyle inconnue %d." #: ../de/init.c:243 #, c-format msgid "Unknown text alignment \"%s\"." msgstr "Alignement de texte inconnu \"%s\"." #: ../de/init.c:319 #, c-format msgid "'based_on' for %s points back to the style itself." msgstr "'based_on' pour %s pointe vers le style lui-même." #: ../de/init.c:322 #, c-format msgid "Unknown base style. \"%s\"" msgstr "Style de base inconnu. \"%s\"" #: ../de/font.c:47 #, c-format msgid "" "Fontset for font pattern '%s' implements context dependent drawing, which is " "unsupported. Expect clutter." msgstr "" "Fontset pour le modèle de police '%s' implémentant les dessins dépendant du contexte" "ce qui n'est pas supporté. Cafouillages attendus." #: ../de/font.c:59 #, c-format msgid "Could not load font \"%s\", trying \"%s\"" msgstr "N'a pu charger la police \"%s\", essai avec \"%s\"." #: ../de/font.c:63 msgid "Failed to load fallback font." msgstr "Echec de chargement de la police de secours." #: ../de/style.c:315 #, c-format msgid "Style is still in use [%d] but the module is being unloaded!" msgstr "Le style est toujours utilisé [%d], mais le module est déchargé!" #: ../ion/ion.c:42 ../pwm/pwm.c:42 msgid "X display to use" msgstr "Affichage X a utiliser" #: ../ion/ion.c:45 ../pwm/pwm.c:45 msgid "Configuration file" msgstr "Fichier de configuration" #: ../ion/ion.c:48 ../pwm/pwm.c:48 msgid "Add directory to search path" msgstr "Ajouter le répertoire au chemin de la recherche" #: ../ion/ion.c:51 ../pwm/pwm.c:51 msgid "Manage default screen only" msgstr "Gérer l'écra par défault seulement" #: ../ion/ion.c:54 ../pwm/pwm.c:54 msgid "Name of session (affects savefiles)" msgstr "Nom de session (affecte les savefiles)" #: ../ion/ion.c:57 ../pwm/pwm.c:57 msgid "Session manager client ID" msgstr "ID client du gestionnaire de sessions" #: ../ion/ion.c:60 ../pwm/pwm.c:60 msgid "Do not create startup error log and display it with xmessage." msgstr "Ne pas crée de journal d'erreurs de démarrage et les afficher avec xmessage." #: ../ion/ion.c:64 ../pwm/pwm.c:64 msgid "Show this help" msgstr "Affiche cette aide" #: ../ion/ion.c:67 ../pwm/pwm.c:67 msgid "Show program version" msgstr "Afficher la version du programme" #: ../ion/ion.c:70 ../pwm/pwm.c:70 msgid "Show about text" msgstr "Afficher le text à propos" #: ../ion/ion.c:85 msgid "Could not get user configuration file directory." msgstr "N'a pas trouvé le répertoire de fichier de configuration utilisateur" #: ../ion/ion.c:99 #, c-format msgid "%s/welcome.txt" msgstr "%s/welcome.txt" #: ../ion/ion.c:132 ../pwm/pwm.c:79 #, c-format msgid "" "Usage: %s [options]\n" "\n" msgstr "" "Usage: %s [options]\n" "\n" # #: ../ion/ion.c:200 ../pwm/pwm.c:150 msgid "Invalid command line." msgstr "Ligne de commande invalide." #: ../ion/ion.c:222 msgid "Ion startup error log:\n" msgstr "Registre d'erreurs Ion:\n" #: ../ion/ion.c:233 ../pwm/pwm.c:183 msgid "Refusing to start due to encountered errors." msgstr "Refus de démarrage en raison des erreurs rencontrées." #: ../pwm/pwm.c:172 msgid "PWM startup error log:\n" msgstr "Registre d'erreurs PWM:\n" #: ../libextl/readconfig.c:86 msgid "$HOME not set" msgstr "$HOME non définis" #: ../libextl/readconfig.c:113 msgid "User directory not set. Unable to set session directory." msgstr "Répertoire d'utilisateur non définis. Incapable de définir le répertoire de sessions" #: ../libextl/readconfig.c:254 #, c-format msgid "Falling back to %s." msgstr "Retour forcé à %s" #: ../libextl/readconfig.c:474 #, c-format msgid "Unable to create session directory \"%s\"." msgstr "Incapable de créer le répertoire de session \"%s\"." #: ../libextl/luaextl.c:117 msgid "Lua stack full." msgstr "Pile Lua pleine." # #: ../libextl/luaextl.c:143 msgid "Unknown Lua error." msgstr "Erreur Lua inconnue." #: ../libextl/luaextl.c:490 msgid "Stack trace:" msgstr "tracage de pile:" #: ../libextl/luaextl.c:497 #, c-format msgid "" "\n" "(Unable to get debug info for level %d)" msgstr "" "\n" "(Incapable d'obtenir les informations de déboggage pour le niveau %d)" #: ../libextl/luaextl.c:515 msgid "" "\n" " [Skipping unnamed C functions.]" msgstr "" "\n" " [Evitement de la fonction C annonyme.]" #: ../libextl/luaextl.c:566 msgid "Internal error." msgstr "Erreur interne" #: ../libextl/luaextl.c:585 msgid "Unable to initialize Lua." msgstr "Incapable d'initialiser Lua." #: ../libextl/luaextl.c:1336 msgid "Too many return values. Use a C compiler that has va_copy to support more." msgstr "Trop de valeurs renvoyées. Utilisez un compileur C qui a un va_copy pour en suporter plus" #: ../libextl/luaextl.c:1356 msgid "Returned dead object." msgstr "A renvoyé un objet mort." #: ../libextl/luaextl.c:1359 #, c-format msgid "Invalid return value (expected '%c', got lua type \"%s\")." msgstr "Valeur retournée invalide (attendu %c, de type lua \"%s\")." #: ../libextl/luaextl.c:1395 ../libextl/luaextl.c:1750 msgid "Stack full." msgstr "Pile pleine." #: ../libextl/luaextl.c:1761 #, c-format msgid "Argument %d to %s is a dead object." msgstr "Argument %d à %s est un objet mort." #: ../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 à %s est de type invalide. (Le modèle d'argument est '%s', ave le " "type lua %s)." #: ../libextl/luaextl.c:1827 msgid "L1 call handler upvalues corrupt." msgstr "upvalue niveau 1 de la pile d'appel corrompue" #: ../libextl/luaextl.c:1832 msgid "Called function has been unregistered." msgstr "La fonction invoquée a été désenregistrée." #: ../libextl/luaextl.c:1843 #, c-format msgid "Attempt to call an unsafe function \"%s\" in restricted mode." msgstr "Temptative d'appel d'une fonction non-sécurisée \"%s\" en mode restreint." #: ../libextl/luaextl.c:1956 #, c-format msgid "Function '%s' has more parameters than the level 1 call handler can handle" msgstr "La fonction '%s' a plus de paramètres que le niveau 1 de la pile d'appel puisse supporter" # #: ../libextl/luaextl.c:2347 msgid "Maximal serialisation depth reached." msgstr "Profondeur de sérialisation maximale atteinte." #: ../libextl/luaextl.c:2368 #, c-format msgid "Unable to serialise type %s." msgstr "Incapable de sérialiser le type %s." #: ../libextl/luaextl.c:2399 msgid "-- This file has been generated by %s. Do not edit.\n" msgstr "-- Ce fichier a été généré par %s, Ne pas éditer.\n" #: ../libextl/misc.c:17 #, c-format msgid "" "Type checking failed in level 2 call handler for parameter %d (got %s, " "expected %s)." msgstr "" "Le contrôle de type a échoué au niveau 2 de la pile d'appel au paramètre %d " "(obtenu %s, attendu %s)." msgid "Scroll the message or completions up/down." msgstr "Parcourrir le message ou les complétions en haut/bas." msgid "Close the query/message box, not executing bound actions." msgstr "Fermer la boite de requètes/messages, ne pas exécuter les actions lièes." msgid "Close the query and execute bound action." msgstr "Fermer la requète et exécuter les actions liées." msgid "Complete from history" msgstr "Compléter depuis l'historique." msgid "Try to complete the entered text or cycle through completions." msgstr "Essayer de compléter le texte entré ou parcourrir les complétions." # msgid "Clear mark/cancel selection." msgstr "Effacer/annuler la sélection" # msgid "Copy selection." msgstr "Copier la sélection." # msgid "Cut selection." msgstr "Couper la sélection." # msgid "Set mark/begin selection." msgstr "Définir le début de la sélection." msgid "Paste from the clipboard." msgstr "Coller depuis le presse papier." msgid "Select next/previous (matching) history entry." msgstr "Sélectionner l'entrèe (correspondante) suivante/précédente dans l'historique" msgid "Transpose characters." msgstr "Transposer le caractère." msgid "Delete the whole line." msgstr "Effacer toute la ligne." msgid "Delete to end of line." msgstr "Effacer jusqu'a la fin de la ligne." msgid "Delete one word forward/backward." msgstr "Effacer un mot en avant/arrière." msgid "Delete previous character." msgstr "Effacer le caractère précédent." msgid "Delete next character." msgstr "Effacer le caractère suivant." msgid "Skip one word forward/backward." msgstr "Sauter un mot en avant/arrière." msgid "Go to end/beginning." msgstr "Aller au début / à la fin" msgid "Move one character forward/backward." msgstr "Déplacer d'un caractère en avant/arrière." msgid "Kill" msgstr "Tuer" msgid "Attach tagged" msgstr "Attacher Marqués" msgid "Rename" msgstr "Renommer" msgid "Close" msgstr "Fermer" msgid "De/reattach" msgstr "Dé/attacher" msgid "Toggle tag" msgstr "(dès)Activer le marquage" msgid "Window info" msgstr "Informations fenêtre" msgid "Clear tags" msgstr "Effacer les marqueurs" msgid "Exit" msgstr "Sortir" msgid "Restart TWM" msgstr "Redémarrer TWM" msgid "Restart" msgstr "Redémarrer" msgid "Save" msgstr "Sauvegarder" msgid "Session" msgstr "Sessions" msgid "Styles" msgstr "Styles" msgid "About Ion" msgstr "A Propos de Ion" msgid "Help" msgstr "Aide" msgid "Lock screen" msgstr "Vérouiller l'écran" msgid "Terminal" msgstr "Termial" # msgid "Run..." msgstr "Exécuter..." # msgid "Move in specified direction." msgstr "Déplacer dans la direction spécifiée." msgid "Shrink in specified direction." msgstr "Réduire dans la direction spécifiée." # msgid "Grow in specified direction." msgstr "Agrandir dans la dimension spécifiée." msgid "End the resize mode." msgstr "mettre fin au mode de redimensionnement." msgid "Cancel the resize mode." msgstr "Annuler le mode redimensionnement." msgid "Move the frame." msgstr "Déplacer le cadre." msgid "Lower the frame." msgstr "Descendre le cadre" # msgid "Raise the frame." msgstr "Monter le cadre" msgid "Toggle shade mode" msgstr "basculer l'état shade" msgid "Attach tagged objects to this frame." msgstr "Attacher les objets marqués à ce cadre." msgid "Maximize the frame horizontally/vertically." msgstr "Maximiser le cadre horizontalement/verticalement" msgid "Move current object within the frame left/right." msgstr "Déplacer l'objet courrant dans le cadre à gauche/droite." msgid "Switch to next/previous object within the frame." msgstr "Basculer vers l'objet suivan,t/précédent dans le cadre." msgid "Switch to n:th object within the frame." msgstr "Basculer vers l'objet n:th dans le cadre." # msgid "Query for a client window to attach." msgstr "Demande pour une fenêtre cliente a attacher." msgid "Move objects between frames by dragging and dropping the tab." msgstr "Déplacer les objets entre les cadres en glissant et déposant le signet." msgid "Resize the frame." msgstr "Redimensionner le cadre." msgid "Switch the frame to display the object indicated by the tab." msgstr "Activer le cadre pour afficher l'objet indiqué part le signet." msgid "Begin move/resize mode." msgstr "Démarrer le mode déplacer/redimensionner." msgid "Display context menu." msgstr "Afficher le menu contextuel." # msgid "Query for a client window to go to." msgstr "Demande pour une fençetre cliente vers laquelle aller." msgid "Query for workspace to go to or create a new one." msgstr "Demande d'un espace de travail vers lequel aller ou de cration d'un nouveau." msgid "Query for file to view." msgstr "Demande d'un fichier pour visualisation." msgid "Query for file to edit." msgstr "Demande d'un fichier pour l'édition." msgid "Query for host to connect to with SSH." msgstr "Demande de connexion de l'hôte via SSH." msgid "Query for Lua code to execute." msgstr "Demande d'exécution d'un code Lua." msgid "Query for command line to execute." msgstr "Demande d'exécution d'une ligne de commande." # msgid "Run a terminal emulator." msgstr "Lancer un émulateur de terminal." msgid "Show the Ion manual page." msgstr "Afficher la page de manuel de Ion." msgid "Query for manual page to be displayed." msgstr "Demande pour l'affichage de pages de manuel." msgid "Toggle tag of current object." msgstr "Basculer le marqueur de l'objet courrant." msgid "Detach (float) or reattach an object to its previous location." msgstr "Détacher (float) ou réattacher un objet à sa position précédente." msgid "Close current object." msgstr "Fermer l'objet courrant." msgid "Toggle client window group full-screen mode" msgstr "Basculer le groupe de fenêtre client en mode plein écran." # msgid "" "Send next key press to the client window. Some programs may not allow this " "by default." msgstr "" "Envoyer le prochain pressage de touche a la fenêtre de client. certains programmes" "n'autoriseront pas celà par default" # msgid "Kill client owning the client window." msgstr "Tuer le client qui gère la fenêtre de client." # msgid "" "Nudge the client window. This might help with some programs' resizing " "problems." msgstr "" "Secouer la fenêtre de client. Celà peut aider pour les problèmes de " "redimentionnement de certains programmes." msgid "Raise focused object, if possible." msgstr "Remonter l'objet ayant le focus, si possible." msgid "Backward-circulate focus." msgstr "Décrémenter le focus." msgid "Forward-circulate focus." msgstr "Incrémenter le focus." msgid "Display the window list menu." msgstr "Affiche le menu de listage des fenêtres." msgid "Display the main menu." msgstr "Affiche le menu principal." # # msgid "Create a new workspace of chosen default type." msgstr "Créer un nouvel espace de travail du type par défault choisis." # msgid "Go to next/previous screen on multihead setup." msgstr "Aller sur l'écran suivant/précédent sur une configuration multihead." # msgid "Go to n:th screen on multihead setup." msgstr "Aller sur l'écran n:th sur une configuration multihead." # msgid "Clear all tags." msgstr "Effacer tous les marqueurs." msgid "Go to first region demanding attention or previously active one." msgstr "Aller dans la première région qui demande de l'attention ou la dernière active." # msgid "Switch to next/previous object within current screen." msgstr "basculer vers l'objet suivant/précédent dans l'écran en cours." # msgid "" "Switch to n:th object (workspace, full screen client window) within current " "screen." msgstr "" "Basculer vers l'objet n:th (Espace de travail, fenêtre de clien plein écran) dans l'écran" "courrant." msgid "List" msgstr "Liste" msgid "New" msgstr "Nouveau" msgid "Dillo" msgstr "Dillo" msgid "Konqueror" msgstr "Konqueror" msgid "Links" msgstr "Links" msgid "Opera" msgstr "Opera" msgid "Rxvt" msgstr "Rxvt" msgid "W3M" msgstr "W3M" msgid "XTerm" msgstr "XTerm" # msgid "Workspaces" msgstr "Espaces de travail" msgid "Programs" msgstr "Programmes" msgid "Show the PWM manual page." msgstr "Afficher le manuel de PWM." msgid "Toggle scratchpad." msgstr "Basculer vers le Bloc note." msgid "" "\n" "%sClass: %s\n" "%sRole: %s\n" "%sInstance: %s\n" "%sXID: 0x%x" msgstr "" "\n" "%sClasse: %s\n" "%sRole: %s\n" "%sInstance: %s\n" "%sXID: 0x%x" msgid "No entry '%s'" msgstr "pas d'entrée '%s'" msgid "%s:" msgstr "%s:" msgid "Missing submenu " msgstr "Sous menu manquant" msgid "Unknown menu %s." msgstr "Menu Inconnu %s." msgid "Lua code:" msgstr "code Lua:" msgid "Manual page (%s):" msgstr "Page de manuel (%s):" msgid "SSH to:" msgstr "SSH vers:" msgid "Failed to open ~/.ssh/config" msgstr "Echec d'ouverture ~/.ssh/config" msgid "Failed to open ~/.ssh/known_hosts" msgstr "Echec d'ouverture ~/.ssh/known_hosts" msgid "Run:" msgstr "Exécuter:" msgid "View file:" msgstr "Voir le fichier:" msgid "Edit file:" msgstr "Editer le fichier:" msgid "Workspace name:" msgstr "Nom de l'espace de travail:" msgid "Frame name:" msgstr "Nom du cadre:" msgid "Restart Notion (y/n)?" msgstr "Redémarrer Notion (y/n)?" msgid "Exit Notion/Shutdown session (y/n)?" msgstr "Quiter Notion/Fermer la session (y/n)?" msgid "Go to or create workspace:" msgstr "Aller à ou créer l'espace de travail:" msgid "Attach window:" msgstr "Attacher la fenêtre:" msgid "Go to window:" msgstr "Aller à la fenêtre:" msgid "New workspace layout (default):" msgstr "Nouvelle disposition d'espace de travail (default):" msgid "Unknown error" msgstr "Erreur inconnue" msgid "Unknown layout" msgstr "Disposition inconnue" msgid "Cannot attach: different root windows." msgstr "Ne peut attacher: différentes fenêtres racine." msgid "Could not find client window %s." msgstr "Ne peut trouver la fenêtre de client %s." msgid "Too much result data" msgstr "Trop de donnée résultantes" msgid "Could not find %s" msgstr "Ne peut trouver %s" msgid "Not a directory." msgstr "N'est pas un répertoire." msgid "Invalid command" msgstr "Commande invalide" msgid "Error in command string: " msgstr "Erreur dans la ligne de commande: " msgid "Error compiling guard: %s" msgstr "Erreur de compilation du garde: %s" msgid "Invalid guard %s." msgstr "Garde invalide %s." msgid "Main menu:" msgstr "Menu Principal:" msgid "Context menu:" msgstr "Menu Contextuel:" msgid "Floating frame" msgstr "Cadre flottant" msgid "Tiled frame" msgstr "Cadre Empilé" msgid "Tiling" msgstr "Empilement" # msgid "Workspace" msgstr "Espace de travail" msgid "Screen" msgstr "Ecran" msgid "Frame" msgstr "Cadre" msgid "Recursive table - unable to deepcopy" msgstr "Table récursive - incapable de deepcopy" 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 "" "Application des mappages d'urgence suivants:\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 "Incapable d'associer a un menu non-table" msgid "Cannot save selection." msgstr "Ne peut sauver la sélection." msgid "Save look selection in %s?" msgstr "Sauvegarder la sélection de look dans %s?" msgid "ion-statusd quit." msgstr "ion-statusd quite." msgid "Errors starting ion-statusd:\n" msgstr "Erreur de lancement de ion-statusd:\n" msgid "Failed to start ion-statusd." msgstr "Echec de lancement de ion-statusd." msgid "Screen not found." msgstr "Ecran non trouvé." msgid "Screen already has an stdisp. Refusing to create a statusbar." msgstr "L'écra a déja un stdisp. Refus de création d'une barre de status." msgid "Failed to create statusbar." msgstr "Echec de la création de la barre de status." msgid "Split current frame vertically." msgstr "Scinder le cadre courrant verticalement" msgid "Go to frame above/below/right/left of current frame." msgstr "Aller au cadre sous/sur/droite/gauche du cadre courrant." msgid "Split current frame horizontally." msgstr "Sciender le cadre courrant horizontalement" msgid "Destroy current frame." msgstr "Detruire le cadre courrant." msgid "Tile frame, if no tiling exists on the workspace" msgstr "Empiler le cadre, si aucun empuilement n'existe sur l'espace de travail" msgid "Destroy frame" msgstr "Detruire le cadre" msgid "Split vertically" msgstr "Scinder Verticalement" msgid "Split horizontally" msgstr "Sciender Horizontalement" msgid "Flip" msgstr "Tourner" msgid "Transpose" msgstr "Transposer" msgid "Untile" msgstr "Désempiler" msgid "Float split" msgstr "Rupture flotante" msgid "At left" msgstr "A gauche" msgid "At right" msgstr "A droite" msgid "Above" msgstr "Dessus" msgid "Below" msgstr "Sous" msgid "At root" msgstr "A la racine" msgid "New tiling" msgstr "Nouvel empilement" msgid "Close the menu." msgstr "fermer le menu." # # msgid "Activate current menu entry." msgstr "Activer l'entrée courrante du menu." msgid "Select next/previous menu entry." msgstr "Choisir l'entrée suivante/précédente du menu." msgid "Clear the menu's typeahead find buffer." msgstr "Effacer le tampon d'autocomplétion du menu." msgid "Toggle floating dock." msgstr "(dès)activer le doc flottant." msgid "Pos-TL" msgstr "position HG" msgid "Pos-TR" msgstr "position HD" msgid "Pos-BL" msgstr "position BG" msgid "Pos-BR" msgstr "position BD" msgid "Grow-L" msgstr "Agrandir vers la gauche" msgid "Grow-R" msgstr "Agrandir vers le droite" msgid "Grow-U" msgstr "Agrandir vers le haut" msgid "Grow-D" msgstr "Agrandir vers le bas" msgid "press" msgstr "presser" msgid "click" msgstr "clic" msgid "drag" msgstr "glisser" msgid "double click" msgstr "double clic" msgid "%s %s" msgstr "%s %s" msgid "%s %s at %s" msgstr "%s %s na %s" notion-3+2012042300/pwm/000077500000000000000000000000001174530661200143655ustar00rootroot00000000000000notion-3+2012042300/pwm/Makefile000066400000000000000000000023031174530661200160230ustar00rootroot00000000000000## ## 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 ###################################### EXECUTABLE = pwm3plus SOURCES = pwm.c ETC = cfg_pwm.lua INCLUDES += $(X11_INCLUDES) INCLUDES += $(LIBMAINLOOP_INCLUDES) $(LIBTU_INCLUDES) $(LIBEXTL_INCLUDES) INCLUDES += -I.. LIBS += $(X11_LIBS) -lSM -lICE LIBS += $(WHOLEA) $(LIBMAINLOOP_LIBS) $(LIBEXTL_LIBS) $(LIBTU_LIBS) $(NO_WHOLEA) LIBS += $(LUA_LIBS) $(DL_LIBS) LIBS += -lm MODULE_PATH = $(TOPDIR) 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 ###################################### _install: executable_install $(INSTALLDIR) $(DESTDIR)$(PWM_ETCDIR) for i in $(ETC); do \ $(INSTALL) -m $(DATA_MODE) $$i $(DESTDIR)$(PWM_ETCDIR); \ done notion-3+2012042300/pwm/cfg_pwm.lua000066400000000000000000000063701174530661200165200ustar00rootroot00000000000000-- -- 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_notioncore.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_notioncore contains configuration of the Ion 'core' dopath("cfg_notioncore") -- Load some modules. --dopath("cfg_modules") --dopath("mod_query") dopath("mod_menu") --dopath("mod_tiling") --dopath("mod_statusbar") dopath("mod_dock") --dopath("mod_sp") -- -- PWM customisations to bindings and menus -- -- Unbind anything using mod_query and rebind to mod_menu where -- applicable. defbindings("WScreen", { bdoc("Display the main menu."), kpress(ALTMETA.."F12", "mod_menu.menu(_, _sub, 'mainmenu', {big=true})"), }) defbindings("WMPlex.toplevel", { kpress(ALTMETA.."F1", nil), 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), bdoc("Show the PWM manual page."), kpress(META.. "F1", "ioncore.exec_on(_, ':man pwm3')"), bdoc("Display context menu."), kpress(META.."M", "mod_menu.menu(_, _sub, 'ctxmenu')"), }) defbindings("WFrame.toplevel", { kpress(META.."A", nil), }) -- 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"), 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(_)"), }) -- Workspace menu defmenu("wsmenu", { menuentry("New", "ioncore.create_ws(_)"), menuentry("Close", "WRegion.rqclose(_sub)", "_sub:WGroupWS"), submenu("List", "workspacelist"), }) notion-3+2012042300/pwm/pwm.c000066400000000000000000000137771174530661200153530ustar00rootroot00000000000000/* * ion/pwm/pwm.c * * Copyright (c) Tuomo Valkonen 1999-2009. * * See the included file LICENSE for details. */ #include #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 screen only")}, {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"), libtu_progname()); 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=0; int opt; ErrorLog el; FILE *ef=NULL; char *efnam=NULL; bool may_continue=FALSE; bool noerrorlog=FALSE; char *localedir; libtu_init(argv[0]); #ifdef CF_RELOCATABLE_BIN_LOCATION prefix_set(argv[0], CF_RELOCATABLE_BIN_LOCATION); #endif localedir=prefix_add(LOCALEDIR); if(!ioncore_init(CF_EXECUTABLE, argc, argv, localedir)) return EXIT_FAILURE; if(localedir!=NULL) free(localedir); prefix_wrap_simple(extl_add_searchdir, EXTRABINDIR); /* ion-completefile */ prefix_wrap_simple(extl_add_searchdir, MODULEDIR); prefix_wrap_simple(extl_add_searchdir, ETCDIR); #ifdef PWM_ETCDIR prefix_wrap_simple(extl_add_searchdir, PWM_ETCDIR); #endif prefix_wrap_simple(extl_add_searchdir, SHAREDIR); prefix_wrap_simple(extl_add_searchdir, LCDIR); extl_set_userdirs(CF_EXECUTABLE); 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('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_display(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; } notion-3+2012042300/system.mk000066400000000000000000000136771174530661200154550ustar00rootroot00000000000000## ## System settings ## ## ## Installation paths ## # Installation path prefix. Unless you know what you're doing, the default # of /usr/local is likely the correct choice. 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/notion # Some .lua files and ion-* shell scripts SHAREDIR=$(PREFIX)/share/notion # Manual pages MANDIR=$(PREFIX)/share/man # Some documents DOCDIR=$(PREFIX)/share/doc/notion # Nothing at the moment INCDIR=$(PREFIX)/include/notion # Nothing at the moment LIBDIR=$(PREFIX)/lib # Modules MODULEDIR=$(LIBDIR)/notion/mod # Compiled Lua source code LCDIR=$(LIBDIR)/notion/lc # ion-completefile (does not belong in SHAREDIR being a binary file) EXTRABINDIR=$(LIBDIR)/notion/bin # For notion-completeman system-wide cache VARDIR=/var/cache/notion # Message catalogs LOCALEDIR=$(PREFIX)/share/locale # Executable suffix (for Cygwin). #BIN_SUFFIX = .exe ## ## Modules ## # Set PRELOAD_MODULES=1 if your system does not support dynamically loaded # modules through 'libdl' or has non-standard naming conventions. # You will likely need this option on e.g. Cygwin and Mac OS X. #PRELOAD_MODULES=1 # Flags to link with libdl. Even if PRELOAD_MODULES=1, you may need this # setting (for e.g. Lua, when not instructed by pkg-config). DL_LIBS=-ldl ## ## Lua ## # To skip auto-detection of lua uncomment this and edit the variables below to # suit your installation of lua. #LUA_MANUAL=1 # Default to paths and names that should work for a build installed from the # official Lua 5.1 source tarball. 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 # Attempt to autodect lua using pkg-config. ifndef LUA_MANUAL ifeq (5.1,$(findstring 5.1,$(shell pkg-config --exists lua5.1 && pkg-config --modversion lua5.1))) LUA_LIBS=`pkg-config --libs lua5.1` LUA_INCLUDES=`pkg-config --cflags lua5.1` LUA=`which lua5.1` LUAC=`which luac5.1` else ifeq (5.1,$(findstring 5.1,$(shell pkg-config --exists lua && pkg-config --modversion lua))) LUA_LIBS=`pkg-config --libs lua` LUA_INCLUDES=`pkg-config --cflags lua` LUA=`which lua` LUAC=`which luac` endif # lua endif # LUA_MANUAL ## ## X libraries, includes and options ## # Paths X11_PREFIX=/usr/X11R6 # SunOS/Solaris #X11_PREFIX=/usr/openwin X11_LIBS=-L$(X11_PREFIX)/lib -lX11 -lXext X11_INCLUDES=-I$(X11_PREFIX)/include # XFree86 libraries up to 4.3.0 have a bug that can cause a segfault. # The following setting should work around that situation. DEFINES += -DCF_XFREE86_TEXTPROP_BUG_WORKAROUND # Use the Xutf8 routines (XFree86 extension) instead of the Xmb routines # in an UTF-8 locale. (No, you don't need this in UTF-8 locales, and # most likely don't even want. It's only there because both Xmb and # Xutf8 routines are broken, in different ways.) #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 ## ## Localisation ## # 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 -DCF_NO_GETTEXT # On some other systems you may need to explicitly link against libintl. #EXTRA_LIBS += -lintl # You may also need to give the location of its headers. The following # should work on Mac OS X (which needs the above option as well) with # macports. #EXTRA_INCLUDES += -I/opt/local/include ## ## libc ## # You may uncomment this if you know that your system C libary provides # asprintf and vasprintf. (GNU libc does.) If HAS_SYSTEM_ASPRINTF is not # defined, an implementation provided in libtu/sprintf_2.2/ is used. #HAS_SYSTEM_ASPRINTF=1 # The following setting is needed with GNU libc for clock_gettime and the # monotonic clock. Other systems may not need it, or may not provide a # monotonic clock at all (which Ion can live with, and usually detect). EXTRA_LIBS += -lrt # Cygwin needs this. #DEFINES += -DCF_NO_GETLOADAVG # # If you're using/have gcc, it is unlikely that you need to modify # any of the settings below this line. # ##################################################################### ## ## 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=-Os $(WARN) $(DEFINES) $(INCLUDES) $(EXTRA_INCLUDES) LDFLAGS=-Wl,--as-needed $(LIBS) $(EXTRA_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_C_SOURCE=200112L # 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) $(INCLUDES) $(EXTRA_INCLUDES) MAKE_DEPEND=$(DO_MAKE_DEPEND) $(SOURCES) > $(DEPEND_FILE) ## ## AR ## AR=ar ARFLAGS=cr RANLIB=ranlib ## ## Install & strip ## INSTALL=sh $(TOPDIR)/install-sh -c INSTALL_STRIP=-s INSTALLDIR=mkdir -p BIN_MODE=755 DATA_MODE=644 RM=rm ## ## Debugging ## INSTALL_STRIP = CFLAGS += -g ifeq ($(PRELOAD_MODULES),1) X11_LIBS += -lXinerama -lXrandr endif notion-3+2012042300/utils/000077500000000000000000000000001174530661200147225ustar00rootroot00000000000000notion-3+2012042300/utils/Makefile000066400000000000000000000012401174530661200163570ustar00rootroot00000000000000## ## Notion utils/ Makefile ## # System-specific configuration is in system.mk TOPDIR=.. include $(TOPDIR)/build/system-inc.mk ###################################### SUBDIRS=ion-completefile ion-statusd INSTALL_SUBDIRS=$(SUBDIRS) SHELLSCRIPTS = ion-runinxterm ion-completeman TARGETS = ion-completeman ###################################### include $(TOPDIR)/build/rules.mk ###################################### _install: $(INSTALLDIR) $(DESTDIR)$(SHAREDIR) for i in $(SHELLSCRIPTS); do \ $(INSTALL) -m $(BIN_MODE) $$i $(DESTDIR)$(SHAREDIR); \ done %: %.in sed 's#@SHAREDIR@#$(SHAREDIR)#g' $< | \ sed 's#@VARDIR@#$(VARDIR)#g' > $@ chmod $(BIN_MODE) $@ notion-3+2012042300/utils/ion-completefile/000077500000000000000000000000001174530661200201555ustar00rootroot00000000000000notion-3+2012042300/utils/ion-completefile/Makefile000066400000000000000000000007441174530661200216220ustar00rootroot00000000000000## ## Notion ion-completefile Makefile ## # System-specific configuration is in system.mk TOPDIR=../.. include $(TOPDIR)/build/system-inc.mk ###################################### EXTRA_EXECUTABLE = ion-completefile SOURCES=ion-completefile.c LIBS += $(LIBTU_LIBS) INCLUDES += $(LIBTU_INCLUDES) CFLAGS += $(XOPEN_SOURCE) $(C99_SOURCE) ###################################### include $(TOPDIR)/build/rules.mk ###################################### _install: executable_install notion-3+2012042300/utils/ion-completefile/ion-completefile.c000066400000000000000000000363241174530661200235640ustar00rootroot00000000000000/* * 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 /* needed for NGROUPS_MAX on Solaris 10 */ #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: $prog [-icase] [-mid] (-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 $icase "$linebeg$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 vardir=${ION_VAR_PATH-@VARDIR@} syscache="$vardir/mancache" case "$action" in complete) if test "x$usercache" != x -a -f "$usercache"; then cachefile="$usercache" elif 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 notion-3+2012042300/utils/ion-runinxterm000077500000000000000000000012471174530661200176520ustar00rootroot00000000000000#!/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 "$@" notion-3+2012042300/utils/ion-statusd/000077500000000000000000000000001174530661200171745ustar00rootroot00000000000000notion-3+2012042300/utils/ion-statusd/Makefile000066400000000000000000000017151174530661200206400ustar00rootroot00000000000000## ## Notion ion-statusd Makefile ## # System-specific configuration is in system.mk TOPDIR=../.. include $(TOPDIR)/build/system-inc.mk ###################################### EXTRA_EXECUTABLE = ion-statusd SOURCES = ion-statusd.c exec.c extlrx.c 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)\" \ -DCF_ION_EXECUTABLE=\"notion\" LUA_SOURCES = statusd_date.lua statusd_mail.lua statusd_load.lua MAKE_EXPORTS = statusd include $(TOPDIR)/libmainloop/rx.mk ###################################### include $(TOPDIR)/build/rules.mk ###################################### _install: lc_install executable_install notion-3+2012042300/utils/ion-statusd/exec.c000066400000000000000000000012151174530661200202630ustar00rootroot00000000000000/* * ion/utils/ion-statusd/exec.c * * Copyright (c) Tuomo Valkonen 2005-2009. * * See the included file LICENSE for details. */ #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); } notion-3+2012042300/utils/ion-statusd/extlrx.c000066400000000000000000000010511174530661200206630ustar00rootroot00000000000000/* * ion/utils/ion-statusd/extlrx.c * * Copyright (c) Tuomo Valkonen 2004-2009. * * See the included file LICENSE for details. */ #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); } /*}}}*/ notion-3+2012042300/utils/ion-statusd/ion-statusd.c000066400000000000000000000163451174530661200216230ustar00rootroot00000000000000/* * ion/utils/ion-statusd/ion-statusd.c * * Copyright (c) Tuomo Valkonen 2004-2009. * * See the included file LICENSE for details. */ #include #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-2009."; static const char statusd_license[]=DUMMY_TR( "This software is licensed under the GNU Lesser General Public License\n" "(LGPL), version 2.1, extended with terms applying to the use of the name\n" "of the project, Ion(tm), unless otherwise indicated in components taken\n" "from elsewhere. For details, see the file LICENSE that you should have\n" "received with this software.\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.\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"), libtu_progname()); 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]); #ifdef CF_RELOCATABLE_BIN_LOCATION prefix_set(argv[0], CF_RELOCATABLE_BIN_LOCATION); #endif extl_init(); if(!statusd_register_exports()) return EXIT_FAILURE; prefix_wrap_simple(extl_add_searchdir, EXTRABINDIR); prefix_wrap_simple(extl_add_searchdir, MODULEDIR); prefix_wrap_simple(extl_add_searchdir, ETCDIR); prefix_wrap_simple(extl_add_searchdir, SHAREDIR); prefix_wrap_simple(extl_add_searchdir, LCDIR); extl_set_userdirs(CF_ION_EXECUTABLE); 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); } EXTL_EXPORT ExtlTab statusd_getloadavg() { ExtlTab t=extl_create_table(); #ifndef CF_NO_GETLOADAVG double l[3]; int n; n=getloadavg(l, 3); if(n>=1) extl_table_sets_d(t, "1min", l[0]); if(n>=2) extl_table_sets_d(t, "5min", l[1]); if(n>=3) extl_table_sets_d(t, "15min", l[2]); #endif return t; } notion-3+2012042300/utils/ion-statusd/statusd_date.lua000066400000000000000000000013521174530661200223640ustar00rootroot00000000000000-- -- ion/mod_statusbar/ion-statusd/statusd_date.lua -- -- Copyright (c) Tuomo Valkonen 2004-2009. -- -- See the included file LICENSE for details. -- 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) notion-3+2012042300/utils/ion-statusd/statusd_load.lua000066400000000000000000000032651174530661200223730ustar00rootroot00000000000000-- -- ion/mod_statusbar/ion-statusd/statusd_load.lua -- -- Copyright (c) Tuomo Valkonen 2004-2009. -- -- See the included file LICENSE for details. -- -- -- 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="1min", important_threshold=1.5, critical_threshold=4.0 } local settings=table.join(statusd.get_config("load"), defaults) local load_timer local function get_hint(v) 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 function fmt(l) if not l then return "?" else return string.format("%0.2f", l) end end local function update_load() local lds = statusd.getloadavg() f1, f5, f15 = fmt(lds["1min"]), fmt(lds["5min"]), fmt(lds["15min"]) statusd.inform("load", f1..", "..f5..", "..f15) statusd.inform("load_hint", get_hint(lds[settings.load_hint])) statusd.inform("load_1min", f1) statusd.inform("load_1min_hint", get_hint(lds["1min"])) statusd.inform("load_5min", f5) statusd.inform("load_5min_hint", get_hint(lds["5min"])) statusd.inform("load_15min", f15) statusd.inform("load_15min_hint", get_hint(lds["15min"])) load_timer:set(settings.update_interval, update_load) end -- Init --statusd.inform("load_template", "0.00, 0.00, 0.00"); load_timer=statusd.create_timer() update_load() notion-3+2012042300/utils/ion-statusd/statusd_mail.lua000066400000000000000000000073661174530661200224040ustar00rootroot00000000000000-- -- ion/mod_statusbar/ion-statusd/statusd_mail.lua -- -- Copyright (c) Tuomo Valkonen 2004-2009. -- -- See the included file LICENSE for details. -- -- 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() notion-3+2012042300/version.h000066400000000000000000000004131174530661200154160ustar00rootroot00000000000000#define NOTION_RELEASE "3-2012042300" #define NOTION_VERSION NOTION_RELEASE #define NOTION_API_VERSION NOTION_RELEASE /** and for backwards-compatibility */ #define ION_RELEASE NOTION_RELEASE #define ION_VERSION NOTION_RELEASE #define ION_API_VERSION NOTION_RELEASE