WMII Reforge  Check-in [15eae1e8e6]

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Import sources to have something to work with
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 15eae1e8e6d2c5f44cb7d45393ec8257a7b2a2274876a1eaabce19972ac143c7
User & Date: KhazAkar 2019-06-20 16:42:23.150
Context
2019-06-20
17:26
Delete old, insanely made, build system check-in: 544f8a0572 user: KhazAkar tags: trunk
16:42
Import sources to have something to work with check-in: 15eae1e8e6 user: KhazAkar tags: trunk
16:23
initial empty check-in check-in: a6b737c603 user: KhazAkar tags: trunk
Changes
Unified Diff Ignore Whitespace Patch
Added DISTRIBUTORS.
















































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
Distributors
============

This file only applies to those who wish to distribute wmii in any
form. The conditions herein do not apply to end users in any manner
whatsoever.

License Terms
=============

wmii is licensed under the liberal MIT License. This license allows
for free modification and distribution of the source code, so long
as credit is given to the author. To this end, the LICENSE file, or
an equivalent statement of its contents, MUST be distributed with
any significant binary or source portions of this software. The file
SHOULD be included with the software's documentation. The default
installation sequence provides for this.

Note that this condition only applies to distribution, and that the
end user is under no obligation to keep or install a copy of the
LICENSE file. Note also that this software may be sub-licensed under
more restrictive terms, though the original LICENSE text MUST remain.

The wmii Name
=============

The following conditions apply to any distribution which uses the
name wmii. These conditions apply only to wmii name, and not to its
source code or any other included materials.

When in doubt about any of these conditions or other matters of
packaging or distribution, please contact the wmii mailing lists at
<dev@suckless.org> or Kris Maglione <maglione.k@gmail.com>. The
conditions herein MAY be contravened by any more lenient
distribution terms agreed upon by the latter, which SHOULD replace
this file in the form of a PGP signed permissions notice.

Version Strings
---------------

Any binary distribution of wmii MUST have a properly set VERSION
string. This is the string printed by the 'wmii' binary when
invoked with the '-v' flag.

This string may normally be set in 'mk/wmii.mk'. Unmodified builds
from the Mercurial tree automatically set this string based on the
Mercurial local revision number, so long as the 'hg' command is
present and properly functioning.

Any version which is an official release, alpha, or beta, MUST
contain the release version. Alpha and beta releases MUST be
proceeded directly by "a" or "b" followed by the alpha or beta
number respectively. wmii 4.0, Alpha 3, for instance, MUST be
formatted as 4.0a3

Any version which is not an official release or snapshot MUST
contain the Mercurial local revision number or changeset hash in its
version string. The local revision number MUST be within 5 revisions
of the equivalent changeset in the official canonical repositories
at http://hg.suckless.org/ and http://wmii.googlecode.com/. This
SHOULD be formatted as hgXXXX, where XXXX is the decimal revision
number.

The version string of any official snapshot release MUST, if it does
not contain Mercurial revision information as above, contain the
date of the snapshot in the form YYYYMMDD, and SHOULD contain the
word snap or snapshot.

The version string of a snapshot MAY contain the version name of a
full release that the snapshot is expected to precede, but it MUST
be either directly preceded, or directly followed by, the word
'pre', optionally separated by a non-alphanumeric character,
including -~_,./.

Modifications
-------------

Any binary distribution which is modified in any non-trivial way
MUST signify the modifications in its name or version string. This
DOES NOT include minor patches to improve consistency with the rest
of the system, including changing the default terminal emulator,
POSIX-compliant shell, or installation prefix.

Source form distribution MAY include non-trivial patches without
such modifications, provided that the user is made clearly aware of
them at build or install time and/or prompted in some way to enable
or disable them.

Added FAQ.








































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
			     wmii 3.10

@@ If you're reading this file before it's been installed,
@@ substitute the following directories for their placeholders.
@@   @DOCDIR@		doc/
@@   @EXAMPLES@		examples/
@@   @LOCALCONF@	~/.wmii or ~/.wmii-hg (if this is a snapshot)
@@   @ALTDOC@		alternative_wmiircs/
@@
Frequently Asked Questions
==========================

1. I've just upgraded and something doesn't work.
-------------------------------------------------
If you compiled wmii yourself, please try again from a clean source
tree. If wmii still fails, try deleting or renaming your old
configuration files. If this fails, see questions #2.

2. I've read all of the docs, but I still need help.
----------------------------------------------------
You can get support[1] in the #suckless irc channel on irc.oftc.net, or
you can subscribe to the mailing list by sending an email to
dev+subscribe@suckless.org. You can also browse or search the
mailing list archives[2] at Gmane.

3. I think I've found a bug.
----------------------------
You can report bugs at the issue tracker at the Google Code
project[3]. Please be sure to search for your problem before you
open a new issue.

4. How do I get a list of keyboard shortcuts?
---------------------------------------------
The default keyboard shortcuts are listed in both the wmii(1)
manpage and in the user guide[4]. You can also get a list of all
current keyboard shortcuts at any time by selecting 'showkeys' from
the actions menu (M-a).

5. How do I customize wmii? How do I change my keyboard shortcuts?
------------------------------------------------------------------
You can customize wmii by editing its configuration script. If
you're using the stock wmiirc configuration script, you can add key
bindings and change your theme by editing @LOCALCONF@/wmiirc_local.
See @EXAMPLES@/wmiirc_local for more information. You should also be
sure to read wmii(1) and the user guide[4].

6. I've heard I can do my configuration in any language?
--------------------------------------------------------
While wmii is driven by a sh(1) script by default, it ships with
configuration scripts in several other languages, and more still are
available elsewhere on the web. See @ALTDOC@/README for more
information.

7. I've made some changes to my configuration. How do I reload it?
------------------------------------------------------------------
You can rerun your wmiirc script from the actions menu at any time.
If you're running the stock wmiirc, just select wmiirc. If you're
running the python wmiirc (and haven't moved it to
@LOCALCONF@/wmiirc), type python/wmiirc, and so on.

8. How do I restart wmii without killing X?
-------------------------------------------
You can either run 'exec wmii' from the actions menu or write 
'exec wmii' to the /ctl virtual file.

9. Why is there space around my terminal windows?
-------------------------------------------------
Your terminal has asked to only be resized in certain increments,
and there's not enough space for another row. `wmii` is forced to
compromise and leave blank space around it. If you'd rather wmii to
ignore the terminal's request, write 'incmode ignore'[5] to the /ctl
virtual file. Note, though, that this will prevent `wmii`'s normal
behavior of trying to keep these increment gaps as small as
possible, and will therefore result in more wasted space than
otherwise.

10. On FreeBSD, using p9p[6], I get an error about not being able to open /dev/fd/7.
------------------------------------------------------------------------------------
You need to mount fdescfs on /dev/fd. See the BUGS section of rc(1)
for details.

11. How do I set a background image?
------------------------------------
This isn't the job of a window manager. You can set the background
with a third party tool, such as wmsetbg, Esetbg, feh, qiv,
xsetroot, etc.

12. How do I enable sequential shortcuts (like in ratpoison)?
 or How do I use wmii with emacs? The shortcuts collide!
-------------------------------------------------------------

Some applications make extensive use of the Alt key. The preferred
solution is to use the Windows, Apple, or Penguin key in its place.
It's assigned the identifier Mod4 on most systems. Set the
following in wmiirc_local:

    MODKEY=Mod4

Alternatively, you can use key chains, so you're required to press a
certain key combination before wmii accepts its shortcuts:

    MODKEY=Control-i,

13. How do I find out the names of keys to define keyboard shorcuts?
--------------------------------------------------------------------
The easiest way is to run wikeyname(1) and type the key you want to
bind.


[1] http://suckless.org/community
[2] http://dir.gmane.org/gmane.comp.misc.suckless
[3] http://wmii.googlecode.com/
[4] @DOCDIR@/wmii.pdf
[5] For more information, see wmii(1).
[6] http://plan9.us

Added LICENSE.












































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

Copyright © 2006-2010 Kris Maglione <maglione.k@gmail.com>
Copyright © 2003-2006 Anselm R Garbe <anselm@garbe.us>
Portions Copyright © 2002 by Lucent Technologies.

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.
Added Makefile.


























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
ROOT=.
include $(ROOT)/mk/hdr.mk
include $(ROOT)/mk/wmii.mk

DIRS = \
	doc	     \
	examples     \
	man	     \
	lib	     \
	cmd	     \
	rc	     \
	alternative_wmiircs

DOCS = FAQ \
       LICENSE \
       README.md

deb-dep:
	IFS=', '; \
	apt-get -qq install build-essential $$(sed -n 's/([^)]*)//; s/^Build-Depends: \(.*\)/\1/p' debian/control)

DISTRO = unstable
deb:
	$(ROOT)/util/genchangelog wmii-hg $(VERSION) $(DISTRO)
	dpkg-buildpackage -rfakeroot -b -nc
	[ -d .hg ] && hg revert debian/changelog || true

include $(ROOT)/mk/dir.mk

Added NEWS.




























































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
3.10b1:
    * /colrules widths may now be specified in pixels.
    * /tagrules has been replaced with the more general /rules
    * The format of the bar files has changed.
    * Add witray system tray program.
    * Floating clients can be collapsed by clicking their layout boxes.
    * Dock windows act more like dock windows.
    * The FocusFloating and FocusColumn events have been removed.
    * The tag '!' is no longer special.
    * Xft is now loaded on demand.

3.9.2:             
    * Fixed some managed move bugs.
    * Work around mawk bug that broke wmiirc.

3.9.1:
    * Workaround a dash 0.5.6 bug that broke wmiirc.
    * Noticably speed-up python wmiirc startup.
    * Disable static linking which breaks wmiir in glibc 2.12.

3.9:
    * wmii9menu is now Xinerama aware.
    * Install READMEs to $(PREFIX)/share/doc/wmii/.
    * Documentation updates. Add wmiir.1, wmii9menu.1.
    * Allow dragging floating clients from anywhere in their titlebars.
    * Allow specifying screen in area specs.
    * Change default $MODKEY to Mod4.
    * Minor changes to pygmi.events API.
    * Allow client to follow tag change in python wmiirc.
    * Update /tag/*/index to be more useful on Xinerama.
    * Add showkeys action to shell and python wmiirc.
    * Restore windows from floating layer to their original Xinerama screen.
    * Hide bar on non-primary Xinerama screens.
    * Allow resizing of rightmost and leftmost column dividers.

3.9a2:
    * Add Suraj's Rumai-based wmiirc.
    * Move rc.wmii to alternative_wmiircs/plan9port/wmiirc.
    * Install wmii.pdf to $(PREFIX)/share/doc/.
    * Focus windows regardless of whether they form a new group.
    * Update selection and execution of wmiirc: no more magic.
    * Update wmii.1
    * Add alternative_wmiircs READMEs.

3.9a1:
    * Add new wmii guide. See doc/wmii.pdf
    * Allow for programmable completion in wimenu.
    * Use pkg-config globally.
    * Add Xft (antialiased font) support.
    * Add python wmiirc/9P client library
    * Allow bindings to work regardless of caps lock.
    * Add M-f fullscreen toggle key binding.
    * Augment /client/*/ctl Fullscreen command.
    * Allow setting of increment display from /ctl.
    * Show a client's extra tags in its titlebar.
    * Darken background when floating area selected.
    * Allow bar on top or bottom.
    * Allow for wmiirc_local.
    * Add grow and nudge commands to /tag/*/ctl.
    * Cascade windows when the floating layer fills.
    * Support alpha-transparant windows.
    * Add regex tag support.
    * It is now possible to float/unfloat windows with the mouse.
    * Make the bar Xdnd aware; DND between views is now possible. Fixed some window raising/moving bugs.
    * Add a notification bar.
    * Improved floating mouse resizing.
    * Improved mouse move/resize support for managed mode.
    * Better return from floating/fullscreen to managed mode.
    * Allow comments (#.*\n) in rules and ctl files.
    * Add /client/*/ctl ‘slay’ command.
    * Detect unresponsive clients on ‘kill’.
    * Draw titlebars of floating clients differently.
    * Add wihack: LD_PRELOAD hack to set window properties of programs:
    * Respect window groups
    * Add ‘Kill’ to client right-click menu
    * wmii9menu now takes similar args to wimenu
    * Document grow/nudge commands.
    * Add wimenu with history and caret support
    * Add wistrut. Undocumented, not built by default.
    * EWMH strut support.
    * Basic EWMH support.
    * Better fullscreen support.
    * XRandR support.
    * Xinerama support.

2008-08-25:
    * libixp version 97 now required
    * Stack and max modes now affect floating clients:
        - max: Collapsed clients disappear, all clients disappear
               when managed layer is selected.
        - stack: All clients but selected are collapsed.
    * Adobe's Flash plugin's fullscreen mode now works.
    * Some annoying focus bugs are fixed.

Added PKGBUILD.














































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

pkgname=(wmii-hg python-pyxp-hg python-pygmi-hg)
pkgver=2755
pkgrel=1
pkgdesc="The latest hg pull of wmii, a lightweight, dynamic window manager for X11"
url="http://wmii.suckless.org"
license=(MIT)
arch=(i686 x86_64)
makedepends=(mercurial python "libixp-hg>="$(sed -rn <mk/wmii.mk 's/.*IXP_NEEDAPI=([0-9]+).*/\1/p'))
options=(!strip)
source=()

FORCE_VER=$(hg log -r . --template {rev})

_make() {
    cd $startdir
    make PREFIX=/usr		\
	 PYPREFIX=--prefix=/usr	\
         ETC=/etc		\
         DESTDIR="$pkgdir"	\
         "$@"
}

build() {
    _make "${flags[@]}" || return 1
}

package_wmii-hg() {
    depends=(libx11 libxinerama libxrandr)
    optdepends=("plan9port: for use of the alternative plan9port wmiirc" \
            "${pkgname[2]}: for use of the alternative Python wmiirc" \
            "ruby-rumai: for use of the alternative Ruby wmiirc" \
            "libxft: for anti-aliased font support")
    provides=(wmii)
    conflicts=(wmii)
    _make install PYMODULES= || return 1

    install -m644 -D ./debian/file/wmii.desktop "$pkgdir/etc/X11/sessions/wmii.desktop"
    install -m644 -D ./LICENSE "$pkgdir/usr/share/licenses/$pkgname/LICENSE"
}

package_python-pyxp-hg() {
    pkgdesc="Python 9P client library"
    arch=(any)
    depends=(python)
    _make -C alternative_wmiircs/python pyclean pyxp.install
}

package_python-pygmi-hg() {
    pkgdesc="Python wmii interaction library"
    arch=(any)
    depends=(python-pyxp-hg)
    _make -C alternative_wmiircs/python pyclean pygmi.install
}

Added README.md.






























































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
[![Build Status](https://travis-ci.org/0intro/wmii.svg?branch=master)](https://travis-ci.org/0intro/wmii)
[![Coverity Scan Build Status](https://scan.coverity.com/projects/0intro-wmii/badge.svg)](https://scan.coverity.com/projects/0intro-wmii)

Abstract
--------
wmii is a dynamic window manager for X11.  It supports classic and
tiled window management with extended keyboard, mouse, and 9P-based[1]
remote control.  It consists of the wmii(1) window manager and the
wmiir(1) the remote access utility.


Requirements
------------
In order to build wmii you need the Xlib header files and libixp.
xmessage is used by the default scripts.  Libixp, if not provided, can
be obtained from http://libs.suckless.org/.  On debian, you should be
able to obtain all dependencies by running `make deb-dep`.  Python is
recommended for more advanced configurations.


Installation
------------
First, edit config.mk to match your local setup.

To build, simply run:
	make

To install, run the following, as root if necessary:
	make install

On debian, you should only have to run `make deb` to create a debian
package.  No further configuration should be necessary.


Running wmii
------------
Add the following line to your .xinitrc to start wmii using startx:

    until wmii; do :; done

In order to connect wmii to a specific display, make sure that the
DISPLAY environment variable is set correctly.  For example:

    DISPLAY=:1 wmii

This will start wmii on display :1.


Configuration
-------------
The configuration of wmii is done by customizing the rc script wmiirc,
which remotely controls the window manager and handles various events.
The main wmiirc script lives in @GLOBALCONF@ while wmiirc_local goes
in @LOCALCONF@.

More advanced versions of wmiirc are provided in python and ruby.
For more information on them, see alternative_wmiircs/README.

Credits
-------
The following people have contributed especially to wmii in various
ways:

- Christoph Wegscheider <christoph dot wegscheider at wegi dot net>
- Georg Neis <gn at suckless dot org>
- Uwe Zeisberger <zeisberg at informatik dot uni-freiburg dot de>
- Uriel <uriel99 at gmail dot com>
- Scot Doyle <scot at scotdoyle dot com>
- Sebastian Hartmann <seb dot wmi at gmx dot de>
- Bernhard Leiner <bleiner at gmail dot com>
- Jonas Domeij <jonas dot domeij at gmail dot com>
- Vincent <10 dot 50 at free dot fr>
- Oliver Kopp <olly at flupp dot de>
- Sebastian Roth <sebastian dot roth at gmail dot com>
- Nico Golde <nico at ngolde dot de>
- Steve Hoffman <steveh at g2switchworks dot com>
- Christof Musik <christof at senfdax dot de>
- Steffen Liebergeld <perl at gmx dot org>
- Tobias Walkowiak <wal at ivu dot de>
- Sander van Dijk <a dot h dot vandijk at gmail dot com>
- Salvador Peiro <saoret dot one at gmail dot com>
- Anthony Martin <ality at pbrane dot org>
- Icarus Sparry <wmii at icarus dot freeuk dot com>
- Norman Golisz <norman dot golisz at arcor dot de>
- Stefano K. Lee <wizinblack at gmail dot com >
- Stefan Tibus <sjti at gmx dot net>
- Neptun <neptun at gmail dot com>
- Daniel Wäber <_wabu at web dot de>


References
----------
[1] http://9p.cat-v.org
[2] http://plan9.us

Added TODO.


























>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
BUGS
* collapsed clients outside stacked mode don't always uncollapse when they receive focus
* various qiv brokenness
* dosbox won't grab the mouse

4.0
* Opaque managed moves. I know I've argued against it, but it may be doable.
* Clicking layout boxes should do useful things.
* Collapse/uncollapse frames with the keyboard.
* Open modes (to replace colmodes).
* Resizable managed area. Maybe. Struts seem to do everything this might.
* New dmenu, with real cursor; snarfable.

Added alternative_wmiircs/Makefile.


























>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
ROOT=..
include $(ROOT)/mk/hdr.mk
include $(ROOT)/mk/wmii.mk

BIN = $(GLOBALCONF)
DIRS =	python	\
	plan9port \
	ruby

DOCS   = README
DOCDIR = $(DOC)/alternative_wmiircs

include $(ROOT)/mk/dir.mk
Added alternative_wmiircs/README.














































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Alternative wmiirc scripts
==========================

This folder contains alternative implementations of wmii's rc
scripts. Each folder contains a different implementation,
described below, including its own README, wmiirc script, and
possibly other suppporting files and libraries. These scripts
are installed along with wmii to $(ETC) as defined in config.mk.

It usually suffices to start the included `wmiirc` script at
wmii startup. Invoking wmii with the flag '-r python/wmiirc',
for instance, will start the python implementation.
Alternatively, if you use a session manager, you can add this
line to @LOCALCONF@/wmiirc (which must be executable):

    wmiir xwrite /ctl spawn python/wmiirc

  Index
  ------------- ----------------------------------------------------
  python/	A pure Python wmiirc implementation. 
  plan9port/	A Plan 9 Port/rc shell based wmiirc implementation
  ruby/		A pure-ruby wmiirc implementation, by Suraj Kurapati

Added alternative_wmiircs/plan9port/Makefile.


















>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
ROOT=../..
include $(ROOT)/mk/hdr.mk
include $(ROOT)/mk/wmii.mk

DOCS    = README
EXECS   = wmiirc

DIR    = $(GLOBALCONF)/plan9port
DOCDIR = $(DOC)/alternative_wmiircs/plan9port
Added alternative_wmiircs/plan9port/README.




















>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
plan9port wmiirc
================

This directory contains a Plan 9 based wmiirc script.  This script was
traditionally the default wmiirc for wmii, but has been moved for
portability reasons.  To run this script, either Plan 9 from User
Space[1] (plan9port for short) or 9base[2] is required. Modifications
can be placed in @LOCALCONF@/wmiirc_local.rc, which must
be executable.

Added alternative_wmiircs/plan9port/wmiirc.
























































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
#!/bin/sh -f
test $#* '=' 0 || {
	p="$PATH"
	which rc >/dev/null || PATH="$PLAN9:$p"
	which rc >/dev/null || PATH="/usr/local/plan9/bin:$p"
	which rc >/dev/null || PATH="/usr/local/9/bin:$p"
	which rc >/dev/null || PATH="/opt/plan9/bin:$p"
	which rc >/dev/null || PATH="/opt/9/bin:$p"
	which rc >/dev/null || PATH="/usr/plan9/bin:$p"
	which rc >/dev/null || PATH="/usr/9/bin:$p"
	exec rc $0
}

cd
scriptname=$0
oldpath=$path; path=($PLAN9/bin $path)
. wmii.rc wmiirc # Include utility functions

# WMII Configuration

# Keys
MODKEY=Mod4
UP=k
DOWN=j
LEFT=h
RIGHT=l

# Bars
noticetimeout=5
noticebar=/rbar/!notice

# Theme
wmiifont='drift,-*-fixed-*-*-*-*-9-*-*-*-*-*-*-*'
wmiifont='-*-fixed-medium-r-*-*-13-*-*-*-*-*-*-*'
wmiinormcol=('#000000' '#c1c48b' '#81654f')
wmiifocuscol=('#000000' '#81654f' '#000000')
wmiibackground='#333333'
wmiifloatbackground='#222222'
fn setbackground { xsetroot -solid $* }

# Programs
WMII_TERM=(xterm)

# Column Rules
wmiir write /colrules <<!
    /gimp/ -> 17+83+41
    /.*/ -> 62+38 # Golden Ratio
!

# Tagging Rules
wmiir write /rules <<!
    # Apps with system tray icons like to their main windows
    # Give them permission.
    /^Pidgin:/ allow=+activate

    # MPlayer and VLC don't float by default, but should.
    /MPlayer|VLC/ floating=on

    # ROX puts all of its windows in the same group, so they open
    # with the same tags.  Disable grouping for ROX Filer.
    /^ROX-Filer:/ group=0
!

# Status Bar Info
fn status {
	echo -n label `{uptime | sed 's/.*://; s/, / /g'} '|' \
		      `{date} }

# Generic overridable startup details
fn startup { witray & }

# End Configuration

# For the time being, this file follows the lisp bracing
# convention. i.e.:
#  if(frob this) {
#    frob that
#    if(frob theother) {
#      unfrob this
#      unfrob that}}
# Comments welcome.

confpath=`{echo $WMII_CONFPATH | sed 'y/:/ /'}

# Events
fn sigexit {
	rm -f $progs_file
	wi_cleankeys}

fn Event-CreateTag {
	echo colors $wmiinormcol $wi_newline label $* | wmiir create /lbar/$"*}
fn Event-DestroyTag {
	wmiir remove /lbar/$"*}
fn Event-FocusTag {
	wmiir xwrite /lbar/$"* colors $wmiifocuscol}
fn Event-UnfocusTag {
	wmiir xwrite /lbar/$"* colors $wmiinormcol}
fn Event-UrgentTag {
	shift
	wmiir xwrite /lbar/$"* label '*'$"*}
fn Event-NotUrgentTag {
	shift
	wmiir xwrite /lbar/$"* label $"*}
fn Event-AreaFocus {
	if(~ $1 '~')
		setbackground $wmiifloatbackground
	if not
		setbackground $wmiibackground }

fn Event-Unresponsive {
	client = $1; shift
	@{
		msg = 'The following client is not responding. What would you like to do?'
		resp = `{wihack -transient $client \
				xmessage -nearmouse -buttons Kill,Wait -print \
				$msg $wi_newline '' `{wmiir read /client/$client/label}}
		if(~ $resp Kill)
			wmiir xwrite /client/$client/ctl slay
	}&}
fn Event-Notice {
	wmiir xwrite $noticebar label $wi_arg

	/bin/kill $xpid >[2]/dev/null # Let's hope this isn't reused...
	{ sleep $noticetimeout; wmiir xwrite $noticebar ' ' }& # Bug...
	xpid = $apid}

fn Event-LeftBar^(Click DND) {
	shift; wmiir xwrite /ctl view $*}

fn ClientMenu-3-Delete {
	wmiir xwrite /client/$1/ctl kill}
fn ClientMenu-3-Kill {
	wmiir xwrite /client/$1/ctl slay}
fn ClientMenu-3-Fullscreen {
	wmiir xwrite /client/$1/ctl Fullscreen on}
fn Event-ClientMouseDown {
	wi_fnmenu Client $2 $1 &}

fn LBarMenu-3-Delete {
	tag=$1; clients=`{wmiir read /tag/$tag/index | awk '/[^#]/{print $2}'}
	for(c in $clients) {
		if(~ $tag `{wmiir read /client/$c/tags})
			wmiir xwrite /client/$c/ctl kill
		if not
			wmiir xwrite /client/$c/tags -$tag}
	if(~ $tag `{wi_seltag}) {
		newtag = `{wi_tags | awk -v't='$tag '
			$1 == t { if(!l) getline l
				  print l
				  exit }
			{ l = $0 }'}
		wmiir xwrite /ctl view $newtag}}
fn Event-LeftBarMouseDown {
	wi_fnmenu LBar $* &}

# Actions
fn Action-exec {
	wmiir xwrite /ctl exec $*}
fn Action-quit {
	wmiir xwrite /ctl quit}
fn Action-rehash {
	comm -23 <{ls `{namespace}^/proglist.* >[2]/dev/null | awk -F'.' '{print $NF}'} \
		 <{ps | awk '{print $2}'} |
	while(id=`{read})
		rm `{namespace}^/proglist.$id
	wi_proglist $PATH >$progs_file}
fn Action-showkeys {
	echo $wmiikeyhelp | xmessage -file -
}
fn Action-status {
	flag x -; flag r -
	if(wmiir remove /rbar/status >[2]/dev/null)
		sleep 2
	echo colors $wmiinormcol | wmiir create /rbar/status
	while(status | wmiir write /rbar/status)
		sleep 1
}

# Source Variables, &c
if(~ $0 ('' */)wmiirc_local.rc)
	wi_notice This file should not be named wmiirc_local.rc
if not
	. `{wi_script -f wmiirc_local.rc}
echo colors $wmiinormcol | wmiir create $noticebar
startup

# Key Bindings
_keys = `{wi_getfuns Key}
fn keygroup {
	wmiikeyhelp = $wmiikeyhelp ^ $wi_newline ^ '  ' ^ $"* ^ $wi_newline}
fn key {
	help=$1; shift
	key=()
	for(k) {
		if(! ~ $k $_keys) {
			ifs=() { wmiikeyhelp = `{
				printf "%s    %- 20s %s\n" $wmiikeyhelp $k $help}}
			key = ($key Key-$k)}}
	~ $#key 0}

keygroup Moving around
key 'Select the client to the left' $MODKEY-$LEFT || fn $key {
	wmiir xwrite /tag/sel/ctl select left}
key 'Select the client to the right' $MODKEY-$RIGHT || fn $key {
	wmiir xwrite /tag/sel/ctl select right}
key 'Select the client below' $MODKEY-$DOWN || fn $key {
	wmiir xwrite /tag/sel/ctl select down}
key 'Select the client above' $MODKEY-$UP || fn $key {
	wmiir xwrite /tag/sel/ctl select up}

key 'Toggle between floating and managed layers' $MODKEY-space || fn $key {
	wmiir xwrite /tag/sel/ctl select toggle}

keygroup Moving through stacks
key 'Select the stack below' $MODKEY-Control-$DOWN || fn $key {
	wmiir xwrite /tag/sel/ctl select down stack}
key 'Select the stack above' $MODKEY-Control-$UP || fn $key {
	wmiir xwrite /tag/sel/ctl select up stack}

keygroup Moving clients around
key 'Move selected client to the left' $MODKEY-Shift-$LEFT || fn $key {
	wmiir xwrite /tag/sel/ctl send sel left}
key 'Move selected client to the right' $MODKEY-Shift-$RIGHT || fn $key {
	wmiir xwrite /tag/sel/ctl send sel right}
key 'Move selected client down' $MODKEY-Shift-$DOWN || fn $key {
	wmiir xwrite /tag/sel/ctl send sel down}
key 'Move selected client up' $MODKEY-Shift-$UP || fn $key {
	wmiir xwrite /tag/sel/ctl send sel up}

key 'Toggle selected client between floating and managed layers' $MODKEY-Shift-space || fn $key {
	wmiir xwrite /tag/sel/ctl send sel toggle}

keygroup Client actions
key 'Toggle selected client''s fullsceen state' $MODKEY-f || fn $key {
	wmiir xwrite /client/sel/ctl Fullscreen toggle}
key 'Close client' $MODKEY-Shift-c || fn $key {
	wmiir xwrite /client/sel/ctl kill}

keygroup Changing column modes
key 'Set column to default mode' $MODKEY-d || fn $key {
	wmiir xwrite /tag/sel/ctl colmode sel default-max}
key 'Toggle between floating and managed layers' $MODKEY-s || fn $key {
	wmiir xwrite /tag/sel/ctl colmode sel stack-max}
key 'Set column to max mode' $MODKEY-m || fn $key {
	wmiir xwrite /tag/sel/ctl colmode sel stack+max}

keygroup Running programs
key 'Open wmii actions menu' $MODKEY-a || fn $key {
	Action `{wi_actions | wimenu -h $hist.action -n $histlen} &}
key 'Open program menu' $MODKEY-p || fn $key {
	ifs=() { cmd = `{wimenu -h $hist.prog -n $histlen <$progs_file} }
	wi_runcmd $cmd & }

key 'Launch a terminal' $MODKEY-Return || fn $key {
	wi_runcmd $WMII_TERM &}

keygroup Other
key 'Toggle all other key bindings' $MODKEY-Control-t || fn $key {
	switch(`{wmiir read /keys | wc -l}) {
	case 0 1
		wmiir xwrite /keys $keys
		wmiir xwrite /ctl grabmod $MODKEY
	case *
		ifs=() { keys=`{wmiir read /keys} }
		wmiir xwrite /keys $MODKEY-Control-t
		wmiir xwrite /ctl grabmod Mod3
	}}

keygroup Tag actions
key 'Change to another tag' $MODKEY-t || fn $key {
	tag=`{wi_tags | wimenu -h $hist.tag -n 50} && wmiir xwrite /ctl view $tag &}
key 'Retag the selected client' $MODKEY-Shift-t || fn $key {
	sel=`{wi_selclient} {
	tag=`{wi_tags | wimenu -h $hist.tag -n 50} && wmiir xwrite /client/$sel/tags $tag } &}
key 'Move to the next tag' $MODKEY-n || fn $key {
	wmiir xwrite /ctl view `{wi_tags | wi_nexttag}}
key 'Move to the previous tag' $MODKEY-b || fn $key {
	wmiir xwrite /ctl view `{wi_tags | sort -r | wi_nexttag}}

key 'Move to the numbered view' $MODKEY-^`{seq 0 9} || fn $key {
	wmiir xwrite /ctl view `{echo $1 | sed 's/.*-//'}}
key 'Retag selected client with the numbered tag' Shift-$MODKEY-^`{seq 0 9} || fn $key {
	wmiir xwrite /client/sel/tags `{echo $1 | sed 's/.*-//'}}
# WM Configuration
wmiir write /ctl <<!
	grabmod $MODKEY
	border 2
	font $wmiifont
	focuscolors $wmiifocuscol
	normcolors $wmiinormcol
!
setbackground $wmiibackground

# Source Overrides
Action overridekeys

# Misc Setup
progs_file=`{namespace}^/proglist.$pid
hist=`{echo $WMII_CONFPATH | sed 's,:.*,/,'}^/history
histlen=5000
Action status &
Action rehash &

# Tag Bar Setup
ifs=$wi_newline {
	rc -c 'wmiir rm /lbar/^$*' >[2]/dev/null \
		`{comm -23 <{wmiir ls /lbar} \
			   <{wi_tags}}
	seltag=`{wi_seltag}
	for(tag in `{wi_tags}) {{
		if(~ $tag $seltag)
			echo colors $wmiifocuscol $wi_newline label $tag
		if not
			echo colors $wmiinormcol $wi_newline label $tag
		} | wmiir create /lbar/$tag}}
wi_eventloop
Added alternative_wmiircs/python/Makefile.




























>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
ROOT=../..
include $(ROOT)/mk/hdr.mk
include $(ROOT)/mk/wmii.mk

PYMODULES = pyxp pygmi

DOCS   = README
EXECS  = wmiirc
TEXT   = wmiirc.py

DIR    = $(GLOBALCONF)/python
DOCDIR = $(DOC)/alternative_wmiircs/python

include $(ROOT)/mk/python.mk
Added alternative_wmiircs/python/README.
































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Python wmiirc
=============

This directory contains a pure Python implementation of
wmiirc. The two included libraries, pyxp and pygmi, are a 9P
client and wmii filesystem utility module, respectively. To
use this library, simply copy the contents of this direcctory
to @LOCALCONF@. To customize it, either modify wmiirc.py
directly, or create wmii_local.py and store your modifications
there. The latter approach is preferable in that future
modifications to wmiirc.py can usually be painlessly
integrated.

The documentation is sparse, but wmiirc.py should serve as a
fairly comprehensive example.

Added alternative_wmiircs/python/pygmi.py.




























>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/usr/bin/env python

from distutils.core import setup

setup(name='pygmi',
      version='0.2',
      description='Python wmii interaction library',
      author='Kris Maglione',
      author_email='maglione.k@gmail.com',
      url='http://wmii.suckless.org',
      packages=['pygmi'],
      license='MIT',
     )

Added alternative_wmiircs/python/pygmi/__init__.py.






















































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import os
import sys

from pyxp.asyncclient import Client

if 'WMII_ADDRESS' in os.environ:
    client = Client(os.environ['WMII_ADDRESS'])
else:
    client = Client(namespace='wmii')

confpath = os.environ.get('WMII_CONFPATH', '%s/.wmii' % os.environ['HOME']).split(':')
shell = os.environ['SHELL']

sys.path += confpath

from pygmi.util import *
from pygmi.event import *
from pygmi.fs import *
from pygmi.menu import *
from pygmi.monitor import *
from pygmi import util, event, fs, menu, monitor

__all__ = (fs.__all__ + monitor.__all__ + event.__all__ +
           menu.__all__ + util.__all__ +
           ('client', 'confpath', 'shell'))

# vim:se sts=4 sw=4 et:
Added alternative_wmiircs/python/pygmi/event.py.








































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
import os
import re
import sys
import traceback

import pygmi
from pygmi.util import prop
from pygmi import monitor, client, curry, call, program_list, _

__all__ = ('keys', 'events', 'Match')

class Match(object):
    """
    A class used for matching events based on simple patterns.
    """
    def __init__(self, *args):
        """
        Creates a new Match object based on arbitrary arguments
        which constitute a match pattern. Each argument matches an
        element of the original event. Arguments are matched based
        on their type:

            _:      Matches anything
            set:    Matches any string equal to any of its elements
            list:   Matches any string equal to any of its elements
            tuple:  Matches any string equal to any of its elements

        Additionally, any type with a 'search' attribute matches if
        that callable attribute returns True given element in
        question as its first argument.

        Any other object matches if it compares equal to the
        element.
        """
        self.args = args
        self.matchers = []
        for a in args:
            if a is _:
                a = lambda k: True
            elif isinstance(a, basestring):
                a = a.__eq__
            elif isinstance(a, (list, tuple, set)):
                a = (lambda ary: (lambda k: k in ary))(a)
            elif hasattr(a, 'search'):
                a = a.search
            else:
                a = str(a).__eq__
            self.matchers.append(a)

    def match(self, string):
        """
        Returns true if this object matches an arbitrary string when
        split on ascii spaces.
        """
        ary = string.split(' ', len(self.matchers))
        if all(m(a) for m, a in zip(self.matchers, ary)):
            return ary

def flatten(items):
    """
    Given an iterator which returns (key, value) pairs, returns a
    new iterator of (k, value) pairs such that every list- or
    tuple-valued key in the original sequence yields an individual
    pair.

    Example: flatten({(1, 2, 3): 'foo', 4: 'bar'}.items()) ->
        (1, 'foo'), (2: 'foo'), (3: 'foo'), (4: 'bar')
    """
    for k, v in items:
        if isinstance(k, (list, tuple)):
            for key in k:
                yield key, v
        else:
            yield k, v

class Events():
    """
    A class to handle events read from wmii's '/event' file.
    """
    def __init__(self):
        """
        Initializes the event handler
        """
        self.events = {}
        self.eventmatchers = {}
        self.alive = True

    def dispatch(self, event, args=''):
        """
        Distatches an event to any matching event handlers.

        The handler which specifically matches the event name will
        be called first, followed by any handlers with a 'match'
        method which matches the event name concatenated to the args
        string.

        Param event: The name of the event to dispatch.
        Param args:  The single arguments string for the event.
        """
        try:
            if event in self.events:
                self.events[event](args)
            for matcher, action in self.eventmatchers.iteritems():
                ary = matcher.match(' '.join((event, args)))
                if ary is not None:
                    action(*ary)
        except Exception, e:
            try:
                traceback.print_exc(sys.stderr)
            except:
                pass

    def loop(self):
        """
        Enters the event loop, reading lines from wmii's '/event'
        and dispatching them, via #dispatch, to event handlers.
        Continues so long as #alive is True.
        """
        keys.mode = 'main'
        for line in client.readlines('/event'):
            if not self.alive:
                break
            self.dispatch(*line.split(' ', 1))
        self.alive = False

    def bind(self, items={}, **kwargs):
        """
        Binds a number of event handlers for wmii events. Keyword
        arguments other than 'items' are added to the 'items' dict.
        Handlers are called by #loop when a matching line is read
        from '/event'. Each handler is called with, as its sole
        argument, the string read from /event with its first token
        stripped.

        Param items: A dict of action-handler pairs to bind. Passed
            through pygmi.event.flatten. Keys with a 'match' method,
            such as pygmi.event.Match objects or regular expressions,
            are matched against the entire event string. Any other
            object matches if it compares equal to the first token of
            the event.
        """
        kwargs.update(items)
        for k, v in flatten(kwargs.iteritems()):
            if hasattr(k, 'match'):
                self.eventmatchers[k] = v
            else:
                self.events[k] = v

    def event(self, fn):
        """
        A decorator which binds its wrapped function, as via #bind,
        for the event which matches its name.
        """
        self.bind({fn.__name__: fn})
events = Events()

class Keys(object):
    """
    A class to manage wmii key bindings.
    """
    def __init__(self):
        """
        Initializes the class and binds an event handler for the Key
        event, as via pygmi.event.events.bind.

        Takes no arguments.
        """
        self.modes = {}
        self.modelist = []
        self.mode = 'main'
        self.defs = {}
        events.bind(Key=self.dispatch)

    def _add_mode(self, mode):
        if mode not in self.modes:
            self.modes[mode] = {
                'name': mode,
                'desc': {},
                'groups': [],
                'keys': {},
                'import': {},
            }
            self.modelist.append(mode)

    mode = property(lambda self: self._mode,
                   doc="The current mode for which to dispatch keys")
    @mode.setter
    def mode(self, mode):
        self._add_mode(mode)
        self._mode = mode
        self._keys = dict((k % self.defs, v) for k, v in
                          self.modes[mode]['keys'].items() +
                          self.modes[mode]['import'].items());
        if hasattr(self, 'defs'):
            client.write('/keys', '\n'.join(self._keys.keys()) + '\n')


    @prop(doc="Returns a short help text describing the bound keys in all modes")
    def help(self):
        return '\n\n'.join(
            ('Mode %s\n' % mode['name']) +
            '\n\n'.join(('  %s\n' % str(group or '')) +
                        '\n'.join('    %- 20s %s' % (key % self.defs,
                                                     mode['keys'][key].__doc__)
                                  for key in mode['desc'][group])
                        for group in mode['groups'])
            for mode in (self.modes[name]
                         for name in self.modelist))

    def bind(self, mode='main', keys=(), import_={}):
        """
        Binds a series of keys for the given 'mode'. Keys may be
        specified as a dict or as a sequence of tuple values and
        strings.
        
        In the latter case, documentation may be interspersed with
        key bindings. Any value in the sequence which is not a tuple
        begins a new key group, with that value as a description.
        A tuple with two values is considered a key-value pair,
        where the value is the handler for the named key. A
        three valued tuple is considered a key-description-value
        tuple, with the same semantics as above.

        Each key binding is interpolated with the values of
        #defs, as if processed by (key % self.defs)

        Param mode: The name of the mode for which to bind the keys.
        Param keys: A sequence of keys to bind.
        Param import_: A dict specifying keys which should be
                       imported from other modes, of the form 
                         { 'mode': ['key1', 'key2', ...] }
        """
        self._add_mode(mode)
        mode = self.modes[mode]
        group = None
        def add_desc(key, desc):
            if group not in mode['desc']:
                mode['desc'][group] = []
                mode['groups'].append(group)
            if key not in mode['desc'][group]:
                mode['desc'][group].append(key);

        if isinstance(keys, dict):
            keys = keys.iteritems()
        for obj in keys:
            if isinstance(obj, tuple) and len(obj) in (2, 3):
                if len(obj) == 2:
                    key, val = obj
                    desc = ''
                elif len(obj) == 3:
                    key, desc, val = obj
                mode['keys'][key] = val
                add_desc(key, desc)
                val.__doc__ = str(desc)
            else:
                group = obj

        def wrap_import(mode, key):
            return lambda k: self.modes[mode]['keys'][key](k)
        for k, v in flatten((v, k) for k, v in import_.iteritems()):
            mode['import'][k % self.defs] = wrap_import(v, k)

    def dispatch(self, key):
        """
        Dispatches a key event for the current mode.

        Param key: The key spec for which to dispatch.
        """
        mode = self.modes[self.mode]
        if key in self._keys:
            return self._keys[key](key)
keys = Keys()

class Actions(object):
    """
    A class to represent user-callable actions. All methods without
    leading underscores in their names are treated as callable actions.
    """
    def __getattr__(self, name):
        if name.startswith('_') or name.endswith('_'):
            raise AttributeError()
        if hasattr(self, name + '_'):
            return getattr(self, name + '_')
        cmd = pygmi.find_script(name)
        if not cmd:
            raise AttributeError()
        return lambda args='': call(pygmi.shell, '-c', '$* %s' % args, '--', cmd,
                                    background=True)

    def _call(self, args):
        """
        Calls a method named for the first token of 'args', with the
        rest of the string as its first argument. If the method
        doesn't exist, a trailing underscore is appended.
        """
        a = args.split(' ', 1)
        if a:
            getattr(self, a[0])(*a[1:])

    @prop(doc="Returns the names of the public methods callable as actions, with trailing underscores stripped.")
    def _choices(self):
        return sorted(
            program_list(pygmi.confpath) +
            [re.sub('_$', '', k) for k in dir(self)
             if not re.match('^_', k) and callable(getattr(self, k))])


# vim:se sts=4 sw=4 et:
Added alternative_wmiircs/python/pygmi/fs.py.


















































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
import collections
from datetime import datetime, timedelta
import re

from pyxp import *
from pyxp.client import *
from pygmi import *
from pygmi.util import prop

__all__ = ('wmii', 'Tags', 'Tag', 'Area', 'Frame', 'Client',
           'Button', 'Colors', 'Color', 'Toggle', 'Always', 'Never')

spacere = re.compile(r'\s')
sentinel = {}

def tounicode(obj):
    if isinstance(obj, str):
        return obj.decode('UTF-8')
    return unicode(obj)

class utf8(object):
    def __str__(self):
        return unicode(self).encode('utf-8')

@apply
class Toggle(utf8):
    def __unicode__(self):
        return unicode(self.__class__.__name__)
@apply
class Always(Toggle.__class__):
    pass
@apply
class Never(Toggle.__class__):
    pass

def constrain(min, max, val):
    return min if val < min else max if val > max else val

class Map(collections.Mapping):
    def __init__(self, cls, *args):
        self.cls = cls
        self.args = args
    def __repr__(self):
        return 'Map(%s%s)' % (self.cls.__name__, (', %s' % ', '.join(map(repr, self.args)) if self.args else ''))
    def __getitem__(self, item):
        ret = self.cls(*(self.args + (item,)))
        if not ret.exists:
            raise KeyError('no such %s %s' % (self.cls.__name__.lower(), repr(item)))
        return ret
    def __len__(self):
        return len(iter(self))
    def __keys__(self):
        return [v for v in self.cls.all(*self.args)]
    def __iter__(self):
        return (v for v in self.cls.all(*self.args))
    def iteritems(self):
        return ((v, self.cls(*(self.args + (v,)))) for v in self.cls.all(*self.args))
    def itervalues(self):
        return (self.cls(*(self.args + (v,))) for v in self.cls.all(*self.args))

class Ctl(object):
    """
    An abstract class to represent the 'ctl' files of the wmii filesystem.
    Instances act as live, writable dictionaries of the settings represented
    in the file.

    Abstract roperty ctl_path: The path to the file represented by this
            control.
    Property ctl_hasid: When true, the first line of the represented
            file is treated as an id, rather than a key-value pair. In this
            case, the value is available via the 'id' property.
    Property ctl_types: A dict mapping named dictionary keys to two valued
            tuples, each containing a decoder and encoder function for the
            property's plain text value.
    """
    ctl_types = {}
    ctl_hasid = False
    ctl_open = 'aopen'
    ctl_file = None

    def __eq__(self, other):
        if self.ctl_hasid and isinstance(other, Ctl) and other.ctl_hasid:
            return self.id == other.id
        return False

    def __init__(self):
        self.cache = {}

    def ctl(self, *args):
        """
        Arguments are joined by ascii spaces and written to the ctl file.
        """
        def next(file):
            if file:
                self.ctl_file = file
                file.awrite(u' '.join(map(tounicode, args)))
        if self.ctl_file:
            return next(self.ctl_file)
        getattr(client, self.ctl_open)(self.ctl_path, callback=next, mode=OWRITE)

    def __getitem__(self, key):
        for line in self.ctl_lines():
            key_, rest = line.split(' ', 1)
            if key_ == key:
                if key in self.ctl_types:
                    return self.ctl_types[key][0](rest)
                return rest
        raise KeyError()
    def __hasitem__(self, key):
        return key in self.keys()
    def __setitem__(self, key, val):
        assert '\n' not in key
        self.cache[key] = val
        if key in self.ctl_types:
            if self.ctl_types[key][1] is None:
                raise NotImplementedError('%s: %s is not writable' % (self.ctl_path, key))
            val = self.ctl_types[key][1](val)
        self.ctl(key, val)

    def get(self, key, default=sentinel):
        """
        Gets the instance's dictionary value for 'key'. If the key doesn't
        exist, 'default' is returned. If 'default' isn't provided and the key
        doesn't exist, a KeyError is raised.
        """
        try:
            return self[key]
        except KeyError, e:
            if default is not self.sentinel:
                return default
            raise e
    def set(self, key, val):
        """
        Sets the dictionary value for 'key' to 'val', as self[key] = val
        """
        self[key] = val

    def keys(self):
        return [line.split(' ', 1)[0]
                for line in self.ctl_lines()]
    def iteritems(self):
        return (tuple(line.split(' ', 1))
                for line in self.ctl_lines())
    def items(self):
        return [tuple(line.split(' ', 1))
                for line in self.ctl_lines()]

    def ctl_lines(self):
        """
        Returns the lines of the ctl file as a tuple, with the first line
        stripped if #ctl_hasid is set.
        """
        lines = tuple(client.readlines(self.ctl_path))
        if self.ctl_hasid:
            lines = lines[1:]
        return lines

    _id = None
    @prop(doc="If #ctl_hasid is set, returns the id of this ctl file.")
    def id(self):
        if self._id is None and self.ctl_hasid:
            return self.name_read(client.read(self.ctl_path).split('\n', 1)[0])
        return self._id

class Dir(Ctl):
    """
    An abstract class representing a directory in the wmii filesystem with a
    ctl file and sub-objects.

    Abstract property base_path: The path directly under which all objects
            represented by this class reside. e.g., /client, /tag
    """
    ctl_hasid = True
    name_read = unicode
    name_write = unicode

    def __init__(self, id):
        """
        Initializes the directory object.

        Param id: The id of the object in question. If 'sel', the object
                dynamically represents the selected object, even as it
                changes. In this case, #id will return the actual ID of the
                object.
        """
        super(Dir, self).__init__()
        if isinstance(id, Dir):
            id = id.id
        if id != 'sel':
            self._id = self.name_read(id)

    def __eq__(self, other):
        return (self.__class__ == other.__class__ and
                self.id == other.id)

    class ctl_property(object):
        """
        A class which maps instance properties to ctl file properties.
        """
        def __init__(self, key):
            self.key = key
        def __get__(self, dir, cls):
            return dir.get(self.key, None)
        def __set__(self, dir, val):
            dir[self.key] = val

    class toggle_property(ctl_property):
        """
        A class which maps instance properties to ctl file properties. The
        values True and False map to the strings "on" and "off" in the
        filesystem.
        """
        props = {
            'on': True,
            'off': False,
            'toggle': Toggle,
            'always': Always,
            'never': Never
        }
        def __get__(self, dir, cls):
            val = dir[self.key]
            if val in self.props:
                return self.props[val]
            return val
        def __set__(self, dir, val):
            for k, v in self.props.iteritems():
                if v == val:
                    val = k
                    break
            dir[self.key] = val

    class file_property(object):
        """
        A class which maps instance properties to files in the directory
        represented by this object.
        """
        def __init__(self, name, writable=False):
            self.name = name
            self.writable = writable
        def __get__(self, dir, cls):
            return client.read('%s/%s' % (dir.path, self.name))
        def __set__(self, dir, val):
            if not self.writable:
                raise NotImplementedError('File %s is not writable' % self.name)
            return client.awrite('%s/%s' % (dir.path, self.name),
                                 str(val))

    @prop(doc="The path to this directory's ctl file")
    def ctl_path(self):
        return '%s/ctl' % self.path

    @prop(doc="The path to this directory")
    def path(self):
        return '%s/%s' % (self.base_path, self.name_write(self._id or 'sel'))
    @prop(doc="True if the given object exists in the wmii filesystem")
    def exists(self):
        return bool(client.stat(self.path))

    @classmethod
    def all(cls):
        """
        Returns all of the objects that exist for this type of directory.
        """
        return (cls.name_read(s.name)
                for s in client.readdir(cls.base_path)
                if s.name != 'sel')
    @classmethod
    def map(cls, *args):
        return Map(cls, *args)

    def __repr__(self):
        return '%s(%s)' % (self.__class__.__name__,
                           repr(self._id or 'sel'))

class Client(Dir):
    """
    A class which represents wmii clients. Maps to the directories directly
    below /client.
    """
    base_path = '/client'
    ctl_types = {
        'group': (lambda s: int(s, 16), str),
        'pid': (int, None),
    }
    @staticmethod
    def name_read(name):
        if isinstance(name, int):
            return name
        try:
            return int(name, 16)
        except:
            return unicode(name)
    name_write = lambda self, name: name if isinstance(name, basestring) else '%#x' % name

    allow  = Dir.ctl_property('allow')
    fullscreen = Dir.toggle_property('fullscreen')
    group  = Dir.ctl_property('group')
    pid    = Dir.ctl_property('pid')
    tags   = Dir.ctl_property('tags')
    urgent = Dir.toggle_property('urgent')

    label = Dir.file_property('label', writable=True)
    props = Dir.file_property('props')

    def kill(self):
        """Politely asks a client to quit."""
        self.ctl('kill')

    def slay(self):
        """Forcibly severs a client's connection to the X server."""
        self.ctl('slay')

class liveprop(object):
    def __init__(self, get):
        self.get = get
        self.attr = str(self)
    def __get__(self, area, cls):
        if getattr(area, self.attr, sentinel) is not sentinel:
            return getattr(area, self.attr)
        return self.get(area)
    def __set__(self, area, val):
        setattr(area, self.attr, val)

class Area(object):
    def __init__(self, tag, ord, screen='sel', offset=sentinel, width=sentinel, height=sentinel, frames=sentinel):
        self.tag = tag
        if ':' in str(ord):
            screen, ord = ord.split(':', 2)
        self.ord = str(ord)
        self.screen = str(screen)
        self.offset = offset
        self.width = width
        self.height = height
        self.frames = frames

    def prop(key):
        @liveprop
        def prop(self):
            for area in self.tag.index:
                if str(area.ord) == str(self.ord):
                    return getattr(area, key)
        return prop
    offset = prop('offset')
    width = prop('width')
    height = prop('height')
    frames = prop('frames')

    @property
    def spec(self):
        if self.screen is not None:
            return '%s:%s' % (self.screen, self.ord)
        return self.ord

    @property
    def mode(self):
        for k, v in self.tag.iteritems():
            if k == 'colmode':
                v = v.split(' ')
                if v[0] == self.ord:
                    return v[1]
    @mode.setter
    def mode(self, val):
        self.tag['colmode %s' % self.spec] = val

    def grow(self, dir, amount=None):
        self.tag.grow(self, dir, amount)
    def nudge(self, dir, amount=None):
        self.tag.nudge(self, dir, amount)

class Frame(object):
    live = False

    def __init__(self, client, area=sentinel, ord=sentinel, offset=sentinel, height=sentinel):
        self.client = client
        self.ord = ord
        self.offset = offset
        self.height = height

    @property
    def width(self):
        return self.area.width

    def prop(key):
        @liveprop
        def prop(self):
            for area in self.tag.index:
                for frame in area.frames:
                    if frame.client == self.client:
                        return getattr(frame, key)
        return prop
    offset = prop('area')
    offset = prop('ord')
    offset = prop('offset')
    height = prop('height')

    def grow(self, dir, amount=None):
        self.area.tag.grow(self, dir, amount)
    def nudge(self, dir, amount=None):
        self.area.tag.nudge(self, dir, amount)

class Tag(Dir):
    base_path = '/tag'

    @classmethod
    def framespec(cls, frame):
        if isinstance(frame, Frame):
            frame = frame.client
        if isinstance(frame, Area):
            frame = (frame.ord, 'sel')
        if isinstance(frame, Client):
            if frame._id is None:
                return 'sel sel'
            return 'client %s' % frame.id
        elif isinstance(frame, basestring):
            return frame
        else:
            return '%s %s' % tuple(map(str, frame))
    def dirspec(cls, dir):
        if isinstance(dir, tuple):
            dir = ' '.join(dir)
        return dir

    @property
    def selected(self):
        return tuple(self['select'].split(' '))
    @selected.setter
    def selected(self, frame):
        if not isinstance(frame, basestring) or ' ' not in frame:
            frame = self.framespec(frame)
        self['select'] = frame

    @property
    def selclient(self):
        for k, v in self.iteritems():
            if k == 'select' and 'client' in v:
                return Client(v.split(' ')[1])
        return None
    @selclient.setter
    def selclient(self, val):
        self['select'] = self.framespec(val)

    @property
    def selcol(self):
        return Area(self, self.selected[0])

    @property
    def index(self):
        areas = []
        for l in (l.split(' ')
                  for l in client.readlines('%s/index' % self.path)
                  if l):
            if l[0] == '#':
                m = re.match(r'(?:(\d+):)?(\d+|~)', l[1])
                if m.group(2) == '~':
                    area = Area(tag=self, screen=m.group(1), ord=l[1], width=l[2],
                                height=l[3], frames=[])
                else:
                    area = Area(tag=self, screen=m.group(1) or 0,
                                height=None, ord=m.group(2), offset=l[2], width=l[3],
                                frames=[])
                areas.append(area)
                i = 0
            else:
                area.frames.append(
                    Frame(client=Client(l[1]), area=area, ord=i,
                          offset=l[2], height=l[3]))
                i += 1
        return areas

    def delete(self):
        id = self.id
        for a in self.index:
            for f in a.frames:
                if f.client.tags == id:
                    f.client.kill()
                else:
                    f.client.tags = '-%s' % id
        if self == Tag('sel'):
            Tags.instance.select(Tags.instance.next())

    def select(self, frame, stack=False):
        self['select'] = '%s %s' % (
            self.framespec(frame),
            stack and 'stack' or '')

    def send(self, src, dest, stack=False, cmd='send'):
        if isinstance(src, tuple):
            src = ' '.join(src)
        if isinstance(src, Frame):
            src = src.client
        if isinstance(src, Client):
            src = src._id or 'sel'

        if isinstance(dest, tuple):
            dest = ' '.join(dest)

        self[cmd] = '%s %s' % (src, dest)

    def swap(self, src, dest):
        self.send(src, dest, cmd='swap')
    
    def nudge(self, frame, dir, amount=None):
        frame = self.framespec(frame)
        self['nudge'] = '%s %s %s' % (frame, dir, str(amount or ''))
    def grow(self, frame, dir, amount=None):
        frame = self.framespec(frame)
        self['grow'] = '%s %s %s' % (frame, dir, str(amount or ''))

class Color(utf8):
    def __init__(self, colors):
        if isinstance(colors, Color):
            colors = colors.rgb
        elif isinstance(colors, basestring):
            match = (re.match(r'^#(..)(..)(..)((?:..)?)$', colors) or
                     re.match(r'^rgba:(..)/(..)/(..)/(..)$', colors))
            colors = tuple(int(match.group(group), 16) for group in range(1, 4))
            if match.group(4):
                colors += int(match.group(4), 16),
        def toint(val):
            if isinstance(val, float):
                val = int(255 * val)
            assert 0 <= val <= 255
            return val
        self.rgb = tuple(map(toint, colors))

    def __getitem__(self, key):
        if isinstance(key, basestring):
            key = {'red': 0, 'green': 1, 'blue': 2}[key]
        return self.rgb[key]

    @property
    def hex(self):
        if len(self.rgb) > 3:
            return 'rgba:%02x/%02x/%02x/%02x' % self.rgb
        return '#%02x%02x%02x' % self.rgb

    def __unicode__(self):
        if len(self.rgb) > 3:
            return 'rgba(%d, %d, %d, %d)' % self.rgb
        return 'rgb(%d, %d, %d)' % self.rgb
    def __repr__(self):
        return 'Color(%s)' % repr(self.rgb)

class Colors(utf8):
    def __init__(self, foreground=None, background=None, border=None):
        vals = foreground, background, border
        self.vals = tuple(map(Color, vals))

    def __iter__(self):
        return iter(self.vals)
    def __list__(self):
        return list(self.vals)
    def __tuple__(self):
        return self.vals

    @classmethod
    def from_string(cls, val):
        return cls(*val.split(' '))

    def __getitem__(self, key):
        if isinstance(key, basestring):
            key = {'foreground': 0, 'background': 1, 'border': 2}[key]
        return self.vals[key]

    def __unicode__(self):
        return ' '.join(c.hex for c in self.vals)
    def __repr__(self):
        return 'Colors(%s, %s, %s)' % tuple(repr(c.rgb) for c in self.vals)

class Button(Ctl):
    sides = {
        'left': 'lbar',
        'right': 'rbar',
    }
    ctl_types = {
        'colors': (Colors.from_string, lambda c: str(Colors(*c))),
    }
    ctl_open = 'acreate'
    colors = Dir.ctl_property('colors')
    label  = Dir.ctl_property('label')

    def __init__(self, side, name, colors=None, label=None):
        super(Button, self).__init__()
        self.side = side
        self.name = name
        self.base_path = self.sides[side]
        self.ctl_path = '%s/%s' % (self.base_path, self.name)
        self.ctl_file = None
        if colors or label:
            self.create(colors, label)

    def create(self, colors=None, label=None):
        if not self.ctl_file:
            self.ctl_file = client.create(self.ctl_path, ORDWR)
        if colors:
            self.colors = colors
        if label:
            self.label = label

    def remove(self):
        if self.ctl_file:
            self.ctl_file.aremove()
            self.ctl_file = None

    @property
    def exists(self):
        return bool(self.file.stat() if self.file else client.stat(self.ctl_path))

    @classmethod
    def all(cls, side):
        return (s.name
                for s in client.readdir(cls.sides[side])
                if s.name != 'sel')
    @classmethod
    def map(cls, *args):
        return Map(cls, *args)

class Rules(collections.MutableMapping, utf8):

    _items = ()
    def __init__(self, path, rules=None):
        self.path = path
        if rules:
            self.setitems(rules)

    _quotere = re.compile(ur'(\\(.)|/)')
    @classmethod
    def quoteslash(cls, str):
        return cls._quotere.sub(lambda m: m.group(0) if m.group(2) else r'\/', str)

    __get__ = lambda self, obj, cls: self
    def __set__(self, obj, val):
        self.setitems(val)

    def __getitem__(self, key):
        for k, v in self.iteritems():
            if k == key:
                return v
        raise KeyError()
    def __setitem__(self, key, val):
        items = [(k, v) for k, v in self.iteritems() if k != key]
        items.append((key, val))
        self.setitems(items)
    def __delitem__(self, key):
        self.setitems((k, v) for k, v in self.iteritems() if k != key)

    def __len__(self):
        return len(tuple(self.iteritems()))
    def __iter__(self):
        return (k for k, v in self.iteritems())
    def __list__(self):
        return list(iter(self))
    def __tuple__(self):
        return tuple(iter(self))

    def append(self, item):
        self.setitems(self + (item,))
    def __add__(self, items):
        return tuple(self.iteritems()) + tuple(items)

    def rewrite(self):
        client.awrite(self.path, unicode(self))
    def setitems(self, items):
        self._items = [(k, v if isinstance(v, Rule) else Rule(self, k, v))
                       for (k, v) in items]
        self.rewrite()

    def __unicode__(self):
        return u''.join(unicode(value) for (key, value) in self.iteritems()) or u'\n'

    def iteritems(self):
        return iter(self._items)
    def items(self):
        return list(self._items())

class Rule(collections.MutableMapping, utf8):
    _items = ()
    parent = None

    @classmethod
    def quotekey(cls, key):
        if key.endswith('_'):
            key = key[:-1]
        return key.replace('_', '-')
    @classmethod
    def quotevalue(cls, val):
        if val is True:   return "on"
        if val is False:  return "off"
        if val in (Toggle, Always, Never):
            return unicode(val).lower()
        return tounicode(val)

    def __get__(self, obj, cls):
        return self
    def __set__(self, obj, val):
        self.setitems(val)

    def __init__(self, parent, key, items={}):
        self.key = key
        self._items = []
        self.setitems(items.iteritems() if isinstance(items, dict) else items)
        self.parent = parent

    def __getitem__(self, key):
        for k, v in reversed(self._items):
            if k == key:
                return v
        raise KeyError()

    def __setitem__(self, key, val):
        items = [(k, v) for k, v in self.iteritems() if k != key]
        items.append((key, val))
        self.setitems(items)

    def __delitem__(self, key):
        self.setitems([(k, v) for k, v in self.iteritems() if k != key])

    def __len__(self):
        return len(self._items)
    def __iter__(self):
        return iter(self._items)
    def __list__(self):
        return list(iter(self))
    def __tuple__(self):
        return tuple(iter(self))

    def append(self, item):
        self.setitems(self + (item,))
    def __add__(self, items):
        return tuple(self.iteritems()) + tuple(items)

    def setitems(self, items):
        items = list(items)
        assert not any('=' in key or
                       spacere.search(self.quotekey(key)) or
                       spacere.search(self.quotevalue(val)) for (key, val) in items)
        self._items = items
        if self.parent:
            self.parent.rewrite()

    def __unicode__(self):
        return u'/%s/ %s\n' % (
            Rules.quoteslash(self.key),
            u' '.join(u'%s=%s' % (self.quotekey(k), self.quotevalue(v))
                      for (k, v) in self.iteritems()))

    def iteritems(self):
        return iter(self._items)
    def items(self):
        return list(self._items)


@apply
class wmii(Ctl):
    ctl_path = '/ctl'
    ctl_types = {
        'normcolors': (Colors.from_string, lambda c: str(Colors(*c))),
        'focuscolors': (Colors.from_string, lambda c: str(Colors(*c))),
        'border': (int, str),
    }

    clients = Client.map()
    tags = Tag.map()
    lbuttons = Button.map('left')
    rbuttons = Button.map('right')

    rules    = Rules('/rules')

class Tags(object):
    PREV = []
    NEXT = []

    def __init__(self, normcol=None, focuscol=None):
        self.ignore = set()
        self.tags = {}
        self.sel = None
        self.normcol = normcol
        self.focuscol = focuscol
        self.lastselect = datetime.now()
        for t in wmii.tags:
            self.add(t)
        for b in wmii.lbuttons.itervalues():
            if b.name not in self.tags:
                b.remove()
        self.focus(Tag('sel').id)

        self.mru = [self.sel.id]
        self.idx = -1
        Tags.instance = self

    def add(self, tag):
        self.tags[tag] = Tag(tag)
        self.tags[tag].button = Button('left', tag, self.normcol or wmii.cache['normcolors'], tag)
    def delete(self, tag):
        self.tags.pop(tag).button.remove()

    def focus(self, tag):
        self.sel = self.tags[tag]
        self.sel.button.colors = self.focuscol or wmii.cache['focuscolors']
    def unfocus(self, tag):
        self.tags[tag].button.colors = self.normcol or wmii.cache['normcolors']

    def set_urgent(self, tag, urgent=True):
        self.tags[tag].button.label = urgent and '*' + tag or tag

    def next(self, reverse=False):
        tags = [t for t in wmii.tags if t not in self.ignore]
        tags.append(tags[0])
        if reverse:
            tags.reverse()
        for i in range(0, len(tags)):
            if tags[i] == self.sel.id:
                return tags[i+1]
        return self.sel

    def select(self, tag, take_client=None):
        def goto(tag):
            if take_client:
                # Make a new instance in case this is Client('sel'),
                # which would cause problems given 'sel' changes in the
                # process.
                client = Client(take_client.id)

                sel = Tag('sel').id
                client.tags = '+%s' % tag
                wmii['view'] = tag
                if tag != sel:
                    client.tags = '-%s' % sel
            else:
                wmii['view'] = tag

        if tag is self.PREV:
            if self.sel.id not in self.ignore:
                self.idx -= 1
        elif tag is self.NEXT:
            self.idx += 1
        else:
            if isinstance(tag, Tag):
                tag = tag.id
            goto(tag)

            if tag not in self.ignore:
                if self.idx < -1:
                    self.mru = self.mru[:self.idx + 1]
                    self.idx = -1
                if self.mru and datetime.now() - self.lastselect < timedelta(seconds=.5):
                    self.mru[self.idx] = tag
                elif tag != self.mru[-1]:
                    self.mru.append(tag)
                    self.mru = self.mru[-10:]
                self.lastselect = datetime.now()
            return

        self.idx = constrain(-len(self.mru), -1, self.idx)
        goto(self.mru[self.idx])

# vim:se sts=4 sw=4 et:
Added alternative_wmiircs/python/pygmi/menu.py.














































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
from threading import Thread
from pygmi.util import call

__all__ = 'Menu', 'ClickMenu'

def inthread(name, args, action, **kwargs):
    fn = lambda: call(*args, **kwargs)
    if not action:
        return fn()
    t = Thread(target=lambda: action(fn()))
    t.name += '-%s' % name
    t.daemon = True
    t.start()

class Menu(object):
    def __init__(self, choices=(), action=None,
                 histfile=None, nhist=None):
        self.choices = choices
        self.action = action
        self.histfile = histfile
        self.nhist = nhist

    def __call__(self, choices=None):
        if choices is None:
            choices = self.choices
        if callable(choices):
            choices = choices()
        args = ['wimenu']
        if self.histfile:
            args += ['-h', self.histfile]
        if self.nhist:
            args += ['-n', self.nhist]
        return inthread('Menu', map(str, args), self.action, input='\n'.join(choices))
    call = __call__

class ClickMenu(object):
    def __init__(self, choices=(), action=None,
                 histfile=None, nhist=None):
        self.choices = choices
        self.action = action
        self.prev = None

    def __call__(self, choices=None):
        if choices is None:
            choices = self.choices
        if callable(choices):
            choices = choices()
        args = ['wmii9menu']
        if self.prev:
            args += ['-i', self.prev]
        args += ['--'] + list(choices)
        return inthread('ClickMenu', map(str, args), self.action)
    call = __call__

# vim:se sts=4 sw=4 et:
Added alternative_wmiircs/python/pygmi/monitor.py.




























































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
from threading import Lock, Timer

from pygmi import client
from pygmi.fs import *

__all__ = 'monitors', 'defmonitor', 'Monitor'

monitors = {}

def defmonitor(*args, **kwargs):
    """
    Defines a new monitor to appear in wmii's bar based on
    the wrapped function. Creates a new Monitor object,
    initialized with *args and **kwargs. The wrapped function
    is assigned to the 'action' keyword argument for the
    Monitor, its name is assigned to the 'name' argument.

    The new monitor is added to the 'monitors' dict in this
    module.
    """
    def monitor(fn):
        kwargs['action'] = fn
        if not args and 'name' not in kwargs:
            kwargs['name'] = fn.__name__
        monitor = Monitor(*args, **kwargs)
        monitors[monitor.name] = monitor
        return monitor
    if args and callable(args[0]):
        fn = args[0]
        args = args[1:]
        return monitor(fn)
    return monitor

class Monitor(object):
    """
    A class to manage status monitors for wmii's bar. The bar item
    is updated on a fixed interval based on the values returned
    by the 'action' method.

    Property active: When true, the monitor is updated at regular
             intervals. When false, monitor is hidden.
    Property name: The name of the monitor, which acts as the name
           of the bar in wmii's filesystem.
    Property interval: The update interval, in seconds.
    Property side: The side of the bar on which to place the monitor.
    Property action: A function of no arguments which returns the
            value of the monitor. Called at each update interval.
            May return a string, a tuple of (Color, string), or None
            to hide the monitor for one iteration.
    """
    side = 'right'
    interval = 1.0

    define = classmethod(defmonitor)

    def __init__(self, name=None, interval=None, side=None,
                 action=None, colors=None, label=None):
        """
        Initializes the new monitor. For parameter values, see the
        corresponding property values in the class's docstring.

        Param colors: The initial colors for the monitor.
        Param label:  The initial label for the monitor.
        """
        if side:
            self.side = side
        if name:
            self.name = name
        if interval:
            self.interval = interval
        if action:
            self.action = action

        self.lock = Lock()
        self.timer = None
        self.button = Button(self.side, self.name, colors, label)
        self.tick()

    def tick(self):
        """
        Called internally at the interval defined by #interval.
        Calls #action and updates the monitor based on the result.
        """
        if self.timer and monitors.get(self.name, None) is not self:
            return
        if self.active:
            label = self.getlabel()
            if isinstance(label, basestring):
                label = None, label
            with self.lock:
                if self.active:
                    if label is None:
                        self.button.remove()
                    else:
                        self.button.create(*label)

                    self.timer = Timer(self.interval, self.tick)
                    self.timer.name = 'Monitor-Timer-%s' % self.name
                    self.timer.daemon = True
                    self.timer.start()

    def getlabel(self):
        """
        Calls #action and returns the result, ignoring any
        exceptions.
        """
        try:
            return self.action(self)
        except Exception:
            return None

    _active = True
    @property
    def active(self):
        return self._active

    @active.setter
    def active(self, val):
        with self.lock:
            self._active = bool(val)
        if val:
            self.tick()
        else:
            self.button.remove()

# vim:se sts=4 sw=4 et:
Added alternative_wmiircs/python/pygmi/util.py.












































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
from functools import partial, update_wrapper, wraps
import os
import signal
import subprocess

import pygmi

__all__ = 'call', 'message', 'program_list', 'curry', 'find_script', '_', 'prop'

def _():
    pass

def call(*args, **kwargs):
    background = kwargs.pop('background', False)
    stdin = subprocess.PIPE if not background else open('/dev/null', 'r')
    pipe  = subprocess.PIPE if not background else None
    input = kwargs.pop('input', None)
    p = subprocess.Popen(args, stdin=stdin, stdout=pipe, stderr=pipe,
                         preexec_fn=lambda: signal.signal(signal.SIGPIPE, signal.SIG_DFL),
                         cwd=os.environ['HOME'], close_fds=True, **kwargs)
    if not background:
        return p.communicate(input)[0].rstrip('\n')

def message(message):
    args = ['xmessage', '-file', '-'];
    font = pygmi.wmii['font']
    if not font.startswith('xft:'):
        args += ['-fn', font.split(',')[0]]
    call(*args, input=message)

def program_list(path):
    names = set()
    for d in path:
        try:
            for f in os.listdir(d):
                p = '%s/%s' % (d, f)
                if (f not in names and os.access(p, os.X_OK) and
                    os.path.isfile(p)):
                    names.add(f)
        except Exception:
            pass
    return sorted(names)

def curry(func, *args, **kwargs):
    if _ in args:
        blank = [i for i in range(0, len(args)) if args[i] is _]
        @wraps(func)
        def curried(*newargs, **newkwargs):
            ary = list(args)
            for k, v in zip(blank, newargs):
                ary[k] = v
            ary = tuple(ary) + newargs[len(blank):]
            return func(*ary, **dict(kwargs, **newkwargs))
    else:
        curried = update_wrapper(partial(func, *args, **kwargs), func)
    curried.__name__ += '__curried__'
    return curried

def find_script(name):
    for path in pygmi.confpath:
        if os.access('%s/%s' % (path, name), os.X_OK):
            return '%s/%s' % (path, name)

def prop(**kwargs):
    def prop_(wrapped):
        kwargs['fget'] = wrapped
        return property(**kwargs)
    return prop_

# vim:se sts=4 sw=4 et:
Added alternative_wmiircs/python/pyxp.py.




























>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/usr/bin/env python

from distutils.core import setup

setup(name='pyxp',
      version='0.2',
      description='Python 9P client library',
      author='Kris Maglione',
      author_email='maglione.k@gmail.com',
      url='http://wmii.suckless.org',
      packages=['pyxp'],
      license='MIT',
     )

Added alternative_wmiircs/python/pyxp/__init__.py.














>
>
>
>
>
>
>
1
2
3
4
5
6
7
from pyxp.client import Client
from pyxp.dial import dial
from pyxp.types import Qid, Stat

VERSION = '9P2000'

# vim:se sts=4 sw=4 et:
Added alternative_wmiircs/python/pyxp/asyncclient.py.






































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
from pyxp import client, fcall
from pyxp.client import *
from functools import wraps

def send(iter, val, default=None):
    try:
        return iter.send(val)
    except StopIteration:
        return default

def awithfile(fn):
    @wraps(fn)
    def wrapper(self, path, *args, **kwargs):
        gen = fn(self, *args, **kwargs)
        callback, fail, mode = next(gen)
        def cont(file):
            send(gen, file)
        self.aopen(path, cont, fail=fail or callback, mode=mode)
    return wrapper

def requestchain(fn):
    @wraps(fn)
    def wrapper(self, *args, **kwargs):
        gen = fn(self, *args, **kwargs)
        callback, fail = next(gen)

        def cont(val):
            data = gen.send(val)
            if isinstance(data, fcall.Fcall):
                self._dorpc(data, cont, fail or callback)
            else:
                Client.respond(callback, data)
        cont(None)
    return wrapper

class Client(client.Client):
    ROOT_FID = 0

    def _awalk(fn):
        @wraps(fn)
        @requestchain
        def wrapper(self, *args, **kwargs):
            gen = fn(self, *args, **kwargs)
            path, callback, fail = next(gen)

            path = self._splitpath(path)
            fid  = self._getfid()
            ofid = ROOT_FID

            def fail_(resp, exc, tb):
                if ofid != ROOT_FID:
                    self._aclunk(fid)
                self.respond(fail or callback, resp, exc, tb)
            yield callback, fail_

            while path:
                wname = path[:fcall.MAX_WELEM]
                path = path[fcall.MAX_WELEM:]

                resp = yield fcall.Twalk(fid=ofid, newfid=fid, wname=wname)
                ofid = fid

            resp = fid
            while resp is not None:
                resp = yield send(gen, resp)

        return wrapper

    _file = property(lambda self: File)

    @_awalk
    def _aopen(self, path, mode, fcall, callback, fail=None, origpath=None):
        path = self._splitpath(path)

        fcall.fid = yield path, callback, fail
        resp = yield fcall
        yield self._file(self, origpath or '/'.join(path), resp, fcall.fid, mode,
                         cleanup=lambda: self._aclunk(fcall.fid))

    def aopen(self, path, callback=True, fail=None, mode=OREAD):
        assert callable(callback)
        self._aopen(path, mode, fcall.Topen(mode=mode), callback, fail)

    def acreate(self, path, callback=True, fail=None, mode=OREAD, perm=0):
        path = self._splitpath(path)
        name = path.pop()

        self._aopen(path, mode,
                    fcall.Tcreate(mode=mode, name=name, perm=perm),
                    callback if callable(callback) else lambda resp: resp and resp.close(),
                    fail, origpath='/'.join(path + [name]))

    @_awalk
    def aremove(self, path, callback=True, fail=None):
        yield fcall.Tremove(fid=(yield path, callback, fail))

    @_awalk
    def astat(self, path, callback, fail=None):
        resp = yield fcall.Tstat(fid=(yield path, callback, fail))
        yield resp.stat

    @awithfile
    def aread(self, callback, fail=None, count=None, offset=None, buf=''):
        file = yield callback, fail, OREAD
        file.aread(callback, fail, count, offset, buf)

    @awithfile
    def awrite(self, data, callback=True, fail=None, offset=None):
        file = yield callback, fail, OWRITE
        file.awrite(data, callback, fail, offset)

    @awithfile
    def areadlines(self, callback):
        file = yield callback, fail, OREAD
        file.areadlines(callback)

class File(client.File):

    @requestchain
    def stat(self, callback, fail=None):
        yield callback, fail
        resp = yield fcall.Tstat()
        yield resp.stat

    @requestchain
    def aread(self, callback, fail=None, count=None, offset=None, buf=''):
        yield callback, fail

        setoffset = offset is None
        if count is None:
            count = self.iounit
        if offset is  None:
            offset = self.offset

        res = []
        while count > 0:
            n = min(count, self.iounit)
            count -= n
            resp = yield fcall.Tread(offset=offset, count=n)
            res.append(resp.data)
            offset += len(resp.data)
            if len(resp.data) == 0:
                break

        if setoffset:
            self.offset = offset
        yield ''.join(res)

    def areadlines(self, callback):
        class ctxt:
            last = None
        def cont(data, exc, tb):
            res = True
            if data:
                lines = data.split('\n')
                if ctxt.last:
                    lines[0] = ctxt.last + lines[0]
                for i in range(0, len(lines) - 1):
                    res = callback(lines[i])
                    if res is False:
                        return
                ctxt.last = lines[-1]
                self.aread(cont)
            else:
                if ctxt.last:
                    callback(ctxt.last)
                callback(None)
        self.aread(cont)

    @requestchain
    def awrite(self, data, callback=True, fail=None, offset=None):
        yield callback, fail
        setoffset = offset is None
        if offset is None:
            offset = self.offset

        off = 0
        while off < len(data):
            n = min(len(data), self.iounit)
            resp = yield fcall.Twrite(offset=offset, data=data[off:off+n])
            off    += resp.count
            offset += resp.count

        if setoffset:
            self.offset = offset
        yield off

    @requestchain
    def aremove(self, callback=True, fail=None):
        yield callback, fail
        yield fcall.Tremove()
        self.close()
        yield True

# vim:se sts=4 sw=4 et:
Added alternative_wmiircs/python/pyxp/client.py.
























































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
# Copyright (C) 2009 Kris Maglione

import operator
import os
import re
import sys
from threading import *
import traceback

import pyxp
from pyxp import fcall, fields
from pyxp.mux import Mux
from pyxp.types import *

if os.environ.get('NAMESPACE', None):
    namespace = os.environ['NAMESPACE']
else:
    try:
        namespace = '/tmp/ns.%s.%s' % (
            os.environ['USER'], 
            re.sub(r'\.0$', '', os.environ['DISPLAY']))
    except Exception:
        pass
NAMESPACE = namespace

OREAD = 0x00
OWRITE = 0x01
ORDWR = 0x02
OEXEC = 0x03
OEXCL = 0x04
OTRUNC = 0x10
OREXEC = 0x20
ORCLOSE = 0x40
OAPPEND = 0x80

ROOT_FID = 0

class ProtocolException(Exception):
    pass
class RPCError(Exception):
    pass

class Client(object):
    ROOT_FID = 0

    @staticmethod
    def respond(callback, data, exc=None, tb=None):
        if hasattr(callback, 'func_code'):
            callback(*(data, exc, tb)[0:callback.func_code.co_argcount])
        elif callable(callback):
            callback(data)

    def __enter__(self):
        return self
    def __exit__(self, *args):
        self._cleanup()

    def __init__(self, conn=None, namespace=None, root=None):
        if not conn and namespace:
            conn = 'unix!%s/%s' % (NAMESPACE, namespace)
        try:
            self.lastfid = ROOT_FID
            self.fids = set()
            self.lock = RLock()

            def process(data):
                return fcall.Fcall.unmarshall(data)[1]
            self.mux = Mux(conn, process, maxtag=256)

            resp = self._dorpc(fcall.Tversion(version=pyxp.VERSION, msize=65535))
            if resp.version != pyxp.VERSION:
                raise ProtocolException, "Can't speak 9P version '%s'" % resp.version
            self.msize = resp.msize

            self._dorpc(fcall.Tattach(fid=ROOT_FID, afid=fcall.NO_FID,
                       uname=os.environ['USER'], aname=''))

            if root:
                path = self._splitpath(root)
                resp = self._dorpc(fcall.Twalk(fid=ROOT_FID,
                                               newfid=ROOT_FID,
                                               wname=path))
        except Exception:
            traceback.print_exc(sys.stdout)
            if getattr(self, 'mux', None):
                self.mux.fd.close()
            raise

    def _cleanup(self):
        try:
            for f in self.files:
                f.close()
        finally:
            self.mux.fd.close()
            self.mux = None

    def _dorpc(self, req, callback=None, error=None):
        def doresp(resp):
            if isinstance(resp, fcall.Rerror):
                raise RPCError, "%s[%d] RPC returned error: %s" % (
                    req.__class__.__name__, resp.tag, resp.ename)
            if req.type != resp.type ^ 1:
                raise ProtocolException, "Missmatched RPC message types: %s => %s" % (
                    req.__class__.__name__, resp.__class__.__name__)
            return resp

        def next(mux, resp):
            try:
                res = doresp(resp)
            except Exception, e:
                self.respond(error or callback, None, e, None)
            else:
                self.respond(callback, res)

        if not callback:
            return doresp(self.mux.rpc(req))
        self.mux.rpc(req, next)

    def _splitpath(self, path):
        if isinstance(path, list):
            return path
        return [v for v in path.split('/') if v != '']

    def _getfid(self):
        with self.lock:
            if self.fids:
                return self.fids.pop()
            self.lastfid += 1
            return self.lastfid
    def _putfid(self, fid):
        with self.lock:
            self.fids.add(fid)

    def _aclunk(self, fid, callback=None):
        def next(resp, exc, tb):
            if resp:
                self._putfid(fid)
            self.respond(callback, resp, exc, tb)
        self._dorpc(fcall.Tclunk(fid=fid), next)

    def _clunk(self, fid):
        try:
            self._dorpc(fcall.Tclunk(fid=fid))
        finally:
            self._putfid(fid)

    def _walk(self, path):
        fid = self._getfid()
        ofid = ROOT_FID
        while True:
            self._dorpc(fcall.Twalk(fid=ofid, newfid=fid,
                                   wname=path[0:fcall.MAX_WELEM]))
            path = path[fcall.MAX_WELEM:]
            ofid = fid
            if len(path) == 0:
                break

        @apply
        class Res:
            def __enter__(res):
                return fid
            def __exit__(res, exc_type, exc_value, traceback):
                if exc_type:
                    self._clunk(fid)
        return Res

    _file = property(lambda self: File)
    def _open(self, path, mode, fcall, origpath=None):
        resp = None

        with self._walk(path) as nfid:
            fid = nfid
            fcall.fid = fid
            resp = self._dorpc(fcall)

        def cleanup():
            self._aclunk(fid)
        file = self._file(self, origpath or '/'.join(path), resp, fid, mode, cleanup)
        return file

    def open(self, path, mode=OREAD):
        path = self._splitpath(path)

        return self._open(path, mode, fcall.Topen(mode=mode))

    def create(self, path, mode=OREAD, perm=0):
        path = self._splitpath(path)
        name = path.pop()

        return self._open(path, mode, fcall.Tcreate(mode=mode, name=name, perm=perm),
                          origpath='/'.join(path + [name]))

    def remove(self, path):
        path = self._splitpath(path)

        with self._walk(path) as fid:
            self._dorpc(fcall.Tremove(fid=fid))

    def stat(self, path):
        path = self._splitpath(path)

        try:
            with self._walk(path) as fid:
                resp = self._dorpc(fcall.Tstat(fid= fid))
                st = resp.stat
                self._clunk(fid)
            return st
        except RPCError:
            return None

    def read(self, path, *args, **kwargs):
        with self.open(path) as f:
            return f.read(*args, **kwargs)
    def readlines(self, path, *args, **kwargs):
        with self.open(path) as f:
            for l in f.readlines(*args, **kwargs):
                yield l
    def readdir(self, path, *args, **kwargs):
        with self.open(path) as f:
            for s in f.readdir(*args, **kwargs):
                yield s
    def write(self, path, *args, **kwargs):
        with self.open(path, OWRITE) as f:
            return f.write(*args, **kwargs)

class File(object):

    def __enter__(self):
        return self
    def __exit__(self, *args):
        self.close()

    def __init__(self, client, path, fcall, fid, mode, cleanup):
        self.lock = RLock()
        self.client = client
        self.path = path
        self.fid = fid
        self._cleanup = cleanup
        self.mode = mode
        self.iounit = fcall.iounit
        self.qid = fcall.qid
        self.closed = False

        self.offset = 0
    def __del__(self):
        if not self.closed:
            self._cleanup()

    def _dorpc(self, fcall, async=None, error=None):
        if hasattr(fcall, 'fid'):
            fcall.fid = self.fid
        return self.client._dorpc(fcall, async, error)

    def stat(self):
        resp = self._dorpc(fcall.Tstat())
        return resp.stat

    def read(self, count=None, offset=None, buf=''):
        if count is None:
            count = self.iounit
        res = []
        with self.lock:
            offs = self.offset
            if offset is not None:
                offs = offset
            while count > 0:
                n = min(count, self.iounit)
                count -= n

                resp = self._dorpc(fcall.Tread(offset=offs, count=n))
                data = resp.data

                offs += len(data)
                res.append(data)

                if len(data) < n:
                    break
            if offset is None:
                self.offset = offs
        return ''.join(res)

    def readlines(self):
        last = None
        while True:
            data = self.read()
            if not data:
                break
            lines = data.split('\n')
            if last:
                lines[0] = last + lines[0]
                last = None
            for i in range(0, len(lines) - 1):
                yield lines[i]
            last = lines[-1]
        if last:
            yield last

    def write(self, data, offset=None):
        if offset is None:
            offset = self.offset
        off = 0
        with self.lock:
            offs = self.offset
            if offset is not None:
                offs = offset
            while off < len(data):
                n = min(len(data), self.iounit)

                resp = self._dorpc(fcall.Twrite(offset=offs,
                                               data=data[off:off+n]))
                off += resp.count
                offs += resp.count
                if resp.count < n:
                    break
            if offset is None:
                self.offset = offs
        return off
    def readdir(self):
        if not self.qid.type & Qid.QTDIR:
            raise Exception, "Can only call readdir on a directory"
        off = 0
        while True:
            data = self.read(self.iounit, off)
            if not data:
                break
            off += len(data)
            for s in Stat.unmarshall_list(data):
                yield s

    def close(self):
        assert not self.closed
        self.closed = True
        try:
            self._cleanup()
        except:
            pass
        self.tg = None
        self.fid = None
        self.client = None
        self.qid = None

    def remove(self):
        try:
            self._dorpc(fcall.Tremove())
        finally:
            self.close()

# vim:se sts=4 sw=4 et:
Added alternative_wmiircs/python/pyxp/dial.py.






































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
from socket import *

__all__ = 'dial',

def dial_unix(address):
    sock = socket(AF_UNIX, SOCK_STREAM, 0)
    sock.connect(address)
    return sock

def dial_tcp(host):
    host = host.split('!')
    if len(host) != 2:
        return
    host, port = host

    res = getaddrinfo(host, port, AF_INET, SOCK_STREAM, 0, AI_PASSIVE)
    for family, socktype, protocol, name, addr in res:
        try:
            sock = socket(family, socktype, protocol)
            sock.connect(addr)
            return sock
        except error:
            if sock:
                sock.close()

def dial(address):
    proto, address = address.split('!', 1)
    if proto == 'unix':
        return dial_unix(address)
    elif proto == 'tcp':
        return dial_tcp(address)
    else:
        raise Exception('invalid protocol')

# vim:se sts=4 sw=4 et:
Added alternative_wmiircs/python/pyxp/fcall.py.






































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
from pyxp.messages import MessageBase, Message
from pyxp.fields import *
from types import Qid, Stat

__all__ = 'Fcall',

NO_FID = 1<<32 - 1
MAX_WELEM = 16

class FcallBase(MessageBase):
    idx = 99
    def __new__(cls, name, bases, attrs):
        new_cls = super(FcallBase, cls).__new__(cls, name, bases, attrs)
        new_cls.type = FcallBase.idx
        if new_cls.type > 99:
            new_cls.types[new_cls.type] = new_cls
        FcallBase.idx += 1
        return new_cls

class Fcall(Message):
    __metaclass__ = FcallBase
    types = {}

    def response(self, *args, **kwargs):
        assert self.type % 2 == 0, "No respense type for response fcalls"
        kwargs['tag'] = self.tag
        return self.types[self.type + 1]()

    @classmethod
    def unmarshall(cls, data, offset=0):
        res = super(Fcall, cls).unmarshall(data, offset)
        if cls.type < 100:
            res = cls.types[res[1].type].unmarshall(data, offset)
        return res

    size = Size(4, 4)
    type = Int(1)
    tag  = Int(2)

class Tversion(Fcall):
    msize   = Int(4)
    version = String()
class Rversion(Fcall):
    msize   = Int(4)
    version = String()

class Tauth(Fcall):
    afid  = Int(4)
    uname = String()
    aname = String()
class Rauth(Fcall):
    aqid  = Qid.field()

class Tattach(Fcall):
    fid   = Int(4)
    afid  = Int(4)
    uname = String()
    aname = String()
class Rattach(Fcall):
    qid   = Qid.field()

class Terror(Fcall):
    def __init__(self):
        raise Exception("Illegal 9P tag 'Terror' encountered")
class Rerror(Fcall):
    ename = String()

class Tflush(Fcall):
    oldtag = Int(2)
class Rflush(Fcall):
    pass

class Twalk(Fcall):
    fid    = Int(4)
    newfid = Int(4)
    wname  = Array(2, String())
class Rwalk(Fcall):
    wqid   = Array(2, Qid.field())

class Topen(Fcall):
    fid    = Int(4)
    mode   = Int(1)
class Ropen(Fcall):
    qid    = Qid.field()
    iounit = Int(4)

class Tcreate(Fcall):
    fid    = Int(4)
    name   = String()
    perm   = Int(4)
    mode   = Int(1)
class Rcreate(Fcall):
    qid    = Qid.field()
    iounit = Int(4)

class Tread(Fcall):
    fid    = Int(4)
    offset = Int(8)
    count  = Int(4)
class Rread(Fcall):
    data   = Data(4)

class Twrite(Fcall):
    fid    = Int(4)
    offset = Int(8)
    data   = Data(4)
class Rwrite(Fcall):
    count  = Int(4)

class Tclunk(Fcall):
    fid    = Int(4)
class Rclunk(Fcall):
    pass

class Tremove(Tclunk):
    pass
class Rremove(Fcall):
    pass

class Tstat(Tclunk):
    pass
class Rstat(Fcall):
    sstat = Size(2)
    stat  = Stat.field()

class Twstat(Rstat):
    pass
class Rwstat(Fcall):
    pass

# vim:se sts=4 sw=4 et:
Added alternative_wmiircs/python/pyxp/fields.py.












































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
from datetime import datetime
import operator

class Field(object):
    idx = 0

    def __init__(self):
        Field.idx += 1
        self.id = Field.idx

    def repr(self):
        return self.__class__.__name__

    def __repr__(self):
        if hasattr(self, 'name'):
            return '<Field %s "%s">' % (self.repr(), self.name)
        return super(Field, self).__repr__()

class Int(Field):
    encoders = {}
    decoders = {}
    @classmethod
    def encoder(cls, n):
        if n not in cls.encoders:
            exec ('def enc(n):\n' +
                  '    assert n == n & 0x%s, "Arithmetic overflow"\n' +
                  '    return "".join((%s,))'
                 ) % ('ff' * n,
                      ','.join('chr((n >> %d) & 0xff)' % (i * 8)
                               for i in range(0, n)))

            cls.encoders[n] = enc
        return cls.encoders[n]
    @classmethod
    def decoder(cls, n):
        if n not in cls.decoders:
            cls.decoders[n] = eval('lambda data, offset: ' +
                                   '|'.join('ord(data[offset + %d]) << %d' % (i, i * 8)
                                            for i in range(0, n)))
        return cls.decoders[n]

    def __init__(self, size):
        super(Int, self).__init__()
        self.size = size
        self.encode = self.encoder(size)
        self.decode = self.decoder(size)
        if self.__class__ == Int:
            self.marshall = self.encode

    def unmarshall(self, data, offset):
        return self.size, self.decode(data, offset)
    def marshall(self, val):
        return self.encode(val)

    def repr(self):
        return '%s(%d)' % (self.__class__.__name__, self.size)

class Size(Int):
    def __init__(self, size, extra=0):
        super(Size, self).__init__(size)
        self.extra = extra

    def marshall(self, val):
        return lambda vals, i: self.encode(
            reduce(lambda n, i: n + len(vals[i]),
                   range(i + 1, len(vals)),
                   self.extra))

class Date(Int):
    def __init__(self):
        super(Date, self).__init__(4)

    def unmarshall(self, data, offset):
        val = self.decode(data, offset)
        return 4, datetime.fromtimestamp(val)
    def marshall(self, val):
        return self.encode(int(val.strftime('%s')))

class Data(Int):
    def __init__(self, size=2):
        super(Data, self).__init__(size)
    def unmarshall(self, data, offset):
        n = self.decode(data, offset)
        offset += self.size
        assert offset + n <= len(data), "String too long to unpack"
        return self.size + n, data[offset:offset + n]
    def marshall(self, val):
        if isinstance(val, unicode):
            val = val.encode('UTF-8')
        return [self.encode(len(val)), val]

# Note: Py3K strings are Unicode by default. They can't store binary
#       data.
class String(Data):
    def unmarshall(self, data, offset):
        off, val = super(String, self).unmarshall(data, offset)
        return off, val.decode('UTF-8')
    def marshall(self, val):
        if isinstance(val, str):
            # Check for valid UTF-8
            str.decode('UTF-8')
        else:
            val = val.encode('UTF-8')
        return super(String, self).marshall(val)

class Array(Int):
    def __init__(self, size, spec):
        super(Array, self).__init__(size)
        self.spec = spec

    def unmarshall(self, data, offset):
        start = offset
        n = self.decode(data, offset)
        offset += self.size
        res = []
        for i in range(0, n):
            size, val = self.spec.unmarshall(data, offset)
            if isinstance(val, list):
                res += val
            else:
                res.append(val)
            offset += size
        return offset - start, res
    def marshall(self, vals):
        res = [self.encode(len(vals))]
        for val in vals:
            val = self.spec.marshall(val)
            if isinstance(val, list):
                res += val
            else:
                res.append(val)
        return res

# vim:se sts=4 sw=4 et:
Added alternative_wmiircs/python/pyxp/messages.py.
















































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
from pyxp.fields import *

class MessageBase(type):
    idx = 0

    def __new__(cls, name, bases, attrs):
        fields = []
        fieldmap = {}
        for k, v in attrs.items():
            if isinstance(v, Field):
                attrs[k] = None
                fields.append(v)
                fieldmap[k] = v
                v.name = k
        fields.sort(lambda a, b: cmp(a.id, b.id))

        new_cls = super(MessageBase, cls).__new__(cls, name, bases, attrs)

        map = getattr(new_cls, 'fieldmap', {})
        map.update(fieldmap)
        new_cls.fields = getattr(new_cls, 'fields', ()) + tuple(fields)
        new_cls.fieldmap = map
        for f in fields:
            f.message = new_cls
        return new_cls

class Message(object):
    __metaclass__ = MessageBase
    def __init__(self, *args, **kwargs):
        if args:
            args = dict(zip((f.name for f in self.fields), args))
            args.update(kwargs)
            kwargs = args;
        for k, v in kwargs.iteritems():
            assert k in self.fieldmap, "Invalid keyword argument"
            setattr(self, k, v)

    @classmethod
    def field(cls):
        class MessageField(Field):
            def repr(self):
                return cls.__name__
            def unmarshall(self, data, offset):
                return cls.unmarshall(data, offset)
            def marshall(self, val):
                return val.marshall()
        return MessageField()

    @classmethod
    def unmarshall(cls, data, offset=0):
        vals = {}
        start = offset
        for field in cls.fields:
            size, vals[field.name] = field.unmarshall(data, offset)
            offset += size
        return offset - start, cls(**vals)
    def marshall(self):
        res = []
        callbacks = []
        for field in self.fields:
            val = field.marshall(getattr(self, field.name, None))
            if callable(val):
                callbacks.append((val, len(res)))
            if isinstance(val, list):
                res += val
            else:
                res.append(val)
        for fn, i in reversed(callbacks):
            res[i] = fn(res, i)
        return res

# vim:se sts=4 sw=4 et:
Added alternative_wmiircs/python/pyxp/mux.py.




















































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
# Derived from libmux, available in Plan 9 under /sys/src/libmux
# under the following terms:
#
# Copyright (C) 2003-2006 Russ Cox, Massachusetts Institute of Technology
# 
# 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.

import os
import sys
import traceback

from pyxp import fields
from pyxp.dial import dial
from threading import *
Condition = Condition().__class__

__all__ = 'Mux',

class Mux(object):
    def __init__(self, con, process, flush=None, mintag=0, maxtag=1<<16 - 1):
        self.lock = RLock()
        self.tagcond = Condition(self.lock)
        self.outlock = RLock()
        self.inlock = RLock()
        self.process = process
        self.flush = flush
        self.wait = {}
        self.free = set(range(mintag, maxtag))
        self.mintag = mintag
        self.maxtag = maxtag
        self.muxer = None

        self.async_mux = Queue(self.mux)
        self.async_dispatch = Queue(self.async_dispatch)

        if isinstance(con, basestring):
            con = dial(con)
        self.fd = con

        if self.fd is None:
            raise Exception("No connection")

    def mux(self, rpc):
        with self.lock:
            try:
                rpc.waiting = True
                while self.muxer and self.muxer != rpc and rpc.data is None:
                    rpc.wait()

                if rpc.data is None:
                    assert self.muxer in (rpc, None)
                    self.muxer = rpc
                    try:
                        self.lock.release()
                        while rpc.data is None:
                            data = self.recv()
                            if data is None:
                                raise Exception("unexpected eof")
                            self.dispatch(data)
                    finally:
                        self.lock.acquire()
                        self.electmuxer()
            except Exception:
                traceback.print_exc(sys.stderr)
                if rpc.tag in self.wait:
                    self.wait.pop(rpc.tag)
                if self.flush:
                    self.flush(self, rpc.data)
                raise

        return rpc.data

    def rpc(self, dat, async=None):
        rpc = self.newrpc(dat, async)
        if async:
            self.async_mux.push(rpc)
        else:
            return self.mux(rpc)

    def async_dispatch(self, rpc):
        rpc.async(self, rpc.data)

    def electmuxer(self):
        for rpc in self.wait.itervalues():
            if self.muxer != rpc and rpc.waiting:
                self.muxer = rpc
                rpc.notify()
                return
        self.muxer = None

    def dispatch(self, dat):
        with self.lock:
            rpc = self.wait.get(dat.tag, None)
            if rpc:
                self.puttag(rpc)
                rpc.dispatch(dat)
            elif False:
                print "bad rpc tag: %u (no one waiting on it)" % dat.tag

    def gettag(self, r):
        tag = 0

        while not self.free:
            self.tagcond.wait()

        tag = self.free.pop()

        if tag in self.wait:
            raise Exception("nwait botch")

        self.wait[tag] = r

        r.tag = tag
        r.orig.tag = r.tag
        return r.tag

    def puttag(self, rpc):
        if rpc.tag in self.wait:
            del self.wait[rpc.tag]
        self.free.add(rpc.tag)
        self.tagcond.notify()

    def send(self, dat):
        data = ''.join(dat.marshall())
        n = self.fd.send(data)
        return n == len(data)
    def recv(self):
        def readn(fd, n):
            data = ''
            while len(data) < n:
                try:
                    s = fd.recv(n - len(data))
                    if len(s) == 0:
                        raise Exception('unexpected end of file')
                    data += s
                except os.error, e:
                    if e.errno != os.errno.EINTR:
                        raise e
            return data

        try:
            with self.inlock:
                data = readn(self.fd, 4)
                if data:
                    nmsg = fields.Int.decoders[4](data, 0)
                    data += readn(self.fd, nmsg - 4)
                    return self.process(data)
        except Exception, e:
            print e.__class__.__name__
            print repr(e)
            traceback.print_exc(sys.stderr)
            return None

    def newrpc(self, dat, async=None):
        rpc = Rpc(self, dat, async)
        tag = None

        with self.lock:
            self.gettag(rpc)

        if rpc.tag >= 0 and self.send(dat):
            return rpc

        with self.lock:
            self.puttag(rpc)

class Rpc(Condition):
    def __init__(self, mux, data, async=None):
        super(Rpc, self).__init__(mux.lock)
        self.mux = mux
        self.orig = data
        self.data = None
        self.async = async
        self.waiting = False

    def __repr__(self):
        return '<Rpc tag=%s orig=%s data=%s async=%s waiting=%s>' % tuple(map(repr, (self.tag, self.orig, self.data, self.async, self.waiting)))

    def dispatch(self, data=None):
        self.data = data
        self.notify()
        if callable(self.async):
            self.mux.async_dispatch(self)

class Queue(Thread):
    _id = 1

    def __init__(self, op):
        super(Queue, self).__init__(name='Queue-%d-%s' % (Queue._id, repr(op)))
        Queue._id += 1
        self.cond = Condition()
        self.op = op
        self.queue = []
        self.daemon = True

    def __call__(self, item):
        return self.push(item)

    def push(self, item):
        with self.cond:
            self.queue.append(item)
            if not self.is_alive():
                self.start()
            self.cond.notify()
    def pop(self, item):
        with self.cond:
            if item in self.queue:
                self.queue.remove(item)
                return True
            return False

    def run(self):
        self.cond.acquire()
        while True:
            while self.queue:
                item = self.queue.pop(0)
                self.cond.release()
                try:
                    self.op(item)
                except Exception, e:
                    traceback.print_exc(sys.stderr)
                self.cond.acquire()
            self.cond.wait()

# vim:se sts=4 sw=4 et:
Added alternative_wmiircs/python/pyxp/types.py.














































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
from pyxp.messages import Message
from pyxp.fields import *

__all__ = 'Qid', 'Stat'
          
class Qid(Message):
    QTFILE    = 0x00
    QTLINK    = 0x01
    QTSYMLINK = 0x02
    QTTMP     = 0x04
    QTAUTH    = 0x08
    QTMOUNT   = 0x10
    QTEXCL    = 0x20
    QTAPPEND  = 0x40
    QTDIR     = 0x80

    type    = Int(1)
    version = Int(4)
    path    = Int(8)

class Stat(Message):
    DMDIR       = 0x80000000
    DMAPPEND    = 0x40000000
    DMEXCL      = 0x20000000
    DMMOUNT     = 0x10000000
    DMAUTH      = 0x08000000
    DMTMP       = 0x04000000
    DMSYMLINK   = 0x02000000
    DMDEVICE    = 0x00800000
    DMNAMEDPIPE = 0x00200000
    DMSOCKET    = 0x00100000
    DMSETUID    = 0x00080000
    DMSETGID    = 0x00040000

    @classmethod
    def unmarshall_list(cls, data, offset=0):
        while offset < len(data):
            n, stat = cls.unmarshall(data, offset)
            offset += n
            yield stat

    size   = Size(2)
    type   = Int(2)
    dev    = Int(4)
    qid    = Qid.field()
    mode   = Int(4)
    atime  = Date()
    mtime  = Date()
    length = Int(8)
    name   = String()
    uid    = String()
    gid    = String()
    muid   = String()

# vim:se sts=4 sw=4 et:
Added alternative_wmiircs/python/wmiirc.
























>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
#!/usr/bin/env @PYTHON@
import os, sys
path = []
for p in os.environ.get("WMII_CONFPATH", "").split(':'):
    path += [p, p + '/python']
sys.path = path + sys.path

from pygmi import events
import wmiirc

events.loop()

Added alternative_wmiircs/python/wmiirc.py.
































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
import datetime
import operator
import os
import re
import sys
import traceback
from threading import Thread, Timer

import pygmi
from pygmi import *
from pygmi import event

identity = lambda k: k

# Begin Configuration
#
# Note: This file loads ~/.wmii/wmiirc_local.py if it exists.
# Configuration should be placed in that file, and this file
# left unmodified, if possible. wmiirc_local should import
# wmiirc or any other modules it needs.
#
# Do *not* copy this file to wmiirc_local.py lest you want it
# executed twice.

# Keys
keys.defs = dict(
    mod='Mod4',
    left='h',
    down='j',
    up='k',
    right='l')

# Bars
noticetimeout=5
noticebar=('right', '!notice')

# Theme
background = '#333333'
floatbackground='#222222'

wmii['font'] = 'drift,-*-fixed-*-*-*-*-9-*-*-*-*-*-*-*'
wmii['normcolors'] = '#000000', '#c1c48b', '#81654f'
wmii['focuscolors'] = '#000000', '#81654f', '#000000'
wmii['grabmod'] = keys.defs['mod']
wmii['border'] = 2

def setbackground(color):
    call('xsetroot', '-solid', color, background=True)
setbackground(background)

terminal = 'wmiir', 'setsid', '@TERMINAL@'
pygmi.shell = os.environ.get('SHELL', 'sh')
tray = 'witray',

@defmonitor
def load(self):
    return wmii.cache['normcolors'], re.sub(r'^.*: ', '', call('uptime')).replace(', ', ' ')
@defmonitor
def time(self):
    return wmii.cache['focuscolors'], datetime.datetime.now().strftime('%c')

wmii.rules = (
    # Apps with system tray icons like to their main windows
    # Give them permission.
    (ur'^Pidgin:',       dict(allow='+activate')),

    # MPlayer and VLC don't float by default, but should.
    (ur'MPlayer|VLC',   dict(floating=True)),

    # ROX puts all of its windows in the same group, so they open
    # with the same tags.  Disable grouping for ROX Filer.
    (ur'^ROX-Filer:',   dict(group=0)),
)

def unresponsive_client(client):
    msg = 'The following client is not responding. What would you like to do?'
    resp = call('wihack', '-transient', str(client.id),
                'xmessage', '-nearmouse', '-buttons', 'Kill,Wait', '-print',
                '%s\n  %s' % (msg, client.label))
    if resp == 'Kill':
        client.slay()

# End Configuration

client.awrite('/event', 'Start wmiirc')

tags = Tags()
events.bind({
    ('Quit', Match('Start', 'wmiirc')): lambda *a: sys.exit(),
    'CreateTag':    tags.add,
    'DestroyTag':   tags.delete,
    'FocusTag':     tags.focus,
    'UnfocusTag':   tags.unfocus,
    'UrgentTag':    lambda args: tags.set_urgent(args.split()[1], True),
    'NotUrgentTag': lambda args: tags.set_urgent(args.split()[1], False),

    'AreaFocus':    lambda args: (args == '~' and
                                  (setbackground(floatbackground), True) or
                                  setbackground(background)),

    'Unresponsive': lambda args: Thread(target=unresponsive_client,
                                        args=(Client(args),)).start(),

    'Notice':       lambda args: notice.show(args),

    'ScreenChange': lambda args: wmii.ctl('wipescreens'),

    Match(('LeftBarClick', 'LeftBarDND'), 1): lambda e, b, tag: tags.select(tag),
    Match('LeftBarClick', 4): lambda *a: tags.select(tags.next(True)),
    Match('LeftBarClick', 5): lambda *a: tags.select(tags.next()),

    Match('LeftBarMouseDown', 3):   lambda e, n, tag: clickmenu((
            ('Delete',     lambda t: Tag(t).delete()),
        ), (tag,)),
    Match('ClientMouseDown', _, 3): lambda e, client, n: clickmenu((
            ('Delete',     lambda c: Client(c).kill()),
            ('Kill',       lambda c: Client(c).slay()),
            ('Fullscreen', lambda c: Client(c).set('Fullscreen', 'on')),
        ), (client,)),

    Match('ClientClick', _, 4): lambda e, c, n: Tag('sel').select('up'),
    Match('ClientClick', _, 5): lambda e, c, n: Tag('sel').select('down'),
})

@apply
class Actions(event.Actions):
    def rehash(self, args=''):
        program_menu.choices = program_list(os.environ['PATH'].split(':'))
    def showkeys(self, args=''):
        message(keys.help)
    def quit(self, args=''):
        wmii.ctl('quit')
    def eval_(self, args=''):
        exec args
    def exec_(self, args=''):
        wmii['exec'] = args
    def exit(self, args=''):
        client.awrite('/event', 'Quit')

program_menu = Menu(histfile='%s/history.progs' % confpath[0], nhist=5000,
                    action=curry(call, 'wmiir', 'setsid',
                                 pygmi.shell, '-c', background=True))
action_menu = Menu(histfile='%s/history.actions' % confpath[0], nhist=500,
                   choices=lambda: Actions._choices,
                   action=Actions._call)
tag_menu = Menu(histfile='%s/history.tags' % confpath[0], nhist=100,
                choices=lambda: sorted(tags.tags.keys()))

def clickmenu(choices, args):
    ClickMenu(choices=(k for k, v in choices),
              action=lambda choice: dict(choices).get(choice, identity)(*args)
             ).call()

class Notice(Button):
    def __init__(self):
        super(Notice, self).__init__(*noticebar, colors=wmii.cache['normcolors'])
        self.timer = None
        self.show(' ')

    def tick(self):
        self.create(wmii.cache['normcolors'], ' ')

    def write(self, notice):
        client.awrite('/event', 'Notice %s' % notice.replace('\n', ' '))

    def show(self, notice):
        if self.timer:
            self.timer.cancel()
        self.create(wmii.cache['normcolors'], notice)
        self.timer = Timer(noticetimeout, self.tick)
        self.timer.start()
notice = Notice()

keys.bind('main', (
    "Moving around",
    ('%(mod)s-%(left)s',  "Select the client to the left",
        lambda k: Tag('sel').select('left')),
    ('%(mod)s-%(right)s', "Select the client to the right",
        lambda k: Tag('sel').select('right')),
    ('%(mod)s-%(up)s',    "Select the client above",
        lambda k: Tag('sel').select('up')),
    ('%(mod)s-%(down)s',  "Select the client below",
        lambda k: Tag('sel').select('down')),

    ('%(mod)s-space',     "Toggle between floating and managed layers",
        lambda k: Tag('sel').select('toggle')),

    "Moving through stacks",
    ('%(mod)s-Control-%(up)s',   "Select the stack above",
        lambda k: Tag('sel').select('up', stack=True)),
    ('%(mod)s-Control-%(down)s', "Select the stack below",
        lambda k: Tag('sel').select('down', stack=True)),


    "Moving clients around",
    ('%(mod)s-Shift-%(left)s',  "Move selected client to the left",
        lambda k: Tag('sel').send(Client('sel'), 'left')),
    ('%(mod)s-Shift-%(right)s', "Move selected client to the right",
        lambda k: Tag('sel').send(Client('sel'), 'right')),
    ('%(mod)s-Shift-%(up)s',    "Move selected client up",
        lambda k: Tag('sel').send(Client('sel'), 'up')),
    ('%(mod)s-Shift-%(down)s',  "Move selected client down",
        lambda k: Tag('sel').send(Client('sel'), 'down')),

    ('%(mod)s-Shift-space',     "Toggle selected client between floating and managed layers",
        lambda k: Tag('sel').send(Client('sel'), 'toggle')),

    "Client actions",
    ('%(mod)s-f',       "Toggle selected client's fullsceen state",
        lambda k: Client('sel').set('Fullscreen', 'toggle')),
    ('%(mod)s-Shift-c', "Close client",
        lambda k: Client('sel').kill()),

    "Changing column modes",
    ('%(mod)s-d', "Set column to default mode",
        lambda k: setattr(Tag('sel').selcol, 'mode', 'default-max')),
    ('%(mod)s-s', "Set column to stack mode",
        lambda k: setattr(Tag('sel').selcol, 'mode', 'stack-max')),
    ('%(mod)s-m', "Set column to max mode",
        lambda k: setattr(Tag('sel').selcol, 'mode', 'stack+max')),

    "Running programs",
    ('%(mod)s-a',      "Open wmii actions menu",
        lambda k: action_menu()),
    ('%(mod)s-p',      "Open program menu",
        lambda k: program_menu()),

    ('%(mod)s-Return', "Launch a terminal",
        lambda k: call(*terminal, background=True)),

    "Tag actions",
    ('%(mod)s-t',       "Change to another tag",
        lambda k: tags.select(tag_menu())),
    ('%(mod)s-Shift-t', "Retag the selected client",
        lambda k: setattr(Client('sel'), 'tags', tag_menu())),

    ('%(mod)s-n', "Move to the view to the left",
        lambda k: tags.select(tags.next())),
    ('%(mod)s-b', "Move to the view to the right",
        lambda k: tags.select(tags.next(True))),
    ('%(mod)s-Shift-n', "Move to the view to the left, take along current client",
        lambda k: tags.select(tags.next(), take_client=Client('sel'))),
    ('%(mod)s-Shift-b', "Move to the view to the right, take along current client",
        lambda k: tags.select(tags.next(True), take_client=Client('sel'))),

    ('%(mod)s-i', "Move to the newer tag in the tag stack",
        lambda k: tags.select(tags.NEXT)),
    ('%(mod)s-o', "Move to the older tag in the tag stack",
        lambda k: tags.select(tags.PREV)),
    ('%(mod)s-Shift-i', "Move to the newer tag in the tag stack, take along current client",
        lambda k: tags.select(tags.NEXT, take_client=Client('sel'))),
    ('%(mod)s-Shift-o', "Move to the older tag in the tag stack, take along current client",
        lambda k: tags.select(tags.PREV, take_client=Client('sel'))),

))
def bind_num(i):
    keys.bind('main', (
        "Tag actions",
        ('%%(mod)s-%d' % i,       "Move to view '%d'" % i,
            lambda k: tags.select(str(i))),
        ('%%(mod)s-Shift-%d' % i, "Retag selected client with tag '%d'" % i,
            lambda k: setattr(Client('sel'), 'tags', i)),
    ))
map(bind_num, range(0, 10))

keys.bind('main', (
    "Changing modes",
    ('%(mod)s-Control-r', "Enter resize mode",
        lambda k: setattr(keys, 'mode', 'resize')),
    ('%(mod)s-Control-t', "Enter passthrough mode",
        lambda k: setattr(keys, 'mode', 'passthrough')),
));
keys.bind('passthrough', (
    "Changing modes",
    ('%(mod)s-Control-t', "Leave passthrough mode",
        lambda k: setattr(keys, 'mode', 'main')),
));

keys.bind('resize', (
    ('Escape', "Leave resize mode",
        lambda k: setattr(keys, 'mode', 'main')),
), import_={'main': ('%(mod)s-%(left)s', '%(mod)s-%(right)s',
                     '%(mod)s-%(up)s', '%(mod)s-%(down)s',
                     '%(mod)s-Space')})

def addresize(mod, desc, cmd, *args):
    keys.bind('resize', (
        (mod + '%(left)s',  "%s selected client to the left" % desc,
            lambda k: Tag('sel').ctl(cmd, 'sel sel', 'left',
                                     *args)),
        (mod + '%(right)s', "%s selected client to the right" % desc,
            lambda k: Tag('sel').ctl(cmd, 'sel sel', 'right',
                                     *args)),
        (mod + '%(up)s',    "%s selected client up" % desc,
            lambda k: Tag('sel').ctl(cmd, 'sel sel', 'up',
                                     *args)),
        (mod + '%(down)s',  "%s selected client down" % desc,
            lambda k: Tag('sel').ctl(cmd, 'sel sel', 'down',
                                     *args)),
    ));
addresize('',         'Grow', 'grow')
addresize('Control-', 'Shrink', 'grow', '-1')
addresize('Shift-',   'Nudge', 'nudge')

Thread(target=lambda: Actions.rehash()).start()

if not os.environ.get('WMII_NOPLUGINS', ''):
    dirs = filter(curry(os.access, _, os.R_OK),
                  ('%s/plugins' % dir for dir in confpath))
    files = filter(re.compile(r'\.py$').search,
                   reduce(operator.add, map(os.listdir, dirs), []))
    for f in ['wmiirc_local'] + ['plugins.%s' % file[:-3] for file in files]:
        try:
            __import__(f)
        except Exception, e:
            traceback.print_exc(sys.stdout)

call(*tray, background=True)

# vim:se sts=4 sw=4 et:
Added alternative_wmiircs/ruby/HISTORY.


















































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
= 2006-09-30

* Included 1.1.0 release of Ruby-IXP.


= 2006-09-29

* Fixed bug in toggle_maximize method (in rc.rb) due
  to accessing a nonexistent file in IXP file system.

  Thanks to Christian von Mueffling for reporting this bug.

* Fixed problem with reading
  index (Wmii::Client#index) of
  currently selected client.

* Wmii.find_client now accepts a variable number of places to be searched.


= 2006-09-28

* Added number_view_buttons method (in rc.rb) which numbers
  the view buttons displayed on the bar, from left to right.


= 2006-09-27

* Included two main concurrency fixes for Ruby-IXP.


= 2006-09-24

* Added two-stage event handling,
  to minimize the number of events
  missed while processing an event.


= 2006-09-23

* Fixed event & status bar loop. It was forgotten when I transitioned
  to the new Ixp::Node#method_missing behavior on 2006-09-22.

  Thanks to Fredrik Ternerot for reporting this bug.

* When selecting views based on their first letter: if more than one
  view matches, then they are cycled (adapted from Fredrik Ternerot).

* Added focus_view_matching method in rc.rb.

* Fixed errors that occurred when the tile and
  diamond arrangements were applied to empty views.


= 2006-09-22

* Ixp::Node#method_missing now only dereferences files. Also,
  the ! notation has been removed, as you can see below.

    >> Wmii.fs.bar.status
    => #<Ixp::Node:0xb7b5940c @path="/bar/status">
    >> Wmii.fs.bar.status.read
    => ["colors", "data"]
    >> Wmii.fs.bar.status.data
    => "Fri Sep 22 18:46:11 PDT 2006 | 0.06 0.10 0.08 | 531M 100% /home"
    >> Wmii.fs.bar.status.data!
    => #<Ixp::Node:0xb7b377e4 @path="/bar/status/data!">


= 2006-09-21

* Fix some forgotten changes from show_menu() returning *nil*.

* Exception error message (xmessage) now lets you restart *wmiirc*.

* Updated event loop to generate less 9P traffic.


= 2006-09-20

* Included code from upcoming Ruby-IXP 1.1.0 release.

* Ixp::Node#method_missing now only dereferences a node
  if the method is suffixed with an exclamation mark.

* show_menu now returns *nil* if nothing was chosen.

* Updated event loop for {wmii-3.1's /event overload bug
  fix}[http://wmii.de/pipermail/wmii/2006-September/002718.html].

* Added explicit termination of already running instances
  in *wmiirc* via Process.kill and `ps`, instead of using
  /event as a means of coordinating said task.


= 2006-09-19

* Included Ruby-IXP 1.0.3 release.

* Added Ixp::Node#open method to reduce 9P traffic.

* Added ability to fetch a sub-node
  via Ixp::Node#method_missing, while
  not dereferencing it (reading its
  contents if it is a file), by adding
  an exclamation to the file name.

  For example, consider the following output in *wmiish*.

    >> Wmii.fs.bar.status.data
    => "Tue Sep 19 10:50:41 PDT 2006 | 0.30 0.43 0.29 | 1.7G 98% /home"
    >> Wmii.fs.bar.status.data!
    => #<Ixp::Node:0xb7bf1f18 @path="/bar/status/data">

* *wmiirc* no longer automatically resumes from error. Instead,
  it throws you a terminal and shows you the error details so
  you have a chance to fix it and restart *wmiirc* yourself.


= 2006-09-18

* Included Ruby-IXP 1.0.2 release.


= 2006-09-17

* Added Wmii::View#empty? and Wmii::Area#empty? methods.

* change_tag_from_menu now returns the chosen tag.

* Included Ruby-IXP 1.0.1 release.


= 2006-09-16

* Fixed toggling of maximization
  of currently focused client,
  via toggle_maximize in rc.rb.

  Thanks to Fredrik Ternerot for reporting this bug.


= 2006-09-15

* Added Wmii.get_view and Wmii.get_client
  methods, to further minimize hard-coded
  IXP file system paths. This will make it
  easier to upgrade to wmii-4 later on.

* Fixed ruby-ixp to be internally buffered for Ixp#read.

* Event loop now uses Ixp#read instead of *wmiir*.

* Already running configurations now correctly
  exit when another instance starts up.


= 2006-09-14

* Added ability to swap current client with the
  currently focused client in any other column.


= 2006-09-13

* Reverted to *wmiir* for event loop, because
  Ixp#read isn't internally buffered!

* Changed Wmii::View#each to Wmii::View#each_column because
  floating area isn't a column (it doesn't have /mode file).

* Added shortcuts for setting layouts of all columns in current view.

* Added shortcuts for selection of current column.

* Fixed ability to terminate multiple clients.


= 2006-09-12

* Event loop now uses Ixp#read instead of *wmiir*.

  * Already running configurations now correctly
    exit when another instance starts up.

* Added Wmii::View#diamond! -- a diamond-shaped automated client arrangement.

* Added Wmii::Area#length= for setting number of clients in a column.


= 2006-09-11

* Added exception logging and recovery mechanism.

  * wmiirc is now split into a loader
    file (wmiirc) and a configuration
    file (wmiirc-config.rb), just
    like in the ruby-wmii project.

* IXPException' are no longer hidden away inside Ixp.

* Moved support for destructive area-operations
  from Wmii#with_selection into Array#each so
  that it is generally available.


= 2006-09-10

* Added wmiish--an interactive Ruby shell for controlling wmii.

* Lots of major refactoring in Ixp and Wmii.
  * Moved utility methods from wmiirc into rc.rb.


= 2006-09-09

* Cleaned up IXP abstraction... now
  multiple levels of method_missing
  works, and so does self[sub_path]

* Wmii#with_selection now supports destructive area-operations.

* Update for compliance with new unique-client-id in filesystem patch.


= 2006-08-31

* Added facility which sends the selection
  to temporary view or switches back again.


= 2006-08-30

* Add Wmii#with_selection method for operating on all clients in selection.
Added alternative_wmiircs/ruby/LICENSE.










































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
(the ISC license)

Copyright 2006 Suraj N. Kurapati <sunaku@gmail.com>
Copyright 2007 Kris Maglione <jg@suckless.org>
Copyright 2007 Nick Stenning <nick@whiteink.com>
Copyright 2009 Daniel Wäber <waeber@inf.fu-berlin.de>
Copyright 2009 Michael Andrus <centyx@centyx.net>
Copyright 2009 Simon Hafner <hafnersimon@gmail.com>

Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

Added alternative_wmiircs/ruby/Makefile.


























>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
ROOT=../..
include $(ROOT)/mk/hdr.mk
include $(ROOT)/mk/wmii.mk

DOCS   = README  \
	 HISTORY \
	 LICENSE
EXECS  = wmiirc
TEXT   = config.rb \
	 config.yaml

DIR    = $(GLOBALCONF)/ruby
DOCDIR = $(DOC)/alternative_wmiircs/ruby
Added alternative_wmiircs/ruby/README.


























































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93

This is a modified version of sunaku's wmiirc, designed for
his Rumai Ruby module. Minor configuration changes, namely to
the color scheme and default key bindings, as well as the
configuration search path, exist in this version. Builtin mpd
support has also been removed. Also added is support for
string interpolation in key bindings, as should be apparent in
the included config.yaml.

In particular, not that there is no need to copy any files to
@LOCALCONF@ other than config.yaml. The script will happily load
the requisite files from their default install location. They
can be loaded either by involing wmii as follows:

    wmiir -r ruby/wmiirc

or running the following after startup:

    wmiir xwrite /ctl spawn ruby/wmiirc

The rumai gem is still required, as noted below.

The original readme appears below unmodified:

sunaku's Ruby wmiirc
====================

This is my wmii configuration, described in these articles:

  http://wmii.suckless.org/alternative_wmiirc_scripts

  http://snk.tuxfamily.org/lib/rumai/

  http://article.gmane.org/gmane.comp.window-managers.wmii/1704

  http://snk.tuxfamily.org/web/2006-07-01-wmii-3-1-configuration-in-ruby.html

Dependencies:

    wmii 3.6 or newer (preferably wmii-hg)

    Ruby 1.8.6 or newer

    RubyGems 1.3.1 or newer

Installation:

    # library
    gem install rumai     # required
    gem install librmpd   # optional

    # install
    mv @LOCALCONF@ @LOCALCONF@.backup
    git clone git://github.com/sunaku/wmiirc.git @LOCALCONF@

    # choose
    cd @LOCALCONF@
    git checkout --track -b CHOICE origin/CHOICE # choices are:

    +--------+------------------------------------------------+
    | CHOICE | DESCRIPTION                                    |
    +--------+------------------------------------------------+
    | dvorak | sunaku's personal configuration; DSK friendly! |
    | qwerty | QWERTY port of sunaku's personal configuration |
    | strict | port of the default wmiirc shipped with wmii   |
    | master | barebones template for starting from scratch   |
    +--------+------------------------------------------------+

    # run
    @LOCALCONF@/wmiirc

Documentation:

    # see list of all key bindings
    egrep '^ +\$\{\w+\}' @LOCALCONF@/config.yaml

    # read the configuration file
    less @LOCALCONF@/config.yaml

Configuration:

    Edit @LOCALCONF@/config.yaml to your liking.

    Run @LOCALCONF@/wmiirc to apply your changes.

Contribution:

    Fork this project on GitHub and send pull requests.

Questions:

    Send me an e-mail (see LICENSE for my address).

Added alternative_wmiircs/ruby/config.rb.






































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
# DSL for wmiirc configuration.
#--
# Copyright protects this work.
# See LICENSE file for details.
#++

require 'shellwords'
require 'pathname'
require 'yaml'

require 'rubygems'
gem 'rumai', '~> 3'
require 'rumai'

include Rumai

class Handler < Hash
  def initialize
    super {|h,k| h[k] = [] }
  end

  ##
  # If a block is given, registers a handler
  # for the given key and returns the handler.
  #
  # Otherwise, executes all handlers registered for the given key.
  #
  def handle key, *args, &block
    if block
      self[key] << block

    elsif key? key
      self[key].each do |block|
        block.call(*args)
      end
    end

    block
  end
end

EVENTS  = Handler.new
ACTIONS = Handler.new
KEYS    = Handler.new

##
# If a block is given, registers a handler
# for the given event and returns the handler.
#
# Otherwise, executes all handlers for the given event.
#
def event *a, &b
  EVENTS.handle(*a, &b)
end

##
# Returns a list of registered event names.
#
def events
  EVENTS.keys
end

##
# If a block is given, registers a handler for
# the given action and returns the handler.
#
# Otherwise, executes all handlers for the given action.
#
def action *a, &b
  ACTIONS.handle(*a, &b)
end

##
# Returns a list of registered action names.
#
def actions
  ACTIONS.keys
end

##
# If a block is given, registers a handler for
# the given keypress and returns the handler.
#
# Otherwise, executes all handlers for the given keypress.
#
def key *a, &b
  KEYS.handle(*a, &b)
end

##
# Returns a list of registered action names.
#
def keys
  KEYS.keys
end

##
# Shows a menu (where the user must press keys on their keyboard to
# make a choice) with the given items and returns the chosen item.
#
# If nothing was chosen, then nil is returned.
#
# ==== Parameters
#
# [prompt]
#   Instruction on what the user should enter or choose.
#
def key_menu choices, prompt = nil
  words = ['dmenu', '-fn', CONFIG['display']['font']]

  # show menu at the same location as the status bar
  words << '-b' if CONFIG['display']['bar'] == 'bottom'

  words.concat %w[-nf -nb -sf -sb].zip(
    [
      CONFIG['display']['color']['normal'],
      CONFIG['display']['color']['focus'],

    ].map {|c| c.to_s.split[0,2] }.flatten

  ).flatten

  words.push '-p', prompt if prompt

  command = words.shelljoin
  IO.popen(command, 'r+') do |menu|
    menu.puts choices
    menu.close_write

    choice = menu.read
    choice unless choice.empty?
  end
end

##
# Shows a menu (where the user must click a menu
# item using their mouse to make a choice) with
# the given items and returns the chosen item.
#
# If nothing was chosen, then nil is returned.
#
# ==== Parameters
#
# [choices]
#   List of choices to display in the menu.
#
# [initial]
#   The choice that should be initially selected.
#
#   If this choice is not included in the list
#   of choices, then this item will be made
#   into a makeshift title-bar for the menu.
#
def click_menu choices, initial = nil
  words = ['wmii9menu']

  if initial
    words << '-i'

    unless choices.include? initial
      initial = "<<#{initial}>>:"
      words << initial
    end

    words << initial
  end

  words.concat choices
  command = words.shelljoin

  choice = `#{command}`.chomp
  choice unless choice.empty?
end

##
# Shows a key_menu() containing the given
# clients and returns the chosen client.
#
# If nothing was chosen, then nil is returned.
#
# ==== Parameters
#
# [prompt]
#   Instruction on what the user should enter or choose.
#
# [clients]
#   List of clients to present as choices to the user.
#
#   If this parameter is not specified,
#   its default value will be a list of
#   all currently available clients.
#
def client_menu prompt = nil, clients = Rumai.clients
  choices = []

  clients.each_with_index do |c, i|
    choices << "%d. [%s] %s" % [i, c[:tags].read, c[:label].read.downcase]
  end

  if target = key_menu(choices, prompt)
    clients[target.scan(/\d+/).first.to_i]
  end
end

##
# Returns the basenames of executable files present in the given directories.
#
def find_programs *dirs
  dirs.flatten.
  map {|d| Pathname.new(d).expand_path.children rescue [] }.flatten.
  map {|f| f.basename.to_s if f.file? and f.executable? }.compact.uniq.sort
end

##
# Launches the command built from the given words in the background.
#
def launch *words
  command = words.shelljoin
  system "#{command} &"
end

##
# A button on a bar.
#
class Button < Thread
  ##
  # Creates a new button at the given node and updates its label
  # according to the given refresh rate (measured in seconds).  The
  # given block is invoked to calculate the label of the button.
  #
  # The return value of the given block can be either an
  # array (whose first item is a wmii color sequence for the
  # button, and the remaining items compose the label of the
  # button) or a string containing the label of the button.
  #
  # If the given block raises a standard exception, then that will be
  # rescued and displayed (using error colors) as the button's label.
  #
  def initialize fs_bar_node, refresh_rate, &button_label
    raise ArgumentError, 'block must be given' unless block_given?

    super(fs_bar_node) do |button|
      while true
        label =
          begin
            Array(button_label.call)
          rescue Exception => e
            LOG.error e
            [CONFIG['display']['color']['error'], e]
          end

        # provide default color
        unless label.first =~ /(?:#[[:xdigit:]]{6} ?){3}/
          label.unshift CONFIG['display']['color']['normal']
        end

        button.create unless button.exist?
        button.write label.join(' ')
        sleep refresh_rate
      end
    end
  end

  ##
  # Refreshes the label of this button.
  #
  alias refresh wakeup
end

##
# Loads the given YAML configuration file.
#
def load_config config_file
  Object.const_set :CONFIG, YAML.load_file(config_file)

  # script
    eval CONFIG['script']['before'].to_s, TOPLEVEL_BINDING,
         "#{config_file}:script:before"

  # display
    fo = ENV['WMII_FONT']        = CONFIG['display']['font']
    fc = ENV['WMII_FOCUSCOLORS'] = CONFIG['display']['color']['focus']
    nc = ENV['WMII_NORMCOLORS']  = CONFIG['display']['color']['normal']

    settings = {
      'font'        => fo,
      'focuscolors' => fc,
      'normcolors'  => nc,
      'border'      => CONFIG['display']['border'],
      'bar on'      => CONFIG['display']['bar'],
      'colmode'     => CONFIG['display']['column']['mode'],
      'grabmod'     => CONFIG['control']['grab'],
    }

    begin
      fs.ctl.write settings.map {|pair| pair.join(' ') }.join("\n")

    rescue Rumai::IXP::Error => e
      #
      # settings that are not supported in a particular wmii version
      # are ignored, and those that are supported are (silently)
      # applied.  but a "bad command" error is raised nevertheless!
      #
      warn e.inspect
      warn e.backtrace.join("\n")
    end

    launch 'xsetroot', '-solid', CONFIG['display']['background']

    # column
      fs.colrules.write CONFIG['display']['column']['rule']

    # client
      event 'CreateClient' do |client_id|
        client = Client.new(client_id)

        unless defined? @client_tags_by_regexp
          @client_tags_by_regexp = CONFIG['display']['client'].map {|hash|
            k, v = hash.to_a.first
            [eval(k, TOPLEVEL_BINDING, "#{config_file}:display:client"), v]
          }
        end

        if label = client.props.read rescue nil
          catch :found do
            @client_tags_by_regexp.each do |regexp, tags|
              if label =~ regexp
                client.tags = tags
                throw :found
              end
            end

            # force client onto current view
            begin
              client.tags = curr_tag
              client.focus
            rescue
              # ignore
            end
          end
        end
      end

    # status
      action 'status' do
        fs.rbar.clear

        unless defined? @status_button_by_name
          @status_button_by_name     = {}
          @status_button_by_file     = {}
          @on_click_by_status_button = {}

          CONFIG['display']['status'].each_with_index do |hash, position|
            name, defn = hash.to_a.first

            # buttons appear in ASCII order of their IXP file name
            file = "#{position}-#{name}"

            button = eval(
              "Button.new(fs.rbar[#{file.inspect}], #{defn['refresh']}) { #{defn['content']} }",
              TOPLEVEL_BINDING, "#{config_file}:display:status:#{name}"
            )

            @status_button_by_name[name] = button
            @status_button_by_file[file] = button

            # mouse click handler
            if code = defn['click']
              @on_click_by_status_button[button] = eval(
                "lambda {|mouse_button| #{code} }", TOPLEVEL_BINDING,
                "#{config_file}:display:status:#{name}:click"
              )
            end
          end
        end

        @status_button_by_name.each_value {|b| b.refresh }

      end

      ##
      # Returns the status button associated with the given name.
      #
      # ==== Parameters
      #
      # [name]
      #   Either the the user-defined name of
      #   the status button or the basename
      #   of the status button's IXP file.
      #
      def status_button name
        @status_button_by_name[name] || @status_button_by_file[name]
      end

      ##
      # Refreshes the content of the status button with the given name.
      #
      # ==== Parameters
      #
      # [name]
      #   Either the the user-defined name of
      #   the status button or the basename
      #   of the status button's IXP file.
      #
      def status name
        if button = status_button(name)
          button.refresh
        end
      end

      ##
      # Invokes the mouse click handler for the given mouse
      # button on the status button that has the given name.
      #
      # ==== Parameters
      #
      # [name]
      #   Either the the user-defined name of
      #   the status button or the basename
      #   of the status button's IXP file.
      #
      # [mouse_button]
      #   The identification number of
      #   the mouse button (as defined
      #   by X server) that was clicked.
      #
      def status_click name, mouse_button
        if button = status_button(name) and
           handle = @on_click_by_status_button[button]
        then
          handle.call mouse_button.to_i
        end
      end

  # control
    action 'reload' do
      # reload this wmii configuration
      reload_config
    end

    action 'rehash' do
      # scan for available programs and actions
      @programs = find_programs(ENV['PATH'].squeeze(':').split(':'))
    end

    # kill all currently open clients
    action 'clear' do
      # firefox's restore session feature does not
      # work unless the whole process is killed.
      system 'killall firefox firefox-bin thunderbird thunderbird-bin'

      # gnome-panel refuses to die by any other means
      system 'killall -s TERM gnome-panel'

      Thread.pass until clients.each do |c|
        begin
          c.focus # XXX: client must be on current view in order to be killed
          c.kill
        rescue
          # ignore
        end
      end.empty?
    end

    # kill the window manager only; do not touch the clients!
    action 'kill' do
      fs.ctl.write 'quit'
    end

    # kill both clients and window manager
    action 'quit' do
      action 'clear'
      action 'kill'
    end

    event 'Unresponsive' do |client_id|
      client = Client.new(client_id)

      IO.popen('xmessage -nearmouse -file - -buttons Kill,Wait -print', 'w+') do |f|
        f.puts 'The following client is not responding.', ''
        f.puts client.inspect
        f.puts client.label.read

        f.puts '', 'What would you like to do?'
        f.close_write

        if f.read.chomp == 'Kill'
          client.slay
        end
      end
    end

    event 'Notice' do |*argv|
      unless defined? @notice_mutex
        require 'thread'
        @notice_mutex = Mutex.new
      end

      Thread.new do
        # prevent notices from overwriting each other
        @notice_mutex.synchronize do
          button = fs.rbar['!notice']
          button.create unless button.exist?

          # display the notice
          message = argv.join(' ')

          LOG.info message # also log it in case the user is AFK
          button.write "#{CONFIG['display']['color']['notice']} #{message}"

          # clear the notice
          sleep [1, CONFIG['display']['notice'].to_i].max
          button.remove
        end
      end
    end

    %w[key action event].each do |param|
      if settings = CONFIG['control'][param]
        settings.each do |name, code|
          if param == 'key'
            # expand ${...} expressions in shortcut key sequences
            name = name.gsub(/\$\{(.+?)\}/) { CONFIG['control'][$1] }
          end

          eval "#{param}(#{name.inspect}) {|*argv| #{code} }",
               TOPLEVEL_BINDING, "#{config_file}:control:#{param}:#{name}"
        end
      end
    end

  # script
    action 'status'
    action 'rehash'

    eval CONFIG['script']['after'].to_s, TOPLEVEL_BINDING,
         "#{config_file}:script:after"

end

##
# Reloads the entire wmii configuration.
#
def reload_config
  LOG.info 'reload'
  exec $0
end
Added alternative_wmiircs/ruby/config.yaml.
















































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
#
# High-level wmii configuration.
#
# Ruby code in this file has access
# to a CONFIG constant which contains
# the data in this configuration file.
#
#--
# Copyright protects this work.
# See LICENSE file for details.
#++


##
# Program preferences.
#
program:
  terminal: @TERMINAL@
  browser: firefox
  editor: mousepad
  filer: thunar


##
# Appearance settings.
#
display:

  ##
  # Where to display the horizontal status bar?
  #
  # Possible choices are "top" and "bottom".
  #
  bar: bottom

  ##
  # The font to use in all text drawn by wmii.
  #
  font: -*-fixed-medium-r-*-*-13-*-*-*-*-*-*-*

  ##
  # Thickness of client border (measured in pixels).
  #
  border: 1

  ##
  # Number of seconds a notice should be displayed.
  #
  notice: 5

  ##
  # Color schemes for everything drawn by wmii.
  #
  #   <scheme>: "<text> <background> <border>"
  #
  # You can find more color schemes here:
  #
  #   http://wmii.suckless.org/scripts_n_snips/themes
  #
  color:
    normal:   "#000000 #c1c48b #81654f"
    focus:    "#000000 #81654f #000000"
    error:    "#000000 #81654f #000000"
    notice:   "#000000 #a1956d #413328"
    success:  "#000000 #c1c48b #81654f"

  ##
  # Color of desktop background.
  #
  background: "#333333"

  ##
  # Settings for columns drawn by wmii.
  #
  #   mode: <the wmii "colmode" setting>
  #   rule: <the wmii "colrules" setting>
  #
  column:
    mode: default
    rule: |
      /gimp/ -> 17+83+41
      /.*/ -> 62+38 # Golden Ratio

  ##
  # Mapping of clients to views they must appear on.
  #
  #   - <client props regular expression> : <tags to apply>
  #
  # These mappings are processed in top-to-bottom order.
  # Processing stops after the first matching mapping is applied.
  #
  client:
    - /MPlayer|VLC/ : ~

  ##
  # Self-refreshing buttons on the status bar.
  #
  #   - <button name>:
  #       refresh:  <number of seconds to wait before refreshing the content>
  #       content:  <Ruby code whose result is displayed as the content>
  #       click:    <Ruby code to handle mouse clicks on the status button.
  #                  This code has access to a "mouse_button" variable which is
  #                  an integer representing the mouse button that was clicked.>
  #
  # You can refresh a particular status button in Ruby using:
  #
  #   status "your button name"
  #
  # The horizontal order in which these buttons appear on the status
  # bar reflects the vertical order in which they are defined below.
  #
  status:
    - system_load:
        refresh: 10
        content: |
          load_averages = File.read('/proc/loadavg').split.first(3)
          current_load  = load_averages.first.to_f

          # visually indicate the intensity of system load
          color = case
            when current_load > 3.0 then CONFIG['display']['color']['error']
            when current_load > 1.5 then CONFIG['display']['color']['notice']
          end

          [color, *load_averages]

    - clock:
        refresh: 5
        content: Time.now.to_s


##
# Interaction settings.
#
control:

  ##
  # The wmii "grabmod" setting.
  #
  grab: Mod4

  ##
  # Key sequence prefixes.
  #
  mod:    Mod4
  move:   Mod4-Shift
  swap:   Mod4-w
  view:   Mod4-v
  group:  Mod4-g

  ##
  # Direction keys.
  #
  up:    k
  down:  j
  left:  h
  right: l

  ##
  # Sequence keys.
  #
  prev: b
  next: n

  ##
  # Key bindings.
  #
  #   <key sequence>: <Ruby code to execute>
  #
  # A key sequence may contain ${...} expressions which
  # are replaced with the value corresponding to '...'
  # in the 'control' section of this configuration file.
  #
  # For example, if the 'control' section of
  # this configuration file appeared like this:
  #
  #   control:
  #     foo: Mod4
  #     bar: y
  #
  # and the following key sequence was used:
  #
  #   ${foo}-${bar},${bar}
  #
  # then after ${...} expression replacement,
  # that key sequence would appear like this:
  #
  #   Mod4-y,y
  #
  key:
    #---------------------------------------------------------------------------
    # focus
    #---------------------------------------------------------------------------

    ${mod}-${up}: | # focus above client
      curr_view.select(:up) rescue nil

    ${mod}-${down}: | # focus below client
      curr_view.select(:down) rescue nil

    ${mod}-${left}: | # focus left client
      curr_view.select(:left) rescue nil

    ${mod}-${right}: | # focus right client
      curr_view.select(:right) rescue nil

    ${mod}-space: | # focus floating area (toggle)
      curr_view.select(:toggle)

    ${mod}-${prev}: | # focus previous view
      prev_view.focus

    ${mod}-${next}: | # focus next view
      next_view.focus

    # focus the view whose index or name equals the pressed number
    ${mod}-1: focus_view tags[0] || 1
    ${mod}-2: focus_view tags[1] || 2
    ${mod}-3: focus_view tags[2] || 3
    ${mod}-4: focus_view tags[3] || 4
    ${mod}-5: focus_view tags[4] || 5
    ${mod}-6: focus_view tags[5] || 6
    ${mod}-7: focus_view tags[6] || 7
    ${mod}-8: focus_view tags[7] || 8
    ${mod}-9: focus_view tags[8] || 9
    ${mod}-0: focus_view tags[9] || 10

    # focus the view whose name begins with the pressed alphabet
    ${view},a: t = tags.grep(/^a/i).first and focus_view(t)
    ${view},b: t = tags.grep(/^b/i).first and focus_view(t)
    ${view},c: t = tags.grep(/^c/i).first and focus_view(t)
    ${view},d: t = tags.grep(/^d/i).first and focus_view(t)
    ${view},e: t = tags.grep(/^e/i).first and focus_view(t)
    ${view},f: t = tags.grep(/^f/i).first and focus_view(t)
    ${view},g: t = tags.grep(/^g/i).first and focus_view(t)
    ${view},h: t = tags.grep(/^h/i).first and focus_view(t)
    ${view},i: t = tags.grep(/^i/i).first and focus_view(t)
    ${view},j: t = tags.grep(/^j/i).first and focus_view(t)
    ${view},k: t = tags.grep(/^k/i).first and focus_view(t)
    ${view},l: t = tags.grep(/^l/i).first and focus_view(t)
    ${view},m: t = tags.grep(/^m/i).first and focus_view(t)
    ${view},n: t = tags.grep(/^n/i).first and focus_view(t)
    ${view},o: t = tags.grep(/^o/i).first and focus_view(t)
    ${view},p: t = tags.grep(/^p/i).first and focus_view(t)
    ${view},q: t = tags.grep(/^q/i).first and focus_view(t)
    ${view},r: t = tags.grep(/^r/i).first and focus_view(t)
    ${view},s: t = tags.grep(/^s/i).first and focus_view(t)
    ${view},t: t = tags.grep(/^t/i).first and focus_view(t)
    ${view},u: t = tags.grep(/^u/i).first and focus_view(t)
    ${view},v: t = tags.grep(/^v/i).first and focus_view(t)
    ${view},w: t = tags.grep(/^w/i).first and focus_view(t)
    ${view},x: t = tags.grep(/^x/i).first and focus_view(t)
    ${view},y: t = tags.grep(/^y/i).first and focus_view(t)
    ${view},z: t = tags.grep(/^z/i).first and focus_view(t)

    #---------------------------------------------------------------------------
    # move
    #---------------------------------------------------------------------------

    ${move}-${up}: | # move grouping toward the top
      grouping.each {|c| c.send(:up) rescue nil }

    ${move}-${down}: | # move grouping toward the bottom
      grouping.each {|c| c.send(:down) rescue nil }

    ${move}-${left}: | # move grouping toward the left
      grouping.each {|c| c.send(:left) rescue nil }

    ${move}-${right}: | # move grouping toward the right
      grouping.each {|c| c.send(:right) rescue nil }

    ${move}-space: | # move grouping to floating area (toggle)
      grouping.each {|c| c.send(:toggle) rescue nil }

    ${move}-t: | # move grouping to chosen view
      #
      # Changes the tag (according to a menu choice) of
      # each grouped client and returns the chosen tag.
      #
      # The +tag -tag idea is from Jonas Pfenniger:
      #
      #   http://zimbatm.oree.ch/articles/2006/06/15/wmii-3-and-ruby
      #
      choices = tags.map {|t| [t, "+#{t}", "-#{t}"] }.flatten

      if target = key_menu(choices, 'tag as:')
        grouping.each {|c| c.tags = target }
      end

    # move grouping to the view whose index or name equals the pressed number
    ${move}-1: grouping.each {|c| c.tags = tags[0] || 1  }
    ${move}-2: grouping.each {|c| c.tags = tags[1] || 2  }
    ${move}-3: grouping.each {|c| c.tags = tags[2] || 3  }
    ${move}-4: grouping.each {|c| c.tags = tags[3] || 4  }
    ${move}-5: grouping.each {|c| c.tags = tags[4] || 5  }
    ${move}-6: grouping.each {|c| c.tags = tags[5] || 6  }
    ${move}-7: grouping.each {|c| c.tags = tags[6] || 7  }
    ${move}-8: grouping.each {|c| c.tags = tags[7] || 8  }
    ${move}-9: grouping.each {|c| c.tags = tags[8] || 9  }
    ${move}-0: grouping.each {|c| c.tags = tags[9] || 10 }

    #---------------------------------------------------------------------------
    # group
    #---------------------------------------------------------------------------

    ${group},g: | # toggle current client from grouping
      curr_client.group!

    ${group},c: | # add clients in current area to grouping
      curr_area.group

    ${group},Shift-c: | # remove clients in current area from grouping
      curr_area.ungroup

    ${group},f: | # add clients in floating area to grouping
      Area.floating.group

    ${group},Shift-f: | # remove clients in floating area from grouping
      Area.floating.ungroup

    ${group},m: | # add clients in managed areas to grouping
      curr_view.managed_areas.each {|a| a.group }

    ${group},Shift-m: | # remove clients in managed areas from grouping
      curr_view.managed_areas.each {|a| a.ungroup }

    ${group},v: | # add clients in current view to grouping
      curr_view.group

    ${group},Shift-v: | # remove clients in current view from grouping
      curr_view.ungroup

    ${group},i: | # invert the grouping in the current view
      curr_view.group!

    ${group},Shift-i: | # invert the grouping in all views
      Rumai.group!

    ${group},n: | # remove all clients everywhere from grouping
      Rumai.ungroup

    #---------------------------------------------------------------------------
    # swap
    #---------------------------------------------------------------------------

    ${swap},${up}: | # swap with above client
      curr_client.swap(:up) rescue nil

    ${swap},${down}: | # swap with below client
      curr_client.swap(:down) rescue nil

    ${swap},${left}: | # swap with left client
      curr_client.swap(:left) rescue nil

    ${swap},${right}: | # swap with right client
      curr_client.swap(:right) rescue nil

    # swap current client with the column whose index equals the pressed number
    ${swap},1: curr_client.swap 1
    ${swap},2: curr_client.swap 2
    ${swap},3: curr_client.swap 3
    ${swap},4: curr_client.swap 4
    ${swap},5: curr_client.swap 5
    ${swap},6: curr_client.swap 6
    ${swap},7: curr_client.swap 7
    ${swap},8: curr_client.swap 8
    ${swap},9: curr_client.swap 9
    ${swap},0: curr_client.swap 10

    #---------------------------------------------------------------------------
    # client
    #---------------------------------------------------------------------------

    ${mod}-f: | # zoom client to fullscreen (toggle)
      curr_client.fullscreen!

    ${mod}-Shift-c: | # kill the current client
      curr_client.kill

    #---------------------------------------------------------------------------
    # column
    #---------------------------------------------------------------------------

    ${mod}-d: | # apply equal-spacing layout to current column
      curr_area.layout = 'default-max'

    ${mod}-s: | # apply stacked layout to current column
      curr_area.layout = 'stack-max'

    ${mod}-m: | # apply maximized layout to current column
      curr_area.layout = 'stack+max'

    #---------------------------------------------------------------------------
    # menu
    #---------------------------------------------------------------------------

    ${mod}-a: | # run internal action chosen from a menu
      if choice = key_menu(actions, 'run action:')
        action choice
      end

    ${mod}-p: | # run external program chosen from a menu
      if choice = key_menu(@programs, 'run program:')
        launch choice
      end

    ${mod}-t: | # focus view chosen from a menu
      if choice = key_menu(tags, 'show view:')
        focus_view choice
      end

    #---------------------------------------------------------------------------
    # launcher
    #---------------------------------------------------------------------------

    ${mod}-Return: | # launch a terminal
      #
      # Launch a new terminal and set its
      # working directory to be the same
      # as the currently focused terminal.
      #
      work = ENV['HOME']

      label = curr_client.label.read rescue ''

      # iterate in reverse order because
      # paths are usually at end of label
      label.split(' ').reverse_each do |s|
        path = File.expand_path(s)

        if File.exist? path
          unless File.directory? path
            path = File.dirname(path)
          end

          work = path
          break
        end
      end

      require 'fileutils'
      FileUtils.cd work do
        launch CONFIG['program']['terminal']
      end

  ##
  # Event handlers.
  #
  #   <event name>: <Ruby code to execute>
  #
  # The Ruby code has access to an "argv" variable which
  # is a list of arguments that were passed to the event.
  #
  event:
    CreateTag: |
      tag = argv[0]
      but = fs.lbar[tag]
      but.create unless but.exist?
      but.write "#{CONFIG['display']['color']['normal']} #{tag}"

    DestroyTag: |
      tag = argv[0]
      but = fs.lbar[tag]
      but.remove if but.exist?

    FocusTag: |
      tag = argv[0]
      but = fs.lbar[tag]
      but.write "#{CONFIG['display']['color']['focus']} #{tag}" if but.exist?

    UnfocusTag: |
      tag = argv[0]
      but = fs.lbar[tag]
      but.write "#{CONFIG['display']['color']['normal']} #{tag}" if but.exist?

    UrgentTag: |
      tag = argv[1]
      but = fs.lbar[tag]
      but.write "#{CONFIG['display']['color']['notice']} #{tag}" if but.exist?

    NotUrgentTag: |
      tag = argv[1]
      but = fs.lbar[tag]
      color = curr_view.id == tag ? 'focus' : 'normal'
      but.write "#{CONFIG['display']['color'][color]} #{tag}" if but.exist?

    LeftBarClick: &LeftBarClick |
      mouse_button, view_id = argv

      if mouse_button == '1' # primary button
        focus_view view_id
      end

    ##
    # allows the user to drag a file over a
    # view button and activate that view while
    # still holding on to their dragged file!
    #
    LeftBarDND: *LeftBarClick

    RightBarClick: |
      status_click *argv.reverse

    ClientMouseDown: |
      client_id, mouse_button = argv

      if mouse_button == '3' # secondary button
        client = Client.new(client_id)

        case click_menu %w[stick group fullscreen kill slay], 'client'
        when 'stick'      then client.stick!
        when 'group'      then client.group!
        when 'fullscreen' then client.fullscreen!
        when 'kill'       then client.kill
        when 'slay'       then client.slay
        end
      end

  ##
  # Internal scripts.
  #
  #   <action name>: <Ruby code to execute>
  #
  action:


##
# Arbitrary logic.
#
#   script:
#     before: <Ruby code to execute before processing this file>
#     after:  <Ruby code to execute after processing this file>
#
script:
  before:
  after:
Added alternative_wmiircs/ruby/wmiirc.
















































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#!/usr/bin/env ruby
#
# Bootloader for wmii configuration.
#
#--
# Copyright protects this work.
# See LICENSE file for details.
#++

# create a logger to aid debugging
require 'logger'
LOG = Logger.new(__FILE__ + '.log', 5)

class << LOG
  # emulate IO.write
  alias write <<

  def flush
    # ignore
  end
end

# capture standard output in logger
$stdout = $stderr = LOG

begin
  LOG.info 'birth'

  # load configuration library
    def find_config file
      base_dirs = ENV['WMII_CONFPATH'].to_s.split(/:+/)
      ruby_dirs = base_dirs.map {|dir| File.join(dir, 'ruby') }

      Dir["{#{base_dirs.zip(ruby_dirs).join(',')}}/#{file}"].first
    end

    require find_config('config.rb')

  # terminate any existing wmiirc
    fs.event.write 'Start wmiirc'

    event 'Start' do |arg|
      exit if arg == 'wmiirc'
    end

  # apply user configuration
    load_config find_config('config.yaml')

  # setup tag bar (buttons that correspond to views)
    fs.lbar.clear
    tags.each {|t| event 'CreateTag', t }
    event 'FocusTag', curr_tag

  # register key bindings
    fs.keys.write keys.join("\n")
    event('Key') {|*a| key(*a) }

  # the main event loop
    fs.event.each_line do |line|
      line.split("\n").each do |call|
        name, args = call.split(' ', 2)

        argv = args.to_s.split(' ')
        event name, *argv
      end
    end

rescue SystemExit
  # ignore it; the program wants to terminate

rescue Exception => e
  LOG.error e

  # allow the user to rescue themselves
  system '@TERMINAL@ &'

  IO.popen('xmessage -nearmouse -file - -buttons Recover,Ignore -print', 'w+') do |f|
    f.puts e.inspect, e.backtrace
    f.close_write

    if f.read.chomp == 'Recover'
      reload_config
    end
  end

ensure
  LOG.info 'death'
end
Added cmd/Makefile.










































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
ROOT=..
include $(ROOT)/mk/hdr.mk
include $(ROOT)/mk/wmii.mk

wmiir.c: $(ROOT)/mk/wmii.mk

DIRS =	menu \
	strut \
	tray \
	wmii \
	x11
TARG =	wihack    \
	wmii.rc   \
	wmii.sh   \
	wmiir

LIBS += $(LIBS9) $(LIBIXP)

include $(ROOT)/mk/many.mk
include $(ROOT)/mk/dir.mk

Added cmd/menu/Makefile.
























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
ROOT= ../..
include $(ROOT)/mk/hdr.mk
include $(ROOT)/mk/wmii.mk

main.c: $(ROOT)/mk/wmii.mk

bindings.c: keys.txt Makefile
	( echo "char binding_spec[] ="; \
	  sed 's/.*/	"&\\n"/' keys.txt; \
	  echo "	;" ) >$@

TARG =	wimenu
HFILES=	dat.h fns.h
TAGFILES= dat.h $(ROOT)/include/*.h $(ROOT)/include/stuff/*.h

PACKAGES += $(X11PACKAGES)

LIB = $(LIBS9) $(LIBIXP)
LIBS += -lm
OBJ =	main	\
	caret	\
	history	\
	menu	\
	keys	\
	bindings

include $(ROOT)/mk/one.mk

Added cmd/menu/caret.c.
































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
#include "dat.h"
#include <ctype.h>
#include <string.h>
#include "fns.h"

static int
iswordrune(Rune r) {
	if(isalpharune(r))
		return 1;
	return r < 0x80 && (r == '_' || isdigit(r));
}

static char*
prev_rune(char *start, char *p, Rune *r) {

	*r = 0;
	if(p == start)
		return p;
	while(p > start && (*(--p)&0xC0) == 0x80)
		;
	chartorune(r, p);
	return p;
}

static char*
next_rune(char *p, Rune *r) {
	int i;

	*r = 0;
	if(!*p)
		return p;
	i = chartorune(r, p);
	return p + i;
}

void
caret_set(int start, int end) {
	int len;

	len = input.end - input.string;
	start = max(0, min(len, start));

	input.pos = input.string + start;
	if(end < 0)
		input.pos_end = nil;
	else
		input.pos_end = input.string + max(start, end);
}

char*
caret_find(int dir, int type) {
	char *end;
	char *next, *p;
	Rune r;
	int res;

	p = input.pos;
	if(dir == FORWARD) {
		end = input.end;
		switch(type) {
		case LINE:
			return end;
		case WORD:
			chartorune(&r, p);
			res = iswordrune(r);
			while(next=next_rune(p, &r), r && iswordrune(r) == res && !isspacerune(r))
				p = next;
			while(next=next_rune(p, &r), r && isspacerune(r))
				p = next;
			return p;
		case CHAR:
			return next_rune(p, &r);
		}
	}
	else if(dir == BACKWARD) {
		end = input.string;
		switch(type) {
		case LINE:
			return end;
		case WORD:
			while(next=prev_rune(end, p, &r), r && isspacerune(r))
				p = next;
			prev_rune(end, p, &r);
			res = iswordrune(r);
			while(next=prev_rune(end, p, &r), r && iswordrune(r) == res && !isspacerune(r))
				p = next;
			return p;
		case CHAR:
			return prev_rune(end, p, &r);
		}
	}
	input.pos_end = nil;
	return input.pos;
}

void
caret_move(int dir, int type) {
	input.pos = caret_find(dir, type);
	input.pos_end = nil;
}

void
caret_delete(int dir, int type) {
	char *pos, *p;
	int n;

	if(input.pos_end)
		p = input.pos_end;
	else
		p = caret_find(dir, type);
	pos = input.pos;
	if(p == input.end)
		input.end = pos;
	else {
		if(p < pos) {
			pos = p;
			p = input.pos;
		}
		n = input.end - p;
		memmove(pos, p, n);
		input.pos = pos;
		input.end = pos + n;
	}
	*input.end = '\0';
	input.pos_end = nil;
}

void
caret_insert(char *s, bool clear) {
	int pos, end, len, size;

	if(s == nil)
		return;
	if(clear) {
		input.pos = input.string;
		input.end = input.string;
	}else if(input.pos_end)
		caret_delete(0, 0);

	len = strlen(s);
	pos = input.pos - input.string;
	end = input.end - input.string;

	size = input.size;
	if(input.size == 0)
		input.size = 1;
	while(input.size < end + len + 1)
		input.size <<= 2;
	if(input.size != size)
		input.string = erealloc(input.string, input.size);

	input.pos = input.string + pos;
	input.end = input.string + end + len;
	*input.end = '\0';
	memmove(input.pos + len, input.pos, end - pos);
	memmove(input.pos, s, len);
	input.pos += len;
	input.pos_end = nil;
}

Added cmd/menu/dat.h.
























































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#define _XOPEN_SOURCE 600
#define IXP_P9_STRUCTS
#define IXP_NO_P9_
#include <fmt.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ixp.h>
#include <stuff/x.h>
#include <stuff/util.h>

#ifndef EXTERN
# define EXTERN extern
#endif

enum {
	FORWARD,
	BACKWARD,
	LINE,
	WORD,
	CHAR,
	CARET_LAST,
};

enum {
	LACCEPT,
	LBACKWARD,
	LCHAR,
	LCOMPLETE,
	LDELETE,
	LFIRST,
	LFORWARD,
	LHISTORY,
	LKILL,
	LLAST,
	LLINE,
	LLITERAL,
	LNEXT,
	LNEXTPAGE,
	LPASTE,
	LPREV,
	LPREVPAGE,
	LREJECT,
	LWORD,
};

typedef struct Item	Item;

struct Item {
	char*	string;
	char*	retstring;
	Item*	next_link;
	Item*	next;
	Item*	prev;
	int	len;
	int	width;
};

EXTERN struct {
	char*	string;
	char*	end;
	char*	pos;
	char*	pos_end;
	int	size;

	char*	filter;
	int	filter_start;
} input;

EXTERN struct {
	Window*		win;
	Image*		buf;
	char*		prompt;
	int		height;
	int		rows;
	bool		ontop;
	Rectangle	itemr;
	Point		arrow;
} menu;

extern char	binding_spec[];

EXTERN IxpServer	srv;

EXTERN struct {
	Item*	all;
	Item*	first;
	Item*	start;
	Item*	end;
	Item*	sel;
	int	maxwidth;
} match;

Font*		font;
CTuple		cnorm;
CTuple		csel;

EXTERN Item	hist;
EXTERN Item*	histsel;

EXTERN int	itempad;
EXTERN int	result;

EXTERN char*	(*find)(const char*, const char*);
EXTERN int	(*compare)(const char*, const char*, size_t);

Added cmd/menu/fns.h.
























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

/* caret.c */
void	caret_delete(int, int);
char*	caret_find(int, int);
void	caret_insert(char*, bool);
void	caret_move(int, int);
void	caret_set(int, int);

/* history.c */
void	history_dump(const char*, int);
char*	history_search(int, char*, int);

/* main.c */
void	debug(int, const char*, ...);
Item*	filter_list(Item*, char*);
void	update_filter(bool);
void	update_input(void);

/* menu.c */
void	menu_draw(void);
void	menu_init(void);
void	menu_show(void);

/* keys.c */
void	parse_keys(char*);
char**	find_key(char*, long);
int	getsym(char*);

Added cmd/menu/history.c.




























































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#include "dat.h"
#include <assert.h>
#include <bio.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "fns.h"

static void
splice(Item *i) {
	if(i->next != nil)
		i->next->prev = i->prev;
	if(i->prev != nil)
		i->prev->next = i->next;
}

char*
history_search(int dir, char *string, int n) {
	Item *i;

	if(dir == FORWARD) {
		if(histsel == &hist)
			return hist.string;
		for(i=histsel->next; i != hist.next; i=i->next)
			if(!i->string || !compare(i->string, string, n)) {
				histsel = i;
				return i->string;
			}
		return string;
	}
	assert(dir == BACKWARD);

	if(histsel == &hist) {
		free(hist.string);
		hist.string = estrdup(input.string);
	}

	for(i=histsel->prev; i != &hist; i=i->prev)
		if(!compare(i->string, string, n)) {
			histsel = i;
			return i->string;
		}
	return string;
}

void
history_dump(const char *path, int max) {
	static char *items[20];
	static char *tmp;
	Biobuf b;
	Item *h, *first;
	int i, n, fd;

	SET(first);
	if(fork() != 0)
		return;

	tmp = smprint("%s.XXXXXX", path);
	fd = mkstemp(tmp);
	if(fd < 0) {
		fprint(2, "%s: Can't create temporary history file %q: %r\n", argv0, path);
		_exit(1);
	}

	hist.string = input.string;
	n = 0;
	hist.next->prev = nil;
	for(h=&hist; h; h=h->prev) {
		for(i=0; i < nelem(items); i++)
			if(items[i] && !strcmp(h->string, items[i])) {
				splice(h);
				goto next;
			}
		items[n++ % nelem(items)] = h->string;
		first = h;
		if(!max || n >= max)
			break;
	next:
		continue;
	}

	Binit(&b, fd, OWRITE);
	hist.next = nil;
	for(h=first; h; h=h->next)
		if(Bprint(&b, "%s\n", h->string) < 0) {
			unlink(tmp);
			fprint(2, "%s: Can't write temporary history file %q: %r\n", argv0, path);
			_exit(1);
		}
	Bterm(&b);
	rename(tmp, path);
	_exit(0);
}

Added cmd/menu/keys.c.






















































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
#include "dat.h"
#include <ctype.h>
#include <strings.h>
#include <unistd.h>
#include "fns.h"

typedef struct Key Key;

struct Key {
	Key*	next;
	long	mask;
	char*	key;
	char**	action;
};

static Key*	bindings;
static int	numlock;

/*
 * To do: Find my red black tree implementation.
 */
void
parse_keys(char *spec) {
	static char *lines[1024];
	static char *words[16];
	Key *k;
	char *p, *line;
	int mask;
	int i, nlines, nwords;

	if(!numlock)
		numlock = numlockmask();

	nlines = tokenize(lines, nelem(lines), spec, '\n');
	for(i=0; i < nlines; i++) {
		line = lines[i];
		p = strchr(line, '#');
		if(p)
			*p = '\0';

		nwords = stokenize(words, nelem(words) - 1, line, " \t");
		words[nwords] = nil;
		if(!words[0])
			continue;
		if(parsekey(words[0], &mask, &p)) {
			k = emallocz(sizeof *k);
			k->key    = p;
			k->mask   = mask;
			k->action = strlistdup(words + 1);
			k->next   = bindings;
			bindings = k;
		}
	}
}

char**
find_key(char *key, long mask) {
	Key *k;

	/* Horrible hack. */
	if(!strcmp(key, "ISO_Left_Tab"))
		key = "Tab";

	mask &= ~(numlock | LockMask) & ((1<<8) - 1);
	for(k=bindings; k; k=k->next)
		if(!strcasecmp(k->key, key) && k->mask == mask)
			return k->action;
	return nil;
}

	/* sed 's/"([^"]+)"/L\1/g' | tr 'a-z' 'A-Z' */
	/* awk '{$1=""; print}' keys.txt | perl -e '$_=lc join "", <>; print join "\n", m/(\w+)/g;' | sort -u | sed 's:.*:	"&",:' */
char *symtab[] = {
	"accept",
	"backward",
	"char",
	"complete",
	"delete",
	"first",
	"forward",
	"history",
	"kill",
	"last",
	"line",
	"literal",
	"next",
	"nextpage",
	"paste",
	"prev",
	"prevpage",
	"reject",
	"word",
};

static int
_bsearch(char *s, char **tab, int ntab) {
	int i, n, m, cmp;

	if(s == nil)
		return -1;

	n = ntab;
	i = 0;
	while(n) {
		m = n/2;
		cmp = strcasecmp(s, tab[i+m]);
		if(cmp == 0)
			return i+m;
		if(cmp < 0 || m == 0)
			n = m;
		else {
			i += m;
			n = n-m;
		}
	}
	return -1;
}

int
getsym(char *s) {
	return _bsearch(s, symtab, nelem(symtab));
}

Added cmd/menu/keys.txt.








































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
Control-j           Accept
Control-m           Accept
Return              Accept
Control-Shift-j     Accept literal
Control-Shift-m     Accept literal
Shift-Return        Accept literal

Escape		    Reject
Control-Bracketleft Reject

Left                Backward char
Control-b           Backward char
Right               Forward  char
Control-f           Forward  char

Mod1-b              Backward word
Mod1-f              Forward  word

Control-a           Backward line
Control-e           Forward  line

Control-p           History backward
Up                  History backward
Control-n           History forward
Down                History forward

Backspace           Kill char
Control-h           Kill char
Control-Backspace   Kill word
Control-w           Kill word
Control-u           Kill line
Control-k           Delete line

Tab                 Complete next
Control-i           Complete next
Mod1-l              Complete next

Mod1-p	            Paste PRIMARY

Shift-Tab           Complete prev
Control-Shift-i     Complete prev
Mod1-h              Complete prev

Prior               Complete prevpage
Mod1-k              Complete prevpage
Next                Complete nextpage
Mod1-j              Complete nextpage
Home                Complete first
Mod1-g              Complete first
End                 Complete last
Mod1-Shift-g        Complete last

Added cmd/menu/main.c.






























































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
/* Copyright ©2006-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#define EXTERN
#include "dat.h"
#include <X11/Xproto.h>
#include <locale.h>
#include <strings.h>
#include <unistd.h>
#include <bio.h>
#include <stuff/clientutil.h>
#include "fns.h"
#define link _link

static const char version[] = "wimenu-"VERSION", "COPYRIGHT"\n";
static Biobuf*	cmplbuf;
static Biobuf*	inbuf;
static bool	alwaysprint;
static char*	cmdsep;
static int	screen_hint;

static void
usage(void) {
	fprint(2, "usage: %s -i [-a <address>] [-h <history>] [-p <prompt>] [-r <rows>] [-s <screen>]\n", argv0);
	fprint(2, "       See manual page for full usage details.\n");
	exit(1);
}

static int
errfmt(Fmt *f) {
	return fmtstrcpy(f, ixp_errbuf());
}

static inline void
splice(Item *i) {
	i->next->prev = i->prev;
	i->prev->next = i->next;
}
static inline void
link(Item *i, Item *j) {
	i->next = j;
	j->prev = i;
}

static Item*
populate_list(Biobuf *buf, bool hist) {
	Item ret;
	Item *i;
	char *p;
	bool stop;

	stop = !hist && !isatty(buf->fid);
	ret.next_link = nil;
	i = &ret;
	while((p = Brdstr(buf, '\n', true))) {
		if(stop && p[0] == '\0')
			break;
		i->next_link = emallocz(sizeof *i);
		i = i->next_link;
		i->string = p;
		i->retstring = p;
		if(cmdsep && (p = strstr(p, cmdsep))) {
			*p = '\0';
			i->retstring = p + strlen(cmdsep);
		}
		if(!hist) {
			i->len = strlen(i->string);
			i->width = textwidth_l(font, i->string, i->len) + itempad;
			match.maxwidth = max(i->width, match.maxwidth);
		}
	}

	return ret.next_link;
}

static void
check_competions(IxpConn *c) {
	char *s;

	s = Brdstr(cmplbuf, '\n', true);
	if(!s) {
		ixp_hangup(c);
		return;
	}
	input.filter_start = strtol(s, nil, 10);
	match.all = populate_list(cmplbuf, false);
	update_filter(false);
	menu_draw();
}

Item*
filter_list(Item *i, char *filter) {
	static Item exact;
	Item start, substr;
	Item *exactp, *startp, *substrp;
	Item **ip;
	char *p;
	int len;

	len = strlen(filter);
	exactp = &exact;
	startp = &start;
	substrp = &substr;
	for(; i; i=i->next_link)
		if((p = find(i->string, filter))) {
			ip = &substrp;
			if(p == i->string)
				if(strlen(p) == len)
					ip = &exactp;
				else
					ip = &startp;
			link(*ip, i);
			*ip = i;
		}

	link(substrp, &exact);
	link(startp,  &substr);
	link(exactp,  &start);
	splice(&substr);
	splice(&start);
	splice(&exact);
	return exact.next;
}

void
update_input(void) {
	if(alwaysprint) {
		write(1, input.string, input.pos - input.string);
		write(1, "\n", 1);
		write(1, input.pos, input.end - input.pos);
		write(1, "\n", 1);
	}
}

void
update_filter(bool print) {
	char *filter;

	filter = input.string + min(input.filter_start, input.pos - input.string);
	if(input.pos < input.end)
		filter = freelater(estrndup(filter, input.pos - filter));

	match.sel = nil;
	match.first = match.start = filter_list(match.all, filter);
	if(print)
		update_input();
}

enum { PointerScreen = -1 };

void
init_screens(void) {
	Rectangle *rects;
	Point p;
	int i, n;

	rects = xinerama_screens(&n);
	if(screen_hint >= 0 && screen_hint < n)
		i = screen_hint;
	else {
		/* Pick the screen with the pointer, for now. Later,
		 * try for the screen with the focused window first.
		 */
		p = querypointer(&scr.root);
		for(i=0; i < n; i++)
			if(rect_haspoint_p(rects[i], p))
				break;
		if(i == n)
			i = 0;
	}
	scr.rect = rects[i];
	menu_show();
}

ErrorCode ignored_xerrors[] = {
	{ 0, BadWindow },
	{ X_GetAtomName, BadAtom },
};

int
main(int argc, char *argv[]) {
	static char *address;
	static char *histfile;
	static char *keyfile;
	static bool nokeys;
	Item *item;
	int i;
	long ndump;

	setlocale(LC_ALL, "");
	fmtinstall('r', errfmt);
	quotefmtinstall();

	screen_hint = PointerScreen;

	find = strstr;
	compare = strncmp;

	ndump = -1;

	ARGBEGIN{
	case 'a':
		address = EARGF(usage());
		break;
	case 'c':
		alwaysprint = true;
		break;
	case 'h':
		histfile = EARGF(usage());
		break;
	case 'i':
		find = strcasestr;
		compare = strncasecmp;
		break;
	case 'K':
		nokeys = true;
	case 'k':
		keyfile = EARGF(usage());
		break;
	case 'n':
		ndump = strtol(EARGF(usage()), nil, 10);
		break;
	case 'p':
		menu.prompt = EARGF(usage());
		break;
	case 'r':
		menu.rows = strtol(EARGF(usage()), nil, 10);
		break;
	case 's':
		screen_hint = strtol(EARGF(usage()), nil, 10);
		break;
	case 'S':
		cmdsep = EARGF(usage());
		break;
	case 'v':
		lprint(1, "%s", version);
		return 0;
	default:
		usage();
	}ARGEND;

	if(argc)
		usage();

	initdisplay();

	xext_init();
	if(!isatty(0))
		menu_init();

	client_init(address);

	srv.preselect = event_preselect;
	ixp_listen(&srv, ConnectionNumber(display), nil, event_fdready, event_fdclosed);

	menu.ontop = !strcmp(readctl("/ctl", "bar "), "on top");
	client_readconfig(&cnorm, &csel, &font);

	itempad = (font->height & ~1) + font->pad.min.x + font->pad.max.x;

	cmplbuf = Bfdopen(0, OREAD);
	match.all = populate_list(cmplbuf, false);
	if(!isatty(cmplbuf->fid))
		ixp_listen(&srv, cmplbuf->fid, inbuf, check_competions, nil);

	caret_insert("", true);
	update_filter(false);

	if(!nokeys)
		parse_keys(binding_spec);
	if(keyfile) {
		i = open(keyfile, O_RDONLY);
		if(read(i, buffer, sizeof(buffer)) > 0)
			parse_keys(buffer);
	}

	histsel = &hist;
	link(&hist, &hist);
	if(histfile && (inbuf = Bopen(histfile, OREAD))) {
		item = filter_list(populate_list(inbuf, true), "");
		if(item->string) {
			link(item->prev, &hist);
			link(&hist, item);
		}
		Bterm(inbuf);
	}

	if(menu.win == nil)
		menu_init();

	init_screens();

	i = ixp_serverloop(&srv);
	if(i)
		fprint(2, "%s: error: %r\n", argv0);
	XCloseDisplay(display);

	if(ndump >= 0 && histfile && result == 0)
		history_dump(histfile, ndump);

	return result;
}

Added cmd/menu/menu.c.












































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
#include "dat.h"
#include <ctype.h>
#include <strings.h>
#include <unistd.h>
#include "fns.h"

static Handlers	handlers;
static int	promptw;

void
menu_init(void) {
	WinAttr wa;

	wa.event_mask = ExposureMask | KeyPressMask;
	menu.win = createwindow(&scr.root, Rect(-1, -1, 1, 1), scr.depth, InputOutput,
				&wa, CWEventMask);
	if(scr.xim)
		menu.win->xic = XCreateIC(scr.xim,
					  XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
					  XNClientWindow, menu.win->xid,
					  XNFocusWindow, menu.win->xid,
					  nil);

	changeprop_long(menu.win, Net("WM_WINDOW_TYPE"), "ATOM", (long[]){ TYPE("MENU") }, 1);
	changeprop_string(menu.win, "_WMII_TAGS", "sel");
	changeprop_textlist(menu.win, "WM_CLASS", "STRING", (char*[3]){ "wimenu", "wimenu" });

	sethandler(menu.win, &handlers);
	mapwin(menu.win);

	int i = 0;
	while(!grabkeyboard(menu.win)) {
		if(i++ > 1000)
			fatal("can't grab keyboard");
		usleep(1000);
	}
}

void
menu_show(void) {
	Rectangle r;

	if(menu.prompt)
		promptw = textwidth(font, menu.prompt) + itempad;

	r = textextents_l(font, "<", 1, nil);
	menu.arrow = Pt(Dy(r) + itempad/2, Dx(r) + itempad/2);

	menu.height = labelh(font);

	freeimage(menu.buf);
	menu.buf = allocimage(Dx(scr.rect),
			      !!menu.rows * 2 * menu.arrow.y + (menu.rows + 1) * menu.height,
			      menu.win->depth);

	mapwin(menu.win);
	raisewin(menu.win);
	menu_draw();
}

/* I'd prefer to use ⌃ and ⌄, but few fonts support them. */
static void
drawarrow(Image *img, Rectangle r, int up, Color *col) {
	Point p[3], pt;

	pt = Pt(menu.arrow.x - itempad/2, menu.arrow.y - itempad/2 & ~1);

	p[1] = Pt(r.min.x + Dx(r)/2,	up ? r.min.y + itempad/4 : r.max.y - itempad/4);
	p[0] = Pt(p[1].x - pt.x/2,	up ? p[1].y + pt.y	 : p[1].y - pt.y);
	p[2] = Pt(p[1].x + pt.x/2,	p[0].y);
	drawpoly(img, p, nelem(p), CapProjecting, 1, col);
}

static Rectangle
slice(Rectangle *rp, int x, int y) {
	Rectangle r;

	r = *rp;
	if(x) rp->min.x += x, r.max.x = min(rp->min.x, rp->max.x);
	if(y) rp->min.y += y, r.max.y = min(rp->min.y, rp->max.y);
	return r;
}

static bool
nextrect(Item *i, Rectangle *rp, Rectangle *src) {
	Rectangle r;

	if(menu.rows)
		r = slice(src, 0, menu.height);
	else
		r = slice(src, i->width, 0);
	return (Dx(*src) >= 0 && Dy(*src) >= 0) && (*rp = r, 1);
}

void
menu_draw(void) {
	Rectangle barr, extent, itemr, inputr, r, r2;
	Item *item;
	int inputw, offset;

	barr = r2 = Rect(0, 0, Dx(menu.win->r), menu.height);

	inputw = max(match.maxwidth + textwidth_l(font, input.string, min(input.filter_start, strlen(input.string))),
		     max(itempad    + textwidth(font, input.string),
			 Dx(barr) / 3));

	/* Calculate items box, w/ and w/o arrows */
	if(menu.rows) {
		menu.itemr = barr;
		menu.itemr.max.y += Dy(barr) * (menu.rows - 1);
		if(menu.ontop)
			menu.itemr = rectaddpt(menu.itemr, Pt(0, Dy(barr)));
		itemr = menu.itemr;
		if(match.start != match.first)
			menu.itemr = rectaddpt(menu.itemr, Pt(0, menu.arrow.y));
	}
	else {
		itemr = r2;
		slice(&itemr, inputw + promptw, 0);
		menu.itemr = Rect(itemr.min.x + menu.arrow.x, itemr.min.y,
				  itemr.max.x - menu.arrow.x, itemr.max.y);
	}

	fill(menu.buf, menu.buf->r, &cnorm.bg);

	/* Draw items */
	item = match.start, r2 = menu.itemr;
	nextrect(item, &r, &r2);
	do {
		match.end = item;
		if(item->string)
			fillstring(menu.buf, font, r, West, item->string,
				   (item == match.sel ? &csel : &cnorm), 0);
		item = item->next;
	} while(item != match.first && nextrect(item, &r, &r2));

	/* Adjust dimensions for arrows/number of items */
	if(menu.rows)
		itemr.max.y = r.max.y + (match.end->next != match.first ? menu.arrow.y : 0);
	else
		itemr.max.x = r.max.x + menu.arrow.x;
	if(menu.rows && !menu.ontop)
		barr = rectaddpt(barr, Pt(0, itemr.max.y));

	/* Draw indicators */
	if(!menu.rows && match.start != match.first)
		drawstring(menu.buf, font, itemr, West, "<", &cnorm.fg);
	if(!menu.rows && match.end->next != match.first)
		drawstring(menu.buf, font, itemr, East, ">", &cnorm.fg);

	if(menu.rows && match.start != match.first)
		drawarrow(menu.buf, itemr, 1, &cnorm.fg);
	if(menu.rows && match.end->next != match.first)
		drawarrow(menu.buf, itemr, 0, &cnorm.fg);

	/* Draw prompt */
	r2 = barr;
	if(menu.prompt)
		drawstring(menu.buf, font, slice(&r2, promptw, 0),
			   West, menu.prompt, &cnorm.fg);

	/* Border input/horizontal items */
	border(menu.buf, r2, 1, &cnorm.border);

	/* Draw input */
	inputr = slice(&r2, inputw, 0);
	drawstring(menu.buf, font, inputr, West, input.string, &cnorm.fg);

	/* Draw cursor */
	extent = textextents_l(font, input.string, input.pos - input.string, &offset);
	r2 = insetrect(inputr, 2);
	r2.min.x = inputr.min.x - extent.min.x + offset + font->pad.min.x + itempad/2 - 1;
	r2.max.x = r2.min.x + 1;
	fill(menu.buf, r2, &cnorm.border);

	/* Reshape window */
	r = scr.rect;
	if(menu.ontop)
		r.max.y = r.min.y + itemr.max.y;
	else
		r.min.y = r.max.y - barr.max.y;
	reshapewin(menu.win, r);

	/* Border window */
	r = rectsubpt(r, r.min);
	border(menu.buf, r, 1, &cnorm.border);
	copyimage(menu.win, r, menu.buf, ZP);
}

static Item*
pagestart(Item *i) {
	Rectangle r, r2;

	r = menu.itemr;
	nextrect(i, &r2, &r);
	while(i->prev != match.first->prev && nextrect(i->prev, &r2, &r))
		i = i->prev;
	return i;
}

static void
selectitem(Item *i) {
	if(i != match.sel) {
		caret_set(input.filter_start, input.pos - input.string);
		caret_insert(i->string, 0);
		match.sel = i;
		if(i == match.start->prev)
			match.start = pagestart(i);
		if(i == match.end->next)
			match.start = i;
	}
}

static void
paste(void *aux, char *str) {
	if(str)
		caret_insert(str, false);
	menu_draw();
}

static bool
kdown_event(Window *w, void *aux, XKeyEvent *e) {
	char **action, **p;
	char *key;
	char buf[128];
	int num, status;
	KeySym ksym;

	if(XFilterEvent((XEvent*)e, w->xid))
		return false;

	status = XLookupBoth;
	if(w->xic)
		num = Xutf8LookupString(w->xic, e, buf, sizeof buf - 1, &ksym, &status);
	else
		num = XLookupString(e, buf, sizeof buf - 1, &ksym, nil);

	if(status != XLookupChars && status != XLookupKeySym && status != XLookupBoth)
		return false;

	if(status == XLookupKeySym || status == XLookupBoth) {
		key = XKeysymToString(ksym);
		if(IsKeypadKey(ksym))
			if(ksym == XK_KP_Enter)
				ksym = XK_Return;
			else if(ksym >= XK_KP_0 && ksym <= XK_KP_9)
				ksym = (ksym - XK_KP_0) + XK_0;

		if(IsFunctionKey(ksym)
		|| IsMiscFunctionKey(ksym)
		|| IsKeypadKey(ksym)
		|| IsPrivateKeypadKey(ksym)
		|| IsPFKey(ksym))
			return false;
		action = find_key(key, e->state);
	}

	if(status == XLookupChars || action == nil || action[0] == nil) {
		if(num && !iscntrl(buf[0])) {
			buf[num] = '\0';
			caret_insert(buf, false);
			update_filter(true);
			menu_draw();
		}
	}
	else {
		long mask = 0;
#		define have(val) !!(mask & (1 << val))
		for(p=action+1; *p; p++)
			mask |= 1 << getsym(*p);
		int amount = (
			have(LCHAR) ? CHAR :
			have(LWORD) ? WORD :
			have(LLINE) ? LINE :
			-1);

		switch(getsym(action[0])) {
		default:
			return false;
		case LHISTORY:
			num = input.pos - input.string;
			amount = have(LBACKWARD) ? BACKWARD : FORWARD;
			caret_insert(history_search(amount, input.string, num), true);
			input.pos = input.string + num;
			update_filter(true);
			break;
		case LKILL:
			caret_delete(BACKWARD, amount);
			update_filter(true);
			break;
		case LDELETE:
			caret_delete(FORWARD, amount);
			update_filter(true);
			break;

		case LACCEPT:
			srv.running = false;
			if(!have(LLITERAL) && !match.sel && match.start->retstring)
				if(input.filter_start == 0 && input.pos == input.end)
					selectitem(match.start);

			if(!have(LLITERAL) && match.sel && !strcmp(input.string, match.sel->string))
				lprint(1, "%s", match.sel->retstring);
			else
				lprint(1, "%s", input.string);
			break;
		case LBACKWARD:
			caret_move(BACKWARD, amount);
			update_input();
			break;
		case LCOMPLETE:
			if(have(LNEXT))
				selectitem(match.sel ? match.sel->next : match.first);
			else if(have(LPREV))
				selectitem((match.sel ? match.sel : match.start)->prev);
			else if(have(LFIRST)) {
				match.start = match.first;
				selectitem(match.start);
			}
			else if(have(LLAST))
				selectitem(match.first->prev);
			else if(have(LNEXTPAGE))
				selectitem(match.end->next);
			else if(have(LPREVPAGE)) {
				match.start = pagestart(match.start->prev);
				selectitem(match.start);
			}
			break;
		case LFORWARD:
			caret_move(FORWARD, amount);
			update_input();
			break;
		case LPASTE:
			getselection(action[1] ? action[1] : "PRIMARY", paste, nil);
			break;
		case LREJECT:
			srv.running = false;
			result = 1;
			break;
		}
		menu_draw();
	}
	return false;
}

static bool
expose_event(Window *w, void *aux, XExposeEvent *e) {

	USED(w);
	menu_draw();
	return false;
}

static Handlers handlers = {
	.expose = expose_event,
	.kdown = kdown_event,
};

Added cmd/strut/Makefile.






































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
ROOT= ../..
include $(ROOT)/mk/hdr.mk
include $(ROOT)/mk/wmii.mk

main.c: $(ROOT)/mk/wmii.mk

TARG =	wistrut
HFILES=	dat.h fns.h

PACKAGES += $(X11PACKAGES)

LIB = $(LIBS9)
LIBS += -lm
OBJ =	main	\
	ewmh	\
	win

include $(ROOT)/mk/one.mk

Added cmd/strut/dat.h.


































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <fmt.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdlib.h>
#include <unistd.h>
#include <stuff/x.h>
#include <stuff/util.h>

#ifndef EXTERN
# define EXTERN extern
#endif

enum { DAuto, DHorizontal, DVertical };

EXTERN Handlers	handlers;
EXTERN int	direction;

Added cmd/strut/ewmh.c.






















































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "dat.h"
#include <limits.h>
#include <string.h>
#include "fns.h"

enum {
	Left, Right, Top, Bottom,
	LeftMin, LeftMax,
	RightMin, RightMax,
	TopMin, TopMax,
	BottomMin, BottomMax,
	Last
};

void
ewmh_getstrut(Window *w, Rectangle struts[4]) {
	long *strut;
	ulong n;

	memset(struts, 0, sizeof struts);

	n = getprop_long(w, Net("WM_STRUT_PARTIAL"), "CARDINAL",
		0L, &strut, Last);
	if(n != Last) {
		free(strut);
		n = getprop_long(w, Net("WM_STRUT"), "CARDINAL",
			0L, &strut, 4L);
		if(n != 4) {
			free(strut);
			return;
		}
		strut = erealloc(strut, Last * sizeof *strut);
		strut[LeftMin] = strut[RightMin] = 0;
		strut[LeftMax] = strut[RightMax] = INT_MAX;
		strut[TopMin] = strut[BottomMin] = 0;
		strut[TopMax] = strut[BottomMax] = INT_MAX;
	}
	struts[Left] =   Rect(0,                strut[LeftMin],  strut[Left],      strut[LeftMax]);
	struts[Right] =  Rect(-strut[Right],    strut[RightMin], 0,                strut[RightMax]);
	struts[Top] =    Rect(strut[TopMin],    0,               strut[TopMax],    strut[Top]);
	struts[Bottom] = Rect(strut[BottomMin], -strut[Bottom],  strut[BottomMax], 0);
	free(strut);
}

void
ewmh_setstrut(Window *w, Rectangle struts[4]) {
	long strut[Last];
	int i;

	strut[LeftMin] = struts[Left].min.y;
	strut[Left] = struts[Left].max.x;
	strut[LeftMax] = struts[Left].max.y;

	strut[RightMin] = struts[Right].min.y;
	strut[Right] = -struts[Right].min.x;
	strut[RightMax] = struts[Right].max.y;

	strut[TopMin] = struts[Top].min.x;
	strut[Top] = struts[Top].max.y;
	strut[TopMax] = struts[Top].max.x;

	strut[BottomMin] = struts[Bottom].min.x;
	strut[Bottom] = -struts[Bottom].min.y;
	strut[BottomMax] = struts[Bottom].max.x;

	for(i=0; i<Last; i++)
		if(strut[i] < 0)
			strut[i] = 0;

	changeprop_long(w, Net("WM_STRUT_PARTIAL"), "CARDINAL", strut, nelem(strut));
}

Added cmd/strut/fns.h.












>
>
>
>
>
>
1
2
3
4
5
6

void	restrut(Window*);

void	ewmh_getstrut(Window*, Rectangle[4]);
void	ewmh_setstrut(Window*, Rectangle[4]);

Added cmd/strut/main.c.


























































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
/* Copyright ©2006-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#define EXTERN
#include "dat.h"
#include <X11/Xproto.h>
#include <locale.h>
#include <string.h>
#include <time.h>
#include "fns.h"

static Window*	testwin;
static ulong	testtime[2];

static const char version[] = "wistrut-"VERSION", "COPYRIGHT"\n";

static void manage(ulong);

static void
usage(void) {
	fatal("usage: %s [-HV] <window|class>...\n", argv0);
}

static void
search_wins(char *pattern) {
	ulong *wins;
	ulong n, num;
	int i;
	char **class;
	Reprog *regexp;
	Window *win;

	regexp = regcomp(pattern);

	num = getprop_ulong(&scr.root, "_NET_CLIENT_LIST", "WINDOW", 0L, &wins, 1024L);
	for(i = 0; i < num; i++) {
		win = window(wins[i]);

		n = getprop_textlist(win, "WM_CLASS", &class);
		bufclear();
		bufprint("%s:%s:%s",
			 (n > 0 ? class[0] : "<nil>"),
			 (n > 1 ? class[1] : "<nil>"),
			 freelater(windowname(win)));
		freestringlist(class);
		if(regexec(regexp, buffer, nil, 0))
			manage(wins[i]);
	}
	free(wins);
}

static Window
findframe(Window *w) {
	XWindow *children;
	XWindow xw, par, root;
	Window ret = {0, };
	uint n;

	xw = w->xid;
	for(par=w->xid; par != scr.root.xid; ) {
		xw = par;
		XQueryTree(display, xw, &root, &par, &children, &n);
		XFree(children);
	}
	ret.type = WWindow;
	ret.xid = xw;
	ret.parent = &scr.root;
	return ret;
}

static void
getwinsize(Window *win) {
	int x, y;
	uint w, h;
	XWindow root;
	uint border, depth;

	XGetGeometry(display, win->xid, &root,
		     &x, &y, &w, &h,
		     &border, &depth);
	win->r = rectaddpt(Rect(0, 0, w, h),
			   Pt(x+border, y+border));
}

static bool
managable(ulong xid) {
	ulong *ret;
	ulong n;
	bool retval;

	n = getprop_ulong(window(xid), "_WMII_STRUT", "WINDOW", 0L, &ret, 1L);
	if(n < 0)
		retval = true;
	else {
		if(ret[0] == xid)
			retval = ret[0] != testtime[0] || ret[1] != testtime[1];
		else
			retval = managable(ret[0]);
	}
	free(ret);
	return retval;
}

static void
manage(ulong xid) {
	Window *frame;
	Window *win;

	if(!managable(xid))
		return;

	win = emallocz(sizeof *win);
	frame = emalloc(sizeof *frame);

	win->type = WWindow;
	win->xid = xid;
	*frame = findframe(win);
	frame->aux = win;

	getwinsize(frame);
	restrut(frame);
	sethandler(frame, &handlers);
	selectinput(frame, StructureNotifyMask);

	changeprop_ulong(frame, "_WMII_STRUT", "WINDOW", testtime, nelem(testtime));
}

int
main(int argc, char *argv[]) {
	ulong win;
	char *s;

	setlocale(LC_CTYPE, "");
	fmtinstall('E', fmtevent);

	ARGBEGIN{
	case 'H':
		direction = DHorizontal;
		break;
	case 'V':
		direction = DVertical;
		break;
	case 'v':
		lprint(1, "%s", version);
		return 0;
	default:
		usage();
	}ARGEND;

	initdisplay();

	testwin = createwindow(&scr.root, Rect(0, 0, 1, 1), 0,
			    InputOnly, nil, 0);
	testtime[0] = testwin->xid;
	testtime[1] = time(nil);

	while(argc) {
		s = ARGF();
		if(getulong(s, &win))
			manage(win);
		else
			search_wins(s);
	}

	changeprop_ulong(testwin, "_WMII_STRUT", "WINDOW", testtime, nelem(testtime));

	if(windowmap.nmemb > 0)
		event_loop();

	XCloseDisplay(display);
	return 0;
}

Added cmd/strut/win.c.
















































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
/* Copyright ©2008-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "dat.h"
#include <string.h>
#include "fns.h"

void
restrut(Window *frame) {
	enum { Left, Right, Top, Bottom };
	Rectangle strut[4];
	Rectangle r;

	r = frame->r;
	memset(strut, 0, sizeof strut);
	if(Dx(r) < Dx(scr.rect)/2 && direction != DVertical) {
		if(r.min.x <= scr.rect.min.x) {
			strut[Left] = r;
			strut[Left].min.x = 0;
			strut[Left].max.x -= scr.rect.min.x;
		}
		if(r.max.x >= scr.rect.max.x) {
			strut[Right] = r;
			strut[Right].min.x -= scr.rect.max.x;
			strut[Right].max.x = 0;
		}
	}
	if(Dy(r) < Dy(scr.rect)/2 && direction != DHorizontal) {
		if(r.min.y <= scr.rect.min.y) {
			strut[Top] = r;
			strut[Top].min.y = 0;
			strut[Top].max.y -= scr.rect.min.y;
		}
		if(r.max.y >= scr.rect.max.y) {
			strut[Bottom] = r;
			strut[Bottom].min.y -= scr.rect.max.y;
			strut[Bottom].max.y = 0;
		}
	}

	/* Choose the struts which take up the least space.
	 * Not ideal.
	 */
	if(Dy(strut[Top])) {
		if(Dx(strut[Left]))
			if(Dy(strut[Top]) < Dx(strut[Left]))
				strut[Left] = ZR;
			else
				strut[Top] = ZR;
		if(Dx(strut[Right]))
			if(Dy(strut[Top]) < Dx(strut[Right]))
				strut[Right] = ZR;
			else
				strut[Top] = ZR;
	}
	if(Dy(strut[Bottom])) {
		if(Dx(strut[Left]))
			if(Dy(strut[Bottom]) < Dx(strut[Left]))
				strut[Left] = ZR;
			else
				strut[Bottom] = ZR;
		if(Dx(strut[Right]))
			if(Dy(strut[Bottom]) < Dx(strut[Right]))
				strut[Right] = ZR;
			else
				strut[Bottom] = ZR;
	}

#if 0
#define pstrut(name) \
	if(!eqrect(strut[name], ZR)) \
		fprint(2, "strut["#name"] = %R\n", strut[name])
	pstrut(Left);
	pstrut(Right);
	pstrut(Top);
	pstrut(Bottom);
#endif

	ewmh_setstrut(frame->aux, strut);
}

static bool
config_event(Window *frame, void *aux, XConfigureEvent *ev) {

	frame->r = rectaddpt(Rect(ev->x, ev->y, ev->width, ev->height),
			     Pt(ev->border_width, ev->border_width));
	restrut(frame);
	return false;
}

static bool
destroy_event(Window *w, void *aux, XDestroyWindowEvent *ev) {

	USED(ev);
	sethandler(w, nil);
	event_looprunning = windowmap.nmemb > 0;
	return false;
}

Handlers handlers = {
	.config = config_event,
	.destroy = destroy_event,
};

Added cmd/tray/Makefile.














































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
ROOT= ../..
include $(ROOT)/mk/hdr.mk
include $(ROOT)/mk/wmii.mk

main.c: $(ROOT)/mk/wmii.mk

TARG =	witray
HFILES=	dat.h fns.h selection.h

PACKAGES += $(X11PACKAGES)

LIB = $(LIBS9) $(LIBIXP)
LIBS += -lm
OBJ = \
	client	\
	ewmh	\
	main	\
	selection	\
	tray	\
	xembed

include $(ROOT)/mk/one.mk

Added cmd/tray/client.c.




























































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
/* Copyright ©2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "dat.h"
#include "fns.h"
#include <string.h>

static Handlers	handlers;

static void client_cleanup(XEmbed*);

void
client_manage(XWindow w) {
	Client **cp;
	Client *c;
	WinAttr wa;
	int size;

	c = emallocz(sizeof *c);
	c->w.type = WWindow;
	c->w.xid = w;
	c->w.aux = c;

	Dprint("client_manage(%W)\n", &c->w);

	traperrors(true);
	XAddToSaveSet(display, w);
	c->xembed = xembed_swallow(tray.win, &c->w, client_cleanup);
	if(traperrors(false)) {
		fprint(2, "client_manage(0x%ulx): Caught error.\n", w);
		xembed_disown(c->xembed);
		return;
	}

	wa.background_pixel = pixelvalue(&scr.root, &tray.selcolors.bg);
	size = max(tray.iconsize / 4, 4);

	c->indicator = createwindow(tray.win, Rect(0, 0, size, size), scr.depth,
				    InputOutput, &wa, CWBackPixel);
	setborder(c->indicator, 1, &tray.selcolors.border);

	sethandler(&c->w, &handlers);

	for(cp=&tray.clients; *cp; cp=&(*cp)->next)
		;
	*cp = c;

	tray_update();
}

void
client_disown(Client *c) {

	Dprint("client_disown(%W)\n", &c->w);
	xembed_disown(c->xembed);
}

static void
client_cleanup(XEmbed *e) {
	Client **cp;
	Client *c;

	c = e->w->aux;
	if (c->indicator)
		destroywindow(c->indicator);

	for(cp=&tray.clients; *cp; cp=&(*cp)->next)
		if(*cp == c) {
			*cp = c->next;
			break;
		}
	cleanupwindow(&c->w);
	free(c);
	tray_update();
}

Client*
client_find(Window *w) {
	Client *c;

	for(c=tray.clients; c; c=c->next)
		if(&c->w == w)
			return c;
	return nil;
}

void
message_cancel(Client *c, long id) {
	Message *m, **mp;

	for(mp=&c->message; (m = *mp) && m->id != id; mp=&m->next)
		;

	if(m) {
		*mp = m->next;
		free(m->msg.data);
		free(m);
	}
}

bool
client_hasmessage(Client *c) {
	Message *m;

	for(m=c->message; m; m=m->next)
		if(m->msg.pos == m->msg.end)
			return true;
	return false;
}

void
client_opcode(Client *c, long message, long l1, long l2, long l3) {
	Message *m, **mp;

	Dprint("client_opcode(%p, %s, %ulx, %ulx, %ulx)\n",
	       c,
	       message == TrayRequestDock   ? "TrayRequestDock" :
	       message == TrayBeginMessage  ? "TrayBeginMessage" :
	       message == TrayCancelMessage ? "TrayCancelMessage" :
	       sxprint("%lx", message),
	       l1, l2, l3);

	if(message == TrayBeginMessage)
		message_cancel(c, l1);
	else if(message == TrayBeginMessage) {
		if(l2 > 5 * 1024) /* Don't bother with absurdly large messages. */
			return;

		m = emallocz(sizeof *m);
		m->timeout = l1;
		m->msg     = ixp_message(emallocz(l2), l2, MsgPack);
		m->id      = l3;

		/* Add the message to the end of the queue. */
		for(mp=&c->message; *mp; mp=&(*mp)->next)
			;
		*mp = m;
	}
}

void
client_message(Client *c, long type, int format, ClientMessageData* data) {
	Message *m;

	if(format == 8 && type == NET("SYSTEM_TRAY_MESSAGE_DATA")) {
		/* Append the data to the last incomplete message. */
		for(m = c->message; m && m->msg.pos >= m->msg.end; m++)
			;
		if(m) {
			memcpy(m->msg.pos, data, min(20, m->msg.end - m->msg.pos));
			m->msg.pos += min(20, m->msg.end - m->msg.pos);
		}
	}
}

static bool
config_event(Window *w, void *aux, XConfigureEvent *e) {
	Client *c;

	c = aux;
	if(false)
		movewin(c->indicator, addpt(w->r.min, Pt(1, 1)));
	return false;
}

static bool
configreq_event(Window *w, void *aux, XConfigureRequestEvent *e) {

	Dprint("configreq_event(%W)\n", w);
	/* This seems, sadly, to be necessary. */
	tray_update();
	return false;
}

static bool
map_event(Window *w, void *aux, XMapEvent *e) {

	Dprint("client map_event(%W)\n", w);
	w->mapped = true;
	tray_update();
	return false;
}

static bool
unmap_event(Window *w, void *aux, XUnmapEvent *e) {

	Dprint("client map_event(%W)\n", w);
	tray_update();
	return false;
}

static bool
reparent_event(Window *w, void *aux, XReparentEvent *e) {

	Dprint("client reparent_event(%W)\n", w);
	return false;
}

static Handlers handlers = {
	.config = config_event,
	.configreq = configreq_event,
	.map = map_event,
	.unmap = unmap_event,
	.reparent = reparent_event,
};

Added cmd/tray/dat.h.


















































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#include <fmt.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdlib.h>
#include <unistd.h>
#include <ixp.h>
#include <stuff/x.h>
#include <stuff/util.h>
#include "selection.h"

#ifndef EXTERN
# define EXTERN extern
#endif

enum { OAuto, OHorizontal, OVertical };

enum XEmbedFlags {
	XEmbedMapped = (1 << 0),
};

enum TrayOpcodes {
	TrayRequestDock,
	TrayBeginMessage,
	TrayCancelMessage,
};

typedef struct Client		Client;
typedef struct Message		Message;
typedef struct XEmbed		XEmbed;

struct Client {
	Client*		next;
	XEmbed*		xembed;
	Window		w;
	Window*		indicator;
	Message*	message;
};

struct Message {
	Message*	next;
	long		id;
	ulong		timeout;
	IxpMsg		msg;
};

struct XEmbed {
	Window*	w;
	Window*	owner;
	void	(*cleanup)(XEmbed*);
	int	version;
	ulong	flags;
};

EXTERN IxpServer	srv;
EXTERN char**		program_args;
EXTERN int		debug;

EXTERN struct {
	Window*		win;
	Image*		pixmap;
	Client*		clients;
	Selection*	selection;
	char*		tags;
	Rectangle	r;
	ulong		iconsize;
	ulong		padding;
	long		edge;
	int		orientation;
	Font*		font;
	CTuple		selcolors;
	CTuple		normcolors;
} tray;

Added cmd/tray/ewmh.c.


























































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "dat.h"
#include <limits.h>
#include <string.h>
#include "fns.h"

enum {
	Left, Right, Top, Bottom,
	LeftMin, LeftMax,
	RightMin, RightMax,
	TopMin, TopMax,
	BottomMin, BottomMax,
	Last
};

void
ewmh_setstrut(Window *w, Rectangle struts[4]) {
	long strut[Last];
	int i;

	strut[LeftMin] = struts[Left].min.y;
	strut[Left] = struts[Left].max.x;
	strut[LeftMax] = struts[Left].max.y;

	strut[RightMin] = struts[Right].min.y;
	strut[Right] = -struts[Right].min.x;
	strut[RightMax] = struts[Right].max.y;

	strut[TopMin] = struts[Top].min.x;
	strut[Top] = struts[Top].max.y;
	strut[TopMax] = struts[Top].max.x;

	strut[BottomMin] = struts[Bottom].min.x;
	strut[Bottom] = -struts[Bottom].min.y;
	strut[BottomMax] = struts[Bottom].max.x;

	for(i=0; i<Last; i++)
		if(strut[i] < 0)
			strut[i] = 0;

	changeprop_long(w, Net("WM_STRUT_PARTIAL"), "CARDINAL", strut, nelem(strut));
}

Added cmd/tray/fns.h.












































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

void	cleanup(Selection*);
Client*	client_find(Window*);
bool	client_hasmessage(Client*);
void	client_disown(Client*);
void	client_manage(XWindow);
void	client_message(Client*, long, int, ClientMessageData*);
void	client_opcode(Client*, long, long, long, long);
void	ewmh_setstrut(Window*, Rectangle[4]);
int	main(int, char*[]);
void	message(Selection*, XClientMessageEvent*);
void	message_cancel(Client*, long);
void	restrut(Window*, int);
void	tray_init(void);
void	tray_resize(Rectangle);
void	tray_update(void);
void	xembed_disown(XEmbed*);
XEmbed*	xembed_swallow(Window*, Window*, void (*)(XEmbed*));

#define Debug if(debug)
#define Dprint Debug print

Added cmd/tray/main.c.




















































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
/* Copyright ©2006-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#define EXTERN
#include "dat.h"
#include <X11/Xproto.h>
#include <locale.h>
#include <stdio.h>
#include <string.h>
#include <stuff/clientutil.h>
#include <sys/signal.h>
#include "fns.h"

static const char version[] = "witray-"VERSION", "COPYRIGHT"\n";

static int	exitsignal;
static struct sigaction	sa;

static void
usage(void) {
	fprint(2, "usage: %s [-a <address>] [-NESW] [-HVn] [-p <padding>] [-s <iconsize>] [-t tags]\n"
	          "       %s -v\n", argv0, argv0);
	exit(1);
}

static int
errfmt(Fmt *f) {
	return fmtstrcpy(f, ixp_errbuf());
}

static void
cleanup_handler(int signal) {
	sa.sa_handler = SIG_DFL;
	sigaction(signal, &sa, nil);

	selection_release(tray.selection);
	srv.running = false;

	switch(signal) {
	case SIGINT:
	case SIGTERM:
		sa.sa_handler = cleanup_handler;
		sigaction(SIGALRM, &sa, nil);
		alarm(1);
	default:
		exitsignal = signal;
		break;
	case SIGALRM:
		raise(SIGTERM);
	}
}

static void
init_traps(void) {

	sa.sa_flags = 0;
	sa.sa_handler = cleanup_handler;
	sigaction(SIGINT, &sa, nil);
	sigaction(SIGTERM, &sa, nil);
	sigaction(SIGQUIT, &sa, nil);
	sigaction(SIGHUP, &sa, nil);
	sigaction(SIGUSR1, &sa, nil);
	sigaction(SIGUSR2, &sa, nil);
}

void
cleanup(Selection *s) {
	USED(s);

	while(tray.clients)
		client_disown(tray.clients);
	tray.selection = nil;
	srv.running = false;
}

void
message(Selection *s, XClientMessageEvent *ev) {
	Window *w;
	Client *c;

	USED(s);

	Dprint("message(%A) 0x%lx\n", ev->message_type, ev->window);
	Dprint("\t0x%lx, 0x%lx, 0x%ulx, 0x%ulx, 0x%ulx\n",
	       ev->data.l[0], ev->data.l[1], ev->data.l[2], ev->data.l[3], ev->data.l[4]);

	w = findwin(ev->window);
	if(w == nil)
		return;

	if(w == tray.selection->owner) {
		if(ev->message_type == NET("SYSTEM_TRAY_OPCODE") && ev->format == 32)
			if(ev->data.l[1] == TrayRequestDock)
				client_manage(ev->data.l[2]);
		return;
	}

	if((c = client_find(w))) {
		if(ev->message_type == NET("SYSTEM_TRAY_OPCODE") && ev->format == 32)
			client_opcode(w->aux, ev->data.l[1], ev->data.l[2], ev->data.l[3], ev->data.l[4]);
		else
			client_message(w->aux, ev->message_type, ev->format, (ClientMessageData*)&ev->data);
		return;
	}
}

ErrorCode ignored_xerrors[] = {
	{ 0, BadWindow },
	{ X_GetAtomName, BadAtom },
};

int
main(int argc, char *argv[]) {
	static char* address;
	bool steal;

	program_args = argv;

	setlocale(LC_CTYPE, "");
	fmtinstall('r', errfmt);
	fmtinstall('E', fmtevent);

	steal = true;
	tray.orientation = OHorizontal;
	tray.tags = "/./";
	tray.padding = 1;

	ARGBEGIN{
	case 'N':
		tray.edge = (tray.edge & ~South) | North;
		break;
	case 'S':
		tray.edge = (tray.edge & ~North) | South;
		break;
	case 'E':
		tray.edge = (tray.edge & ~West) | East;
		break;
	case 'W':
		tray.edge = (tray.edge & ~East) | West;
		break;
	case 'H':
		tray.orientation = OHorizontal;
		break;
	case 'V':
		tray.orientation = OVertical;
		break;
	case 'n':
		steal = false;
		break;
	case 'p':
		if(!getulong(EARGF(usage()), &tray.padding))
			usage();
		tray.padding = max(1, min(10, (int)tray.padding));
		break;
	case 's':
		if(!getulong(EARGF(usage()), &tray.iconsize))
			usage();
		tray.iconsize = max(1, (int)tray.iconsize);
		break;
	case 't':
		tray.tags = EARGF(usage());
		break;
	case 'a':
		address = EARGF(usage());
		break;
	case 'D':
		debug++;
		break;
	case 'v':
		lprint(1, "%s", version);
		return 0;
	default:
		usage();
	}ARGEND;

	if(argc)
		usage();

	init_traps();
	initdisplay();

	srv.preselect = event_preselect;
	ixp_listen(&srv, ConnectionNumber(display), nil, event_fdready, event_fdclosed);

	event_updatextime();
	tray.selection = selection_manage(sxprint(Net("SYSTEM_TRAY_S%d"), scr.screen),
					  event_xtime, message, cleanup, steal);
	if(tray.selection == nil)
		fatal("Another system tray is already running.");
	if(tray.selection->oldowner)
		lprint(1, "%s: Replacing currently running system tray.\n", argv0);

	xext_init();
	tray_init();

	client_init(address);

	if(tray.edge == 0)
		tray.edge = West | (!strcmp(readctl("/ctl", "bar on "), "top") ? North : South);

	client_readconfig(&tray.normcolors, &tray.selcolors, &tray.font);

	if(tray.iconsize == 0) /* Default to wmii's bar size. */
		tray.iconsize = labelh(tray.font) - 2 * tray.padding;

	srv.running = true;
	ixp_serverloop(&srv);

	if(tray.selection)
		selection_release(tray.selection);

	XCloseDisplay(display);

	if(exitsignal)
		raise(exitsignal);
	return 0;
}

Added cmd/tray/selection.c.










































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
/* Copyright ©2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "dat.h"
#include "fns.h"

static Handlers selection_handlers;
static Handlers steal_handlers;

static Selection*
_selection_create(char *selection, ulong time,
		 void (*request)(Selection*, XSelectionRequestEvent*),
		 void (*cleanup)(Selection*),
		 bool lazy) {
	Selection *s;

	if(time == 0)
		time = event_xtime;

	s = emallocz(sizeof *s);
	s->owner = createwindow(&scr.root, Rect(0, 0, 1, 1), 0,
				InputOnly, nil, 0);
	s->owner->aux = s;
	s->request = request;
	s->cleanup = cleanup;
	s->time_start = time;

	sethandler(s->owner, &selection_handlers);

	if (!lazy) {
		XSetSelectionOwner(display, xatom(selection), s->owner->xid, time);

		/*
		 * There is a race here that ICCCM doesn't mention. It's
		 * possible that we've gained and lost the selection in this
		 * time, and a client's sent us a selection request. We're
		 * required to reply to it, but since we're destroying the
		 * window, we'll never hear about it. Since ICCCM doesn't
		 * mention it, we assume that other clients behave likewise,
		 * and therefore clients must be prepared to deal with such
		 * behavior regardless.
		 */
		if(XGetSelectionOwner(display, xatom(selection)) != s->owner->xid) {
			destroywindow(s->owner);
			free(s);
			return nil;
		}
	}

	s->selection = estrdup(selection);
	return s;
}

Selection*
selection_create(char *selection, ulong time,
		 void (*request)(Selection*, XSelectionRequestEvent*),
		 void (*cleanup)(Selection*)) {
	return _selection_create(selection, time, request, cleanup, false);
}

static void
_selection_manage(Selection *s) {

	if (s->oldowner) {
		Dprint("[selection] Grabbing.\n");
		XSetSelectionOwner(display, xatom(s->selection), s->owner->xid, s->time_start);
		if(XGetSelectionOwner(display, xatom(s->selection)) != s->owner->xid) {
			selection_release(s);
			return;
		}
	}

	Dprint("[selection] Notifying.\n");
	clientmessage(&scr.root, "MANAGER", SubstructureNotifyMask|StructureNotifyMask, 32,
		      (ClientMessageData){ .l = {s->time_start, xatom(s->selection), s->owner->xid} });
}

static void
timeout(long timer, void *v) {
	Selection *s;

	s = v;
	Dprint("[selection] Done waiting. Killing 0x%ulx.\n", s->oldowner);
	s->timer = 0;
	XKillClient(display, s->oldowner);
	sync();
}

Selection*
selection_manage(char *selection, ulong time,
		 void (*message)(Selection*, XClientMessageEvent*),
		 void (*cleanup)(Selection*),
		 bool steal) {
	Selection *s;
	Window *w;
	XWindow old;

	if((old = XGetSelectionOwner(display, xatom(selection)))) {
		if (!steal)
			return nil;

		w = emallocz(sizeof *w);
		w->type = WWindow;
		w->xid = old;
		selectinput(w, StructureNotifyMask);

		/* Hack for broken Qt systray implementation. If it
		 * finds a new system tray running when the old one
		 * dies, it never selects the StructureNotify mask
		 * on it, and therefore never disassociates from it,
		 * and completely ignores any future MANAGER
		 * messages it receives.
		 */
		XSetSelectionOwner(display, xatom(selection), 0, time);
	}

	s = _selection_create(selection, time, nil, cleanup, old);
	if(s) {
		s->message = message;
		s->oldowner = old;
		if(!old)
			_selection_manage(s);
		else {
			Dprint("[selection] Waiting for old owner %W to die...\n", w);
			pushhandler(w, &steal_handlers, s);
			s->timer = ixp_settimer(&srv, 2000, timeout, s);
		}
	}

	return s;
}

void
selection_release(Selection *s) {
	if(s->cleanup)
		s->cleanup(s);
	if(!s->time_end)
		XSetSelectionOwner(display, xatom(s->selection), None, s->time_start);
	destroywindow(s->owner);
	free(s->selection);
	free(s);
}

static void
selection_notify(Selection *s, XSelectionRequestEvent *ev, bool success) {
	XSelectionEvent notify;

	notify.type = SelectionNotify;
	notify.requestor = ev->requestor;
	notify.selection = ev->selection;
	notify.target = ev->target;
	notify.property = success ? ev->property : None;
	notify.time = ev->time;

	sendevent(window(ev->requestor), false, 0L, &notify);
}

static bool
message_event(Window *w, void *aux, XClientMessageEvent *ev) {
	Selection *s;

	s = aux;
	if(s->message)
		s->message(s, ev);
	return false;
}

static bool
selectionclear_event(Window *w, void *aux, XSelectionClearEvent *ev) {
	Selection *s;

	USED(w, ev);
	Dprint("[selection] Lost selection\n");
	s = aux;
	s->time_end = ev->time;
	selection_release(s);
	return false;
}

static bool
selectionrequest_event(Window *w, void *aux, XSelectionRequestEvent *ev) {
	Selection *s;

	s = aux;
	if(ev->property == None)
		ev->property = ev->target; /* Per ICCCM §2.2. */

	Dprint("[selection] Request: %A\n", ev->target);
	if(ev->target == xatom("TIMESTAMP")) {
		/* Per ICCCM §2.6.2. */
		changeprop_ulong(window(ev->requestor),
				 atomname(ev->property), "TIMESTAMP",
				 &s->time_start, 1);
		selection_notify(s, ev, true);
		return false;
	}

	if(s->request)
		s->request(s, ev);
	else
		selection_notify(s, ev, false);
	return false;
}

static Handlers selection_handlers = {
	.message = message_event,
	.selectionclear = selectionclear_event,
	.selectionrequest = selectionrequest_event,
};

static bool
destroy_event(Window *w, void *aux, XDestroyWindowEvent *e) {
	Selection *s;

	Dprint("[selection] Old owner is dead.\n");
	s = aux;
	if(s->timer)
		ixp_unsettimer(&srv, s->timer);
	s->timer = 0;

	_selection_manage(s);
	s->oldowner = 0;
	return false;
}

static Handlers steal_handlers = {
	.destroy = destroy_event,
};

Added cmd/tray/selection.h.




































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
typedef struct Selection	Selection;

struct Selection {
	Window*	owner;
	char*	selection;
	ulong	time_start;
	ulong	time_end;
	void	(*cleanup)(Selection*);
	void	(*message)(Selection*, XClientMessageEvent*);
	void	(*request)(Selection*, XSelectionRequestEvent*);
	long	timer;
	ulong	oldowner;
};

Selection*	selection_create(char*, ulong, void (*)(Selection*, XSelectionRequestEvent*), void (*)(Selection*));
Selection*	selection_manage(char*, ulong, void (*)(Selection*, XClientMessageEvent*), void (*)(Selection*), bool);
void		selection_release(Selection*);

Added cmd/tray/tray.c.














































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
/* Copyright ©2008-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "dat.h"
#include <string.h>
#include <strings.h>
#include "fns.h"

static Handlers handlers;
static Handlers root_handlers;

void
restrut(Window *w, int orientation) {
	enum { Left, Right, Top, Bottom };
	Rectangle strut[4];
	Rectangle r;

	r = w->r;
	memset(strut, 0, sizeof strut);
	if(Dx(r) < Dx(scr.rect)/2 && orientation != OHorizontal) {
		if(r.min.x <= scr.rect.min.x) {
			strut[Left] = r;
			strut[Left].min.x = 0;
			strut[Left].max.x -= scr.rect.min.x;
		}
		if(r.max.x >= scr.rect.max.x) {
			strut[Right] = r;
			strut[Right].min.x -= scr.rect.max.x;
			strut[Right].max.x = 0;
		}
	}
	if(Dy(r) < Dy(scr.rect)/2 && orientation != OVertical) {
		if(r.min.y <= scr.rect.min.y) {
			strut[Top] = r;
			strut[Top].min.y = 0;
			strut[Top].max.y -= scr.rect.min.y;
		}
		if(r.max.y >= scr.rect.max.y) {
			strut[Bottom] = r;
			strut[Bottom].min.y -= scr.rect.max.y;
			strut[Bottom].max.y = 0;
		}
	}

#if 0
#define pstrut(name) \
	if(!eqrect(strut[name], ZR)) \
		fprint(2, "strut["#name"] = %R\n", strut[name])
	pstrut(Left);
	pstrut(Right);
	pstrut(Top);
	pstrut(Bottom);
#endif

	ewmh_setstrut(w, strut);
}

void
tray_init(void) {
	WinAttr wa;
	XWMHints hints = { 0, };

	wa.background_pixmap = None;
	wa.bit_gravity = NorthEastGravity;
	wa.border_pixel = 0;
	wa.event_mask = ExposureMask
		      | ButtonPressMask
		      | ButtonReleaseMask
		      | StructureNotifyMask
		      | SubstructureNotifyMask
		      /* Disallow clients reconfiguring themselves. */
		      | SubstructureRedirectMask;
	tray.win = createwindow(&scr.root, Rect(0, 0, 1, 1), scr.depth, InputOutput,
				       &wa, CWBackPixmap
					  | CWBitGravity
					  | CWEventMask);

	sethandler(tray.win, &handlers);
	pushhandler(&scr.root, &root_handlers, nil);
	selectinput(&scr.root, scr.root.eventmask | PropertyChangeMask);


	changeprop_string(tray.win, "_WMII_TAGS", tray.tags);

	changeprop_ulong(tray.win, "XdndAware", "ATOM", (ulong[1]){ 5 }, 1);

	changeprop_ulong(tray.selection->owner, Net("SYSTEM_TRAY_VISUAL"), "VISUALID",
			 &scr.visual->visualid, 1);
	changeprop_long(tray.win, Net("WM_WINDOW_TYPE"), "ATOM",
			(long[1]){ TYPE("DOCK") }, 1);

	changeprop_string(tray.win, Net("WM_NAME"), "witray");
	changeprop_textlist(tray.win, "WM_NAME", "STRING",
			    (char*[2]){ "witray", nil });
	changeprop_textlist(tray.win, "WM_CLASS", "STRING",
			    (char*[3]){ "witray", "witray", nil });
	changeprop_textlist(tray.win, "WM_COMMAND", "STRING", program_args);

	hints.flags = InputHint;
	hints.input = false;
	XSetWMHints(display, tray.win->xid, &hints);
	tray_resize(tray.win->r);
}

static void
tray_unmap(void) {
	unmapwin(tray.win);
	sendevent(&scr.root, false, SubstructureNotifyMask,
		  &(XUnmapEvent){
			.type = UnmapNotify,
			.event = scr.root.xid,
			.window = tray.win->xid
		  });
}

static void
tray_draw(Rectangle r) {
	int borderwidth;

	if(!tray.pixmap)
		return;

	borderwidth = 1;

	r = rectsetorigin(r, ZP);
	border(tray.pixmap, r, borderwidth, &tray.selcolors.border);
	r = insetrect(r, borderwidth);
	fill(tray.pixmap, r, &tray.selcolors.bg);
	XClearWindow(display, tray.win->xid);
}

void
tray_resize(Rectangle r) {
	WinHints hints;
	Image *oldimage;
	WinAttr wa;

	hints = ZWinHints;
	hints.position = true;
	hints.min = hints.max = Pt(Dx(r), Dy(r));
	hints.grav = Pt(tray.edge & East ? 0 :
			tray.edge & West ? 2 : 1,
			tray.edge & North ? 0 :
			tray.edge & South ? 2 : 1);
	/* Not necessary, since we specify fixed size, but... */
	// hints.base = Pt(2, 2);
	// hints.inc  = Pt(tray.iconsize, tray.iconsize);
	sethints(tray.win, &hints);

	if(!eqrect(tray.win->r, r)) {
		oldimage = tray.pixmap;

		tray.pixmap = allocimage(Dx(r), Dy(r), tray.win->depth);
		tray_draw(r);
		wa.background_pixmap = tray.pixmap->xid;
		setwinattr(tray.win, &wa, CWBackPixmap);

		freeimage(oldimage);
	}

	tray.r = r;
	tray.win->r = ZR; /* Force the configure event. */
	reshapewin(tray.win, r);
	restrut(tray.win, tray.orientation);
}

void
tray_update(void) {
	Rectangle r;
	Point p, offset, padding;
	Client *c;

	r = Rect(0, 0, tray.iconsize, tray.iconsize);
	padding = Pt(tray.padding, tray.padding);
	offset = padding;
	Dprint("tray_update()\n");
	for(c=tray.clients; c; c=c->next) {
		if(c->w.mapped) {
			reshapewin(&c->w, rectaddpt(r, offset));
			/* This seems, sadly, to be necessary. */
			sendevent(&c->w, false, StructureNotifyMask, &(XEvent){
				  .xconfigure = {
					.type = ConfigureNotify,
					.event = c->w.xid,
					.window = c->w.xid,
					.above = None,
					.x = c->w.r.min.x,
					.y = c->w.r.min.y,
					.width = Dx(c->w.r),
					.height = Dy(c->w.r),
					.border_width = 0,
				  }
			  });

			movewin(c->indicator, addpt(offset, Pt(2, 2)));
			if(tray.orientation == OHorizontal)
				offset.x += tray.iconsize + tray.padding;
			else
				offset.y += tray.iconsize + tray.padding;
		}
		if(c->w.mapped && client_hasmessage(c))
			mapwin(c->indicator);
		else
			unmapwin(c->indicator);
	}

	if(eqpt(offset, padding))
		tray_unmap();
	else {
		if(tray.orientation == OHorizontal)
			offset.y += tray.iconsize + tray.padding;
		else
			offset.x += tray.iconsize + tray.padding;

		r = Rpt(ZP, offset);
		p = subpt(scr.rect.max, r.max);
		if(tray.edge & East)
			p.x = 0;
		if(tray.edge & North)
			p.y = 0;
		tray_resize(rectaddpt(r, p));
		mapwin(tray.win);
	}

	tray_draw(tray.win->r);
}

static bool
config_event(Window *w, void *aux, XConfigureEvent *ev) {

	USED(aux);
	if(ev->send_event) {
		/*
		 * Per ICCCM §4.2.3, the window manager sends
		 * synthetic configure events in the root coordinate
		 * space when it changes the window configuration.
		 * This code assumes wmii's generous behavior of
		 * supplying all relevant members in every configure
		 * notify event.
		 */
		w->r = rectaddpt(rectsetorigin(Rect(0, 0, ev->width, ev->height),
					       Pt(ev->x, ev->y)),
				 Pt(ev->border_width, ev->border_width));
		restrut(w, tray.orientation);
	}
	return false;
}

static bool
expose_event(Window *w, void *aux, XExposeEvent *ev) {

	USED(w, aux, ev);
	tray_draw(tray.win->r);
	return false;
}

typedef struct Dnd Dnd;

struct Dnd {
	ulong	source;
	ulong	dest;
	long	data[4];
	Point	p;
	bool	have_actions;
};

static Dnd dnd;

#define Point(l) Pt((ulong)(l) >> 16, (ulong)(l) & 0xffff)
#define Long(p) ((long)(((ulong)(p).x << 16) | (ulong)(p).y))
#define sendmessage(...) BLOCK( \
		Dprint("(%W) %s 0x%ulx, 0x%ulx, 0x%ulx, 0x%ulx, 0x%ulx\n", __VA_ARGS__); \
		sendmessage(__VA_ARGS__); \
       )

static void
dnd_updatestatus(ulong dest) {
	if(dest == dnd.dest)
		return;

	if(dnd.dest && dnd.dest != ~0UL)
		sendmessage(window(dnd.dest), "XdndLeave", tray.win->xid, 0, 0, 0, 0);
	dnd.dest = dest;
	if(dest)
		sendmessage(window(dest), "XdndEnter", tray.win->xid,
			    dnd.data[0], dnd.data[1], dnd.data[2], dnd.data[3]);
	else
		sendmessage(window(dnd.source), "XdndStatus", tray.win->xid, (1<<1),
			    Long(tray.win->r.min), (Dx(tray.win->r)<<16) | Dy(tray.win->r), 0UL);
}

static void
copyprop_long(Window *src, Window *dst, char *atom, char *type, long max) {
	long *data;
	long n;

	/* Round trip. Really need to switch to XCB. */
	if((n = getprop_long(src, atom, type, 0, &data, max)))
		changeprop_long(dst, atom, type, data, n);
	free(data);
}

static void
copyprop_char(Window *src, Window *dst, char *atom, char *type, long max) {
	uchar *data;
	ulong actual, n;
	int format;

	n = getprop(src, atom, type, &actual, &format, 0, &data, max);
	if(n > 0 && format == 8 && xatom(type) == actual)
		changeprop_char(dst, atom, type, (char*)data, n);
	free(data);
}

static bool
message_event(Window *w, void *aux, XClientMessageEvent *e) {
	Client *c;
	long *l;
	Rectangle r;
	Point p;
	ulong msg;

	msg = e->message_type;
	l = e->data.l;
	Dprint("ClientMessage: %A\n", msg);
	if(e->format == 32)
		Dprint("\t0x%ulx, 0x%ulx, 0x%ulx, 0x%ulx, 0x%ulx\n",
		       l[0], l[1], l[2], l[3], l[4]);

	if(msg == xatom("XdndEnter")) {
		if(e->format != 32)
			return false;
		dnd = (Dnd){0};
		dnd.dest = ~0UL;
		dnd.source = l[0];
		bcopy(&l[1], dnd.data, sizeof dnd.data);

		copyprop_long(window(dnd.source), tray.win, "XdndSelection", "ATOM", 128);
		if(l[1] & 0x01)
			copyprop_long(window(dnd.source), tray.win, "XdndTypeList", "ATOM", 128);
		return false;
	}else
	if(msg == xatom("XdndLeave")) {
		if(e->format != 32)
			return false;
		dnd.source = 0UL;
		if(dnd.dest)
			sendmessage(window(dnd.dest), "XdndLeave", tray.win->xid, l[1], 0, 0, 0);
		return false;
	}else
	if(msg == xatom("XdndPosition")) {
		if(e->format != 32)
			return false;

		if(!dnd.have_actions && l[4] == xatom("XdndActionAsk")) {
			dnd.have_actions = true;
			copyprop_long(window(dnd.source), tray.win, "XdndActionList", "ATOM", 16);
			copyprop_char(window(dnd.source), tray.win, "XdndActionDescription", "ATOM", 16 * 32);
		}

		dnd.p = subpt(Point(l[2]), tray.win->r.min);
		for(c=tray.clients; c; c=c->next)
			if(rect_haspoint_p(c->w.r, dnd.p)) {
				dnd_updatestatus(c->w.xid);
				sendmessage(&c->w, "XdndPosition", tray.win->xid, l[1], l[2], l[3], l[4]);
				return false;
			}
		dnd_updatestatus(0UL);
		return false;
	}else
	if(msg == xatom("XdndStatus")) {
		if(e->format != 32)
			return false;
		if(l[0] != dnd.dest)
			return false;

		for(c=tray.clients; c; c=c->next)
			if(c->w.xid == dnd.dest) {
				p = Point(l[2]);
				r = Rpt(p, addpt(p, Point(l[3])));
				r = rect_intersection(r, rectaddpt(c->w.r, tray.win->r.min));

				sendmessage(window(dnd.source), "XdndStatus", tray.win->xid, l[1],
					    Long(r.min), (Dx(r)<<16) | Dy(r), l[4]);
				break;
			}
		return false;
	}else
	if(msg == xatom("XdndDrop") || msg == xatom("XdndFinished")) {
		if(e->format != 32)
			return false;

		for(c=tray.clients; c; c=c->next)
			if(c->w.xid == dnd.dest) {
				sendmessage(&c->w, atomname(msg),
					    tray.win->xid, l[1], l[2], 0L, 0L);
				break;
			}
		return false;
	}

	return true;
}

static Handlers handlers = {
	.message = message_event,
	.config = config_event,
	.expose = expose_event,
};

static bool
property_event(Window *w, void *aux, XPropertyEvent *ev) {
	if(ev->atom == NET("CURRENT_DESKTOP"))
		tray_resize(tray.r);
	Debug if(ev->atom == NET("CURRENT_DESKTOP"))
		print("property_event(_NET_CURRENT_DESKTOP)\n");
	return false;
}

static Handlers root_handlers = {
	.property = property_event,
};

Added cmd/tray/xembed.c.


























































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
/* Copyright ©2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "dat.h"
#include "fns.h"

#define DEAD ~0UL

enum { XEmbedVersion = 0 };

enum XEmbedMessage {
	XEmbedEmbeddedNotify,
	XEmbedWindowActivate,
	XEmbedWindowDeactivate,
	XEmbedRequestFocus,
	XEmbedFocusIn,
	XEmbedFocusOut,
	XEmbedFocusNext,
	XEmbedFocusPrev,
	XEmbedModalityOn = 10,
	XEmbedModalityOff,
	XEmbedRegisterAccelerator,
	XEmbedUnregisterAccelerator,
	XEmbedActivateAccelerator,
};

enum XEmbedFocusDetail {
	XEmbedFocusCurrent,
	XEmbedFocusFirst,
	XEmbedFocusLast,
};

static Handlers	handlers;

static void	xembed_updateinfo(XEmbed*);
static void	xembed_sendmessage(XEmbed*, long, long, long, long);

XEmbed*
xembed_swallow(Window *parent, Window *client, void (*cleanup)(XEmbed*)) {
	XEmbed *xembed;

	xembed = emallocz(sizeof *xembed);
	xembed->w = client;
	xembed->owner = parent;
	xembed->cleanup = cleanup;
	selectinput(client, client->eventmask | PropertyChangeMask | StructureNotifyMask);
	pushhandler(client, &handlers, xembed);

	reparentwindow(client, parent, ZP);
	xembed_updateinfo(xembed);
	xembed_sendmessage(xembed, XEmbedEmbeddedNotify, 0, parent->xid, min(XEmbedVersion, xembed->version));
	return xembed;
}

void
xembed_disown(XEmbed *xembed) {

	pophandler(xembed->w, &handlers);
	if(xembed->flags != DEAD) {
		reparentwindow(xembed->w, &scr.root, ZP);
		unmapwin(xembed->w);
	}
	if(xembed->cleanup)
		xembed->cleanup(xembed);
	free(xembed);
}

static void
xembed_updateinfo(XEmbed *xembed) {
	ulong *res;
	int n;

	n = getprop_ulong(xembed->w, "_XEMBED_INFO", "_XEMBED_INFO", 0, &res, 2);
	xembed->flags = 0UL;
	if(n >= 2) {
		xembed->version = res[0];
		xembed->flags = res[1];
	}
	else {
		/* Deal with a Qt system tray replacement bug. */
		xembed->flags = XEmbedMapped;
	}
	free(res);

	Dprint("xembed_updateinfo(0x%ulx) XEmbedMapped=%s\n",
	       xembed->w,
	       xembed->flags & XEmbedMapped ? "true" : "false");

	if(xembed->flags & XEmbedMapped)
		mapwin(xembed->w);
	else
		unmapwin(xembed->w);
}

static void
xembed_sendmessage(XEmbed *xembed, long message, long detail, long data1, long data2) {

	traperrors(true);
	sendmessage(xembed->w, "_XEMBED", event_xtime, message, detail, data1, data2);
	traperrors(false);
}

static bool
destroy_event(Window *w, void *aux, XDestroyWindowEvent *ev) {
	XEmbed *xembed;

	xembed = aux;
	xembed->flags = DEAD;
	xembed_disown(xembed);
	return false;
}

static bool
property_event(Window *w, void *aux, XPropertyEvent *ev) {
	XEmbed *xembed;

	Dprint("property_event(%W, %p, %A)\n",
	       w, aux, ev->atom);
	xembed = aux;
	if(ev->atom == xatom("_XEMBED_INFO"))
		xembed_updateinfo(xembed);
	return false;
}

static bool
reparent_event(Window *w, void *aux, XReparentEvent *ev) {
	XEmbed *xembed;

	xembed = aux;
	if(ev->parent != xembed->owner->xid) {
		xembed->flags = DEAD;
		xembed_disown(xembed);
	}
	return false;
}

static Handlers handlers = {
	.destroy = destroy_event,
	.property = property_event,
	.reparent = reparent_event,
};
Added cmd/wihack.sh.
























































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#!/bin/sh -f
unset WMII_HACK_TRANSIENT WMII_HACK_TYPE WMII_HACK_TAGS

usage() {
	echo 1>&2 Usage: \
	"$0 [-transient <window>] [-type <window_type>[,...]] [-tags <tags>] <command> [<arg> ...]"
	exit 1
}

checkarg='[ ${#@} -gt 0 ] || usage'
export WMII_HACK_TIME=$(date +%s)

while [ ${#@} -gt 0 ]
do
	case $1 in
	-transient)
		shift; eval $checkarg
		export WMII_HACK_TRANSIENT=$1
		shift;;
	-type)
		shift; eval $checkarg
		export WMII_HACK_TYPE=$1
		shift;;
	-tags)
		shift; eval $checkarg
		export WMII_HACK_TAGS=$1
		shift;;
	-*)
		usage;;
	*)
		break;;
	esac
done

eval $checkarg

if [ ! -u "`which $1`" -a ! -g "`which $1`" ]
then
	export LD_PRELOAD="@LIBDIR@/libwmii_hack.so"
else
	unset WMII_HACK_TRANSIENT WMII_HACK_TYPE WMII_HACK_TAGS
fi
exec "$@"

Added cmd/wmii.rc.rc.


















































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185

# For the time being, this file follows the lisp bracing
# convention. i.e.:
#  if(frob this) {
#    frob that
#    if(frob theother) {
#      unfrob this
#      unfrob that}}

wmiiscript=$1
wmiikeys=()
wmiikeyhelp=''

wi_newline='
'

echo Start $wmiiscript | wmiir write /event >[2]/dev/null \
	|| exit write

if(~ $scriptname '')
	scriptname=$wmiiscript

# Blech.
if(! test -x $PLAN9/bin/read)
	fn read { sh -c 'read -r x || exit 1; echo $x' }

fn wi_atexit {}
fn sigexit {
	wi_atexit
}

fn wi_fatal {
	echo $scriptname: Fatal: $*
	exit fatal
}

fn wi_notice {
	xmessage $scriptname: Notice: $*
}

fn wi_readctl { wmiir read /ctl | sed -n 's/^'$1' (.*)/\1/p' }

wmiifont=`{wi_readctl font}
wmiinormcol=`{wi_readctl normcolors}
wmiifocuscol=`{wi_readctl focuscolors}

fn wi_fnmenu {
	group=$1^Menu-$2 last=$group^_last fns=`{wi_getfuns $group} {
	shift 2
	if(! ~ $#fns 0) {
		res = `{wmii9menu -i $"($last) $fns} \
		if(! ~ $res '') {
			($last) = $res
			$group-$res $*}}}
}

fn wi_fn-p {
	rc -c 'whatis '$1 >[2]/dev/null | grep -s '^fn '
}

fn wi_proglist {
	ifs=: { wmiir proglist -- `{echo -n $*} | sort | uniq }
}

fn wi_actions {
	{	wi_proglist $WMII_CONFPATH
	 	wi_getfuns Action
	} | sort | uniq
}

fn wi_script {
	noprog=true prog=() {
		if(~ $1 -f) {
			shift
			noprog=/dev/null
		}
		prog = `{rc -c 'path=$confpath whatis '$1 >[2]/dev/null \
			| grep -v '^fn |=' || echo $noprog}
		shift; echo $prog $*}
}


fn wi_initkeys {
	ifs=() {
		wmiikeys = `{wmiir read /keys} {
			mykeys = `{comm -23 \
				<{wi_getfuns Key | sort | uniq} \
				<{echo $wmiikeys | sort | uniq}}
			{echo $wmiikeys; wi_getfuns Key} \
				| sort | uniq \
				| wmiir write /keys }}
	fn wi_atexit {
		wi_cleankeys
	}
}

fn wi_cleankeys {
	ifs=() {
		wmiikeys = `{wmiir read /keys} {
			comm -23 <{echo $wmiikeys | sort | uniq} \
				 <{echo $mykeys} \
				 | wmiir write /keys }}
}

fn wi_runcmd { @{
		rfork ns
		path=$oldpath
		if(~ $1 -t) {
			shift
			* = (wihack -tags `{wmiir read /tag/sel/ctl | sed 1q} $*) }
		fn `{env | 9 sed -n 's/^fn#([^=]+).*/\1/p'}
		mykeys=()
		if(! ~ $* '')
			eval exec $* & }
}

fn wi_getfuns {
	env | sed -n 's/^fn#'^$1^'-([^=]+).*/\1/p' | sort | uniq
}

for(i in Key Event Action '*Menu')
	fns=`{wi_getfuns $i} {
		if(! ~ $fns '')
			fn $i-^$fns}

fn wi_tags {
	wmiir ls /tag | sed 's,/,,; /^sel$/d'
}

fn wi_seltag {
	wmiir read /tag/sel/ctl | sed 1q
}

fn wi_selclient {
	wmiir read /client/sel/ctl | sed 1q
}

fn wi_nexttag {
	awk -v 'curtag='^`{wi_seltag} '
		NR==1 {first = $0}
		$0==curtag { if(getline) print $0; else print first; exit }'
}

fn wi_eventloop {
	wi_initkeys

	{
		if(~ $1 -i)
			cat
		if not
			wmiir read /event
	} | while(ifs=$wi_newline{wi_event=`{read}}) {
		ifs=$wi_newline{
			wi_arg=`{echo $wi_event | sed 's/^[^ ]+ //'}}
		* = `{echo $wi_event}
		event = $1; shift
		Event-$"event $*
	} >[2]/dev/null </dev/null
	true
}

fn Event-Key {
	Key-$1 $1
}

fn Event-Quit {
	exit
}

fn Event-Start {
       if(~ $1 $wmiiscript)
               exit
}

fn Action {
	cmd=$1 action=Action-$"cmd { shift
		if(! ~ $cmd '') {
			if(wi_fn-p $action)
				$action $*
			if not
				wi_runcmd `{wi_script $cmd} $*
		}
	}
}

Added cmd/wmii.sh.sh.




































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226

[ -z "$scriptname" ] && scriptname="$wmiiscript"
echo Start $wmiiscript | wmiir write /event 2>/dev/null ||
	exit 1

wi_newline='
'

_wi_script() {
	# Awk script to mangle key/event/action definition spec
	# into switch-case functions and lists of the cases. Also
	# generates a simple key binding help text based on KeyGroup
	# clauses and comments that appear on key lines.
	#
	# Each clause (Key, Event, Action) generates a function of the
	# same name which executes the indented text after the matching
	# clause. Clauses are selected based on the first argument passed
	# to the mangled function. Additionally, a variable is created named
	# for the plouralized version of the clause name (Keys, Events,
	# Actions) which lists each case value. These are used for actions
	# menus and to write wmii's /keys file.
	cat <<'!'
	BEGIN {
		arg[1] = "Nop"
		narg = 1;
		body = "";
		keyhelp = ""
	}
	function quote(s) {
		gsub(/'/, "'\\''", s)
		return "'" s "'"
	}
	function addevent() {
		var = arg[1] "s"
		for(i=2; i <= narg; i++) {
			if(body == "")
				delete a[arg[1],arg[i]]
			else
				a[arg[1],arg[i]] = body
			if(i == 2) {
				# There's a bug here. Can you spot it?
				gsub("[^a-zA-Z_0-9]", "_", arg[2]);
				body = sprintf("%s %s \"$@\"", arg[1], arg[2])
			}
		}
	}
	/^(Key)Group[ \t]/ {
		sub(/^[^ \t]+[ \t]+/, "")
		keyhelp = keyhelp "\n  " $0 "\n"
	}
	/^(Event|Key|Action|Menu)[ \t]/ {
		addevent()
		split($0, tmp, /[ \t]+#[ \t]*/)
		narg = split(tmp[1], arg)
		if(arg[1] == "Key" && tmp[2])
			for (i=2; i <= narg; i++)
				keyhelp = keyhelp sprintf("    %-20s %s\n",
					        arg[i], tmp[2])
		body = ""
	}
	/^[ \t]/ {
		sub(/^(        |\t)/, "")
		body = body"\n"$0
	}

	END {
		addevent()
		for(k in a) {
			split(k, b, SUBSEP)
			c[b[1]] = c[b[1]] b[2] "\n"
			if(body != "")
				d[b[1]] = d[b[1]] quote(b[2]) ")" a[k] "\n;;\n"
		}
		for(k in c)
			printf "%ss=%s\n", k, quote(c[k])
		for(k in d) {
			printf "%s() {\n", k
			printf " %s=$1; shift\n", tolower(k)
			printf "case $%s in\n%s\n*) return 1\nesac\n", tolower(k), d[k]
			printf "}\n"
		}
		print "KeysHelp=" quote(keyhelp)
	}
!
}

_wi_text() {
	eval "cat <<!
$( (test ! -t 0 && cat; for a; do eval "$a"; done) | sed '/^[ 	]/s/\([$`\\]\)/\\\1/g')
!
"
}

_wi_events=""
wi_events() {
	eval=""; [ "$1" = -e ] && eval=1 && shift
	_wi_events="$(_wi_text "$@")
$_wi_events"
	# -n "$eval" ] && printf %s "$_wi_events" | awk "$(_wi_script)" >&2
	[ -n "$eval" ] && eval "$(printf %s "$_wi_events" | awk "$(_wi_script)")"
}

wi_events <<'!'
Event Key
	Key "$@"
!

wi_fatal() {
	echo $scriptname: Fatal: $*
	exit 1
}

wi_notice() {
	xmessage $scriptname: Notice: $*
}

wi_readctl() {
	wmiir read /ctl | sed -n 's/^'$1' //p'
}

wmiifont="$(wi_readctl font)"
wmiinormcol="$(wi_readctl normcolors)"
wmiifocuscol="$(wi_readctl focuscolors)"

wi_fnmenu() {
	group="$1-$2"; shift 2
	_last="$(echo $group|tr - _)_last"
	eval "last=\"\$$_last\""
	res=$(set -- $(echo "$Menus" | awk -v "s=$group" 'BEGIN{n=length(s)}
		         substr($1,1,n) == s{print substr($1,n+2)}')
	      [ $# != 0 ] && wmii9menu -i "$last" "$@")
	if [ -n "$res" ]; then
		eval "$_last="'"$res"'
		Menu $group-$res "$@"
	fi
}

wi_proglist() {
        wmiir proglist -- $(echo $* | sed 'y/:/ /') | sort | uniq
}

wi_actions() {
	{	wi_proglist $WMII_CONFPATH
	 	echo -n "$Actions"
	} | sort | uniq
}

wi_runconf() {
	sflag=""; if [ "$1" = -s ]; then sflag=1; shift; fi
	which="$(which which)"
	prog=$(PATH="$WMII_CONFPATH" "$which" -- $1 2>/dev/null); shift
	if [ -n "$prog" ]; then
		if [ -z "$sflag" ]
		then "$prog" "$@"
		else . "$prog"
		fi
	else return 1
	fi
}

wi_script() {
	_noprog=true
	if [ "$1" = -f ]; then
		shift
		_noprog=/dev/null
	fi
	which=$(which which)
	_prog=$(PATH="$WMII_CONFPATH" $which $1 || echo $_noprog); shift
	shift; echo "$_prog $*"
}

wi_runcmd() {
	if [ "$1" = -t ]; then
		shift
		set -- wihack -tags $(wmiir read /tag/sel/ctl | sed 1q) "$*"
	fi
	eval exec "$*" &
}

wi_tags() {
	wmiir ls /tag | sed 's,/,,; /^sel$/d'
}

wi_seltag() {
	wmiir read /tag/sel/ctl | sed 1q | tr -d '\012'
}

wi_selclient() {
	wmiir read /client/sel/ctl | sed 1q | tr -d '\012'
}

wi_nexttag() {
	awk -v curtag=$(wi_seltag) '
		NR==1 {first = $0}
		$0==curtag { if(getline) print $0; else print first; exit }'
}

wi_eventloop() {
	echo "$Keys" | wmiir write /keys

	if [ "$1" = -i ]
	then cat
	else wmiir read /event
	fi |
	while read wi_event; do
		IFS="$wi_newline"
		wi_arg=$(echo "$wi_event" | sed 's/^[^ ]* //')
		unset IFS
		set -- $wi_event
		event=$1; shift
		[ "$event" = Start -a "$1" = "$wmiiscript" ] &&
			exit
		Event $event "$@"
	done
	true
}

action() {
	action=$1; shift
	if [ -n "$action" ]; then
		set +x
		Action $action "$@" \
		|| wi_runconf $action "$@"
	fi
}

Added cmd/wmii/Makefile.






















































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
ROOT= ../..
include $(ROOT)/mk/hdr.mk
include $(ROOT)/mk/wmii.mk

main.c: $(ROOT)/mk/wmii.mk

TARG     = wmii
HFILES   = dat.h fns.h
TAGFILES = dat.h

PACKAGES += $(X11PACKAGES) xext xrandr xrender xinerama

LIB = $(LIBIXP) $(LIBS9)
LIBS += -lm

OBJ =	area	\
	bar	\
	backtrace \
	client	\
	column	\
	div	\
	error	\
	event	\
	ewmh	\
	float	\
	frame	\
	fs	\
	key	\
	layout	\
	main	\
	message	\
	mouse	\
	print   \
	root	\
	rule	\
	screen	\
	stack	\
	utf	\
	view	\
	xdnd

include $(ROOT)/mk/one.mk

Added cmd/wmii/area.c.






























































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
/* Copyright ©2006-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "dat.h"
#include <math.h>
#include <limits.h>
#include "fns.h"

Client*
area_selclient(Area *a) {
	if(a && a->sel)
		return a->sel->client;
	return nil;
}

int
area_idx(Area *a) {
	View *v;
	Area *ap;
	uint i;

	v = a->view;
	if(a->floating)
		return -1;
	i = 1;
	for(ap=v->areas[a->screen]; a != ap; ap=ap->next)
		i++;
	return i;
}

static Rectangle
area_rect(void *v) {
	Area *a;

	a = v;
	return a->r;
}

Area*
area_find(View *v, Rectangle r, int dir, bool wrap) {
	static Vector_ptr vec;
	Area *a;
	int s;

	vec.n = 0;
	foreach_column(v, s, a)
		vector_ppush(&vec, a);

	return findthing(r, dir, &vec, area_rect, wrap);
}

int
afmt(Fmt *f) {
	Area *a;

	a = va_arg(f->args, Area*);
	if(a == nil)
		return fmtstrcpy(f, "<nil>");
	if(a->floating)
		return fmtstrcpy(f, "~");
	if(a->screen > 0 || (f->flags & FmtSharp))
		return fmtprint(f, "%d:%d", a->screen, area_idx(a));
	return fmtprint(f, "%d", area_idx(a));
}

char*
area_name(Area *a) {

	if(a == nil)
		return "<nil>";
	if(a->floating)
		return "~";
	return sxprint("%d", area_idx(a));
}

Area*
area_create(View *v, Area *pos, int scrn, uint width) {
	static ushort id = 1;
	int i, j;
	uint minwidth, index;
	int numcols;
	Area *a;

	assert(!pos || pos->screen == scrn);
	SET(index);
	if(v->areas) { /* Creating a column. */
		minwidth = column_minwidth();
		index = pos ? area_idx(pos) : 0;
		numcols = 0;
		for(a=v->areas[scrn]; a; a=a->next)
			numcols++;

		/* TODO: Need a better sizing/placing algorithm. */
		if(width == 0) {
			if(numcols >= 0) {
				width = view_newcolwidth(v, scrn, index);
				if(width == 0)
					width = Dx(v->r[scrn]) / (numcols + 1);
			}
			else
				width = Dx(v->r[scrn]);
		}

		if(width < minwidth)
			width = minwidth;
		minwidth = numcols * minwidth + minwidth;
		if(minwidth > Dx(v->r[scrn]))
			return nil;

		i = minwidth - Dx(v->pad[scrn]) - Dx(v->r[scrn]);
		if(i > 0 && Dx(v->pad[scrn])) {
			j = min(i/2, v->pad[scrn].min.x);
			v->pad[scrn].min.x -= j;
			v->pad[scrn].max.x += i - j;
		}

		view_scale(v, scrn, Dx(v->r[scrn]) - width);
	}

	a = emallocz(sizeof *a);
	a->view = v;
	a->screen = scrn;
	a->id = id++;
	a->floating = !v->floating;
	if(a->floating)
		a->mode = Coldefault;
	else
		a->mode = def.colmode;
	a->frame = nil;
	a->sel = nil;

	a->r = v->r[scrn];
	a->r.min.x = 0;
	a->r.max.x = width;

	if(a->floating) {
		v->floating = a;
		a->screen = -1;
	}
	else if(pos) {
		a->next = pos->next;
		a->prev = pos;
	}
	else {
		a->next = v->areas[scrn];
		v->areas[scrn] = a;
	}
	if(a->prev)
		a->prev->next = a;
	if(a->next)
		a->next->prev = a;

	if(v->sel == nil && !a->floating)
		area_focus(a);

	if(!a->floating)
		event("CreateColumn %ud\n", index);
	return a;
}

void
area_destroy(Area *a) {
	Area *newfocus;
	View *v;
	int idx;

	v = a->view;

	if(a->frame)
		die("destroying non-empty area");

	if(v->revert == a)
		v->revert = nil;
	if(v->oldsel == a)
		v->oldsel = nil;

	idx = area_idx(a);

	if(a->prev && !a->prev->floating)
		newfocus = a->prev;
	else
		newfocus = a->next;

	/* Can only destroy the floating area when destroying a
	 * view---after destroying all columns.
	 */
	assert(!a->floating || !v->areas[0]);
	if(a->prev)
		a->prev->next = a->next;
	else if(!a->floating)
		v->areas[a->screen] = a->next;
	else
		v->floating = nil;
	if(a->next)
		a->next->prev = a->prev;

	if(newfocus && v->sel == a)
		area_focus(newfocus);

	view_arrange(v);
	event("DestroyArea %d\n", idx);

	free(a);
}

void
area_moveto(Area *to, Frame *f) {
	Area *from;

	assert(to->view == f->view);

	if(f->client->fullscreen >= 0 && !to->floating)
		return;

	from = f->area;
	if(from == to)
		return;

	area_detach(f);

	/* Temporary kludge. */
	if(!to->floating
	&& to->floating != from->floating
	&& !eqrect(f->colr, ZR))
		column_attachrect(to, f, f->colr);
	else
		area_attach(to, f);
}

void
area_setsel(Area *a, Frame *f) {
	View *v;

	v = a->view;
	/* XXX: Stack. */
	for(; f && f->collapsed && f->anext; f=f->anext)
		;
	for(; f && f->collapsed && f->aprev; f=f->aprev)
		;

	if(a == v->sel && f)
		frame_focus(f);
	else
		a->sel = f;
}

void
area_attach(Area *a, Frame *f) {

	f->area = a;
	if(a->floating)
		float_attach(a, f);
	else
		column_attach(a, f);

	view_arrange(a->view);
	event("AreaAttach %s %a %#C\n", a->view->name, a, f->client);

	if(btassert("4 full", a->frame && a->sel == nil))
		a->sel = a->frame;
}

void
area_detach(Frame *f) {
	View *v;
	Area *a;

	a = f->area;
	v = a->view;

	event("AreaDetach %s %a %#C\n", v->name, a, f->client);
	if(a->floating)
		float_detach(f);
	else
		column_detach(f);

	if(v->sel->sel == nil && v->floating->sel)
	if(!v->floating->sel->client->nofocus)
		v->sel = v->floating;

	view_arrange(v);
}

void
area_focus(Area *a) {
	Frame *f;
	View *v;
	Area *old_a;

	v = a->view;
	f = a->sel;
	old_a = v->sel;

	if(!a->floating && view_fullscreen_p(v, a->screen))
		return;

	v->sel = a;
	if(!a->floating) {
		v->selcol = area_idx(a);
		v->selscreen = a->screen;
	}
	if(a != old_a)
		v->oldsel = nil;

	if(old_a && a->floating != old_a->floating) {
		v->revert = old_a;
		if(v->floating->max)
			view_update(v);
	}

	if(v == selview) {
		move_focus(old_a->sel, f);
		client_focus(f ? f->client : nil);

		if(a != old_a)
			event("AreaFocus %a\n", a);
	}
}

Added cmd/wmii/backtrace.c.














































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
/* Copyright ©2008-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <signal.h>

#include <bio.h>
#include <plan9.h>
#undef nelem
#include <stuff/util.h>
#include "debug.h"

#ifdef __linux__
# define PROGTXT "exe"
#else
# define PROGTXT "file"
#endif

static void
_backtrace(int pid, char *btarg) {
	char *proc, *spid, *gdbcmd;
	int fd[3], p[2];
	int status, cmdfd;

	gdbcmd = estrdup("/tmp/gdbcmd.XXXXXX");
	if(pipe(p) < 0)
		goto done;
	closeexec(p[0]);

	cmdfd = mkstemp(gdbcmd);
	if(cmdfd < 0)
		goto done;

	fprint(cmdfd, "bt %s\n", btarg);
	fprint(cmdfd, "detach\n");
	close(cmdfd);

	fd[0] = open("/dev/null", O_RDONLY);
	fd[1] = p[1];
	fd[2] = dup(2);

	proc = sxprint("/proc/%d/" PROGTXT, pid);
	spid = sxprint("%d", pid);
	if(spawn3l(fd, "gdb", "gdb", "-batch", "-x", gdbcmd, proc, spid, nil) < 0) {
		unlink(gdbcmd);
		goto done;
	}

	Biobuf bp;
	char *s;

	Binit(&bp, p[0], OREAD);
	while((s = Brdstr(&bp, '\n', 1))) {
		Dprint(DStack, "%s\n", s);
		free(s);
	}
	unlink(gdbcmd);

done:
	free(gdbcmd);
	kill(pid, SIGKILL);
	waitpid(pid, &status, 0);
}

void
backtrace(char *btarg) {
	int pid;

	/* Fork so we can backtrace the child. Keep this stack
	 * frame minimal, so the trace is fairly clean.
	 */
	Debug(DStack)
		switch(pid = fork()) {
		case -1:
			return;
		case 0:
			kill(getpid(), SIGSTOP);
			_exit(0);
		default:
			_backtrace(pid, btarg);
			break;
		}

}
Added cmd/wmii/bar.c.




























































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
/* Copyright ©2006-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "dat.h"
#include "fns.h"

static Handlers handlers;

#define foreach_bar(s, b) \
	for(int __bar_n=0; __bar_n < nelem((s)->bar); __bar_n++) \
		for((b)=(s)->bar[__bar_n]; (b); (b)=(b)->next)

void
bar_init(WMScreen *s) {
	WinAttr wa;

	if(s->barwin && (s->barwin->depth == 32) == s->barwin_rgba)
		return;

	s->brect = s->r;
	s->brect.min.y = s->brect.max.y - labelh(def.font);

	wa.override_redirect = 1;
	wa.event_mask = ExposureMask
		      | ButtonPressMask
		      | ButtonReleaseMask
		      | FocusChangeMask;
	if(s->barwin_rgba)
		s->barwin = createwindow_rgba(&scr.root, s->brect,
				&wa, CWOverrideRedirect
				   | CWEventMask);
	else
		s->barwin = createwindow(&scr.root, s->brect, scr.depth, InputOutput,
				&wa, CWOverrideRedirect
				   | CWEventMask);
	s->barwin->aux = s;
	xdnd_initwindow(s->barwin);
	sethandler(s->barwin, &handlers);
	if(s == screens[0])
		mapwin(s->barwin);
}

void
bar_resize(WMScreen *s) {

	s->brect = s->r;
	s->brect.min.y = s->r.max.y - labelh(def.font);
	if(s == screens[0])
		reshapewin(s->barwin, s->brect);
	else
		s->brect.min.y = s->r.max.y;
	/* FIXME: view_arrange. */
}

void
bar_setbounds(WMScreen *s, int left, int right) {
	Rectangle *r;

	if(s != screens[0])
		return;

	r = &s->brect;
	r->min.x = left;
	r->max.x = right;
	if(Dy(*r))
		reshapewin(s->barwin, *r);
}

void
bar_sety(WMScreen *s, int y) {
	Rectangle *r;
	int dy;

	r = &s->brect;

	dy = Dy(*r);
	r->min.y = y;
	r->max.y = y + dy;
	if(Dy(*r))
		reshapewin(s->barwin, *r);
}

Bar*
bar_create(Bar **bp, const char *name) {
	static uint id = 1;
	WMScreen *s, **sp;
	Bar *b;
	uint i;

	b = bar_find(*bp, name);;
	if(b)
		return b;

	b = emallocz(sizeof *b);
	b->id = id++;
	utflcpy(b->name, name, sizeof b->name);
	b->colors = def.normcolor;

	strlcat(b->buf, b->colors.colstr, sizeof b->buf);
	strlcat(b->buf, " ", sizeof b->buf);
	strlcat(b->buf, b->text, sizeof b->buf);

	SET(i);
	for(sp=screens; (s = *sp); sp++) {
		i = bp - s->bar;
		if(i < nelem(s->bar))
			break;
	}
	b->bar = i;
	b->screen = s;

	for(; *bp; bp = &bp[0]->next)
		if(strcmp(bp[0]->name, name) >= 0)
			break;
	b->next = *bp;
	*bp = b;

	return b;
}

void
bar_destroy(Bar **bp, Bar *b) {
	Bar **p;

	for(p = bp; *p; p = &p[0]->next)
		if(*p == b) break;
	*p = b->next;
	free(b);
}

void
bar_draw(WMScreen *s) {
	Bar *b, *tb, *largest, **pb;
	Image *ibuf;
	Rectangle r;
	Align align;
	uint width, tw;
	float shrink;

	/* To do: Generalize this. */

	largest = nil;
	width = 0;
	s->barwin_rgba = false;
	foreach_bar(s, b) {
		b->r.min = ZP;
		b->r.max.y = Dy(s->brect);
		b->r.max.x = (def.font->height & ~1) + def.font->pad.min.x + def.font->pad.max.x;
		if(b->text && strlen(b->text))
			b->r.max.x += textwidth(def.font, b->text);
		width += Dx(b->r);
		s->barwin_rgba += RGBA_P(b->colors);
	}

	if(width > Dx(s->brect)) { /* Not enough room. Shrink bars until they all fit. */
		foreach_bar(s, b) {
			for(pb=&largest; *pb; pb=&pb[0]->smaller)
				if(Dx(pb[0]->r) < Dx(b->r))
					break;
			b->smaller = *pb;
			*pb = b;
		}
		SET(shrink);
		tw = 0;
		for(tb=largest; tb; tb=tb->smaller) {
			width -= Dx(tb->r);
			tw += Dx(tb->r);
			shrink = (Dx(s->brect) - width) / (float)tw;
			if(tb->smaller && Dx(tb->r) * shrink < Dx(tb->smaller->r))
				continue;
			if(width + (int)(tw * shrink) <= Dx(s->brect))
				break;
		}
		if(tb)
			for(b=largest; b != tb->smaller; b=b->smaller)
				b->r.max.x *= shrink;
		width += tw * shrink;
	}

	if(s->bar[BRight])
		s->bar[BRight]->r.max.x += Dx(s->brect) - width;
	tb = nil;
	foreach_bar(s, b) {
		if(tb)
			b->r = rectaddpt(b->r, Pt(tb->r.max.x, 0));
		tb = b;
	}

	ibuf = s->barwin_rgba ? disp.ibuf32 : disp.ibuf;

	r = rectsubpt(s->brect, s->brect.min);
	fill(ibuf, r, &def.normcolor.bg);
	border(ibuf, r, 1, &def.normcolor.border);
	foreach_bar(s, b) {
		align = Center;
		if(b == s->bar[BRight])
			align = East;
		fillstring(ibuf, def.font, b->r, align, b->text, &b->colors, 1);
	}

	if(s->barwin_rgba != (s->barwin->depth == 32))
		bar_init(s);
	copyimage(s->barwin, r, ibuf, ZP);
}

Bar*
bar_find(Bar *bp, const char *name) {
	Bar *b;

	for(b=bp; b; b=b->next)
		if(!strcmp(b->name, name))
			break;
	return b;
}

static char *barside[] = {
	[BLeft]  = "Left",
	[BRight] = "Right",
};

static Bar*
findbar(WMScreen *s, Point p) {
	Bar *b;

	foreach_bar(s, b)
		if(rect_haspoint_p(b->r, p))
			return b;
	return nil;
}

static bool
bdown_event(Window *w, void *aux, XButtonPressedEvent *e) {
	WMScreen *s;
	Bar *b;

	/* Ungrab so a menu can receive events before the button is released */
	XUngrabPointer(display, e->time);
	sync();

	s = aux;
	b = findbar(s, Pt(e->x, e->y));
	if(b)
		event("%sBarMouseDown %d %s\n", barside[b->bar], e->button, b->name);
	return false;
}

static bool
bup_event(Window *w, void *aux, XButtonPressedEvent *e) {
	WMScreen *s;
	Bar *b;

	s = aux;
	b = findbar(s, Pt(e->x, e->y));
	if(b)
		event("%sBarClick %d %s\n", barside[b->bar], e->button, b->name);
	return false;
}

static Rectangle
dndmotion_event(Window *w, void *aux, Point p) {
	WMScreen *s;
	Bar *b;

	s = aux;
	b = findbar(s, p);
	if(b) {
		event("%sBarDND 1 %s\n", barside[b->bar], b->name);
		return b->r;
	}
	return ZR;
}

static bool
expose_event(Window *w, void *aux, XExposeEvent *e) {
	USED(w, e);
	bar_draw(aux);
	return false;
}

static Handlers handlers = {
	.bdown = bdown_event,
	.bup = bup_event,
	.dndmotion = dndmotion_event,
	.expose = expose_event,
};

Added cmd/wmii/client.c.














































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
/* Copyright ©2004-2006 Anselm R. Garbe <garbeam at gmail dot com>
 * Copyright ©2006-2014 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "dat.h"
#include <ctype.h>
#include <strings.h>
#include <signal.h>
#include <X11/Xatom.h>
#include "fns.h"

#define Mbsearch(k, l, cmp) bsearch(k, l, nelem(l), sizeof(*l), cmp)

static Handlers handlers;

enum {
	ClientMask = StructureNotifyMask
		   | PropertyChangeMask
		   | EnterWindowMask
		   | FocusChangeMask,
	ButtonMask = ButtonPressMask
		   | ButtonReleaseMask,
};

static Group*	group;

void
group_init(Client *c) {
	Group *g;
	long *ret;
	XWindow w;
	long n;

	w = c->w.hints->group;
	if(w == 0) {
		/* Not quite ICCCM compliant, but it seems to work. */
		n = getprop_long(&c->w, "WM_CLIENT_LEADER", "WINDOW", 0L, &ret, 1L);
		if(n == 0)
			return;
		w = *ret;
		free(ret);
	}

	for(g=group; g; g=g->next)
		if(g->leader == w)
			break;
	if(g == nil) {
		g = emallocz(sizeof *g);
		g->leader = w;
		g->next = group;
		group = g;
	}
	c->group = g;
	g->ref++;
}

void
group_remove(Client *c) {
	Group **gp;
	Group *g;

	g = c->group;
	c->group = nil;
	if(g == nil)
		return;
	if(g->client == c)
		g->client = nil;
	g->ref--;
	if(g->ref == 0) {
		for(gp=&group; *gp; gp=&gp[0]->next)
			if(*gp == g) break;
		assert(*gp == g);
		gp[0] = gp[0]->next;
		free(g);
	}
}

Client*
group_leader(Group *g) {
	Client *c;

	c = win2client(g->leader);
	if(c)
		return c;
	if(g->client)
		return g->client;
	/* Could do better. */
	for(c=client; c; c=c->next)
		if(c->frame && c->group == g)
			break;
	return c;
}

Client*
client_create(XWindow w, XWindowAttributes *wa) {
	Client **t, *c;
	char **host = nil;
	ulong *pid = nil;

	c = emallocz(sizeof *c);
	c->fullscreen = -1;
	c->border = wa->border_width;

	c->r = rectsetorigin(Rect(0, 0, wa->width, wa->height),
			     Pt(wa->x, wa->y));

	c->w.type = WWindow;
	c->w.visual = wa->visual;
	c->w.xid = w;
	c->w.r = c->r;
	c->w.aux = c;

	setborder(&c->w, 0, &(Color){0});

	client_prop(c, xatom("WM_PROTOCOLS"));
	client_prop(c, xatom("WM_TRANSIENT_FOR"));
	client_prop(c, xatom("WM_NORMAL_HINTS"));
	client_prop(c, xatom("WM_HINTS"));
	client_prop(c, xatom("WM_CLASS"));
	client_prop(c, xatom("WM_NAME"));
	client_prop(c, xatom("_MOTIF_WM_HINTS"));

	gethostname(hostname, sizeof(hostname) - 1);
	if(getprop_textlist(&c->w, "WM_CLIENT_MACHINE", &host) &&
	   getprop_ulong(&c->w, Net("WM_PID"), "CARDINAL", 0, &pid, 1) &&
	   !strcmp(hostname, *host))
		c->pid = (int)*pid;
	freestringlist(host);
	free(pid);

	c->rgba = render_argb_p(c->w.visual);
	client_reparent(c);

	sethandler(&c->w, &handlers);

	selectinput(&c->w, ClientMask);

	group_init(c);

	grab_button(c->framewin->xid, AnyButton, AnyModifier);

	for(t=&client ;; t=&t[0]->next)
		if(!*t) {
			c->next = *t;
			*t = c;
			break;
		}


	/*
	 * It's actually possible for a window to be destroyed
	 * before we get a chance to reparent it. Check for that
	 * now, because otherwise we'll wind up mapping a
	 * perceptibly empty frame before it's destroyed.
	 */
	traperrors(true);
	XAddToSaveSet(display, w);
	if(traperrors(false)) {
		client_destroy(c);
		return nil;
	}

	ewmh_initclient(c);

	event("CreateClient %#C\n", c);
	client_manage(c);
	return c;
}

void
client_reparent(Client *c) {
	Window *fw;
	WinAttr wa;
	bool rgba;

	rgba = c->rgba | RGBA_P(def.normcolor) | RGBA_P(def.focuscolor);

	fw = c->framewin;
	if(fw && (fw->depth == 32) == rgba)
		return;

	wa.background_pixmap = None;
	wa.bit_gravity = NorthWestGravity;
	wa.event_mask = ButtonPressMask
		       | ButtonReleaseMask
		       | EnterWindowMask
		       | ExposureMask
		       | PointerMotionMask
		       | StructureNotifyMask
		       | SubstructureNotifyMask
		       | SubstructureRedirectMask;
	wa.override_redirect = true;
	if(rgba)
		c->framewin = createwindow_rgba(&scr.root, c->r,
				&wa, CWBackPixmap
				   | CWBitGravity
				   | CWEventMask
				   | CWOverrideRedirect);
	else
		c->framewin = createwindow(&scr.root, c->r, scr.depth, InputOutput,
				&wa, CWBackPixmap
				   | CWBitGravity
				   | CWEventMask
				   | CWOverrideRedirect);

	c->framewin->aux = c;
	sethandler(c->framewin, &framehandler);
	reparentwindow(&c->w, c->framewin, ZP);
	if(fw)
		destroywindow(fw);
}

static bool
apply_rules(Client *c) {
	IxpMsg m;
	Rule *r;
	Ruleval *rv;
	bool ret, more;

	ret = true;
	more = true;
	for(r=def.rules.rule; r && more; r=r->next)
		if(regexec(r->regex, c->props, nil, 0)) {
			more = false;
			for(rv=r->values; rv; rv=rv->next) {
				if(!strcmp(rv->key, "continue"))
					more = true;
				else if(!strcmp(rv->key, "tags"))
					utflcpy(c->tags, rv->value, sizeof c->tags);
				else if(!strcmp(rv->key, "force-tags")) {
					utflcpy(c->tags, rv->value, sizeof c->tags);
					ret = false;
				}else {
					bufclear();
					bufprint("%s %s", rv->key, rv->value);
					m = ixp_message(buffer, _buf_end - buffer, MsgPack);
					if(waserror())
						warning("processing rule %q=%q: %r", rv->key, rv->value);
					else {
						message_client(c, &m);
						poperror();
					}
				}
			}
		}
	return ret;
}

void
client_manage(Client *c) {
	Client *leader;
	char *tags;
	bool dotags;

	if(Dx(c->r) == Dx(screen->r))
	if(Dy(c->r) == Dy(screen->r))
	if(c->w.ewmh.type == 0)
		fullscreen(c, true, -1);

	dotags = apply_rules(c);

	if(!c->tags[0] || dotags) {
		leader = win2client(c->trans);
		if(leader == nil && c->group)
			leader = group_leader(c->group);

		tags = getprop_string(&c->w, "_WMII_TAGS");
		if(tags)
			utflcpy(c->tags, tags, sizeof c->tags);
		else if(leader)
			utflcpy(c->tags, leader->tags, sizeof c->tags);
		free(tags);
	}

	if(c->tags[0])
		client_applytags(c, c->tags);
	else
		client_applytags(c, "sel");

	if(!starting)
		view_update_all();

	bool newgroup = !c->group
		     || c->group->ref == 1
		     || selclient() && (selclient()->group == c->group)
		     || group_leader(c->group)
		        && !client_viewframe(group_leader(c->group),
					     c->sel->view);

	/* f = c->sel; */
	if(!(c->w.ewmh.type & TypeSplash))
		if(newgroup) {
			/* XXX: Look over this.
			if(f->area != f->view->sel)
				f->view->oldsel = f->view->sel;
			*/
		}else {
			frame_restack(c->sel, c->sel->area->sel);
			view_restack(c->sel->view);
		}
}

void
client_destroy(Client *c) {
	Rectangle r;
	char *none;
	Client **tc;
	bool hide;

	unmapwin(c->framewin);
	client_seturgent(c, false, UrgClient);

	for(tc=&client; *tc; tc=&tc[0]->next)
		if(*tc == c) {
			*tc = c->next;
			break;
		}

	r = client_grav(c, ZR);

	hide = (!c->sel || c->sel->view != selview);

	/* In case the client is already destroyed. */
	traperrors(true);

	sethandler(&c->w, nil);
	if(hide)
		reparentwindow(&c->w, &scr.root, screen->r.max);
	else
		reparentwindow(&c->w, &scr.root, r.min);

	if(starting >= 0)
		XRemoveFromSaveSet(display, c->w.xid);

	none = nil;
	client_setviews(c, &none);
	if(starting >= 0) {
		client_unmap(c, WithdrawnState);
		delproperty(&c->w, "_WMII_TAGS");
	}
	refree(&c->tagre);
	refree(&c->tagvre);
	free(c->retags);

	traperrors(false);

	destroywindow(c->framewin);

	ewmh_destroyclient(c);
	group_remove(c);
	if(starting >= 0)
		event("DestroyClient %#C\n", c);

	event_flush(FocusChangeMask, true);
	cleanupwindow(&c->w);
	free(c);
}

/* Convenience functions */
Frame*
client_viewframe(Client *c, View *v) {
	Frame *f;

	for(f=c->frame; f; f=f->cnext)
		if(f->view == v)
			break;
	return f;
}

Client*
selclient(void) {
	if(selview->sel->sel)
		return selview->sel->sel->client;
	return nil;
}

Client*
win2client(XWindow w) {
	Client *c;
	for(c=client; c; c=c->next)
		if(c->w.xid == w) break;
	return c;
}

int
Cfmt(Fmt *f) {
	Client *c;

	c = va_arg(f->args, Client*);
	if(c)
		if(f->flags & FmtSharp)
			return fmtprint(f, "%W", &c->w);
		else
			return fmtprint(f, "%s", c->name);
	return fmtprint(f, "<nil>");
}

Rectangle
client_grav(Client *c, Rectangle rd) {
	Rectangle r, cr;
	Point sp;
	WinHints *h;

	h = c->w.hints;

	if(eqrect(rd, ZR)) {
		if(c->sel) {
			r = c->sel->floatr;
			cr = frame_rect2client(c, r, true);
		}else {
			cr = c->r;
			r = frame_client2rect(c, cr, true);
			r = rectsetorigin(r, cr.min);
		}
		sp = subpt(cr.min, r.min);
		r = gravitate(r, cr, h->grav);
		if(!h->gravstatic)
			r = rectsubpt(r, sp);
		return frame_rect2client(c, r, true);
	}else {
		r = frame_client2rect(c, rd, true);
		sp = subpt(rd.min, r.min);
		r = gravitate(rd, r, h->grav);
		if(!h->gravstatic)
			r = rectaddpt(r, sp);
		return frame_client2rect(c, r, true);
	}
}

bool
client_floats_p(Client *c) {
	if(c->floating == Never)
		return false;
	return c->trans
	    || c->floating
	    || c->fixedsize
	    || c->titleless
	    || c->borderless
	    || c->fullscreen >= 0
	    || (c->w.ewmh.type & (TypeDialog|TypeSplash|TypeDock|TypeMenu|TypeToolbar));
}

Frame*
client_groupframe(Client *c, View *v) {
	if(c->group && c->group->client)
		return client_viewframe(c->group->client, v);
	return nil;
}

Rectangle
frame_hints(Frame *f, Rectangle r, Align sticky) {
	Rectangle or;
	WinHints h;
	Point p;
	Client *c;

	c = f->client;
	if(c->w.hints == nil)
		return r;

	or = r;
	h = frame_gethints(f);
	r = sizehint(&h, r);

	if(!f->area->floating) {
		/* Not allowed to grow */
		if(Dx(r) > Dx(or))
			r.max.x = r.min.x+Dx(or);
		if(Dy(r) > Dy(or))
			r.max.y = r.min.y+Dy(or);
	}

	p = ZP;
	if((sticky&(East|West)) == East)
		p.x = Dx(or) - Dx(r);
	if((sticky&(North|South)) == South)
		p.y = Dy(or) - Dy(r);
	return rectaddpt(r, p);
}

static void
client_setstate(Client * c, int state) {
	long data[] = { state, None };

	changeprop_long(&c->w, "WM_STATE", "WM_STATE", data, nelem(data));
}

void
client_map(Client *c) {
	if(!c->w.mapped) {
		mapwin(&c->w);
		client_setstate(c, NormalState);
	}
}

void
client_unmap(Client *c, int state) {
	if(c->w.mapped)
		unmapwin(&c->w);
	client_setstate(c, state);
}

int
client_mapframe(Client *c) {
	return mapwin(c->framewin);
}

int
client_unmapframe(Client *c) {
	return unmapwin(c->framewin);
}

void
focus(Client *c, bool user) {
	View *v;
	Frame *f;

	Dprint(DFocus, "focus(%#C, %d)\n", c, user);
	if(!c->nofocus || user)
	if((f = c->sel)) {
		v = f->view;
		if(v != selview)
			view_focus(screen, v);
		frame_focus(c->sel);
		view_restack(c->sel->view);
	}
}

void
client_focus(Client *c) {
	/* Round trip. */

	if(c && c->group)
		c->group->client = c;

	sync();
	event_flush(FocusChangeMask, true);

	Dprint(DFocus, "client_focus([%#C]%C) collapsed=%s\n",
	       c, c, c && c->sel->collapsed ? "true" : "false");
	Dprint(DFocus, "\t[%#C]%C\n\t=> [%#C]%C\n",
	       disp.focus, disp.focus, c, c);

	if(disp.focus != c) {
		if(c && !c->sel->collapsed) {
			if(!c->noinput)
				setfocus(&c->w, RevertToParent);
			else if(c->proto & ProtoTakeFocus) {
				event_updatextime();
				client_message(c, "WM_TAKE_FOCUS", 0);
			}
		}else
			setfocus(screen->barwin, RevertToParent);

		sync();
		event_flush(FocusChangeMask, true);
	}
}

void
client_resize(Client *c, Rectangle r) {
	Frame *f;

	f = c->sel;
	frame_resize(f, r);

	if(f->view != selview) {
		client_unmap(c, IconicState);
		client_unmapframe(c);
		return;
	}

	c->r = rectaddpt(f->crect, f->r.min);

	if(f->collapsed) {
		if(f->area->max && !resizing)
			client_unmapframe(c);
		else {
			reshapewin(c->framewin, f->r);
			movewin(&c->w, f->crect.min);
			client_mapframe(c);
		}
		client_unmap(c, IconicState);
	}else {
		client_map(c);
		reshapewin(c->framewin, f->r);
		reshapewin(&c->w, f->crect);
		client_mapframe(c);
		if(!eqrect(c->r, c->configr))
			client_configure(c);
		ewmh_framesize(c);
	}
}

void
client_setcursor(Client *c, Cursor cur) {
	WinAttr wa;

	if(c->cursor != cur) {
		c->cursor = cur;
		wa.cursor = cur;
		setwinattr(c->framewin, &wa, CWCursor);
	}
}

void
client_configure(Client *c) {
	Rectangle r;

	c->configr = c->r;
	r = rectsubpt(c->r, Pt(c->border, c->border));

	sendevent(&c->w, false, StructureNotifyMask,
		  &(XConfigureEvent) {
			  .type = ConfigureNotify,
			  .event = c->w.xid,
			  .window = c->w.xid,

			  .x = r.min.x,
			  .y = r.min.y,
			  .width = Dx(r),
			  .height = Dy(r),
			  .border_width = c->border,
		  });
}

void
client_message(Client *c, char *msg, long l2) {
	sendmessage(&c->w, "WM_PROTOCOLS", xatom(msg), event_xtime, l2, 0, 0);
}

void
client_kill(Client *c, bool nice) {

	if(!nice) {
		if(c->pid)
			kill(c->pid, SIGKILL);
		XKillClient(display, c->w.xid);
	}
	else if(c->proto & ProtoDelete) {
		c->dead = 1;
		client_message(c, "WM_DELETE_WINDOW", 0);
		ewmh_checkresponsive(c);
	}
	else
		XKillClient(display, c->w.xid);
}

void
fullscreen(Client *c, int fullscreen, long screen) {
	Client *leader;
	Frame *f;
	bool wassel;

	if(fullscreen == Toggle)
		fullscreen = (c->fullscreen >= 0) ^ On;
	if(fullscreen == (c->fullscreen >= 0))
		return;

	event("Fullscreen %#C %s\n", c, (fullscreen ? "on" : "off"));

	c->fullscreen = -1;
	if(!fullscreen)
		for(f=c->frame; f; f=f->cnext) {
			if(f->oldarea == 0) {
				frame_resize(f, f->floatr);
				if(f->view == selview) /* FIXME */
					client_resize(f->client, f->r);

			}
			else if(f->oldarea > 0) {
				wassel = (f == f->area->sel);
				area_moveto(view_findarea(f->view, f->oldscreen, f->oldarea, true),
					    f);
				if(wassel)
					frame_focus(f);
			}
		}
	else {
		c->fullscreen = 0;
		if(screen >= 0)
			c->fullscreen = screen;
		else if(c->sel)
			c->fullscreen = ownerscreen(c->r);
		else if(c->group && (leader = group_leader(c->group)) && leader->sel)
			c->fullscreen = ownerscreen(leader->r);
		else if(selclient())
			c->fullscreen = ownerscreen(selclient()->r);

		for(f=c->frame; f; f=f->cnext)
			f->oldarea = -1;
		if((f = c->sel))
			view_update(f->view);
	}
	ewmh_updatestate(c);
}

void
client_seturgent(Client *c, int urgent, int from) {
	XWMHints *wmh;
	char *cfrom, *cnot;
	Frame *f;

	if(urgent == Toggle)
		urgent = c->urgent ^ On;

	cfrom = (from == UrgManager ? "Manager" : "Client");
	cnot = (urgent ? "" : "Not");

	if(urgent != c->urgent) {
		event("%sUrgent %#C %s\n", cnot, c, cfrom);
		c->urgent = urgent;
		ewmh_updatestate(c);
		if(c->sel)
			frame_draw(c->sel);

		for(f=c->frame; f; f=f->cnext)
			view_update_urgency(f->view, cfrom);
	}

	if(from == UrgManager) {
		wmh = XGetWMHints(display, c->w.xid);
		if(wmh == nil)
			wmh = emallocz(sizeof *wmh);

		wmh->flags &= ~XUrgencyHint;
		if(urgent)
			wmh->flags |= XUrgencyHint;
		XSetWMHints(display, c->w.xid, wmh);
		XFree(wmh);
	}
}

/* X11 stuff */
void
update_class(Client *c) {

	snprint(c->props, sizeof c->props, "%s:%s", c->class, c->name);
}

static void
client_updatename(Client *c) {
	char *str;

	c->name[0] = '\0';
	if((str = windowname(&c->w))) {
		utflcpy(c->name, str, sizeof c->name);
		free(str);
	}

	update_class(c);
	if(c->sel)
		frame_draw(c->sel);
}

static void
updatemwm(Client *c) {
	enum {
		All =		0x1,
		Border =	0x2,
		Title =		0x8,
		FlagDecor =	0x2,
		Flags =		0,
		Decor =		2,
	};
	Rectangle r;
	ulong *ret;
	int n;

	/* To quote Metacity, or KWin quoting Metacity:
	 *
	 *   We support MWM hints deemed non-stupid
	 *
	 * Our definition of non-stupid is a bit less lenient than
	 * theirs, though. In fact, we don't really even support the
	 * idea of supporting the hints that we support, but apps
	 * like xmms (which no one should use) break if we don't.
	 */

	n = getprop_ulong(&c->w, "_MOTIF_WM_HINTS", "_MOTIF_WM_HINTS",
			0L, &ret, 3L);

	/* FIXME: Should somehow handle all frames of a client. */
	if(c->sel)
		r = client_grav(c, ZR);

	c->borderless = 0;
	c->titleless = 0;
	if(n >= 3 && (ret[Flags] & FlagDecor)) {
		if(ret[Decor] & All)
			ret[Decor] ^= ~0;
		c->borderless = !(ret[Decor] & Border);
		c->titleless = !(ret[Decor] & Title);
	}
	free(ret);

	if(false && c->sel) {
		c->sel->floatr = client_grav(c, r);
		if(c->sel->area->floating) {
			client_resize(c, c->sel->floatr);
			frame_draw(c->sel);
		}
	}
}

bool
client_prop(Client *c, Atom a) {
	WinHints h;
	XWMHints *wmh;
	char **class;
	int n;

	if(a == xatom("WM_PROTOCOLS"))
		c->proto = ewmh_protocols(&c->w);
	else
	if(a == xatom("_NET_WM_NAME"))
		goto wmname;
	else
	if(a == xatom("_MOTIF_WM_HINTS"))
		updatemwm(c);
	else
	switch (a) {
	default:
		return true;
	case XA_WM_TRANSIENT_FOR:
		XGetTransientForHint(display, c->w.xid, &c->trans);
		break;
	case XA_WM_NORMAL_HINTS:
		memset(&h, 0, sizeof h);
		if(c->w.hints)
			bcopy(c->w.hints, &h, sizeof h);
		gethints(&c->w);
		if(c->w.hints)
			c->fixedsize = eqpt(c->w.hints->min, c->w.hints->max);
		if(memcmp(&h, c->w.hints, sizeof h))
			if(c->sel)
				view_update(c->sel->view);
		break;
	case XA_WM_HINTS:
		wmh = XGetWMHints(display, c->w.xid);
		if(wmh) {
			c->noinput = (wmh->flags&InputFocus) && !wmh->input;
			client_seturgent(c, (wmh->flags & XUrgencyHint) != 0, UrgClient);
			XFree(wmh);
		}
		break;
	case XA_WM_CLASS:
		n = getprop_textlist(&c->w, "WM_CLASS", &class);
		snprint(c->class, sizeof c->class, "%s:%s",
			(n > 0 ? class[0] : "<nil>"),
			(n > 1 ? class[1] : "<nil>"));
		freestringlist(class);
		update_class(c);
		break;
	case XA_WM_NAME:
	wmname:
		client_updatename(c);
		break;
	}
	return false;
}

/* Handlers */
static bool
configreq_event(Window *w, void *aux, XConfigureRequestEvent *e) {
	Rectangle r;
	Client *c;

	c = aux;

	r = client_grav(c, ZR);
	r.max = subpt(r.max, r.min);

	if(e->value_mask & CWX)
		r.min.x = e->x;
	if(e->value_mask & CWY)
		r.min.y = e->y;
	if(e->value_mask & CWWidth)
		r.max.x = e->width;
	if(e->value_mask & CWHeight)
		r.max.y = e->height;

	if(e->value_mask & CWBorderWidth)
		c->border = e->border_width;

	r.max = addpt(r.min, r.max);
	r = client_grav(c, r);

	if(c->sel->area->floating)
		client_resize(c, r);
	else {
		c->sel->floatr = r;
		client_configure(c);
	}
	return false;
}

static bool
destroy_event(Window *w, void *aux, XDestroyWindowEvent *e) {
	USED(w, e);

	client_destroy(aux);
	return false;
}

static bool
enter_event(Window *w, void *aux, XCrossingEvent *e) {
	Client *c;

	c = aux;
	if(e->detail != NotifyInferior) {
		if(e->detail != NotifyVirtual)
		if(e->serial > event_lastconfigure && disp.focus != c) {
			Dprint(DFocus, "enter_notify([%#C]%s)\n", c, c->name);
			focus(c, false);
		}
		client_setcursor(c, cursor[CurNormal]);
	}else
		Dprint(DFocus, "enter_notify(%#C[NotifyInferior]%s)\n", c, c->name);
	return false;
}

static bool
focusin_event(Window *w, void *aux, XFocusChangeEvent *e) {
	Client *c, *old;

	c = aux;

	print_focus("focusin_event", c, c->name);

	if(e->mode == NotifyGrab)
		disp.hasgrab = c;

	old = disp.focus;
	disp.focus = c;
	if(c != old) {
		event("ClientFocus %#C\n", c);
		if(c->sel)
			frame_draw(c->sel);
	}
	return false;
}

static bool
focusout_event(Window *w, void *aux, XFocusChangeEvent *e) {
	Client *c;

	c = aux;
	if((e->mode == NotifyWhileGrabbed) && (disp.hasgrab != &c_root)) {
		if(disp.focus)
			disp.hasgrab = disp.focus;
	}else if(disp.focus == c) {
		print_focus("focusout_event", &c_magic, "<magic>");
		disp.focus = &c_magic;
		if(c->sel)
			frame_draw(c->sel);
	}
	return false;
}

static bool
unmap_event(Window *w, void *aux, XUnmapEvent *e) {
	Client *c;

	c = aux;
	if(!e->send_event && w->parent != c->framewin)
		c->w.unmapped++;
	if(e->send_event || c->w.unmapped < 0)
		client_destroy(c);
	return false;
}

static bool
map_event(Window *w, void *aux, XMapEvent *e) {
	Client *c;

	USED(e);

	c = aux;
	if(c == selclient())
		client_focus(c);
	return true;
}

static bool
property_event(Window *w, void *aux, XPropertyEvent *e) {

	if(e->state == PropertyDelete) /* FIXME */
		return true;
	return client_prop(aux, e->atom);
}

static Handlers handlers = {
	.configreq = configreq_event,
	.destroy = destroy_event,
	.enter = enter_event,
	.focusin = focusin_event,
	.focusout = focusout_event,
	.map = map_event,
	.unmap = unmap_event,
	.property = property_event,
};

/* Other */
void
client_setviews(Client *c, char **tags) {
	Frame **fp, *f;
	int cmp;

	fp = &c->frame;
	while(*fp || *tags) {
		SET(cmp);
		while(*fp) {
			if(*tags) {
				cmp = strcmp(fp[0]->view->name, *tags);
				if(cmp >= 0)
					break;
			}

			f = *fp;
			view_detach(f);
			*fp = f->cnext;
			if(c->sel == f)
				c->sel = *fp;
			free(f);
		}
		if(*tags) {
			if(!*fp || cmp > 0) {
				f = frame_create(c, view_create(*tags));
				Dprint(DGeneric, "%#C %p %R %R %R %C\n", c, c->sel, c->r, f->floatr, c->sel ? c->sel->floatr : ZR, c);
				if(f->view == selview || !c->sel)
					c->sel = f;
				kludge = c; /* FIXME */
				view_attach(f->view, f);
				kludge = nil;
				f->cnext = *fp;
				*fp = f;
			}
			if(fp[0]) fp=&fp[0]->cnext;
			tags++;
		}
	}
	if(c->sel == nil)
		c->sel = c->frame;
	if(c->sel)
		frame_draw(c->sel);
}

static int
bsstrcmp(const void *a, const void *b) {
	return strcmp((char*)a, *(char**)b);
}

static int
strpcmp(const void *ap, const void *bp) {
	char **a, **b;

	a = (char**)ap;
	b = (char**)bp;
	return strcmp(*a, *b);
}

static char *badtags[] = {
	".",
	"..",
	"sel",
};

char*
client_extratags(Client *c) {
	Fmt fmt;
	Frame *f;
	char *toks[32];
	char **tags;
	int i;

	i = 0;
	toks[i++] = "";
	for(f=c->frame; f && i < nelem(toks)-1; f=f->cnext)
		if(f != c->sel)
			toks[i++] = f->view->name;
	toks[i] = nil;
	tags = comm(CLeft, toks, c->retags);

	if(i == 1 && !c->tagre.regex && !c->tagvre.regex) {
		free(tags);
		return nil;
	}

	fmtstrinit(&fmt);
	if(i > 1)
		join(tags, "+", &fmt);
	free(tags);

	if(c->tagre.regex)
		fmtprint(&fmt, "+/%s/", c->tagre.regex);
	if(c->tagvre.regex)
		fmtprint(&fmt, "-/%s/", c->tagvre.regex);
	return fmtstrflush(&fmt);
}

bool
client_applytags(Client *c, const char *tags) {
	Fmt fmt;
	uint i, j, k;
	char buf[512];
	char *toks[32];
	char **p;
	char *cur, *s;
	int add, old;

	buf[0] = 0;
	if(memchr("+-^", tags[0], 4))
		utflcpy(buf, c->tags, sizeof c->tags);
	else {
		refree(&c->tagre);
		refree(&c->tagvre);
	}
	strlcat(buf, tags, sizeof buf);

	j = 0;
	s = buf;
	old = '+';
	while((cur = mask(&s, &add, &old))) {
		/* Check for regex. */
		if(cur[0] == '/') {
			cur++;
			*strrchr(cur, '/') = '\0';
			if(add == '+')
				reinit(&c->tagre, cur);
			else if(add == '-')
				reinit(&c->tagvre, cur);
		}
		else if(!strcmp(cur, "~"))
			c->floating = add ? On : Never;
		else {
			trim(cur, " \t\r\n");
			if(!strcmp(cur, "sel"))
				cur = selview->name;
			else if(Mbsearch(cur, badtags, bsstrcmp))
				continue;

			if(j < nelem(toks)-1) {
				if(add == '^')
					add = bsearch(cur, toks, j, sizeof *toks, bsstrcmp) ? '-' : '+';
				if(add == '+')
					toks[j++] = cur;
				else {
					for(i = 0, k = 0; i < j; i++)
						if(strcmp(toks[i], cur))
							toks[k++] = toks[i];
					j = k;
				}
			}
		}
	}

	toks[j] = nil;
	qsort(toks, j, sizeof *toks, strpcmp);
	uniq(toks);

	fmtstrinit(&fmt);
	join(toks, "+", &fmt);
	if(c->tagre.regex)
		fmtprint(&fmt, "+/%s/", c->tagre.regex);
	if(c->tagvre.regex)
		fmtprint(&fmt, "-/%s/", c->tagvre.regex);

	s = fmtstrflush(&fmt);
	utflcpy(c->tags, s, sizeof c->tags);
	changeprop_string(&c->w, "_WMII_TAGS", c->tags);
	free(s);

	free(c->retags);
	p = view_names();
	grep(p, c->tagre.regc, 0);
	grep(p, c->tagvre.regc, GInvert);
	c->retags = comm(CRight, toks, p);
	free(p);

	if(c->retags[0] == nil && toks[0] == nil) {
		toks[0] = "orphans";
		toks[1] = nil;
	}

	p = comm(~0, c->retags, toks);
	client_setviews(c, p);
	free(p);
	return true;
}

Added cmd/wmii/column.c.
















































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
/* Copyright ©2004-2006 Anselm R. Garbe <garbeam at gmail dot com>
 * Copyright ©2006-2014 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "dat.h"
#include <math.h>
#include <strings.h>
#include "fns.h"

static void	column_resizeframe_h(Frame*, Rectangle);

char *modes[] = {
	[Coldefault] =	"default",
	[Colstack] =	"stack",
	[Colmax] =	"max",
};

bool
column_setmode(Area *a, const char *mode) {
	char *str, *tok;
	int add, old;

	/*
	 * The mapping between the current internal
	 * representation and the external interface
	 * is currently a bit complex. That will probably
	 * change.
	 */

	str = freelater(estrdup(mode));
	old = '+';
	while((tok = mask(&str, &add, &old))) {
		if(!strcmp(tok, "max")) {
			if(add == '\0' || add == '+')
				a->max = true;
			else if(add == '-')
				a->max = false;
			else
				a->max = !a->max;
		}else
		if(!strcmp(tok, "stack")) {
			if(add == '\0' || add == '+')
				a->mode = Colstack;
			else if(add == '-')
				a->mode = Coldefault;
			else
				a->mode = a->mode == Colstack ? Coldefault : Colstack;
		}else
		if(!strcmp(tok, "default")) {
			if(add == '\0' || add == '+') {
				a->mode = Coldefault;
				column_arrange(a, true);
			}else if(add == '-')
				a->mode = Colstack;
			else
				a->mode = a->mode == Coldefault ? Colstack : Coldefault;
		}else
			return false;
	}
	return true;
}

char*
column_getmode(Area *a) {
	return sxprint("%s%cmax", a->mode == Colstack ? "stack" : "default",
				  a->max ? '+' : '-');
}

int
column_minwidth(void)
{
	return 4 * labelh(def.font);
}

static void
columns_update(View *v) {
	Area *a;
	Frame *f;
	int s;

	foreach_frame(v, s, a, f) {
		f->screen = s;
		f->column = area_idx(a);
	}
}

Area*
column_new(View *v, Area *pos, int scrn, uint w) {
	Area *a;

	assert(!pos || !pos->floating && pos->screen == scrn);
	a = area_create(v, pos, scrn, w);
	return a;
#if 0
	if(!a)
		return nil;

	view_arrange(v);
	columns_update(v);
	view_update(v);
#endif
}

void
column_insert(Area *a, Frame *f, Frame *pos) {

	f->area = a;
	if(f->client->floating == On)
		f->client->floating = Off;
	f->screen = a->screen;
	f->column = area_idx(a);
	frame_insert(f, pos);
	if(a->sel == nil)
		area_setsel(a, f);
}

void
column_destroy(Area *a) {
	View *v;

	v = a->view;
	area_destroy(a);
	columns_update(v);
}

void
column_attach(Area *a, Frame *f) {
	Frame *first;
	int nframe, dy, h;

	f->colr = a->r;

	if(a->sel) {
		stack_info(a->sel, &first, nil, &dy, &nframe);
		h = dy / (nframe+1);
		f->colr.max.y = f->colr.min.y + h;
		stack_scale(first, dy - h);
	}

	column_insert(a, f, a->sel);
	column_arrange(a, false);
}

void
column_detach(Frame *f) {
	Frame *first;
	Area *a;
	int dy;

	a = f->area;
	stack_info(f, &first, nil, &dy, nil);
	if(first && first == f)
		first = f->anext;
	column_remove(f);
	if(a->frame) {
		if(first)
			stack_scale(first, dy);
		column_arrange(a, false);
	}else if(a->view->areas[a->screen]->next && !a->permanent)
		column_destroy(a);
}

static void column_scale(Area*);

void
column_attachrect(Area *a, Frame *f, Rectangle r) {
	Frame *fp, *pos;

	pos = nil;
	for(fp=a->frame; fp; pos=fp, fp=fp->anext) {
		if(r.max.y < fp->r.min.y || r.min.y > fp->r.max.y)
			continue;
		/*
		before = fp->r.min.y - r.min.y;
		after = -fp->r.max.y + r.max.y;
		*/
	}
	column_insert(a, f, pos);
	column_resizeframe_h(f, r);
	column_scale(a);
}

void
column_remove(Frame *f) {
	Frame *pr;
	Area *a;

	a = f->area;
	pr = f->aprev;

	frame_remove(f);

	f->area = nil;
	if(a->sel == f) {
		if(pr == nil)
			pr = a->frame;
		if(pr && pr->collapsed)
			if(pr->anext && !pr->anext->collapsed)
				pr = pr->anext;
			else
				pr->collapsed = false;
		a->sel = nil;
		area_setsel(a, pr);
	}
}

static int
column_surplus(Area *a) {
	Frame *f;
	int surplus;

	surplus = Dy(a->r);
	for(f=a->frame; f; f=f->anext)
		surplus -= Dy(f->r);
	return surplus;
}

static void
column_fit(Area *a, uint *n_colp, uint *n_uncolp) {
	Frame *f, **fp;
	uint minh, dy;
	uint n_col, n_uncol;
	uint col_h, uncol_h;
	int surplus, i, j;

	/* The minimum heights of collapsed and uncollpsed frames.
	 */
	minh = labelh(def.font);
	col_h = labelh(def.font);
	uncol_h = minh + col_h + 1;
	if(a->max && !resizing)
		col_h = 0;

	/* Count collapsed and uncollapsed frames. */
	n_col = 0;
	n_uncol = 0;
	for(f=a->frame; f; f=f->anext) {
		frame_resize(f, f->colr);
		if(f->collapsed)
			n_col++;
		else
			n_uncol++;
	}

	if(n_uncol == 0) {
		n_uncol++;
		n_col--;
		(a->sel ? a->sel : a->frame)->collapsed = false;
	}

	/* FIXME: Kludge. See frame_attachrect. */
	dy = Dy(a->view->r[a->screen]) - Dy(a->r);
	minh = col_h * (n_col + n_uncol - 1) + uncol_h;
	if(dy && Dy(a->r) < minh)
		a->r.max.y += min(dy, minh - Dy(a->r));

	surplus = Dy(a->r)
		- (n_col * col_h)
		- (n_uncol * uncol_h);

	/* Collapse until there is room */
	if(surplus < 0) {
		i = ceil(-1.F * surplus / (uncol_h - col_h));
		if(i >= n_uncol)
			i = n_uncol - 1;
		n_uncol -= i;
		n_col += i;
		surplus += i * (uncol_h - col_h);
	}
	/* Push to the floating layer until there is room */
	if(surplus < 0) {
		i = ceil(-1.F * surplus / col_h);
		if(i > n_col)
			i = n_col;
		n_col -= i;
		surplus += i * col_h;
	}

	/* Decide which to collapse and which to float. */
	j = n_uncol - 1;
	i = n_col - 1;
	for(fp=&a->frame; *fp;) {
		f = *fp;
		if(f != a->sel) {
			if(!f->collapsed) {
				if(j < 0)
					f->collapsed = true;
				j--;
			}
			if(f->collapsed) {
				if(i < 0) {
					f->collapsed = false;
					area_moveto(f->view->floating, f);
					continue;
				}
				i--;
			}
		}
		/* Doesn't change if we 'continue' */
		fp = &f->anext;
	}

	if(n_colp) *n_colp = n_col;
	if(n_uncolp) *n_uncolp = n_uncol;
}

void
column_settle(Area *a) {
	Frame *f;
	uint yoff, yoffcr;
	int surplus, n_uncol, n;

	n_uncol = 0;
	surplus = column_surplus(a);
	for(f=a->frame; f; f=f->anext)
		if(!f->collapsed) n_uncol++;

	if(n_uncol == 0) {
		fprint(2, "%s: Badness: No uncollapsed frames, column %d, view %q\n",
		       argv0, area_idx(a), a->view->name);
		return;
	}
	if(surplus < 0)
		fprint(2, "%s: Badness: surplus = %d in column_settle, column %d, view %q\n",
		       argv0, surplus, area_idx(a), a->view->name);

	yoff = a->r.min.y;
	yoffcr = yoff;
	n = surplus % n_uncol;
	surplus /= n_uncol;
	for(f=a->frame; f; f=f->anext) {
		f->r = rectsetorigin(f->r, Pt(a->r.min.x, yoff));
		f->colr = rectsetorigin(f->colr, Pt(a->r.min.x, yoffcr));
		f->r.min.x = a->r.min.x;
		f->r.max.x = a->r.max.x;
		if(def.incmode == ISqueeze && !resizing)
		if(!f->collapsed) {
			f->r.max.y += surplus;
			if(n-- > 0)
				f->r.max.y++;
		}
		yoff = f->r.max.y;
		yoffcr = f->colr.max.y;
	}
}

/*
 * Returns how much a frame "wants" to grow.
 */
static int
foo(Frame *f) {
	WinHints h;
	int maxh;

	h = frame_gethints(f);
	maxh = 0;
	if(h.aspect.max.x)
		maxh = h.baspect.y +
		       (Dx(f->r) - h.baspect.x) *
		       h.aspect.max.y / h.aspect.max.x;
	maxh = max(maxh, h.max.y);

	if(Dy(f->r) >= maxh)
		return 0;
	return h.inc.y - (Dy(f->r) - h.base.y) % h.inc.y;
}

static int
comp_frame(const void *a, const void *b) {
	int ia, ib;

	ia = foo(*(Frame**)a);
	ib = foo(*(Frame**)b);
	/*
	 * I'd like to favor the selected client, but
	 * it causes windows to jump as focus changes.
	 */
	return ia < ib ? -1 :
	       ia > ib ?  1 :
	                  0;
}

static void
column_squeeze(Area *a) {
	static Vector_ptr fvec;
	Frame *f;
	int surplus, osurplus, dy, i;

	fvec.n = 0;
	for(f=a->frame; f; f=f->anext)
		if(!f->collapsed) {
			f->r = frame_hints(f, f->r, 0);
			vector_ppush(&fvec, f);
		}

	surplus = column_surplus(a);
	for(osurplus=0; surplus != osurplus;) {
		osurplus = surplus;
		qsort(fvec.ary, fvec.n, sizeof *fvec.ary, comp_frame);
		for(i=0; i < fvec.n; i++) {
			f=fvec.ary[i];
			dy = foo(f);
			if(dy > surplus)
				break;
			surplus -= dy;
			f->r.max.y += dy;
		}
	}
}

/*
 * Frobs a column. Which is to say, *temporary* kludge.
 * Essentially seddles the column and resizes its clients.
 */
void
column_frob(Area *a) {
	Frame *f;

	for(f=a->frame; f; f=f->anext)
		f->r = f->colr;
	column_settle(a);
	if(a->view == selview)
	for(f=a->frame; f; f=f->anext)
		client_resize(f->client, f->r);
}

static void
column_scale(Area *a) {
	Frame *f;
	uint dy;
	uint ncol, nuncol;
	uint colh;
	int surplus;

	if(!a->frame)
		return;

	column_fit(a, &ncol, &nuncol);

	colh = labelh(def.font);
	if(a->max && !resizing)
		colh = 0;

	dy = 0;
	surplus = Dy(a->r);
	for(f=a->frame; f; f=f->anext) {
		if(f->collapsed)
			f->colr.max.y = f->colr.min.y + colh;
		else if(Dy(f->colr) == 0)
			f->colr.max.y++;
		surplus -= Dy(f->colr);
		if(!f->collapsed)
			dy += Dy(f->colr);
	}
	for(f=a->frame; f; f=f->anext) {
		f->dy = Dy(f->colr);
		f->colr.min.x = a->r.min.x;
		f->colr.max.x = a->r.max.x;
		if(!f->collapsed)
			f->colr.max.y += ((float)f->dy / dy) * surplus;
		if(btassert("6 full", !(f->collapsed ? Dy(f->r) >= 0 : dy > 0)))
			warning("Something's fucked: %s:%d:%s()",
				__FILE__, __LINE__, __func__);
		frame_resize(f, f->colr);
	}

	if(def.incmode == ISqueeze && !resizing)
		column_squeeze(a);
	column_settle(a);
}

void
column_arrange(Area *a, bool dirty) {
	Frame *f;
	View *v;

	if(a->floating)
		float_arrange(a);
	if(a->floating || !a->frame)
		return;

	v = a->view;

	switch(a->mode) {
	case Coldefault:
		if(dirty)
			for(f=a->frame; f; f=f->anext)
				f->colr = Rect(0, 0, 100, 100);
		break;
	case Colstack:
		/* XXX */
		for(f=a->frame; f; f=f->anext)
			f->collapsed = (f != a->sel);
		break;
	default:
		fprint(2, "Dieing: %s: screen: %d a: %p mode: %x floating: %d\n",
		       v->name, a->screen, a, a->mode, a->floating);
		die("not reached");
		break;
	}
	column_scale(a);
	/* XXX */
	if(a->sel->collapsed)
		area_setsel(a, a->sel);
	if(v == selview) {
		//view_restack(v);
		client_resize(a->sel->client, a->sel->r);
		for(f=a->frame; f; f=f->anext)
			client_resize(f->client, f->r);
	}
}

void
column_resize(Area *a, int w) {
	Area *an;
	int dw;

	an = a->next;
	assert(an != nil);

	dw = w - Dx(a->r);
	a->r.max.x += dw;
	an->r.min.x += dw;

	/* view_arrange(a->view); */
	view_update(a->view);
}

static void
column_resizeframe_h(Frame *f, Rectangle r) {
	Area *a;
	Frame *fn, *fp;
	uint minh;

	minh = labelh(def.font);

	a = f->area;
	fn = f->anext;
	fp = f->aprev;

	if(fp)
		r.min.y = max(r.min.y, fp->colr.min.y + minh);
	else
		r.min.y = max(r.min.y, a->r.min.y);

	if(fn)
		r.max.y = min(r.max.y, fn->colr.max.y - minh);
	else
		r.max.y = min(r.max.y, a->r.max.y);

	if(fp) {
		fp->colr.max.y = r.min.y;
		frame_resize(fp, fp->colr);
	}
	else
		r.min.y = min(r.min.y, r.max.y - minh);

	if(fn) {
		fn->colr.min.y = r.max.y;
		frame_resize(fn, fn->colr);
	}
	else
		r.max.y = max(r.max.y, r.min.y + minh);

	f->colr = r;
	frame_resize(f, r);
}

void
column_resizeframe(Frame *f, Rectangle r) {
	Area *a, *al, *ar;
	View *v;
	uint minw;

	a = f->area;
	v = a->view;

	minw = column_minwidth();

	al = a->prev;
	ar = a->next;

	if(al)
		r.min.x = max(r.min.x, al->r.min.x + minw);
	else { /* Hm... */
		r.min.x = max(r.min.x, v->r[a->screen].min.x);
		r.max.x = max(r.max.x, r.min.x + minw);
	}

	if(ar)
		r.max.x = min(r.max.x, ar->r.max.x - minw);
	else {
		r.max.x = min(r.max.x, v->r[a->screen].max.x);
		r.min.x = min(r.min.x, r.max.x - minw);
	}

	a->r.min.x = r.min.x;
	a->r.max.x = r.max.x;
	if(al) {
		al->r.max.x = a->r.min.x;
		column_arrange(al, false);
	}else {
		v->pad[a->screen].min.x = r.min.x - v->r[a->screen].min.x;
	}
	if(ar) {
		ar->r.min.x = a->r.max.x;
		column_arrange(ar, false);
	}else {
		v->pad[a->screen].max.x = r.max.x - v->r[a->screen].max.x;
	}

	column_resizeframe_h(f, r);

	view_update(v);
}

Added cmd/wmii/dat.h.
























































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
/* Copyright ©2007-2014 Kris Maglione <jg@suckless.org>
 * See LICENSE file for license details.
 */

#define _XOPEN_SOURCE 600
#define IXP_NO_P9_
#include <assert.h>
#include <regexp9.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include <utf.h>
#include <ixp.h>
#include <stuff/x.h>
#include <stuff/util.h>
#include "debug.h"

#define FONT		"fixed"
#define FOCUSCOLORS	"#000000 #81654f #000000"
#define NORMCOLORS	"#000000 #c1c48b #81654f"

/* From CGO */
#define assert_equal(x, y) typedef char _##x##_does_not_equal_##y[((x)-(y))*((x)-(y))*-2+1]

enum Barpos {
	BBottom,
	BTop,
};

enum {
	Coldefault, Colstack, Colmax, Collast
};

enum {
	CurNormal,
	CurNECorner, CurNWCorner, CurSECorner, CurSWCorner,
	CurDHArrow, CurDVArrow, CurMove, CurInput, CurSizing,
	CurTCross, CurIcon,
	CurNone,
	CurLast,
};
Cursor	cursor[CurLast];

enum IncMode {
	IIgnore,
	IShow,
	ISqueeze,
};

enum {
	PDesktop,
	PExtents,
	PMonitors = PExtents + 4,
	PState = PMonitors + 4,
	PLast = PState + 3
};

enum ClientPermission {
	PermActivate	= 1<<0,
};

enum {
	PingTime = 10000,
	PingPeriod = 4000,
	PingPartition = 10,
};

enum Protocols {
	ProtoDelete	= 1<<0,
	ProtoTakeFocus	= 1<<1,
	ProtoPing	= 1<<2,
};

enum {
	SourceUnknown,
	SourceClient,
	SourcePager
};

enum EWMHType {
	TypeDesktop	= 1<<0,
	TypeDock	= 1<<1,
	TypeToolbar	= 1<<2,
	TypeMenu	= 1<<3,
	TypeUtility	= 1<<4,
	TypeSplash	= 1<<5,
	TypeDialog	= 1<<6,
	TypeNormal	= 1<<7,
};

enum {
	UrgManager,
	UrgClient,
};

extern char*	modes[];

#define toggle(val, x)	\
	((val) = ((x) == On     ? true   : \
		  (x) == Off    ? false  : \
		  (x) == Toggle ? !(val) : (val)))
#define TOGGLE(x) \
	((x) == On     ? "on"     : \
	 (x) == Off    ? "off"    : \
	 (x) == Toggle ? "toggle" : "<toggle>")
enum {
	Never = -1,
	Off,
	On,
	/* Xlib defines this. :( */
	// Always,
	Toggle,
};

assert_equal(Always, 2);

enum {
	NCOL = 16,
};

/* Data Structures */
typedef struct Area Area;
typedef struct Bar Bar;
typedef struct Client Client;
typedef struct Divide Divide;
typedef struct Frame Frame;
typedef struct Group Group;
typedef struct Key Key;
typedef struct Rule Rule;
typedef struct Ruleset Ruleset;
typedef struct Ruleval Ruleval;
typedef struct Strut Strut;
typedef struct View View;
typedef struct WMScreen WMScreen;

struct Area {
	Area*	next;
	Area*	prev;
	Frame*	frame;
	Frame*	frame_old;
	Frame*	stack;
	Frame*	sel;
	View*	view;
	bool	floating;
	ushort	id;
	int	mode;
	int	screen;
	bool	max;
	bool	permanent;
	Rectangle	r;
	Rectangle	r_old;
};

struct Bar {
	Bar*	next;
	Bar*	smaller;
	char	buf[280];
	char	text[256];
	char	name[256];
	int	bar;
	ushort	id;
	CTuple	colors;
	Rectangle	r;
	WMScreen*	screen;
};

struct Client {
	Client*	next;
	Frame*	frame;
	Frame*	sel;
	Window	w;
	Window*	framewin;
	XWindow	trans;
	Regex	tagre;
	Regex	tagvre;
	Group*	group;
	Strut*	strut;
	Cursor	cursor;
	Rectangle configr;
	Rectangle r;
	char**	retags;
	char	class[256];
	char	name[256];
	char	props[512];
	char	tags[256];
	char	proplen[PLast];
	long	propcache[PLast];
	long	permission;
	long	proto;
	int	border;
	int	dead;
	int	floating;
	int	fullscreen;
	int	pid;
	bool	borderless;
	bool	fixedsize;
	bool	nofocus;
	bool	noinput;
	bool	rgba;
	bool	titleless;
	bool	urgent;
};

struct Divide {
	Divide*	next;
	Window*	w;
	Area*	left;
	Area*	right;
	bool	mapped;
	int	x;
};

struct Frame {
	Frame*	cnext;
	Frame*	anext;
	Frame*	aprev;
	Frame*	anext_old;
	Frame*	snext;
	Frame*	sprev;
	Client*	client;
	View*	view;
	Area*	area;
	int	oldscreen;
	int	oldarea;
	int	screen;
	int	column;
	ushort	id;
	bool	collapsed;
	int	dy;
	Rectangle	r;
	Rectangle	colr;
	Rectangle	colr_old;
	Rectangle	floatr;
	Rectangle	crect;
	Rectangle	grabbox;
	Rectangle	titlebar;
};

struct Group {
	Group*	next;
	XWindow	leader;
	Client*	client;
	int	ref;
};

struct Key {
	Key*	next;
	Key*	lnext;
	Key*	tnext;
	ushort	id;
	char	name[128];
	ulong	mod;
	KeyCode	key;
};

struct Rule {
	Rule*		next;
	Reprog*		regex;
	char*		value;
	Ruleval*	values;
};

struct Ruleset {
	Rule*	rule;
	char*	string;
	uint	size;
};

struct Ruleval {
	Ruleval*	next;
	char*		key;
	char*		value;
};

struct Strut {
	Rectangle	left;
	Rectangle	right;
	Rectangle	top;
	Rectangle	bottom;
};

#define firstarea areas[screen->idx]
#define screenr r[screen->idx]
struct View {
	View*	next;
	char	name[256];
	ushort	id;
	Area*	floating;
	Area**	areas;
	Area*	sel;
	Area*	oldsel;
	Area*	revert;
	int	selcol;
	int	selscreen;
	bool	dead;
	bool	urgent;
	Rectangle *r;
	Rectangle *pad;
};

#ifndef EXTERN
#  define EXTERN extern
#endif

/* global variables */
typedef struct Defs Defs;
EXTERN struct Defs {
	CTuple	focuscolor;
	CTuple	normcolor;
	Font*	font;
	char*	keys;
	uint	keyssz;
	Ruleset	colrules;
	Ruleset	rules;
	long	mod;
	uint	border;
	uint	snap;
	int	colmode;
	int	incmode;
} def;

enum {
	BLeft, BRight
};

EXTERN struct WMScreen {
	Bar*	bar[2];
	Window*	barwin;
	bool	barwin_rgba;
	bool	showing;
	int	barpos;
	int	idx;

	Rectangle r;
	Rectangle brect;
} **screens, *screen;
EXTERN uint	nscreens;

EXTERN struct {
	Client*	focus;
	Client*	hasgrab;
	Image*	ibuf;
	Image*	ibuf32;
	bool	sel;
} disp;

EXTERN Client	c_magic;
EXTERN Client	c_root;
EXTERN Client*	client;
EXTERN Divide*	divs;
EXTERN Key*	key;
EXTERN View*	selview;
EXTERN View*	view;

EXTERN Handlers	framehandler;

/* IXP */
EXTERN IxpServer srv;
EXTERN Ixp9Srv	p9srv;

/* X11 */
EXTERN uint	numlock_mask;
EXTERN uint	valid_mask;

/* Misc */
EXTERN char*	execstr;
EXTERN char	hostname[HOST_NAME_MAX + 1];
EXTERN long	ignoreenter;
EXTERN bool	resizing;
EXTERN int	starting;
EXTERN char*	user;
EXTERN long	nscreens_new;

EXTERN Client*	kludge;

extern char*	debugtab[];

Added cmd/wmii/debug.h.
















































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdbool.h>

enum DebugOpt {
	D9p	= 1<<0,
	DDnd	= 1<<1,
	DEvent	= 1<<2,
	DEvents	= 1<<3,
	DEwmh	= 1<<4,
	DFocus	= 1<<5,
	DGeneric= 1<<6,
	DStack  = 1<<7,
	NDebugOpt = 8,
};

#define Debug(x) if(((debugflag|debugfile)&(x)) && setdebug(x))
#define Dprint(x, ...) BLOCK( if((debugflag|debugfile)&(x)) debug(x, __VA_ARGS__) )

void	debug(int, const char*, ...);
void	dwrite(int, void*, int, bool);
bool	setdebug(int);
void	vdebug(int, const char*, va_list);

long	debugflag;
long	debugfile;
Added cmd/wmii/div.c.
































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
/* Copyright ©2006-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "dat.h"
#include "fns.h"

static Image*	divimg;
static Image*	divmask;
static CTuple	divcolor;
static Handlers	handlers;

static Divide*
getdiv(Divide ***dp) {
	WinAttr wa;
	Divide *d;

	if(**dp)
		d = **dp;
	else {
		d = emallocz(sizeof *d);

		wa.override_redirect = true;
		wa.cursor = cursor[CurDHArrow];
		wa.event_mask =
			  ExposureMask
			| EnterWindowMask
			| ButtonPressMask
			| ButtonReleaseMask;
		d->w = createwindow(&scr.root, Rect(0, 0, 1, 1), scr.depth,
				    InputOutput, &wa,
			  CWOverrideRedirect
			| CWEventMask
			| CWCursor);
		d->w->aux = d;
		sethandler(d->w, &handlers);
		**dp = d;
	}
	*dp = &d->next;
	return d;
}

static void
mapdiv(Divide *d) {
	mapwin(d->w);
}

static void
unmapdiv(Divide *d) {
	unmapwin(d->w);
}

void
div_set(Divide *d, int x) {
	Rectangle r;
	int scrn;

	scrn = d->left ? d->left->screen : d->right->screen;

	d->x = x;
	r = rectaddpt(divimg->r, Pt(x - Dx(divimg->r)/2, 0));
	r.min.y = selview->r[scrn].min.y;
	r.max.y = selview->r[scrn].max.y;

	reshapewin(d->w, r);
	mapdiv(d);
}

static void
drawimg(Image *img, Color cbg, Color cborder, Divide *d) {
	Point pt[8];
	int n, start, w;

	w = Dx(img->r)/2;
	n = 0;
	pt[n++] = Pt(w,		0);
	pt[n++] = Pt(0,		0);
	pt[n++] = Pt(w - 1,	w - 1);

	pt[n++] = Pt(w - 1,	Dy(img->r));
	pt[n++] = Pt(w,		pt[n-1].y);

	pt[n++] = Pt(w,		w - 1);
	pt[n++] = Pt(2*w - 1,	0);
	pt[n++] = Pt(w,		0);

	start = d->left		? 0 : n/2;
	n = d->right && d->left ? n : n/2;

	fillpoly(img, pt + start, n, &cbg);
	drawpoly(img, pt + start, n, CapNotLast, 1, &cborder);
}

static void
drawdiv(Divide *d) {

	fill(divmask, divmask->r, &(Color){0});
	drawimg(divmask, (Color){~0,~0,~0}, (Color){~0,~0,~0}, d);
	drawimg(divimg, divcolor.bg, divcolor.border, d);

	copyimage(d->w, divimg->r, divimg, ZP);
	setshapemask(d->w, divmask, ZP);
}

static void
update_imgs(void) {
	Divide *d;
	int w, h;

	w = 2 * (labelh(def.font) / 3);
	w = max(w, 10);
	/* XXX: Multihead. */
	h = Dy(scr.rect);

	if(divimg) {
		if(w == Dx(divimg->r) && h == Dy(divimg->r)
		&& !memcmp(&divcolor, &def.normcolor, sizeof divcolor))
			return;
		freeimage(divimg);
		freeimage(divmask);
	}

	divimg = allocimage(w, h, scr.depth);
	divmask = allocimage(w, h, 1);
	divcolor = def.normcolor;

	for(d = divs; d && d->w->mapped; d = d->next)
		drawdiv(d);
}

void
div_update_all(void) {
	Divide **dp, *d;
	Area *a, *ap;
	View *v;
	int s;

	update_imgs();

	v = selview;
	dp = &divs;
	ap = nil;
	foreach_column(v, s, a) {
		if(ap && ap->screen != s)
			ap = nil;

		d = getdiv(&dp);
		d->left = ap;
		d->right = a;
		div_set(d, a->r.min.x);
		drawdiv(d);
		ap = a;

		if(!a->next) {
			d = getdiv(&dp);
			d->left = a;
			d->right = nil;
			div_set(d, a->r.max.x);
			drawdiv(d);
		}
	}
	for(d = *dp; d; d = d->next)
		unmapdiv(d);
}

/* Div Handlers */
static bool
bdown_event(Window *w, void *aux, XButtonEvent *e) {
	Divide *d;

	USED(e);

	d = aux;
	mouse_resizecol(d);
	return false;
}

static bool
expose_event(Window *w, void *aux, XExposeEvent *e) {
	Divide *d;

	USED(e);

	d = aux;
	drawdiv(d);
	return false;
}

static Handlers handlers = {
	.bdown = bdown_event,
	.expose = expose_event,
};

Added cmd/wmii/error.c.


















































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/* Copyright ©2007-2010 Kris Maglione <jg@suckless.org>
 * See LICENSE file for license details.
 */

#include "dat.h"
#include "fns.h"

static jmp_buf	errjmp[16];
static long	nerror;

void
error(char *fmt, ...) {
	char errbuf[IXP_ERRMAX];
	va_list ap;

	va_start(ap, fmt);
	vsnprint(errbuf, IXP_ERRMAX, fmt, ap);
	va_end(ap);
	ixp_errstr(errbuf, IXP_ERRMAX);

	nexterror();
}

void
nexterror(void) {
	assert(nerror > 0);
	longjmp(errjmp[--nerror], 1);
}

void
poperror(void) {
	assert(nerror > 0);
	--nerror;
}

jmp_buf*
pusherror(void) {
	assert(nerror < nelem(errjmp));
	return &errjmp[nerror++];
}

Added cmd/wmii/event.c.






























































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
/* Copyright ©2006-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "dat.h"
#include <X11/keysym.h>
#include "fns.h"

void
debug_event(XEvent *e) {
	Dprint(DEvent, "%E\n", e);
}

void
print_focus(const char *fn, Client *c, const char *to) {
	Dprint(DFocus, "%s() disp.focus:\n", fn);
	Dprint(DFocus, "\t%#C => %#C\n", disp.focus, c);
	Dprint(DFocus, "\t%C => %s\n", disp.focus, to);
}

void
event_focusin(XFocusChangeEvent *ev) {
	Window *w;
	Client *c;

	/* Yes, we're focusing in on nothing, here. */
	if(ev->detail == NotifyDetailNone) {
		print_focus("focusin", &c_magic, "<magic[none]>");
		disp.focus = &c_magic;
		setfocus(screen->barwin, RevertToParent);
		return;
	}

	if(!((ev->detail == NotifyNonlinear)
	   ||(ev->detail == NotifyNonlinearVirtual)
	   ||(ev->detail == NotifyVirtual)
	   ||(ev->detail == NotifyInferior)
	   ||(ev->detail == NotifyAncestor)))
		return;
	if((ev->mode == NotifyWhileGrabbed) && (disp.hasgrab != &c_root))
		return;

	if(ev->window == screen->barwin->xid) {
		print_focus("focusin", nil, "<nil>");
		disp.focus = nil;
	}
	else if((w = findwin(ev->window)))
		event_handle(w, focusin, ev);
	else if(ev->mode == NotifyGrab) {
		/* Some unmanaged window has grabbed focus */
		if((c = disp.focus)) {
			print_focus("focusin", &c_magic, "<magic>");
			disp.focus = &c_magic;
			if(c->sel)
				frame_draw(c->sel);
		}
	}
}

void
event_focusout(XFocusChangeEvent *ev) {
	XEvent me;
	Window *w;

	if(!((ev->detail == NotifyNonlinear)
	   ||(ev->detail == NotifyNonlinearVirtual)
	   ||(ev->detail == NotifyVirtual)
	   ||(ev->detail == NotifyInferior)
	   ||(ev->detail == NotifyAncestor)))
		return;
	if(ev->mode == NotifyUngrab)
		disp.hasgrab = nil;

	if((ev->mode == NotifyGrab)
	&& XCheckMaskEvent(display, KeyPressMask, &me))
		event_dispatch(&me);
	else if((w = findwin(ev->window)))
		event_handle(w, focusout, ev);
}

Added cmd/wmii/ewmh.c.






















































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "dat.h"
#include <limits.h>
#include "fns.h"

Window *ewmhwin;

static void	ewmh_getwinstate(Client*);
static void	ewmh_setstate(Client*, Atom, int);
static void	tick(long, void*);

static Handlers	client_handlers;
static Handlers	root_handlers;

static void
clientprop_long(Client *c, int cache, char *prop, char *type, long *data, int l) {
	if(l != c->proplen[cache] || memcmp(&c->propcache[cache], data, l * sizeof *data)) {
		c->proplen[cache] = l;
		memcpy(&c->propcache[cache], data, l * sizeof *data);
		changeprop_long(&c->w, prop, type, data, l);
	}
}
static void
clientprop_del(Client *c, int cache, char *prop) {
	c->proplen[cache] = 0;
	delproperty(&c->w, prop);
}

void
ewmh_init(void) {
	char myname[] = "wmii";
	long win;

	ewmhwin = createwindow(&scr.root,
		Rect(0, 0, 1, 1), 0 /*depth*/,
		InputOnly, nil, 0);

	win = ewmhwin->xid;
	changeprop_long(&scr.root, Net("SUPPORTING_WM_CHECK"), "WINDOW", &win, 1);
	changeprop_long(ewmhwin, Net("SUPPORTING_WM_CHECK"), "WINDOW", &win, 1);
	changeprop_string(ewmhwin, Net("WM_NAME"), myname);

	changeprop_long(&scr.root, Net("DESKTOP_VIEWPORT"), "CARDINAL",
			(long[2]){0, 0}, 2);

	pushhandler(&scr.root, &root_handlers, nil);

	tick(0L, nil);

	long supported[] = {
		/* Misc */
		NET("SUPPORTED"),
		/* Root Properties/Messages */
		NET("ACTIVE_WINDOW"),
		NET("CLOSE_WINDOW"),
		NET("CURRENT_DESKTOP"),
		/* Client Properties */
		NET("FRAME_EXTENTS"),
		NET("WM_DESKTOP"),
		NET("WM_FULLSCREEN_MONITORS"),
		NET("WM_NAME"),
		NET("WM_PID"),
		NET("WM_STRUT"),
		NET("WM_STRUT_PARTIAL"),
		/* Set this so clients don't update Net("USER_TIME") */
		NET("USER_TIME_WINDOW"),
		/* States */
		NET("WM_STATE"),
		STATE("DEMANDS_ATTENTION"),
		STATE("FULLSCREEN"),
		STATE("SHADED"),
		/* Window Types */
		NET("WM_WINDOW_TYPE"),
		TYPE("DIALOG"),
		TYPE("DOCK"),
		TYPE("NORMAL"),
		TYPE("MENU"),
		TYPE("SPLASH"),
		TYPE("TOOLBAR"),
		/* Actions */
		NET("WM_ALLOWED_ACTIONS"),
		ACTION("FULLSCREEN"),
		/* Desktops */
		NET("DESKTOP_NAMES"),
		NET("NUMBER_OF_DESKTOPS"),
		/* Client List */
		NET("CLIENT_LIST"),
		NET("CLIENT_LIST_STACKING"),
	};
	changeprop_long(&scr.root, Net("SUPPORTED"), "ATOM", supported, nelem(supported));
}

inline bool
ewmh_responsive_p(Client *c) {
	return c->w.ewmh.ping == 0 || (ulong)(nsec() / 1000000) - c->w.ewmh.ping < PingTime;
}

void
ewmh_checkresponsive(Client *c) {

	if(!ewmh_responsive_p(c))
		if(!c->dead)
			frame_draw(c->sel);
		else if(c->dead++ == 1)
			event("Unresponsive %#C\n", c);
}

static void
tick(long id, void *v) {
	static int count;
	Client *c;
	ulong time;
	int mod, i;

	time = nsec() / 1000000;
	count++;
	mod = count % PingPartition;
	for(i=0, c=client; c; c=c->next, i++)
		if(c->proto & ProtoPing) {
			if(!ewmh_responsive_p(c))
				ewmh_checkresponsive(c);
			if(i % PingPartition == mod)
				sendmessage(&c->w, "WM_PROTOCOLS", NET("WM_PING"), time, c->w.xid, 0, 0);
			if(i % PingPartition == mod)
				Dprint(DEwmh, "_NET_WM_PING %#C %,uld\n", c, time);
		}

	ixp_settimer(&srv, PingPeriod / PingPartition, tick, nil);
}

void
ewmh_updateclientlist(void) {
	Vector_long vec;
	Client *c;

	vector_linit(&vec);
	for(c=client; c; c=c->next)
		vector_lpush(&vec, c->w.xid);
	changeprop_long(&scr.root, Net("CLIENT_LIST"), "WINDOW", vec.ary, vec.n);
	free(vec.ary);
}

void
ewmh_updatestacking(void) {
	Vector_long vec;
	Frame *f;
	Area *a;
	View *v;
	int s;

	vector_linit(&vec);

	for(v=view; v; v=v->next) {
		foreach_column(v, s, a)
			for(f=a->frame; f; f=f->anext)
				if(f->client->sel == f)
					vector_lpush(&vec, f->client->w.xid);
	}
	for(v=view; v; v=v->next) {
		for(f=v->floating->stack; f; f=f->snext)
			if(!f->snext) break;
		for(; f; f=f->sprev)
			if(f->client->sel == f)
				vector_lpush(&vec, f->client->w.xid);
	}

	changeprop_long(&scr.root, Net("CLIENT_LIST_STACKING"), "WINDOW", vec.ary, vec.n);
	vector_lfree(&vec);
}

void
ewmh_initclient(Client *c) {
	long allowed[] = {
		ACTION("FULLSCREEN"),
	};

	changeprop_long(&c->w, Net("WM_ALLOWED_ACTIONS"), "ATOM",
		allowed, nelem(allowed));
	ewmh_getwintype(c);
	ewmh_getwinstate(c);
	ewmh_getstrut(c);
	ewmh_framesize(c);
	ewmh_updateclientlist();
	pushhandler(&c->w, &client_handlers, c);
}

void
ewmh_destroyclient(Client *c) {

	ewmh_updateclientlist();

	free(c->strut);
}

#ifdef notdef
static ulong
usertime(Window *w) {
	long *l;
	long ret;

	ret = 0;
	if(getprop_long(w, Net("WM_USER_TIME_WINDOW"), "CARDINAL", 0, &l, 1)) {
		w = window(*l);
		free(l);
	}
	if(getprop_long(w, Net("WM_USER_TIME"), "CARDINAL", 0, &l, 1)) {
		ret = *l;
		free(l);
	}
	return ret;
}
#endif

static bool
event_client_clientmessage(Window *w, void *aux, XClientMessageEvent *e) {
	Client *c;
	ulong *l;
	ulong msg;
	int action;

	c = aux;
	l = (ulong*)e->data.l;
	msg = e->message_type;
	Dprint(DEwmh, "ClientMessage: %A\n", msg);

	if(msg == NET("WM_STATE")) {
		enum {
			StateUnset,
			StateSet,
			StateToggle,
		};
		if(e->format != 32)
			return false;

		switch(l[0]) {
		case StateUnset:  action = Off;    break;
		case StateSet:    action = On;     break;
		case StateToggle: action = Toggle; break;
		default: return false;
		}

		Dprint(DEwmh, "\tAction: %s\n", TOGGLE(action));
		ewmh_setstate(c, l[1], action);
		ewmh_setstate(c, l[2], action);
		return false;
	}else
	if(msg == NET("ACTIVE_WINDOW")) {
		if(e->format != 32)
			return false;
		Dprint(DEwmh, "\tsource:    %uld\n", l[0]);
		Dprint(DEwmh, "\ttimestamp: %,uld\n", l[1]);
		Dprint(DEwmh, "\tactive:    %#ulx\n", l[2]);
		Dprint(DEwmh, "\twindow:    %#ulx\n", e->window);
		Dprint(DEwmh, "\tclient:    %C\n", c);

		if(l[0] == SourceClient && !(c->permission & PermActivate))
			return false;
		if(l[0] == SourceClient || l[0] == SourcePager)
			focus(c, true);
		return false;
	}else
	if(msg == NET("CLOSE_WINDOW")) {
		if(e->format != 32)
			return false;
		Dprint(DEwmh, "\tsource: %ld\n", l[0]);
		Dprint(DEwmh, "\twindow: %#ulx\n", e->window);
		client_kill(c, true);
		return false;
	}

	return false;
}

static bool
event_client_property(Window *w, void *aux, XPropertyEvent *e) {
	return ewmh_prop(aux, e->atom);
}

static Handlers client_handlers = {
	.message = event_client_clientmessage,
	.property = event_client_property,
};

bool
ewmh_prop(Client *c, Atom a) {
	if(a == NET("WM_WINDOW_TYPE"))
		ewmh_getwintype(c);
	else
	if(a == NET("WM_STRUT_PARTIAL"))
		ewmh_getstrut(c);
	else
		return true;
	return false;
}

typedef struct Prop Prop;
struct Prop {
	char*	name;
	long	mask;
	Atom	atom;
};

static long
getmask(Prop *props, ulong *vals, int n) {
	Prop *p;
	long ret;

	if(props[0].atom == 0)
		for(p=props; p->name; p++)
			p->atom = xatom(p->name);

	ret = 0;
	while(n--) {
		Dprint(DEwmh, "\tvals[%d] = \"%A\"\n", n, vals[n]);
		for(p=props; p->name; p++)
			if(p->atom == vals[n]) {
				ret |= p->mask;
				break;
			}
	}
	return ret;
}

static long
getprop_mask(Window *w, char *prop, Prop *props) {
	ulong *vals;
	long n, mask;

	n = getprop_ulong(w, prop, "ATOM",
		0L, &vals, 16);
	mask = getmask(props, vals, n);
	free(vals);
	return mask;
}

void
ewmh_getwintype(Client *c) {
	static Prop props[] = {
		{Type("DESKTOP"), TypeDesktop},
		{Type("DOCK"),    TypeDock},
		{Type("TOOLBAR"), TypeToolbar},
		{Type("MENU"),    TypeMenu},
		{Type("UTILITY"), TypeUtility},
		{Type("SPLASH"),  TypeSplash},
		{Type("DIALOG"),  TypeDialog},
		{Type("NORMAL"),  TypeNormal},
		{0, }
	};
	long mask;

	mask = getprop_mask(&c->w, Net("WM_WINDOW_TYPE"), props);

	c->w.ewmh.type = mask;
	if(mask & (TypeDock|TypeMenu|TypeToolbar)) {
		c->borderless = true;
		c->titleless = true;
	}
	if(mask & (TypeSplash|TypeDock))
		c->nofocus = true;
}

static void
ewmh_getwinstate(Client *c) {
	ulong *vals;
	long n;

	n = getprop_ulong(&c->w, Net("WM_STATE"), "ATOM",
		0L, &vals, 16);
	while(--n >= 0)
		ewmh_setstate(c, vals[n], On);
	free(vals);
}

long
ewmh_protocols(Window *w) {
	static Prop props[] = {
		{"WM_DELETE_WINDOW", ProtoDelete},
		{"WM_TAKE_FOCUS", ProtoTakeFocus},
		{Net("WM_PING"), ProtoPing},
		{0, }
	};

	return getprop_mask(w, "WM_PROTOCOLS", props);
}

void
ewmh_getstrut(Client *c) {
	enum {
		Left, Right, Top, Bottom,
		LeftMin, LeftMax,
		RightMin, RightMax,
		TopMin, TopMax,
		BottomMin, BottomMax,
		Last
	};
	long *strut;
	ulong n;

	if(c->strut != nil)
		free(c->strut);
	c->strut = nil;

	n = getprop_long(&c->w, Net("WM_STRUT_PARTIAL"), "CARDINAL",
		0L, &strut, Last);
	if(n != Last) {
		free(strut);
		n = getprop_long(&c->w, Net("WM_STRUT"), "CARDINAL",
			0L, &strut, 4L);
		if(n != 4) {
			free(strut);
			return;
		}
		Dprint(DEwmh, "ewmh_getstrut(%#C[%C]) Using WM_STRUT\n", c, c);
		strut = erealloc(strut, Last * sizeof *strut);
		strut[LeftMin] = strut[RightMin] = 0;
		strut[LeftMax] = strut[RightMax] = INT_MAX;
		strut[TopMin] = strut[BottomMin] = 0;
		strut[TopMax] = strut[BottomMax] = INT_MAX;
	}
	c->strut = emalloc(sizeof *c->strut);
	c->strut->left =   Rect(0,                strut[LeftMin],  strut[Left],      strut[LeftMax]);
	c->strut->right =  Rect(-strut[Right],    strut[RightMin], 0,                strut[RightMax]);
	c->strut->top =    Rect(strut[TopMin],    0,               strut[TopMax],    strut[Top]);
	c->strut->bottom = Rect(strut[BottomMin], -strut[Bottom],  strut[BottomMax], 0);
	Dprint(DEwmh, "ewmh_getstrut(%#C[%C])\n", c, c);
	Dprint(DEwmh, "\ttop: %R\n", c->strut->top);
	Dprint(DEwmh, "\tleft: %R\n", c->strut->left);
	Dprint(DEwmh, "\tright: %R\n", c->strut->right);
	Dprint(DEwmh, "\tbottom: %R\n", c->strut->bottom);
	free(strut);
	view_update(selview);
}

static void
ewmh_setstate(Client *c, Atom state, int action) {

	Dprint(DEwmh, "\tSTATE = %A\n", state);
	if(state == 0)
		return;

	if(state == STATE("FULLSCREEN"))
		fullscreen(c, action, -1);
	else
	if(state == STATE("DEMANDS_ATTENTION"))
		client_seturgent(c, action, UrgClient);
}

static bool
event_root_clientmessage(Window *w, void *aux, XClientMessageEvent *e) {
	Client *c;
	View *v;
	ulong *l;
	ulong msg;
	int i;

	l = (ulong*)e->data.l;
	msg = e->message_type;
	Debug(DEwmh)
		if(msg != xatom("WM_PROTOCOLS") && l[0] != NET("WM_PING"))
			Dprint(DEwmh, "ClientMessage: %A\n", msg);

	if(msg == NET("CURRENT_DESKTOP")) {
		if(e->format != 32)
			return false;
		for(v=view, i=l[0]; v; v=v->next, i--)
			if(i == 0)
				break;
		Dprint(DEwmh, "\t%s\n", v->name);
		if(i == 0)
			view_select(v->name);
		return 1;
	}
	if(msg == xatom("WM_PROTOCOLS")) {
		if(e->format != 32)
			return false;
		if(l[0] == NET("WM_PING")) {
			if(e->window != scr.root.xid)
				return false;
			if(!(c = win2client(l[2])))
				return false;
			i = ewmh_responsive_p(c);
			c->w.ewmh.ping = nsec() / 1000000;
			c->w.ewmh.lag = (c->w.ewmh.ping & 0xffffffff) - (l[1] & 0xffffffff);
			if(i == false)
				frame_draw(c->sel);
			return false;
		}
		return false;
	}

	return false;
}

static Handlers root_handlers = {
	.message = event_root_clientmessage,
};


void
ewmh_framesize(Client *c) {
	Rectangle rc, rf;
	Frame *f;

	if((f = c->sel)) {
		rc = f->crect;
		rf = f->r;
	}
	else {
		rf = frame_client2rect(c, ZR, c->floating);
		rc = rectsubpt(ZR, rf.min);
	}

	long extents[] = {
		rc.min.x, Dx(rf) - rc.max.x,
		rc.min.y, Dy(rf) - rc.max.y,
	};
	clientprop_long(c, PExtents, Net("FRAME_EXTENTS"), "CARDINAL",
			extents, nelem(extents));
}

void
ewmh_updatestate(Client *c) {
	long state[16];
	Frame *f;
	int i;

	f = c->sel;
	if(f == nil || f->view != selview)
		return;

	i = 0;
	if(f->collapsed)
		state[i++] = STATE("SHADED");
	if(c->fullscreen >= 0)
		state[i++] = STATE("FULLSCREEN");
	if(c->urgent)
		state[i++] = STATE("DEMANDS_ATTENTION");

	if(i > 0)
		clientprop_long(c, PState, Net("WM_STATE"), "ATOM", state, i);
	else
		clientprop_del(c, PState, Net("WM_STATE"));

	if(c->fullscreen >= 0)
		clientprop_long(c, PMonitors, Net("WM_FULLSCREEN_MONITORS"), "CARDINAL",
				(long[]) { c->fullscreen, c->fullscreen,
					   c->fullscreen, c->fullscreen },
				4);
	else
		clientprop_del(c, PMonitors, Net("WM_FULLSCREEN_MONITORS"));
}

/* Views */
void
ewmh_updateviews(void) {
	View *v;
	Vector_ptr tags;
	long i;

	if(starting)
		return;

	vector_pinit(&tags);
	for(v=view, i=0; v; v=v->next, i++)
		vector_ppush(&tags, v->name);
	vector_ppush(&tags, nil);
	changeprop_textlist(&scr.root, Net("DESKTOP_NAMES"), "UTF8_STRING", (char**)tags.ary);
	changeprop_long(&scr.root, Net("NUMBER_OF_DESKTOPS"), "CARDINAL", &i, 1);
	vector_pfree(&tags);
	ewmh_updateview();
	ewmh_updateclients();
}

static int
viewidx(View *v) {
	View *vp;
	int i;

	for(vp=view, i=0; vp; vp=vp->next, i++)
		if(vp == v)
			break;
	assert(vp);
	return i;
}

void
ewmh_updateview(void) {
	long i;

	if(starting)
		return;

	i = viewidx(selview);
	changeprop_long(&scr.root, Net("CURRENT_DESKTOP"), "CARDINAL", &i, 1);
}

void
ewmh_updateclient(Client *c) {
	long i;

	i = -1;
	if(c->sel)
		i = viewidx(c->sel->view);
	clientprop_long(c, PDesktop, Net("WM_DESKTOP"), "CARDINAL", &i, 1);
}

void
ewmh_updateclients(void) {
	Client *c;

	if(starting)
		return;

	for(c=client; c; c=c->next)
		ewmh_updateclient(c);
}

Added cmd/wmii/float.c.




























































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
/* Copyright ©2006-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "dat.h"
#include <limits.h>
#include "fns.h"

static void float_placeframe(Frame*);

void
float_attach(Area *a, Frame *f) {

	if(f->client->floating == Off)
		f->client->floating = On;

	f->r = f->floatr;
	float_placeframe(f);
	assert(a->sel != f);
	frame_insert(f, a->sel);

	if(a->sel == nil)
		area_setsel(a, f);
}

void
float_detach(Frame *f) {
	Frame *pr;
	Area *a, *sel, *oldsel;
	View *v;

	v = f->view;
	a = f->area;
	sel = view_findarea(v, v->selscreen, v->selcol, false);
	oldsel = v->oldsel;
	pr = f->aprev;

	frame_remove(f);

	if(a->sel == f) {
		while(pr && pr->client->nofocus)
			pr = pr->aprev;
		if(!pr)
			for(pr=a->frame; pr && pr->anext; pr=pr->anext)
				if(!pr->client->nofocus) break;
		a->sel = nil;
		area_setsel(a, pr);
	}
	f->area = nil;

	if(oldsel)
		area_focus(oldsel);
	else if(!a->frame || pr && pr->client->nofocus)
		if(sel && sel->frame)
			area_focus(sel);
}

void
float_resizeframe(Frame *f, Rectangle r) {

	if(f->area->view == selview)
		client_resize(f->client, r);
	else
		frame_resize(f, r);
}

void
float_arrange(Area *a) {
	Frame *f;

	assert(a->floating);

	switch(a->mode) {
	case Coldefault:
		break;
	case Colstack:
		for(f=a->frame; f; f=f->anext)
			f->collapsed = !(f->client->w.ewmh.type & (TypeDock|TypeMenu|TypeToolbar))
				    && (f != a->sel);
		break;
	default:
		die("not reached");
		break;
	}
	for(f=a->frame; f; f=f->anext)
		f->r = f->floatr;
	view_update(a->view);
}

static void
rect_push(Vector_rect *vec, Rectangle r) {
	Rectangle *rp;
	int i;

	for(i=0; i < vec->n; i++) {
		rp = &vec->ary[i];
		if(rect_contains_p(*rp, r))
			return;
		if(rect_contains_p(r, *rp)) {
			*rp = r;
			return;
		}
	}
	vector_rpush(vec, r);
}

Vector_rect*
unique_rects(Vector_rect *vec, Rectangle orig) {
	static Vector_rect vec1, vec2;
	Vector_rect *v1, *v2, *v;
	Rectangle r1, r2;
	int i, j;

	v1 = &vec1;
	v2 = &vec2;
	v1->n = 0;
	vector_rpush(v1, orig);
	for(i=0; i < vec->n; i++) {
		v2->n = 0;
		r1 = vec->ary[i];
		for(j=0; j < v1->n; j++) {
			r2 = v1->ary[j];
			if(!rect_intersect_p(r1, r2)) {
				rect_push(v2, r2);
				continue;
			}
			if(r2.min.x < r1.min.x)
				rect_push(v2, Rect(r2.min.x, r2.min.y, r1.min.x, r2.max.y));
			if(r2.min.y < r1.min.y)
				rect_push(v2, Rect(r2.min.x, r2.min.y, r2.max.x, r1.min.y));
			if(r2.max.x > r1.max.x)
				rect_push(v2, Rect(r1.max.x, r2.min.y, r2.max.x, r2.max.y));
			if(r2.max.y > r1.max.y)
				rect_push(v2, Rect(r2.min.x, r1.max.y, r2.max.x, r2.max.y));
		}
		v = v1;
		v1 = v2;
		v2 = v;
	}
	return v1;
}

Rectangle
max_rect(Vector_rect *vec) {
	Rectangle *r, *rp;
	int i, a, area;

	area = 0;
	r = 0;
	for(i=0; i < vec->n; i++) {
		rp = &vec->ary[i];
		a = Dx(*rp) * Dy(*rp);
		if(a > area) {
			area = a;
			r = rp;
		}
	}
	return r ? *r : ZR;
}

static void
float_placeframe(Frame *f) {
	static Vector_rect vec;
	Vector_rect *vp;
	Rectangle r;
	Point dim, p;
	Area *a, *sel;
	Client *c;
	Frame *ff;
	View *v;
	long area, l;
	int i, s;

	v = f->view;
	a = f->area;
	c = f->client;

	/*
	if(c->trans)
		return;
	*/

	if(c->fullscreen >= 0 || c->w.hints->position || starting) {
		f->r = f->floatr;
		return;
	}

	/* Find all rectangles on the floating layer into which
	 * the new frame would fit.
	 */
	vec.n = 0;
	for(ff=a->frame; ff; ff=ff->anext)
		/* TODO: Find out why this check is needed.
		 * The frame hasn't been inserted yet, but somehow,
		 * its old rectangle winds up in the list.
		 */
		if(ff->client != f->client)
			vector_rpush(&vec, ff->r);

	/* Decide which screen we want to place this on.
	 * Ideally, it should probably Do the Right Thing
	 * when a screen fills, but what's the right thing?
	 * I think usage will show...
	 */
	s = -1;
	ff = client_groupframe(c, f->view);
	if(f->screen >= 0)
		s = f->screen;
	else if(ff)
		s = ownerscreen(ff->r);
	else if(selclient())
		s = ownerscreen(selclient()->sel->r);
	else {
		sel = view_findarea(a->view, a->view->selscreen, a->view->selcol, false);
		if(sel)
			s = sel->screen;
	}

	if (s == -1)
		r = a->r;
	else
		r = v->r[s];

	vp = unique_rects(&vec, r);

	area = LONG_MAX;
	dim.x = Dx(f->r);
	dim.y = Dy(f->r);
	p = ZP;

	for(i=0; i < vp->n; i++) {
		r = vp->ary[i];
		if(Dx(r) < dim.x || Dy(r) < dim.y)
			continue;
		l = Dx(r) * Dy(r);
		if(l < area) {
			area = l;
			p = r.min;
		}
	}

	if(area == LONG_MAX) {
		/* Cascade. */
		s = max(s, 0);
		ff = a->sel;
		if(ff)
			p = addpt(ff->r.min, Pt(Dy(ff->titlebar), Dy(ff->titlebar)));
		if(p.x + Dx(f->r) > screens[s]->r.max.x ||
		   p.y + Dy(f->r) > screens[s]->r.max.y)
			p = screens[s]->r.min;
	}

	f->floatr = rectsetorigin(f->r, p);
}

Added cmd/wmii/fns.h.












































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
/* Copyright ©2007-2014 Kris Maglione <jg@suckless.org>
 * See LICENSE file for license details.
 */

#include <setjmp.h>

#ifdef VARARGCK
# pragma varargck	argpos	debug	2
# pragma varargck	argpos	dprint	1
# pragma varargck	argpos	event	1
# pragma varargck	argpos	warning	1
#
# pragma varargck	type	"a"	Area*
# pragma varargck	type	"C"	Client*
# pragma varargck	type	"r"	void
#endif

#define _cond(cond, n) (cond) && __alive++ == n
#define _cont(cont) (void)(__alive--, cont)

#define with(type, var) \
	for(type var=(type)-1; (var == (type)-1) && ((var=0) || true);)

/* Grotesque, but worth it. */

#define foreach_area(v, s, a) \
	with(int, __alive)				\
	with(Area*, __anext)				\
	for(s=0; _cond(s <= nscreens, 0); _cont(s++))	\
		for((a)=(s < nscreens ? (v)->areas[s] : v->floating), __anext=((a)?(a)->next:NULL); \
		    _cond(a, 1);			\
		    _cont(((a)=__anext) && (__anext=(a)->next)))

#define foreach_column(v, s, a) \
	with(int, __alive)				\
	with(Area*, __anext)				\
	for(s=0; _cond(s < nscreens, 0); _cont(s++))	\
		for((a)=(v)->areas[s], __anext=((a)?(a)->next:NULL);	\
		    _cond(a, 1);				\
		    _cont(((a)=__anext) && (__anext=(a)->next)))

#define foreach_frame(v, s, a, f) \
	with(Frame*, __fnext)     \
	foreach_area(v, s, a)     \
		for((void)(((f)=(a)->frame) && (__fnext=(f)?((f)->anext):NULL));	\
		    _cond(f, 2);					\
		    _cont(((f)=__fnext) && (__fnext=(f)->anext)))

#define btassert(arg, cond) \
	(cond ? fprint(1, __FILE__":%d: failed assertion: " #cond "\n", __LINE__), backtrace(arg), true : false)

/* area.c */
int	afmt(Fmt*);
void	area_attach(Area*, Frame*);
Area*	area_create(View*, Area *pos, int scrn, uint w);
void	area_destroy(Area*);
void	area_detach(Frame*);
Area*	area_find(View*, Rectangle, int, bool);
void	area_focus(Area*);
int	area_idx(Area*);
void	area_moveto(Area*, Frame*);
char*	area_name(Area*);
Client*	area_selclient(Area*);
void	area_setsel(Area*, Frame*);

/* bar.c */
Bar*	bar_create(Bar**, const char*);
void	bar_destroy(Bar**, Bar*);
void	bar_draw(WMScreen*);
Bar*	bar_find(Bar*, const char*);
void	bar_init(WMScreen*);
void	bar_resize(WMScreen*);
void	bar_sety(WMScreen*, int);
void	bar_setbounds(WMScreen*, int, int);

/* client.c */
int	Cfmt(Fmt *f);
bool	client_applytags(Client*, const char*);
void	client_configure(Client*);
Client*	client_create(XWindow, XWindowAttributes*);
void	client_destroy(Client*);
char*	client_extratags(Client*);
bool	client_floats_p(Client*);
void	client_focus(Client*);
Frame*	client_groupframe(Client*, View*);
void	client_kill(Client*, bool);
void	client_manage(Client*);
void	client_map(Client*);
void	client_message(Client*, char*, long);
bool	client_prop(Client*, Atom);
void	client_reparent(Client*);
void	client_resize(Client*, Rectangle);
void	client_setcursor(Client*, Cursor);
void	client_seturgent(Client*, int, int);
void	client_setviews(Client*, char**);
void	client_unmap(Client*, int state);
Frame*	client_viewframe(Client *c, View *v);
void	focus(Client*, bool user);
void	fullscreen(Client*, int, long);
void	group_init(Client*);
Client*	group_leader(Group*);
void	group_remove(Client*);
int	client_mapframe(Client*);
Client*	selclient(void);
int	client_unmapframe(Client*);
void	update_class(Client*);
Client*	win2client(XWindow);
Rectangle	client_grav(Client*, Rectangle);

/* column.c */
bool	column_setmode(Area*, const char*);
char*	column_getmode(Area*);
void	column_arrange(Area*, bool dirty);
void	column_attach(Area*, Frame*);
void	column_attachrect(Area*, Frame*, Rectangle);
void	column_destroy(Area*);
void	column_detach(Frame*);
void	column_frob(Area*);
void	column_insert(Area*, Frame*, Frame*);
int	column_minwidth(void);
Area*	column_new(View*, Area*, int, uint);
void	column_remove(Frame*);
void	column_resize(Area*, int);
void	column_resizeframe(Frame*, Rectangle);
void	column_settle(Area*);
void	div_draw(Divide*);
void	div_set(Divide*, int x);
void	div_update_all(void);

/* error.c */
#define waserror() setjmp(*pusherror())
void	error(char*, ...);
void	nexterror(void);
void	poperror(void);
jmp_buf*	pusherror(void);

/* event.c */
void	debug_event(XEvent*);
void	print_focus(const char*, Client*, const char*);

/* ewmh.c */
void	ewmh_checkresponsive(Client*);
void	ewmh_destroyclient(Client*);
void	ewmh_framesize(Client*);
void	ewmh_getstrut(Client*);
void	ewmh_getwintype(Client*);
void	ewmh_init(void);
void	ewmh_initclient(Client*);
bool	ewmh_prop(Client*, Atom);
long	ewmh_protocols(Window*);
bool	ewmh_responsive_p(Client*);
void	ewmh_updateclient(Client*);
void	ewmh_updateclientlist(void);
void	ewmh_updateclients(void);
void	ewmh_updatestacking(void);
void	ewmh_updatestate(Client*);
void	ewmh_updateview(void);
void	ewmh_updateviews(void);

/* float.c */
void	float_arrange(Area*);
void	float_attach(Area*, Frame*);
void	float_detach(Frame*);
void	float_resizeframe(Frame*, Rectangle);
Vector_rect*	unique_rects(Vector_rect*, Rectangle);
Rectangle	max_rect(Vector_rect*);

/* frame.c */
Frame*	frame_create(Client*, View*);
int	frame_delta_h(void);
void	frame_draw(Frame*);
void	frame_draw_all(void);
void	frame_focus(Frame*);
uint	frame_idx(Frame*);
void	frame_insert(Frame*, Frame *pos);
void	frame_remove(Frame*);
void	frame_resize(Frame*, Rectangle);
bool	frame_restack(Frame*, Frame*);
void	frame_swap(Frame*, Frame*);
int	ingrabbox_p(Frame*, int x, int y);
void	move_focus(Frame*, Frame*);
Rectangle constrain(Rectangle, int);
Rectangle frame_client2rect(Client*, Rectangle, bool);
WinHints  frame_gethints(Frame*);
Rectangle frame_hints(Frame*, Rectangle, Align);
Rectangle frame_rect2client(Client*, Rectangle, bool);

/* fs.c */
void	fs_attach(Ixp9Req*);
void	fs_clunk(Ixp9Req*);
void	fs_create(Ixp9Req*);
void	fs_flush(Ixp9Req*);
void	fs_freefid(IxpFid*);
void	fs_open(Ixp9Req*);
void	fs_read(Ixp9Req*);
void	fs_remove(Ixp9Req*);
void	fs_stat(Ixp9Req*);
void	fs_walk(Ixp9Req*);
void	fs_write(Ixp9Req*);
void	event(const char*, ...);

/* key.c */
void	init_lock_keys(void);
void	kpress(XWindow, ulong mod, KeyCode);
void	update_keys(void);

/* main.c */
void	init_screens(void);
void	spawn_command(const char*);
void	wipe_screens(void);

/* message.c */
char*	mask(char**, int*, int*);
char*	message_bar(Bar*, IxpMsg*);
char*	message_client(Client*, IxpMsg*);
char*	message_root(void*, IxpMsg*);
char*	message_view(View*, IxpMsg*);
void	msg_debug(char*);
void	msg_eatrunes(IxpMsg*, int (*)(Rune), int);
char*	msg_getword(IxpMsg*, char*);
void	msg_parsecolors(IxpMsg*, CTuple*);
char*	msg_selectarea(Area*, IxpMsg*);
char*	msg_sendclient(View*, IxpMsg*, bool swap);
char*	readctl_bar(Bar*);
char*	readctl_client(Client*);
char*	readctl_root(void);
char*	readctl_view(View*);
Area*	strarea(View*, ulong, const char*);
void	warning(const char*, ...);

/* mouse.c */
Window*	constraintwin(Rectangle);
void	destroyconstraintwin(Window*);
void	grab_button(XWindow, uint button, ulong mod);
void	mouse_checkresize(Frame*, Point, bool);
void	mouse_movegrabbox(Client*, bool);
void	mouse_resize(Client*, Align, bool);
void	mouse_resizecol(Divide*);
bool	readmotion(Point*);
int	readmouse(Point*, uint*);
Align	snap_rect(const Rectangle *rects, int num, Rectangle *current, Align *mask, int snapw);

/* print.c */
int	Ffmt(Fmt*);

/* root.c */
void	root_init(void);

/* screen.c */
void*	findthing(Rectangle, int, Vector_ptr*, Rectangle(*)(void*), bool);
int	ownerscreen(Rectangle);

/* rule.c */
void	update_rules(Rule**, char*);

/* stack.c */
bool	find(Area* *, Frame**, int, bool, bool);
int	stack_count(Frame*, int*);
Frame*	stack_find(Area*, Frame*, int, bool);
void	stack_info(Frame*, Frame**, Frame**, int*, int*);
void	stack_scale(Frame*, int);


/* view.c */
void	view_arrange(View*);
void	view_attach(View*, Frame*);
View*	view_create(const char*);
void	view_destroy(View*);
void	view_detach(Frame*);
Area*	view_findarea(View*, int, int, bool);
void	view_focus(WMScreen*, View*);
bool	view_fullscreen_p(View*, int);
char*	view_index(View*);
void	view_init(View*, int iscreen);
char**	view_names(void);
uint	view_newcolwidth(View*, int, int);
void	view_restack(View*);
void	view_scale(View*, int, int);
Client*	view_selclient(View*);
void	view_select(const char*);
void	view_update(View*);
void	view_update_all(void);
void	view_update_rect(View*);
void	view_update_screens(View*);
void	view_update_urgency(View*, char*);
Rectangle*	view_rects(View*, uint *num, Frame *ignore);

/* utf.c */
char*	toutf8(const char*);
char*	toutf8n(const char*, size_t);

/* xdnd.c */
void	xdnd_initwindow(Window*);

Added cmd/wmii/frame.c.






























































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
/* Copyright ©2006-2014 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "dat.h"
#include <math.h>
#include "fns.h"

uint
frame_idx(Frame *f) {
	Frame *fp;
	uint i;

	fp = f->area->frame;
	for(i = 1; fp != f; fp = fp->anext)
		i++;
	return i;
}

Frame*
frame_create(Client *c, View *v) {
	static ushort id = 1;
	Frame *f;

	f = emallocz(sizeof *f);
	f->id = id++;
	f->client = c;
	f->view = v;

	if(c->sel) {
		f->floatr = c->sel->floatr;
		f->r = c->sel->r;
	}else if(c->sel) {
		f->floatr = c->frame->floatr;
		f->r = c->frame->r;
	}else {
		f->r = client_grav(c, c->r);
		f->floatr = f->r;
		c->sel = f;
	}
	f->collapsed = false;
	f->screen = -1;
	f->oldarea = -1;
	f->oldscreen = -1;

	return f;
}

void
frame_remove(Frame *f) {
	Area *a;

	a = f->area;
	if(f->aprev)
		f->aprev->anext = f->anext;
	if(f->anext)
		f->anext->aprev = f->aprev;
	if(f == a->frame)
		a->frame = f->anext;

	if(a->floating) {
		if(f->sprev)
			f->sprev->snext = f->snext;
		if(f->snext)
			f->snext->sprev = f->sprev;
		if(f == a->stack)
			a->stack = f->snext;
	}
	f->anext = f->aprev = f->snext = f->sprev = nil;
}

void
frame_insert(Frame *f, Frame *pos) {
	Area *a;

	a = f->area;

	if(pos) {
		assert(pos != f);
		f->aprev = pos;
		f->anext = pos->anext;
	}else {
		assert(f->area->frame != f);
		f->anext = f->area->frame;
		f->area->frame = f;
	}
	if(f->aprev)
		f->aprev->anext = f;
	if(f->anext)
		f->anext->aprev = f;

	if(a->floating) {
		assert(f->sprev == nil);
		frame_restack(f, nil);
	}
}

bool
frame_restack(Frame *f, Frame *above) {
	Area *a;

	a = f->area;
	if(!a->floating)
		return false;
	if(f == above)
		return false;

	if(f->sprev || f == a->stack)
	if(f->sprev == above)
		return false;

	if(f->sprev)
		f->sprev->snext = f->snext;
	else if(f->snext)
		a->stack = f->snext;
	if(f->snext)
		f->snext->sprev = f->sprev;

	f->sprev = above;
	if(above == nil) {
		f->snext = a->stack;
		a->stack = f;
	}
	else {
		f->snext = above->snext;
		above->snext = f;
	}
	if(f->snext)
		f->snext->sprev = f;
	assert(f->snext != f && f->sprev != f);

	return true;
}

/* Handlers */
static bool
bup_event(Window *w, void *aux, XButtonEvent *e) {
	if((e->state & def.mod) != def.mod)
		XAllowEvents(display, ReplayPointer, e->time);
	else
		XUngrabPointer(display, e->time);
	if(!e->subwindow)
		event("ClientClick %#C %d\n", aux, e->button);
	return false;
}

static bool
bdown_event(Window *w, void *aux, XButtonEvent *e) {
	Frame *f;
	Client *c;

	c = aux;
	f = c->sel;

	if((e->state & def.mod) == def.mod) {
		switch(e->button) {
		case Button1:
			focus(c, false);
			mouse_resize(c, Center, true);
			break;
		case Button2:
			frame_restack(f, nil);
			view_restack(f->view);
			focus(c, false);
			grabpointer(c->framewin, nil, cursor[CurNone], ButtonReleaseMask);
			break;
		case Button3:
			focus(c, false);
			mouse_resize(c, quadrant(f->r, Pt(e->x_root, e->y_root)), true);
			break;
		default:
			XAllowEvents(display, ReplayPointer, e->time);
			break;
		}
	}else {
		if(e->button == Button1) {
			if(!e->subwindow) {
				frame_restack(f, nil);
				view_restack(f->view);
				mouse_checkresize(f, Pt(e->x, e->y), true);
			}

			if(f->client != selclient())
				focus(c, false);
		}
		if(e->subwindow)
			XAllowEvents(display, ReplayPointer, e->time);
		else {
			/* Ungrab so a menu can receive events before the button is released */
			XUngrabPointer(display, e->time);
			sync();

			event("ClientMouseDown %#C %d\n", f->client, e->button);
		}
	}
	return false;
}

static bool
enter_event(Window *w, void *aux, XCrossingEvent *e) {
	Client *c;
	Frame *f;

	c = aux;
	f = c->sel;
	if(disp.focus != c || selclient() != c) {
		Dprint(DFocus, "%E\n", e);
		Dprint(DFocus, "enter_notify(f) => [%#C]%s%s\n",
		       f->client, f->client->name,
		       e->serial <= event_lastconfigure ? " (ignored)" : "");
		if(e->detail != NotifyInferior)
		if(e->serial > event_lastconfigure && !f->collapsed)
			focus(f->client, false);
	}
	mouse_checkresize(f, Pt(e->x, e->y), false);
	return false;
}

static bool
expose_event(Window *w, void *aux, XExposeEvent *e) {
	Client *c;

	USED(e);

	c = aux;
	if(c->sel)
		frame_draw(c->sel);
	return false;
}

static bool
motion_event(Window *w, void *aux, XMotionEvent *e) {
	Client *c;

	c = aux;
	mouse_checkresize(c->sel, Pt(e->x, e->y), false);
	return false;
}

Handlers framehandler = {
	.bup = bup_event,
	.bdown = bdown_event,
	.enter = enter_event,
	.expose = expose_event,
	.motion = motion_event,
};

WinHints
frame_gethints(Frame *f) {
	WinHints h;
	Client *c;
	Rectangle r;
	Point d;
	int minh;

	minh = labelh(def.font);

	c = f->client;
	h = *c->w.hints;

	r = frame_client2rect(c, ZR, f->area->floating);
	d = subpt(r.max, r.min);

	if(!f->area->floating && def.incmode == IIgnore)
		h.inc = Pt(1, 1);

	if(h.min.x < 2*minh)
		h.min.x = minh + (2*minh) % h.inc.x;
	if(h.min.y < minh)
		h.min.y = minh + minh % h.inc.y;

	h.min.x += d.x;
	h.min.y += d.y;
	/* Guard against overflow. */
	h.max.x = max(h.max.x + d.x, h.max.x);
	h.max.y = max(h.max.y + d.y, h.max.y);

	h.base.x += d.x;
	h.base.y += d.y;
	h.baspect.x += d.x;
	h.baspect.y += d.y;

	h.group = 0;
	h.grav = ZP;
	h.gravstatic = 0;
	h.position = 0;
	return h;
}

#define ADJ(PE, ME) \
	if(c->fullscreen >= 0)                       \
		return r;                            \
						     \
	if(!floating) {                              \
		r.min.x PE 1;                        \
		r.min.y PE labelh(def.font);         \
		r.max.x ME 1;                        \
		r.max.y ME 1;                        \
	}else {                                      \
		if(!c->borderless) {                 \
			r.min.x PE def.border;       \
			r.max.x ME def.border;       \
			r.max.y ME def.border;       \
		}                                    \
		if(!c->titleless)                    \
			r.min.y PE labelh(def.font); \
	}                                            \

Rectangle
frame_rect2client(Client *c, Rectangle r, bool floating) {

	ADJ(+=, -=)

	/* Force clients to be at least 1x1 */
	r.max.x = max(r.max.x, r.min.x+1);
	r.max.y = max(r.max.y, r.min.y+1);
	return r;
}

Rectangle
frame_client2rect(Client *c, Rectangle r, bool floating) {

	ADJ(-=, +=)

	return r;
}

#undef ADJ

void
frame_resize(Frame *f, Rectangle r) {
	Client *c;
	Rectangle fr, cr;
	int collapsed, dx;

	if(btassert("8 full", Dx(r) <= 0 || Dy(r) < 0
		           || Dy(r) == 0 && (!f->area->max || resizing)
			      && !f->collapsed)) {
		fprint(2, "Frame rect: %R\n", r);
		r.max.x = max(r.min.x+1, r.max.x);
		r.max.y = max(r.min.y+1, r.max.y);
	}

	c = f->client;
	if(c->fullscreen >= 0) {
		f->r = screens[c->fullscreen]->r;
		f->crect = rectsetorigin(f->r, ZP);
		return;
	}

	/*
	if(f->area->floating)
		f->collapsed = false;
	*/

	fr = frame_hints(f, r, get_sticky(f->r, r));
	if(f->area->floating && !c->strut)
		fr = constrain(fr, -1);

	/* Collapse managed frames which are too small */
	/* XXX. */
	collapsed = f->collapsed;
	if(!f->area->floating && f->area->mode == Coldefault) {
		f->collapsed = false;
		if(Dy(r) < 2 * labelh(def.font))
			f->collapsed = true;
	}
	if(collapsed != f->collapsed)
		ewmh_updatestate(c);

	fr.max.x = max(fr.max.x, fr.min.x + 2*labelh(def.font));
	if(f->collapsed && f->area->floating)
		fr.max.y = fr.min.y + labelh(def.font);

	cr = frame_rect2client(c, fr, f->area->floating);
	if(f->area->floating)
		f->r = fr;
	else {
		f->r = r;
		dx = Dx(r) - Dx(cr);
		dx -= 2 * (cr.min.x - fr.min.x);
		cr.min.x += dx / 2;
		cr.max.x += dx / 2;
	}
	f->crect = rectsubpt(cr, f->r.min);

	if(f->area->floating && !f->collapsed)
		f->floatr = f->r;
}

static void
pushlabel(Image *img, Rectangle *rp, char *s, CTuple *col) {
	Rectangle r;
	int w;

	w = textwidth(def.font, s) + def.font->height;
	w = min(w, Dx(*rp) - 30); /* Magic number. */
	if(w > 0) {
		r = *rp;
		r.min.x = r.max.x - w;
		rp->max.x -= w;
		if(0)
		drawline(img, Pt(rp->max.x, r.min.y+2),
			      Pt(rp->max.x, r.max.y-2),
			      CapButt, 1, &col->border);
		drawstring(img, def.font, r, East,
			   s, &col->fg);
	}
	free(s);
}

void
frame_draw(Frame *f) {
	Rectangle r, fr;
	Client *c;
	CTuple *col;
	Image *img;
	char *s;
	int n, m;

	if(f == nil || f->view != selview || f->area == nil)
		return;

	c = f->client;
	img = c->framewin->depth == 32 ? disp.ibuf32 : disp.ibuf;
	fr = rectsetorigin(c->framewin->r, ZP);

	/* Pick colors. */
	if((c == selclient() || c == disp.focus) && disp.sel)
		col = &def.focuscolor;
	else
		col = &def.normcolor;

	/* Background/border */
	r = fr;
	fill(img, r, &col->bg);
	border(img, r, 1, &col->border);

	/* Title border */
	r.max.y = r.min.y + labelh(def.font);
	border(img, r, 1, &col->border);

	f->titlebar = insetrect(r, 3);
	f->titlebar.max.y += 3;

	f->grabbox = insetrect(r, 2);
	f->grabbox.max.x = f->grabbox.min.x + Dy(f->grabbox);

	/* Odd focus. Unselected, with keyboard focus. */
	/* Draw a border just inside the titlebar. */
	if(c != selclient() && c == disp.focus) {
		border(img, insetrect(r, 1), 1, &def.normcolor.bg);
		border(img, insetrect(r, 2), 1, &def.focuscolor.border);
	}

	if(c->urgent)
		fill(img, f->grabbox, &col->fg);
	border(img, f->grabbox, 1, &col->border);

	/* Odd focus. Selected, without keyboard focus. */
	/* Draw a border around the grabbox. */
	if(c != disp.focus && col == &def.focuscolor)
		border(img, insetrect(r, -1), 1, &def.normcolor.bg);

	/* Draw a border on borderless+titleless selected apps. */
	if(c->borderless && c->titleless && f->area->floating && !c->fullscreen && c == selclient())
		setborder(c->framewin, def.border, &def.focuscolor.border);
	else
		setborder(c->framewin, 0, &def.focuscolor.border);

	/* Label */
	r = Rect(f->grabbox.max.x, 0, fr.max.x, labelh(def.font));

	/* Draw count on frames in 'max' columns. */
	if(f->area->max && !resizing) {
		n = stack_count(f, &m);
		pushlabel(img, &r, smprint("%d/%d", m, n), col);
	}

	/* Label clients with extra tags. */
	if((s = client_extratags(c)))
		pushlabel(img, &r, s, col);

	if(f->area->floating)  /* Make sure floating clients have room for their indicators. */
		r.max.x -= f->grabbox.max.x;

	if(!ewmh_responsive_p(c))
		r.min.x += drawstring(img, def.font, r, West, "(wedged) ", &col->fg);
	r.min.x += drawstring(img, def.font, r, West, c->name, &col->fg);

	/* Draw inner border on floating clients. */
	if(f->area->floating) {
		r.min.x += 10;
		r.max.x += Dx(f->grabbox);
		r.min.y = f->grabbox.min.y;
		r.max.y = f->grabbox.max.y;
		border(img, r, 1, &col->border);
	}

	/* Border increment gaps... */
	r.min.y = f->crect.min.y;
	r.min.x = max(1, f->crect.min.x - 1);
	r.max.x = min(fr.max.x - 1, f->crect.max.x + 1);
	r.max.y = min(fr.max.y - 1, f->crect.max.y + 1);
	border(img, r, 1, &col->border);

	/* Why? Because some non-ICCCM-compliant apps feel the need to
	 * change the background properties of all of their ancestor windows
	 * in order to implement pseudo-transparency.
	 * What's more, the designers of X11 felt that it would be unfair to
	 * implementers to make it possible to detect, or forbid, such changes.
	 */
	XSetWindowBackgroundPixmap(display, c->framewin->xid, None);

	copyimage(c->framewin, fr, img, ZP);
}

void
frame_draw_all(void) {
	Client *c;

	for(c=client; c; c=c->next)
		if(c->sel && c->sel->view == selview)
			frame_draw(c->sel);
}

void
frame_swap(Frame *fa, Frame *fb) {
	Frame **fp;
	Client *c;

	if(fa == fb) return;

	for(fp = &fa->client->frame; *fp; fp = &fp[0]->cnext)
		if(*fp == fa) break;
	fp[0] = fp[0]->cnext;

	for(fp = &fb->client->frame; *fp; fp = &fp[0]->cnext)
		if(*fp == fb) break;
	fp[0] = fp[0]->cnext;

	c = fa->client;
	fa->client = fb->client;
	fb->client = c;
	fb->cnext = c->frame;
	c->frame = fb;

	c = fa->client;
	fa->cnext = c->frame;
	c->frame = fa;

	if(c->sel)
		view_update(c->sel->view);
}

void
move_focus(Frame *old_f, Frame *f) {
	int noinput;

	noinput = (old_f && old_f->client->noinput) ||
		  (f && f->client->noinput) ||
		  disp.hasgrab != &c_root;

	if(noinput || true) {
		if(old_f)
			frame_draw(old_f);
		if(f)
			frame_draw(f);
	}
}

void
frame_focus(Frame *f) {
	Frame *old_f, *ff;
	View *v;
	Area *a, *old_a;

	v = f->view;
	a = f->area;
	old_a = v->sel;

	if(0 && f->collapsed) {
		for(ff=f; ff->collapsed && ff->anext; ff=ff->anext)
			;
		for(; ff->collapsed && ff->aprev; ff=ff->aprev)
			;
		/* XXX */
		f->colr.max.y = f->colr.min.y + Dy(ff->colr);
		ff->colr.max.y = ff->colr.min.y + labelh(def.font);
	}
	else if(f->area->mode == Coldefault) {
		/* XXX */
		for(; f->collapsed && f->anext; f=f->anext)
			;
		for(; f->collapsed && f->aprev; f=f->aprev)
			;
	}

	old_f = old_a->sel;
	a->sel = f;

	if(a != old_a)
		area_focus(f->area);
	if(old_a != v->oldsel && f != old_f)
		v->oldsel = nil;

	if(f->area->floating)
		f->collapsed = false;

	if(v == selview && a == v->sel && !resizing) {
		move_focus(old_f, f);
		if(a->floating)
			float_arrange(a);

		// if(!a->floating && ((a->mode == Colstack) || (a->mode == Colmax)))
		if(true)
			column_arrange(a, false);

		client_focus(f->client);
	}
}

int
frame_delta_h(void) {
	return def.border + labelh(def.font);
}

Rectangle
constrain(Rectangle r, int inset) {
	WMScreen **sp;
	WMScreen *s, *sbest;
	Rectangle isect;
	Point p;
	int best, n;

	if(inset < 0)
		inset = Dy(screen->brect);
	/*
	 * FIXME: This will cause problems for windows with
	 * D(r) < 2 * inset
	 */

	SET(best);
	sbest = nil;
	for(sp=screens; (s = *sp); sp++) {
		if(!screen->showing)
			continue;

		isect = rect_intersection(r, insetrect(s->r, inset));
		if(Dx(isect) >= 0 && Dy(isect) >= 0)
			return r;

		if(Dx(isect) <= 0 && Dy(isect) <= 0)
			n = max(Dx(isect), Dy(isect));
		else
			n = min(Dx(isect), Dy(isect));

		if(!sbest || n > best) {
			sbest = s;
			best = n;
		}
	}

	isect = insetrect(sbest->r, inset);
	p = ZP;
	p.x -= min(r.max.x - isect.min.x, 0);
	p.x -= max(r.min.x - isect.max.x, 0);
	p.y -= min(r.max.y - isect.min.y, 0);
	p.y -= max(r.min.y - isect.max.y, 0);
	return rectaddpt(r, p);
}

Added cmd/wmii/fs.c.




















































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
/* Copyright ©2006-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "dat.h"
#include <ctype.h>
#include <stdarg.h>
#include <time.h>
#include "fns.h"

typedef union IxpFileIdU IxpFileIdU;
union IxpFileIdU {
	Bar*		bar;
	Bar**		bar_p;
	CTuple*		col;
	Client*		client;
	Ruleset*	rule;
	View*		view;
	char*		buf;
	void*		ref;
};

#include <ixp_srvutil.h>

static IxpPending	events;
static IxpPending	pdebug[NDebugOpt];

/* Constants */
enum {	/* Dirs */
	FsDBars,
	FsDClient,
	FsDClients,
	FsDDebug,
	FsDTag,
	FsDTags,
	FsRoot,
	/* Files */
	FsFBar,
	FsFCctl,
	FsFClabel,
	FsFColRules,
	FsFCtags,
	FsFDebug,
	FsFEvent,
	FsFKeys,
	FsFRctl,
	FsFRules,
	FsFTctl,
	FsFTindex,
	FsFprops,
};

/* Error messages */
static char
	Enoperm[] = "permission denied",
	Enofile[] = "file not found",
	Ebadvalue[] = "bad value",
	Einterrupted[] = "interrupted";

/* Macros */
#define QID(t, i) (((vlong)((t)&0xFF)<<32)|((i)&0xFFFFFFFF))

/* Global Vars */
/***************/
Ixp9Srv p9srv = {
	.open=	fs_open,
	.walk=	fs_walk,
	.read=	fs_read,
	.stat=	fs_stat,
	.write=	fs_write,
	.clunk=	fs_clunk,
	.flush=	fs_flush,
	.attach=fs_attach,
	.create=fs_create,
	.remove=fs_remove,
	.freefid=fs_freefid
};

/* ad-hoc file tree. Empty names ("") indicate dynamic entries to be filled
 * in by lookup_file
 */
static IxpDirtab
dirtab_root[]=	 {{".",		QTDIR,		FsRoot,		0500|DMDIR },
		  {"rbar",	QTDIR,		FsDBars,	0700|DMDIR },
		  {"lbar",	QTDIR,		FsDBars,	0700|DMDIR },
		  {"debug",	QTDIR,		FsDDebug,	0500|DMDIR, FLHide },
		  {"client",	QTDIR,		FsDClients,	0500|DMDIR },
		  {"tag",	QTDIR,		FsDTags,	0500|DMDIR },
		  {"ctl",	QTAPPEND,	FsFRctl,	0600|DMAPPEND },
		  {"colrules",	QTFILE,		FsFColRules,	0600 },
		  {"event",	QTFILE,		FsFEvent,	0600 },
		  {"keys",	QTFILE,		FsFKeys,	0600 },
		  {"rules",	QTFILE,		FsFRules,	0600 },
		  {nil}},
dirtab_clients[]={{".",		QTDIR,		FsDClients,	0500|DMDIR },
		  {"",		QTDIR,		FsDClient,	0500|DMDIR },
		  {nil}},
dirtab_client[]= {{".",		QTDIR,		FsDClient,	0500|DMDIR },
		  {"ctl",	QTAPPEND,	FsFCctl,	0600|DMAPPEND },
		  {"label",	QTFILE,		FsFClabel,	0600 },
		  {"tags",	QTFILE,		FsFCtags,	0600 },
		  {"props",	QTFILE,		FsFprops,	0400 },
		  {nil}},
dirtab_debug[]=  {{".",		QTDIR,		FsDDebug,	0500|DMDIR, FLHide },
		  {"",		QTFILE,		FsFDebug,	0400 },
		  {nil}},
dirtab_bars[]=	 {{".",		QTDIR,		FsDBars,	0700|DMDIR },
		  {"",		QTFILE,		FsFBar,		0600 },
		  {nil}},
dirtab_tags[]=	 {{".",		QTDIR,		FsDTags,	0500|DMDIR },
		  {"",		QTDIR,		FsDTag,		0500|DMDIR },
		  {nil}},
dirtab_tag[]=	 {{".",		QTDIR,		FsDTag,		0500|DMDIR },
		  {"ctl",	QTAPPEND,	FsFTctl,	0600|DMAPPEND },
		  {"index",	QTFILE,		FsFTindex,	0400 },
		  {nil}};
static IxpDirtab* dirtab[] = {
	[FsRoot] = dirtab_root,
	[FsDBars] = dirtab_bars,
	[FsDClients] = dirtab_clients,
	[FsDClient] = dirtab_client,
	[FsDDebug] = dirtab_debug,
	[FsDTags] = dirtab_tags,
	[FsDTag] = dirtab_tag,
};
typedef char* (*MsgFunc)(void*, IxpMsg*);
typedef char* (*BufFunc)(void*);

typedef struct ActionTab ActionTab;
static struct ActionTab {
	MsgFunc		msg;
	BufFunc		read;
	size_t		buffer;
	size_t		size;
	int		max;
} actiontab[] = {
	[FsFBar]      = { .msg = (MsgFunc)message_bar,          .read = (BufFunc)readctl_bar },
	[FsFCctl]     = { .msg = (MsgFunc)message_client,     	.read = (BufFunc)readctl_client },
	[FsFRctl]     = { .msg = (MsgFunc)message_root,       	.read = (BufFunc)readctl_root },
	[FsFTctl]     = { .msg = (MsgFunc)message_view,       	.read = (BufFunc)readctl_view },
	[FsFTindex]   = { .msg = (MsgFunc)0,		    	.read = (BufFunc)view_index },
	[FsFColRules] = { .buffer = offsetof(Ruleset, string),	.size = offsetof(Ruleset, size) },
	[FsFKeys]     = { .buffer = offsetof(Defs, keys),	.size = offsetof(Defs, keyssz) },
	[FsFRules]    = { .buffer = offsetof(Ruleset, string), 	.size = offsetof(Ruleset, size) },
	[FsFClabel]   = { .buffer = offsetof(Client, name),	.max = sizeof ((Client*)0)->name },
	[FsFCtags]    = { .buffer = offsetof(Client, tags),   	.max = sizeof ((Client*)0)->tags },
	[FsFprops]    = { .buffer = offsetof(Client, props),  	.max = sizeof ((Client*)0)->props },
};

void
event(const char *format, ...) {
	va_list ap;

	va_start(ap, format);
	vsnprint(buffer, sizeof buffer, format, ap);
	va_end(ap);

	ixp_pending_write(&events, buffer, strlen(buffer));
	Dprint(DEvents, "%s", buffer);
}

static int dflags;

bool
setdebug(int flag) {
	dflags = flag;
	return true;
}

void
vdebug(int flag, const char *fmt, va_list ap) {
	char *s;

	if(flag == 0)
		flag = dflags;

	if(!((debugflag|debugfile) & flag))
		return;

	s = vsmprint(fmt, ap);
	dwrite(flag, s, strlen(s), false);
	free(s);
}

void
debug(int flag, const char *fmt, ...) {
	va_list ap;

	va_start(ap, fmt);
	vdebug(flag, fmt, ap);
	va_end(ap);
}

void
dwrite(int flag, void *buf, int n, bool always) {
	int i;

	if(flag == 0)
		flag = dflags;

	if(always || debugflag&flag)
		write(2, buf, n);

	if(debugfile&flag)
	for(i=0; i < nelem(pdebug); i++)
		if(flag & (1<<i))
			ixp_pending_write(pdebug+i, buf, n);
}

static uint	fs_size(IxpFileId*);

static void
dostat(IxpStat *s, IxpFileId *f) {
	s->type = 0;
	s->dev = 0;
	s->qid.path = QID(f->tab.type, f->id);
	s->qid.version = 0;
	s->qid.type = f->tab.qtype;
	s->mode = f->tab.perm;
	s->atime = time(nil);
	s->mtime = s->atime;
	s->length = fs_size(f);;
	s->name = f->tab.name;
	s->uid = user;
	s->gid = user;
	s->muid = user;
}

/*
 * All lookups and directory organization should be performed through
 * lookup_file, mostly through the dirtab[] tree.
 */
static IxpFileId*
lookup_file(IxpFileId *parent, char *name)
{
	IxpFileId *ret, *file, **last;
	IxpDirtab *dir;
	Client *c;
	View *v;
	Bar *b;
	uint id;
	int i;


	if(!(parent->tab.perm & DMDIR))
		return nil;
	dir = dirtab[parent->tab.type];
	last = &ret;
	ret = nil;
	for(; dir->name; dir++) {
#               define push_file(nam, id_, vol)   \
			file = ixp_srv_getfile(); \
			file->id = id_;           \
			file->volatil = vol;      \
			*last = file;             \
			last = &file->next;       \
			file->tab = *dir;         \
			file->tab.name = estrdup(nam)
		/* Dynamic dirs */
		if(dir->name[0] == '\0') {
			switch(parent->tab.type) {
			case FsDClients:
				if(!name || !strcmp(name, "sel")) {
					if((c = selclient())) {
						push_file("sel", c->w.xid, true);
						file->p.client = c;
						file->index = c->w.xid;
					}
					if(name)
						goto LastItem;
				}
				SET(id);
				if(name) {
					id = (uint)strtol(name, &name, 16);
					if(*name)
						goto NextItem;
				}
				for(c=client; c; c=c->next) {
					if(!name || c->w.xid == id) {
						push_file(sxprint("%#C", c), c->w.xid, true);
						file->p.client = c;
						file->index = c->w.xid;
						assert(file->tab.name);
						if(name)
							goto LastItem;
					}
				}
				break;
			case FsDDebug:
				for(i=0; i < nelem(pdebug); i++)
					if(!name || !strcmp(name, debugtab[i])) {
						push_file(debugtab[i], i, false);
						if(name)
							goto LastItem;
					}
				break;
			case FsDTags:
				if(!name || !strcmp(name, "sel")) {
					if(selview) {
						push_file("sel", selview->id, true);
						file->p.view = selview;
					}
					if(name)
						goto LastItem;
				}
				for(v=view; v; v=v->next) {
					if(!name || !strcmp(name, v->name)) {
						push_file(v->name, v->id, true);
						file->p.view = v;
						if(name)
							goto LastItem;
					}
				}
				break;
			case FsDBars:
				for(b=*parent->p.bar_p; b; b=b->next) {
					if(!name || !strcmp(name, b->name)) {
						push_file(b->name, b->id, true);
						file->p.bar = b;
						if(name)
							goto LastItem;
					}
				}
				break;
			}
		}else /* Static dirs */
		if(!name && !(dir->flags & FLHide) || name && !strcmp(name, dir->name)) {
			push_file(file->tab.name, 0, false);
			file->p.ref = parent->p.ref;
			file->index = parent->index;
			/* Special considerations: */
			switch(file->tab.type) {
			case FsDBars:
				if(!strcmp(file->tab.name, "lbar"))
					file->p.bar_p = &screen[0].bar[BLeft];
				else
					file->p.bar_p = &screen[0].bar[BRight];
				file->id = (int)(uintptr_t)file->p.bar_p;
				break;
			case FsFColRules:
				file->p.rule = &def.colrules;
				break;
			case FsFKeys:
				file->p.ref = &def;
				break;
			case FsFRules:
				file->p.rule = &def.rules;
				break;
			}
			if(name)
				goto LastItem;
		}
	NextItem:
		continue;
#		undef push_file
	}
LastItem:
	*last = nil;
	return ret;
}

/* Service Functions */
void
fs_attach(Ixp9Req *r) {
	IxpFileId *f;

	f = ixp_srv_getfile();
	f->tab = dirtab[FsRoot][0];
	f->tab.name = estrdup("/");
	r->fid->aux = f;
	r->fid->qid.type = f->tab.qtype;
	r->fid->qid.path = QID(f->tab.type, 0);
	r->ofcall.rattach.qid = r->fid->qid;
	ixp_respond(r, nil);
}

void
fs_walk(Ixp9Req *r) {

	ixp_srv_walkandclone(r, lookup_file);
}

static uint
fs_size(IxpFileId *f) {
	ActionTab *t;

	t = &actiontab[f->tab.type];
	if(f->tab.type < nelem(actiontab))
		if(t->size)
			return structmember(f->p.ref, int, t->size);
		else if(t->buffer && t->max)
			return strlen(structptr(f->p.ref, char, t->buffer));
		else if(t->buffer)
			return strlen(structmember(f->p.ref, char*, t->buffer));
		else if(t->read)
			return strlen(t->read(f->p.ref));
	return 0;
}

void
fs_stat(Ixp9Req *r) {
	IxpMsg m;
	IxpStat s;
	int size;
	char *buf;
	IxpFileId *f;

	f = r->fid->aux;

	if(!ixp_srv_verifyfile(f, lookup_file)) {
		ixp_respond(r, Enofile);
		return;
	}

	dostat(&s, f);
	size = ixp_sizeof_stat(&s);
	r->ofcall.rstat.nstat = size;
	buf = emallocz(size);

	m = ixp_message(buf, size, MsgPack);
	ixp_pstat(&m, &s);

	r->ofcall.rstat.stat = (uchar*)m.data;
	ixp_respond(r, nil);
}

void
fs_read(Ixp9Req *r) {
	char *buf;
	IxpFileId *f;
	ActionTab *t;
	int n, found;

	f = r->fid->aux;
	found = 0;

	if(!ixp_srv_verifyfile(f, lookup_file)) {
		ixp_respond(r, Enofile);
		return;
	}

	if(f->tab.perm & DMDIR && f->tab.perm & 0400) {
		ixp_srv_readdir(r, lookup_file, dostat);
		return;
	}
	else{
		if(f->pending) {
			ixp_pending_respond(r);
			return;
		}
		t = &actiontab[f->tab.type];
		if(f->tab.type < nelem(actiontab)) {
			if(t->read)
				buf = t->read(f->p.ref);
			else if(t->buffer && t->max)
				buf = structptr(f->p.ref, char, t->buffer);
			else if(t->buffer)
				buf = structmember(f->p.ref, char*, t->buffer);
			else
				goto done;
			n = t->size ? structmember(f->p.ref, int, t->size) : strlen(buf);
			ixp_srv_readbuf(r, buf, n);
			ixp_respond(r, nil);
			found++;
		}
	done:
		switch(f->tab.type) {
		default:
			if(found)
				return;
		}
	}
	/* This should not be called if the file is not open for reading. */
	die("Read called on an unreadable file");
}

void
fs_write(Ixp9Req *r) {
	IxpFileId *f;
	ActionTab *t;
	char *errstr;
	int found;

	found = 0;
	errstr = nil;
	if(r->ifcall.io.count == 0) {
		ixp_respond(r, nil);
		return;
	}
	f = r->fid->aux;

	if(!ixp_srv_verifyfile(f, lookup_file)) {
		ixp_respond(r, Enofile);
		return;
	}

	switch(f->tab.type) {
	case FsFCtags:
		r->ofcall.io.count = r->ifcall.io.count;
		ixp_srv_data2cstring(r);
		client_applytags(f->p.client, r->ifcall.io.data);
		ixp_respond(r, nil);
		return;
	}

	if(waserror()) {
		ixp_respond(r, ixp_errbuf());
		return;
	}

	t = &actiontab[f->tab.type];
	if(f->tab.type < nelem(actiontab)) {
		if(t->msg) {
			errstr = ixp_srv_writectl(r, t->msg);
			r->ofcall.io.count = r->ifcall.io.count;
		}
		else if(t->buffer && t->max)
			ixp_srv_writebuf(r, (char*[]){ structptr(f->p.ref, char, t->buffer) },
					 t->size ? structptr(f->p.ref, uint, t->size)
					         : (uint[]){ strlen(structptr(f->p.ref, char, t->buffer)) },
					 t->max);
		else if(t->buffer)
			ixp_srv_writebuf(r, structptr(f->p.ref, char*, t->buffer),
					 t->size ? structptr(f->p.ref, uint, t->size) : nil,
					 t->max);
		else
			goto done;
		ixp_respond(r, errstr);
		found++;
	}
done:
	switch(f->tab.type) {
	case FsFClabel:
		frame_draw(f->p.client->sel);
		update_class(f->p.client);
		break;
	case FsFCtags:
		client_applytags(f->p.client, f->p.client->tags);
		break;
	case FsFEvent:
		if(r->ifcall.io.data[r->ifcall.io.count-1] == '\n')
			event("%.*s", (int)r->ifcall.io.count, r->ifcall.io.data);
		else
			event("%.*s\n", (int)r->ifcall.io.count, r->ifcall.io.data);
		r->ofcall.io.count = r->ifcall.io.count;
		ixp_respond(r, nil);
		break;
	default:
		/* This should not be called if the file is not open for writing. */
		if(!found)
			die("Write called on an unwritable file");
	}
	poperror();
	return;
}

void
fs_open(Ixp9Req *r) {
	IxpFileId *f;

	f = r->fid->aux;

	if(!ixp_srv_verifyfile(f, lookup_file)) {
		ixp_respond(r, Enofile);
		return;
	}

	switch(f->tab.type) {
	case FsFEvent:
		ixp_pending_pushfid(&events, r->fid);
		break;
	case FsFDebug:
		ixp_pending_pushfid(pdebug+f->id, r->fid);
		debugfile |= 1<<f->id;
		break;
	}

	if((r->ifcall.topen.mode&3) == OEXEC
	|| (r->ifcall.topen.mode&3) != OREAD && !(f->tab.perm & 0200)
	|| (r->ifcall.topen.mode&3) != OWRITE && !(f->tab.perm & 0400)
	|| (r->ifcall.topen.mode & ~(3|OAPPEND|OTRUNC)))
		ixp_respond(r, Enoperm);
	else
		ixp_respond(r, nil);
}

void
fs_create(Ixp9Req *r) {
	IxpFileId *f;

	f = r->fid->aux;

	switch(f->tab.type) {
	default:
		ixp_respond(r, Enoperm);
		return;
	case FsDBars:
		if(!strlen(r->ifcall.tcreate.name)) {
			ixp_respond(r, Ebadvalue);
			return;
		}
		bar_create(f->p.bar_p, r->ifcall.tcreate.name);
		f = lookup_file(f, r->ifcall.tcreate.name);
		if(!f) {
			ixp_respond(r, Enofile);
			return;
		}
		r->ofcall.ropen.qid.type = f->tab.qtype;
		r->ofcall.ropen.qid.path = QID(f->tab.type, f->id);
		f->next = r->fid->aux;
		r->fid->aux = f;
		ixp_respond(r, nil);
		break;
	}
}

void
fs_remove(Ixp9Req *r) {
	IxpFileId *f;
	WMScreen *s;

	f = r->fid->aux;
	if(!ixp_srv_verifyfile(f, lookup_file)) {
		ixp_respond(r, Enofile);
		return;
	}

	switch(f->tab.type) {
	default:
		ixp_respond(r, Enoperm);
		return;
	case FsFBar:
		s = f->p.bar->screen;
		bar_destroy(f->next->p.bar_p, f->p.bar);
		bar_draw(s);
		break;
	case FsDClient:
		client_kill(f->p.client, true);
		break;
	}
	ixp_respond(r, nil);
}

void
fs_clunk(Ixp9Req *r) {
	IxpFileId *f;

	f = r->fid->aux;
	if(!ixp_srv_verifyfile(f, lookup_file)) {
		ixp_respond(r, nil);
		return;
	}

	if(f->pending) {
		/* Should probably be in freefid */
		if(ixp_pending_clunk(r)) {
			if(f->tab.type == FsFDebug)
				debugfile &= ~(1<<f->id);
		}
		return;
	}

	switch(f->tab.type) {
	case FsFColRules:
	case FsFRules:
		update_rules(&f->p.rule->rule, f->p.rule->string);
		break;
	case FsFKeys:
		update_keys();
		break;
	}
	ixp_respond(r, nil);
}

void
fs_flush(Ixp9Req *r) {
	Ixp9Req *or;
	IxpFileId *f;

	or = r->oldreq;
	f = or->fid->aux;
	if(f->pending)
		ixp_pending_flush(r);
	/* else die() ? */
	ixp_respond(r->oldreq, Einterrupted);
	ixp_respond(r, nil);
}

void
fs_freefid(IxpFid *f) {
	IxpFileId *id, *tid;

	tid = f->aux;
	while((id = tid)) {
		tid = id->next;
		ixp_srv_freefile(id);
	}
}

Added cmd/wmii/key.c.






















































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
/* Copyright ©2006-2010 Kris Maglione <fbsdaemon at Gmail>
 * Copyright ©2004-2006 Anselm R. Garbe <garbeam at gmail dot com>
 * See LICENSE file for license details.
 */
#include "dat.h"
#include <X11/keysym.h>
#include "fns.h"

static void
freekey(Key *k) {
	Key *n;

	while((n = k)) {
		k = k->next;
		free(n);
	}
}

static void
_grab(XWindow w, int keycode, uint mod) {
	XGrabKey(display, keycode, mod, w,
		 true, GrabModeAsync, GrabModeAsync);
}

static void
grabkey(Key *k) {
	_grab(scr.root.xid, k->key, k->mod);
	_grab(scr.root.xid, k->key, k->mod | LockMask);
	if(numlock_mask) {
		_grab(scr.root.xid, k->key, k->mod | numlock_mask);
		_grab(scr.root.xid, k->key, k->mod | numlock_mask | LockMask);
	}
}

static void
ungrabkey(Key *k) {
	XUngrabKey(display, k->key, k->mod, scr.root.xid);
	XUngrabKey(display, k->key, k->mod | LockMask, scr.root.xid);
	if(numlock_mask) {
		XUngrabKey(display, k->key, k->mod | numlock_mask, scr.root.xid);
		XUngrabKey(display, k->key, k->mod | numlock_mask | LockMask, scr.root.xid);
	}
}

static Key*
name2key(const char *name) {
	Key *k;

	for(k=key; k; k=k->lnext)
		if(!strcmp(k->name, name))
			return k;
	return nil;
}

static Key*
getkey(const char *name) {
	Key *k, *r;
	char buf[128];
	char *seq[8];
	char *kstr;
	int mask;
	uint i, toks;
	static ushort id = 1;

	r = nil;

	if((k = name2key(name))) {
		ungrabkey(k);
		return k;
	}
	utflcpy(buf, name, sizeof buf);
	toks = tokenize(seq, 8, buf, ',');
	for(i = 0; i < toks; i++) {
		if(!k)
			r = k = emallocz(sizeof *k);
		else {
			k->next = emallocz(sizeof *k);
			k = k->next;
		}
		utflcpy(k->name, name, sizeof k->name);
		if(parsekey(seq[i], &mask, &kstr)) {
			k->key = keycode(kstr);
			k->mod = mask;
		}
		if(k->key == 0) {
			freekey(r);
			return nil;
		}
	}
	if(r) {
		r->id = id++;
		r->lnext = key;
		key = r;
	}

	return r;
}

static void
next_keystroke(ulong *mod, KeyCode *code) {
	XEvent e;
	KeySym sym;
	*mod = 0;

	do {
		XMaskEvent(display, KeyPressMask, &e);
		*mod |= e.xkey.state & valid_mask;
		*code = (KeyCode)e.xkey.keycode;
		sym = XKeycodeToKeysym(display, e.xkey.keycode, 0);
	} while(IsModifierKey(sym));
}

static void
fake_keypress(ulong mod, KeyCode key) {
	XKeyEvent e;
	Client *c;

	c = disp.focus;
	if(c == nil || c->w.xid == 0)
		return;

	e.time = event_xtime;
	e.window = c->w.xid;
	e.state = mod;
	e.keycode = key;

	e.type = KeyPress;
	sendevent(&c->w, true, KeyPressMask, (XEvent*)&e);
	e.type = KeyRelease;
	sendevent(&c->w, true, KeyReleaseMask, (XEvent*)&e);

	sync();
}

static Key *
match_keys(Key *k, ulong mod, KeyCode keycode, bool seq) {
	Key *ret, *next;
	int i; /* shut up ken */

	ret = nil;
	for(next = k->tnext; k; i = (k=next) && (next=k->tnext)) {
		if(seq)
			k = k->next;
		if(k && (k->mod == mod) && (k->key == keycode)) {
			k->tnext = ret;
			ret = k;
		}
	}
	USED(i);
	return ret;
}

static void
kpress_seq(XWindow w, Key *done) {
	ulong mod;
	KeyCode key;
	Key *found;

	next_keystroke(&mod, &key);
	found = match_keys(done, mod, key, true);
	if((done->mod == mod) && (done->key == key))
		fake_keypress(mod, key); /* double key */
	else {
		if(!found)
			XBell(display, 0);
		else if(!found->tnext && !found->next)
			event("Key %s\n", found->name);
		else
			kpress_seq(w, found);
	}
}

void
kpress(XWindow w, ulong mod, KeyCode keycode) {
	Key *k, *found;

	for(k=key; k; k=k->lnext)
		 k->tnext = k->lnext;

	found = match_keys(key, mod, keycode, false);
	if(!found) /* grabbed but not found */
		XBell(display, 0);
	else if(!found->tnext && !found->next)
		event("Key %s\n", found->name);
	else {
		XGrabKeyboard(display, w, true, GrabModeAsync, GrabModeAsync, CurrentTime);
		event_flush(FocusChangeMask, true);
		kpress_seq(w, found);
		XUngrabKeyboard(display, CurrentTime);
	}
}

void
update_keys(void) {
	Key *k;
	char *l, *p;

	numlock_mask = numlockmask();
	valid_mask = 0xff & ~(numlock_mask | LockMask);
	while((k = key)) {
		key = key->lnext;
		ungrabkey(k);
		freekey(k);
	}
	for(l = p = def.keys; p && *p; p++) {
		if(*p == '\n') {
			*p = 0;
			if((k = getkey(l)))
				grabkey(k);
			*p = '\n';
			l = p + 1;
		}
	}
	if(l < p && strlen(l)) {
		if((k = getkey(l)))
			grabkey(k);
	}
}

Added cmd/wmii/layout.c.
































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
/* Copyright ©2006-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "dat.h"
#include "fns.h"

/* Here be dragons. */
/* Actually, I'm happy to say, the dragons have dissipated. */

enum {
	ButtonMask =
		ButtonPressMask | ButtonReleaseMask,
	MouseMask =
		ButtonMask | PointerMotionMask
};

static Handlers handlers;

enum { OHoriz, OVert };
typedef struct Framewin Framewin;
struct Framewin {
	/* Todo... give these better names. */
	Window* w;
	Rectangle grabbox;
	Frame*	f;
	Area*	ra;
	Point	pt;
	int	orientation;
	int	xy;
	int	screen;
};

static Rectangle
framerect(Framewin *f) {
	Rectangle r;
	Point p;
	int scrn;

	r.min = ZP;
	if(f->orientation == OHoriz) {
		r.max.x = f->xy;
		r.max.y = f->grabbox.max.y + f->grabbox.min.y;
	}else {
		r.max.x = f->grabbox.max.x + f->grabbox.min.x;
		r.max.y = f->xy;
		r = rectsubpt(r, Pt(Dx(r)/2, 0));
	}
	r = rectaddpt(r, f->pt);

	scrn = f->screen;
	if(scrn == -1)
		scrn = max(ownerscreen(f->f->r), 0);

	/* Keep onscreen */
	p = ZP;
	p.x -= min(0, r.min.x);
	p.x -= max(0, r.max.x - screens[scrn]->r.max.x);
	p.y -= max(0, r.max.y - screens[scrn]->brect.min.y - Dy(r)/2);
	return rectaddpt(r, p);
}

static void
frameadjust(Framewin *f, Point pt, int orientation, int xy) {
	f->orientation = orientation;
	f->xy = xy;
	f->pt = pt;
}

static Framewin*
framewin(Frame *f, Point pt, int orientation, int n) {
	WinAttr wa;
	Framewin *fw;

	fw = emallocz(sizeof *fw);
	wa.override_redirect = true;
	wa.event_mask = ExposureMask;
	fw->w = createwindow(&scr.root, Rect(0, 0, 1, 1),
			scr.depth, InputOutput,
			&wa, CWEventMask);
	fw->w->aux = fw;
	sethandler(fw->w, &handlers);

	fw->f = f;
	fw->screen = f->area->screen;
	fw->grabbox = f->grabbox;
	frameadjust(fw, pt, orientation, n);
	reshapewin(fw->w, framerect(fw));

	raisewin(fw->w);

	return fw;
}

static void
framedestroy(Framewin *f) {
	destroywindow(f->w);
	free(f);
}

static bool
expose_event(Window *w, void *aux, XExposeEvent *e) {
	Rectangle r;
	Framewin *f;
	Image *buf;
	CTuple *c;

	USED(e);

	f = aux;
	c = &def.focuscolor;
	buf = disp.ibuf;

	r = rectsubpt(w->r, w->r.min);
	fill(buf, r, &c->bg);
	border(buf, r, 1, &c->border);
	border(buf, f->grabbox, 1, &c->border);
	border(buf, insetrect(f->grabbox, -f->grabbox.min.x), 1, &c->border);

	copyimage(w, r, buf, ZP);
	return false;
}

static Handlers handlers = {
	.expose = expose_event,
};

static Area*
find_area(Point pt) {
	View *v;
	Area *a;
	int s;

	v = selview;
	for(s=0; s < nscreens; s++) {
		if(!rect_haspoint_p(screens[s]->r, pt))
			continue;
		for(a=v->areas[s]; a; a=a->next)
			if(pt.x < a->r.max.x)
				return a;
	}
	return nil;
}

static void
vplace(Framewin *fw, Point pt) {
	Vector_long vec = {0};
	Rectangle r;
	Frame *f;
	Area *a;
	long l;
	int hr;

	a = find_area(pt);
	if(a == nil)
		return;

	fw->ra = a;
	fw->screen = a->screen;

	pt.x = a->r.min.x;
	frameadjust(fw, pt, OHoriz, Dx(a->r));

	r = fw->w->r;
	hr = Dy(r)/2;
	pt.y -= hr;

	if(a->frame == nil)
		goto done;

	vector_lpush(&vec, a->frame->r.min.y);
	for(f=a->frame; f; f=f->anext) {
		if(f == fw->f)
			vector_lpush(&vec, f->r.min.y + 0*hr);
		else if(f->collapsed)
			vector_lpush(&vec, f->r.min.y + 1*hr);
		else
			vector_lpush(&vec, f->r.min.y + 2*hr);
		if(!f->collapsed && f->anext != fw->f)
			vector_lpush(&vec, f->r.max.y - 2*hr);
	}

	for(int i=0; i < vec.n; i++) {
		l = vec.ary[i];
		if(abs(pt.y - l) < hr) {
			pt.y = l;
			break;
		}
	}
	vector_lfree(&vec);

done:
	pt.x = a->r.min.x;
	frameadjust(fw, pt, OHoriz, Dx(a->r));
	reshapewin(fw->w, framerect(fw));
}

static void
hplace(Framewin *fw, Point pt) {
	Area *a;
	int minw;

	a = find_area(pt);
	if(a == nil)
		return; /* XXX: Multihead. */

	fw->screen = a->screen;
	fw->ra = nil;
	minw = column_minwidth();
	if(abs(pt.x - a->r.min.x) < minw/2) {
		pt.x = a->r.min.x;
		fw->ra = a->prev;
	}
	else if(abs(pt.x - a->r.max.x) < minw/2) {
		pt.x = a->r.max.x;
		fw->ra = a;
	}

	pt.y = a->r.min.y;
	frameadjust(fw, pt, OVert, Dy(a->r));
	reshapewin(fw->w, framerect(fw));
}

static Point
grabboxcenter(Frame *f) {
	Point p;

	p = addpt(f->r.min, f->grabbox.min);
	p.x += Dx(f->grabbox)/2;
	p.y += Dy(f->grabbox)/2;
	return p;
}

static int tvcol(Frame*, bool);
static int thcol(Frame*, bool);
static int tfloat(Frame*, bool);

enum {
	TDone,
	TVCol,
	THCol,
	TFloat,
};

static int (*tramp[])(Frame*, bool) = {
	0,
	tvcol,
	thcol,
	tfloat,
};

/* Trampoline to allow properly tail recursive move/resize routines.
 * We could probably get away with plain tail calls, but I don't
 * like the idea.
 */
static void
trampoline(int fn, Frame *f, bool grabbox) {
	int moved;

	moved = 0;
	while(fn > 0) {
		if(grabbox)
			warppointer(grabboxcenter(f));
		//f->collapsed = false;
		fn = tramp[fn](f, moved++);
	}
	ungrabpointer();
}

static void
resizemode(int mode) {
	bool orig;

	orig = resizing;
	resizing = mode && mode != TFloat;
	if(resizing != orig)
		view_update(selview);
}

void
mouse_movegrabbox(Client *c, bool grabmod) {
	Frame *f;
	Point p;
	float x, y;

	f = c->sel;

	SET(x);
	SET(y);
	if(grabmod) {
		p = querypointer(f->client->framewin);
		x = (float)p.x / Dx(f->r);
		y = (float)p.y / Dy(f->r);
	}

	if(f->area->floating)
		trampoline(TFloat, f, !grabmod);
	else
		trampoline(THCol, f, true);

	if(grabmod)
		warppointer(addpt(f->r.min, Pt(x * Dx(f->r),
					       y * Dy(f->r))));
	else
		warppointer(grabboxcenter(f));
}

static int
_openstack_down(Frame *f, int h) {
	int ret;
	int dy;

	if(f == nil)
		return 0;;
	ret = 0;
	if(!f->collapsed) {
		dy = Dy(f->colr) - labelh(def.font);
		if(dy >= h) {
			f->colr.min.y += h;
			return h;
		}else {
			f->collapsed = true;
			f->colr.min.y += dy;
			ret = dy;
			h -= dy;
		}
	}
	dy = _openstack_down(f->anext, h);
	f->colr.min.y += dy;
	f->colr.max.y += dy;
	return ret + dy;
}

static int
_openstack_up(Frame *f, int h) {
	int ret;
	int dy;

	if(f == nil)
		return 0;
	ret = 0;
	if(!f->collapsed) {
		dy = Dy(f->colr) - labelh(def.font);
		if(dy >= h) {
			f->colr.max.y -= h;
			return h;
		}else {
			f->collapsed = true;
			f->colr.max.y -= dy;
			ret = dy;
			h -= dy;
		}
	}
	dy = _openstack_up(f->aprev, h);
	f->colr.min.y -= dy;
	f->colr.max.y -= dy;
	return ret + dy;
}

static void
column_openstack(Area *a, Frame *f, int h) {

	if(f == nil)
		_openstack_down(a->frame, h);
	else {
		h -= _openstack_down(f->anext, h);
		if(h)
			_openstack_up(f->aprev, h);
	}
}

static void
column_drop(Area *a, Frame *f, int y) {
	Frame *ff;
	int dy, extra_y;

	extra_y = Dy(a->r);
	for(ff=a->frame; ff; ff=ff->anext) {
		assert(ff != f);
		extra_y -= Dy(ff->colr);
	}

	if(a->frame == nil || y <= a->frame->r.min.y) {
		f->collapsed = true;
		f->colr.min.y = 0;
		f->colr.max.y = labelh(def.font);
		column_openstack(a, nil, labelh(def.font));
		column_insert(a, f, nil);
		return;
	}
	for(ff=a->frame; ff->anext; ff=ff->anext)
		if(y <= ff->colr.max.y) break;

	y = max(y, ff->colr.min.y + labelh(def.font));
	y = min(y, ff->colr.max.y);
	dy = ff->colr.max.y - y;
	if(dy <= labelh(def.font)) {
		f->collapsed = true;
		f->colr.min.y = 0;
		f->colr.max.y = labelh(def.font);
		column_openstack(a, ff, labelh(def.font) - dy);
	}else {
		f->colr.min.y = y;
		f->colr.max.y = ff->colr.max.y + extra_y;
		ff->colr.max.y = y;
	}
	column_insert(a, f, ff);
}

static int
thcol(Frame *f, bool moved) {
	Framewin *fw;
	Frame *fp, *fn;
	Area *a;
	Point pt, pt2;
	uint button;
	int ret, collapsed;

	focus(f->client, false);

	ret = TDone;
	if(!grabpointer(&scr.root, nil, None, MouseMask))
		return TDone;

	readmotion(&pt);
	pt2.x = f->area->r.min.x;
	pt2.y = pt.y;
	fw = framewin(f, pt2, OHoriz, Dx(f->area->r));

	if(moved)
		goto casemotion;

	vplace(fw, pt);
	for(;;)
		switch (readmouse(&pt, &button)) {
		case MotionNotify:
		casemotion:
			moved = 1;
			resizemode(THCol);
			if(mapwin(fw->w))
				grabpointer(&scr.root, nil, cursor[CurIcon], MouseMask);
			vplace(fw, pt);
			break;
		case ButtonRelease:
			if(!moved)
				goto done;

			if(button != 1)
				continue;
			SET(collapsed);
			SET(fp);
			SET(fn);
			a = f->area;
			if(a->floating)
				area_detach(f);
			else {
				collapsed = f->collapsed;
				fp = f->aprev;
				fn = f->anext;
				column_remove(f);
				if(!f->collapsed)
					if(fp)
						fp->colr.max.y = f->colr.max.y;
					else if(fn && fw->pt.y > fn->r.min.y)
						fn->colr.min.y = f->colr.min.y;
			}

			column_drop(fw->ra, f, fw->pt.y);
			if(!a->floating && collapsed) {
				/* XXX */
				for(; fn && fn->collapsed; fn=fn->anext)
					;
				if(fn == nil)
					for(fn=fp; fn && fn->collapsed; fn=fn->aprev)
						;
				if(fp)
					fp->colr.max.x += labelh(def.font);
			}


 			if(!a->frame && !a->floating && a->view->areas[a->screen]->next)
 				area_destroy(a);

			frame_focus(f);
			goto done;
		case ButtonPress:
			if(button == 2)
				ret = TVCol;
			else if(button == 3)
				ret = TFloat;
			else
				continue;
			goto done;
		}
done:
	resizemode(0);
	framedestroy(fw);
	return ret;
}

static int
tvcol(Frame *f, bool moved) {
	Framewin *fw;
	Window *cwin;
	Rectangle r;
	Point pt, pt2;
	uint button;
	int ret, scrn;

	focus(f->client, false);

	pt = querypointer(&scr.root);
	pt2.x = pt.x;
	pt2.y = f->area->r.min.y;

	scrn = f->area->screen > -1 ? f->area->screen : find_area(pt) ? find_area(pt)->screen : 0;
	r = f->view->r[scrn];
	fw = framewin(f, pt2, OVert, Dy(r));
	mapwin(fw->w);

	r.min.y += fw->grabbox.min.y + Dy(fw->grabbox)/2;
	r.max.y = r.min.y + 1;
	cwin = createwindow(&scr.root, r, 0, InputOnly, nil, 0);
	mapwin(cwin);

	ret = TDone;
	if(!grabpointer(&scr.root, cwin, cursor[CurIcon], MouseMask))
		goto done;

	resizemode(TVCol);

	hplace(fw, pt);
	for(;;)
		switch (readmouse(&pt, &button)) {
		case MotionNotify:
			moved = 1;
			hplace(fw, pt);
			continue;
		case ButtonPress:
			if(button == 2)
				ret = THCol;
			else if(button == 3)
				ret = TFloat;
			else
				continue;
			goto done;
		case ButtonRelease:
			if(button != 1)
				continue;
			if(fw->ra) {
				fw->ra = column_new(f->view, fw->ra, screen->idx, 0);
				area_moveto(fw->ra, f);
			}
			goto done;
		}

done:
	framedestroy(fw);
	destroywindow(cwin);
	resizemode(0);
	return ret;
}

static int
tfloat(Frame *f, bool moved) {
	Rectangle *rects;
	Rectangle frect, origin;
	Point pt, pt1;
	Client *c;
	Align align;
	uint nrect, button;
	int ret;

	c = f->client;
	if(!f->area->floating) {
		if(f->anext)
			f->anext->colr.min.y = f->colr.min.y;
		else if(f->aprev)
			f->aprev->colr.max.y = f->colr.max.y;
		area_moveto(f->view->floating, f);
		view_update(f->view);
		warppointer(grabboxcenter(f));
	}
	client_mapframe(f->client);
	if(!f->collapsed)
		focus(f->client, false);

	ret = TDone;
	if(!grabpointer(c->framewin, nil, cursor[CurMove], MouseMask))
		return TDone;

	rects = view_rects(f->view, &nrect, f);
	origin = f->r;
	frect = f->r;

	if(!readmotion(&pt)) {
		focus(f->client, false);
		goto done;
	}
	/* pt1 = grabboxcenter(f); */
	pt1 = pt;
	goto case_motion;

shut_up_ken:
	for(;;pt1=pt)
		switch (readmouse(&pt, &button)) {
		default: goto shut_up_ken;
		case MotionNotify:
			moved = 1;
		case_motion:
			origin = rectaddpt(origin, subpt(pt, pt1));
			origin = constrain(origin, -1);
			frect = origin;

			align = Center;
			snap_rect(rects, nrect, &frect, &align, def.snap);

			frect = frame_hints(f, frect, Center);
			frect = constrain(frect, -1);
			client_resize(c, frect);
			continue;
		case ButtonRelease:
			if(button != 1)
				continue;
			if(!moved) {
				f->collapsed = !f->collapsed;
				client_resize(f->client, f->floatr);
			}
			goto done;
		case ButtonPress:
			if(button != 3)
				continue;
			client_unmapframe(f->client);
			ret = THCol;
			goto done;
		}
done:
	free(rects);
	return ret;
}

Added cmd/wmii/main.c.






























































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
/* Copyright ©2004-2006 Anselm R. Garbe <garbeam at gmail dot com>
 * Copyright ©2006-2014 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#define EXTERN
#include "dat.h"
#include <X11/Xproto.h>
#include <X11/cursorfont.h>
#include <errno.h>
#include <fcntl.h>
#include <locale.h>
#include <pwd.h>
#include <sys/signal.h>
#include <sys/stat.h>
#include "fns.h"

static const char
	version[] = "wmii-"VERSION", "COPYRIGHT"\n";

static char*	address;
static char*	ns_path;
static int	sleeperfd;
static int	sock;
static int	exitsignal;

static struct sigaction	sa;
static struct passwd*	passwd;

static void
usage(void) {
	fatal("usage: wmii [-a <address>] [-r <wmiirc>] [-v]\n");
}

static int
errfmt(Fmt *f) {
	return fmtstrcpy(f, ixp_errbuf());
}

static void
scan_wins(void) {
	int i;
	uint num;
	XWindow *wins;
	XWindowAttributes wa;
	XWindow root, parent;

	if(XQueryTree(display, scr.root.xid, &root, &parent, &wins, &num)) {
		for(i = 0; i < num; i++) {
			if(!XGetWindowAttributes(display, wins[i], &wa) || wa.override_redirect)
				continue;
			if(!XGetTransientForHint(display, wins[i], &parent))
			if(wa.map_state == IsViewable)
				client_create(wins[i], &wa);
		}
		/* Manage transients. */
		for(i = 0; i < num; i++) {
			if(!XGetWindowAttributes(display, wins[i], &wa) || wa.override_redirect)
				continue;
			if(XGetTransientForHint(display, wins[i], &parent))
			if(wa.map_state == IsViewable)
				client_create(wins[i], &wa);
		}
	}
	if(wins)
		XFree(wins);
}

static void
init_ns(void) {
	char *s;

	if(address && strncmp(address, "unix!", 5) == 0) {
		ns_path = estrdup(&address[5]);
		s = strrchr(ns_path, '/');
		if(s != nil)
			*s = '\0';
		if(ns_path[0] != '/' || ns_path[0] == '\0')
			fatal("address %q is not an absolute path", address);
		setenv("NAMESPACE", ns_path, true);
	}else
		ns_path = ixp_namespace();

	if(ns_path == nil)
		fatal("Bad namespace path: %r\n");
}

static void
init_environment(void) {
	init_ns();

	if(address)
		setenv("WMII_ADDRESS", address, true);
	else
		address = smprint("unix!%s/wmii", ns_path);
	setenv("WMII_CONFPATH",
	       sxprint("%s/.%s:%s", getenv("HOME"), CONFDIR, GLOBALCONF),
	       true);
}

static void
create_cursor(int ident, uint shape) {
	cursor[ident] = XCreateFontCursor(display, shape);
}

static void
init_cursors(void) {
	static char zchar[1];
	Pixmap pix;
	XColor black, dummy;

	create_cursor(CurNormal, XC_left_ptr);
	create_cursor(CurNECorner, XC_top_right_corner);
	create_cursor(CurNWCorner, XC_top_left_corner);
	create_cursor(CurSECorner, XC_bottom_right_corner);
	create_cursor(CurSWCorner, XC_bottom_left_corner);
	create_cursor(CurMove, XC_fleur);
	create_cursor(CurDHArrow, XC_sb_h_double_arrow);
	create_cursor(CurDVArrow, XC_sb_v_double_arrow);
	create_cursor(CurInput, XC_xterm);
	create_cursor(CurSizing, XC_sizing);
	create_cursor(CurIcon, XC_icon);
	create_cursor(CurTCross, XC_tcross);

	XAllocNamedColor(display, scr.colormap,
			"black", &black, &dummy);
	pix = XCreateBitmapFromData(
			display, scr.root.xid,
			zchar, 1, 1);

	cursor[CurNone] = XCreatePixmapCursor(display,
			pix, pix,
			&black, &black,
			0, 0);

	XFreePixmap(display, pix);
}

/*
 * There's no way to check accesses to destroyed windows, thus
 * those cases are ignored (especially on UnmapNotifies).
 * Other types of errors call Xlib's default error handler, which
 * calls exit().
 */
ErrorCode ignored_xerrors[] = {
	{ 0, BadWindow },
	{ X_SetInputFocus, BadMatch },
	{ X_PolyText8, BadDrawable },
	{ X_PolyFillRectangle, BadDrawable },
	{ X_PolySegment, BadDrawable },
	{ X_ConfigureWindow, BadMatch },
	{ X_GrabKey, BadAccess },
	{ X_GetAtomName, BadAtom },
	{ 0, }
};

void
regerror(char *err) {
	fprint(2, "%s: %s\n", argv0, err);
}

static bool keep_screens = true;

void
init_screens(void) {
	static int old_n, old_nscreens;
	Rectangle *rects, *r;
	View *v;
	int i, j, n, m;

#ifdef notdef
	d.x = Dx(scr.rect) - Dx(screen->r);
	d.y = Dy(scr.rect) - Dy(screen->r);
	for(v=view; v; v=v->next) {
		v->r.max.x += d.x;
		v->r.max.y += d.y;
	}
#endif

	/* Reallocate screens, zero any new ones. */
	rects = xinerama_screens(&n);
	r = malloc(n * sizeof *r);

	/* Weed out subsumed/cloned screens */
	for(m=-1; m < n; n=m) {
		for(i=n-1, m=0; i >= 0; i--) {
			for(j=0; j < n; j++)
				if (i != j &&
				    eqrect(rects[i],
					   rect_intersection(rects[i], rects[j])))
					break;
			if (j == n)
				r[m++] = rects[i];
		}
		for(i=m-1, j=0; i >= 0; i--)
			rects[j++] = r[i];
	}
	free(r);

	m = nscreens;
	nscreens_new = keep_screens ? max(n, nscreens) : n;

	for(v=view; v; v=v->next)
		view_update_screens(v);

	nscreens = nscreens_new;
	screens = erealloc(screens, (nscreens + 1) * sizeof *screens);
	screens[nscreens] = nil;

	/* Reallocate buffers. */
	freeimage(disp.ibuf);
	freeimage(disp.ibuf32);
	disp.ibuf = allocimage(Dx(scr.rect), Dy(scr.rect), scr.depth);
	disp.ibuf32 = nil; /* Probably shouldn't do this until it's needed. */
	if(render_visual)
		disp.ibuf32 = allocimage(Dx(scr.rect), Dy(scr.rect), 32);

	/* Resize and initialize screens. */
	for(i=0; i < nscreens; i++) {
		if(i >= m)
			screens[i] = emallocz(sizeof *screens[i]);

		screen = screens[i];
		screen->idx = i;

		screen->showing = i < n;
		if(screen->showing)
			screen->r = rects[i];
		else
			screen->r = rectsetorigin(screen->r, scr.rect.max);

		if(i >= m)
			for(v=view; v; v=v->next)
				view_init(v, i);

		def.snap = Dy(screen->r) / 63;
		bar_init(screens[i]);
	}
	screen = screens[0];
	if(selview)
		view_update(selview);

	if (old_n != n || old_nscreens != nscreens)
		event("ScreenChange %d %d\n", n, nscreens);

	old_n = n;
	old_nscreens = nscreens;
}

void
wipe_screens(void) {
	keep_screens = false;
	init_screens();
	keep_screens = true;
}

static void
cleanup(void) {
	starting = -1;
	while(client)
		client_destroy(client);
	ixp_server_close(&srv);
	close(sleeperfd);
}

static void
cleanup_handler(int signal) {
	sa.sa_handler = SIG_DFL;
	sigaction(signal, &sa, nil);

	srv.running = false;

	switch(signal) {
	case SIGTERM:
		sa.sa_handler = cleanup_handler;
		sigaction(SIGALRM, &sa, nil);
		alarm(1);
	default:
		exitsignal = signal;
		break;
	case SIGALRM:
		raise(SIGTERM);
	case SIGINT:
		break;
	}
}

static void
init_traps(void) {
	char buf[1];
	int fd[2];

	if(pipe(fd) != 0)
		fatal("Can't pipe(): %r");

	if(doublefork() == 0) {
		close(fd[1]);
		close(ConnectionNumber(display));
		setsid();

		display = XOpenDisplay(nil);
		if(!display)
			fatal("Can't open display");

		/* Wait for parent to exit */
		read(fd[0], buf, 1);

		setfocus(pointerwin, RevertToPointerRoot);
		XCloseDisplay(display);
		exit(0);
	}

	close(fd[0]);
	sleeperfd = fd[1];

	sa.sa_flags = 0;
	sa.sa_handler = cleanup_handler;
	sigaction(SIGINT, &sa, nil);
	sigaction(SIGTERM, &sa, nil);
	sigaction(SIGQUIT, &sa, nil);
	sigaction(SIGHUP, &sa, nil);
	sigaction(SIGUSR1, &sa, nil);
	sigaction(SIGUSR2, &sa, nil);
}

void
spawn_command(const char *cmd) {
	char *shell, *p;


	if(doublefork() == 0) {
		if((p = pathsearch(getenv("WMII_CONFPATH"), cmd, true)))
			cmd = p;

		if(setsid() == -1)
			fatal("Can't setsid: %r");

		/* Run through the user's shell as a login shell */
		shell = passwd->pw_shell;
		if(shell[0] != '/')
			fatal("Shell is not an absolute path: %s", shell);
		p = smprint("-%s", strrchr(shell, '/') + 1);

		close(0);
		open("/dev/null", O_RDONLY);

		execl(shell, p, "-c", cmd, nil);
		fatal("Can't exec '%s': %r", cmd);
		/* NOTREACHED */
	}
}

static void
closedisplay(IxpConn *c) {
	USED(c);

	XCloseDisplay(display);
}

static void
printfcall(IxpFcall *f) {
	Dprint(D9p, "%F\n", f);
}

int
main(int argc, char *argv[]) {
	char **oargv;
	char *wmiirc;
	int i;

	IXP_ASSERT_VERSION;

	setlocale(LC_CTYPE, "");
	fmtinstall('r', errfmt);
	fmtinstall('a', afmt);
	fmtinstall('C', Cfmt);
	fmtinstall('E', fmtevent);
	quotefmtinstall();

	wmiirc = "wmiirc";

	oargv = argv;
	ARGBEGIN{
	case 'a':
		address = EARGF(usage());
		break;
	case 'r':
		wmiirc = EARGF(usage());
		break;
	case 'v':
		lprint(1, "%s", version);
		exit(0);
	case 'D':
		if(waserror())
			fatal("parsing debug flags: %r");
		msg_debug(EARGF(usage()));
		poperror();
		break;
	default:
		usage();
		break;
	}ARGEND;

	if(argc)
		usage();

	starting = true;

	initdisplay();

	traperrors(true);
	selectinput(&scr.root, SubstructureRedirectMask);
	if(traperrors(false))
		fatal("another window manager is already running");

	passwd = getpwuid(getuid());
	user = estrdup(passwd->pw_name);
	gethostname(hostname, sizeof(hostname) - 1);

	init_environment();

	fmtinstall('F', Ffmt);
	ixp_printfcall = printfcall;

	sock = ixp_announce(address);
	if(sock < 0)
		fatal("Can't create socket %q: %r", address);
	closeexec(ConnectionNumber(display));
	closeexec(sock);

	if(wmiirc[0])
		spawn_command(wmiirc);

	init_traps();
	init_cursors();
	update_keys();
	ewmh_init();
	xext_init();

	event_debug = debug_event;

	srv.preselect = event_preselect;
	ixp_listen(&srv, sock, &p9srv, ixp_serve9conn, nil);
	ixp_listen(&srv, ConnectionNumber(display), nil, event_fdready, closedisplay);

	def.border = 1;
	def.colmode = Colstack;
	def.font = loadfont(FONT);
	def.incmode = ISqueeze;

	def.mod = Mod1Mask;

	loadcolor(&def.focuscolor, FOCUSCOLORS, nil);
	loadcolor(&def.normcolor, NORMCOLORS, nil);

	disp.sel = pointerscreen();

	init_screens();
	root_init();

	disp.focus = nil;
	setfocus(screen->barwin, RevertToParent);
	view_select("1");

	scan_wins();
	starting = false;

	view_update_all();
	ewmh_updateviews();

	event("FocusTag %s\n", selview->name);

	i = ixp_serverloop(&srv);
	if(i)
		fprint(2, "%s: error: %r\n", argv0);
	else
		event("Quit");

	cleanup();

	if(exitsignal)
		raise(exitsignal);
	if(execstr) {
		char *toks[32];
		int n;

		n = unquote(strdup(execstr), toks, nelem(toks)-1);
		toks[n] = nil;
		execvp(toks[0], toks);
		fprint(2, "%s: failed to exec %q: %r\n", argv0, execstr);
		execvp(argv0, oargv);
		fatal("failed to exec myself");
	}
	return i;
}

Added cmd/wmii/message.c.


































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
/* Copyright ©2006-2014 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "dat.h"
#include <ctype.h>
#include "fns.h"

static char* msg_grow(View*, IxpMsg*);
static char* msg_nudge(View*, IxpMsg*);
static char* msg_selectframe(Area*, IxpMsg*, int);
static char* msg_sendframe(Frame*, int, bool);

#define DIR(s) (\
	s == LUP	? North : \
	s == LDOWN	? South : \
	s == LLEFT	? West  : \
	s == LRIGHT	? East  : \
	(error(Ebadvalue), 0))

static char
	Ebadcmd[] = "bad command",
	Ebadvalue[] = "bad value",
	Ebadusage[] = "bad usage";

/* Edit |sort Edit |sed 's/"([^"]+)"/L\1/g' | tr 'a-z' 'A-Z' */
enum {
	LALLOW,
	LBAR,
	LBORDER,
	LCLIENT,
	LCOLMODE,
	LCOLORS,
	LDEBUG,
	LDOWN,
	LEXEC,
	LFLOATING,
	LFOCUSCOLORS,
	LFONT,
	LFONTPAD,
	LFULLSCREEN,
	LGRABMOD,
	LGROUP,
	LGROW,
	LINCMODE,
	LKILL,
	LLABEL,
	LLEFT,
	LNORMCOLORS,
	LNUDGE,
	LOFF,
	LON,
	LQUIT,
	LRIGHT,
	LSELCOLORS,
	LSELECT,
	LSEND,
	LSLAY,
	LSPAWN,
	LSWAP,
	LTAGS,
	LTOGGLE,
	LUP,
	LURGENT,
	LVIEW,
	LWIPESCREENS,
	LTILDE,
};
char *symtab[] = {
	"allow",
	"bar",
	"border",
	"client",
	"colmode",
	"colors",
	"debug",
	"down",
	"exec",
	"floating",
	"focuscolors",
	"font",
	"fontpad",
	"fullscreen",
	"grabmod",
	"group",
	"grow",
	"incmode",
	"kill",
	"label",
	"left",
	"normcolors",
	"nudge",
	"off",
	"on",
	"quit",
	"right",
	"selcolors",
	"select",
	"send",
	"slay",
	"spawn",
	"swap",
	"tags",
	"toggle",
	"up",
	"urgent",
	"view",
	"wipescreens",
	"~",
};

static char* barpostab[] = {
	"bottom", "top",
};
char* debugtab[] = {
	"9p",
	"dnd",
	"event",
	"events",
	"ewmh",
	"focus",
	"generic",
	"stack",
	nil
};
static char* permtab[] = {
	"activate", nil
};
static char* incmodetab[] = {
	"ignore", "show", "squeeze",
};
static char* floatingtab[] = {
	"never", "off", "on", "always"
};
static char* toggletab[] = {
	"off", "on", "toggle",
};

/* Edit ,y/^[a-zA-Z].*\n.* {\n/d
 * Edit s/^([a-zA-Z].*)\n(.*) {\n/\1 \2;\n/
 * Edit ,x/^static.*\n/d
 */

static int
_bsearch(char *from, char **tab, int ntab) {
	int i, n, m, cmp;
	char *to, *end;
	Rune r;

	if(from == nil)
		return -1;

	end = buffer + sizeof buffer - UTFmax - 1;
	for(to=buffer; *from && to < end;) {
		from += chartorune(&r, from);
		if(r != 0x80) {
			r = tolowerrune(r);
			to += runetochar(to, &r);
		}
	}
	*to = '\0';
	to = buffer;

	n = ntab;
	i = 0;
	while(n) {
		m = n/2;
		cmp = strcmp(to, tab[i+m]);
		if(cmp == 0)
			return i+m;
		if(cmp < 0 || m == 0)
			n = m;
		else {
			i += m;
			n = n-m;
		}
	}
	return -1;
}

static int
_lsearch(char *from, char **tab, int ntab) {
	int i;

	if(from != nil)
		for(i=0; i < ntab; i++)
			if(!strcmp(from, tab[i]))
				return i;
	error(Ebadvalue);
	return 0;
}

static int
getsym(char *s) {
	return _bsearch(s, symtab, nelem(symtab));
}

static void
setdef(int *ptr, char *s, char *tab[], int ntab) {
	int i;

	i = _bsearch(s, tab, ntab);
	if(i < 0)
		error(Ebadvalue);
	*ptr = i;
}

static int
gettoggle(char *s) {
	return _lsearch(s, toggletab, nelem(toggletab));
}

static int
getdirection(IxpMsg *m) {
	int i;

	switch(i = getsym(msg_getword(m, 0))) {
	case LLEFT:
	case LRIGHT:
	case LUP:
	case LDOWN:
		return i;
	}
	error(Ebadusage);
	return -1;
}

static ulong
msg_getulong(const char *s) {
	ulong l;

	if(!(s && getulong(s, &l)))
		error(Ebadvalue);
	return l;
}

static long
msg_getlong(const char *s) {
	long l;

	if(!(s && getlong(s, &l)))
		error(Ebadvalue);
	return l;
}

void
msg_eatrunes(IxpMsg *m, int (*p)(Rune), int val) {
	Rune r;
	int n;

	while(m->pos < m->end) {
		n = chartorune(&r, m->pos);
		if(p(r) != val)
			break;
		m->pos += n;
	}
	if(m->pos > m->end)
		m->pos = m->end;
}

char*
msg_getword(IxpMsg *m, char *errmsg) {
	char *ret;
	Rune r;
	int n;

	msg_eatrunes(m, isspacerune, true);
	ret = m->pos;
	msg_eatrunes(m, isspacerune, false);
	n = chartorune(&r, m->pos);
	*m->pos = '\0';
	m->pos += n;
	msg_eatrunes(m, isspacerune, true);

	/* Filter out comments. */
	if(*ret == '#') {
		*ret = '\0';
		m->pos = m->end;
	}
	if(*ret == '\\')
		if(ret[1] == '\\' || ret[1] == '#')
			ret++;
	if(*ret == '\0')
		ret = nil;
	if(ret == nil && errmsg)
		error(errmsg);
	return ret;
}

typedef struct Mask Mask;
struct Mask {
	long*	mask;
	char**	table;
};

static int
Mfmt(Fmt *f) {
	Mask m;

	m = va_arg(f->args, Mask);
	return unmask(f, *m.mask, m.table, '+');
}

char*
mask(char **s, int *add, int *old) {
	static char seps[] = "+-^";
	char *p, *q;

again:
	p = *s;
	if(*old == '\0')
		return nil;
	*add = *old;

	if(*p == '/') {
		/* Check for regex. */
		if(!(q = strchr(p+1, '/')))
			goto fail;
		if(*q++ != '/' || !memchr(seps, (*old=*q), sizeof seps))
			goto fail;
	}
	else {
		for(q=p; (*old=*q) && !strchr(seps, *q);)
			q++;
		if(memchr(p, '/', q-p))
			goto fail;
	}

	*q++ = '\0';
	*s = q;
	if(p + 1 == q)
		goto again;
	return p;
fail:
	while((*old=*q) && !strchr(seps, *q))
		q++;
	goto again;
}

static void
setmask(Mask m, char *s) {
	char *opt;
	int add, old, i, n;
	long newmask;

	if(s == nil)
		s = "";
	for(n=0; m.table[n]; n++)
		;
	newmask = memchr("+-^", s[0], 3) ? *m.mask : 0L;

	old = '+';
	while((opt = mask(&s, &add, &old))) {
		i = _bsearch(opt, m.table, n);
		if(i == -1)
			error(Ebadvalue);
		else if(add = '^')
			newmask ^= 1<<i;
		else if(add == '+')
			newmask |= 1<<i;
		else if(add == '-')
			newmask &= ~(1<<i);
	}
	*m.mask = newmask;
}

void
msg_debug(char *s) {
	setmask((Mask){&debugflag, debugtab}, s);
}

static Client*
strclient(View *v, char *s) {
	Client *c;

	/*
	 * sel
	 * 0x<window xid>
	 */

	if(s && !strcmp(s, "sel"))
		c = view_selclient(v);
	else
		c = win2client(msg_getulong(s));
	if(c == nil)
		error(Ebadvalue);
	return c;
}

Area*
strarea(View *v, ulong scrn, const char *area) {
	Area *a;
	const char *screen;
	char *p;
	long i;

	/*
	 * sel
	 * ~
	 * <column number>
	 */

	if(area == nil)
		error(Ebadvalue);

	if((p = strchr(area, ':'))) {
		/* <screen>:<area> */
		*p++ = '\0';
		screen = area;
		area = p;

		if(!strcmp(screen, "sel"))
			scrn = v->selscreen;
		else
			scrn = msg_getulong(screen);
	}
	else if(!strcmp(area, "sel"))
		return v->sel;

	if(!strcmp(area, "sel")) {
		if(scrn != v->selscreen)
			error(Ebadvalue);
		return v->sel;
	}

	if(!strcmp(area, "~"))
		return v->floating;

	if(scrn < 0)
		error(Ebadvalue);

	i = msg_getlong(area);
	if(i == 0)
		error(Ebadvalue);

	if(i > 0) {
		for(a = v->areas[scrn]; a; a = a->next)
			if(i-- == 1) break;
	}
	else {
		/* FIXME: Switch to circularly linked list. */
		for(a = v->areas[scrn]; a->next; a = a->next)
			;
		for(; a; a = a->prev)
			if(++i == 0) break;
	}
	if(a == nil)
		error(Ebadvalue);
	return a;
}

static Frame*
getframe(View *v, int scrn, IxpMsg *m) {
	Frame *f;
	Area *a;
	char *s;
	ulong l;

	s = msg_getword(m, Ebadvalue);
	if(!strcmp(s, "client"))
		f = client_viewframe(strclient(v, msg_getword(m, Ebadvalue)),
				     v);
	else {
		/* XXX: Multihead */
		a = strarea(v, scrn, s);

		s = msg_getword(m, Ebadvalue);
		f = nil;
		if(!strcmp(s, "sel"))
			f = a->sel;
		else {
			l = msg_getulong(s);
			for(f=a->frame; f; f=f->anext)
				if(--l == 0) break;
		}
	}
	if(f == nil)
		error(Ebadvalue);
	return f;
}

char*
readctl_bar(Bar *b) {
	bufclear();
	bufprint("colors %s\n", b->colors.colstr);
	bufprint("label %s\n", b->text);
	return buffer;
}

char*
message_bar(Bar *b, IxpMsg *m) {

	switch(getsym(msg_getword(m, nil))) {
	case LCOLORS:
		msg_parsecolors(m, &b->colors);
		break;
	case LLABEL:
		utflcpy(b->text, (char*)m->pos, sizeof b->text);
		break;
	default:
		error(Ebadvalue);
	}
	bar_draw(b->screen);
	return nil;
}

char*
readctl_client(Client *c) {
	bufclear();
	bufprint("%#C\n", c);
	bufprint("allow %M\n", (Mask){&c->permission, permtab});
	bufprint("floating %s\n", floatingtab[c->floating + 1]);
	if(c->fullscreen >= 0)
		bufprint("fullscreen %d\n", c->fullscreen);
	else
		bufprint("fullscreen off\n");
	bufprint("group %#ulx\n", c->group ? c->group->leader : 0);
	if(c->pid)
		bufprint("pid %d\n", c->pid);
	bufprint("tags %s\n", c->tags);
	bufprint("urgent %s\n", TOGGLE(c->urgent));
	return buffer;
}

char*
message_client(Client *c, IxpMsg *m) {
	char *s;
	long l;

	s = msg_getword(m, Ebadcmd);

	/*
	 * Toggle ::= on
	 *	    | off
	 *	    | toggle
	 *	    | <screen>
	 * floating <toggle>
	 * fullscreen <toggle>
	 * kill
	 * slay
	 * tags <tags>
	 * urgent <toggle>
	 */

	switch(getsym(s)) {
	case LALLOW:
		setmask((Mask){&c->permission, permtab}, msg_getword(m, 0));
		break;
	case LFLOATING:
		c->floating = -1 + _lsearch(msg_getword(m, Ebadvalue), floatingtab, nelem(floatingtab));
		break;
	case LFULLSCREEN:
		s = msg_getword(m, Ebadvalue);
		if(getlong(s, &l))
			fullscreen(c, On, l);
		else
			fullscreen(c, gettoggle(s), -1);
		break;
	case LGROUP:
		group_remove(c);
		c->w.hints->group = msg_getulong(msg_getword(m, Ebadvalue));
		if(c->w.hints->group)
			group_init(c);
		break;
	case LKILL:
		client_kill(c, true);
		break;
	case LSLAY:
		client_kill(c, false);
		break;
	case LTAGS:
		client_applytags(c, m->pos);
		break;
	case LURGENT:
		client_seturgent(c, gettoggle(msg_getword(m, Ebadvalue)), UrgManager);
		break;
	default:
		error(Ebadcmd);
	}
	return nil;
}

char*
message_root(void *p, IxpMsg *m) {
	Font *fn;
	char *s, *ret;
	ulong n;
	int i;

	USED(p);
	ret = nil;
	s = msg_getword(m, 0);
	if(s == nil)
		return nil;

	if(!strcmp(s, "backtrace")) {
		backtrace(m->pos);
		return nil;
	}

	if(!strcmp(s, "xinerama")) {
		setenv("XINERAMA_SCREENS", m->pos, 1);
		init_screens();
		return nil;
	}

	switch(getsym(s)) {
	case LBAR: /* bar on? <"top" | "bottom"> */
		s = msg_getword(m, Ebadvalue);
		if(!strcmp(s, "on"))
			s = msg_getword(m, Ebadvalue);
		setdef(&screen->barpos, s, barpostab, nelem(barpostab));
		view_update(selview);
		break;

	case LBORDER:
		def.border = msg_getulong(msg_getword(m, 0));;
		view_update(selview);
		break;

	case LCOLMODE:
		setdef(&def.colmode, msg_getword(m, 0), modes, Collast);
		break;

	case LDEBUG:
		msg_debug(msg_getword(m, 0));
		break;

	case LEXEC:
		execstr = strdup(m->pos);
		srv.running = 0;
		break;

	case LSPAWN:
		spawn_command(m->pos);
		break;

	case LFOCUSCOLORS:
		msg_parsecolors(m, &def.focuscolor);
		goto updatecolors;

	case LFONT:
		fn = loadfont(m->pos);
		if(fn) {
			freefont(def.font);
			def.font = fn;
			for(n=0; n < nscreens; n++)
				bar_resize(screens[n]);
		}else
			ret = "can't load font";
		view_update(selview);
		break;

	case LFONTPAD:
		if(!getint(msg_getword(m, 0), &def.font->pad.min.x) ||
		   !getint(msg_getword(m, 0), &def.font->pad.max.x) ||
		   !getint(msg_getword(m, 0), &def.font->pad.max.y) ||
		   !getint(msg_getword(m, 0), &def.font->pad.min.y))
			ret = "invalid rectangle";
		else {
			for(n=0; n < nscreens; n++)
				bar_resize(screens[n]);
			view_update(selview);
		}
		break;

	case LGRABMOD:
		s = msg_getword(m, Ebadvalue);
		if(!parsekey(s, &i, nil) || i == 0)
			return Ebadvalue;

		def.mod = i;
		break;

	case LINCMODE:
		setdef(&def.incmode, msg_getword(m, 0), incmodetab, nelem(incmodetab));
		view_update(selview);
		break;

	case LNORMCOLORS:
		msg_parsecolors(m, &def.normcolor);
	updatecolors:
		for(Client *c=client; c; c=c->next)
			client_reparent(c);
		view_update(selview);
		break;

	case LSELCOLORS:
		warning("selcolors have been removed");
		return Ebadcmd;

	case LVIEW:
		view_select(m->pos);
		break;

	case LWIPESCREENS:
		wipe_screens();
		break;

	case LQUIT:
		srv.running = 0;
		break;
	default:
		return Ebadcmd;
	}
	return ret;
}

char*
readctl_root(void) {
	fmtinstall('M', Mfmt);
	bufclear();
	bufprint("bar on %s\n", barpostab[screen->barpos]);
	bufprint("border %d\n", def.border);
	bufprint("colmode %s\n", modes[def.colmode]);
	if(debugflag)
		bufprint("debug %M\n", (Mask){&debugflag, debugtab});
	if(debugfile)
		bufprint("debugfile %M", (Mask){&debugfile, debugtab});
	bufprint("focuscolors %s\n", def.focuscolor.colstr);
	bufprint("font %s\n", def.font->name);
	bufprint("fontpad %d %d %d %d\n", def.font->pad.min.x, def.font->pad.max.x,
		 def.font->pad.max.y, def.font->pad.min.y);
	bufprint("grabmod %s\n", (Mask){&def.mod, modkey_names});
	bufprint("incmode %s\n", incmodetab[def.incmode]);
	bufprint("normcolors %s\n", def.normcolor.colstr);
	bufprint("view %s\n", selview->name);
	return buffer;
}

char*
message_view(View *v, IxpMsg *m) {
	Area *a;
	char *s;

	s = msg_getword(m, 0);
	if(s == nil)
		return nil;

	/*
	 * area ::= ~
	 *        | <column number>
	 *        | sel
	 * direction ::= left
	 *             | right
	 *             | up
	 *             | down
	 * # This *should* be identical to <frame>
	 * place ::= <column number>
	 *	  #| ~ # This should be, but isn't.
	 *	   | <direction>
	 *         | toggle
	 * colmode ::= default
	 *           | stack
	 *           | normal
	 * column ::= sel
	 *          | <column number>
	 * frame ::= up
	 *         | down
	 *         | left
	 *         | right
	 *         | toggle
	 *         | client <window xid>
	 *         | sel
	 *         | ~
	 *         | <column> <frame number>
	 *         | <column>
	 * amount ::=
	 *	    | <number>
	 *          | <number>px
	 *
	 * colmode <area> <colmode>
	 * select <area>
	 * send <frame> <place>
	 * swap <frame> <place>
	 * grow <thing> <direction> <amount>
	 * nudge <thing> <direction> <amount>
	 * select <ordframe>
	 */

	switch(getsym(s)) {
	case LCOLMODE:
		s = msg_getword(m, Ebadvalue);
		a = strarea(v, screen->idx, s);

		s = msg_getword(m, Ebadvalue);
		if(!column_setmode(a, s))
			return Ebadvalue;

		column_arrange(a, false);
		view_restack(v);

		view_update(v);
		return nil;
	case LGROW:
		return msg_grow(v, m);
	case LNUDGE:
		return msg_nudge(v, m);
	case LSELECT:
		return msg_selectarea(v->sel, m);
	case LSEND:
		return msg_sendclient(v, m, false);
	case LSWAP:
		return msg_sendclient(v, m, true);
	default:
		return Ebadcmd;
	}
	/* not reached */
}

char*
readctl_view(View *v) {
	Area *a;
	int s;

	bufclear();
	bufprint("%s\n", v->name);

	bufprint("urgent %s\n", TOGGLE(v->urgent));

	/* select <area>[ <frame>] */
	bufprint("select %a", v->sel);
	if(v->sel->sel)
		bufprint(" %d", frame_idx(v->sel->sel));
	bufprint("\n");

	/* select client <client> */
	if(v->sel->sel)
		bufprint("select client %#C\n", v->sel->sel->client);

	foreach_area(v, s, a)
		bufprint("colmode %a %s\n", a, column_getmode(a));
	return buffer;
}

static void
getamt(IxpMsg *m, Point *amt) {
	char *s, *p;
	long l;

	s = msg_getword(m, 0);
	if(s) {
		p = strend(s, 2);
		if(!strcmp(p, "px")) {
			*p = '\0';
			amt->x = 1;
			amt->y = 1;
		}

		l = msg_getlong(s);
		amt->x *= l;
		amt->y *= l;
	}
}

static char*
msg_grow(View *v, IxpMsg *m) {
	Client *c;
	Frame *f;
	Rectangle h, r;
	Point amount;
	int dir;

	f = getframe(v, screen->idx, m);
	c = f->client;
	h = c->w.hints->aspect;

	dir = getdirection(m);

	amount.x = Dy(f->titlebar);
	amount.y = Dy(f->titlebar);
	if(amount.x < c->w.hints->inc.x)
		amount.x = c->w.hints->inc.x;
	if(amount.y < c->w.hints->inc.y)
		amount.y = c->w.hints->inc.y;
	getamt(m, &amount);

	if (dir == LLEFT || dir == LRIGHT)
		amount.y = h.min.x ? amount.x * h.min.y / h.min.x : 0;
	else
		amount.x = h.min.y ? amount.y * h.min.x / h.min.y : 0;

	if(f->area->floating)
		r = f->r;
	else
		r = f->colr;

	if (dir == LLEFT || dir == LUP)
		r.min = subpt(r.min, amount);
	else if (dir == LRIGHT || dir == LDOWN)
		r.max = addpt(r.max, amount);

	if(f->area->floating)
		float_resizeframe(f, r);
	else
		column_resizeframe(f, r);

	return nil;
}

static char*
msg_nudge(View *v, IxpMsg *m) {
	Frame *f;
	Rectangle r;
	Point amount;
	int dir;

	f = getframe(v, screen->idx, m);
	dir = getdirection(m);

	amount.x = Dy(f->titlebar);
	amount.y = Dy(f->titlebar);
	getamt(m, &amount);

	if(f->area->floating)
		r = f->r;
	else
		r = f->colr;
	switch(dir) {
	case LLEFT:	r = rectaddpt(r, Pt(-amount.x, 0)); break;
	case LRIGHT:	r = rectaddpt(r, Pt( amount.x, 0)); break;
	case LUP:	r = rectaddpt(r, Pt(0, -amount.y)); break;
	case LDOWN:	r = rectaddpt(r, Pt(0,  amount.y)); break;
	default:	abort();
	}

	if(f->area->floating)
		float_resizeframe(f, r);
	else
		column_resizeframe(f, r);
	return nil;
}

void
msg_parsecolors(IxpMsg *m, CTuple *col) {
	CTuple tpl;
	char n;

	n = loadcolor(&tpl, m->pos, m->end);
	m->pos += n;
	if(n == 0 || msg_getword(m, nil))
		error("bad color string");
	*col = tpl;
}

char*
msg_selectarea(Area *a, IxpMsg *m) {
	Frame *f;
	Area *ap;
	View *v;
	char *s;
	ulong i;
	int sym;

	v = a->view;
	s = msg_getword(m, Ebadvalue);
	sym = getsym(s);

	switch(sym) {
	case LTOGGLE:
		if(!a->floating)
			ap = v->floating;
		else if(v->revert && v->revert != a)
			ap = v->revert;
		else
			ap = v->firstarea;
		break;
	case LLEFT:
	case LRIGHT:
	case LUP:
	case LDOWN:
	case LCLIENT:
		return msg_selectframe(a, m, sym);
	case LTILDE:
		ap = v->floating;
		break;
	default:
		/* XXX: Multihead */
		ap = strarea(v, a->screen, s);
		if(ap->floating)
			return Ebadvalue;

		if((s = msg_getword(m, 0))) {
			i = msg_getulong(s);
			for(f = ap->frame; f; f = f->anext)
				if(--i == 0) break;
			if(i != 0)
				return Ebadvalue;
			frame_focus(f);
			return nil;
		}
	}

	area_focus(ap);
	return nil;
}

static char*
msg_selectframe(Area *a, IxpMsg *m, int sym) {
	Client *c;
	Frame *f, *fp;
	char *s;
	bool stack;
	ulong i, dy;

	f = a->sel;
	fp = f;

	stack = false;
	if(sym == LUP || sym == LDOWN)
		if((s = msg_getword(m, 0)))
			if(!strcmp(s, "stack"))
				stack = true;
			else
				return Ebadvalue;

	if(sym == LCLIENT) {
		s = msg_getword(m, Ebadvalue);
		i = msg_getulong(s);
		c = win2client(i);
		if(c == nil)
			return Ebadvalue;
		f = client_viewframe(c, a->view);
		if(!f)
			return Ebadvalue;
	}
	else if(!find(&a, &f, DIR(sym), true, stack))
		return Ebadvalue;

	area_focus(a);

	if(f != nil) {
		/* XXX */
		if(fp && fp->area == a)
		if(f->collapsed && !f->area->floating && f->area->mode == Coldefault) {
			dy = Dy(f->colr);
			f->colr.max.y = f->colr.min.y + Dy(fp->colr);
			fp->colr.max.y = fp->colr.min.y + dy;
			column_arrange(a, false);
		}

		frame_focus(f);
		frame_restack(f, nil);
		if(f->view == selview)
			view_restack(a->view);
	}
	return nil;
}

static char*
sendarea(Frame *f, Area *to, bool swap) {
	Client *c;

	c = f->client;
	if(!to)
		return Ebadvalue;

	if(!swap)
		area_moveto(to, f);
	else if(to->sel)
		frame_swap(f, to->sel);
	else
		return Ebadvalue;

	frame_focus(client_viewframe(c, f->view));
	/* view_arrange(v); */
	view_update_all();
	return nil;
}

char*
msg_sendclient(View *v, IxpMsg *m, bool swap) {
	Area *to, *a;
	Frame *f, *ff;
	Client *c;
	char *s;
	int sym;

	c = strclient(v, msg_getword(m, 0));
	f = client_viewframe(c, v);
	if(f == nil)
		return Ebadvalue;

	a = f->area;
	to = nil;

	s = msg_getword(m, Ebadvalue);
	sym = getsym(s);

	/* FIXME: Should use a helper function. */
	switch(sym) {
	case LUP:
	case LDOWN:
		return msg_sendframe(f, sym, swap);
	case LLEFT:
		if(a->floating)
			return Ebadvalue;
		to = a->prev;
		break;
	case LRIGHT:
		if(a->floating)
			return Ebadvalue;
		to = a->next;
		break;
	case LTOGGLE:
		if(!a->floating)
			to = v->floating;
		else if(f->column)
			to = view_findarea(v, f->screen, f->column, true);
		else
			to = view_findarea(v, v->selscreen, v->selcol, true);
		break;
	case LTILDE:
		if(a->floating)
			return Ebadvalue;
		to = v->floating;
		break;
	default:
		to = strarea(v, v->selscreen, s);
		// to = view_findarea(v, scrn, i, true);
		break;
	}


	if(!to && !swap) {
		/* XXX: Multihead - clean this up, move elsewhere. */
		if(!f->anext && f == f->area->frame) {
			ff = f;
			to = a;
			if(!find(&to, &ff, DIR(sym), false, false))
				return Ebadvalue;
		}
		else {
			to = (sym == LLEFT) ? nil : a;
			to = column_new(v, to, a->screen, 0);
		}
	}

	return sendarea(f, to, swap);
}

static char*
msg_sendframe(Frame *f, int sym, bool swap) {
	Client *c;
	Area *a;
	Frame *fp;

	SET(fp);
	c = f->client;

	a = f->area;
	fp = f;
	if(!find(&a, &fp, DIR(sym), false, false))
		return Ebadvalue;
	if(a != f->area)
		return sendarea(f, a, swap);

	switch(sym) {
	case LUP:
		fp = f->aprev;
		if(!fp)
			return Ebadvalue;
		if(!swap)
			fp = fp->aprev;
		break;
	case LDOWN:
		fp = f->anext;
		if(!fp)
			return Ebadvalue;
		break;
	default:
		die("can't get here");
	}

	if(swap)
		frame_swap(f, fp);
	else {
		frame_remove(f);
		frame_insert(f, fp);
	}

	/* view_arrange(f->view); */

	frame_focus(client_viewframe(c, f->view));
	view_update_all();
	return nil;
}

void
warning(const char *fmt, ...) {
	va_list ap;
	char *s;

	va_start(ap, fmt);
	s = vsmprint(fmt, ap);
	va_end(ap);

	event("Warning %s\n", s);
	fprint(2, "%s: warning: %s\n", argv0, s);
	free(s);
}

Added cmd/wmii/mouse.c.


































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
/* Copyright ©2006-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "dat.h"
#include "fns.h"

/* Here be dragons. */

enum {
	ButtonMask =
		ButtonPressMask | ButtonReleaseMask,
	MouseMask =
		ButtonMask | PointerMotionMask
};

static Cursor
quad_cursor(Align align) {
	switch(align) {
	case NEast: return cursor[CurNECorner];
	case NWest: return cursor[CurNWCorner];
	case SEast: return cursor[CurSECorner];
	case SWest: return cursor[CurSWCorner];
	case South:
	case North: return cursor[CurDVArrow];
	case East:
	case West:  return cursor[CurDHArrow];
	default:    return cursor[CurMove];
	}
}

static bool
cwin_expose(Window *w, void *aux, XExposeEvent *e) {

	fill(w, rectsubpt(w->r, w->r.min), &def.focuscolor.bg);
	fill(w, w->r, &def.focuscolor.bg);
	return false;
}

static Handlers chandler = {
	.expose = cwin_expose,
};

Window*
constraintwin(Rectangle r) {
	Window *w;

	w = createwindow(&scr.root, r, 0, InputOnly, nil, 0);
	if(0) {
		Window *w2;

		w2 = createwindow(&scr.root, r, 0, InputOutput, nil, 0);
		selectinput(w2, ExposureMask);
		w->aux = w2;

		setborder(w2, 1, &def.focuscolor.border);
		sethandler(w2, &chandler);
		mapwin(w2);
		raisewin(w2);
	}
	mapwin(w);
	return w;
}

void
destroyconstraintwin(Window *w) {

	if(w->aux)
		destroywindow(w->aux);
	destroywindow(w);
}

static Window*
gethsep(Rectangle r) {
	Window *w;
	WinAttr wa;

	wa.background_pixel = pixelvalue(&scr.root, &def.normcolor.border);
	w = createwindow(&scr.root, r, scr.depth, InputOutput, &wa, CWBackPixel);
	mapwin(w);
	raisewin(w);
	return w;
}

static void
rect_morph(Rectangle *r, Point d, Align *mask) {
	int n;

	if(*mask & North)
		r->min.y += d.y;
	if(*mask & West)
		r->min.x += d.x;
	if(*mask & South)
		r->max.y += d.y;
	if(*mask & East)
		r->max.x += d.x;

	if(r->min.x > r->max.x) {
		n = r->min.x;
		r->min.x = r->max.x;
		r->max.x = n;
		*mask ^= East|West;
	}
	if(r->min.y > r->max.y) {
		n = r->min.y;
		r->min.y = r->max.y;
		r->max.y = n;
		*mask ^= North|South;
	}
}

/* Yes, yes, macros are evil. So are patterns. */
#define frob(x, y) \
	const Rectangle *rp;                                              \
	int i, tx;                                                        \
									  \
	for(i=0; i < nrect; i++) {                                        \
		rp = &rects[i];                                           \
		if((rp->min.y <= r->max.y) && (rp->max.y >= r->min.y)) {  \
			tx = rp->min.x;                                   \
			if(abs(tx - x) <= abs(dx))                        \
				dx = tx - x;                              \
									  \
			tx = rp->max.x;                                   \
			if(abs(tx - x) <= abs(dx))                        \
				dx = tx - x;                              \
		}                                                         \
	}                                                                 \
	return dx                                                         \

static int
snap_hline(const Rectangle *rects, int nrect, int dx, const Rectangle *r, int y) {
	frob(y, x);
}

static int
snap_vline(const Rectangle *rects, int nrect, int dx, const Rectangle *r, int x) {
	frob(x, y);
}

#undef frob

/* Returns a gravity for increment handling. It's normally the
 * opposite of the mask (the directions that we're resizing in),
 * unless a snap occurs, in which case, it's the direction of the
 * snap.
 */
Align
snap_rect(const Rectangle *rects, int num, Rectangle *r, Align *mask, int snap) {
	Align ret;
	Point d;

	d.x = snap+1;
	d.y = snap+1;

	if(*mask&North)
		d.y = snap_hline(rects, num, d.y, r, r->min.y);
	if(*mask&South)
		d.y = snap_hline(rects, num, d.y, r, r->max.y);

	if(*mask&East)
		d.x = snap_vline(rects, num, d.x, r, r->max.x);
	if(*mask&West)
		d.x = snap_vline(rects, num, d.x, r, r->min.x);

	ret = Center;
	if(abs(d.x) <= snap)
		ret ^= East|West;
	else
		d.x = 0;

	if(abs(d.y) <= snap)
		ret ^= North|South;
	else
		d.y = 0;

	rect_morph(r, d, mask);
	return ret ^ *mask;
}

int
readmouse(Point *p, uint *button) {
	XEvent ev;

	for(;;) {
		XMaskEvent(display, MouseMask|ExposureMask|PropertyChangeMask, &ev);
		debug_event(&ev);
		switch(ev.type) {
		case Expose:
		case NoExpose:
		case PropertyNotify:
			event_dispatch(&ev);
		default:
			Dprint(DEvent, "readmouse(): ignored: %E\n", &ev);
			continue;
		case ButtonPress:
		case ButtonRelease:
			*button = ev.xbutton.button;
		case MotionNotify:
			p->x = ev.xmotion.x_root;
			p->y = ev.xmotion.y_root;
			if(p->x == scr.rect.max.x - 1)
				p->x = scr.rect.max.x;
			if(p->y == scr.rect.max.y - 1)
				p->y = scr.rect.max.y;
			break;
		}
		return ev.type;
	}
}

bool
readmotion(Point *p) {
	uint button;

	for(;;)
		switch(readmouse(p, &button)) {
		case MotionNotify:
			return true;
		case ButtonRelease:
			return false;
		}
}

static void
mouse_resizecolframe(Frame *f, Align align) {
	Window *cwin, *hwin = nil;
	Divide *d;
	View *v;
	Area *a;
	Rectangle r;
	Point pt, min;
	int s;

	assert((align&(East|West)) != (East|West));
	assert((align&(North|South)) != (North|South));

	f->collapsed = false;

	v = selview;
	d = divs;
	SET(a);
	foreach_column(v, s, a) {
		if(a == f->area)
			break;
		d = d->next;
	}

	if(align & East)
		d = d->next;

	min.x = column_minwidth();
	min.y = /*frame_delta_h() +*/ labelh(def.font);
	/* Set the limits of where this box may be dragged. */
#define frob(pred, f, aprev, rmin, rmax, plus, minus, xy, use_screen) BLOCK( \
		if(pred) {                                           \
			r.rmin.xy = f->aprev->r.rmin.xy plus min.xy; \
			r.rmax.xy = f->r.rmax.xy minus min.xy;       \
		}else if(use_screen) {                               \
			r.rmin.xy = v->r[f->screen].rmin.xy plus 1;  \
			r.rmax.xy = a->r.rmax.xy minus min.xy;       \
		}else {                                              \
			r.rmin.xy = a->r.rmin.xy;                    \
			r.rmax.xy = r.rmin.xy plus 1;                \
		})

	r = f->r;
	if(align & North)
		frob(f->aprev, f, aprev, min, max, +, -, y, false);
	else if(align & South)
		frob(f->anext, f, anext, max, min, -, +, y, false);
	if(align & West)
		frob(a->prev,  a, prev,  min, max, +, -, x, true);
	else if(align & East)
		frob(a->next,  a, next,  max, min, -, +, x, true);
#undef frob

	cwin = constraintwin(r);

	r = f->r;
	if(align & North)
		r.min.y--;
	else if(align & South)
		r.min.y = r.max.y - 1;
	r.max.y = r.min.y + 2;

	if(align & (North|South))
		hwin = gethsep(r);

	if(!grabpointer(&scr.root, cwin, cursor[CurSizing], MouseMask))
		goto done;

	pt.x = (align & West ? f->r.min.x : f->r.max.x);
	pt.y = (align & North ? f->r.min.y : f->r.max.y);
	warppointer(pt);

	while(readmotion(&pt)) {
		if(align & West)
			r.min.x = pt.x;
		else if(align & East)
			r.max.x = pt.x;

		if(align & South)
			r.min.y = pt.y;
		else if(align & North)
			r.min.y = pt.y - 1;
		r.max.y = r.min.y+2;

		if(align & (East|West))
			div_set(d, pt.x);
		if(hwin)
			reshapewin(hwin, r);
	}

	r = f->r;
	if(align & West)
		r.min.x = pt.x;
	else if(align & East)
		r.max.x = pt.x;
	if(align & North)
		r.min.y = pt.y;
	else if(align & South)
		r.max.y = pt.y;
	column_resizeframe(f, r);

	/* XXX: Magic number... */
	if(align & West)
		pt.x = f->r.min.x + 4;
	else if(align & East)
		pt.x = f->r.max.x - 4;

	if(align & North)
		pt.y = f->r.min.y + 4;
	else if(align & South)
		pt.y = f->r.max.y - 4;
	warppointer(pt);

done:
	ungrabpointer();
	destroyconstraintwin(cwin);
	if (hwin)
		destroywindow(hwin);
}

void
mouse_resizecol(Divide *d) {
	Window *cwin;
	View *v;
	Rectangle r;
	Point pt;
	int minw, scrn;

	v = selview;

	scrn = (d->left ? d->left : d->right)->screen;

	pt = querypointer(&scr.root);

	minw = column_minwidth();
	r.min.x = d->left  ? d->left->r.min.x + minw  : v->r[scrn].min.x;
	r.max.x = d->right ? d->right->r.max.x - minw : v->r[scrn].max.x;
	r.min.y = pt.y;
	r.max.y = pt.y+1;

	cwin = constraintwin(r);

	if(!grabpointer(&scr.root, cwin, cursor[CurNone], MouseMask))
		goto done;

	while(readmotion(&pt))
		div_set(d, pt.x);

	if(d->left)
		d->left->r.max.x = pt.x;
	else
		v->pad[scrn].min.x = pt.x - v->r[scrn].min.x;

	if(d->right)
		d->right->r.min.x = pt.x;
	else
		v->pad[scrn].max.x = pt.x - v->r[scrn].max.x;

	view_arrange(v);

done:
	ungrabpointer();
	destroyconstraintwin(cwin);
}

void
mouse_resize(Client *c, Align align, bool grabmod) {
	Rectangle *rects;
	Rectangle frect, origin;
	Align grav;
	Cursor cur;
	Point d, pt, hr;
	float rx, ry, hrx, hry;
	uint nrect;
	Frame *f;

	f = c->sel;
	if(f->client->fullscreen >= 0) {
		ungrabpointer();
		return;
	}
	if(!f->area->floating) {
		if(align==Center)
			mouse_movegrabbox(c, grabmod);
		else
			mouse_resizecolframe(f, align);
		return;
	}

	cur = quad_cursor(align);
	if(align == Center)
		cur = cursor[CurSizing];

	if(!grabpointer(c->framewin, nil, cur, MouseMask))
		return;

	origin = f->r;
	frect = f->r;
	rects = view_rects(f->area->view, &nrect, c->frame);

	pt = querypointer(c->framewin);
	rx = (float)pt.x / Dx(frect);
	ry = (float)pt.y / Dy(frect);

	pt = querypointer(&scr.root);

	SET(hrx);
	SET(hry);

	if(align != Center) {
		hr = subpt(frect.max, frect.min);
		hr = divpt(hr, Pt(2, 2));
		d = hr;
		if(align&North) d.y -= hr.y;
		if(align&South) d.y += hr.y;
		if(align&East) d.x += hr.x;
		if(align&West) d.x -= hr.x;

		pt = addpt(d, f->r.min);
		warppointer(pt);
	}else {
		hrx = (double)(Dx(scr.rect)
			     + Dx(frect)
			     - 2 * labelh(def.font))
		    / Dx(scr.rect);
		hry = (double)(Dy(scr.rect)
			     + Dy(frect)
			     - 3 * labelh(def.font))
		    / Dy(scr.rect);

		pt.x = frect.max.x - labelh(def.font);
		pt.y = frect.max.y - labelh(def.font);
		d.x = pt.x / hrx;
		d.y = pt.y / hry;

		warppointer(d);
	}
	sync();
	event_flush(PointerMotionMask, false);

	while(readmotion(&d)) {
		if(align == Center) {
			d.x = (d.x * hrx) - pt.x;
			d.y = (d.y * hry) - pt.y;
		}else
			d = subpt(d, pt);
		pt = addpt(pt, d);

		rect_morph(&origin, d, &align);
		frect = constrain(origin, -1);

		grav = snap_rect(rects, nrect, &frect, &align, def.snap);

		frect = frame_hints(f, frect, grav);
		frect = constrain(frect, -1);

		client_resize(c, frect);
	}

	pt = addpt(c->framewin->r.min,
		   Pt(Dx(frect) * rx,
		      Dy(frect) * ry));
	if(pt.y > scr.rect.max.y)
		pt.y = scr.rect.max.y - 1;
	warppointer(pt);

	free(rects);
	ungrabpointer();
}

static int
pushstack_down(Frame *f, int y) {
	int ret;
	int dh, dy;

	if(f == nil)
		return 0;;
	ret = 0;
	dy = y - f->colr.min.y;
	if(dy < 0)
		return 0;
	if(!f->collapsed) {
		dh = Dy(f->colr) - labelh(def.font);
		if(dy <= dh) {
			f->colr.min.y += dy;
			return dy;
		}else {
			f->collapsed = true;
			f->colr.min.y += dh;
			ret = dh;
			dy -= dh;
		}
	}
	dy = pushstack_down(f->anext, f->colr.max.y + dy);
	f->colr.min.y += dy;
	f->colr.max.y += dy;
	return ret + dy;
}

static int
pushstack_up(Frame *f, int y) {
	int ret;
	int dh, dy;

	if(f == nil)
		return 0;
	ret = 0;
	dy = f->colr.max.y - y;
	if(dy < 0)
		return 0;
	if(!f->collapsed) {
		dh = Dy(f->colr) - labelh(def.font);
		if(dy <= dh) {
			f->colr.max.y -= dy;
			return dy;
		}else {
			f->collapsed = true;
			f->colr.max.y -= dh;
			ret = dh;
			dy -= dh;
		}
	}
	dy = pushstack_up(f->aprev, f->colr.min.y - dy);
	f->colr.min.y -= dy;
	f->colr.max.y -= dy;
	return ret + dy;
}

static void
mouse_tempvertresize(Area *a, Point p) {
	Frame *fa, *fb, *f;
	Window *cwin;
	Rectangle r;
	Point pt;
	int incmode, nabove, nbelow;

	if(a->mode != Coldefault)
		return;

	for(fa=a->frame; fa; fa=fa->anext)
		if(p.y < fa->r.max.y + labelh(def.font)/2)
			break;
	if(!(fa && fa->anext))
		return;
	fb = fa->anext;
	nabove=0;
	nbelow=0;
	for(f=fa; f; f=f->aprev)
		nabove++;
	for(f=fa->anext; f; f=f->anext)
		nbelow++;

	incmode = def.incmode;
	def.incmode = IIgnore;
	resizing = true;
	column_arrange(a, false);

	r.min.x = p.x;
	r.max.x = p.x + 1;
	r.min.y = a->r.min.y + labelh(def.font) * nabove;
	r.max.y = a->r.max.y - labelh(def.font) * nbelow;
	cwin = constraintwin(r);

	if(!grabpointer(&scr.root, cwin, cursor[CurDVArrow], MouseMask))
		goto done;

	for(f=a->frame; f; f=f->anext)
		f->colr_old = f->colr;

	while(readmotion(&pt)) {
		for(f=a->frame; f; f=f->anext) {
			f->collapsed = false;
			f->colr = f->colr_old;
		}
		if(pt.y > p.y)
			pushstack_down(fb, pt.y);
		else
			pushstack_up(fa, pt.y);
		fa->colr.max.y = pt.y;
		fb->colr.min.y = pt.y;
		column_frob(a);
	}

done:
	ungrabpointer();
	destroyconstraintwin(cwin);
	def.incmode = incmode;
	resizing = false;
	column_arrange(a, false);
}

void
mouse_checkresize(Frame *f, Point p, bool exec) {
	Rectangle r;
	Cursor cur;
	int q;

	cur = cursor[CurNormal];
	if(rect_haspoint_p(f->crect, p)) {
		client_setcursor(f->client, cur);
		return;
	}

	r = rectsubpt(f->r, f->r.min);
	q = quadrant(r, p);
	if(rect_haspoint_p(f->grabbox, p)) {
		cur = cursor[CurTCross];
		if(exec)
			mouse_movegrabbox(f->client, false);
	}
	else if(f->area->floating) {
		if(p.x <= 2
		|| p.y <= 2
		|| r.max.x - p.x <= 2
		|| r.max.y - p.y <= 2) {
			cur = quad_cursor(q);
			if(exec)
				mouse_resize(f->client, q, false);
		}
		else if(exec && rect_haspoint_p(f->titlebar, p))
			mouse_movegrabbox(f->client, true);
	}else {
		if(f->aprev && p.y <= 2
		|| f->anext && r.max.y - p.y <= 2) {
			cur = cursor[CurDVArrow];
			if(exec)
				mouse_tempvertresize(f->area, addpt(p, f->r.min));
		}
	}

	if(!exec)
		client_setcursor(f->client, cur);
}

static void
_grab(XWindow w, uint button, ulong mod) {
	XGrabButton(display, button, mod, w, false, ButtonMask,
			GrabModeSync, GrabModeSync, None, None);
}

/* Doesn't belong here */
void
grab_button(XWindow w, uint button, ulong mod) {
	_grab(w, button, mod);
	if((mod != AnyModifier) && numlock_mask) {
		_grab(w, button, mod | numlock_mask);
		_grab(w, button, mod | numlock_mask | LockMask);
	}
}

Added cmd/wmii/print.c.
























































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#include "dat.h"
#include <fmt.h>
#include "fns.h"

static char* fcnames[] = {
	"TVersion",
	"RVersion",
	"TAuth",
	"RAuth",
	"TAttach",
	"RAttach",
	"TError",
	"RError",
	"TFlush",
	"RFlush",
	"TWalk",
	"RWalk",
	"TOpen",
	"ROpen",
	"TCreate",
	"RCreate",
	"TRead",
	"RRead",
	"TWrite",
	"RWrite",
	"TClunk",
	"RClunk",
	"TRemove",
	"RRemove",
	"TStat",
	"RStat",
	"TWStat",
	"RWStat",
};

static int
qid(Fmt *f, IxpQid *q) {
	return fmtprint(f, "(%uhd,%uld,%ullx)", q->type, q->version, q->path);
}

int
Ffmt(Fmt *f) {
	IxpFcall *fcall;

	fcall = va_arg(f->args, IxpFcall*);
	fmtprint(f, "% 2d %s\t", fcall->hdr.tag, fcnames[fcall->hdr.type - TVersion]);
	switch(fcall->hdr.type) {
	case TVersion:
	case RVersion:
		fmtprint(f, " msize: %uld version: \"%s\"", (ulong)fcall->version.msize, fcall->version.version);
		break;
	case TAuth:
		fmtprint(f, " afid: %uld uname: \"%s\" aname: \"%s\"", (ulong)fcall->tauth.afid, fcall->tauth.uname, fcall->tauth.aname);
		break;
	case RAuth:
		fmtprint(f, " aqid: ");
		qid(f, &fcall->rauth.aqid);
		break;
	case RAttach:
		fmtprint(f, " qid: ");
		qid(f, &fcall->rattach.qid);
		break;
	case TAttach:
		fmtprint(f, " fid: %uld afid: %uld uname: \"%s\" aname: \"%s\"", (ulong)fcall->hdr.fid, (ulong)fcall->tattach.afid, fcall->tattach.uname, fcall->tattach.aname);
		break;
	case RError:
		fmtprint(f, " \"%s\"", fcall->error.ename);
		break;
	case TFlush:
		fmtprint(f, " oldtag: %uld", (ulong)fcall->tflush.oldtag);
		break;
	case TWalk:
		fmtprint(f, " newfid: %uld wname: {", (ulong)fcall->twalk.newfid);
		for(int i=0; i<fcall->twalk.nwname; i++) {
			if(i > 0) fmtprint(f, ", ");
			fmtprint(f, "\"%s\"", fcall->twalk.wname[i]);
		}
		fmtprint(f, "}");
		break;
	case RWalk:
		fmtprint(f, " wqid: {");
		for(int i=0; i<fcall->rwalk.nwqid; i++) {
			if(i > 0) fmtprint(f, ", ");
			qid(f, &fcall->rwalk.wqid[i]);
		}
		fmtprint(f, "}");
		break;
	case TOpen:
		fmtprint(f, " fid: %uld mode: %ulo", (ulong)fcall->hdr.fid, (ulong)fcall->topen.mode);
		break;
	case ROpen:
	case RCreate:
		fmtprint(f, " qid: ");
		qid(f, &fcall->ropen.qid);
		fmtprint(f, " %uld", (ulong)fcall->ropen.iounit);
		break;
	case TCreate:
		fmtprint(f, " fid: %uld name:  \"%s\" perm: %ulo mode: %ulo", (ulong)fcall->hdr.fid, fcall->tcreate.name, (ulong)fcall->tcreate.perm, (ulong)fcall->tcreate.mode);
		break;
	case TRead:
		fmtprint(f, " fid: %uld offset: %ulld count: %uld", (ulong)fcall->hdr.fid, fcall->tread.offset, (ulong)fcall->tread.count);
		break;
	case RRead:
		fmtprint(f, " data: {data: %uld}", fcall->rread.count);
		break;
	case TWrite:
		fmtprint(f, " fid: %uld offset: %ulld data: {data: %uld}", (ulong)fcall->hdr.fid, fcall->twrite.offset, fcall->twrite.count);
		break;
	case RWrite:
		fmtprint(f, " count: %uld", (ulong)fcall->rwrite.count);
		break;
	case TClunk:
	case TRemove:
	case TStat:
		fmtprint(f, " fid: %uld", (ulong)fcall->hdr.fid);
		break;
	case RStat:
		fmtprint(f, " stat: {data: %uld}", fcall->rstat.nstat);
		break;
	}

	return 0;
}

Added cmd/wmii/root.c.












































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
/* Copyright ©2008-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "dat.h"
#include "fns.h"

static Handlers handlers;

void
root_init(void) {
	WinAttr wa;

	wa.cursor = cursor[CurNormal];
	wa.event_mask = EnterWindowMask
		      | FocusChangeMask
		      | LeaveWindowMask
		      | PointerMotionMask
		      | SubstructureNotifyMask
		      | SubstructureRedirectMask;
	setwinattr(&scr.root, &wa, CWCursor
			         | CWEventMask);
	sethandler(&scr.root, &handlers);
}

static bool
enter_event(Window *w, void *aux, XCrossingEvent *e) {
	disp.sel = true;
	frame_draw_all();
	return false;
}

static bool
leave_event(Window *w, void *aux, XCrossingEvent *e) {
	if(!e->same_screen) {
		disp.sel = false;
		frame_draw_all();
	}
	return false;
}

static bool
focusin_event(Window *w, void *aux, XFocusChangeEvent *e) {
	if(e->mode == NotifyGrab)
		disp.hasgrab = &c_root;
	return false;
}

static bool
mapreq_event(Window *w, void *aux, XMapRequestEvent *e) {
	XWindowAttributes wa;

	if(!XGetWindowAttributes(display, e->window, &wa) || wa.override_redirect)
		return false;
	if(!win2client(e->window))
		client_create(e->window, &wa);
	return false;
}

static bool
motion_event(Window *w, void *aux, XMotionEvent *e) {
	Rectangle r, r2;

	r = rectsetorigin(Rect(0, 0, 1, 1), Pt(e->x_root, e->y_root));
	r2 = constrain(r, 0);
	if(!eqrect(r, r2))
		warppointer(r2.min);
	return false;
}

static bool
kdown_event(Window *w, void *aux, XKeyEvent *e) {

	e->state &= valid_mask;
	kpress(w->xid, e->state, (KeyCode)e->keycode);
	return false;
}

static Handlers handlers = {
	.enter = enter_event,
	.focusin = focusin_event,
	.kdown = kdown_event,
	.leave = leave_event,
	.mapreq = mapreq_event,
	.motion = motion_event,
};

Added cmd/wmii/rule.c.
























































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
/* Copyright ©2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */

#include "dat.h"
#include "fns.h"

void
update_rules(Rule **rule, char *data) {
#define putc(m, c) BLOCK(if((m)->pos < (m)->end) *(m)->pos++ = c;)
#define getc(m) ((m)->pos < (m)->end ? *(m)->pos++ : 0)
#define ungetc(m) BLOCK(if((m)->pos > (m)->data) --(m)->pos)

	IxpMsg buf, valuebuf, rebuf;
	Reprog *re;
	Rule *r;
	Ruleval **rvp;
	Ruleval *rv;
	char *w;
	char regexp[256];
	char c;
	int len;

	while((r = *rule)) {
		*rule = r->next;
		while((rv = r->values)) {
			r->values = rv->next;
			free(rv);
		}
		free(r->regex);
		free(r->value);
		free(r);
	}

	if(!data || !data[0])
		return;

	buf = ixp_message(data, strlen(data), MsgUnpack);

begin:
	msg_eatrunes(&buf, isspacerune, true);
	if(getc(&buf) == '/')
		goto regexp;
	/* Regexp not at begining of the line. Rest of the line is junk. */
	while((c = getc(&buf)))
		if(c == '\n')
			goto begin;
	goto done;

regexp:
	rebuf = ixp_message(regexp, sizeof regexp - 1, MsgPack);
	while((c = getc(&buf)))
		if(c == '/')
			goto value;
		else if(c != '\\')
			putc(&rebuf, c);
		else if(buf.pos[1] == '/' || buf.pos[1] == '\\' && buf.pos[2] == '/')
			putc(&rebuf, getc(&buf));
		else {
			putc(&rebuf, c);
			putc(&rebuf, getc(&buf));
		}
	goto done;

value:
	valuebuf = ixp_message(buffer, sizeof buffer - 1, MsgPack);
	while((c = getc(&buf))) {
		if(c == '\n') {
			putc(&valuebuf, ' ');
			msg_eatrunes(&buf, isspacerune, true);
			if((c = getc(&buf)) == '/') {
				ungetc(&buf);
				break;
			}
		}
		putc(&valuebuf, c);
	}

	putc(&rebuf, '\0');
	re = regcomp(regexp);
	if(!re)
		goto begin;
	r = emallocz(sizeof *r);
	*rule = r;
	rule = &r->next;
	r->regex = re;

	valuebuf.end = valuebuf.pos;
	valuebuf.pos = valuebuf.data;
	rvp = &r->values;
	while((w = msg_getword(&valuebuf, 0))) {
		free(r->value);
		r->value = estrdup(w);
		if(strchr(w, '=')) {
			len = strlen(w) + 1;
			*rvp = rv = emallocz(sizeof *rv + len);
			rvp = &rv->next;

			memcpy(&rv[1], w, len);
			tokenize(&rv->key, 2, (char*)&rv[1], '=');
		}
	}
	goto begin;

done:
	return;
}

Added cmd/wmii/screen.c.
























































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
/* Copyright ©2006-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "dat.h"
#include <math.h>
#include "fns.h"

#ifdef notdef
void
mapscreens(void) {
	WMScreen *s, *ss;
	Rectangle r;
	int i, j;

#define frob(left, min, max, x, y) \
	if(Dy(r) > 0) /* If they intersect at some point on this axis */        \
	if(ss->r.min.x < s->r.min.x) {                                          \
		if((!s->left)                                                   \
		|| (abs(Dy(r)) < abs(s->left.max.x - s->min.x))) \
			s->left = ss;                                           \
	}

	/* Variable hell? Certainly. */
	for(i=0; i < nscreens; i++) {
		s = screens[i];
		for(j=0; j < nscreens; j++) {
			if(i == j)
				continue;
			ss = screens[j];
			r = rect_intersection(ss->r, s->r);
			frob(left,   min, max, x, y);
			frob(right,  max, min, x, y);
			frob(atop,   min, max, y, x);
			frob(below,  max, min, y, x);
		}
	}
#undef frob
}

int	findscreen(Rectangle, int);
int
findscreen(Rectangle rect, int direction) {
	Rectangle r;
	WMScreen *ss, *s;
	int best, i, j;

	best = -1;
#define frob(min, max, x, y)
	if(Dy(r) > 0) /* If they intersect at some point on this axis */
	if(ss->r.min.x < rect.min.x) {
		if(best == -1
		|| (abs(ss->r.max.x - rect.min.x) < abs(screens[best]->r.max.x - rect.min.x)))
			best = s->idx;
	}

	/* Variable hell? Certainly. */
	for(i=0; i < nscreens; i++) {
		ss = screens[j];
		r = rect_intersection(ss->r, rect);
		switch(direction) {
		default:
			return -1;
		case West:
			frob(min, max, x, y);
			break;
		case East:
			frob(max, min, x, y);
			break;
		case North:
			frob(min, max, y, x);
			break;
		case South:
			frob(max, min, y, x);
			break;
		}
	}
#undef frob
}
#endif

static Rectangle
leastthing(Rectangle rect, int direction, Vector_ptr *vec, Rectangle (*key)(void*)) {
	Rectangle r;
	int i, best, d;

	SET(d);
	SET(best);
	for(i=0; i < vec->n; i++) {
		r = key(vec->ary[i]);
		switch(direction) {
		case South: d =  r.min.y; break;
		case North: d = -r.max.y; break;
		case East:  d =  r.min.x; break;
		case West:  d = -r.max.x; break;
		}
		if(i == 0 || d < best)
			best = d;
	}
	switch(direction) {
	case South: rect.min.y = rect.max.y =  best; break;
	case North: rect.min.y = rect.max.y = -best; break;
	case East:  rect.min.x = rect.max.x =  best; break;
	case West:  rect.min.x = rect.max.x = -best; break;
	}
	return rect;
}

void*
findthing(Rectangle rect, int direction, Vector_ptr *vec, Rectangle (*key)(void*), bool wrap) {
	Rectangle isect;
	Rectangle r, bestisect = {0,}, bestr = {0,};
	void *best, *p;
	int i, n;

	best = nil;

	/* For the record, I really hate these macros. */
#define frob(min, max, LT, x, y) \
	if(D##y(isect) > 0) /* If they intersect at some point on this axis */  \
	if(r.min.x LT rect.min.x) {                                             \
		n = abs(r.max.x - rect.min.x) - abs(bestr.max.x - rect.min.x);  \
		if(best == nil                                                  \
		|| n == 0 && D##y(isect) > D##y(bestisect)                      \
		|| n < 0                                                        \
		) {                                                             \
			best = p;                                               \
			bestr = r;                                              \
			bestisect = isect;                                      \
		}                                                               \
	}

	/* Variable hell? Certainly. */
	for(i=0; i < vec->n; i++) {
		p = vec->ary[i];
		r = key(p);
		isect = rect_intersection(rect, r);
		switch(direction) {
		default:
			die("not reached");
			/* Not reached */
		case West:
			frob(min, max, <, x, y);
			break;
		case East:
			frob(max, min, >, x, y);
			break;
		case North:
			frob(min, max, <, y, x);
			break;
		case South:
			frob(max, min, >, y, x);
			break;
		}
	}
#undef frob
	if(!best && wrap) {
		r = leastthing(rect, direction, vec, key);
		return findthing(r, direction, vec, key, false);
	}
	return best;
}

static int
area(Rectangle r) {
	return Dx(r) * Dy(r) *
	       (Dx(r) < 0 && Dy(r) < 0 ? -1 : 1);
}

int
ownerscreen(Rectangle r) {
	Rectangle isect;
	int s, a, best, besta;

	SET(besta);
	best = -1;
	for(s=0; s < nscreens; s++) {
		if(!screens[s]->showing)
			continue;
		isect = rect_intersection(r, screens[s]->r);
		a = area(isect);
		if(best < 0 || a > besta) {
			besta = a;
			best = s;
		}
	}
	return best;
}

Added cmd/wmii/stack.c.
























































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
/* Copyright ©2009-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "dat.h"
#include "fns.h"

void
stack_scale(Frame *first, int height) {
	Frame *f;
	uint dy;
	int surplus;

	/*
	 * Will need something like this.
	a = first->area;
	column_fit(a, &ncol, &nuncol);
	*/

	dy = 0;
	for(f=first; f && !f->collapsed; f=f->anext)
		dy += Dy(f->colr);

	/* Distribute the surplus.
	 */
	surplus = height - dy;
	for(f=first; f && !f->collapsed; f=f->anext)
		f->colr.max.y += ((float)Dy(f->r) / dy) * surplus;
}

void
stack_info(Frame *f, Frame **firstp, Frame **lastp, int *dyp, int *nframep) {
	Frame *ft, *first, *last;
	int dy, nframe;

	nframe = 0;
	dy = 0;
	first = f;
	last = f;

	for(ft=f; ft && ft->collapsed; ft=ft->anext)
		;
	if(ft && ft != f) {
		f = ft;
		dy += Dy(f->colr);
	}
	for(ft=f; ft && !ft->collapsed; ft=ft->aprev) {
		first = ft;
		nframe++;
		dy += Dy(ft->colr);
	}
	for(ft=f->anext; ft && !ft->collapsed; ft=ft->anext) {
		if(first == nil)
			first = ft;
		last = ft;
		nframe++;
		dy += Dy(ft->colr);
	}
	if(nframep) *nframep = nframe;
	if(firstp) *firstp = first;
	if(lastp) *lastp = last;
	if(dyp) *dyp = dy;
}

int
stack_count(Frame *f, int *mp) {
	Frame *fp;
	int n, m;

	n = 0;
	for(fp=f->aprev; fp && fp->collapsed; fp=fp->aprev)
		n++;
	m = ++n;
	for(fp=f->anext; fp && fp->collapsed; fp=fp->anext)
		n++;
	if(mp) *mp = m;
	return n;
}

Frame*
stack_find(Area *a, Frame *f, int dir, bool stack) {
	Frame *fp;

#define predicate(f) !((f)->collapsed && stack || (f)->client->nofocus)
	switch (dir) {
	default:
		die("not reached");
	case North:
		if(f)
			for(f=f->aprev; f && !predicate(f); f=f->aprev)
				;
		else {
			f = nil;
			for(fp=a->frame; fp; fp=fp->anext)
				if(predicate(fp))
					f = fp;
		}
		break;
	case South:
		if(f)
			for(f=f->anext; f && !predicate(f); f=f->anext)
				;
		else
			for(f=a->frame; f && !predicate(f); f=f->anext)
				;
		break;
	}
#undef predicate
	return f;
}

bool
find(Area **ap, Frame **fp, int dir, bool wrap, bool stack) {
	Rectangle r;
	Frame *f;
	Area *a;

	f = *fp;
	a = *ap;
	r = f ? f->r : a->r;

	if(dir == North || dir == South) {
		*fp = stack_find(a, f, dir, stack);
		if(*fp)
			return true;
		if(!a->floating)
			*ap = area_find(a->view, r, dir, wrap);
		if(!*ap)
			return false;
		*fp = stack_find(*ap, *fp, dir, stack);
		return true;
	}
	if(dir != East && dir != West)
		die("not reached");
	*ap = area_find(a->view, r, dir, wrap);
	if(!*ap)
		return false;
	*fp = ap[0]->sel;
	return true;
}

Added cmd/wmii/utf.c.
























































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
/* Public Domain --Kris Maglione */
#include "dat.h"
#include <errno.h>
#include <iconv.h>
#include <langinfo.h>
#include <string.h>
#include "fns.h"

char*
toutf8n(const char *str, size_t nstr) {
	static iconv_t cd;
	static bool haveiconv;
	char *buf, *pos;
	size_t nbuf, bsize;

	if(cd == nil) {
		cd = iconv_open("UTF-8", nl_langinfo(CODESET));
		if((long)cd == -1)
			warning("Can't convert from local character encoding to UTF-8");
		else
			haveiconv = true;
	}
	if(!haveiconv) {
		buf = emalloc(nstr+1);
		memcpy(buf, str, nstr);
		buf[nstr+1] = '\0';
		return buf;
	}

	iconv(cd, nil, nil, nil, nil);

	bsize = (nstr+1) << 1;
	buf = emalloc(bsize);
	pos = buf;
	nbuf = bsize-1;
	/* The (void*) cast is because, while the BSDs declare:
	 * size_t iconv(iconv_t, const char**, size_t*, char**, size_t*),
	 * GNU/Linux and POSIX declare:
	 * size_t iconv(iconv_t, char**, size_t*, char**, size_t*).
	 * This just happens to be safer than declaring our own
	 * prototype.
	 */
	while(iconv(cd, (void*)&str, &nstr, &pos, &nbuf) == -1)
		if(errno == E2BIG) {
			bsize <<= 1;
			nbuf = pos - buf;
			buf = erealloc(buf, bsize);
			pos = buf + nbuf;
			nbuf = bsize - nbuf - 1;
		}else
			break;
	*pos++ = '\0';
	return erealloc(buf, pos-buf);
}

char*
toutf8(const char *str) {
	return toutf8n(str, strlen(str));
}

Added cmd/wmii/view.c.








































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
/* Copyright ©2004-2006 Anselm R. Garbe <garbeam at gmail dot com>
 * Copyright ©2006-2014 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "dat.h"
#include "fns.h"

static bool
empty_p(View *v) {
	Frame *f;
	Area *a;
	char **p;
	int cmp;
	int s;

	foreach_frame(v, s, a, f) {
		cmp = 1;
		for(p=f->client->retags; *p; p++) {
			cmp = strcmp(*p, v->name);
			if(cmp >= 0)
				break;
		}
		if(cmp)
			return false;
	}
	return true;
}

static void
_view_select(View *v) {
	if(selview != v) {
		if(selview)
			event("UnfocusTag %s\n",selview->name);
		selview = v;
		event("FocusTag %s\n", v->name);
		event("AreaFocus %a\n", v->sel);
		ewmh_updateview();
	}
}

Client*
view_selclient(View *v) {
	if(v->sel && v->sel->sel)
		return v->sel->sel->client;
	return nil;
}

bool
view_fullscreen_p(View *v, int scrn) {
	Frame *f;

	for(f=v->floating->frame; f; f=f->anext)
		if(f->client->fullscreen == scrn)
			return true;
	return false;
}

View*
view_create(const char *name) {
	static ushort id = 1;
	View **vp;
	Client *c;
	View *v;
	int i;

	for(vp=&view; *vp; vp=&(*vp)->next) {
		i = strcmp((*vp)->name, name);
		if(i == 0)
			return *vp;
		if(i > 0)
			break;
	}

	v = emallocz(sizeof *v);
	v->id = id++;
	v->r = emallocz(nscreens * sizeof *v->r);
	v->pad = emallocz(nscreens * sizeof *v->pad);

	utflcpy(v->name, name, sizeof v->name);

	event("CreateTag %s\n", v->name);
	area_create(v, nil, screen->idx, 0);

	v->areas = emallocz(nscreens * sizeof *v->areas);

	for(i=0; i < nscreens; i++)
		view_init(v, i);

	area_focus(v->firstarea);

	v->next = *vp;
	*vp = v;

	/* FIXME: Belongs elsewhere */
	/* FIXME: Can do better. */
	for(c=client; c; c=c->next)
		if(c != kludge)
			client_applytags(c, c->tags);

	view_arrange(v);
	if(!selview)
		_view_select(v);
	ewmh_updateviews();
	return v;
}

void
view_init(View *v, int iscreen) {
	v->r[iscreen] = screens[iscreen]->r;
	v->pad[iscreen] = ZR;
	v->areas[iscreen] = nil;
	column_new(v, nil, iscreen, 0);
}

void
view_update_screens(View *v) {
	Area *a;
	Frame *f;
	int s;

	if (v->sel->screen > (long)nscreens)
		v->sel = v->floating->next;

	v->selscreen = min(v->selscreen, nscreens);

	if (nscreens_new < nscreens) {
		foreach_frame(v, s, a, f) {
			f->oldscreen = min(f->oldscreen, nscreens_new);
			if (a->screen >= nscreens_new) {
				a->permanent = true;
				area_detach(f);
				view_attach(v, f);
			}
		}

		foreach_area(v, s, a) {
			if (a->screen >= nscreens_new)
				area_destroy(a);
		}
	}

	v->areas = erealloc(v->areas, nscreens_new * sizeof *v->areas);
	v->r = erealloc(v->r, nscreens_new * sizeof *v->r);
	v->pad = erealloc(v->pad, nscreens_new * sizeof *v->pad);
}

void
view_destroy(View *v) {
	View **vp;
	Frame *f;
	View *tv;
	Area *a;
	int s;

	if(v->dead)
		return;
	v->dead = true;

	for(vp=&view; *vp; vp=&(*vp)->next)
		if(*vp == v) break;
	*vp = v->next;
	assert(v != v->next);

	/* Detach frames held here by regex tags. */
	/* FIXME: Can do better. */
	foreach_frame(v, s, a, f)
		client_applytags(f->client, f->client->tags);

	foreach_area(v, s, a)
		area_destroy(a);

	event("DestroyTag %s\n", v->name);

	if(v == selview) {
		for(tv=view; tv; tv=tv->next)
			if(tv->next == *vp) break;
		if(tv == nil)
			tv = view;
		if(tv)
			view_focus(screen, tv);
	}
	free(v->areas);
	free(v->r);
	free(v->pad);
	free(v);
	ewmh_updateviews();
}

Area*
view_findarea(View *v, int screen, int idx, bool create) {
	Area *a;

	assert(screen >= 0 && screen < nscreens);

	for(a=v->areas[screen]; a && --idx > 0; a=a->next)
		if(create && a->next == nil)
			return area_create(v, a, screen, 0);
	return a;
}

static void
frames_update_sel(View *v) {
	Frame *f;
	Area *a;
	int s;

	foreach_frame(v, s, a, f)
		f->client->sel = f;
}

/* Don't let increment hints take up more than half
 * of the screen, in either direction.
 */
static Rectangle
fix_rect(Rectangle old, Rectangle new) {
	double r;

	new = rect_intersection(new, old);

	r = (Dy(old) - Dy(new)) / Dy(old);
	if(r > .5) {
		r -= .5;
		new.min.y -= r * (new.min.y - old.min.y);
		new.max.y += r * (old.max.y - new.max.y);
	}
	r = (Dx(old) - Dx(new)) / Dx(old);
	if(r > .5) {
		r -= .5;
		new.min.x -= r * (new.min.x - old.min.x);
		new.max.x += r * (old.max.x - new.max.x);
	}
	return new;
}

void
view_update_rect(View *v) {
	static Vector_rect vec;
	static Vector_rect *vp;
	Rectangle r, sr, rr, brect, scrnr;
	WMScreen *scrn;
	Strut *strut;
	Frame *f;
	int left, right, top, bottom;
	int s, i;

	/* XXX:
	if(v != selview)
		return false;
	*/

	top = 0;
	left = 0;
	right = 0;
	bottom = 0;
	vec.n = 0;
	for(f=v->floating->frame; f; f=f->anext) {
		strut = f->client->strut;
		if(!strut)
			continue;
		/* Can do better in the future. */
		top = max(top, strut->top.max.y);
		left = max(left, strut->left.max.x);
		right = min(right, strut->right.min.x);
		bottom = min(bottom, strut->bottom.min.y);
		vector_rpush(&vec, strut->top);
		vector_rpush(&vec, strut->left);
		vector_rpush(&vec, rectaddpt(strut->right, Pt(scr.rect.max.x, 0)));
		vector_rpush(&vec, rectaddpt(strut->bottom, Pt(0, scr.rect.max.y)));
	}
	vp = unique_rects(&vec, scr.rect);
	scrnr = scr.rect;
	scrnr.min.y += top;
	scrnr.min.x += left;
	scrnr.max.x += right;
	scrnr.max.y += bottom;

	/* FIXME: Multihead. */
	v->floating->r = scr.rect;

	for(s=0; s < nscreens; s++) {
		scrn = screens[s];
		r = fix_rect(scrn->r, scrnr);

		/* Ugly. Very, very ugly. */
		/*
		 * Try to find some rectangle near the edge of the
		 * screen where the bar will fit. This way, for
		 * instance, a system tray can be placed there
		 * without taking up too much extra screen real
		 * estate.
		 */
		rr = r;
		brect = scrn->brect;
		for(i=0; i < vp->n; i++) {
			sr = rect_intersection(vp->ary[i], scrn->r);
			if(Dx(sr) < Dx(r)/2 || Dy(sr) < Dy(brect))
				continue;
			if(scrn->barpos == BTop && sr.min.y < rr.min.y
			|| scrn->barpos != BTop && sr.max.y > rr.max.y)
				rr = sr;
		}

		if(scrn->barpos == BTop) {
			bar_sety(scrn, rr.min.y);
			r.min.y = max(r.min.y, scrn->brect.max.y);
		}else {
			bar_sety(scrn, rr.max.y - Dy(brect));
			r.max.y = min(r.max.y, scrn->brect.min.y);
		}
		bar_setbounds(scrn, rr.min.x, rr.max.x);
		v->r[s] = r;
	}
}

void
view_update(View *v) {
	Client *c;
	Frame *f;
	Area *a;
	int s;

	if(v == selview && !starting) {
		frames_update_sel(v);

		foreach_frame(v, s, a, f)
			if(f->client->fullscreen >= 0) {
				f->collapsed = false;
				if(!f->area->floating) {
					f->oldarea = area_idx(f->area);
					f->oldscreen = f->area->screen;
					area_moveto(v->floating, f);
					area_setsel(v->floating, f);
				}else if(f->oldarea == -1)
					f->oldarea = 0;
			}

		view_arrange(v);

		for(c=client; c; c=c->next) {
			f = c->sel;
			if((f && f->view == v)
			&& (f->area == v->sel || !(f->area && f->area->max && f->area->floating))) {
				if(f->area)
					client_resize(c, f->r);
			}else {
				client_unmapframe(c);
				client_unmap(c, IconicState);
			}
			ewmh_updatestate(c);
			ewmh_updateclient(c);
		}

		view_restack(v);
		if(!v->sel->floating && view_fullscreen_p(v, v->sel->screen))
			area_focus(v->floating);
		else
			area_focus(v->sel);
		frame_draw_all();
	}
	view_update_urgency(v, nil);
}

void
view_update_urgency(View *v, char *from) {
	Area *a;
	Frame *f;
	int s, urgent;

	urgent = 0;
	foreach_frame(v, s, a, f)
		if (f->client->urgent) {
			urgent++;
			break;
		}

	if (urgent != v->urgent)
		event("%sUrgentTag %s %s\n",
		      urgent ? "" : "Not",
		      from ? from : "Unknown",
		      v->name);

	v->urgent = urgent;
}

void
view_focus(WMScreen *s, View *v) {

	USED(s);

	_view_select(v);
	view_update(v);
}

void
view_select(const char *arg) {
	char buf[256];

	utflcpy(buf, arg, sizeof buf);
	trim(buf, " \t+/");

	if(buf[0] == '\0')
		return;
	if(!strcmp(buf, ".") || !strcmp(buf, ".."))
		return;

	_view_select(view_create(buf));
	view_update_all(); /* performs view_focus */
}

void
view_attach(View *v, Frame *f) {
	Client *c;
	Frame *ff;
	Area *a, *oldsel;

	c = f->client;

	oldsel = v->oldsel;
	a = v->sel;

	if(c->floating == Never)
		a = view_findarea(v, v->selscreen, v->selcol, false);
	else if(client_floats_p(c)) {
		if(v->sel != v->floating && c->fullscreen < 0)
			oldsel = v->sel;
		a = v->floating;
	}
	else if(c->sel && c->sel->screen >= 0 && c->sel->screen < nscreens_new &&
		c->sel->screen != v->sel->screen)
		a = view_findarea(v, c->sel->screen, 0, false);
	else if((ff = client_groupframe(c, v))) {
		if (ff->client != c && ff->area->screen < nscreens_new)
			a = ff->area;
		if(v->oldsel && ff->client == view_selclient(v))
			a = v->oldsel;
	}
	else if(v->sel->floating) {
		if(v->oldsel)
			a = v->oldsel;
		/* Don't float a frame when starting or when its
		 * last focused frame didn't float. Important when
		 * tagging with +foo.
		 */
		else if(starting
		     || c->sel && c->sel->area && !c->sel->area->floating)
			a = v->firstarea;
	}
	if(!a->floating && c->floating != Never && view_fullscreen_p(v, a->screen))
		a = v->floating;

	event("ViewAttach %s %#C\n", v->name, c);
	area_attach(a, f);
	/* TODO: Decide whether to focus this frame */
	bool newgroup = !c->group
		     || c->group->ref == 1
		     || view_selclient(v)
		        && view_selclient(v)->group == c->group
		     || group_leader(c->group)
		        && !client_viewframe(group_leader(c->group),
					     c->sel->view);
	USED(newgroup);

	if(!(c->w.ewmh.type & (TypeSplash|TypeDock))) {
		if(!(c->tagre.regex && regexec(c->tagre.regc, v->name, nil, 0)))
			frame_focus(f);
		else if(c->group && f->area->sel->client->group == c->group)
			/* XXX: Stack. */
			area_setsel(f->area, f);
	}

	if(oldsel)
		v->oldsel = oldsel;

	if(c->sel == nil)
		c->sel = f;
	view_update(v);
}

void
view_detach(Frame *f) {
	Client *c;
	View *v;

	v = f->view;
	c = f->client;

	area_detach(f);
	if(c->sel == f)
		c->sel = f->cnext;

	event("ViewDetach %s %#C\n", v->name, c);
	view_update(v);
	if(v != selview && empty_p(v))
		view_destroy(v);
}

char**
view_names(void) {
	Vector_ptr vec;
	View *v;

	vector_pinit(&vec);
	for(v=view; v; v=v->next)
		vector_ppush(&vec, v->name);
	vector_ppush(&vec, nil);
	return erealloc(vec.ary, vec.n * sizeof *vec.ary);
}

void
view_restack(View *v) {
	static Vector_long wins;
	Divide *d;
	Frame *f;
	Area *a;
	int s;

	if(v != selview)
		return;

	wins.n = 0;

	for(f=v->floating->stack; f; f=f->snext)
		vector_lpush(&wins, f->client->framewin->xid);

	for(int s=0; s < nscreens; s++)
		vector_lpush(&wins, screens[s]->barwin->xid);

	for(d = divs; d && d->w->mapped; d = d->next)
		vector_lpush(&wins, d->w->xid);

	foreach_column(v, s, a)
		if(a->frame) {
			vector_lpush(&wins, a->sel->client->framewin->xid);
			for(f=a->frame; f; f=f->anext)
				if(f != a->sel)
					vector_lpush(&wins, f->client->framewin->xid);
		}

	ewmh_updatestacking();
	if(wins.n)
		XRestackWindows(display, (ulong*)wins.ary, wins.n);
}

void
view_scale(View *v, int scrn, int width) {
	uint xoff, numcol;
	uint minwidth;
	Area *a;
	float scale;
	int dx, minx;

	minwidth = column_minwidth();
	minx = v->r[scrn].min.x + v->pad[scrn].min.x;

	if(!v->areas[scrn])
		return;

	numcol = 0;
	dx = 0;
	for(a=v->areas[scrn]; a; a=a->next) {
		numcol++;
		dx += Dx(a->r);
	}

	scale = (float)width / dx;
	xoff = minx;
	for(a=v->areas[scrn]; a; a=a->next) {
		a->r.max.x = xoff + Dx(a->r) * scale;
		a->r.min.x = xoff;
		if(!a->next)
			a->r.max.x = v->r[scrn].min.x + width;
		xoff = a->r.max.x;
	}

	if(numcol * minwidth > width)
		return;

	xoff = minx;
	for(a=v->areas[scrn]; a; a=a->next) {
		a->r.min.x = xoff;

		if(Dx(a->r) < minwidth)
			a->r.max.x = xoff + minwidth;
		if(!a->next)
			a->r.max.x = minx + width;
		xoff = a->r.max.x;
	}
}

/* XXX: Multihead. */
void
view_arrange(View *v) {
	Area *a;
	int s;

	if(!v->firstarea)
		return;

	view_update_rect(v);
	for(s=0; s < nscreens; s++)
		view_scale(v, s, Dx(v->r[s]) + Dx(v->pad[s]));
	foreach_area(v, s, a) {
		if(a->floating)
			continue;
		/* This is wrong... */
		a->r.min.y = v->r[s].min.y;
		a->r.max.y = v->r[s].max.y;
		column_arrange(a, false);
	}
	if(v == selview)
		div_update_all();
}

Rectangle*
view_rects(View *v, uint *num, Frame *ignore) {
	Vector_rect result;
	Frame *f;
	int i;

	vector_rinit(&result);

	for(f=v->floating->frame; f; f=f->anext)
		if(f != ignore)
			vector_rpush(&result, f->r);
	for(i=0; i < nscreens; i++) {
		vector_rpush(&result, v->r[i]);
		vector_rpush(&result, screens[i]->r);
	}

	*num = result.n;
	return result.ary;
}

void
view_update_all(void) {
	View *n, *v, *old;

	old = selview;
	for(v=view; v; v=v->next)
		frames_update_sel(v);

	for(v=view; v; v=n) {
		n=v->next;
		if(v != old && empty_p(v))
			view_destroy(v);
	}

	view_update(selview);
}

uint
view_newcolwidth(View *v, int scrn, int num) {
	Rule *r;
	char *toks[16];
	char buf[sizeof r->value];
	ulong n;

	/* XXX: Multihead. */
	for(r=def.colrules.rule; r; r=r->next)
		if(regexec(r->regex, v->name, nil, 0)) {
			utflcpy(buf, r->value, sizeof buf);
			n = tokenize(toks, 16, buf, '+');

			if(num < n)
				if(getulong(toks[num], &n))
					return Dx(v->r[scrn]) * (n / 100.0);
				else if(!strcmp("px", strend(toks[num], 2))) {
					toks[num][strlen(toks[num]) - 2] = '\0';
					if(getulong(toks[num], &n))
						return n;
				}
			break;
		}
	return 0;
}

char*
view_index(View *v) {
	Rectangle *r;
	Frame *f;
	Area *a;
	int s;

	bufclear();
	foreach_area(v, s, a) {
		if(a->floating)
			bufprint("# %a %d %d\n", a, Dx(a->r), Dy(a->r));
		else
			bufprint("# %a %d %d\n", a, a->r.min.x, Dx(a->r));

		for(f=a->frame; f; f=f->anext) {
			r = &f->r;
			if(a->floating)
				bufprint("%a %#C %d %d %d %d %s\n",
						a, f->client,
						r->min.x, r->min.y,
						Dx(*r), Dy(*r),
						f->client->props);
			else
				bufprint("%a %#C %d %d %s\n",
						a, f->client,
						r->min.y, Dy(*r),
						f->client->props);
		}
	}
	return buffer;
}

Added cmd/wmii/xdnd.c.
















































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
/* Copyright ©2008-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "dat.h"
#include "fns.h"

static Handlers	handlers;

void
xdnd_initwindow(Window *w) {
	long l;

	l = 3; /* They are insane. Why is this an ATOM?! */
	changeprop_long(w, "XdndAware", "ATOM", &l, 1);
	pushhandler(w, &handlers, nil);
}

typedef struct Dnd Dnd;
struct Dnd {
	XWindow		source;
	Rectangle	r;
};

static bool
clientmessage_event(Window *w, void *aux, XClientMessageEvent *e) {
	Dnd *dnd;
	long *l;
	Rectangle r;
	Point p;
	long pos, siz;
	ulong msg;

	dnd = nil;
	msg = e->message_type;
	l = e->data.l;
	Dprint(DDnd, "ClientMessage: %A\n", msg);

	if(msg == xatom("XdndEnter")) {
		if(e->format != 32)
			return false;
		if(w->dnd == nil)
			w->dnd = emallocz(sizeof *dnd);
		dnd = w->dnd;
		dnd->source = l[0];
		dnd->r = ZR;
		return false;
	}else
	if(msg == xatom("XdndLeave")) {
		if(e->format != 32)
			return false;
		if(w->dnd) {
			free(w->dnd);
			w->dnd = nil;
		}
		return false;
	}else
	if(msg == xatom("XdndPosition")) {
		if(e->format != 32)
			return false;
		r = ZR;
		dnd = w->dnd;
		if(dnd) {
			p.x = (ulong)l[2] >> 16;
			p.y = (ulong)l[2] & 0xffff;
			p = subpt(p, w->r.min);
			Dprint(DDnd, "\tw: %W\n", w);
			Dprint(DDnd, "\tp: %P\n", p);
			if(eqrect(dnd->r, ZR) || !rect_haspoint_p(dnd->r, p))
				if(w->handler->dndmotion)
					dnd->r = w->handler->dndmotion(w, w->aux, p);
			r = dnd->r;
			if(!eqrect(r, ZR))
				r = rectaddpt(r, w->r.min);
			Dprint(DDnd, "\tr: %R\n", r);
		}
		pos = (r.min.x<<16) | r.min.y;
		siz = (Dx(r)<<16) | Dy(r);
		sendmessage(window(l[0]), "XdndStatus", e->window, 0, pos, siz, 0);
		return false;
	}

	return true;
}

static Handlers handlers = {
	.message = clientmessage_event
};

Added cmd/wmiir.c.


























































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
/* Copyight ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#define IXP_NO_P9_
#define IXP_P9_STRUCTS
#include <dirent.h>
#include <errno.h>
#include <limits.h>
#include <locale.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/signal.h>
#include <time.h>
#include <unistd.h>
#include <wchar.h>

#include <ixp.h>
#include <stuff/util.h>
#include <bio.h>
#include <fmt.h>

static IxpClient* client;
static Biobuf*	outbuf;
static bool	binary;

static void
usage(void) {
	lprint(1,
	       "usage: %s [-a <address>] [-b] {create | ls [-dlp] | read | remove | write} <file>\n"
	       "       %s [-a <address>] xwrite <file> <data>\n"
	       "       %s -v\n", argv0, argv0, argv0);
	exit(1);
}

static int
errfmt(Fmt *f) {
	return fmtstrcpy(f, ixp_errbuf());
}

static bool
flush(IxpCFid *fid, char *in, int len, bool binary) {
	static mbstate_t state;
	static char buf[IXP_MAX_MSG];
	static char *out = buf, *outend = buf + sizeof buf;
	char *inend;
	wchar_t w;
	Rune r;
	int res;

	if(binary)
		return ixp_write(fid, in, len) == len;

	inend = in + len;
	do {
		if(in == nil || out + UTFmax > outend) {
			if(ixp_write(fid, buf, out - buf) != out - buf)
				return false;
			out = buf;
		}
		if(in == nil) {
			state = (mbstate_t){0};
			return true;
		}

		switch((res = mbrtowc(&w, in, inend - in, &state))) {
		case -1:
			return false;
		case 0:
		case -2:
			return true;
		default:
			in += res;
			r = w < Runemax ? w : Runesync;
			out += runetochar(out, &r);
		}
	} while(in < inend);
	return true;
}

static bool
unflush(int fd, char *in, int len, bool binary) {
	static mbstate_t state;
	static char buf[IXP_MAX_MSG], extra[UTFmax];
	static char *out = buf, *outend = buf + sizeof buf;
	static int nextra;
	char *start;
	Rune r;
	int res, n;

	if(binary)
		return write(fd, in, len) == len;

	if(in) {
		if((n = nextra)) {
			nextra = 0;
			while(len > 0 && n < UTFmax && !fullrune(extra, n)) {
				extra[n++] = *in++;
				len--;
			}
			unflush(fd, extra, n, binary);
		}
		n = utfnlen(in, len);
	}

	start = in;
	do {
		if(in == nil || out + MB_LEN_MAX > outend) {
			if(write(fd, buf, out - buf) != out - buf)
				return false;
			out = buf;
		}
		if(in == nil || n == 0) {
			state = (mbstate_t){0};
			return true;
		}

		in += chartorune(&r, in);
		n--;
		res = wcrtomb(out, r, &state);
		if(res == -1)
			*out++ = '?';
		else
			out += res;
	} while(n > 0);

	if(in < start + len) {
		nextra = min(sizeof extra, len - (in - start));
		memcpy(extra, in, nextra);
	}
	return true;
}

/* Utility Functions */
static void
write_data(IxpCFid *fid, char *name, bool binary) {
	char buf[IXP_MAX_MSG];
	int len;

	while((len = read(0, buf, fid->iounit)) > 0)
		if(!flush(fid, buf, len, binary))
			fatal("cannot write file %q\n", name);

	if(!binary)
		flush(fid, nil, 0, binary);
}

static int
comp_stat(const void *s1, const void *s2) {
	Stat *st1, *st2;

	st1 = (Stat*)s1;
	st2 = (Stat*)s2;
	return strcmp(st1->name, st2->name);
}

static void
setrwx(long m, char *s) {
	static char *modes[] = {
		"---", "--x", "-w-",
		"-wx", "r--", "r-x",
		"rw-", "rwx",
	};
	strncpy(s, modes[m], 3);
}

static char *
modestr(uint mode) {
	static char buf[16];

	buf[0]='-';
	if(mode & P9_DMDIR)
		buf[0]='d';
	buf[1]='-';
	setrwx((mode >> 6) & 7, &buf[2]);
	setrwx((mode >> 3) & 7, &buf[5]);
	setrwx((mode >> 0) & 7, &buf[8]);
	buf[11] = 0;
	return buf;
}

static char*
timestr(time_t val) {
	static char buf[32];

	strftime(buf, sizeof buf, "%Y-%m-%d %H:%M", localtime(&val));
	return buf;
}

static void
print_stat(Stat *s, int lflag, char *file, int pflag) {
	char *slash;

	slash = "";
	if(pflag)
		slash = "/";
	else
		file = "";

	if(lflag)
		Blprint(outbuf, "%s %s %s %5llud %s %s%s%s\n",
			modestr(s->mode), s->uid, s->gid, s->length,
			timestr(s->mtime), file, slash, s->name);
	else {
		if((s->mode&P9_DMDIR) && strcmp(s->name, "/"))
			Blprint(outbuf, "%s%s%s/\n", file, slash, s->name);
		else
			Blprint(outbuf, "%s%s%s\n", file, slash, s->name);
	}
}

/* Service Functions */
static int
xwrite(int argc, char *argv[]) {
	IxpCFid *fid;
	char *file;

	ARGBEGIN{
	default:
		usage();
	}ARGEND;

	file = EARGF(usage());
	fid = ixp_open(client, file, P9_OWRITE);
	if(fid == nil)
		fatal("Can't open file '%s': %r\n", file);

	write_data(fid, file, binary);
	ixp_close(fid);
	return 0;
}

static int
xawrite(int argc, char *argv[]) {
	IxpCFid *fid;
	char *file, *buf;
	int nbuf, i;

	ARGBEGIN{
	default:
		usage();
	}ARGEND;

	file = EARGF(usage());
	fid = ixp_open(client, file, P9_OWRITE);
	if(fid == nil)
		fatal("Can't open file '%s': %r\n", file);

	nbuf = 1;
	for(i=0; i < argc; i++)
		nbuf += strlen(argv[i]) + (i > 0);
	buf = emalloc(nbuf);
	buf[0] = '\0';
	while(argc) {
		strcat(buf, ARGF());
		if(argc)
			strcat(buf, " ");
	}

	if(!(flush(fid, buf, nbuf, binary) && (binary || flush(fid, 0, 0, binary))))
		fatal("cannot write file '%s': %r\n", file);
	ixp_close(fid);
	free(buf);
	return 0;
}

static int
xcreate(int argc, char *argv[]) {
	IxpCFid *fid;
	char *file;

	ARGBEGIN{
	default:
		usage();
	}ARGEND;

	file = EARGF(usage());
	fid = ixp_create(client, file, 0777, P9_OWRITE);
	if(fid == nil)
		fatal("Can't create file '%s': %r\n", file);

	if((fid->qid.type&P9_DMDIR) == 0)
		write_data(fid, file, binary);
	ixp_close(fid);
	return 0;
}

static int
xremove(int argc, char *argv[]) {
	char *file;

	ARGBEGIN{
	default:
		usage();
	}ARGEND;

	file = EARGF(usage());
	do {
		if(!ixp_remove(client, file))
			lprint(2, "%s: Can't remove file '%s': %r\n", argv0, file);
	}while((file = ARGF()));
	return 0;
}

static int
xread(int argc, char *argv[]) {
	IxpCFid *fid;
	char *file, *buf;
	int count;

	ARGBEGIN{
	default:
		usage();
	}ARGEND;

	if(argc == 0)
		usage();
	file = EARGF(usage());
	do {
		fid = ixp_open(client, file, P9_OREAD);
		if(fid == nil)
			fatal("Can't open file '%s': %r\n", file);

		buf = emalloc(fid->iounit);
		while((count = ixp_read(fid, buf, fid->iounit)) > 0) {
			unflush(1, buf, count, binary);
			if (!binary && count < fid->iounit)
				unflush(1, 0, 0, binary);
		}
		if(!binary)
			unflush(1, 0, 0, binary);
		ixp_close(fid);

		if(count == -1)
			lprint(2, "%s: cannot read file '%s': %r\n", argv0, file);
	} while((file = ARGF()));

	return 0;
}

static int
xls(int argc, char *argv[]) {
	IxpMsg m;
	Stat *stat;
	IxpCFid *fid;
	char *file;
	char *buf;
	int lflag, dflag, pflag;
	int count, nstat, mstat, i;

	lflag = dflag = pflag = 0;

	ARGBEGIN{
	case 'l':
		lflag++;
		break;
	case 'd':
		dflag++;
		break;
	case 'p':
		pflag++;
		break;
	default:
		usage();
	}ARGEND;

	count = 0;
	file = EARGF(usage());
	do {
		stat = ixp_stat(client, file);
		if(stat == nil)
			fatal("cannot stat file '%s': %r\n", file);

		i = strlen(file);
		if(file[i-1] == '/') {
			file[i-1] = '\0';
			if(!(stat->mode&P9_DMDIR))
				fatal("%s: not a directory", file);
		}
		if(dflag || (stat->mode&P9_DMDIR) == 0) {
			print_stat(stat, lflag, file, pflag);
			ixp_freestat(stat);
			continue;
		}
		ixp_freestat(stat);

		fid = ixp_open(client, file, P9_OREAD);
		if(fid == nil)
			fatal("Can't open file '%s': %r\n", file);

		nstat = 0;
		mstat = 16;
		stat = emalloc(mstat * sizeof *stat);
		buf = emalloc(fid->iounit);
		while((count = ixp_read(fid, buf, fid->iounit)) > 0) {
			m = ixp_message(buf, count, MsgUnpack);
			while(m.pos < m.end) {
				if(nstat == mstat) {
					mstat <<= 1;
					stat = erealloc(stat, mstat * sizeof *stat);
				}
				ixp_pstat(&m, &stat[nstat++]);
			}
		}
		ixp_close(fid);

		qsort(stat, nstat, sizeof *stat, comp_stat);
		for(i = 0; i < nstat; i++) {
			print_stat(&stat[i], lflag, file, pflag);
			ixp_freestat(&stat[i]);
		}
		free(stat);
	} while((file = ARGF()));

	if(count == -1)
		fatal("cannot read directory '%s': %r\n", file);
	return 0;
}

static int
xnamespace(int argc, char *argv[]) {
	char *path;

	ARGBEGIN{
	default:
		usage();
	}ARGEND;

	path = ixp_namespace();
	if(path == nil)
		fatal("can't find namespace: %r\n");
	Blprint(outbuf, "%s", path);
	return 0;
}

static int
xproglist(int argc, char *argv[]) {
	DIR *d;
	struct dirent *de;
	struct stat st;
	char *dir, *cwd;
	int i;

	quotefmtinstall();

	ARGBEGIN{
	default:
		usage();
	}ARGEND;

	i = 7, cwd = nil;
	do
		cwd = erealloc(cwd, 1<<i);
	while(!getcwd(cwd, 1<<i) && errno == ERANGE);

	while((dir = ARGF()))
		/* Don't use Blprint. wimenu expects UTF-8. */
		if(!chdir(cwd) && !chdir(dir) && (d = opendir(dir))) {
			while((de = readdir(d))) {
				stat(de->d_name, &st);
				if(S_ISREG(st.st_mode) && !access(de->d_name, X_OK))
					Bprint(outbuf, "%q\n", de->d_name);
			}
			closedir(d);
		}

	return 0; /* NOTREACHED */
}

static int
xsetsid(int argc, char *argv[]) {
	char *av0;
	bool dofork;

	av0 = nil;
	dofork = false;
	ARGBEGIN{
	case '0':
		av0 = EARGF(usage());
		break;
	case 'f':
		dofork = true;
		break;
	default:
		usage();
	}ARGEND;
	if(av0 == nil)
		av0 = argv[0];
	if(av0 == nil)
		return 1;

	setsid();
	if(dofork)
		switch(fork()) {
		case 0:  break;
		case -1: fatal("can't fork: %r\n");
		default: return 0;
		}

	execvp(av0, argv);
	fatal("setsid: can't exec: %r");
	return 1; /* NOTREACHED */
}

typedef struct exectab exectab;
struct exectab {
	char *cmd;
	int (*fn)(int, char**);
} fstab[] = {
	{"cat", xread},
	{"create", xcreate},
	{"ls", xls},
	{"read", xread},
	{"remove", xremove},
	{"rm", xremove},
	{"write", xwrite},
	{"xwrite", xawrite},
	{0, }
}, utiltab[] = {
	{"namespace", xnamespace},
	{"ns", xnamespace},
	{"proglist", xproglist},
	{"setsid", xsetsid},
	{0, }
};

int
main(int argc, char *argv[]) {
	char *address;
	exectab *tab;
	int ret;

	IXP_ASSERT_VERSION;

	setlocale(LC_ALL, "");
	binary = utf8locale();

	quotefmtinstall();
	fmtinstall('r', errfmt);

	address = getenv("WMII_ADDRESS");

	ARGBEGIN{
	case 'b':
		binary = true;
		break;
	case 'v':
		lprint(1, "%s-" VERSION ", " COPYRIGHT "\n", argv0);
		exit(0);
	case 'a':
		address = EARGF(usage());
		break;
	default:
		usage();
	}ARGEND;

	if(argc < 1)
		usage();

	outbuf = Bfdopen(1, OWRITE);

	for(tab=utiltab; tab->cmd; tab++)
		if(!strcmp(*argv, tab->cmd)) {
			ret = tab->fn(argc, argv);
			goto done;
		}

	if(address && *address)
		client = ixp_mount(address);
	else
		client = ixp_nsmount("wmii");
	if(client == nil)
		fatal("can't mount: %r\n");

	signal(SIGPIPE, SIG_DFL);

	for(tab=fstab; tab->cmd; tab++)
		if(strcmp(*argv, tab->cmd) == 0) break;
	if(tab->cmd == 0)
		usage();

	ret = tab->fn(argc, argv);

	ixp_unmount(client);
done:
	Bterm(outbuf);
	return ret;
}

Added cmd/x11/Makefile.




































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
ROOT= ../..
include $(ROOT)/mk/hdr.mk
include $(ROOT)/mk/wmii.mk

TARG =	wikeyname \
	wmii9menu

PACKAGES += $(X11PACKAGES)

LIB = $(LIBS9) $(LIBIXP)
LIBS += $(LIB)
CFLAGS += $(INCX11)

wiclick.out: wiclick.o
	$(LINK) $@ $< -lXtst

include $(ROOT)/mk/many.mk

Added cmd/x11/setfocus.c.






















































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/* Copyight 2008 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include <ctype.h>
#include <stdarg.h>
#include <stdbool.h>
#include <string.h>
#include <stuff/x.h>
#include <stuff/util.h>
#include <fmt.h>

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

	ARGBEGIN{
	}ARGEND;

	initdisplay();

	if(!getulong(EARGF(exit(1)), &w))
		exit(1);

	XSetInputFocus(display, w, RevertToParent, CurrentTime);
	XCloseDisplay(display);
}

Added cmd/x11/wikeyname.c.




























































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
/* Copyright ©2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include <stuff/util.h>
#include <stuff/x.h>
#include <fmt.h>
#include <locale.h>
#include <unistd.h>

static const char version[] = "wikeyname-"VERSION", "COPYRIGHT"\n";

static Handlers handlers;
static char*	keyname;
static int	nkeys;

static void
usage(void) {
	lprint(2, "usage: wikeyname\n");
	exit(1);
}

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

	setlocale(LC_CTYPE, "");

	ARGBEGIN{
	case 'v':
		lprint(2, version);
		return 0;
	default: usage();
	}ARGEND;

	if(argc)
		usage();

	fmtinstall('K', fmtkey);
	initdisplay();

	selectinput(&scr.root, KeyPressMask|KeyReleaseMask);
	sethandler(&scr.root, &handlers);
	if(!grabkeyboard(&scr.root))
		fatal("can't grab keyboard\n");

	if(isatty(1))
		lprint(2, "Please press a key...\n");
	event_loop();
	lprint(1, "%s\n", keyname);

	XCloseDisplay(display);
	return 0;
}

static bool
kdown_event(Window *w, void *aux, XKeyEvent *ev) {

	USED(w, aux);
	nkeys++;
	free(keyname);
	keyname = smprint("%K", ev);
	return false;
}

static bool
kup_event(Window *w, void *aux, XKeyEvent *ev) {

	USED(w, aux, ev);
	if(keyname != nil && --nkeys <= 0)
		event_looprunning = false;
	return false;
}


static Handlers handlers = {
	.kup = kup_event,
	.kdown = kdown_event,
};

Added cmd/x11/wiwarp.c.




































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/* Copyight 2008 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include <ctype.h>
#include <stdarg.h>
#include <stdbool.h>
#include <string.h>
#include <stuff/x.h>
#include <stuff/util.h>
#include <fmt.h>

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

	ARGBEGIN{
	}ARGEND;

	initdisplay();

	if(argc) {
		if(!getint(EARGF(exit(1)), &pt.x))
			exit(1);
		if(!getint(EARGF(exit(1)), &pt.y))
			exit(1);
	}else {
		pt = querypointer(&scr.root);
		lprint(1, "%d %d\n", pt.x, pt.y);
	}

	warppointer(pt);
	XCloseDisplay(display);
}

Added cmd/x11/wmii9menu.c.
























































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
/* Licence
 * =======
 * 
 *   9menu is free software, and is Copyright (c) 1994 by David Hogan and
 *   Arnold Robbins. Permission is granted to all sentient beings to use
 *   this software, to make copies of it, and to distribute those copies,
 *   provided that:
 * 
 *       (1) the copyright and licence notices are left intact
 *       (2) the recipients are aware that it is free software
 *       (3) any unapproved changes in functionality are either
 *             (i) only distributed as patches
 *         or (ii) distributed as a new program which is not called 9menu
 *                 and whose documentation gives credit where it is due
 *       (4) the authors are not held responsible for any defects
 *           or shortcomings in the software, or damages caused by it.
 * 
 *   There is no warranty for this software.  Have a nice day.
 * 
 * --
 * Arnold Robbins
 * arnold@skeeve.com
 *
 * 9menu.c
 *
 * This program puts up a window that is just a menu, and executes
 * commands that correspond to the items selected.
 *
 * Initial idea: Arnold Robbins
 * Version using libXg: Matty Farrow (some ideas borrowed)
 * This code by: David Hogan and Arnold Robbins
 */

/* 
 * Heavily modified by Kris Maglione for use with wmii.
 */

#define IXP_NO_P9_
#define IXP_P9_STRUCTS
#include <fmt.h>
#include <ixp.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>

#include <stuff/clientutil.h>
#include <stuff/util.h>
#include <stuff/x.h>

char version[] = "wmii9menu-"VERSION" "COPYRIGHT", ©1994 David Hogan, Arnold Robbins";

static Window*	menuwin;

static CTuple	cnorm;
static CTuple	csel;
static Font*	font;

static int	wborder;

static char*	initial = "";
static int	cur;

static char**	labels;		/* list of labels and commands */
static char**	commands;
static int	numitems;

void usage(void);
void run_menu(void);
void create_window(void);
void size_window(int, int);
void redraw(int, int);
void warpmouse(int, int);

void
init_screens(void) {
	Rectangle *rects;
	Point p;
	int i, n;

	rects = xinerama_screens(&n);
	p = querypointer(&scr.root);
	for(i=0; i < n; i++) {
		if(rect_haspoint_p(rects[i], p))
			break;
	}
	if(i == n)
		i = 0;
	scr.rect = rects[i];
}

/* main --- crack arguments, set up X stuff, run the main menu loop */

int
main(int argc, char **argv)
{
	static char *address;
	char *cp;
	int i;

	ARGBEGIN{
	case 'v':
		lprint(1, "%s\n", version);
		return 0;
	case 'a':
		address = EARGF(usage());
		break;
	case 'i':
		initial = EARGF(usage());
		break;
	default:
		usage();
	}ARGEND;

	if(argc == 0)
		usage();

	initdisplay();
	xext_init();
	init_screens();
	create_window();

	numitems = argc;
	labels = emalloc(numitems * sizeof *labels);
	commands = emalloc(numitems * sizeof *labels);

	for(i = 0; i < numitems; i++) {
		labels[i] = argv[i];
		commands[i] = argv[i];
		if((cp = strchr(labels[i], ':')) != nil) {
			*cp++ = '\0';
			commands[i] = cp;
		}
		if(strcmp(labels[i], initial) == 0)
			cur = i;
	}

	client_init(address);

	wborder = strtol(readctl("/ctl", "border "), nil, 10);
	client_readconfig(&cnorm, &csel, &font);

	run_menu();

	XCloseDisplay(display);
	return 0;
}

void
usage(void)
{
	lprint(2, "usage: %s [-a <address>] [-i <arg>] <menitem>[:<command>] ...\n", argv0);
	lprint(2, "       %s -v\n", argv0);
	exit(0);
}

enum {
	MouseMask = ButtonPressMask
		  | ButtonReleaseMask
		  | ButtonMotionMask
		  | PointerMotionMask,
	MenuMask = MouseMask
		 | StructureNotifyMask
		 | ExposureMask
};

void
run_menu(void)
{
	XEvent ev;
	int i, old, wide, high;

	high = labelh(font);
	wide = 0;
	for(i = 0; i < numitems; i++)
		wide = max(wide, textwidth(font, labels[i]));
	wide += font->height & ~1;

	size_window(wide, high);
	warpmouse(wide, high);

	for(;;) {
		XNextEvent(display, &ev);
		switch (ev.type) {
		default:
			lprint(2, "%s: unknown ev.type %d\n", argv0, ev.type);
			break;
		case ButtonRelease:
			i = ev.xbutton.y / high;
			if(ev.xbutton.x < 0 || ev.xbutton.x > wide)
				return;
			else if(i < 0 || i >= numitems)
				return;

			lprint(1, "%s\n", commands[i]);
			return;
		case ButtonPress:
		case MotionNotify:
			old = cur;
			cur = ev.xbutton.y / high;
			if(ev.xbutton.x < 0 || ev.xbutton.x > wide)
				cur = ~0;
			if(cur == old)
				break;
			redraw(high, wide);
			break;
		case Expose:
			redraw(high, wide);
			break;
		case MapNotify:
		case ConfigureNotify:
		case MappingNotify:
			break;
		}
	}
}

void
create_window(void)
{
	WinAttr wa = { 0 };

	wa.override_redirect = true;
	menuwin = createwindow(&scr.root, Rect(-1, -1, 0, 0),
			       scr.depth, InputOutput,
			       &wa, CWOverrideRedirect);
	selectinput(menuwin, MenuMask);
	mapwin(menuwin);
	if(!grabpointer(menuwin, nil, 0, MouseMask))
		fatal("Failed to grab the mouse\n");
}

void
size_window(int wide, int high)
{
	Rectangle r;
	Point p;
	int h;

	h = high * numitems;
	r = Rect(0, 0, wide, h);

	p = querypointer(&scr.root);
	p.x -= wide / 2;
	p.x = max(p.x, scr.rect.min.x);
	p.x = min(p.x, scr.rect.max.x - wide);

	p.y -= cur * high + high / 2;
	p.y = max(p.y, scr.rect.min.y);
	p.y = min(p.y, scr.rect.max.y - h);

	reshapewin(menuwin, rectaddpt(r, p));
	setborder(menuwin, 1, &cnorm.border);
}

void
redraw(int high, int wide)
{
	Rectangle r;
	int i;

	r = Rect(0, 0, wide, high);
	for(i = 0; i < numitems; i++) {
		r = rectsetorigin(r, Pt(0, i * high));
		fillstring(menuwin, font, r, Center, labels[i], (cur == i ? &csel : &cnorm), 0);
	}
}

void
warpmouse(int wide, int high)
{
	Point p;
	int offset;

	/* move tip of pointer into middle of menu item */
	offset = labelh(font) / 2;
	offset += 6;	/* fudge factor */

	p = Pt(wide / 2, cur*high - high/2 + offset);
	p = addpt(p, menuwin->r.min);

	warppointer(p);
}

Added config.mk.










































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# Customize below to fit your system

# Paths
PREFIX = /usr/local
  BIN = $(PREFIX)/bin
  MAN = $(PREFIX)/share/man
  DOC = $(PREFIX)/share/doc/wmii
  ETC = $(PREFIX)/etc
  LIBDIR = $(PREFIX)/lib
  INCLUDE = $(PREFIX)/include
  PYPREFIX = --prefix=$(PREFIX)

# Includes and libs
INCLUDES = -I. -I$(ROOT)/include -I$(INCLUDE) -I/usr/include
LIBS = -L$(ROOT)/lib -L/usr/lib

TERMINAL = xterm

# Flags
include $(ROOT)/mk/gcc.mk
CFLAGS += -Os $(DEBUGCFLAGS)
LDFLAGS += -g

# Compiler, Linker. Linker should usually *not* be ld.
CC = cc -c
LD = cc
# Archiver
AR = ar crs

PYTHON = python

X11PACKAGES = x11 xinerama xrender xrandr
INCX11 = $$(pkg-config --cflags $(X11PACKAGES))
LIBIXP = $(LIBDIR)/libixp.a

# Enable RTLD. Only necessary for Xft support.
CFLAGS += -DHAVE_RTLD
LDFLAGS += -ldl # Comment this out on BSD systems.

SOLDFLAGS += $(LDFLAGS)
SHARED = -shared -Wl,-soname=$(SONAME)
STATIC = -static

# Your make shell. By default, the first found of /bin/dash, /bin/ksh,
# /bin/sh. Except with bsdmake, which assumes /bin/sh is sane. bash and zsh
# are painfully slow, and should be avoided.
#BINSH = /bin/ash

## Operating System Configurations

# KenCC
# Note: wmii *must* always compile under KenCC. It's vital for
# argument checking in formatted IO, and similar diagnostics.
#CFLAGS = -wF
#STATIC = # Implied
#CC=pcc -c
#LD=pcc

# Darwin
#STATIC = # Darwin doesn't like static linking
#SHARED = -dynamiclib
#SOEXT = dylib

# Solaris
#SHELL = /bin/bash
#CFLAGS = -fast $(INCS)
#LDFLAGS = $(LIBS) -R$(PREFIX)/lib -lsocket -lnsl
#CFLAGS += -xtarget=ultra

Added debian/changelog.










>
>
>
>
>
1
2
3
4
5
wmii-hg (2728) unstable; urgency=low

  * Remove long deprecated special treatment of the '!' tag.

 -- Kris Maglione <kris@suckless.org>  Tue, 15 Jun 2010 15:13:12 -0400
Added debian/compat.


>
1
7
Added debian/control.








































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Source: wmii-hg
Section: x11
Priority: optional
Maintainer: Kris Maglione <jg@suckless.org>
Build-Depends: libixp-hg, dash, python, libx11-dev, libxft-dev, libxext-dev, libxinerama-dev, libxrandr-dev, x11proto-xext-dev, quilt, debhelper (>= 4.0)
Standards-Version: 3.8.4
Homepage: http://wmii.suckless.org/

Package: wmii-hg
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}, dash (>= 0.1)
Conflicts: wmii, wmii2
Replaces: wmii, wmii2
Provides: x-window-manager
Description: lightweight tabbed and tiled X11 window manager
 wmii is a dynamic window manager for X11, which is highly customizable and
 usable with keyboard and mouse. It supports conventional, tabbed and tiled
 window management with low memory usage. It is highly modularized and uses an
 inter-process communication interface which is oriented on the 9p protocol of
 plan9.
Added debian/copyright.
































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
Debian packaging based on that of Daniel Baumann <daniel.baumann@panthera-systems.net>,
now maintained by the upstream author.

It was downloaded from <http://wmii.suckless.org/>.
Upstream Author: Kris Maglione <jg@suckless.org>

© 2006-2010 Kris Maglione <maglione.k at Gmail>
© 2003-2006 Anselm R. Garbe <garbeam at suckless dot org>

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.

Original debian packaging is (C) 2005-2007, Daniel Baumann <daniel@debian.org>
and is licensed under the GPL, see `/usr/share/common-licenses/GPL'.

Portions by Kris Maglione are in the Public Domain.

Added debian/docs.




>
>
1
2
FAQ
README
Added debian/file/wmii.desktop.














>
>
>
>
>
>
>
1
2
3
4
5
6
7
[Desktop Entry]
Encoding=UTF-8
Name=wmii
Comment=wmii pre-3.6 Development Snapshot
Exec=wmii
Icon=wmii.png
Type=XSession
Added debian/rules.
































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#!/usr/bin/make -f

PACKAGE=$(shell awk 'NR == 1 { print $$2 }' debian/control)

FLAGS = DESTDIR=$(CURDIR)/debian/$(PACKAGE)/ \
 	PREFIX=/usr 	\
 	ETC=/etc/X11	\
 	STATIC= 	\
 	TERMINAL=x-terminal-emulator

%:
	dh $@

override_dh_installwm:
	dh_installwm --priority=60

override_dh_compress:
	dh_compress -X.pdf

override_dh_auto_build:
	$(MAKE) $(FLAGS) all

override_dh_auto_install:
	$(MAKE) $(FLAGS) install

	rm debian/$(PACKAGE)/usr/share/doc/wmii/LICENSE
	install -D -m0644 img/icon.png debian/$(PACKAGE)/usr/share/icons/wmii.png
	install -D -m0644 debian/file/wmii.desktop debian/$(PACKAGE)/usr/share/xsessions/wmii.desktop

.depend:
	true

Added debian/wmii.menu.






>
>
>
1
2
3
?package(wmii):needs="wm" section="WindowManagers"\
  title="wmii" longtitle="wmii pre-3.6 Development Snapshot"\
  command="/usr/bin/wmii"
Added debian/wmii.wm.


>
1
/usr/bin/wmii
Added doc/Makefile.


























>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
ROOT=..
include $(ROOT)/mk/hdr.mk
include $(ROOT)/mk/wmii.mk

TARG =	wmii.pdf

all: $(TARG)

install: $(TARG:.pdf=.install)
uninstall: $(TARG:.pdf=.uninstall)

clean:; true

Added doc/customizing.tex.




























































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
\chapter{Customizing \wmii}

There are several configuration schemes available for \wmii. If
you're only looking to add basic key bindings, status monitors,
\emph{et cetera}, you should have no trouble modifying the stock
configuration for your language of choice. If you're looking for
deeper knowledge of \wmii's control interface though, this
section is for you. We'll proceed by building a configuration
script in \POSIX\ |sh| syntax and then move on to a discussion
of the higher level constructs in the stock configuration
scripts.

For the purposes of pedagogy, we'll construct the script in the
literate programming style of Knuth, whereby we construct the
code in fragments and explain each one in detail. For your
convenience, each fragment name is linked to its definition.

\section{Events}

The \wmii\ control interface is largely event driven. Each event
is represented by a single, plain-text line written to the
|/event| file. You can think of this file as a named pipe. When
reading it, you won't receive an EOF\footnote{End of File} until
\wmii\ exits. Moreover, any lines written to the file will be
transmitted to everyone currently reading from it. Notable
events include key presses, the creation and destruction of
windows, and changes of focus and views.

We'll start building our configuration with an event processing
framework:

\begin{Fragment}{Event Loop}
  # Broadcast a custom event
  wmiir xwrite /event Start wmiirc

  # Turn off globbing
  set -f
  # Open /event for reading
  wmiir read /event |
  # Read the events line by line
  while read line; do
      # Split the line into words, store in $@
      set -- $line
      event=$1; shift
      line = "$(echo $line | sed ‘s/^[^ ]* //’ | tr -d ‘\n’)"
      # Process the event
      case $event in
      Start) # Quit when a new instance starts
          [ $1 = wmiirc ] && exit;;
      «Event Handlers»
      esac
  done
\end{Fragment}

Now, we need to consider which types of events we'll need to
handle:

\begin{Fragment}{Event Handlers}
  «View Button Events»
  «Urgency Events»
  «Unresponsive Clients»
  «Notice Events»
  «Key Events»
  «Client Menu Events»
  «Tag Menu Events»
\end{Fragment}

\section{Bar Items}

The bar is described by the files in the two directories |/lbar/| and
|/rbar/| for buttons on the left and right side of the bar,
respectively. The files act as control files (section
\ref{sec:controlfiles}) with the contents:

\begin{code}
  color ‹Color Tuple›
  label ‹Label›
\end{code}

A ‹Color Tuple› is defined as:

\begin{code}
  ‹Color Tuple› ≔ ‹foreground color› ‹background color› ‹border color›
  ‹* Color›     ≔ ‹RGB color› | ‹RGBA color›
  ‹RGB color›   ≔ #‹6 character RGB hex color code›
  ‹RGBA color›  ≔ rgba:‹red›/‹green›/‹blue›/‹alpha›
\end{code}

\noindent
where all of the colors are represented as lowercase,
hexidecimal values. In the case of RGBA colors, they may be 1--4
characters long, though they will be standardized internally to
2 characters.

\medskip

Let's define our basic theme information now:

\begin{Fragment}{Theme Definitions}
  normcolors=‘#000000 #c1c48b #81654f’
  focuscolors=‘#000000 #81654f #000000’
  background=‘#333333’
  font=‘drift,-*-fixed-*-*-*-*-9-*-*-*-*-*-*-*’
\end{Fragment}

\subsection{View Buttons}

With a basic understanding of bar items in mind, we can write
our view event handlers:

\index{events!CreateTag}
\index{events!DestroyTag}
\index{events!FocusTag}
\index{events!UnfocusTag}
\begin{Fragment}{View Button Events}
  CreateTag)  # CreateTag ‹Tag Name›
      echo $normcolors $1 | wmiir create /lbar/$1;;
  DestroyTag) # DestroyTag ‹Tag Name›
      wmiir rm /lbar/$1;;
  FocusTag)   # FocusTag ‹Tag Name›
      wmiir xwrite /lbar/$1 $focuscolors $1;;
  UnfocusTag) # UnfocusTag ‹Tag Name›
      wmiir xwrite /lbar/$1 $normcolors $1;;
\end{Fragment}

\subsection{Urgency}

\index{events!UrgentTag|(}
\index{events!NotUrgentTag|(}
Windows can specify that they require attention, and in X11
parlance, this is called urgency\footnote{\ICCCM{4.1.2.4}}. When
a window requests attention as such, or declares that it's been
satisfied, \wmii\ broadcasts an event for the client and an
event for each view that it belongs to. It also fills in the
layout box of any client deemed urgent. It's the job of a script
to decide how to handle urgency events above and beyond that
basic measure. The standard scripts simply mark urgent views
with an asterisk:

\begin{Fragment}{Urgency Events}
  # The urgency events are ‘Client’ events when the program
  # owning the window sets its urgency state. They're ‘Manager’
  # events when wmii or the wmii user sets the state.
  UrgentTag)    # UrgentTag ‹‘Client’ or ‘Manager’› ‹Tag Name›
      wmiir xwrite /lbar/$2 $2;;
  NotUrgentTag) # NotUrgentTag ‹‘Client’ or ‘Manager’› ‹Tag Name›
      wmiir xwrite /lbar/$2 $2;;
\end{Fragment}
\index{events!UrgentTag|)}
\index{events!NotUrgentTag|)}

\subsection{Notices}

The standard scripts provide a custom Notice event for
displaying status information. The events appear in the long bar
between the left and right sides for five seconds.

\begin{Fragment}{Notice Events}
  Notice)
      wmiir xwrite /rbar/!notice $line
      kill $xpid 2>/dev/null # Let's hope this isn't reused...
      { sleep 5; wmiir xwrite /rbar/!notice ‘ ’; } &
      xpid = $!;;
\end{Fragment}

\section{Keys}

\label{sec:keybindings}
\index{key bindings}
\index{filesystem!/!keys}
\index{filesystem!/!event}
Now to the part you've no doubt been waiting for: binding keys.
When binding keys, you need to be aware of two files, |/keys|
and |/event|. The former defines which keys \wmii\ needs to
grab, and the latter broadcasts the events when they're pressed.

Key names are specified as a series of modifiers followed by a
key name, all separated by hyphens. Valid modifier names are
|Control|, |Shift|, |Mod1| (usually Alt), |Mod2|, |Mod3|, |Mod4|
(usually the Windows® key), and |Mod5|. Modifier keys can be
changed via |xmodmap(1)|, the details of which are beyond the
scope of this document.

Key names can be detected by running |xev| from a
terminal, pressing the desired key, and looking at the output
(it's in the parentheses, after the keysym). Or, more simply,
you can run the \man 1 {wikeyname} utility bundled with \wmii\
and press the key you wish to bind.

Examples of key bindings:

\begin{description}
  \item[Windows® key + Capital A] |Mod4-Shift-A|
  \item[Control + Alt + Space]    |Mod1-Control-Space|
\end{description}

Now, let's bind the keys we plan on using:

\begin{Fragment}{Bind Keys}
  {
  cat <<!
  Mod4-space
  Mod4-d
  Mod4-s
  Mod4-m
  Mod4-a
  Mod4-p
  Mod4-t
  Mod4-Return
  Mod4-Shift-space
  Mod4-f
  Mod4-Shift-c
  Mod4-Shift-t
  Mod4-h
  Mod4-j
  Mod4-k
  Mod4-l
  Mod4-Shift-h
  Mod4-Shift-j
  Mod4-Shift-k
  Mod4-Shift-l
  !
  for i in 1 2 3 4 5 6 7 8 9 0; do
      echo Mod4-$i
      echo Mod4-Shift-$i
  done
  } | wmiir write /keys
\end{Fragment}

and lay a framework for processing their events:

\begin{Fragment}{Key Events}
  Key) # Key ‹Key Name›
      case $1 in
      «Motion Keys»
      «Client Movement Keys»
      «Column Mode Keys»
      «Client Command Keys»
      «Command Execution Keys»
      «Tag Selection Keys»
      «Tagging Keys»
      esac;;
\end{Fragment}

\section{Click Menus}

Sometimes, you have your hand on the mouse and don't want to
reach for the keyboard. To help cope, \wmii\ provides a
mouse-driven, single-click menu. The default configuration uses
it for client and tag menus.

\begin{Fragment}{Click Menu Initialization}
  clickmenu() {
      if res=$(wmii9menu -- “$@”); then eval “$res”; fi
  }
\end{Fragment}

\section{Control Files}

\label{sec:controlfiles}

Several directories including the root, have control files,
named |ctl|. These files are used to control the object (e.g., a
client or tag) represented by the directory. Each line of the
file, with the possible section of the first, represents a
control variable and its value. In the case of all but the root
|/ctl| file, the first line represents the id of the directory.
In the case of |/tag/foo/ctl|, for instance, the first line
should read |foo|. This is useful when dealing with the special
|sel/| directories. For instance, when |foo| is the selected
tag, the special |/tag/sel| directory is a link to |/tag/foo|,
and the first line of |/tag/sel/ctl| will read |foo|, just as
if you'd accessed |/tag/foo/ctl| directly.

The rest of the lines, the control variables, can be modified by
writing new values to the control file. For instance, if a
client is fullscreen, its control file will contain the line:

\begin{code}
  fullscreen on
\end{code}

\noindent To restore the client from fullscreen, either of the
following lines may be written to its control file:

\begin{code}
  fullscreen off
  fullscreen toggle
\end{code}

When next read, the |fullscreen on| line will have been replaced
with |fullscreen off|.  No care need be taken to preserve the
other contents of the file. They're generated anew each time
it's read.

\section{Clients}

\def\clientlabel{/client/$\langle\mathit{client}\rangle$/}
\index{filesystem!/client/*/@\clientlabel|(}
Clients are represented by directories under the |/client/|
tree. Subdirectory names represent the client's X11 window ID.
The special |sel/| directory represents the currently selected
client. The files in these directories are:

\begin{description}
  \item[ctl] The client's control file, containing the following
    properties:
    \index{filesystem!/client/*/@\clientlabel!ctl}
    \begin{description}
      \item[allow] The set of unusual actions the client is
        allowed to perform, in the same format as the tag set.
        \begin{description}
     \item[activate] The client is allowed to activate
       itself—that is, focus its window and, as the case may
       require, uncollapse it and select a tag it resides on.
       This flag must be set on a client if you wish it able to
       activate itself from the system tray.
        \end{description}
      \item[floating] Defines whether this client is likely to
        float when attached to a new view. May be |on|, |off|,
        |always|, or |never|.  Ordinarilly, the value changes
        automatically whenever the window is moved between the
        floating and managed layers. However, setting a value of
        |always| or |never| overrides this behavior.
      \item[fullscreen] The client's fullscreen state. When
        |on|, the client is displayed fullscreen on all of its
        views. Possible values are |on|, |off|, and |toggle|.
      \item[group] The client's group ID, or |0| if not part of
        a group. Clients tend to open with the same tags and in
        the same columns as the last active member of their
        group. Setting this property is only useful when done
        via the rules file.
      \item[kill] When written, the window is closed politely,
        if possible.
      \item[pid] Read-only value of the PID of the program that
        owns the window, if the value is available and the
        process is on the same machine as wmii.
      \item[slay] When written, the client is disconnected
        peremptorily. If the client's PID is available and the
        process is the same machine as wmii, its parent process
        is killed
      \item[tags] The client's tags. The same as the tags file.
      \item[urgent] The client's urgency state. When |on|, the
        client's layout box will be highlighted. Possible values
        are |on|, |off|, and |toggle|.
    \end{description}
  \item[props] The client's window class (the X11
    |WM_CLASS|\footnote{\ICCCM{4.1.2.5}}
    property) and title string, separated by colons. This file
    is not writable.
    \index{filesystem!/client/*/@\clientlabel!props}
  \item[label] The client's window title. May be written to
    change the client's title.
    \index{filesystem!/client/*/@\clientlabel!label}
  \item[tags]
    \index{filesystem!/client/*/@\clientlabel!tags}
    The client's tags. Tag names are separated by |+|, |-|, or
    |^| signs. Tag names which directly follow a |+| sign are
    added, while whose following a |-| sign are removed and
    those following a |^| are toggled.  If the value written
    begins with one of these characters, the value is appended
    to the clients tags rather than replacing them. 
    
    Tags formatted as |/‹regex›/| are treated as regular
    expressions, which place the client on any extant matching
    tag\footnote{While a client with a regex tag will always
    appear in all matching views, it will not keep those views
    in existence. When the last client explicitly tagged with a
    view is removed, the view is deleted as soon as it becomes
    inactive.}.  Regular expression tags which directly follow a
    minus sign are treated as exclusion expressions. For
    example, the tag string |+/foo/-/food/| will match the tag
    |foobar|, but not the tag |foodstand|.
\end{description}

\index{filesystem!/client/*/@\clientlabel|)}

\subsection{Key Bindings}

To control clients, we'll add the following key bindings:

\begin{Fragment}{Client Command Keys}
  Mod4-Shift-c) wmiir xwrite /client/sel/ctl kill;;
  Mod4-f) wmiir xwrite /client/sel/ctl Fullscreen toggle;;
\end{Fragment}

And to manage their tags, we'll need:

\begin{Fragment}{Tagging Keys}
  Mod4-Shift-t)
    # Get the selected client's id
    c=$(wmiir read /client/sel/ctl | sed 1q)
    # Prompt the user for new tags
    tags=$(wmiir ls /tag | sed ‘s,/,,; /^sel$/d’ | wimenu)
    # Write them to the client
    wmiir xwrite /client/$c/tags $tag;;
  Mod4-Shift-[0-9])
    wmiir xwrite /client/sel/tags ${1##*-};;
\end{Fragment}

\subsection{Click Menus}

\index{events!ClientMouseDown}
\begin{Fragment}{Client Menu Events}
  ClientMouseDown) # ClientMouseDown ‹Client ID› ‹Button›
    [ $2 = 3 ] && clickmenu \
      “Delete:wmiir xwrite /client/$1/ctl kill” \
      “Kill: wmiirxwrite /client/$1/ctl slay” \
      “Fullscreen:wmiir xwrite /client/$1/ctl fullscreen on”
\end{Fragment}

\subsection{Unresponsive Clients}

\index{events!UnresponsiveClient|(}
When \wmii\ tries to close a window, it waits 8 seconds for the
client to respond, and then lets its scripts decide what to do
with it. The stock scripts prompt the user for input:

\begin{Fragment}{Unresponsive Clients}
  UnresponsiveClient) # UnresponsiveClient ‹Client ID›
  {
      # Use wihack to make the xmessage a transient window of
      # the problem client. This will force it to open in the
      # floaing layer of whatever views the client is attached to
      resp=$(wihack -transient $1 \
             xmessage -nearmouse -buttons Kill,Wait -print \
             “The following client is not responding.” \
             “What would you like to do?$(echo)” \
             $(wmiir read /client/$1/label))
      [ $resp = Kill ] && wmiir xwrite /client/$1/ctl slay
  } &;;
\end{Fragment}
\index{events!UnresponsiveClient|)}

\section{Views}

\def\taglabel{/tag/$\langle\mathit{tag}\rangle$/}
\index{filesystem!/tag/*/@\taglabel|(}
Views are represented by directories under the |/tag/| tree. The
special |sel/| directory represents the currently selected
client. The |sel| tag is treated similarly elsewhere. The files
in these directories are:

\begin{description}
  \item[ctl]
    The view's control file. The properties are:
    \index{filesystem!/tag/*/@\taglabel!ctl|(}
    \begin{description}
      \item[select ‹Area›] Select the column ‹Area›, where
        ‹Area› is a 1-based column index, or |~| for the floating
        area. It may be optionally preceded by ‹Screen›|:|, where
        ‹Screen› is a 0-based Xinerama screen index, or “sel”. When
        omitted, ‹Screen› defaults to 0, the primary screen.
      \item[select ‹Area› ‹Client Index›] Select the column ‹Area›, and
        the ‹Client Index›th client.
      \item[select client ‹Client ID›] Select the client with the
        X11 window ID ‹Client ID›.
      \item[select ‹Direction›]
        Select the client in ‹Direction› where ‹Direction› may be
        one of ‹up $\wedge$ down $\wedge$ left $\wedge$ right›.
      \item[send client ‹Client ID› ‹Area›] Send ‹Client ID› to
        ‹Area›. ‹Area› may be |sel| for the selected area, and
        |client ‹Client ID›| may be |sel| for the currently selected
        client.
      \item[send client ‹Client ID› ‹Direction›]
        Send ‹Client ID› to a column or position in its column in
        the given direction.
      \item[send client ‹Client ID› toggle] If ‹Client ID› is
        floating, send it to the managed layer. If it's managed,
        send it to the floating layer.
      \item[swap client ‹Client ID› \ldots] The same as the |send|
        commands, but swap ‹Client ID› with the client at the given
        location.
      \item[colmode ‹Area› ‹Mode›] Set ‹Area›'s mode to ‹Mode›,
        where ‹Mode› is a string of values similar to tag
        specifications. Values which may be added and removed are as
        follows for managed areas:

        \begin{description}
          \item[stack] One and only one client in the area is
            uncollapsed at any given time. When a new client is
            selected, it is uncollapsed and the previously selected
            client is collapsed.
          \item[max] Collapsed clients are hidden from view
            entirely. Uncollapsed clients display an indicator
            {\it‹n›/‹m›}, where ‹m› is the number of collapsed
            clients directly above and below the client, plus one,
            and ‹n› is the client's index in the stack.
          \item[default] Like subtracting the stack mode, but all
            clients in the column are given equal height.
        \end{description}

        For the floating area, the values are the same, except that
        in |max| mode, floating clients are hidden when the managed
        layer is selected.
      \item[grow ‹Frame› ‹Direction› {[‹Amount›]}] Grow ‹Frame› in
        the given direction, by ‹Amount›. ‹Amount› may be any
        integer, positive or negative. If suffixed with |px|,
        it specifies an exact pixel amount, otherwise it specifies a
        “reasonable increment”. Defaults to 1.

        ‹Frame› may be one of:
        \begin{itemize}
          \item client ‹Client ID›
          \item ‹Area› ‹Client Index›
        \end{itemize}
      \item[nudge ‹Frame› ‹Direction› {[‹Amount›]}] Like
        |grow|, but move the client in ‹Direction› instead of
        resizing it.
  \end{description}
  \index{filesystem!/tag/*/@\taglabel!ctl|)}
\end{description}

\index{filesystem!/tag/*/@\taglabel|)}

\subsection{Key Bindings}

We'll use the following key bindings to interact with views:

\begin{Fragment}{Motion Keys}
  Mod4-h) wmiir xwrite /tag/sel/ctl select left;;
  Mod4-l) wmiir xwrite /tag/sel/ctl select right;;
  Mod4-k) wmiir xwrite /tag/sel/ctl select up;;
  Mod4-j) wmiir xwrite /tag/sel/ctl select down;;
  Mod4-space) wmiir xwrite /tag/sel/ctl select toggle;;
\end{Fragment}

\begin{Fragment}{Client Movement Keys}
  Mod4-Shift-h) wmiir xwrite /tag/sel/ctl send sel left;;
  Mod4-Shift-l) wmiir xwrite /tag/sel/ctl send sel right;;
  Mod4-Shift-k) wmiir xwrite /tag/sel/ctl send sel up;;
  Mod4-Shift-j) wmiir xwrite /tag/sel/ctl send sel down;;
  Mod4-Shift-space) wmiir xwrite /tag/sel/ctl send sel toggle;;
\end{Fragment}

\begin{Fragment}{Column Mode Keys}
  Mod4-d) wmiir xwrite /tag/sel/ctl colmode sel -stack-max;;
  Mod4-s) wmiir xwrite /tag/sel/ctl colmode sel stack-max;;
  Mod4-m) wmiir xwrite /tag/sel/ctl colmode sel stack+max;;
\end{Fragment}

\subsection{Click Menus}

\index{events!LeftBarMouseDown}
\begin{Fragment}{Tag Menu Events}
  LeftBarMouseDown) # LeftBarMouseDown ‹Button› ‹Bar Name›
    [ $1 = 3 ] && clickmenu \
      “Delete:delete_view $2”
\end{Fragment}

\section{Command and Program Execution}

Perhaps the most important function we need to provide for is
the execution of programs. Since \wmii\ users tend to use
terminals often, we'll add a direct shortcut to launch one.
Aside from that, we'll add a menu to launch arbitrary programs
(with completions) and a separate menu to launch wmii specific
commands.

We use |wmiir setsid| to launch programs with their own session
IDs to prevent untoward effects when this script dies.

\begin{Fragment}{Command Execution Initialization}
  terminal() { wmiir setsid xterm “$@” }
  proglist() {
      IFS=:
      wmiir proglist $1 | sort | uniq
      unset IFS
  }
\end{Fragment}

\subsection{Key Bindings}
\begin{Fragment}{Command Execution Keys}
  Mod4-Return) terminal & ;;
  Mod4-p) eval exec wmiir setsid “$(proglist $PATH | wimenu)” &;;
  Mod4-a) {
      set -- $(proglist $WMII_CONFPATH | wimenu)
      which=$(which which)
      prog=$(PATH=$WMII_CONFPATH $which $1); shift
      eval exec $prog “$@”
  } &;;
\end{Fragment}

\section{The Root}

The root filesystem contains the following:

\index{!filesystem!/|(}
\begin{description}
  \item[ctl] The control file. The properties are:
    \index{filesystem!/!ctl}
    \begin{description}
      \item[bar on ‹top $\wedge$ bottom›] Controls where the bar
        is shown.
      \item[border] The border width, in pixels, of floating
        clients.
      \item[colmode ‹Mode›] The default column mode for newly
        created columns.
      \item[focuscolors ‹Color Tuple›] The colors of focused
        clients.
      \item[normcolors ‹Color Tuple›] The colors of unfocused
        clients and the default color of bar buttons.
      \item[font ‹Font›] The font used throughout \wmii. If
        prefixed with |xft:|, the Xft font renderer is used, and
        fonts may be antialiased. Xft font names follow the
        fontconfig formula. For instance, 10pt, italic Lucida
        Sans would be specified as

        \begin{code}
          xft:Lucida Sans-10:italic
        \end{code}

        See \man 1 {fc-match}.

      \item[grabmod ‹Modifier Keys›] The key which must be
        pressed to move and resize windows with the mouse
        without clicking hot spots.
      \item[incmode ‹Mode›] Controls how X11 increment hints are
        handled in managed mode. Possible values are:
        \begin{description}
          \item[ignore] Increment hints are ignored entirely.
            Clients are stretched to fill their full allocated
            space.
          \item[show] Gaps are shown around managed client
            windows when their increment hints prevent them from
            filling their entire allocated space.
          \item[squeeze] When increment hints cause gaps to show
            around clients, \wmii\ will try to adjust the sizes
            of the clients in the column to minimize lost space.
        \end{description}
      \item[view ‹Tag›] Change the currently visible view.
      \item[exec ‹Command›] Replaces this \wmii\ instance with
        ‹Command›. ‹Command› is split according to rc quoting
        rules, and no expansion occurs. If the command fails to
        execute, \wmii\ will respawn.
      \item[spawn ‹Command›] Spawns ‹Command› as it would spawn
        |wmiirc| at startup. If ‹Command› is a single argument
        and doesn't begin with |/| or |./|,%
        \hskip 1ex|$WMII_CONF|\-|PATH| is
        searched for the executable. Otherwise, the whole
        argument is passed to the shell for evaluation.
    \end{description}
  \item[keys]  The global keybindings. See section \ref{sec:keybindings}.
               \index{filesystem!/!keys|primary}
  \item[event] The global event feed. See section \ref{sec:keybindings}.
               \index{filesystem!/!event|primary}
  \item[colrules]
    \index{filesystem!/!colrules}
        The |/colrules| file contains a list of
        rules which affect the width of newly created columns.
        Rules have the form:

        \begin{quote}\texttt{
          /‹regex›/ -> ‹width›{\color{gray}[}+‹width›{\color{gray}]*}}
        \end{quote}

        Where,

        \begin{code}
    ‹width› ≔ ‹percent of screen› | ‹pixels›px
        \end{code}

        When a new column, ‹n›, is created on a view whose name
        matches ‹regex›, it is given the ‹n›th supplied ‹width›.
        If there is no ‹n›th width, it is given
        $1/\mbox{‹ncol›th}$ of the screen.

  \item[rules]
    \index{filesystem!/!rules}
    The |/rules| file contains a list of
    rules similar to the colrules. These rules set
    properties for a client when it is created.
    Rules are specified:

    \begin{quote}\texttt{
      /‹regex›/ -> ‹key›{\color{gray}=}‹value› {\color{gray}\ldots}}
    \end{quote}

    When a client's ‹name›:‹class›:‹title› matches
    ‹regex›, the matching rules are applied. For each
    ‹key›=‹value› pair, the |ctl| file property matching
    ‹key› is set to ‹value›.  Additionally,  the  following
    keys are accepted and have special meaning:

    \begin{description}
      \item[continue]
        Normally, when a matching rule  is  encountered,
        rule  matching  stops.  When the continue key is
        provided (with any value), matching continues at
        the next rule.
      \item[force-tags]
        Like  tags,  but overrides any settings obtained
        obtained from the client's  group  or  from  the
        |_WMII_TAGS| window property.
    \end{description}

\end{description}

\index{!filesystem!/|)}

\subsection{Configuration}

We'll need to let \wmii\ know about our previously defined theme
information:

\begin{Fragment}{Configuration}
  «Theme Definitions»

  xsetroot -solid $background
  wmiir write /ctl <<!
  border 2
  focuscolors $focuscolors
  normcolors $normcolors
  font $font
  grabmod Mod4
  !
\end{Fragment}

\subsection{Key Bindings}

And we need a few more key bindings to select our views:

\begin{Fragment}{Tag Selection Keys}
  Mod4-t)
    # Prompt the user for a tag
    tags=$(wmiir ls /tag | sed ‘s,/,,; /^sel$/d’ | wimenu)
    # Write it to the filesystem.
    wmiir xwrite /ctl view $tags;;
  Mod4-[0-9])
    wmiir xwrite /ctl view ${1##*-};;
\end{Fragment}

\section{Tieing it All Together}

\begin{code}
  #!/bin/sh
  «Click Menu Initialization»
  «Command Execution Initialization»

  «Configuration»

  «Bind Keys»
  «Event Loop»
\end{code}

\section{The End Result}

For clarity, here is the end result:

\begin{code}
  #!/bin/sh
  # «Click Menu Initialization»
  clickmenu() {
      if res=$(wmii9menu -- “$@”); then eval “$res”; fi
  }
  # «Command Execution Initialization»
  terminal() { wmiir setsid xterm “$@” }
  proglist() {
      IFS=:
      wmiir proglist $1 | sort | uniq
      unset IFS
  }

  # «Configuration»
  # «Theme Definitions»
  normcolors=‘#000000 #c1c48b #81654f’
  focuscolors=‘#000000 #81654f #000000’
  background=‘#333333’
  font=‘drift,-*-fixed-*-*-*-*-9-*-*-*-*-*-*-*’

  xsetroot -solid $background
  wmiir write /ctl <<!
  border 2
  focuscolors $focuscolors
  normcolors $normcolors
  font $font
  grabmod Mod4
  !

  # «Bind Keys»
  {
  cat <<!
  Mod4-space
  Mod4-d
  Mod4-s
  Mod4-m
  Mod4-a
  Mod4-p
  Mod4-t
  Mod4-Return
  Mod4-Shift-space
  Mod4-f
  Mod4-Shift-c
  Mod4-Shift-t
  Mod4-h
  Mod4-j
  Mod4-k
  Mod4-l
  Mod4-Shift-h
  Mod4-Shift-j
  Mod4-Shift-k
  Mod4-Shift-l
  !
  for i in 1 2 3 4 5 6 7 8 9 0; do
      echo Mod4-$i
      echo Mod4-Shift-$i
  done
  } | wmiir write /keys

  # «Event Loop»
  # Broadcast a custom event
  wmiir xwrite /event Start wmiirc

  # Turn off globbing
  set -f
  # Open /event for reading
  wmiir read /event |
  # Read the events line by line
  while read line; do
      # Split the line into words, store in $@
      set -- $line
      event=$1; shift
      line = "$(echo $line | sed ‘s/^[^ ]* //’ | tr -d ‘\n’)"

      # Process the event
      case $event in
      Start) # Quit when a new instance starts
          [ $1 = wmiirc ] && exit;;

      # «Event Handlers»
      # «View Button Events»
      CreateTag)  # CreateTag ‹Tag Name›
          echo $normcolors $1 | wmiir create /lbar/$1;;
      DestroyTag) # DestroyTag ‹Tag Name›
          wmiir rm /lbar/$1;;
      FocusTag)   # FocusTag ‹Tag Name›
          wmiir xwrite /lbar/$1 $focuscolors $1;;
      UnfocusTag) # UnfocusTag ‹Tag Name›
          wmiir xwrite /lbar/$1 $normcolors $1;;

      # «Urgency Events»
      # The urgency events are ‘Client’ events when the program
      # owning the window sets its urgency state. They're ‘Manager’
      # events when wmii or the wmii user sets the state.
      UrgentTag)    # UrgentTag ‹‘Client’ or ‘Manager’› ‹Tag Name›
          wmiir xwrite /lbar/$2 $2;;
      NotUrgentTag) # NotUrgentTag ‹‘Client’ or ‘Manager’› ‹Tag Name›
          wmiir xwrite /lbar/$2 $2;;

      # «Unresponsive Clients»
      UnresponsiveClient) # UnresponsiveClient ‹Client ID›
      {
          # Use wihack to make the xmessage a transient window of
          # the problem client. This will force it to open in the
          # floaing layer of whatever views the client is attached to
          resp=$(wihack -transient $1 \
                 xmessage -nearmouse -buttons Kill,Wait -print \
                 “The following client is not responding.” \
                 “What would you like to do?$(echo)” \
                 $(wmiir read /client/$1/label))
          [ $resp = Kill ] && wmiir xwrite /client/$1/ctl slay
      } &;;

      # «Notice Events»
      Notice)
          wmiir xwrite /rbar/!notice $line
          kill $xpid 2>/dev/null # Let's hope this isn't reused...
          { sleep 5; wmiir xwrite /rbar/!notice ‘ ’; } &
          xpid = $!;;

      # «Key Events»
      Key) # Key ‹Key Name›
          case $1 in
          # «Motion Keys»
          Mod4-h) wmiir xwrite /tag/sel/ctl select left;;
          Mod4-l) wmiir xwrite /tag/sel/ctl select right;;
          Mod4-k) wmiir xwrite /tag/sel/ctl select up;;
          Mod4-j) wmiir xwrite /tag/sel/ctl select down;;
          Mod4-space) wmiir xwrite /tag/sel/ctl select toggle;;

          # «Client Movement Keys»
          Mod4-Shift-h) wmiir xwrite /tag/sel/ctl send sel left;;
          Mod4-Shift-l) wmiir xwrite /tag/sel/ctl send sel right;;
          Mod4-Shift-k) wmiir xwrite /tag/sel/ctl send sel up;;
          Mod4-Shift-j) wmiir xwrite /tag/sel/ctl send sel down;;
          Mod4-Shift-space) wmiir xwrite /tag/sel/ctl send sel toggle;;

          # «Column Mode Keys»
          Mod4-d) wmiir xwrite /tag/sel/ctl colmode sel -stack-max;;
          Mod4-s) wmiir xwrite /tag/sel/ctl colmode sel stack-max;;
          Mod4-m) wmiir xwrite /tag/sel/ctl colmode sel stack+max;;

          # «Client Command Keys»
          Mod4-Shift-c) wmiir xwrite /client/sel/ctl kill;;
          Mod4-f) wmiir xwrite /client/sel/ctl fullscreen toggle;;

          # «Command Execution Keys»
          Mod4-Return) terminal & ;;
          Mod4-p) eval exec wmiir setsid “$(proglist $PATH | wimenu)” &;;
          Mod4-a) {
              set -- $(proglist $WMII_CONFPATH | wimenu)
              prog=$(PATH=$WMII_CONFPATH which $1); shift
              eval exec $prog “$@”
          } &;;

          # «Tag Selection Keys»
          Mod4-t)
            # Prompt the user for a tag
            tags=$(wmiir ls /tag | sed ‘s,/,,; /^sel$/d’ | wimenu)
            # Write it to the filesystem.
            wmiir xwrite /ctl view $tag;;
          Mod4-[0-9])
            wmiir xwrite /ctl view ${1##*-};;

          # «Tagging Keys»
          Mod4-Shift-t)
            # Get the selected client's id
            c=$(wmiir read /client/sel/ctl | sed 1q)
            # Prompt the user for new tags
            tags=$(wmiir ls /tag | sed ‘s,/,,; /^sel$/d’ | wimenu)
            # Write them to the client
            wmiir xwrite /client/$c/tags $tag;;
          Mod4-Shift-[0-9])
            wmiir xwrite /client/sel/tags ${1##*-};;

          esac;;

      # «Client Menu Events»
      ClientMouseDown) # ClientMouseDown ‹Client ID› ‹Button›
        [ $2 = 3 ] && clickmenu \
          “Delete:wmiir xwrite /client/$1/ctl kill” \
          “Kill:wmiir xwrite /client/$1/ctl slay” \
          “Fullscreen:wmiir xwrite /client/$1/ctl fullscreen on”

      # «Tag Menu Events»
      LeftBarMouseDown) # LeftBarMouseDown ‹Button› ‹Bar Name›
        [ $1 = 3 ] && clickmenu \
          “Delete:delete_view $2”
      esac
  done
\end{code}

Added doc/floating.png.

cannot compute difference between binary files

Added doc/focused.png.

cannot compute difference between binary files

Added doc/gettingstarted.tex.












































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
\chapter{Getting Started}

This section will walk you through your first \wmii\ startup.
For your first experience, we recommend running \wmii\ in its
own X session, so you can easily switch back to a more
comfortable environment if you get lost. Though you may start
\wmii\ from a session manager in your day to day use, these
instructions will use |xinit|. To begin with, copy this file
to your home directory, so we can open it in your new X session.
Then setup your |~/.xinitrc| as follows:

\begin{code}
  cd

  # Start a PDF viewer with this guide. Use any viewer
  # you're comfortable with.
  xpdf wmii.pdf &

  # Launch wmii
  exec wmii

  # That was easy.
\end{code}

Before you run |xinit|, make sure you know how to switch
between terminals. Depending on your system, your current X
session is probably on terminal 5 or 7. You should be able to
switch between your terminals by pressing
Ctrl-Alt-F$\langle n\rangle$. Assuming that your current X
session is on terminal 7, you should be able to switch between
it and your new session by pressing Ctrl-Alt-F7 and Ctrl-Alt-F8.
Now you should be ready to start \wmii. When you run the
following command, you should be presented with a new X session
running wmii and a PDF viewer showing this document.

\begin{code}
  xinit
\end{code}

When you're there, find this page in the new PDF viewer and
continue.

\section{Your First Steps}

If everything went according to plan, you should be viewing this
from a nearly empty \wmii\ session. We're going to be using the
keyboard a lot, so let's start with a convention for key
notation. We'll be using the key modifiers Control, Alt, Shift,
and Meta\footnote{The Windows$^{\mbox{\tiny®}}$ key on most
keyboards. The Penguin key on the more tongue in cheek
varieties.}, which we'll specify as C-, A-, S-, and M-,
respectively. So, <C-S-a> means pressing ‘|a|’ while holding
|Control| and |Shift|.  We'll also express mouse clicks this
way, with <M-Mouse1> signifying a press of the right mouse
button, with the Meta key depressed.  Buttons 4 and 5 are the up
and down scroll wheel directions, respectively.

\subsection{Floating Mode}

Beginning with what's familiar to most users, we'll first explore
floating mode. First, we need to select the floating layer.
Press <M-Space>. You should see the titlebar of this window
change color. Now, press <M-Return> to launch a terminal.
The easiest way to drag the terminal around is to press and hold
<M-Mouse1> over the window and simply drag the window
around. You should be able to drag the window anywhere onscreen
without ever releasing the mouse button. As you drag near the
screen edges, you should notice a snap. If you try to drag the
window fully off-screen, you'll find it constrained so that a
portion always remains visible. Now, release the window and move
the mouse toward one of its corners. Press and hold
<M-Mouse3>\footnote{The right button.}. As you drag the
mouse around, you should see the window resized accordingly.

To move the window without the modifier key, move the pointer
over the layout box to the left of its titlebar. You should see
the cursor change. Now, simply click and drag. To resize it,
move the pointer toward the window's edge until you see the
cursor change, and again, click and drag. Now, to close the
window, move the mouse over the windows titlebar, press and hold
<Mouse3>, select |Delete|, and release it. You should
see this window's titlebar return to its original color,
indicating that it's regained focus.

\subsection{Managed Mode}

Now, for the fun part. We'll start exploring managed mode by
looking at the basics of columns. In the default configuration,
columns have three modes:

\begin{description}
  \item[Stack] <M-s> The default mode for new columns. Only one window
    is fully visible per column at once. The others only display
    their title bars. When new windows are added to the column,
    the active window collapses, and the new one takes its
    place. Whenever a collapsed client is selected, the active
    window is collapsed to take its place.
  \item[Max] <M-m> Like stack mode, but the titlebars of collapsed
    clients are hidden.
  \item[Default] <M-d> Multiple uncollapsed windows may be visible at
    once. New windows split the space with the other uncollapsed
    windows in their vicinity. Windows may still be collapsed by
    shrinking them to the size of their titlebars. At this
    point, the behavior of a stack of collapsed and uncollapsed
    clients is similar to that of stack mode.
\end{description}

Before we open any new windows in managed mode, we need to
explore the column modes a bit. Column modes are activated with
the key bindings listed above. This column should be in stack
mode now. Watch the right side of the titlebar as you press
<M-m> to enter max mode. You should see an indicator appear.
This tells you the number of hidden windows directly above and
below the current window, and its position in that stack. Press
<M-d> to enter default mode. Now we're ready to open another
client. Press <M-Return> to launch another terminal. Now,
press <M-S-l> to move the terminal to a new column to the
right of this one. Once it's there, press <M-Return> two
more times to launch two more terminals. Now that you have more
than one window in a column, cycle through the three column
modes again until they seem familiar.

\subsection{Keyboard Navigation}

To begin, switch back to default mode. The basic keyboard
navigation keys, <M-h>, <M-j>, <M-k>, and <M-l>,
derive from vi, and represent moving left, down, up, and right
respectively. Try selecting each of the four windows currently
visible on screen. Notice that navigation wraps from one side of
the screen to the other, and from the top to the bottom. Now,
return to the write column, switch to stack mode, and select
each of the three terminals again. Do the same in max mode,
paying careful attention to the indicator to the right of the
titlebar.

Now that you can select windows, you'll want to move them
around. To move a window, just add the Shift key to the
direction keys. So, to move a window left, instead of <M-h>,
type <M-S-h>. Now, experiment with moving windows, just as
you did with navigating them, in each of the three column modes.
Once you're comfortable with that, move a window to the floating
layer. Since we toggled between the floating and managed layers
with <M-Space>, we'll move windows between them with
<M-S-Space>. Try moving some windows back and forth until it
becomes familiar. Now, move several windows to the floating
layer and try switching between them with the keyboard. You'll
notice that <M-h> and <M-l> don't function in the
floating layer. This is for both historical and logistical
reasons. <M-j> and <M-k> cycle through floating windows
in order of their most recent use.

\subsection{Mouse Navigation}

\wmii\ uses the “sloppy focus” model, which is to say, it focuses
windows when the mouse enters them and when you click them. It
focuses windows only when you select them with the keyboard,
click their titlebars, or press click them with <M-Mouse2>.
Collapsed windows may be opened with the mouse by clicking their
titlebars. Moving and resizing floating windows should be
largely familiar, and has already been covered. The same can't
be said for managed windows.

Let's begin working with the mouse in the managed layer. Return
to a layout with this document in a column on the left, and
three terminals in a column to the right. Switch the right
column to default mode. Now, bring the mouse to the top of the
third terminal's titlebar until you see a resize cursor. Click
and drag the titlebar to the very top of the screen. Now, move
the cursor to the top of the second terminal's titlebar and drag
it to the very bottom of the screen. Press <M-d> to restore the
terminals to their original sizes. Now, click and hold the
layout box of the second terminal. Drag it to the middle of the
terminal's window and release. Click and hold the layout box of
the third terminal and drag it to the middle of the first
terminal's window. Finally, drag the first terminal's layout box
to halfway down this window. <M-Mouse1> works to the same
effect as dragging the layout box, but allows you to click
anywhere in the window.

Now that you've seen the basics of moving and dragging windows,
let's move on to columns. Click and drag the border between the
two columns. If that's a difficult target to click, there's a
triangle at the top of the division between the two columns that
you can click and drag as well. If that's still too hard a
target, try using <M-Mouse3>, which works anywhere and provides
much richer functionality.

\subsection{Window Focus and Selection}

For the purposes of keyboard navigation, \wmii\ keeps track of
which window is currently selected, and confers its titlebar a
different color scheme from the other windows. This window is
the basis of relative motion commands, such as “select the
window to the left”, and the target of commands such as “close
this window”. Normally, the selected window is the same as the
focused window, i.e., the window that receives keyboard events.
Some applications, however, present strange corner cases.

\begin{description}
  \item[Focused, selected window] This is the normal case of a
    window which is both selected and has the keyboard focus.
    \titlebar{selected}
  \item[Unfocused, unselected window] This is the normal case for an
    unselected window which does not have the keyboard focus.
    \titlebar{unselected}
  \item[Unfocused, selected window] This is the first unusual
    case. This is the selected window, for the purposes of
    keyboard navigation, but it does not receive keyboard events.
    A good example is an onscreen keyboard, which will receive
    mouse clicks and translate them to keyboard events, but
    won't absorb those keyboard events itself. Other examples
    include any window whilst another (such as \wimenu) has
    grabbed the keyboard.
    \titlebar{unfocused}
  \item[Focused, unselected window] This is the second unusual
    focus case. The window has the keyboard focus, but for the
    purposes of keyboard navigation, it is not considered
    selected. In the case of an onscreen keyboard, this is the
    window which will receive the generated events. In the case
    of a keyboard grab, the will likely be the window holding
    the grab.
    \titlebar{focused}
\end{description}

\section{Running Programs}

You've already seen the convenient key binding to launch a
terminal, but what about other programs? To get a menu of all of
the executables in your path, type <M-p>. This should replace
the bar at the bottom of the screen with a prompt, followed by a
string of completions. Start typing the name of a program that
you want to open. You can press <Tab> and <S-Tab> to cycle
through the completions, or you can just press <Return> to
select the first one. If you want to execute a more complex
command, just type it out and press <Return>. If you want to
recall that command later, use \wimenu's history. Start typing
the command you want and then press <C-p> until you come to it.

When you're done with a program, you'll probably want an easy
way to close it. The first way is to ask the program to close
itself. Since that can be tedious (and sometimes impossible),
\wmii\ provides other ways. As mentioned, you can right click
the titlebar and select |Delete|. If you're at the keyboard,
you can type <M-S-c>. These two actions cause \wmii\ to ask
nicely that the program exit. In those sticky cases where the
program doesn't respond, \wmii\ will wait 10 seconds before
prompting you to kill the program. If you don't feel like
waiting, you can select |Kill| from the window's titlebar
menu, in which case \wmii\ will forcefully and immediately kill
it. Beware, killing clients is a last resort. In cases where the
same program opens multiple windows, killing one will kill them
all—without warning.

\section{Using Views}

As already noticed, \wmii's concept of virtual workspaces is
somewhat unique, so let's begin exploring it. Open up a terminal
and press <M-S-2>. You should see a new button on the bar at the
bottom of the screen. When you click it, you should see your
original terminal. Press <M-1> to come back here. Now, press
<M-3>, and <M-1> again to return here once more. Notice that the
views were created when needed, and destroyed when no longer
necessary. If you want to select a view with a proper name, use
<M-t> and enter the name. Other than the dynamic creation of
views, this is still similar to the familiar X11 workspace
model. But that's just the beginning of \wmii's model. Open a new
terminal, and type:

\begin{code}
  echo ‘Hello world!’
\end{code}

\noindent Now, type <M-S-t>. In the menu that appears, enter
|1+2+3|. Now, visit the views |1|, |2|, and |3|, and you'll see
the client on each. To remove a tag, type <M-S-t> again, and
this time enter |-2|. You'll notice that the client is no longer
on the |2| view. Finally, tag names needn't be discrete,
ordinary strings. They can also be regular expressions. Select
the terminal again, and enter |+/^5/|. Now, switch to the |5|
view.  Now try the |6| view. Finally, type <M-t> and enter |50|
to check the |50| view. Clients tagged with regular expressions
are attached to any matching views when they're created. So,
when you switch to an empty view, or tag a client with a new
tag, any clients with matching regular expressions are
automatically added to it. When all explicitly tagged clients
disappear from the view, and it's no longer visible, clients
held there by regular expressions are automatically removed.

\section{Learning More}

For full tables of the standard key bindings, and descriptions
of the precise semantics of the topics discussed above, you
should refer to \wmii's |man| pages.

Added doc/introduction.tex.












































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
\chapter{Introduction}

\wmii\ is a simple but powerful window manager for the X Window
System. It provides both the classic (“floating”) and tiling
(“managed”) window management paradigms, which is to say, it does
the job of managing your windows, so you don't have to. It also
provides programability by means of a simple file-like
interface, which allows the user to program in virtually any
language he chooses. These basic features have become
indispensable to the many users of \wmii\ and other similar
window managers, but they come at a cost. Though our penchant
for simplicity makes \wmii's learning curve significantly
shorter than most of its competitors, there's still a lot to
learn. The rest of this guide will be devoted to familiarizing
new users with \wmii's novel features and eccentricities, as
well as provide advanced users with an in-depth look at our
customization facilities.

\section{Concepts}

As noted, \wmii\ provides two management styles:

\begin{description}
  \item[Managed] This is the primary style of window management
    in \wmii. Windows managed in this style are automatically
    arranged by \wmii\ into columns. Columns are created and
    destroyed on demand. Individual windows in the column may be
    moved or resized, and are often collapsed or hidden
    entirely. Ad-hoc stacks of collapsed and uncollapsed windows
    allow the user to efficiently manage their tasks. When
    switching from an active to a collapsed window, the active
    window collapses and the collapsed one effectively takes
    its place.

    Managed windows have an unadorned titlebar:

    \titlebar{managed}

  \item[Floating] Since some programs aren't designed in ways
    conducive to the managed work flow, \wmii\ also provides the
    classic “floating” window management model. Windows managed
    in this model float above the managed windows and may be moved
    freely about. Other than automatic placement of new windows
    and snapping of edges, \wmii\ doesn't manage floating
    windows at all.

    Floating windows are indicated by a decorated titlebar:

    \titlebar{floating}

  \item[Fullscreen] Fullscreen mode is actually a subset of the
    floating style. Windows may be toggled to and from
    fullscreen mode at will. When fullscreen, windows reside in
    the floating layer, above the managed windows. They have no
    borders or titlebars, and occupy the full area of the
    screen. Other than that, however, they're not special in any
    way. Other floating windows may appear above them and the
    user can still select, open, and close other windows at
    will.
\end{description}

\subsection{The Filesystem}

All of \wmii's customization is done via a virtual filesystem.
Since the filesystem is implemented in the standardized \ninep\
protocol, it can be accessed in many ways. \wmii\ provides a
simple command-line client, \wmiir, but many alternatives exist,
including libraries for Python, Perl, Ruby, PHP, and C. It can
even be mounted, either by Linux's 9p.ko kernel module or
indirectly via FUSE.

The filesystem that \wmii\ provides is “virtual”, which is to
say that it doesn't reside on disk anywhere. In a sense, it's a
figment of \wmii's imagination. Files, when read, represent
\wmii's current configuration or state. When written, they
perform actions, update the UI, etc. For instance, the directory
|/client/| contains a directory for each window that \wmii\
is currently managing. Each of those directories, in turn,
contains files describing the client's properties (its title,
its views\footnote{Views in \wmii\ are akin to workspaces or
virtual desktops in other window managers, but with some subtle
differences.}, its state). Most files can be written to update
the state they describe. For instance,
|/client/sel/ctl| describes the state of the selected
client. If a client is fullscreen, it contains the line:

\begin{code}
  fullscreen on
\end{code}

\noindent To change this, you'd update the file with the line
% XXX: Line broken at /ctl cmd.
|fullscreen off| or even |fullscreen| |toggle| to toggle
the client's fullscreen state.

The concept of controlling a program via a filesystem derives
from \plannine, where such interfaces are extensive and well
proven\footnote{The concept has also taken hold on most Unixes
in the form of \texttt{/proc} and \texttt{/sys} virtual
filesystems, but tends to be very kernel-centric. On \plannine,
where the model is more pervasive, there are more virtual
filesystems for user-level applications than for the kernel.}.
The metaphor has shown itself to be quite intuitive to Unix
users, once the shock of a “virtual” filesystem wears off.  The
flexibility of being able to control \wmii\ from myriad
programming languages, including the standard Unix shell and
even from the command line, is well worth the shock.

\subsection{Views and Tags}

Like most X11 window managers, \wmii\ provides virtual
workspaces. Unlike other window managers though, \wmii's
workspaces are created and destroyed on demand. Instead of being
sent to a workspace, windows in \wmii\ are tagged with any
number of names. Views are created dynamically from these tags,
and automatically if the user tries to access them.  For
instance, if a window is given the tags ‘foo’ and ‘bar’, the two
views ‘foo’ and ‘bar’ are created, if they don't already exist.
The window is now visible on both of them. Moreover, tags can be
specified as regular expressions. So, a client tagged with {\tt
\verb+/^foo/+} will appear on any view named ‘foo’, ‘foo:bar’,
and so forth. Any time a client is tagged with a matching tag,
or the user opens a matching view, the window is automatically
added to it.

\subsection{The Bar}

\wmii\ provides a general purpose information bar at the top or
bottom of the screen. The bar is divided into a left and a right
section. Each section is made up of buttons, with a single
button spanning the gap between the two sides. Buttons can be
individually styled and can hold any text content the user
wishes. By convention, the buttons to the left show view names,
and those to the right display status information.

\subsection{The Menus}

\wmii\ includes two simple, external menu programs. The first,
\wimenu, is keyboard-based, and is used to launch programs and
generally prompt the user for input. It provides a list of
completions which are automatically filtered as you type. The
second, \wiIXmenu, is mouse-based, and is generally used to
provide context menus for titlebars and view buttons. Both menus
can be easily launched from shell scripts or the command line,
as well as from more complex scripting languages.

\subsection{The Keyboard}

\wmii\ is a very keyboard friendly window manager. Most actions
can be performed without touching the mouse, including
launching, closing, moving, resizing, and selecting programs.
New keybindings of any complexity can easily be added to handle
any missing functionality, or to simplify any repetitive tasks.

\subsection{The Mouse}

Despite being highly keyboard-accessible, \wmii\ strives to be
highly mouse accessible as well. Windows can be moved or resized
by dragging their window borders. When combined with a key
press, they can be moved, resized, or raised by dragging any
visible portion of the window. Mouse menus are accessed with a
single click and drag. View buttons in the bar and client
titlebars respond to the mouse wheel; view buttons can be
activated by dragging any draggable object (e.g., a file from a
file manager) over them.

Added doc/license.tex.


























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
\chapter*{License}

This file is distributed under the same terms as wmii:

\begingroup
\ttfamily
\parindent=0pt
\parskip=1em

Copyright © 2009-2010 Kris Maglione <\href{mailto:maglione.k@gmail.com}{maglione.k@gmail.com}>

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.
\endgroup
Added doc/managed.png.

cannot compute difference between binary files

Added doc/mkfile.


































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
MKSHELL=rc

TARG = wmii.pdf
SRC = ${TARG:%.pdf=%.tex}
TEX = xelatex

default:V: all
all:V: $TARG

# mk doesn't recognize nested `{}
deps = `{ ash -c 'dep() { echo $1; for f in $(9 sed -n "s/.*\\include{([^}]+)}.*/\1.tex/p; s/.*\\input (.*)/\1.tex/p" $1); do dep $f; done; }; dep $SRC' }
# `
$TARG: $deps

junk = aux idx ilg ind log toc out
CLEAN = ${TARG:%.pdf=%}
CLEAN = ${junk:%=$CLEAN.%} ${deps:%.tex=%.aux}
clean:V:
	rm -f $CLEAN

%.pdf: %.tex mkfile
	flag x +
	if (~ $MAINFONT '')
		fn tex { builtin $TEX $stem.tex }
	if not
		fn tex { builtin $TEX '\def\mainfont{'$"MAINFONT'}\input{'$stem.tex'}' }
	
	tex
	makeindex $stem
	tex
	rm -f $stem.out 2>/dev/null

# vim:se ft=make:
Added doc/selected.png.

cannot compute difference between binary files

Added doc/unfocused.png.

cannot compute difference between binary files

Added doc/unselected.png.

cannot compute difference between binary files

Added doc/wmii.pdf.

cannot compute difference between binary files

Added doc/wmii.tex.






































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
\documentclass[letterpaper,oneside]{scrbook}

\usepackage{txfonts}

\usepackage{fontspec}
\usepackage{xunicode}
\usepackage{xltxtra}

\usepackage{fancyvrb}
\usepackage[top=1in,bottom=1in]{geometry}
\usepackage{graphicx}
\usepackage{makeidx}
\usepackage{xcolor}
\usepackage[xetex,breaklinks,colorlinks,linkcolor=black]{hyperref}

\let\EA=\expandafter

\newif\ifexpandfragments
\newif\ifdefinefragments

%% Indexes
\makeindex
\let\primary=\textbf

\ifx\mainfont\undefined
  \def\mainfont{Palatino LT Std}
\fi

\setmainfont[Mapping=tex-text, Numbers=OldStyle]\mainfont
\def\lining{\addfontfeature{Numbers=Lining}}

\let\primary=\textbf

\def\titlebar#1{%
  \begin{center}\includegraphics[width=5.5in]{#1.png}\end{center}}

\def\man#1#2{#2\textbf{(#1)}}

\makeatletter

%% Key specs
\def\key#1{{\small$\langle$\lining#1\/$\rangle$}}
\let\<=<
\catcode`\<=\active
\def<#1>{\key{#1}}

%% Display ‹...› and «...» as text in left and right pointing
%% angle brackets. I use «» and ‹› because my terminal doesn't
%% display left and right pointing angle brackets properly, and
%% Xorg's compose maps don't provide them, anyway.
\def\«{«}\def\‹{‹}
\catcode`\«=\active
\catcode`\‹=\active
\def‹#1›{$\langle${\itshape#1}$\rangle$}
\def«#1»{$\langle\langle${\itshape#1}$\rangle\rangle$}

\catcode`\∅=\active
\def∅{\box0}
\def«{%
  \let\dofragment@target=\hyperlink%
  \@ifnextchar*\dofragment@@\dofragment@}
\def\dofragment@@*{%
  \let\dofragment@target=\hypertarget%
  \dofragment@}
\def\dofragment@#1»{%
  \setbox0=\hbox{$\langle\langle${\itshape#1}$\rangle\rangle$}%
  \ifexpandfragments%
    \def\a{\sp\sp\comment \boxzero^^J}%
    \begingroup%
      \def\ { }\xdef\@frag@name{#1}%
    \endgroup%
    \UseFragment{∅}\@frag@name%
  \else%
    \dofragment@target{frag:#1}{\box0}%
  \fi}

% Display |...| as verbatim, teletype text.
\DefineShortVerb{\|}

\def\macroname#1{%
  \expandafter\@macroname\string#1}
\def\@macroname#1{}
\def\defverb#1{%
  \EA\def\EA#1\EA{\EA\protect\EA\UseVerb\EA{\macroname#1}}%
  \EA\SaveVerb\EA{\macroname#1}}

\let\idx@@heading\chapter

%% Create a verbatim {code} environment which highlights strings
%% and comments. Several unicode characters are hacked to replace
%% the grabbed characters, since we can't escape them in the
%% verbatim environment.
\colorlet{comment}{gray}
\colorlet{string}{red!100!black!90}
\let\‘=‘
\let\“=“
\def\≔{≔}
\catcode`#=\active
\catcode`\≔=\active
\def\docodes{%
  \catcode`\#=\active%
  \catcode`“=\active%
  \catcode`‘=\active}

\def≔{\ensuremath{\Coloneqq}}
\let#=\#
\begingroup
  \docodes
  \catcode`\#=\active%
  \catcode`¶=6
  \gdef\comment{\itshape\color{comment}\let“=\“\let‘=\‘\#}
  \gdef\dodefineactive{%
    \let#=\comment%
    \gdef“¶¶1”{{\color{string}\“¶¶1”}}%
    \gdef‘¶¶1’{{\color{string}\‘¶¶1’}}}
\endgroup

\DefineVerbatimEnvironment{code}{Verbatim}{xleftmargin=2em,gobble=2,%
  codes={\docodes\catcode`\☺=0},%
  defineactive={\dodefineactive}}

%% Save code fragments for piecing together later
\begingroup
  \catcode`\@=0
  @catcode`\\=12
  @gdef@bcode{@detokenize{\begin{code}^^J}}
  @gdef@ecode{@detokenize{\end{code}^^J}}
  @catcode`@ =12@gdef@sp{ }
@endgroup

% Ripped from fancyverb
% I'm currently rather unfond of it.
\def\Fragment{\FV@Environment{}{Fragment}}
\def\FVB@Fragment#1{%
  \@bsphack
  \begingroup
    \FV@UseKeyValues
    \gdef\Fragment@Name{#1}%
    \xdef\Fragment@Prefix{\«*#1» \≔^^J}
    \xdef\TheFragment{}
    \def\FV@ProcessLine##1{%
      \edef\frag{\detokenize{##1^^J}}%
      \xdef\TheFragment{\TheFragment\frag}}%
    \FV@Scan}
\def\FVE@Fragment{%
  \EA\global\EA\let
  \csname SV@\Fragment@Name\endcsname\TheFragment%
  \endgroup%
  \EA\UseFragment\EA{\Fragment@Prefix}\Fragment@Name}
\DefineVerbatimEnvironment{Fragment}{Fragment}{}

\def\UseFragment#1#2{
  \begingroup
    \EA\let\EA\a\csname SV@#2\endcsname
    \ifx\a\undefined\def\a{\ldots}\fi
    \ifx\FV@EnvironName\relax%
      \edef\a{\bcode\detokenize{++#1}\a\ecode}\else%
      \edef\a{\detokenize{#1}\a}\fi%
    \newtoks\tokens
    \EA\tokens\EA{\a}
    \everyeof{\noexpand}%
    \scantokens\EA{\the\tokens}
  \endgroup
}

%% Convenience defs for the various wmii commands, and a few
%% others.
\defverb\wmii|wmii|
\defverb\wiIXmenu|wimii9menu|
\defverb\wimenu|wimenu|
\defverb\wmiir|wmiir|
\def\ninep{{\lining 9P}}
\def\POSIX{\textsc{POSIX}}
\def\plannine{{\lining Plan 9}}
\def\ICCCM#1{%
  \@ICCCM#1@
  \href{http://www.tronche.com/gui/x/icccm/sec-\@ICCCM@chap.html\#s-#1}{%
    ICCCM \lining§#1}}
\def\@ICCCM#1.#2@{\def\@ICCCM@chap{#1}}

\makeatother

\begin{document}
\thispagestyle{empty}
\leavevmode
\vfill

\begin{center}
  \centerline{\includegraphics[width=2in]{../img/wmii.pdf}}

  \vskip 1in

  \LARGE
  The \wmii\ User Guide

  \vskip .5in

  \Large
  Kris Maglione \\[1em]
  \lining
  13 October 2009

\end{center}

\vfill

\newpage

\frontmatter

\tableofcontents

\newpage

\include{license}

\mainmatter

\include{introduction}
\include{gettingstarted}
\include{customizing}

\backmatter

\printindex

\end{document}
Added examples/Makefile.














>
>
>
>
>
>
>
1
2
3
4
5
6
7
ROOT=..
include $(ROOT)/mk/hdr.mk
include $(ROOT)/mk/wmii.mk

DOCS   = wimenu-file-completion.sh
DOCDIR = $(DOC)/examples

Added examples/wimenu-file-completion.sh.




































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#!/bin/sh
# This script will launch wimenu and provide command
# completion for the first argument and filename completion
# for each following argument, and execute the result.
# Program name completion requires that a program list already
# exist in $(wmiir namespace)/.proglist

fifo="$HOME/.wmii/menu_fifo"
mkfifo $fifo 2>/dev/null

script=$(cat <<'!'
    BEGIN {
        progs = "cat $(wmiir namespace)/.proglist"

        # Print the first set of completions to wimenu’s fifo
        print read(progs) >fifo
        fflush(fifo)
    }

    # Process the input and provide the completions
    {
        # Skip the trailing part of the command.
        # If there is none, this is the result.
        if (!getline rest) {
            print
            exit
        }

        if (!match($0, /.*[ \t]/))
            # First argument, provide the program list
            update(0, progs)
        else {
            # Set the offset to the location of the last
            # space, and save that part of the completion
            offset = RLENGTH
            str = substr($0, offset + 1)

            # If we're completing a sub-directory, adjust
            # the offset to the position of the last /
            if (match(str, ".*/"))
                offset += RLENGTH

            # If the last component of the path begins with
            # a ., include hidden files
            arg = ""
            if(match(str, "(^|/)\\.[^/]*$"))
                    arg = "-A"

            # Substitute ~/ for $HOME/
            sub("^~/", ENVIRON["HOME"] "/", str)

            # Strip the trailing filename
            sub("[^/]+$", "", str)

            update(offset, "ls " arg quote(str))
        }
    }

    # Push out a new set of completions
    function update(offset, cmd) {
        # Only push out the completion if the offset or the
        # option of ls has changed. The behavior will be the
        # same regardless, but this is a minor optimization
        if (offset != loffset || cmd != lcmd) {
            loffset = offset
            lcmd = cmd

            cmpl = read(cmd)
            print offset >fifo
            print cmpl >fifo
            fflush(fifo)
        }
    }

    # Quote a string. This should work in any Bourne
    # or POSIX compatible shell.
    function quote(str) {
        if (!match(str, /[\[\](){}$'"^#~!&;*?|<>]/))
            return str
        gsub(/\\/, "'\\\\'", str)
        gsub(/'/, "'\\''", str)
        return "'" str "'"
    }

    # Read the output of a command and return it
    function read(cmd) {
        if (cmd in cache)
            return cache[cmd]
        res = ""
        while (cmd | getline)
            res = res quote($0) "\n"
        close(cmd)
        return cache[cmd] = res
    }
!
)
res="$(wimenu -c "$@" <$fifo | awk -v "fifo=$fifo" "$script")"
exec ${SHELL:-sh} -c "exec $res"
Added img/icon.png.

cannot compute difference between binary files

Added img/mkfile.
























































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
MKSHELL=rc
path=$PLAN9/bin $path

eps = wmii.eps
calc = rc -c 'echo 4k $* p | dc}

iconwidth  = 16
imagewidth = 154

%.pdf: %.eps
	sh epstopdf $stem.eps

%-small.png: %.eps
	epsbox = `{sed -n '/^%%BoundingBox:/{s/.*://p; q;}' $stem.eps}
	iconscale = `{*=$epsbox; $calc $iconwidth $3 $1 -/}
	iconheight = `{*=$epsbox; $calc $4 $2 - $iconscale '*'}
	* = `{hoc -e'-('$epsbox')'}
	x = $1
	y = $2
	gs -q -dBATCH -dNOPAUSE -s'DEVICE=pngalpha' -s'OutputFile='$target -g$iconwidth^x^$iconheight - <<!
		$iconscale $iconscale scale
		$x $y translate
		($eps) run
		showpage
		quit
	!
	optipng -fix $target

%.png: %.eps
	epsbox = `{sed -n '/^%%BoundingBox:/{s/.*://p; q;}' $stem.eps}
	imagescale = `{*=$epsbox; $calc $imagewidth $3 $1 -/}
	imageheight = `{*=$epsbox; $calc $4 $2 - $imagescale '*'}
	* = `{hoc -e'-('$epsbox')'}
	x = $1
	y = $2
	gs -q -dBATCH -dNOPAUSE -s'DEVICE=pngalpha' -s'OutputFile='$target -g$imagewidth^x^$imageheight - <<!
		$imagescale $imagescale scale
		$x $y translate
		($eps) run
		showpage
		quit
	!
	optipng -fix $target

Added img/wmii.eps.


























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
%!PS-Adobe-2.0 EPSF-1.2
%%BoundingBox: -1 0 51 27
%%Creator: MetaPost
%%CreationDate: 2007.02.27:1944
%%Pages: 1
%%EndProlog
%%Page: 1 1
 0 6.23616 dtransform truncate idtransform setlinewidth pop [] 0 setdash
 0 setlinecap 2 setlinejoin 10 setmiterlimit
newpath 2.83461 17.00761 moveto
2.83461 2.83461 lineto
14.17302 2.83461 lineto
14.17302 17.00761 lineto
14.17302 2.83461 lineto
25.51143 2.83461 lineto
25.51143 14.173 lineto
36.84984 14.173 lineto stroke
 0 setlinejoin
newpath 36.84984 14.173 moveto
36.84984 0 lineto
36.84984 14.173 lineto
48.18825 14.173 lineto
48.18825 0 lineto stroke
newpath 36.84984 20.40916 moveto
36.84984 26.07837 lineto stroke
newpath 48.18825 20.40916 moveto
48.18825 26.07837 lineto stroke
showpage
%%EOF
Added img/wmii.mp.






































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
beginfig(1)
%u=0.6cm;
u=0.2cm;
h=3u;
space=u;
linecap:=butt;
linejoin:=beveled;
pickup pencircle scaled 1.1u;
draw (.5u,h)--(.5u,.5u)--(1.5u+space,.5u)--(1.5u+space,h)--(1.5u+space,.5u)--(2.5u+2space,.5u)\
		--(2.5u+2space,h-.5u)--(3.5u+3space,h-.5u);
linejoin:=mitered;
draw (3.5u+3space,h-.5u)--(3.5u+3space,0)--(3.5u+3space,h-.5u)--(4.5u+4space,h-.5u)--(4.5u+4space,0);;

gap=.6u;
draw (3.5u+3space,h+gap)--(3.5u+3space,h+u+gap);
draw (4.5u+4space,h+gap)--(4.5u+4space,h+u+gap);
endfig

end
Added img/wmii.pdf.

cannot compute difference between binary files

Added img/wmii.png.

cannot compute difference between binary files

Added include/Makefile.












>
>
>
>
>
>
1
2
3
4
5
6
ROOT= ..
include $(ROOT)/mk/hdr.mk

HFILES = ixp.h ixp_fcall.h

include $(ROOT)/mk/common.mk
Added include/bio.h.














































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#ifndef _BIO_H_
#define _BIO_H_ 1

#ifdef AUTOLIB
AUTOLIB(bio)
#endif

#include <sys/types.h>	/* for off_t */
#include <fcntl.h>	/* for O_RDONLY, O_WRONLY */
#include <stdarg.h>	/* for va_list */

typedef	struct	Biobuf	Biobuf;

enum
{
	Bsize		= 8*1024,
	Bungetsize	= 4,		/* space for ungetc */
	Bmagic		= 0x314159,
	Beof		= -1,
	Bbad		= -2,

	Binactive	= 0,		/* states */
	Bractive,
	Bwactive,
	Bracteof,

	Bend
};

struct	Biobuf
{
	int	icount;		/* neg num of bytes at eob */
	int	ocount;		/* num of bytes at bob */
	int	rdline;		/* num of bytes after rdline */
	int	runesize;	/* num of bytes of last getrune */
	int	state;		/* r/w/inactive */
	int	fid;		/* open file */
	int	flag;		/* magic if malloc'ed */
	off_t	offset;		/* offset of buffer in file */
	int	bsize;		/* size of buffer */
	unsigned char*	bbuf;		/* pointer to beginning of buffer */
	unsigned char*	ebuf;		/* pointer to end of buffer */
	unsigned char*	gbuf;		/* pointer to good data in buf */
	unsigned char	b[Bungetsize+Bsize];
};

#define	BGETC(bp)\
	((bp)->icount?(bp)->bbuf[(bp)->bsize+(bp)->icount++]:Bgetc((bp)))
#define	BPUTC(bp,c)\
	((bp)->ocount?(bp)->bbuf[(bp)->bsize+(bp)->ocount++]=(c),0:Bputc((bp),(c)))
#define	BOFFSET(bp)\
	(((bp)->state==Bractive)?\
		(bp)->offset + (bp)->icount:\
	(((bp)->state==Bwactive)?\
		(bp)->offset + ((bp)->bsize + (bp)->ocount):\
		-1))
#define	BLINELEN(bp)\
	(bp)->rdline
#define	BFILDES(bp)\
	(bp)->fid

int	Bbuffered(Biobuf*);
Biobuf*	Bfdopen(int, int);
int	Bfildes(Biobuf*);
int	Bflush(Biobuf*);
int	Bgetc(Biobuf*);
int	Bgetd(Biobuf*, double*);
long	Bgetrune(Biobuf*);
int	Binit(Biobuf*, int, int);
int	Binits(Biobuf*, int, int, unsigned char*, int);
int	Blinelen(Biobuf*);
off_t	Boffset(Biobuf*);
Biobuf*	Bopen(const char*, int);
int	Bprint(Biobuf*, const char*, ...);
int	Bputc(Biobuf*, int);
int	Bputrune(Biobuf*, long);
void*	Brdline(Biobuf*, int);
char*	Brdstr(Biobuf*, int, int);
long	Bread(Biobuf*, void*, long);
off_t	Bseek(Biobuf*, off_t, int);
int	Bterm(Biobuf*);
int	Bungetc(Biobuf*);
int	Bungetrune(Biobuf*);
long	Bwrite(Biobuf*, void*, long);
int	Bvprint(Biobuf*, const char*, va_list);

#endif
Added include/fmt.h.


















































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
#ifndef _FMT_H_
#define _FMT_H_ 1
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
 * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */

#include <stdarg.h>
#include <utf.h>

typedef struct Fmt	Fmt;
struct Fmt{
	unsigned char	runes;		/* output buffer is runes or chars? */
	void	*start;			/* of buffer */
	void	*to;			/* current place in the buffer */
	void	*stop;			/* end of the buffer; overwritten if flush fails */
	int	(*flush)(Fmt *);	/* called when to == stop */
	void	*farg;			/* to make flush a closure */
	int	nfmt;			/* num chars formatted so far */
	va_list	args;			/* args passed to dofmt */
	Rune	r;			/* % format Rune */
	int	width;
	int	prec;
	unsigned long	flags;
	char	*decimal;	/* decimal point; cannot be "" */

	/* For %'d */
	char *thousands;	/* separator for thousands */
	
	/* 
	 * Each char is an integer indicating #digits before next separator. Values:
	 *	\xFF: no more grouping (or \x7F; defined to be CHAR_MAX in POSIX)
	 *	\x00: repeat previous indefinitely
	 *	\x**: count that many
	 */
	char	*grouping;		/* descriptor of separator placement */
};

enum{
	FmtWidth	= 1,
	FmtLeft		= FmtWidth << 1,
	FmtPrec		= FmtLeft << 1,
	FmtSharp	= FmtPrec << 1,
	FmtSpace	= FmtSharp << 1,
	FmtSign		= FmtSpace << 1,
	FmtApost		= FmtSign << 1,
	FmtZero		= FmtApost << 1,
	FmtUnsigned	= FmtZero << 1,
	FmtShort	= FmtUnsigned << 1,
	FmtLong		= FmtShort << 1,
	FmtVLong	= FmtLong << 1,
	FmtComma	= FmtVLong << 1,
	FmtByte		= FmtComma << 1,
	FmtLDouble	= FmtByte << 1,

	FmtFlag		= FmtLDouble << 1
};

extern	int	(*fmtdoquote)(int);

#ifdef VARARGCK
/* *sigh* */
  typedef unsigned char		_fmt_uchar;
  typedef unsigned short	_fmt_ushort;
  typedef unsigned int		_fmt_uint;
  typedef unsigned long		_fmt_ulong;
  typedef unsigned long long	_fmt_uvlong;
  typedef long long		_fmt_vlong;
# pragma varargck	argpos	fmtprint	2
# pragma varargck	argpos	fprint		2
# pragma varargck	argpos	print		1
# pragma varargck	argpos	runeseprint	3
# pragma varargck	argpos	runesmprint	1
# pragma varargck	argpos	runesnprint	3
# pragma varargck	argpos	runesprint	2
# pragma varargck	argpos	seprint		3
# pragma varargck	argpos	smprint		1
# pragma varargck	argpos	snprint		3
# pragma varargck	argpos	sprint		2

# pragma varargck	type	"lld"	_fmt_vlong
# pragma varargck	type	"llx"	_fmt_vlong
# pragma varargck	type	"lld"	_fmt_uvlong
# pragma varargck	type	"llx"	_fmt_uvlong
# pragma varargck	type	"ld"	long
# pragma varargck	type	"lx"	long
# pragma varargck	type	"lb"	long
# pragma varargck	type	"ld"	_fmt_ulong
# pragma varargck	type	"lx"	_fmt_ulong
# pragma varargck	type	"lb"	_fmt_ulong
# pragma varargck	type	"d"	int
# pragma varargck	type	"x"	int
# pragma varargck	type	"c"	int
# pragma varargck	type	"C"	int
# pragma varargck	type	"b"	int
# pragma varargck	type	"d"	_fmt_uint
# pragma varargck	type	"x"	_fmt_uint
# pragma varargck	type	"c"	_fmt_uint
# pragma varargck	type	"C"	_fmt_uint
# pragma varargck	type	"b"	_fmt_uint
# pragma varargck	type	"f"	double
# pragma varargck	type	"e"	double
# pragma varargck	type	"g"	double
# pragma varargck	type	"s"	char*
# pragma varargck	type	"q"	char*
# pragma varargck	type	"S"	Rune*
# pragma varargck	type	"Q"	Rune*
# pragma varargck	type	"r"	void
# pragma varargck	type	"%"	void
# pragma varargck	type	"n"	int*
# pragma varargck	type	"p"	uintptr_t
# pragma varargck	type	"p"	void*
# pragma varargck	flag	','
# pragma varargck	flag	'h'
# pragma varargck	type	"<"	void*
# pragma varargck	type	"["	void*
# pragma varargck	type	"H"	void*
# pragma varargck	type	"lH"	void*
#endif

/* Edit .+1,/^$/ | cfn $PLAN9/src/lib9/fmt/?*.c | grep -v static |grep -v __ */
int		dofmt(Fmt*, const char *fmt);
int		dorfmt(Fmt*, const Rune *fmt);
double		fmtcharstod(int(*f)(void*), void*);
int		fmtfdflush(Fmt*);
int		fmtfdinit(Fmt*, int fd, char *buf, int size);
int		fmtinstall(int, int (*f)(Fmt*));
void		fmtlocaleinit(Fmt*, char *decimal, char *thousands, char *grouping);
int		fmtprint(Fmt*, const char*, ...);
int		fmtrune(Fmt*, int);
int		fmtrunestrcpy(Fmt*, Rune*);
int		fmtstrcpy(Fmt*, const char*);
char*		fmtstrflush(Fmt*);
int		fmtstrinit(Fmt*);
double		fmtstrtod(const char*, char**);
int		fmtvprint(Fmt*, const char*, va_list);
int		fprint(int, const char*, ...);
int		print(const char*, ...);
void		quotefmtinstall(void);
int		quoterunestrfmt(Fmt*);
int		quotestrfmt(Fmt*);
Rune*		runefmtstrflush(Fmt*);
int		runefmtstrinit(Fmt*);
Rune*		runeseprint(Rune*,Rune*, const char*, ...);
Rune*		runesmprint(const char*, ...);
int		runesnprint(Rune*, int, const char*, ...);
int		runesprint(Rune*, const char*, ...);
Rune*		runevseprint(Rune*, Rune *, const char*, va_list);
Rune*		runevsmprint(const char*, va_list);
int		runevsnprint(Rune*, int, const char*, va_list);
char*		seprint(char*, char*, const char*, ...);
char*		smprint(const char*, ...);
int		snprint(char*, int, const char *, ...);
int		sprint(char*, const char*, ...);
int		vfprint(int, const char*, va_list);
char*		vseprint(char*, char*, const char*, va_list);
char*		vsmprint(const char*, va_list);
int		vsnprint(char*, int, const char*, va_list);

#endif
Added include/plan9.h.


























































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/*
 * compiler directive on Plan 9
 */

/*
 * easiest way to make sure these are defined
 */
#ifndef KENC
# ifndef USED
#  define USED(x) if(x);else
# endif
#endif

#define uchar	_p9uchar
#define ushort	_p9ushort
#define uint	_p9uint
#define ulong	_p9ulong
typedef unsigned char		uchar;
typedef unsigned short		ushort;
typedef unsigned int		uint;
typedef unsigned long		ulong;
typedef long long		vlong;
typedef unsigned long long	uvlong;

#include <utf.h>
#include <stdint.h>
#include <fmt.h>
#include <string.h>
#include <unistd.h>

#define OREAD		O_RDONLY
#define OWRITE	O_WRONLY

#define	OCEXEC 0
#define	ORCLOSE	0
#define	OTRUNC	0

#define	exits(x)	exit(x && *x ? 1 : 0)

#undef	nil
#define	nil	((void*)0)

#undef	nelem
#define	nelem(x)	(sizeof (x)/sizeof (x)[0])

Added include/regcomp.h.














































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
/*
 *  substitution list
 */

#define NSUBEXP 32
typedef struct Resublist	Resublist;
struct	Resublist
{
	Resub	m[NSUBEXP];
};

/* max character classes per program */
extern Reprog	RePrOg;
#define	NCLASS	(sizeof(RePrOg.class)/sizeof(Reclass))

/* max rune ranges per character class */
#define NCCRUNE	(sizeof(Reclass)/sizeof(Rune))

/*
 * Actions and Tokens (Reinst types)
 *
 *	02xx are operators, value == precedence
 *	03xx are tokens, i.e. operands for operators
 */
#define RUNE		0177
#define	OPERATOR	0200	/* Bitmask of all operators */
#define	START		0200	/* Start, used for marker on stack */
#define	RBRA		0201	/* Right bracket, ) */
#define	LBRA		0202	/* Left bracket, ( */
#define	OR		0203	/* Alternation, | */
#define	CAT		0204	/* Concatentation, implicit operator */
#define	STAR		0205	/* Closure, * */
#define	PLUS		0206	/* a+ == aa* */
#define	QUEST		0207	/* a? == a|nothing, i.e. 0 or 1 a's */
#define	ANY		0300	/* Any character except newline, . */
#define	ANYNL		0301	/* Any character including newline, . */
#define	NOP		0302	/* No operation, internal use only */
#define	BOL		0303	/* Beginning of line, ^ */
#define	EOL		0304	/* End of line, $ */
#define	CCLASS		0305	/* Character class, [] */
#define	NCCLASS		0306	/* Negated character class, [] */
#define	END		0377	/* Terminate: match found */

/*
 *  regexec execution lists
 */
#define LISTSIZE	10
#define BIGLISTSIZE	(10*LISTSIZE)
typedef struct Relist	Relist;
struct Relist
{
	Reinst*		inst;		/* Reinstruction of the thread */
	Resublist	se;		/* matched subexpressions in this thread */
};
typedef struct Reljunk	Reljunk;
struct	Reljunk
{
	Relist*	relist[2];
	Relist*	reliste[2];
	int	starttype;
	Rune	startchar;
	char*	starts;
	char*	eol;
	Rune*	rstarts;
	Rune*	reol;
};

extern Relist*	_renewthread(Relist*, Reinst*, int, Resublist*);
extern void	_renewmatch(Resub*, int, Resublist*);
extern Relist*	_renewemptythread(Relist*, Reinst*, int, char*);
extern Relist*	_rrenewemptythread(Relist*, Reinst*, int, Rune*);
Added include/regexp9.h.
















































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#ifndef _REGEXP9_H_
#define _REGEXP9_H_ 1

#ifdef AUTOLIB
AUTOLIB(regexp9)
#endif

#include <utf.h>

typedef struct Resub		Resub;
typedef struct Reclass		Reclass;
typedef struct Reinst		Reinst;
typedef struct Reprog		Reprog;

/*
 *	Sub expression matches
 */
struct Resub{
	union {
		char *sp;
		Rune *rsp;
	}s;
	union {
		char *ep;
		Rune *rep;
	}e;
};

/*
 *	character class, each pair of rune's defines a range
 */
struct Reclass{
	Rune	*end;
	Rune	spans[64];
};

/*
 *	Machine instructions
 */
struct Reinst{
	int	type;
	union	{
		Reclass	*cp;		/* class pointer */
		Rune	r;		/* character */
		int	subid;		/* sub-expression id for RBRA and LBRA */
		Reinst	*right;		/* right child of OR */
	}u1;
	union {	/* regexp relies on these two being in the same union */
		Reinst *left;		/* left child of OR */
		Reinst *next;		/* next instruction for CAT & LBRA */
	}u2;
};

/*
 *	Reprogram definition
 */
struct Reprog{
	Reinst	*startinst;	/* start pc */
	Reclass	class[32];	/* .data */
	Reinst	firstinst[5];	/* .text */
};

extern Reprog	*regcomp9(char*);
extern Reprog	*regcomplit9(char*);
extern Reprog	*regcompnl9(char*);
extern void	regerror9(char*);
extern int	regexec9(Reprog*, char*, Resub*, int);
extern void	regsub9(char*, char*, int, Resub*, int);

extern int	rregexec9(Reprog*, Rune*, Resub*, int);
extern void	rregsub9(Rune*, Rune*, int, Resub*, int);

/*
 * Darwin simply cannot handle having routines that
 * override other library routines.
 */
#ifndef NOPLAN9DEFINES
#define regcomp regcomp9
#define regcomplit regcomplit9
#define regcompnl regcompnl9
#define regerror regerror9
#define regexec regexec9
#define regsub regsub9
#define rregexec rregexec9
#define rregsub rregsub9
#endif

#endif
Added include/stuff/base.h.


















































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#pragma once
#define _XOPEN_SOURCE 600

#include <sys/types.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdint.h>

#ifndef nil
#  define nil	((void*)0)
#endif

#ifndef nelem
#  define nelem(ary) (sizeof(ary) / sizeof(*ary))
#endif

#ifndef offsetof
#  define offsetof(type, member) ((size_t)&((type*)0)->member)
#endif
#define structptr(ptr, type, offset) \
	((type*)((char*)(ptr) + (offset)))
#define structmember(ptr, type, offset) \
	(*structptr(ptr, type, offset))

#undef uchar
#undef ushort
#undef uint
#undef ulong
#undef uvlong
#undef vlong
#define uchar	_x_uchar
#define ushort	_x_ushort
#define uint	_x_uint
#define ulong	_x_ulong
#define uvlong	_x_uvlong
#define vlong	_x_vlong

typedef unsigned char		uchar;
typedef unsigned short		ushort;
typedef unsigned int		uint;
typedef unsigned long		ulong;
typedef unsigned long long	uvlong;
typedef long long	vlong;

#define BLOCK(x) do { x; }while(0)

static inline void
_used(long a, ...) {
	if(a){}
}
#ifndef KENC
#  undef USED
#  undef SET
#  define USED(...) _used((long)__VA_ARGS__)
#  define SET(x) (x = 0)
/* # define SET(x) USED(&x) GCC 4 is 'too smart' for this. */
#endif
Added include/stuff/clientutil.h.


























>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
/* Copyright ©2009-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */

#ifndef CLIENTEXTERN
#  define CLIENTEXTERN extern
#endif

char*	readctl(char*, char*);
void	client_init(char*);

CLIENTEXTERN IxpClient*	client;

Added include/stuff/geom.h.












































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#pragma once

#include <stuff/base.h>

typedef struct Point Point;
typedef struct Rectangle Rectangle;

struct Point {
	int x, y;
};

struct Rectangle {
	Point min, max;
};

enum Align {
	North = 0x01,
	East  = 0x02,
	South = 0x04,
	West  = 0x08,
	NEast = North | East,
	NWest = North | West,
	SEast = South | East,
	SWest = South | West,
	Center = NEast | SWest,
};

typedef enum Align Align;

#define Dx(r) ((r).max.x - (r).min.x)
#define Dy(r) ((r).max.y - (r).min.y)
#define Pt(x, y) ((Point){(x), (y)})
#define Rpt(p, q) ((Rectangle){(p), (q)})
#define Rect(x0, y0, x1, y1) Rpt(Pt(x0, y0), Pt(x1, y1))

Point		addpt(Point, Point);
Point		divpt(Point, Point);
int		eqpt(Point, Point);
int		eqrect(Rectangle, Rectangle);
Rectangle	gravitate(Rectangle dst, Rectangle src, Point grav);
Rectangle	insetrect(Rectangle, int);
Point		mulpt(Point p, Point q);
Rectangle	rectaddpt(Rectangle, Point);
Rectangle	rectsetorigin(Rectangle, Point);
Rectangle	rectsubpt(Rectangle, Point);
Point		subpt(Point, Point);

Align		get_sticky(Rectangle src, Rectangle dst);
Align		quadrant(Rectangle, Point);
bool		rect_contains_p(Rectangle, Rectangle);
bool		rect_haspoint_p(Rectangle, Point);
bool		rect_intersect_p(Rectangle, Rectangle);
Rectangle	rect_intersection(Rectangle, Rectangle);

Added include/stuff/util.h.
























































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */

#include <stuff/geom.h>
#include <langinfo.h>
#include <stdarg.h>
#include <bio.h>
#include <fmt.h>
#include <regexp9.h>

/* Types */

typedef struct Regex Regex;

struct Regex {
	char*	regex;
	Reprog*	regc;
};

enum {
	CLeft = 1<<0,
	CCenter = 1<<1,
	CRight = 1<<2,
};

enum {
	GInvert = 1<<0,
};

#define utf8locale() (!strcmp(nl_langinfo(CODESET), "UTF-8"))

#ifdef VARARGCK
# pragma varargck	argpos	_die	3
# pragma varargck	argpos	fatal	1
# pragma varargck	argpos	sxprint	1
#endif

#define strlcat stuff_strlcat
#define strcasestr stuff_strcasestr


int	Blprint(Biobuf*, const char*, ...);
int	Bvlprint(Biobuf*, const char*, va_list);
void	_die(char*, int, char*, ...);
void	backtrace(char*);
void	closeexec(int);
char**	comm(int, char**, char**);
int	doublefork(void);
void*	emalloc(uint);
void*	emallocz(uint);
void*	erealloc(void*, uint);
char*	estrdup(const char*);
char*	estrndup(const char*, uint);
void	fatal(const char*, ...);
Fmt	fmtbuf(char*, int);
void*	freelater(void*);
int	getbase(const char**, long*);
bool	getint(const char*, int*);
bool	getlong(const char*, long*);
bool	getulong(const char*, ulong*);
void	grep(char**, Reprog*, int);
char*	join(char**, char*, Fmt*);
int	localefmt(Fmt*);
void	localefmtinstall(void);
int	localelen(char*, char*);
int	lprint(int, const char*, ...);
int	max(int, int);
int	min(int, int);
uvlong	nsec(void);
char*	pathsearch(const char*, const char*, bool);
void	refree(Regex*);
void	reinit(Regex*, char*);
int	spawn3(int[3], const char*, char*[]);
int	spawn3l(int[3], const char*, ...);
uint	stokenize(char**, uint, char*, char*);
char*	strcasestr(const char*, const char*);
char*	strend(char*, int);
uint	strlcat(char*, const char*, uint);
int	strlcatprint(char*, int, const char*, ...);
char*	sxprint(const char*, ...);
uint	tokenize(char**, uint, char*, char);
void	trim(char *str, const char *chars);
void	uniq(char**);
int	unmask(Fmt*, long, char**, long);
int	unquote(char*, char*[], int);
int	utflcpy(char*, const char*, int);
int	vlprint(int, const char*, va_list);
char*	vsxprint(const char*, va_list);

extern char*	_buffer;
extern char	buffer[8092];
extern char*	const _buf_end;
#define bufclear() \
	BLOCK( _buffer = buffer; _buffer[0] = '\0' )
#define bufprint(...) \
	_buffer = seprint(_buffer, _buf_end, __VA_ARGS__)

#define die(...) \
	_die(__FILE__, __LINE__, __VA_ARGS__)

char *argv0;
#undef ARGBEGIN
#undef ARGEND
#undef ARGF
#undef EARGF
#define ARGBEGIN \
		int _argtmp=0, _inargv; char *_argv=nil;        \
		if(!argv0) argv0=*argv; argv++, argc--;         \
		_inargv=1; USED(_inargv);		        \
		while(argc && argv[0][0] == '-') {              \
			_argv=&argv[0][1]; argv++; argc--;      \
			if(_argv[0] == '-' && _argv[1] == '\0') \
				break; \
			while(*_argv) switch(*_argv++)
#define ARGEND }_inargv=0;USED(_argtmp, _argv, _inargv)

#define EARGF(f) ((_inargv && *_argv) ? \
			(_argtmp=strlen(_argv), _argv+=_argtmp, _argv-_argtmp) \
			: ((argc > 0) ? \
				(--argc, ++argv, _used(argc), *(argv-1)) \
				: ((f), (char*)0)))
#define ARGF() EARGF(_used(0))

/* map.c */
typedef struct Map Map;
typedef struct MapEnt MapEnt;

struct Map {
	MapEnt**bucket;
	uint	nhash;
	uint	nmemb;
};

void**	hash_get(Map*, const char*, bool create);
void*	hash_rm(Map*, const char*);
void**	map_get(Map*, ulong, bool create);
void*	map_rm(Map*, ulong);

/* Yuck. */
#define VECTOR(type, nam, c) \
typedef struct Vector_##nam Vector_##nam;      \
struct Vector_##nam {                          \
	type*	ary;                           \
	long	n;                             \
	long	size;                          \
};                                             \
void	vector_##c##free(Vector_##nam*);       \
void	vector_##c##init(Vector_##nam*);       \
void	vector_##c##push(Vector_##nam*, type); \

VECTOR(long, long, l)
VECTOR(Rectangle, rect, r)
VECTOR(void*, ptr, p)
#undef VECTOR

Added include/stuff/x.h.


































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#include <stuff/base.h>
#include <stuff/x11.h>
#include <fmt.h>

extern void	init_screens(void);

#define Net(x) ("_NET_" x)
#define	Action(x) ("_NET_WM_ACTION_" x)
#define	State(x) ("_NET_WM_STATE_" x)
#define	Type(x) ("_NET_WM_WINDOW_TYPE_" x)
#define NET(x) xatom(Net(x))
#define	ACTION(x) xatom(Action(x))
#define	STATE(x) xatom(State(x))
#define	TYPE(x) xatom(Type(x))

/* printevent.c */
int	fmtevent(Fmt*);

int	fmtkey(Fmt*);

/* xext.c */
void	randr_event(XEvent*);
bool	render_argb_p(Visual*);
void	xext_event(XEvent*);
void	xext_init(void);
Rectangle*	xinerama_screens(int*);

void	client_readconfig(CTuple*, CTuple*, Font**);

#define event_handle(w, fn, ev) \
	_event_handle(w, offsetof(Handlers, fn), (XEvent*)ev)

void	_event_handle(Window*, ulong, XEvent*);

void	event_check(void);
void	event_dispatch(XEvent*);
uint	event_flush(long, bool dispatch);
uint	event_flushenter(void);
void	event_loop(void);
#ifdef IXP_API /* Evil. */
void	event_fdclosed(IxpConn*);
void	event_fdready(IxpConn*);
void	event_preselect(IxpServer*);
#endif
long	event_updatextime(void);

void	event_buttonpress(XButtonPressedEvent*);
void	event_buttonrelease(XButtonPressedEvent*);
void	event_clientmessage(XClientMessageEvent*);
void	event_configurenotify(XConfigureEvent*);
void	event_configurerequest(XConfigureRequestEvent*);
void	event_destroynotify(XDestroyWindowEvent*);
void	event_enternotify(XCrossingEvent*);
void	event_expose(XExposeEvent*);
void	event_focusin(XFocusChangeEvent*);
void	event_focusout(XFocusChangeEvent*);
void	event_keypress(XKeyEvent*);
void	event_keyrelease(XKeyEvent*);
void	event_leavenotify(XCrossingEvent*);
void	event_mapnotify(XMapEvent*);
void	event_mappingnotify(XMappingEvent*);
void	event_maprequest(XMapRequestEvent*);
void	event_motionnotify(XMotionEvent*);
void	event_propertynotify(XPropertyEvent*);
void	event_reparentnotify(XReparentEvent *ev);
void	event_selection(XSelectionEvent*);
void	event_selectionclear(XSelectionClearEvent*);
void	event_selectionrequest(XSelectionRequestEvent*);
void	event_unmapnotify(XUnmapEvent*);

extern long	event_lastconfigure;
extern long	event_xtime;
extern bool	event_looprunning;
extern void	(*event_debug)(XEvent*);

extern Visual*	render_visual;
extern Colormap	render_colormap;
extern bool	have_RandR;
extern bool	have_render;
extern bool	have_xinerama;

Added include/stuff/x11.h.












































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#define Window XWindow
#define Font XFont
#define Screen XScreen
#define Mask XMask
#include <stuff/geom.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/extensions/Xrender.h>
#ifdef _X11_VISIBLE
#  include <X11/Xatom.h>
#  include <X11/extensions/shape.h>
#  include <X11/extensions/Xrandr.h>
#endif
#undef Window
#undef Font
#undef Screen
#undef Mask

enum FontType {
	FX11 = 1,
	FFontSet,
	FXft,
};

enum WindowType {
	WWindow,
	WImage,
};

typedef enum FontType FontType;
typedef enum WindowType WindowType;

typedef XSetWindowAttributes WinAttr;

typedef union ClientMessageData ClientMessageData;
typedef struct Color Color;
typedef struct CTuple CTuple;
typedef struct ErrorCode ErrorCode;
typedef struct Ewmh Ewmh;
typedef struct Font Font;
typedef struct Handlers Handlers;
typedef struct HandlersLink HandlersLink;
typedef struct Screen Screen;
typedef struct WinHints WinHints;
typedef struct Window Image;
typedef struct Window Window;
typedef struct Xft Xft;
typedef struct XftColor XftColor;
typedef void XftDraw;
typedef struct XftFont XftFont;

union ClientMessageData {
	char b[20];
	short s[10];
	long l[5];
};

struct Color {
	ushort	red;
	ushort	green;
	ushort	blue;
	ushort	alpha;
	ulong	pixel;
};

struct CTuple {
	Color bg;
	Color fg;
	Color border;
	char colstr[64];
};

struct ErrorCode {
	uchar rcode;
	uchar ecode;
};

struct Ewmh {
	long	type;
	ulong	ping;
	ulong	lag;
};

struct Font {
	int	type;
	union {
		XFontStruct*	x11;
		XFontSet	set;
		XftFont*	xft;
	} font;
	Rectangle pad;
	int	ascent;
	int	descent;
	uint	height;
	char*	name;
};

struct Handlers {
	Rectangle (*dndmotion)(Window*, void*, Point);
	bool (*bdown)(Window*, void*, XButtonEvent*);
	bool (*bup)(Window*, void*, XButtonEvent*);
	bool (*config)(Window*, void*, XConfigureEvent*);
	bool (*configreq)(Window*, void*, XConfigureRequestEvent*);
	bool (*destroy)(Window*, void*, XDestroyWindowEvent*);
	bool (*enter)(Window*, void*, XCrossingEvent*);
	bool (*expose)(Window*, void*, XExposeEvent*);
	bool (*focusin)(Window*, void*, XFocusChangeEvent*);
	bool (*focusout)(Window*, void*, XFocusChangeEvent*);
	bool (*kdown)(Window*, void*, XKeyEvent*);
	bool (*kup)(Window*, void*, XKeyEvent*);
	bool (*leave)(Window*, void*, XCrossingEvent*);
	bool (*map)(Window*, void*, XMapEvent*);
	bool (*mapreq)(Window*, void*, XMapRequestEvent*);
	bool (*message)(Window*, void*, XClientMessageEvent*);
	bool (*motion)(Window*, void*, XMotionEvent*);
	bool (*property)(Window*, void*, XPropertyEvent*);
	bool (*reparent)(Window*, void*, XReparentEvent*);
	bool (*selection)(Window*, void*, XSelectionEvent*);
	bool (*selectionclear)(Window*, void*, XSelectionClearEvent*);
	bool (*selectionrequest)(Window*, void*, XSelectionRequestEvent*);
	bool (*unmap)(Window*, void*, XUnmapEvent*);
};

struct HandlersLink {
	HandlersLink*	next;
	void*		aux;
	Handlers*	handler;
};

struct WinHints {
	Point	min;
	Point	max;
	Point	base;
	Point	baspect;
	Point	inc;
	Point	grav;
	Rectangle aspect;
	XWindow	group;
	bool	gravstatic;
	bool	position;
};

struct Window {
	int		type;
	XID		xid;
	XIC		xic;
	GC		gc;
	Visual*		visual;
	Colormap	colormap;
	XftDraw*	xft;
	Rectangle	r;
	int		border;
	Window*		parent;
	Window*		next;
	Window*		prev;
	Handlers*	handler;
	HandlersLink*	handler_link;
	WinHints*	hints;
	Ewmh		ewmh;
	long		eventmask;
	void*		dnd;
	void*		aux;
	bool		mapped;
	int		unmapped;
	int		depth;
};

struct Xft {
	XftDraw*	(*drawcreate)(Display*, Drawable, Visual*, Colormap);
	void		(*drawdestroy)(XftDraw*);
	XftFont*	(*fontopen)(Display*, int, const char*);
	XftFont*	(*fontopenname)(Display*, int, const char*);
	XftFont*	(*fontclose)(Display*, XftFont*);
	void		(*textextents)(Display*, XftFont*, const char*, int len, XGlyphInfo*);
	void		(*drawstring)(Display*, XftColor*, XftFont*, int x, int y, const char*, int len);
};

struct XftColor {
    ulong		pixel;
    XRenderColor	color;
};

struct XftFont {
    int		ascent;
    int		descent;
    int		height;
    int		max_advance_width;
    void*	charset;
    void*	pattern;
};

struct Screen {
	int		screen;
	Window		root;
	GC		gc;
	Colormap	colormap;
	Colormap	colormap32;
	Visual*		visual;
	Visual*		visual32;
	Rectangle	rect;
	int		depth;
	int		fd;
	XIM		xim;
};

#ifdef VARARGCK
# pragma varargck	type	"A"	Atom
# pragma varargck	type	"P"	Point
# pragma varargck	type	"R"	Rectangle
# pragma varargck	type	"W"	Window*
#endif

Display *display;
Screen scr;

extern char*		modkey_names[];
extern struct Map	windowmap;
extern struct Map	atommap;
extern struct Map	atomnamemap;
extern const Point ZP;
extern const Rectangle ZR;
extern const WinHints ZWinHints;
extern Window* pointerwin;
extern Xft* xft;

XRectangle XRect(Rectangle r);

#define RGBA_P(tuple) (\
	((long)(tuple).fg.alpha + (long)(tuple).bg.alpha + (long)(tuple).border.alpha) < 3 * 0xff00)

#define changeprop(w, prop, type, data, n) \
	changeproperty(w, prop, type, \
		((sizeof(*(data)) == 8 ? 4 : sizeof(*(data))) * 8), \
		(uchar*)(data), n)

/* x11.c */
XRectangle	XRect(Rectangle);
Image*	allocimage(int w, int h, int depth);
char*	atomname(ulong);
void	border(Image *dst, Rectangle, int w, Color*);
void	changeprop_char(Window*, const char*, const char*, const char*, int);
void	changeprop_long(Window*, const char*, const char*, long[], int);
void	changeprop_short(Window*, const char*, const char*, short[], int);
void	changeprop_string(Window*, const char*, const char*);
void	changeprop_textlist(Window*, const char*, const char*, char*[]);
void	changeprop_ulong(Window*, const char*, const char*, ulong[], int);
void	changeproperty(Window*, const char*, const char*, int width, const uchar*, int);
void	cleanupwindow(Window*);
void	clientmessage(Window*, const char*, long, int, ClientMessageData);
void	copyimage(Image*, Rectangle, Image*, Point);
Window*	createwindow(Window*, Rectangle, int depth, uint class, WinAttr*, int valuemask);
Window*	createwindow_rgba(Window*, Rectangle, WinAttr*, int valuemask);
Window*	createwindow_visual(Window*, Rectangle, int depth, Visual*, uint class, WinAttr*, int);
void	delproperty(Window*, const char*);
void	destroywindow(Window*);
void	drawline(Image*, Point, Point, int cap, int w, Color*);
void	drawpoly(Image*, Point*, int, int cap, int w, Color*);
uint	drawstring(Image*, Font*, Rectangle, Align, const char*, Color*);
void	fill(Image*, Rectangle, Color*);
void	fillpoly(Image*, Point*, int, Color*);
uint	fillstring(Image*, Font*, Rectangle, Align, const char*, CTuple*, int border);
Window*	findwin(XWindow);
void	freefont(Font*);
void	freeimage(Image *);
void	freestringlist(char**);
XWindow	getfocus(void);
void	gethints(Window*);
ulong	getprop(Window*, const char*, const char*, Atom*, int*, ulong, uchar**, ulong);
ulong	getprop_long(Window*, const char*, const char*, ulong, long**, ulong);
char*	getprop_string(Window*, const char*);
int	getprop_textlist(Window *w, const char *name, char **ret[]);
ulong	getprop_ulong(Window*, const char*, const char*, ulong, ulong**, ulong);
ulong	getproperty(Window*, char *prop, char *type, Atom *actual, ulong offset, uchar **ret, ulong length);
Rectangle	getwinrect(Window*);
int	grabkeyboard(Window*);
int	grabpointer(Window*, Window *confine, Cursor, int mask);
void	getselection(char*, void (*)(void*, char*), void*);
bool	havexft(void);
void	initdisplay(void);
KeyCode	keycode(const char*);
uint	labelh(Font*);
int	loadcolor(CTuple*, const char*, const char*);
Font*	loadfont(const char*);
void	lowerwin(Window*);
int	mapwin(Window*);
void	movewin(Window*, Point);
int	numlockmask(void);
bool	parsecolor(const char *name, Color*);
bool	parsekey(char*, int*, char**);
ulong	pixelvalue(Image*, Color*);
int	pointerscreen(void);
bool	pophandler(Window*, Handlers*);
void	pushhandler(Window*, Handlers*, void*);
Point	querypointer(Window*);
void	raisewin(Window*);
void	reparentwindow(Window*, Window*, Point);
void	reshapewin(Window*, Rectangle);
void	selectinput(Window*, long);
void	sendevent(Window*, bool propagate, long mask, void*);
void	sendmessage(Window*, const char*, long, long, long, long, long);
void	setborder(Window*, int, Color*);
void	setfocus(Window*, int mode);
Handlers*	sethandler(Window*, Handlers*);
void	sethints(Window*, WinHints*);
void	setshapemask(Window *dst, Image *src, Point);
void	setwinattr(Window*, WinAttr*, int valmask);
Rectangle	sizehint(WinHints*, Rectangle);
char**	strlistdup(char**);
void	sync(void);
Rectangle	textextents_l(Font*, const char*, uint, int*);
uint	textwidth(Font*, const char*);
uint	textwidth_l(Font*, const char*, uint len);
Point	translate(Window*, Window*, Point);
int	traperrors(bool);
void	ungrabkeyboard(void);
void	ungrabpointer(void);
int	unmapwin(Window*);
void	warppointer(Point);
Window*	window(XWindow);
char*	windowname(Window*);
long	winprotocols(Window*);
Atom	xatom(const char*);

Added include/utf.h.
































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#ifndef _UTF_H_
#define _UTF_H_ 1

typedef unsigned int Rune;	/* 32 bits */

enum
{
	UTFmax		= 4,		/* maximum bytes per rune */
	Runesync	= 0x80,		/* cannot represent part of a UTF sequence (<) */
	Runeself	= 0x80,		/* rune and UTF sequences are the same (<) */
	Runeerror	= 0xFFFD,	/* decoding error in UTF */
	Runemax		= 0x10FFFF	/* maximum rune value */
};

/* Edit .+1,/^$/ | cfn $PLAN9/src/lib9/utf/?*.c | grep -v static |grep -v __ */
int		chartorune(Rune *rune, const char *str);
int		fullrune(const char *str, int n);
int		isalpharune(Rune);
int		islowerrune(Rune);
int		isspacerune(Rune);
int		istitlerune(Rune);
int		isupperrune(Rune);
int		runelen(long);
int		runenlen(const Rune*, int);
Rune*		runestrcat(Rune*, const Rune*);
Rune*		runestrchr(const Rune*, Rune);
int		runestrcmp(const Rune*, const Rune*);
Rune*		runestrcpy(Rune*, const Rune*);
Rune*		runestrdup(const Rune*) ;
Rune*		runestrecpy(Rune*, Rune *e, const Rune*);
long		runestrlen(const Rune*);
Rune*		runestrncat(Rune*, const Rune*, long);
int		runestrncmp(const Rune*, const Rune*, long);
Rune*		runestrncpy(Rune*, const Rune*, long);
Rune*		runestrrchr(const Rune*, Rune);
Rune*		runestrstr(const Rune*, const Rune*);
int		runetochar(char*, const Rune*);
Rune		tolowerrune(Rune);
Rune		totitlerune(Rune);
Rune		toupperrune(Rune);
char*		utfecpy(char*, char*, const char*);
int		utflen(const char*);
int		utfnlen(const char*, long);
char*		utfrrune(const char*, long);
char*		utfrune(const char*, long);
char*		utfutf(const char*, const char*);

#endif
Added lib/Makefile.




































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
ROOT=..
include $(ROOT)/mk/hdr.mk
include $(ROOT)/mk/wmii.mk

PDIRS = \
	libwmii_hack

DIRS =	\
	libstuff  \
	libbio    \
	libfmt	  \
	libregexp \
	libutf	  \
	$(PDIRS)

include $(ROOT)/mk/dir.mk
INSTDIRS = $(PDIRS)

Added lib/libbio/Makefile.






















































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
ROOT= ../..
include $(ROOT)/mk/hdr.mk

VERSION=2.0
TARG=libbio

OBJ=\
	bbuffered\
	bfildes\
	bflush\
	bgetc\
	bgetd\
	bgetrune\
	binit\
	boffset\
	bprint\
	bvprint\
	bputc\
	bputrune\
	brdline\
	brdstr\
	bread\
	bseek\
	bwrite

include $(ROOT)/mk/lib.mk

Added lib/libbio/NOTICE.




































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
This copyright NOTICE applies to all files in this directory and
subdirectories, unless another copyright notice appears in a given
file or subdirectory.  If you take substantial code from this software to use in
other programs, you must somehow include with it an appropriate
copyright notice that includes the copyright notice and the other
notices below.  It is fine (and often tidier) to do that in a separate
file such as NOTICE, LICENCE or COPYING.

	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
	Revisions Copyright © 2000-2005 Vita Nuova Holdings Limited (www.vitanuova.com).  All rights reserved.

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.

----

This software is also made available under the Lucent Public License
version 1.02; see http://plan9.bell-labs.com/plan9dist/license.html

Added lib/libbio/README.










>
>
>
>
>
1
2
3
4
5
This software was packaged for Unix by Russ Cox.
Please send comments to rsc@swtch.com.

http://swtch.com/plan9port/unix

Added lib/libbio/bbuffered.c.








































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include	"plan9.h"
#include	<bio.h>

int
Bbuffered(Biobuf *bp)
{
	switch(bp->state) {
	case Bracteof:
	case Bractive:
		return -bp->icount;

	case Bwactive:
		return bp->bsize + bp->ocount;

	case Binactive:
		return 0;
	}
	fprint(2, "Bbuffered: unknown state %d\n", bp->state);
	return 0;
}
Added lib/libbio/bcat.c.




























































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <fmt.h>
#include "bio.h"

Biobuf bout;

void
bcat(Biobuf *b, char *name)
{
	char buf[1000];
	int n;

	while((n = Bread(b, buf, sizeof buf)) > 0){
		if(Bwrite(&bout, buf, n) < 0)
			fprint(2, "writing during %s: %r\n", name);
	}
	if(n < 0)
		fprint(2, "reading %s: %r\n", name);
}

int
main(int argc, char **argv)
{
	int i;
	Biobuf b, *bp;
	Fmt fmt;

	Binit(&bout, 1, O_WRONLY);
	Bfmtinit(&fmt, &bout);
	fmtprint(&fmt, "hello, world\n");
	Bfmtflush(&fmt);

	if(argc == 1){
		Binit(&b, 0, O_RDONLY);
		bcat(&b, "<stdin>");
	}else{
		for(i=1; i<argc; i++){
			if((bp = Bopen(argv[i], O_RDONLY)) == 0){
				fprint(2, "Bopen %s: %r\n", argv[i]);
				continue;
			}
			bcat(bp, argv[i]);
			Bterm(bp);
		}
	}
	exit(0);
}
Added lib/libbio/bfildes.c.


















>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
#include	"plan9.h"
#include	<bio.h>

int
Bfildes(Biobuf *bp)
{

	return bp->fid;
}
Added lib/libbio/bflush.c.


































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include	"plan9.h"
#include	<bio.h>

int
Bflush(Biobuf *bp)
{
	int n, c;

	switch(bp->state) {
	case Bwactive:
		n = bp->bsize+bp->ocount;
		if(n == 0)
			return 0;
		c = write(bp->fid, bp->bbuf, n);
		if(n == c) {
			bp->offset += n;
			bp->ocount = -bp->bsize;
			return 0;
		}
		bp->state = Binactive;
		bp->ocount = 0;
		break;

	case Bracteof:
		bp->state = Bractive;

	case Bractive:
		bp->icount = 0;
		bp->gbuf = bp->ebuf;
		return 0;
	}
	return Beof;
}
Added lib/libbio/bgetc.c.










































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include	"plan9.h"
#include	<bio.h>

int
Bgetc(Biobuf *bp)
{
	int i;

loop:
	i = bp->icount;
	if(i != 0) {
		bp->icount = i+1;
		return bp->ebuf[i];
	}
	if(bp->state != Bractive) {
		if(bp->state == Bracteof)
			bp->state = Bractive;
		return Beof;
	}
	/*
	 * get next buffer, try to keep Bungetsize
	 * characters pre-catenated from the previous
	 * buffer to allow that many ungets.
	 */
	memmove(bp->bbuf-Bungetsize, bp->ebuf-Bungetsize, Bungetsize);
	i = read(bp->fid, bp->bbuf, bp->bsize);
	bp->gbuf = bp->bbuf;
	if(i <= 0) {
		bp->state = Bracteof;
		if(i < 0)
			bp->state = Binactive;
		return Beof;
	}
	if(i < bp->bsize) {
		memmove(bp->ebuf-i-Bungetsize, bp->bbuf-Bungetsize, i+Bungetsize);
		bp->gbuf = bp->ebuf-i;
	}
	bp->icount = -i;
	bp->offset += i;
	goto loop;
}

int
Bungetc(Biobuf *bp)
{

	if(bp->state == Bracteof)
		bp->state = Bractive;
	if(bp->state != Bractive)
		return Beof;
	bp->icount--;
	return 1;
}
Added lib/libbio/bgetd.c.








































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include "plan9.h"
#include <bio.h>

struct	bgetd
{
	Biobuf*	b;
	int		eof;
};

static int
Bgetdf(void *vp)
{
	int c;
	struct bgetd *bg = vp;

	c = Bgetc(bg->b);
	if(c == Beof)
		bg->eof = 1;
	return c;
}

int
Bgetd(Biobuf *bp, double *dp)
{
	double d;
	struct bgetd b;

	b.b = bp;
	b.eof = 0;
	d = fmtcharstod(Bgetdf, &b);
	if(b.eof)
		return -1;
	Bungetc(bp);
	*dp = d;
	return 1;
}
Added lib/libbio/bgetrune.c.






























































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include	"plan9.h"
#include	<bio.h>
#include	<utf.h>

long
Bgetrune(Biobuf *bp)
{
	int c, i;
	Rune rune;
	char str[UTFmax];

	c = Bgetc(bp);
	if(c < Runeself) {		/* one char */
		bp->runesize = 1;
		return c;
	}
	str[0] = c;

	for(i=1;;) {
		c = Bgetc(bp);
		if(c < 0)
			return c;
		str[i++] = c;

		if(fullrune(str, i)) {
			bp->runesize = chartorune(&rune, str);
			while(i > bp->runesize) {
				Bungetc(bp);
				i--;
			}
			return rune;
		}
	}
}

int
Bungetrune(Biobuf *bp)
{

	if(bp->state == Bracteof)
		bp->state = Bractive;
	if(bp->state != Bractive)
		return Beof;
	bp->icount -= bp->runesize;
	bp->runesize = 0;
	return 1;
}
Added lib/libbio/binit.c.




















































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#include	<stdlib.h>
#include	<plan9.h>
#include	<bio.h>

enum
{
	MAXBUFS	= 20
};

static	Biobuf*	wbufs[MAXBUFS];
static	int		atexitflag;

static
void
batexit(void)
{
	Biobuf *bp;
	int i;

	for(i=0; i<MAXBUFS; i++) {
		bp = wbufs[i];
		if(bp != 0) {
			wbufs[i] = 0;
			Bflush(bp);
		}
	}
}

static
void
deinstall(Biobuf *bp)
{
	int i;

	for(i=0; i<MAXBUFS; i++)
		if(wbufs[i] == bp)
			wbufs[i] = 0;
}

static
void
install(Biobuf *bp)
{
	int i;

	deinstall(bp);
	for(i=0; i<MAXBUFS; i++)
		if(wbufs[i] == 0) {
			wbufs[i] = bp;
			break;
		}
	if(atexitflag == 0) {
		atexitflag = 1;
		atexit(batexit);
	}
}

int
Binits(Biobuf *bp, int f, int mode, unsigned char *p, int size)
{

	p += Bungetsize;	/* make room for Bungets */
	size -= Bungetsize;

	switch(mode&~(OCEXEC|ORCLOSE|OTRUNC)) {
	default:
		fprint(2, "Bopen: unknown mode %d\n", mode);
		return Beof;

	case OREAD:
		bp->state = Bractive;
		bp->ocount = 0;
		break;

	case OWRITE:
		install(bp);
		bp->state = Bwactive;
		bp->ocount = -size;
		break;
	}
	bp->bbuf = p;
	bp->ebuf = p+size;
	bp->bsize = size;
	bp->icount = 0;
	bp->gbuf = bp->ebuf;
	bp->fid = f;
	bp->flag = 0;
	bp->rdline = 0;
	bp->offset = 0;
	bp->runesize = 0;
	return 0;
}


int
Binit(Biobuf *bp, int f, int mode)
{
	return Binits(bp, f, mode, bp->b, sizeof(bp->b));
}

Biobuf*
Bfdopen(int f, int mode)
{
	Biobuf *bp;

	bp = malloc(sizeof(Biobuf));
	if(bp == 0)
		return 0;
	Binits(bp, f, mode, bp->b, sizeof(bp->b));
	bp->flag = Bmagic;
	return bp;
}

Biobuf*
Bopen(const char *name, int mode)
{
	Biobuf *bp;
	int f;

	switch(mode&~(OCEXEC|ORCLOSE|OTRUNC)) {
	default:
		fprint(2, "Bopen: unknown mode %d\n", mode);
		return 0;

	case OREAD:
		f = open(name, mode);
		if(f < 0)
			return 0;
		break;

	case OWRITE:
		f = creat(name, mode);
		if(f < 0)
			return 0;
	}
	bp = Bfdopen(f, mode);
	if(bp == 0)
		close(f);
	return bp;
}

int
Bterm(Biobuf *bp)
{

	deinstall(bp);
	Bflush(bp);
	if(bp->flag == Bmagic) {
		bp->flag = 0;
		close(bp->fid);
		free(bp);
	}
	return 0;
}
Added lib/libbio/bio.3.






































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
.deEX
.ift .ft5
.nf
..
.deEE
.ft1
.fi
..
.TH BIO 3
.SH NAME
Bopen, Bfdopen, Binit, Binits, Brdline, Brdstr, Bgetc, Bgetrune, Bgetd, Bungetc, Bungetrune, Bread, Bseek, Boffset, Bfildes, Blinelen, Bputc, Bputrune, Bprint, Bvprint, Bwrite, Bflush, Bterm, Bbuffered \- buffered input/output
.SH SYNOPSIS
.ta \w'\fLBiobuf* 'u
.B #include <utf.h>
.br
.B #include <fmt.h>
.br
.B #include <bio.h>
.PP
.B
Biobuf*	Bopen(char *file, int mode)
.PP
.B
Biobuf*	Bfdopen(int fd, int mode)
.PP
.B
int	Binit(Biobuf *bp, int fd, int mode)
.PP
.B
int	Binits(Biobufhdr *bp, int fd, int mode, uchar *buf, int size)
.PP
.B
int	Bterm(Biobufhdr *bp)
.PP
.B
int	Bprint(Biobufhdr *bp, char *format, ...)
.PP
.B
int	Bvprint(Biobufhdr *bp, char *format, va_list arglist);
.PP
.B
void*	Brdline(Biobufhdr *bp, int delim)
.PP
.B
char*	Brdstr(Biobufhdr *bp, int delim, int nulldelim)
.PP
.B
int	Blinelen(Biobufhdr *bp)
.PP
.B
vlong	Boffset(Biobufhdr *bp)
.PP
.B
int	Bfildes(Biobufhdr *bp)
.PP
.B
int	Bgetc(Biobufhdr *bp)
.PP
.B
long	Bgetrune(Biobufhdr *bp)
.PP
.B
int	Bgetd(Biobufhdr *bp, double *d)
.PP
.B
int	Bungetc(Biobufhdr *bp)
.PP
.B
int	Bungetrune(Biobufhdr *bp)
.PP
.B
vlong	Bseek(Biobufhdr *bp, vlong n, int type)
.PP
.B
int	Bputc(Biobufhdr *bp, int c)
.PP
.B
int	Bputrune(Biobufhdr *bp, long c)
.PP
.B
long	Bread(Biobufhdr *bp, void *addr, long nbytes)
.PP
.B
long	Bwrite(Biobufhdr *bp, void *addr, long nbytes)
.PP
.B
int	Bflush(Biobufhdr *bp)
.PP
.B
int	Bbuffered(Biobufhdr *bp)
.PP
.SH DESCRIPTION
These routines implement fast buffered I/O.
I/O on different file descriptors is independent.
.PP
.I Bopen
opens
.I file
for mode
.B O_RDONLY
or creates for mode
.BR O_WRONLY .
It calls
.IR malloc (3)
to allocate a buffer.
.PP
.I Bfdopen
allocates a buffer for the already-open file descriptor
.I fd
for mode
.B O_RDONLY
or
.BR O_WRONLY .
It calls
.IR malloc (3)
to allocate a buffer.
.PP
.I Binit
initializes a standard size buffer, type
.IR Biobuf ,
with the open file descriptor passed in
by the user.
.I Binits
initializes a non-standard size buffer, type
.IR Biobufhdr ,
with the open file descriptor,
buffer area, and buffer size passed in
by the user.
.I Biobuf
and
.I Biobufhdr
are related by the declaration:
.IP
.EX
typedef struct Biobuf Biobuf;
struct Biobuf
{
	Biobufhdr;
	uchar b[Bungetsize+Bsize];
};
.EE
.PP
Arguments
of types pointer to Biobuf and pointer to Biobufhdr
can be used interchangeably in the following routines.
.PP
.IR Bopen ,
.IR Binit ,
or
.I Binits
should be called before any of the
other routines on that buffer.
.I Bfildes
returns the integer file descriptor of the associated open file.
.PP
.I Bterm
flushes the buffer for
.IR bp .
If the buffer was allocated by
.IR Bopen ,
the buffer is
.I freed
and the file is closed.
.PP
.I Brdline
reads a string from the file associated with
.I bp
up to and including the first
.I delim
character.
The delimiter character at the end of the line is
not altered.
.I Brdline
returns a pointer to the start of the line or
.L 0
on end-of-file or read error.
.I Blinelen
returns the length (including the delimiter)
of the most recent string returned by
.IR Brdline .
.PP
.I Brdstr
returns a
.IR malloc (3)-allocated
buffer containing the next line of input delimited by
.IR delim ,
terminated by a NUL (0) byte.
Unlike
.IR Brdline ,
which returns when its buffer is full even if no delimiter has been found,
.I Brdstr
will return an arbitrarily long line in a single call.
If
.I nulldelim
is set, the terminal delimiter will be overwritten with a NUL.
After a successful call to
.IR Brdstr ,
the return value of
.I Blinelen
will be the length of the returned buffer, excluding the NUL.
.PP
.I Bgetc
returns the next character from
.IR bp ,
or a negative value
at end of file.
.I Bungetc
may be called immediately after
.I Bgetc
to allow the same character to be reread.
.PP
.I Bgetrune
calls
.I Bgetc
to read the bytes of the next
.SM UTF
sequence in the input stream and returns the value of the rune
represented by the sequence.
It returns a negative value
at end of file.
.I Bungetrune
may be called immediately after
.I Bgetrune
to allow the same
.SM UTF
sequence to be reread as either bytes or a rune.
.I Bungetc
and
.I Bungetrune
may back up a maximum of five bytes.
.PP
.I Bgetd
uses
.I fmtcharstod
(see
.IR fmtstrtod (3))
and
.I Bgetc
to read the formatted
floating-point number in the input stream,
skipping initial blanks and tabs.
The value is stored in
.BR *d.
.PP
.I Bread
reads
.I nbytes
of data from
.I bp
into memory starting at
.IR addr .
The number of bytes read is returned on success
and a negative value is returned if a read error occurred.
.PP
.I Bseek
applies
.IR lseek (2)
to
.IR bp .
It returns the new file offset.
.I Boffset
returns the file offset of the next character to be processed.
.PP
.I Bputc
outputs the low order 8 bits of
.I c
on
.IR bp .
If this causes a
.IR write
to occur and there is an error,
a negative value is returned.
Otherwise, a zero is returned.
.PP
.I Bputrune
calls
.I Bputc
to output the low order
16 bits of
.I c
as a rune
in
.SM UTF
format
on the output stream.
.PP
.I Bprint
is a buffered interface to
.IR print (3).
If this causes a
.IR write
to occur and there is an error,
a negative value
.RB ( Beof )
is returned.
Otherwise, the number of bytes output is returned.
.I Bvprint
does the same except it takes as argument a
.B va_list
parameter, so it can be called within a variadic function.
.PP
.I Bwrite
outputs
.I nbytes
of data starting at
.I addr
to
.IR bp .
If this causes a
.IR write
to occur and there is an error,
a negative value is returned.
Otherwise, the number of bytes written is returned.
.PP
.I Bflush
causes any buffered output associated with
.I bp
to be written.
The return is as for
.IR Bputc .
.I Bflush
is called on
exit for every buffer still open
for writing.
.PP
.I Bbuffered
returns the number of bytes in the buffer.
When reading, this is the number of bytes still available from the last
read on the file; when writing, it is the number of bytes ready to be
written.
.SH SOURCE
.B http://swtch.com/plan9port/unix
.SH SEE ALSO
.IR open (2),
.IR print (3),
.IR atexit (3),
.IR utf (7),
.SH DIAGNOSTICS
.I Bio
routines that return integers yield
.B Beof
if 
.I bp
is not the descriptor of an open file.
.I Bopen
returns zero if the file cannot be opened in the given mode.
All routines set
.I errstr
on error.
.SH BUGS
.I Brdline
returns an error on strings longer than the buffer associated
with the file
and also if the end-of-file is encountered
before a delimiter.
.I Blinelen
will tell how many characters are available
in these cases.
In the case of a true end-of-file,
.I Blinelen
will return zero.
At the cost of allocating a buffer,
.I Brdstr
sidesteps these issues.
.PP
The data returned by
.I Brdline
may be overwritten by calls to any other
.I bio
routine on the same
.IR bp.
Added lib/libbio/boffset.c.


















































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include	"plan9.h"
#include	<bio.h>

off_t
Boffset(Biobuf *bp)
{
	off_t n;

	switch(bp->state) {
	default:
		fprint(2, "Boffset: unknown state %d\n", bp->state);
		n = Beof;
		break;

	case Bracteof:
	case Bractive:
		n = bp->offset + bp->icount;
		break;

	case Bwactive:
		n = bp->offset + (bp->bsize + bp->ocount);
		break;
	}
	return n;
}
Added lib/libbio/bprint.c.




























>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include	"plan9.h"
#include	<bio.h>

int
Bprint(Biobuf *bp, const char *fmt, ...)
{
	int n;
	va_list arg;

	va_start(arg, fmt);
	n = Bvprint(bp, fmt, arg);
	va_end(arg);
	return n;
}
Added lib/libbio/bputc.c.








































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include	"plan9.h"
#include	<bio.h>

int
Bputc(Biobuf *bp, int c)
{
	int i;

	for(;;) {
		i = bp->ocount;
		if(i) {
			bp->ebuf[i++] = c;
			bp->ocount = i;
			return 0;
		}
		if(Bflush(bp) == Beof)
			break;
	}
	return Beof;
}
Added lib/libbio/bputrune.c.














































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include	"plan9.h"
#include	<bio.h>
#include	<utf.h>

int
Bputrune(Biobuf *bp, long c)
{
	Rune rune;
	char str[UTFmax];
	int n;

	rune = c;
	if(rune < Runeself) {
		Bputc(bp, rune);
		return 1;
	}
	n = runetochar(str, &rune);
	if(n == 0)
		return Bbad;
	if(Bwrite(bp, str, n) != n)
		return Beof;
	return n;
}
Added lib/libbio/brdline.c.




























































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#include	"plan9.h"
#include	<bio.h>

void*
Brdline(Biobuf *bp, int delim)
{
	char *ip, *ep;
	int i, j;

	i = -bp->icount;
	if(i == 0) {
		/*
		 * eof or other error
		 */
		if(bp->state != Bractive) {
			if(bp->state == Bracteof)
				bp->state = Bractive;
			bp->rdline = 0;
			bp->gbuf = bp->ebuf;
			return 0;
		}
	}

	/*
	 * first try in remainder of buffer (gbuf doesn't change)
	 */
	ip = (char*)bp->ebuf - i;
	ep = memchr(ip, delim, i);
	if(ep) {
		j = (ep - ip) + 1;
		bp->rdline = j;
		bp->icount += j;
		return ip;
	}

	/*
	 * copy data to beginning of buffer
	 */
	if(i < bp->bsize)
		memmove(bp->bbuf, ip, i);
	bp->gbuf = bp->bbuf;

	/*
	 * append to buffer looking for the delim
	 */
	ip = (char*)bp->bbuf + i;
	while(i < bp->bsize) {
		j = read(bp->fid, ip, bp->bsize-i);
		if(j <= 0) {
			/*
			 * end of file with no delim
			 */
			memmove(bp->ebuf-i, bp->bbuf, i);
			bp->rdline = i;
			bp->icount = -i;
			bp->gbuf = bp->ebuf-i;
			return 0;
		}
		bp->offset += j;
		i += j;
		ep = memchr(ip, delim, j);
		if(ep) {
			/*
			 * found in new piece
			 * copy back up and reset everything
			 */
			ip = (char*)bp->ebuf - i;
			if(i < bp->bsize){
				memmove(ip, bp->bbuf, i);
				bp->gbuf = (unsigned char*)ip;
			}
			j = (ep - (char*)bp->bbuf) + 1;
			bp->rdline = j;
			bp->icount = j - i;
			return ip;
		}
		ip += j;
	}

	/*
	 * full buffer without finding
	 */
	bp->rdline = bp->bsize;
	bp->icount = -bp->bsize;
	bp->gbuf = bp->bbuf;
	return 0;
}

int
Blinelen(Biobuf *bp)
{

	return bp->rdline;
}
Added lib/libbio/brdstr.c.
































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
#include	<stdlib.h>
#include	<plan9.h>
#include	<bio.h>

static char*
badd(char *p, int *np, char *data, int ndata, int delim, int nulldelim)
{
	int n;

	n = *np;
	p = realloc(p, n+ndata+1);
	if(p){
		memmove(p+n, data, ndata);
		n += ndata;
		if(n>0 && nulldelim && p[n-1]==delim)
			p[--n] = '\0';
		else
			p[n] = '\0';
		*np = n;
	}
	return p;
}

char*
Brdstr(Biobuf *bp, int delim, int nulldelim)
{
	char *ip, *ep, *p;
	int i, j;

	i = -bp->icount;
	bp->rdline = 0;
	if(i == 0) {
		/*
		 * eof or other error
		 */
		if(bp->state != Bractive) {
			if(bp->state == Bracteof)
				bp->state = Bractive;
			bp->gbuf = bp->ebuf;
			return nil;
		}
	}

	/*
	 * first try in remainder of buffer (gbuf doesn't change)
	 */
	ip = (char*)bp->ebuf - i;
	ep = memchr(ip, delim, i);
	if(ep) {
		j = (ep - ip) + 1;
		bp->icount += j;
		return badd(nil, &bp->rdline, ip, j, delim, nulldelim);
	}

	/*
	 * copy data to beginning of buffer
	 */
	if(i < bp->bsize)
		memmove(bp->bbuf, ip, i);
	bp->gbuf = bp->bbuf;

	/*
	 * append to buffer looking for the delim
	 */
	p = nil;
	for(;;){
		ip = (char*)bp->bbuf + i;
		while(i < bp->bsize) {
			j = read(bp->fid, ip, bp->bsize-i);
			if(j <= 0 && i == 0)
				return p;
			if(j <= 0 && i > 0){
				/*
				 * end of file but no delim. pretend we got a delim
				 * by making the delim \0 and smashing it with nulldelim.
				 */
				j = 1;
				ep = ip;
				delim = '\0';
				nulldelim = 1;
				*ep = delim;	/* there will be room for this */
			}else{
				bp->offset += j;
				ep = memchr(ip, delim, j);
			}
			i += j;
			if(ep) {
				/*
				 * found in new piece
				 * copy back up and reset everything
				 */
				ip = (char*)bp->ebuf - i;
				if(i < bp->bsize){
					memmove(ip, bp->bbuf, i);
					bp->gbuf = (unsigned char*)ip;
				}
				j = (ep - (char*)bp->bbuf) + 1;
				bp->icount = j - i;
				return badd(p, &bp->rdline, ip, j, delim, nulldelim);
			}
			ip += j;
		}

		/*
		 * full buffer without finding; add to user string and continue
		 */
		p = badd(p, &bp->rdline, (char*)bp->bbuf, bp->bsize, 0, 0);
		i = 0;
		bp->icount = 0;
		bp->gbuf = bp->ebuf;
	}
}
Added lib/libbio/bread.c.


























































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include	"plan9.h"
#include	<bio.h>

long
Bread(Biobuf *bp, void *ap, long count)
{
	long c;
	unsigned char *p;
	int i, n, ic;

	p = ap;
	c = count;
	ic = bp->icount;

	while(c > 0) {
		n = -ic;
		if(n > c)
			n = c;
		if(n == 0) {
			if(bp->state != Bractive)
				break;
			i = read(bp->fid, bp->bbuf, bp->bsize);
			if(i <= 0) {
				bp->state = Bracteof;
				if(i < 0)
					bp->state = Binactive;
				break;
			}
			bp->gbuf = bp->bbuf;
			bp->offset += i;
			if(i < bp->bsize) {
				memmove(bp->ebuf-i, bp->bbuf, i);
				bp->gbuf = bp->ebuf-i;
			}
			ic = -i;
			continue;
		}
		memmove(p, bp->ebuf+ic, n);
		c -= n;
		ic += n;
		p += n;
	}
	bp->icount = ic;
	return count-c;
}
Added lib/libbio/bseek.c.
























































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include	"plan9.h"
#include	<bio.h>

off_t
Bseek(Biobuf *bp, off_t offset, int base)
{
	vlong n, d;
	int bufsz;

	switch(bp->state) {
	default:
		fprint(2, "Bseek: unknown state %d\n", bp->state);
		return Beof;

	case Bracteof:
		bp->state = Bractive;
		bp->icount = 0;
		bp->gbuf = bp->ebuf;

	case Bractive:
		n = offset;
		if(base == 1) {
			n += Boffset(bp);
			base = 0;
		}

		/*
		 * try to seek within buffer
		 */
		if(base == 0) {
			d = n - Boffset(bp);
			bufsz = bp->ebuf - bp->gbuf;
			if(-bufsz <= d && d <= bufsz){
				bp->icount += d;
				if(d >= 0) {
					if(bp->icount <= 0)
						return n;
				} else {
					if(bp->ebuf - bp->gbuf >= -bp->icount)
						return n;
				}
			}
		}

		/*
		 * reset the buffer
		 */
		n = lseek(bp->fid, n, base);
		bp->icount = 0;
		bp->gbuf = bp->ebuf;
		break;

	case Bwactive:
		Bflush(bp);
		n = lseek(bp->fid, offset, base);
		break;
	}
	bp->offset = n;
	return n;
}
Added lib/libbio/bvprint.c.














































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include "plan9.h"
#include <bio.h>

static int
fmtBflush(Fmt *f)
{
	Biobuf *bp;

	bp = f->farg;
	bp->ocount = (char*)f->to - (char*)f->stop;
	if(Bflush(bp) < 0)
		return 0;
	f->stop = bp->ebuf;
	f->to = (char*)f->stop + bp->ocount;
	f->start = f->to;
	return 1;
}

int
Bvprint(Biobuf *bp, const char *fmt, va_list arg)
{
	int n;
	Fmt f;

	f.runes = 0;
	f.stop = bp->ebuf;
	f.start = (char*)f.stop + bp->ocount;
	f.to = f.start;
	f.flush = fmtBflush;
	f.farg = bp;
	f.nfmt = 0;
	fmtlocaleinit(&f, nil, nil, nil);
	n = fmtvprint(&f, fmt, arg);
	bp->ocount = (char*)f.to - (char*)f.stop;
	if(n == 0)
		n = f.nfmt;
	return n;
}

Added lib/libbio/bwrite.c.












































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include	"plan9.h"
#include	<bio.h>

long
Bwrite(Biobuf *bp, void *ap, long count)
{
	long c;
	unsigned char *p;
	int i, n, oc;

	p = ap;
	c = count;
	oc = bp->ocount;

	while(c > 0) {
		n = -oc;
		if(n > c)
			n = c;
		if(n == 0) {
			if(bp->state != Bwactive)
				return Beof;
			i = write(bp->fid, bp->bbuf, bp->bsize);
			if(i != bp->bsize) {
				bp->state = Binactive;
				return Beof;
			}
			bp->offset += i;
			oc = -bp->bsize;
			continue;
		}
		memmove(bp->ebuf+oc, p, n);
		oc += n;
		c -= n;
		p += n;
	}
	bp->ocount = oc;
	return count-c;
}
Added lib/libfmt/Makefile.


































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
ROOT= ../..
include $(ROOT)/mk/hdr.mk

VERSION=2.0
TARG=libfmt

NUM=\
	charstod\
	pow10\
	nan64

OBJ=\
	dofmt\
	dorfmt\
	errfmt\
	fltfmt\
	fmt\
	fmtfd\
	fmtfdflush\
	fmtlocale\
	fmtlock\
	fmtprint\
	fmtquote\
	fmtrune\
	fmtstr\
	fmtvprint\
	fprint\
	print\
	runefmtstr\
	runeseprint\
	runesmprint\
	runesnprint\
	runesprint\
	runevseprint\
	runevsmprint\
	runevsnprint\
	seprint\
	smprint\
	snprint\
	sprint\
	strtod\
	vfprint\
	vseprint\
	vsmprint\
	vsnprint\
	$(NUM)

include $(ROOT)/mk/lib.mk

Added lib/libfmt/NOTICE.


















































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *		Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
 * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
*/

This is a Unix port of the Plan 9 formatted I/O package.

Please send comments about the packaging
to Russ Cox <rsc@swtch.com>.


----

This software is also made available under the Lucent Public License
version 1.02; see http://plan9.bell-labs.com/plan9dist/license.html

Added lib/libfmt/README.










>
>
>
>
>
1
2
3
4
5
This software was packaged for Unix by Russ Cox.
Please send comments to rsc@swtch.com.

http://swtch.com/plan9port/unix

Added lib/libfmt/charstod.c.










































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
 * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */
#include <stdarg.h>
#include <string.h>
#include "plan9.h"
#include "fmt.h"
#include "fmtdef.h"

/*
 * Reads a floating-point number by interpreting successive characters
 * returned by (*f)(vp).  The last call it makes to f terminates the
 * scan, so is not a character in the number.  It may therefore be
 * necessary to back up the input stream up one byte after calling charstod.
 */

double
fmtcharstod(int(*f)(void*), void *vp)
{
	double num, dem;
	int neg, eneg, dig, exp, c;

	num = 0;
	neg = 0;
	dig = 0;
	exp = 0;
	eneg = 0;

	c = (*f)(vp);
	while(c == ' ' || c == '\t')
		c = (*f)(vp);
	if(c == '-' || c == '+'){
		if(c == '-')
			neg = 1;
		c = (*f)(vp);
	}
	while(c >= '0' && c <= '9'){
		num = num*10 + c-'0';
		c = (*f)(vp);
	}
	if(c == '.')
		c = (*f)(vp);
	while(c >= '0' && c <= '9'){
		num = num*10 + c-'0';
		dig++;
		c = (*f)(vp);
	}
	if(c == 'e' || c == 'E'){
		c = (*f)(vp);
		if(c == '-' || c == '+'){
			if(c == '-'){
				dig = -dig;
				eneg = 1;
			}
			c = (*f)(vp);
		}
		while(c >= '0' && c <= '9'){
			exp = exp*10 + c-'0';
			c = (*f)(vp);
		}
	}
	exp -= dig;
	if(exp < 0){
		exp = -exp;
		eneg = !eneg;
	}
	dem = __fmtpow10(exp);
	if(eneg)
		num /= dem;
	else
		num *= dem;
	if(neg)
		return -num;
	return num;
}
Added lib/libfmt/dofmt.c.
























































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
 * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */
#include <stdarg.h>
#include <string.h>
#include "plan9.h"
#include "fmt.h"
#include "fmtdef.h"

/* format the output into f->to and return the number of characters fmted  */
int
dofmt(Fmt *f, const char *fmt)
{
	Rune rune, *rt, *rs;
	int r;
	char *t, *s;
	int n, nfmt;

	nfmt = f->nfmt;
	for(;;){
		if(f->runes){
			rt = (Rune*)f->to;
			rs = (Rune*)f->stop;
			while((r = *(uchar*)fmt) && r != '%'){
				if(r < Runeself)
					fmt++;
				else{
					fmt += chartorune(&rune, fmt);
					r = rune;
				}
				FMTRCHAR(f, rt, rs, r);
			}
			fmt++;
			f->nfmt += rt - (Rune *)f->to;
			f->to = rt;
			if(!r)
				return f->nfmt - nfmt;
			f->stop = rs;
		}else{
			t = (char*)f->to;
			s = (char*)f->stop;
			while((r = *(uchar*)fmt) && r != '%'){
				if(r < Runeself){
					FMTCHAR(f, t, s, r);
					fmt++;
				}else{
					n = chartorune(&rune, fmt);
					if(t + n > s){
						t = (char*)__fmtflush(f, t, n);
						if(t != nil)
							s = (char*)f->stop;
						else
							return -1;
					}
					while(n--)
						*t++ = *fmt++;
				}
			}
			fmt++;
			f->nfmt += t - (char *)f->to;
			f->to = t;
			if(!r)
				return f->nfmt - nfmt;
			f->stop = s;
		}

		fmt = (char*)__fmtdispatch(f, fmt, 0);
		if(fmt == nil)
			return -1;
	}
}

void *
__fmtflush(Fmt *f, void *t, int len)
{
	if(f->runes)
		f->nfmt += (Rune*)t - (Rune*)f->to;
	else
		f->nfmt += (char*)t - (char *)f->to;
	f->to = t;
	if(f->flush == 0 || (*f->flush)(f) == 0 || (char*)f->to + len > (char*)f->stop){
		f->stop = f->to;
		return nil;
	}
	return f->to;
}

/*
 * put a formatted block of memory sz bytes long of n runes into the output buffer,
 * left/right justified in a field of at least f->width charactes
 */
int
__fmtpad(Fmt *f, int n)
{
	char *t, *s;
	int i;

	t = (char*)f->to;
	s = (char*)f->stop;
	for(i = 0; i < n; i++)
		FMTCHAR(f, t, s, ' ');
	f->nfmt += t - (char *)f->to;
	f->to = t;
	return 0;
}

int
__rfmtpad(Fmt *f, int n)
{
	Rune *t, *s;
	int i;

	t = (Rune*)f->to;
	s = (Rune*)f->stop;
	for(i = 0; i < n; i++)
		FMTRCHAR(f, t, s, ' ');
	f->nfmt += t - (Rune *)f->to;
	f->to = t;
	return 0;
}

int
__fmtcpy(Fmt *f, const void *vm, int n, int sz)
{
	Rune *rt, *rs, r;
	char *t, *s, *m, *me;
	ulong fl;
	int nc, w;

	m = (char*)vm;
	me = m + sz;
	fl = f->flags;
	w = 0;
	if(fl & FmtWidth)
		w = f->width;
	if((fl & FmtPrec) && n > f->prec)
		n = f->prec;
	if(f->runes){
		if(!(fl & FmtLeft) && __rfmtpad(f, w - n) < 0)
			return -1;
		rt = (Rune*)f->to;
		rs = (Rune*)f->stop;
		for(nc = n; nc > 0; nc--){
			r = *(uchar*)m;
			if(r < Runeself)
				m++;
			else if((me - m) >= UTFmax || fullrune(m, me-m))
				m += chartorune(&r, m);
			else
				break;
			FMTRCHAR(f, rt, rs, r);
		}
		f->nfmt += rt - (Rune *)f->to;
		f->to = rt;
		if(fl & FmtLeft && __rfmtpad(f, w - n) < 0)
			return -1;
	}else{
		if(!(fl & FmtLeft) && __fmtpad(f, w - n) < 0)
			return -1;
		t = (char*)f->to;
		s = (char*)f->stop;
		for(nc = n; nc > 0; nc--){
			r = *(uchar*)m;
			if(r < Runeself)
				m++;
			else if((me - m) >= UTFmax || fullrune(m, me-m))
				m += chartorune(&r, m);
			else
				break;
			FMTRUNE(f, t, s, r);
		}
		f->nfmt += t - (char *)f->to;
		f->to = t;
		if(fl & FmtLeft && __fmtpad(f, w - n) < 0)
			return -1;
	}
	return 0;
}

int
__fmtrcpy(Fmt *f, const void *vm, int n)
{
	Rune r, *m, *me, *rt, *rs;
	char *t, *s;
	ulong fl;
	int w;

	m = (Rune*)vm;
	fl = f->flags;
	w = 0;
	if(fl & FmtWidth)
		w = f->width;
	if((fl & FmtPrec) && n > f->prec)
		n = f->prec;
	if(f->runes){
		if(!(fl & FmtLeft) && __rfmtpad(f, w - n) < 0)
			return -1;
		rt = (Rune*)f->to;
		rs = (Rune*)f->stop;
		for(me = m + n; m < me; m++)
			FMTRCHAR(f, rt, rs, *m);
		f->nfmt += rt - (Rune *)f->to;
		f->to = rt;
		if(fl & FmtLeft && __rfmtpad(f, w - n) < 0)
			return -1;
	}else{
		if(!(fl & FmtLeft) && __fmtpad(f, w - n) < 0)
			return -1;
		t = (char*)f->to;
		s = (char*)f->stop;
		for(me = m + n; m < me; m++){
			r = *m;
			FMTRUNE(f, t, s, r);
		}
		f->nfmt += t - (char *)f->to;
		f->to = t;
		if(fl & FmtLeft && __fmtpad(f, w - n) < 0)
			return -1;
	}
	return 0;
}

/* fmt out one character */
int
__charfmt(Fmt *f)
{
	char x[1];

	x[0] = va_arg(f->args, int);
	f->prec = 1;
	return __fmtcpy(f, (const char*)x, 1, 1);
}

/* fmt out one rune */
int
__runefmt(Fmt *f)
{
	Rune x[1];

	x[0] = va_arg(f->args, int);
	return __fmtrcpy(f, (const void*)x, 1);
}

/* public helper routine: fmt out a null terminated string already in hand */
int
fmtstrcpy(Fmt *f, const char *s)
{
	int i, j;
	Rune r;

	if(!s)
		return __fmtcpy(f, "<nil>", 5, 5);
	/* if precision is specified, make sure we don't wander off the end */
	if(f->flags & FmtPrec){
		i = 0;
		for(j=0; j<f->prec && s[i]; j++)
			i += chartorune(&r, s+i);
		return __fmtcpy(f, s, j, i);
	}
	return __fmtcpy(f, s, utflen(s), strlen(s));
}

/* fmt out a null terminated utf string */
int
__strfmt(Fmt *f)
{
	char *s;

	s = va_arg(f->args, char *);
	return fmtstrcpy(f, s);
}

/* public helper routine: fmt out a null terminated rune string already in hand */
int
fmtrunestrcpy(Fmt *f, Rune *s)
{
	Rune *e;
	int n, p;

	if(!s)
		return __fmtcpy(f, "<nil>", 5, 5);
	/* if precision is specified, make sure we don't wander off the end */
	if(f->flags & FmtPrec){
		p = f->prec;
		for(n = 0; n < p; n++)
			if(s[n] == 0)
				break;
	}else{
		for(e = s; *e; e++)
			;
		n = e - s;
	}
	return __fmtrcpy(f, s, n);
}

/* fmt out a null terminated rune string */
int
__runesfmt(Fmt *f)
{
	Rune *s;

	s = va_arg(f->args, Rune *);
	return fmtrunestrcpy(f, s);
}

/* fmt a % */
int
__percentfmt(Fmt *f)
{
	Rune x[1];

	x[0] = f->r;
	f->prec = 1;
	return __fmtrcpy(f, (const void*)x, 1);
}

/* fmt an integer */
int
__ifmt(Fmt *f)
{
	char buf[140], *p, *conv;
	/* 140: for 64 bits of binary + 3-byte sep every 4 digits */
	uvlong vu;
	ulong u;
	int neg, base, i, n, fl, w, isv;
	int ndig, len, excess, bytelen;
	char *grouping;
	char *thousands;

	neg = 0;
	fl = f->flags;
	isv = 0;
	vu = 0;
	u = 0;
	if(f->r == 'p'){
		u = (ulong)va_arg(f->args, void*);
		f->r = 'x';
		fl |= FmtUnsigned;
	}else if(fl & FmtVLong){
		isv = 1;
		if(fl & FmtUnsigned)
			vu = va_arg(f->args, uvlong);
		else
			vu = va_arg(f->args, vlong);
	}else if(fl & FmtLong){
		if(fl & FmtUnsigned)
			u = va_arg(f->args, ulong);
		else
			u = va_arg(f->args, long);
	}else if(fl & FmtByte){
		if(fl & FmtUnsigned)
			u = (uchar)va_arg(f->args, int);
		else
			u = (char)va_arg(f->args, int);
	}else if(fl & FmtShort){
		if(fl & FmtUnsigned)
			u = (ushort)va_arg(f->args, int);
		else
			u = (short)va_arg(f->args, int);
	}else{
		if(fl & FmtUnsigned)
			u = va_arg(f->args, uint);
		else
			u = va_arg(f->args, int);
	}
	conv = "0123456789abcdef";
	grouping = "\4";	/* for hex, octal etc. (undefined by spec but nice) */
	thousands = f->thousands;
	switch(f->r){
	case 'd':
	case 'i':
	case 'u':
		base = 10;
		grouping = f->grouping;
		break;
	case 'X':
		conv = "0123456789ABCDEF";
		/* fall through */
	case 'x':
		base = 16;
		thousands = ":";
		break;
	case 'b':
		base = 2;
		thousands = ":";
		break;
	case 'o':
		base = 8;
		break;
	default:
		return -1;
	}
	if(!(fl & FmtUnsigned)){
		if(isv && (vlong)vu < 0){
			vu = -(vlong)vu;
			neg = 1;
		}else if(!isv && (long)u < 0){
			u = -(long)u;
			neg = 1;
		}
	}
	p = buf + sizeof buf - 1;
	n = 0;	/* in runes */
	excess = 0;	/* number of bytes > number runes */
	ndig = 0;
	len = utflen(thousands);
	bytelen = strlen(thousands);
	if(isv){
		while(vu){
			i = vu % base;
			vu /= base;
			if((fl & FmtComma) && n % 4 == 3){
				*p-- = ',';
				n++;
			}
			if((fl & FmtApost) && __needsep(&ndig, &grouping)){
				n += len;
				excess += bytelen - len;
				p -= bytelen;
				memmove(p+1, thousands, bytelen);
			}
			*p-- = conv[i];
			n++;
		}
	}else{
		while(u){
			i = u % base;
			u /= base;
			if((fl & FmtComma) && n % 4 == 3){
				*p-- = ',';
				n++;
			}
			if((fl & FmtApost) && __needsep(&ndig, &grouping)){
				n += len;
				excess += bytelen - len;
				p -= bytelen;
				memmove(p+1, thousands, bytelen);
			}
			*p-- = conv[i];
			n++;
		}
	}
	if(n == 0){
		/*
		 * "The result of converting a zero value with
		 * a precision of zero is no characters."  - ANSI
		 *
		 * "For o conversion, # increases the precision, if and only if
		 * necessary, to force the first digit of the result to be a zero
		 * (if the value and precision are both 0, a single 0 is printed)." - ANSI
		 */
		if(!(fl & FmtPrec) || f->prec != 0 || (f->r == 'o' && (fl & FmtSharp))){
			*p-- = '0';
			n = 1;
			if(fl & FmtApost)
				__needsep(&ndig, &grouping);
		}
		
		/*
		 * Zero values don't get 0x.
		 */
		if(f->r == 'x' || f->r == 'X')
			fl &= ~FmtSharp;
	}
	for(w = f->prec; n < w && p > buf+3; n++){
		if((fl & FmtApost) && __needsep(&ndig, &grouping)){
			n += len;
			excess += bytelen - len;
			p -= bytelen;
			memmove(p+1, thousands, bytelen);
		}
		*p-- = '0';
	}
	if(neg || (fl & (FmtSign|FmtSpace)))
		n++;
	if(fl & FmtSharp){
		if(base == 16)
			n += 2;
		else if(base == 8){
			if(p[1] == '0')
				fl &= ~FmtSharp;
			else
				n++;
		}
	}
	if((fl & FmtZero) && !(fl & (FmtLeft|FmtPrec))){
		w = 0;
		if(fl & FmtWidth)
			w = f->width;
		for(; n < w && p > buf+3; n++){
			if((fl & FmtApost) && __needsep(&ndig, &grouping)){
				n += len;
				excess += bytelen - len;
				p -= bytelen;
				memmove(p+1, thousands, bytelen);
			}
			*p-- = '0';
		}
		f->flags &= ~FmtWidth;
	}
	if(fl & FmtSharp){
		if(base == 16)
			*p-- = f->r;
		if(base == 16 || base == 8)
			*p-- = '0';
	}
	if(neg)
		*p-- = '-';
	else if(fl & FmtSign)
		*p-- = '+';
	else if(fl & FmtSpace)
		*p-- = ' ';
	f->flags &= ~FmtPrec;
	return __fmtcpy(f, p + 1, n, n + excess);
}

int
__countfmt(Fmt *f)
{
	void *p;
	ulong fl;

	fl = f->flags;
	p = va_arg(f->args, void*);
	if(fl & FmtVLong){
		*(vlong*)p = f->nfmt;
	}else if(fl & FmtLong){
		*(long*)p = f->nfmt;
	}else if(fl & FmtByte){
		*(char*)p = f->nfmt;
	}else if(fl & FmtShort){
		*(short*)p = f->nfmt;
	}else{
		*(int*)p = f->nfmt;
	}
	return 0;
}

int
__flagfmt(Fmt *f)
{
	switch(f->r){
	case ',':
		f->flags |= FmtComma;
		break;
	case '-':
		f->flags |= FmtLeft;
		break;
	case '+':
		f->flags |= FmtSign;
		break;
	case '#':
		f->flags |= FmtSharp;
		break;
	case '\'':
		f->flags |= FmtApost;
		break;
	case ' ':
		f->flags |= FmtSpace;
		break;
	case 'u':
		f->flags |= FmtUnsigned;
		break;
	case 'h':
		if(f->flags & FmtShort)
			f->flags |= FmtByte;
		f->flags |= FmtShort;
		break;
	case 'L':
		f->flags |= FmtLDouble;
		break;
	case 'l':
		if(f->flags & FmtLong)
			f->flags |= FmtVLong;
		f->flags |= FmtLong;
		break;
	}
	return 1;
}

/* default error format */
int
__badfmt(Fmt *f)
{
	char x[2+UTFmax];
	int n;

	x[0] = '%';
	n = 1 + runetochar(x+1, &f->r);
	x[n++] = '%';
	f->prec = n;
	__fmtcpy(f, (const void*)x, n, n);
	return 0;
}
Added lib/libfmt/dorfmt.c.




























































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
 * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */
#include <stdarg.h>
#include <string.h>
#include "plan9.h"
#include "fmt.h"
#include "fmtdef.h"

/* format the output into f->to and return the number of characters fmted  */

/* BUG: THIS FILE IS NOT UPDATED TO THE  NEW SPEC */
int
dorfmt(Fmt *f, const Rune *fmt)
{
	Rune *rt, *rs;
	int r;
	char *t, *s;
	int nfmt;

	nfmt = f->nfmt;
	for(;;){
		if(f->runes){
			rt = (Rune*)f->to;
			rs = (Rune*)f->stop;
			while((r = *fmt++) && r != '%'){
				FMTRCHAR(f, rt, rs, r);
			}
			f->nfmt += rt - (Rune *)f->to;
			f->to = rt;
			if(!r)
				return f->nfmt - nfmt;
			f->stop = rs;
		}else{
			t = (char*)f->to;
			s = (char*)f->stop;
			while((r = *fmt++) && r != '%'){
				FMTRUNE(f, t, f->stop, r);
			}
			f->nfmt += t - (char *)f->to;
			f->to = t;
			if(!r)
				return f->nfmt - nfmt;
			f->stop = s;
		}

		fmt = (Rune*)__fmtdispatch(f, (Rune*)fmt, 1);
		if(fmt == nil)
			return -1;
	}
	return 0;		/* not reached */
}
Added lib/libfmt/errfmt.c.
























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
 * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */
#include <stdarg.h>
#include <errno.h>
#include <string.h>
#include "plan9.h"
#include "fmt.h"
#include "fmtdef.h"

int
__errfmt(Fmt *f)
{
	char *s;

	s = strerror(errno);
	return fmtstrcpy(f, s);
}
Added lib/libfmt/fltfmt.c.














































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
 * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */
#include <stdio.h>
#include <math.h>
#include <float.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <stdarg.h>
#include <fmt.h>
#include <assert.h>
#include "plan9.h"
#include "fmt.h"
#include "fmtdef.h"

enum
{
	FDIGIT	= 30,
	FDEFLT	= 6,
	NSIGNIF	= 17
};

/*
 * first few powers of 10, enough for about 1/2 of the
 * total space for doubles.
 */
static double pows10[] =
{
	  1e0,   1e1,   1e2,   1e3,   1e4,   1e5,   1e6,   1e7,   1e8,   1e9,
	 1e10,  1e11,  1e12,  1e13,  1e14,  1e15,  1e16,  1e17,  1e18,  1e19,
	 1e20,  1e21,  1e22,  1e23,  1e24,  1e25,  1e26,  1e27,  1e28,  1e29,
	 1e30,  1e31,  1e32,  1e33,  1e34,  1e35,  1e36,  1e37,  1e38,  1e39,
	 1e40,  1e41,  1e42,  1e43,  1e44,  1e45,  1e46,  1e47,  1e48,  1e49,
	 1e50,  1e51,  1e52,  1e53,  1e54,  1e55,  1e56,  1e57,  1e58,  1e59,
	 1e60,  1e61,  1e62,  1e63,  1e64,  1e65,  1e66,  1e67,  1e68,  1e69,
	 1e70,  1e71,  1e72,  1e73,  1e74,  1e75,  1e76,  1e77,  1e78,  1e79,
	 1e80,  1e81,  1e82,  1e83,  1e84,  1e85,  1e86,  1e87,  1e88,  1e89,
	 1e90,  1e91,  1e92,  1e93,  1e94,  1e95,  1e96,  1e97,  1e98,  1e99,
	1e100, 1e101, 1e102, 1e103, 1e104, 1e105, 1e106, 1e107, 1e108, 1e109,
	1e110, 1e111, 1e112, 1e113, 1e114, 1e115, 1e116, 1e117, 1e118, 1e119,
	1e120, 1e121, 1e122, 1e123, 1e124, 1e125, 1e126, 1e127, 1e128, 1e129,
	1e130, 1e131, 1e132, 1e133, 1e134, 1e135, 1e136, 1e137, 1e138, 1e139,
	1e140, 1e141, 1e142, 1e143, 1e144, 1e145, 1e146, 1e147, 1e148, 1e149,
	1e150, 1e151, 1e152, 1e153, 1e154, 1e155, 1e156, 1e157, 1e158, 1e159,
};
#define	npows10 ((int)(sizeof(pows10)/sizeof(pows10[0])))
#define	pow10(x)  fmtpow10(x)

static double
pow10(int n)
{
	double d;
	int neg;

	neg = 0;
	if(n < 0){
		neg = 1;
		n = -n;
	}

	if(n < npows10)
		d = pows10[n];
	else{
		d = pows10[npows10-1];
		for(;;){
			n -= npows10 - 1;
			if(n < npows10){
				d *= pows10[n];
				break;
			}
			d *= pows10[npows10 - 1];
		}
	}
	if(neg)
		return 1./d;
	return d;
}

/*
 * add 1 to the decimal integer string a of length n.
 * if 99999 overflows into 10000, return 1 to tell caller
 * to move the virtual decimal point.
 */
static int
xadd1(char *a, int n)
{
	char *b;
	int c;

	if(n < 0 || n > NSIGNIF)
		return 0;
	for(b = a+n-1; b >= a; b--) {
		c = *b + 1;
		if(c <= '9') {
			*b = c;
			return 0;
		}
		*b = '0';
	}
	/*
	 * need to overflow adding digit.
	 * shift number down and insert 1 at beginning.
	 * decimal is known to be 0s or we wouldn't
	 * have gotten this far.  (e.g., 99999+1 => 00000)
	 */
	a[0] = '1';
	return 1;
}

/*
 * subtract 1 from the decimal integer string a.
 * if 10000 underflows into 09999, make it 99999
 * and return 1 to tell caller to move the virtual 
 * decimal point.  this way, xsub1 is inverse of xadd1.
 */
static int
xsub1(char *a, int n)
{
	char *b;
	int c;

	if(n < 0 || n > NSIGNIF)
		return 0;
	for(b = a+n-1; b >= a; b--) {
		c = *b - 1;
		if(c >= '0') {
			if(c == '0' && b == a) {
				/*
				 * just zeroed the top digit; shift everyone up.
				 * decimal is known to be 9s or we wouldn't
				 * have gotten this far.  (e.g., 10000-1 => 09999)
				 */
				*b = '9';
				return 1;
			}
			*b = c;
			return 0;
		}
		*b = '9';
	}
	/*
	 * can't get here.  the number a is always normalized
	 * so that it has a nonzero first digit.
	 */
	abort();
}

/*
 * format exponent like sprintf(p, "e%+02d", e)
 */
static void
xfmtexp(char *p, int e, int ucase)
{
	char se[9];
	int i;

	*p++ = ucase ? 'E' : 'e';
	if(e < 0) {
		*p++ = '-';
		e = -e;
	} else
		*p++ = '+';
	i = 0;
	while(e) {
		se[i++] = e % 10 + '0';
		e /= 10;
	}
	while(i < 2)
		se[i++] = '0';
	while(i > 0)
		*p++ = se[--i];
	*p++ = '\0';
}

/*
 * compute decimal integer m, exp such that:
 *	f = m*10^exp
 *	m is as short as possible with losing exactness
 * assumes special cases (NaN, +Inf, -Inf) have been handled.
 */
static void
xdtoa(double f, char *s, int *exp, int *neg, int *ns)
{
	int c, d, e2, e, ee, i, ndigit, oerrno;
	char tmp[NSIGNIF+10];
	double g;

	oerrno = errno; /* in case strtod smashes errno */

	/*
	 * make f non-negative.
	 */
	*neg = 0;
	if(f < 0) {
		f = -f;
		*neg = 1;
	}

	/*
	 * must handle zero specially.
	 */
	if(f == 0){
		*exp = 0;
		s[0] = '0';
		s[1] = '\0';
		*ns = 1;
		return;
	}
		
	/*
	 * find g,e such that f = g*10^e.
	 * guess 10-exponent using 2-exponent, then fine tune.
	 */
	frexp(f, &e2);
	e = (int)(e2 * .301029995664);
	g = f * pow10(-e);
	while(g < 1) {
		e--;
		g = f * pow10(-e);
	}
	while(g >= 10) {
		e++;
		g = f * pow10(-e);
	}

	/*
	 * convert NSIGNIF digits as a first approximation.
	 */
	for(i=0; i<NSIGNIF; i++) {
		d = (int)g;
		s[i] = d+'0';
		g = (g-d) * 10;
	}
	s[i] = 0;

	/*
	 * adjust e because s is 314159... not 3.14159...
	 */
	e -= NSIGNIF-1;
	xfmtexp(s+NSIGNIF, e, 0);

	/*
	 * adjust conversion until strtod(s) == f exactly.
	 */
	for(i=0; i<10; i++) {
		g = fmtstrtod(s, nil);
		if(f > g) {
			if(xadd1(s, NSIGNIF)) {
				/* gained a digit */
				e--;
				xfmtexp(s+NSIGNIF, e, 0);
			}
			continue;
		}
		if(f < g) {
			if(xsub1(s, NSIGNIF)) {
				/* lost a digit */
				e++;
				xfmtexp(s+NSIGNIF, e, 0);
			}
			continue;
		}
		break;
	}

	/*
	 * play with the decimal to try to simplify.
	 */

	/*
	 * bump last few digits up to 9 if we can
	 */
	for(i=NSIGNIF-1; i>=NSIGNIF-3; i--) {
		c = s[i];
		if(c != '9') {
			s[i] = '9';
			g = fmtstrtod(s, nil);
			if(g != f) {
				s[i] = c;
				break;
			}
		}
	}

	/*
	 * add 1 in hopes of turning 9s to 0s
	 */
	if(s[NSIGNIF-1] == '9') {
		strcpy(tmp, s);
		ee = e;
		if(xadd1(tmp, NSIGNIF)) {
			ee--;
			xfmtexp(tmp+NSIGNIF, ee, 0);
		}
		g = fmtstrtod(tmp, nil);
		if(g == f) {
			strcpy(s, tmp);
			e = ee;
		}
	}
	
	/*
	 * bump last few digits down to 0 as we can.
	 */
	for(i=NSIGNIF-1; i>=NSIGNIF-3; i--) {
		c = s[i];
		if(c != '0') {
			s[i] = '0';
			g = fmtstrtod(s, nil);
			if(g != f) {
				s[i] = c;
				break;
			}
		}
	}

	/*
	 * remove trailing zeros.
	 */
	ndigit = NSIGNIF;
	while(ndigit > 1 && s[ndigit-1] == '0'){
		e++;
		--ndigit;
	}
	s[ndigit] = 0;
	*exp = e;
	*ns = ndigit;
	errno = oerrno;
}

#ifdef PLAN9PORT
static char *special[] = { "NaN", "NaN", "+Inf", "+Inf", "-Inf", "-Inf" };
#else
static char *special[] = { "nan", "NAN", "inf", "INF", "-inf", "-INF" };
#endif

int
__efgfmt(Fmt *fmt)
{
	char buf[NSIGNIF+10], *dot, *digits, *p, *s, suf[10], *t;
	double f;
	int c, chr, dotwid, e, exp, fl, ndigits, neg, newndigits;
	int pad, point, prec, realchr, sign, sufwid, ucase, wid, z1, z2;
	Rune r, *rs, *rt;
	
	if(fmt->flags&FmtLong)
		f = va_arg(fmt->args, long double);
	else
		f = va_arg(fmt->args, double);
	
	/* 
	 * extract formatting flags
	 */
	fl = fmt->flags;
	fmt->flags = 0;
	prec = FDEFLT;
	if(fl & FmtPrec)
		prec = fmt->prec;
	chr = fmt->r;
	ucase = 0;
	switch(chr) {
	case 'A':
	case 'E':
	case 'F':
	case 'G':
		chr += 'a'-'A';
		ucase = 1;
		break;
	}

	/*
	 * pick off special numbers.
	 */
	if(__isNaN(f)) {
		s = special[0+ucase];
	special:
		fmt->flags = fl & (FmtWidth|FmtLeft);
		return __fmtcpy(fmt, s, strlen(s), strlen(s));
	}
	if(__isInf(f, 1)) {
		s = special[2+ucase];
		goto special;
	}
	if(__isInf(f, -1)) {
		s = special[4+ucase];
		goto special;
	}

	/*
	 * get exact representation.
	 */
	digits = buf;
	xdtoa(f, digits, &exp, &neg, &ndigits);

	/*
	 * get locale's decimal point.
	 */
	dot = fmt->decimal;
	if(dot == nil)
		dot = ".";
	dotwid = utflen(dot);

	/*
	 * now the formatting fun begins.
	 * compute parameters for actual fmt:
	 *
	 *	pad: number of spaces to insert before/after field.
	 *	z1: number of zeros to insert before digits
	 *	z2: number of zeros to insert after digits
	 *	point: number of digits to print before decimal point
	 *	ndigits: number of digits to use from digits[]
	 *	suf: trailing suffix, like "e-5"
	 */
	realchr = chr;
	switch(chr){
	case 'g':
		/*
		 * convert to at most prec significant digits. (prec=0 means 1)
		 */
		if(prec == 0)
			prec = 1;
		if(ndigits > prec) {
			if(digits[prec] >= '5' && xadd1(digits, prec))
				exp++;
			exp += ndigits-prec;
			ndigits = prec;
		}
		
		/*
		 * extra rules for %g (implemented below):
		 *	trailing zeros removed after decimal unless FmtSharp.
		 *	decimal point only if digit follows.
		 */

		/* fall through to %e */
	default:
	case 'e':
		/* 
		 * one significant digit before decimal, no leading zeros.
		 */
		point = 1;
		z1 = 0;
		
		/*
		 * decimal point is after ndigits digits right now.
		 * slide to be after first.
		 */
		e  = exp + (ndigits-1);

		/*
		 * if this is %g, check exponent and convert prec
		 */
		if(realchr == 'g') {
			if(-4 <= e && e < prec)
				goto casef;
			prec--;	/* one digit before decimal; rest after */
		}

		/*
		 * compute trailing zero padding or truncate digits.
		 */
		if(1+prec >= ndigits)
			z2 = 1+prec - ndigits;
		else {
			/*
			 * truncate digits
			 */
			assert(realchr != 'g');
			newndigits = 1+prec;
			if(digits[newndigits] >= '5' && xadd1(digits, newndigits)) {
				/*
				 * had 999e4, now have 100e5
				 */
				e++;
			}
			ndigits = newndigits;
			z2 = 0;
		}
		xfmtexp(suf, e, ucase);
		sufwid = strlen(suf);
		break;

	casef:
	case 'f':
		/*
		 * determine where digits go with respect to decimal point
		 */
		if(ndigits+exp > 0) {
			point = ndigits+exp;
			z1 = 0;
		} else {
			point = 1;
			z1 = 1 + -(ndigits+exp);
		}

		/*
		 * %g specifies prec = number of significant digits
		 * convert to number of digits after decimal point
		 */
		if(realchr == 'g')
			prec += z1 - point;

		/*
		 * compute trailing zero padding or truncate digits.
		 */
		if(point+prec >= z1+ndigits)
			z2 = point+prec - (z1+ndigits);
		else {
			/*
			 * truncate digits
			 */
			assert(realchr != 'g');
			newndigits = point+prec - z1;
			if(newndigits < 0) {
				z1 += newndigits;
				newndigits = 0;
			} else if(newndigits == 0) {
				/* perhaps round up */
				if(digits[0] >= '5'){
					digits[0] = '1';
					newndigits = 1;
					goto newdigit;
				}
			} else if(digits[newndigits] >= '5' && xadd1(digits, newndigits)) {
				/*
				 * digits was 999, is now 100; make it 1000
				 */
				digits[newndigits++] = '0';
			newdigit:
				/*
				 * account for new digit
				 */
				if(z1)	/* 0.099 => 0.100 or 0.99 => 1.00*/
					z1--;
				else	/* 9.99 => 10.00 */
					point++;
			}
			z2 = 0;
			ndigits = newndigits;
		}	
		sufwid = 0;
		break;
	}
	
	/*
	 * if %g is given without FmtSharp, remove trailing zeros.
	 * must do after truncation, so that e.g. print %.3g 1.001
	 * produces 1, not 1.00.  sorry, but them's the rules.
	 */
	if(realchr == 'g' && !(fl & FmtSharp)) {
		if(z1+ndigits+z2 >= point) {
			if(z1+ndigits < point)
				z2 = point - (z1+ndigits);
			else{
				z2 = 0;
				while(z1+ndigits > point && digits[ndigits-1] == '0')
					ndigits--;
			}
		}
	}

	/*
	 * compute width of all digits and decimal point and suffix if any
	 */
	wid = z1+ndigits+z2;
	if(wid > point)
		wid += dotwid;
	else if(wid == point){
		if(fl & FmtSharp)
			wid += dotwid;
		else
			point++;	/* do not print any decimal point */
	}
	wid += sufwid;

	/*
	 * determine sign
	 */
	sign = 0;
	if(neg)
		sign = '-';
	else if(fl & FmtSign)
		sign = '+';
	else if(fl & FmtSpace)
		sign = ' ';
	if(sign)
		wid++;

	/*
	 * compute padding
	 */
	pad = 0;
	if((fl & FmtWidth) && fmt->width > wid)
		pad = fmt->width - wid;
	if(pad && !(fl & FmtLeft) && (fl & FmtZero)){
		z1 += pad;
		point += pad;
		pad = 0;
	}

	/*
	 * format the actual field.  too bad about doing this twice.
	 */
	if(fmt->runes){
		if(pad && !(fl & FmtLeft) && __rfmtpad(fmt, pad) < 0)
			return -1;
		rt = (Rune*)fmt->to;
		rs = (Rune*)fmt->stop;
		if(sign)
			FMTRCHAR(fmt, rt, rs, sign);
		while(z1>0 || ndigits>0 || z2>0) {
			if(z1 > 0){
				z1--;
				c = '0';
			}else if(ndigits > 0){
				ndigits--;
				c = *digits++;
			}else{
				z2--;
				c = '0';
			}
			FMTRCHAR(fmt, rt, rs, c);
			if(--point == 0) {
				for(p = dot; *p; ){
					p += chartorune(&r, p);
					FMTRCHAR(fmt, rt, rs, r);
				}
			}
		}
		fmt->nfmt += rt - (Rune*)fmt->to;
		fmt->to = rt;
		if(sufwid && __fmtcpy(fmt, suf, sufwid, sufwid) < 0)
			return -1;
		if(pad && (fl & FmtLeft) && __rfmtpad(fmt, pad) < 0)
			return -1;
	}else{
		if(pad && !(fl & FmtLeft) && __fmtpad(fmt, pad) < 0)
			return -1;
		t = (char*)fmt->to;
		s = (char*)fmt->stop;
		if(sign)
			FMTCHAR(fmt, t, s, sign);
		while(z1>0 || ndigits>0 || z2>0) {
			if(z1 > 0){
				z1--;
				c = '0';
			}else if(ndigits > 0){
				ndigits--;
				c = *digits++;
			}else{
				z2--;
				c = '0';
			}
			FMTCHAR(fmt, t, s, c);
			if(--point == 0)
				for(p=dot; *p; p++)
					FMTCHAR(fmt, t, s, *p);
		}
		fmt->nfmt += t - (char*)fmt->to;
		fmt->to = t;
		if(sufwid && __fmtcpy(fmt, suf, sufwid, sufwid) < 0)
			return -1;
		if(pad && (fl & FmtLeft) && __fmtpad(fmt, pad) < 0)
			return -1;
	}
	return 0;
}

Added lib/libfmt/fmt.c.






















































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
 * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */
#include <stdarg.h>
#include <string.h>
#include "plan9.h"
#include "fmt.h"
#include "fmtdef.h"

enum
{
	Maxfmt = 64
};

typedef struct Convfmt Convfmt;
struct Convfmt
{
	int	c;
	volatile	Fmts	fmt;	/* for spin lock in fmtfmt; avoids race due to write order */
};

struct
{
	/* lock by calling __fmtlock, __fmtunlock */
	int	nfmt;
	Convfmt	fmt[Maxfmt];
} fmtalloc;

static Convfmt knownfmt[] = {
	' ',	__flagfmt,
	'#',	__flagfmt,
	'%',	__percentfmt,
	'\'',	__flagfmt,
	'+',	__flagfmt,
	',',	__flagfmt,
	'-',	__flagfmt,
	'C',	__runefmt,	/* Plan 9 addition */
	'E',	__efgfmt,
	'G',	__efgfmt,
	'S',	__runesfmt,	/* Plan 9 addition */
	'X',	__ifmt,
	'b',	__ifmt,		/* Plan 9 addition */
	'c',	__charfmt,
	'd',	__ifmt,
	'e',	__efgfmt,
	'f',	__efgfmt,
	'g',	__efgfmt,
	'h',	__flagfmt,
	'l',	__flagfmt,
	'n',	__countfmt,
	'o',	__ifmt,
	'p',	__ifmt,
	'r',	__errfmt,
	's',	__strfmt,
	'u',	__flagfmt,
	'x',	__ifmt,
	0,	nil,
};


int	(*fmtdoquote)(int);

/*
 * __fmtlock() must be set
 */
static int
__fmtinstall(int c, Fmts f)
{
	Convfmt *p, *ep;

	if(c<=0 || c>=65536)
		return -1;
	if(!f)
		f = __badfmt;

	ep = &fmtalloc.fmt[fmtalloc.nfmt];
	for(p=fmtalloc.fmt; p<ep; p++)
		if(p->c == c)
			break;

	if(p == &fmtalloc.fmt[Maxfmt])
		return -1;

	p->fmt = f;
	if(p == ep){	/* installing a new format character */
		fmtalloc.nfmt++;
		p->c = c;
	}

	return 0;
}

int
fmtinstall(int c, int (*f)(Fmt*))
{
	int ret;

	__fmtlock();
	ret = __fmtinstall(c, f);
	__fmtunlock();
	return ret;
}

static Fmts
fmtfmt(int c)
{
	Convfmt *p, *ep;

	ep = &fmtalloc.fmt[fmtalloc.nfmt];
	for(p=fmtalloc.fmt; p<ep; p++)
		if(p->c == c){
			while(p->fmt == nil)	/* loop until value is updated */
				;
			return p->fmt;
		}

	/* is this a predefined format char? */
	__fmtlock();
	for(p=knownfmt; p->c; p++)
		if(p->c == c){
			__fmtinstall(p->c, p->fmt);
			__fmtunlock();
			return p->fmt;
		}
	__fmtunlock();

	return __badfmt;
}

void*
__fmtdispatch(Fmt *f, const void *fmt, int isrunes)
{
	Rune rune, r;
	int i, n;

	f->flags = 0;
	f->width = f->prec = 0;

	for(;;){
		if(isrunes){
			r = *(Rune*)fmt;
			fmt = (Rune*)fmt + 1;
		}else{
			fmt = (char*)fmt + chartorune(&rune, (char*)fmt);
			r = rune;
		}
		f->r = r;
		switch(r){
		case '\0':
			return nil;
		case '.':
			f->flags |= FmtWidth|FmtPrec;
			continue;
		case '0':
			if(!(f->flags & FmtWidth)){
				f->flags |= FmtZero;
				continue;
			}
			/* fall through */
		case '1': case '2': case '3': case '4':
		case '5': case '6': case '7': case '8': case '9':
			i = 0;
			while(r >= '0' && r <= '9'){
				i = i * 10 + r - '0';
				if(isrunes){
					r = *(Rune*)fmt;
					fmt = (Rune*)fmt + 1;
				}else{
					r = *(char*)fmt;
					fmt = (char*)fmt + 1;
				}
			}
			if(isrunes)
				fmt = (Rune*)fmt - 1;
			else
				fmt = (char*)fmt - 1;
		numflag:
			if(f->flags & FmtWidth){
				f->flags |= FmtPrec;
				f->prec = i;
			}else{
				f->flags |= FmtWidth;
				f->width = i;
			}
			continue;
		case '*':
			i = va_arg(f->args, int);
			if(i < 0){
				/*
				 * negative precision =>
				 * ignore the precision.
				 */
				if(f->flags & FmtPrec){
					f->flags &= ~FmtPrec;
					f->prec = 0;
					continue;
				}
				i = -i;
				f->flags |= FmtLeft;
			}
			goto numflag;
		}
		n = (*fmtfmt(r))(f);
		if(n < 0)
			return nil;
		if(n == 0)
			return (void*)fmt;
	}
}
Added lib/libfmt/fmtdef.h.


































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
 * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */

/*
 * dofmt -- format to a buffer
 * the number of characters formatted is returned,
 * or -1 if there was an error.
 * if the buffer is ever filled, flush is called.
 * it should reset the buffer and return whether formatting should continue.
 */

typedef int (*Fmts)(Fmt*);

typedef struct Quoteinfo Quoteinfo;
struct Quoteinfo
{
	int	quoted;		/* if set, string must be quoted */
	int	nrunesin;	/* number of input runes that can be accepted */
	int	nbytesin;	/* number of input bytes that can be accepted */
	int	nrunesout;	/* number of runes that will be generated */
	int	nbytesout;	/* number of bytes that will be generated */
};

/* Edit .+1,/^$/ |cfn |grep -v static | grep __ */
double       __Inf(int sign);
double       __NaN(void);
int          __badfmt(Fmt *f);
int          __charfmt(Fmt *f);
int          __countfmt(Fmt *f);
int          __efgfmt(Fmt *fmt);
int          __errfmt(Fmt *f);
int          __flagfmt(Fmt *f);
int          __fmtFdFlush(Fmt *f);
int          __fmtcpy(Fmt *f, const void *vm, int n, int sz);
void*        __fmtdispatch(Fmt *f, const void *fmt, int isrunes);
void *       __fmtflush(Fmt *f, void *t, int len);
void         __fmtlock(void);
int          __fmtpad(Fmt *f, int n);
double       __fmtpow10(int n);
int          __fmtrcpy(Fmt *f, const void *vm, int n);
void         __fmtunlock(void);
int          __ifmt(Fmt *f);
int          __isInf(double d, int sign);
int          __isNaN(double d);
int          __needsep(int *ndig, char **grouping);
int          __needsquotes(char *s, int *quotelenp);
int          __percentfmt(Fmt *f);
void         __quotesetup(char *s, Rune *r, int nin, int nout, Quoteinfo *q, int sharp, int runesout);
int          __quotestrfmt(int runesin, Fmt *f);
int          __rfmtpad(Fmt *f, int n);
int          __runefmt(Fmt *f);
int          __runeneedsquotes(Rune *r, int *quotelenp);
int          __runesfmt(Fmt *f);
int          __strfmt(Fmt *f);

#define FMTCHAR(f, t, s, c)\
	do{\
	if(t + 1 > (char*)s){\
		t = __fmtflush(f, t, 1);\
		if(t != nil)\
			s = f->stop;\
		else\
			return -1;\
	}\
	*t++ = c;\
	}while(0)

#define FMTRCHAR(f, t, s, c)\
	do{\
	if(t + 1 > (Rune*)s){\
		t = __fmtflush(f, t, sizeof(Rune));\
		if(t != nil)\
			s = f->stop;\
		else\
			return -1;\
	}\
	*t++ = c;\
	}while(0)

#define FMTRUNE(f, t, s, r)\
	do{\
	Rune _rune;\
	int _runelen;\
	if(t + UTFmax > (char*)s && t + (_runelen = runelen(r)) > (char*)s){\
		t = __fmtflush(f, t, _runelen);\
		if(t != nil)\
			s = f->stop;\
		else\
			return -1;\
	}\
	if(r < Runeself)\
		*t++ = r;\
	else{\
		_rune = r;\
		t += runetochar(t, &_rune);\
	}\
	}while(0)

#ifndef va_copy
#	define va_copy(a,b) (a) = (b)
#endif

Added lib/libfmt/fmtfd.c.
































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
 * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */
#include <stdarg.h>
#include <string.h>
#include "plan9.h"
#include "fmt.h"
#include "fmtdef.h"

/*
 * public routine for final flush of a formatting buffer
 * to a file descriptor; returns total char count.
 */
int
fmtfdflush(Fmt *f)
{
	if(__fmtFdFlush(f) <= 0)
		return -1;
	return f->nfmt;
}

/*
 * initialize an output buffer for buffered printing
 */
int
fmtfdinit(Fmt *f, int fd, char *buf, int size)
{
	f->runes = 0;
	f->start = buf;
	f->to = buf;
	f->stop = buf + size;
	f->flush = __fmtFdFlush;
	f->farg = (void*)(uintptr_t)fd;
	f->flags = 0;
	f->nfmt = 0;
	fmtlocaleinit(f, nil, nil, nil);
	return 0;
}
Added lib/libfmt/fmtfdflush.c.




































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
 * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */
#include <stdarg.h>
#include <unistd.h>
#include "plan9.h"
#include "fmt.h"
#include "fmtdef.h"

/*
 * generic routine for flushing a formatting buffer
 * to a file descriptor
 */
int
__fmtFdFlush(Fmt *f)
{
	int n;

	n = (char*)f->to - (char*)f->start;
	if(n && write((uintptr_t)f->farg, f->start, n) != n)
		return 0;
	f->to = f->start;
	return 1;
}
Added lib/libfmt/fmtinstall.3.






















































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
.deEX
.ift .ft5
.nf
..
.deEE
.ft1
.fi
..
.TH FMTINSTALL 3
.SH NAME
fmtinstall, dofmt, dorfmt, fmtprint, fmtvprint, fmtrune, fmtstrcpy, fmtrunestrcpy, fmtfdinit, fmtfdflush, fmtstrinit, fmtstrflush, runefmtstrinit, runefmtstrflush, errfmt \- support for user-defined print formats and output routines
.SH SYNOPSIS
.B #include <utf.h>
.br
.B #include <fmt.h>
.PP
.ft L
.nf
.ta \w'    'u +\w'    'u +\w'    'u +\w'    'u +\w'    'u
typedef struct Fmt	Fmt;
struct Fmt{
	uchar	runes;		/* output buffer is runes or chars? */
	void	*start;		/* of buffer */
	void	*to;		/* current place in the buffer */
	void	*stop;		/* end of the buffer; overwritten if flush fails */
	int		(*flush)(Fmt*);	/* called when to == stop */
	void	*farg;		/* to make flush a closure */
	int		nfmt;		/* num chars formatted so far */
	va_list	args;		/* args passed to dofmt */
	int		r;			/* % format Rune */
	int		width;
	int		prec;
	ulong	flags;
};

enum{
	FmtWidth	= 1,
	FmtLeft		= FmtWidth << 1,
	FmtPrec		= FmtLeft << 1,
	FmtSharp	= FmtPrec << 1,
	FmtSpace	= FmtSharp << 1,
	FmtSign		= FmtSpace << 1,
	FmtZero		= FmtSign << 1,
	FmtUnsigned	= FmtZero << 1,
	FmtShort	= FmtUnsigned << 1,
	FmtLong		= FmtShort << 1,
	FmtVLong	= FmtLong << 1,
	FmtComma	= FmtVLong << 1,

	FmtFlag		= FmtComma << 1
};
.fi
.PP
.B
.ta \w'\fLchar* 'u

.PP
.B
int	fmtfdinit(Fmt *f, int fd, char *buf, int nbuf);
.PP
.B
int	fmtfdflush(Fmt *f);
.PP
.B
int	fmtstrinit(Fmt *f);
.PP
.B
char*	fmtstrflush(Fmt *f);
.PP
.B
int	runefmtstrinit(Fmt *f);
.PP
.B
Rune*	runefmtstrflush(Fmt *f);

.PP
.B
int	fmtinstall(int c, int (*fn)(Fmt*));
.PP
.B
int	dofmt(Fmt *f, char *fmt);
.PP
.B
int	dorfmt(Fmt*, Rune *fmt);
.PP
.B
int	fmtprint(Fmt *f, char *fmt, ...);
.PP
.B
int	fmtvprint(Fmt *f, char *fmt, va_list v);
.PP
.B
int	fmtrune(Fmt *f, int r);
.PP
.B
int	fmtstrcpy(Fmt *f, char *s);
.PP
.B
int	fmtrunestrcpy(Fmt *f, Rune *s);
.PP
.B
int	errfmt(Fmt *f);
.SH DESCRIPTION
The interface described here allows the construction of custom
.IR print (3)
verbs and output routines.
In essence, they provide access to the workings of the formatted print code.
.PP
The
.IR print (3)
suite maintains its state with a data structure called
.BR Fmt .
A typical call to
.IR print (3)
or its relatives initializes a
.B Fmt
structure, passes it to subsidiary routines to process the output,
and finishes by emitting any saved state recorded in the
.BR Fmt .
The details of the
.B Fmt
are unimportant to outside users, except insofar as the general
design influences the interface.
The
.B Fmt
records whether the output is in runes or bytes,
the verb being processed, its precision and width,
and buffering parameters.
Most important, it also records a
.I flush
routine that the library will call if a buffer overflows.
When printing to a file descriptor, the flush routine will
emit saved characters and reset the buffer; when printing
to an allocated string, it will resize the string to receive more output.
The flush routine is nil when printing to fixed-size buffers.
User code need never provide a flush routine; this is done internally
by the library.
.SS Custom output routines
To write a custom output routine, such as an error handler that
formats and prints custom error messages, the output sequence can be run
from outside the library using the routines described here.
There are two main cases: output to an open file descriptor
and output to a string.
.PP
To write to a file descriptor, call
.I fmtfdinit
to initialize the local
.B Fmt
structure
.IR f ,
giving the file descriptor
.IR fd ,
the buffer
.IR buf ,
and its size
.IR nbuf .
Then call
.IR fmtprint
or
.IR fmtvprint
to generate the output.
These behave like
.B fprint
(see
.IR print (3))
or
.B vfprint
except that the characters are buffered until
.I fmtfdflush
is called and the return value is either 0 or \-1.
A typical example of this sequence appears in the Examples section.
.PP
The same basic sequence applies when outputting to an allocated string:
call
.I fmtstrinit
to initialize the
.BR Fmt ,
then call
.I fmtprint
and
.I fmtvprint
to generate the output.
Finally,
.I fmtstrflush
will return the allocated string, which should be freed after use.
To output to a rune string, use
.I runefmtstrinit
and
.IR runefmtstrflush .
Regardless of the output style or type,
.I fmtprint
or
.I fmtvprint
generates the characters.
.SS Custom format verbs
.I Fmtinstall
is used to install custom verbs and flags labeled by character
.IR c ,
which may be any non-zero Unicode character.
.I Fn
should be declared as
.IP
.EX
int	fn(Fmt*)
.EE
.PP
.IB Fp ->r
is the flag or verb character to cause
.I fn
to be called.
In
.IR fn ,
.IB fp ->width ,
.IB fp ->prec
are the width and precision, and
.IB fp ->flags
the decoded flags for the verb (see
.IR print (3)
for a description of these items).
The standard flag values are:
.B FmtSign
.RB ( + ),
.B FmtLeft
.RB ( - ),
.B FmtSpace
.RB ( '\ ' ),
.B FmtSharp
.RB ( # ),
.B FmtComma
.RB ( , ),
.B FmtLong
.RB ( l ),
.B FmtShort
.RB ( h ),
.B FmtUnsigned
.RB ( u ),
and
.B FmtVLong
.RB ( ll ).
The flag bits
.B FmtWidth
and
.B FmtPrec
identify whether a width and precision were specified.
.PP
.I Fn
is passed a pointer to the
.B Fmt
structure recording the state of the output.
If
.IB fp ->r
is a verb (rather than a flag),
.I fn
should use 
.B Fmt->args
to fetch its argument from the list,
then format it, and return zero.
If
.IB fp ->r
is a flag,
.I fn
should return one.
All interpretation of
.IB fp ->width\f1,
.IB fp ->prec\f1,
and
.IB fp-> flags
is left up to the conversion routine.
.I Fmtinstall
returns 0 if the installation succeeds, \-1 if it fails.
.PP
.IR Fmtprint
and
.IR fmtvprint
may be called to
help prepare output in custom conversion routines.
However, these functions clear the width, precision, and flags.
Both functions return 0 for success and \-1 for failure.
.PP
The functions
.I dofmt
and
.I dorfmt
are the underlying formatters; they
use the existing contents of
.B Fmt
and should be called only by sophisticated conversion routines.
These routines return the number of characters (bytes of UTF or runes)
produced.
.PP
Some internal functions may be useful to format primitive types.
They honor the width, precision and flags as described in
.IR print (3).
.I Fmtrune
formats a single character
.BR r .
.I Fmtstrcpy
formats a string
.BR s ;
.I fmtrunestrcpy
formats a rune string
.BR s .
.I Errfmt
formats the system error string.
All these routines return zero for successful execution.
Conversion routines that call these functions will work properly
regardless of whether the output is bytes or runes.
.\" .PP
.\" .IR 2c (1)
.\" describes the C directive
.\" .B #pragma
.\" .B varargck
.\" that can be used to provide type-checking for custom print verbs and output routines.
.SH EXAMPLES
This function prints an error message with a variable
number of arguments and then quits.
Compared to the corresponding example in
.IR print (3),
this version uses a smaller buffer, will never truncate
the output message, but might generate multiple
.B write
system calls to produce its output.
.IP
.EX
.ta 6n +6n +6n +6n +6n +6n +6n +6n +6n
#pragma	varargck	argpos	error	1

void fatal(char *fmt, ...)
{
	Fmt f;
	char buf[64];
	va_list arg;

	fmtfdinit(&f, 1, buf, sizeof buf);
	fmtprint(&f, "fatal: ");
	va_start(arg, fmt);
	fmtvprint(&f, fmt, arg);
	va_end(arg);
	fmtprint(&f, "\en");
	fmtfdflush(&f);
	exits("fatal error");
}
.EE
.PP
This example adds a verb to print complex numbers.
.IP
.EX
typedef
struct {
	double	r, i;
} Complex;

#pragma	varargck	type	"X"	Complex

int
Xfmt(Fmt *f)
{
	Complex c;

	c = va_arg(f->args, Complex);
	return fmtprint(f, "(%g,%g)", c.r, c.i);
}

main(...)
{
	Complex x = (Complex){ 1.5, -2.3 };

	fmtinstall('X', Xfmt);
	print("x = %X\en", x);
}
.EE
.SH SOURCE
.B http://swtch.com/plan9port/unix
.SH SEE ALSO
.IR print (3),
.IR utf (7)
.SH DIAGNOSTICS
These routines return negative numbers or nil for errors and set
.IR errstr .
Added lib/libfmt/fmtlocale.c.














































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
/* Copyright (c) 2004 Google Inc.; see LICENSE */

#include <stdarg.h>
#include <string.h>
#include "plan9.h"
#include "fmt.h"
#include "fmtdef.h"

/*
 * Fill in the internationalization stuff in the State structure.
 * For nil arguments, provide the sensible defaults:
 *	decimal is a period
 *	thousands separator is a comma
 *	thousands are marked every three digits
 */
void
fmtlocaleinit(Fmt *f, char *decimal, char *thousands, char *grouping)
{
	if(decimal == nil || decimal[0] == '\0')
		decimal = ".";
	if(thousands == nil)
		thousands = ",";
	if(grouping == nil)
		grouping = "\3";
	f->decimal = decimal;
	f->thousands = thousands;
	f->grouping = grouping;
}

/*
 * We are about to emit a digit in e.g. %'d.  If that digit would
 * overflow a thousands (e.g.) grouping, tell the caller to emit
 * the thousands separator.  Always advance the digit counter
 * and pointer into the grouping descriptor.
 */
int
__needsep(int *ndig, char **grouping)
{
	int group;
	
	(*ndig)++;
	group = *(unsigned char*)*grouping;
	/* CHAR_MAX means no further grouping. \0 means we got the empty string */
	if(group == 0xFF || group == 0x7f || group == 0x00)
		return 0;
	if(*ndig > group){
		/* if we're at end of string, continue with this grouping; else advance */
		if((*grouping)[1] != '\0')
			(*grouping)++;
		*ndig = 1;
		return 1;
	}
	return 0;
}

Added lib/libfmt/fmtlock.c.






















































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
 * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */
#include <stdarg.h>
#include "plan9.h"
#include "fmt.h"
#include "fmtdef.h"

void
__fmtlock(void)
{
}

void
__fmtunlock(void)
{
}
Added lib/libfmt/fmtprint.c.
































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
 * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */
#include <stdarg.h>
#include <string.h>
#include "plan9.h"
#include "fmt.h"
#include "fmtdef.h"

/*
 * format a string into the output buffer
 * designed for formats which themselves call fmt,
 * but ignore any width flags
 */
int
fmtprint(Fmt *f, const char *fmt, ...)
{
	va_list va;
	int n;

	f->flags = 0;
	f->width = 0;
	f->prec = 0;
	va_copy(va, f->args);
	va_end(f->args);
	va_start(f->args, fmt);
	n = dofmt(f, fmt);
	va_end(f->args);
	f->flags = 0;
	f->width = 0;
	f->prec = 0;
	va_copy(f->args,va);
	va_end(va);
	if(n >= 0)
		return 0;
	return n;
}

Added lib/libfmt/fmtquote.c.




















































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
 * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */
#include <stdarg.h>
#include <string.h>
#include "plan9.h"
#include "fmt.h"
#include "fmtdef.h"

/*
 * How many bytes of output UTF will be produced by quoting (if necessary) this string?
 * How many runes? How much of the input will be consumed?
 * The parameter q is filled in by __quotesetup.
 * The string may be UTF or Runes (s or r).
 * Return count does not include NUL.
 * Terminate the scan at the first of:
 *	NUL in input
 *	count exceeded in input
 *	count exceeded on output
 * *ninp is set to number of input bytes accepted.
 * nin may be <0 initially, to avoid checking input by count.
 */
void
__quotesetup(char *s, Rune *r, int nin, int nout, Quoteinfo *q, int sharp, int runesout)
{
	int w;
	Rune c;

	q->quoted = 0;
	q->nbytesout = 0;
	q->nrunesout = 0;
	q->nbytesin = 0;
	q->nrunesin = 0;
	if(sharp || nin==0 || (s && *s=='\0') || (r && *r=='\0')){
		if(nout < 2)
			return;
		q->quoted = 1;
		q->nbytesout = 2;
		q->nrunesout = 2;
	}
	for(; nin!=0; nin--){
		if(s)
			w = chartorune(&c, s);
		else{
			c = *r;
			w = runelen(c);
		}

		if(c == '\0')
			break;
		if(runesout){
			if(q->nrunesout+1 > nout)
				break;
		}else{
			if(q->nbytesout+w > nout)
				break;
		}

		if((c <= L' ') || (c == L'\'') || (fmtdoquote!=nil && fmtdoquote(c))){
			if(!q->quoted){
				if(runesout){
					if(1+q->nrunesout+1+1 > nout)	/* no room for quotes */
						break;
				}else{
					if(1+q->nbytesout+w+1 > nout)	/* no room for quotes */
						break;
				}
				q->nrunesout += 2;	/* include quotes */
				q->nbytesout += 2;	/* include quotes */
				q->quoted = 1;
			}
			if(c == '\'')	{
				if(runesout){
					if(1+q->nrunesout+1 > nout)	/* no room for quotes */
						break;
				}else{
					if(1+q->nbytesout+w > nout)	/* no room for quotes */
						break;
				}
				q->nbytesout++;
				q->nrunesout++;	/* quotes reproduce as two characters */
			}
		}

		/* advance input */
		if(s)
			s += w;
		else
			r++;
		q->nbytesin += w;
		q->nrunesin++;

		/* advance output */
		q->nbytesout += w;
		q->nrunesout++;
	}
}

static int
qstrfmt(char *sin, Rune *rin, Quoteinfo *q, Fmt *f)
{
	Rune r, *rm, *rme;
	char *t, *s, *m, *me;
	Rune *rt, *rs;
	ulong fl;
	int nc, w;

	m = sin;
	me = m + q->nbytesin;
	rm = rin;
	rme = rm + q->nrunesin;

	fl = f->flags;
	w = 0;
	if(fl & FmtWidth)
		w = f->width;
	if(f->runes){
		if(!(fl & FmtLeft) && __rfmtpad(f, w - q->nrunesout) < 0)
			return -1;
	}else{
		if(!(fl & FmtLeft) && __fmtpad(f, w - q->nbytesout) < 0)
			return -1;
	}
	t = (char*)f->to;
	s = (char*)f->stop;
	rt = (Rune*)f->to;
	rs = (Rune*)f->stop;
	if(f->runes)
		FMTRCHAR(f, rt, rs, '\'');
	else
		FMTRUNE(f, t, s, '\'');
	for(nc = q->nrunesin; nc > 0; nc--){
		if(sin){
			r = *(uchar*)m;
			if(r < Runeself)
				m++;
			else if((me - m) >= UTFmax || fullrune(m, me-m))
				m += chartorune(&r, m);
			else
				break;
		}else{
			if(rm >= rme)
				break;
			r = *(uchar*)rm++;
		}
		if(f->runes){
			FMTRCHAR(f, rt, rs, r);
			if(r == '\'')
				FMTRCHAR(f, rt, rs, r);
		}else{
			FMTRUNE(f, t, s, r);
			if(r == '\'')
				FMTRUNE(f, t, s, r);
		}
	}

	if(f->runes){
		FMTRCHAR(f, rt, rs, '\'');
		USED(rs);
		f->nfmt += rt - (Rune *)f->to;
		f->to = rt;
		if(fl & FmtLeft && __rfmtpad(f, w - q->nrunesout) < 0)
			return -1;
	}else{
		FMTRUNE(f, t, s, '\'');
		USED(s);
		f->nfmt += t - (char *)f->to;
		f->to = t;
		if(fl & FmtLeft && __fmtpad(f, w - q->nbytesout) < 0)
			return -1;
	}
	return 0;
}

int
__quotestrfmt(int runesin, Fmt *f)
{
	int nin, outlen;
	Rune *r;
	char *s;
	Quoteinfo q;

	nin = -1;
	if(f->flags&FmtPrec)
		nin = f->prec;
	if(runesin){
		r = va_arg(f->args, Rune *);
		s = nil;
	}else{
		s = va_arg(f->args, char *);
		r = nil;
	}
	if(!s && !r)
		return __fmtcpy(f, (void*)"<nil>", 5, 5);

	if(f->flush)
		outlen = 0x7FFFFFFF;	/* if we can flush, no output limit */
	else if(f->runes)
		outlen = (Rune*)f->stop - (Rune*)f->to;
	else
		outlen = (char*)f->stop - (char*)f->to;

	__quotesetup(s, r, nin, outlen, &q, f->flags&FmtSharp, f->runes);
/*print("bytes in %d bytes out %d runes in %d runesout %d\n", q.nbytesin, q.nbytesout, q.nrunesin, q.nrunesout); */

	if(runesin){
		if(!q.quoted)
			return __fmtrcpy(f, r, q.nrunesin);
		return qstrfmt(nil, r, &q, f);
	}

	if(!q.quoted)
		return __fmtcpy(f, s, q.nrunesin, q.nbytesin);
	return qstrfmt(s, nil, &q, f);
}

int
quotestrfmt(Fmt *f)
{
	return __quotestrfmt(0, f);
}

int
quoterunestrfmt(Fmt *f)
{
	return __quotestrfmt(1, f);
}

void
quotefmtinstall(void)
{
	fmtinstall('q', quotestrfmt);
	fmtinstall('Q', quoterunestrfmt);
}

int
__needsquotes(char *s, int *quotelenp)
{
	Quoteinfo q;

	__quotesetup(s, nil, -1, 0x7FFFFFFF, &q, 0, 0);
	*quotelenp = q.nbytesout;

	return q.quoted;
}

int
__runeneedsquotes(Rune *r, int *quotelenp)
{
	Quoteinfo q;

	__quotesetup(nil, r, -1, 0x7FFFFFFF, &q, 0, 0);
	*quotelenp = q.nrunesout;

	return q.quoted;
}
Added lib/libfmt/fmtrune.c.
















































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
 * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */
#include <stdarg.h>
#include <string.h>
#include "plan9.h"
#include "fmt.h"
#include "fmtdef.h"

int
fmtrune(Fmt *f, int r)
{
	Rune *rt;
	char *t;
	int n;

	if(f->runes){
		rt = (Rune*)f->to;
		FMTRCHAR(f, rt, f->stop, r);
		f->to = rt;
		n = 1;
	}else{
		t = (char*)f->to;
		FMTRUNE(f, t, f->stop, r);
		n = t - (char*)f->to;
		f->to = t;
	}
	f->nfmt += n;
	return 0;
}
Added lib/libfmt/fmtstr.c.
























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
 * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */
#include <stdlib.h>
#include <stdarg.h>
#include "plan9.h"
#include "fmt.h"
#include "fmtdef.h"

char*
fmtstrflush(Fmt *f)
{
	if(f->start == nil)
		return nil;
	*(char*)f->to = '\0';
	f->to = f->start;
	return (char*)f->start;
}
Added lib/libfmt/fmtvprint.c.


































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
 * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */
#include <stdarg.h>
#include <string.h>
#include "plan9.h"
#include "fmt.h"
#include "fmtdef.h"


/*
 * format a string into the output buffer
 * designed for formats which themselves call fmt,
 * but ignore any width flags
 */
int
fmtvprint(Fmt *f, const char *fmt, va_list args)
{
	va_list va;
	int n;

	f->flags = 0;
	f->width = 0;
	f->prec = 0;
	va_copy(va,f->args);
	va_end(f->args);
	va_copy(f->args,args);
	n = dofmt(f, fmt);
	f->flags = 0;
	f->width = 0;
	f->prec = 0;
	va_end(f->args);
	va_copy(f->args,va);
	va_end(va);
	if(n >= 0)
		return 0;
	return n;
}

Added lib/libfmt/fprint.c.


























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
 * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */
#include <stdarg.h>
#include "plan9.h"
#include "fmt.h"
#include "fmtdef.h"

int
fprint(int fd, const char *fmt, ...)
{
	int n;
	va_list args;

	va_start(args, fmt);
	n = vfprint(fd, fmt, args);
	va_end(args);
	return n;
}
Added lib/libfmt/libfmt.a.

cannot compute difference between binary files

Added lib/libfmt/nan64.c.
























































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
/*
 * 64-bit IEEE not-a-number routines.
 * This is big/little-endian portable assuming that
 * the 64-bit doubles and 64-bit integers have the
 * same byte ordering.
 */

#include "plan9.h"
#include <assert.h>
#include "fmt.h"
#include "fmtdef.h"

static uvlong uvnan    = ((uvlong)0x7FF00000<<32)|0x00000001;
static uvlong uvinf    = ((uvlong)0x7FF00000<<32)|0x00000000;
static uvlong uvneginf = ((uvlong)0xFFF00000<<32)|0x00000000;

/* gcc sees through the obvious casts. */
static uvlong
d2u(double d)
{
	union {
		uvlong v;
		double d;
	} u;
	assert(sizeof(u.d) == sizeof(u.v));
	u.d = d;
	return u.v;
}

static double
u2d(uvlong v)
{
	union {
		uvlong v;
		double d;
	} u;
	assert(sizeof(u.d) == sizeof(u.v));
	u.v = v;
	return u.d;
}

double
__NaN(void)
{
	return u2d(uvnan);
}

int
__isNaN(double d)
{
	uvlong x;
	
	x = d2u(d);
	/* IEEE 754: exponent bits 0x7FF and non-zero mantissa */
	return (x&uvinf) == uvinf && (x&~uvneginf) != 0;
}

double
__Inf(int sign)
{
	return u2d(sign < 0 ? uvneginf : uvinf);
}

int
__isInf(double d, int sign)
{
	uvlong x;
	
	x = d2u(d);
	if(sign == 0)
		return x==uvinf || x==uvneginf;
	else if(sign > 0)
		return x==uvinf;
	else
		return x==uvneginf;
}
Added lib/libfmt/pow10.c.


















































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
 * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */
#include <stdarg.h>
#include <string.h>
#include "plan9.h"
#include "fmt.h"
#include "fmtdef.h"

/*
 * this table might overflow 127-bit exponent representations.
 * in that case, truncate it after 1.0e38.
 * it is important to get all one can from this
 * routine since it is used in atof to scale numbers.
 * the presumption is that C converts fp numbers better
 * than multipication of lower powers of 10.
 */

static
double	tab[] =
{
	1.0e0, 1.0e1, 1.0e2, 1.0e3, 1.0e4, 1.0e5, 1.0e6, 1.0e7, 1.0e8, 1.0e9,
	1.0e10,1.0e11,1.0e12,1.0e13,1.0e14,1.0e15,1.0e16,1.0e17,1.0e18,1.0e19,
	1.0e20,1.0e21,1.0e22,1.0e23,1.0e24,1.0e25,1.0e26,1.0e27,1.0e28,1.0e29,
	1.0e30,1.0e31,1.0e32,1.0e33,1.0e34,1.0e35,1.0e36,1.0e37,1.0e38,1.0e39,
	1.0e40,1.0e41,1.0e42,1.0e43,1.0e44,1.0e45,1.0e46,1.0e47,1.0e48,1.0e49,
	1.0e50,1.0e51,1.0e52,1.0e53,1.0e54,1.0e55,1.0e56,1.0e57,1.0e58,1.0e59,
	1.0e60,1.0e61,1.0e62,1.0e63,1.0e64,1.0e65,1.0e66,1.0e67,1.0e68,1.0e69,
};

double
__fmtpow10(int n)
{
	int m;

	if(n < 0) {
		n = -n;
		if(n < (int)(sizeof(tab)/sizeof(tab[0])))
			return 1/tab[n];
		m = n/2;
		return __fmtpow10(-m) * __fmtpow10(m-n);
	}
	if(n < (int)(sizeof(tab)/sizeof(tab[0])))
		return tab[n];
	m = n/2;
	return __fmtpow10(m) * __fmtpow10(n-m);
}
Added lib/libfmt/print.3.




































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
.deEX
.ift .ft5
.nf
..
.deEE
.ft1
.fi
..
.\" diffs from /usr/local/plan9/man/man3/print.3:
.\"
.\" - include different headers
.\" - drop reference to bio(3)
.\" - change exits to exit
.\" - text about unsigned verbs
.\" - source pointer
.\"
.TH PRINT 3
.SH NAME
print, fprint, sprint, snprint, seprint, smprint, runesprint, runesnprint, runeseprint, runesmprint, vfprint, vsnprint, vseprint, vsmprint, runevsnprint, runevseprint, runevsmprint \- print formatted output
.SH SYNOPSIS
.B #include <utf.h>
.PP
.B #include <fmt.h>
.PP
.ta \w'\fLchar* 'u
.B
int	print(char *format, ...)
.PP
.B
int	fprint(int fd, char *format, ...)
.PP
.B
int	sprint(char *s, char *format, ...)
.PP
.B
int	snprint(char *s, int len, char *format, ...)
.PP
.B
char*	seprint(char *s, char *e, char *format, ...)
.PP
.B
char*	smprint(char *format, ...)
.PP
.B
int	runesprint(Rune *s, char *format, ...)
.PP
.B
int	runesnprint(Rune *s, int len, char *format, ...)
.PP
.B
Rune*	runeseprint(Rune *s, Rune *e, char *format, ...)
.PP
.B
Rune*	runesmprint(char *format, ...)
.PP
.B
int	vfprint(int fd, char *format, va_list v)
.PP
.B
int	vsnprint(char *s, int len, char *format, va_list v)
.PP
.B
char*	vseprint(char *s, char *e, char *format, va_list v)
.PP
.B
char*	vsmprint(char *format, va_list v)
.PP
.B
int	runevsnprint(Rune *s, int len, char *format, va_list v)
.PP
.B
Rune*	runevseprint(Rune *s, Rune *e, char *format, va_list v)
.PP
.B
Rune*	runevsmprint(Rune *format, va_list v)
.PP
.B
.SH DESCRIPTION
.I Print
writes text to the standard output.
.I Fprint
writes to the named output
file descriptor:
a buffered form
is described in
.IR bio (3).
.I Sprint
places text
followed by the NUL character
.RB ( \e0 )
in consecutive bytes starting at
.IR s ;
it is the user's responsibility to ensure that
enough storage is available.
Each function returns the number of bytes
transmitted (not including the NUL
in the case of
.IR sprint ),
or
a negative value if an output error was encountered.
.PP
.I Snprint
is like
.IR sprint ,
but will not place more than
.I len
bytes in
.IR s .
Its result is always NUL-terminated and holds the maximal
number of complete UTF-8 characters that can fit.
.I Seprint
is like
.IR snprint ,
except that the end is indicated by a pointer
.I e
rather than a count and the return value points to the terminating NUL of the
resulting string.
.I Smprint
is like
.IR sprint ,
except that it prints into and returns a string of the required length, which is
allocated by
.IR malloc (3).
.PP
The routines
.IR runesprint ,
.IR runesnprint ,
.IR runeseprint ,
and
.I runesmprint
are the same as
.IR sprint ,
.IR snprint ,
.IR seprint
and
.I smprint
except that their output is rune strings instead of byte strings.
.PP
Finally, the routines
.IR vfprint ,
.IR vsnprint ,
.IR vseprint ,
.IR vsmprint ,
.IR runevsnprint ,
.IR runevseprint ,
and
.I runevsmprint
are like their
.BR v-less
relatives except they take as arguments a
.B va_list
parameter, so they can be called within a variadic function.
The Example section shows a representative usage.
.PP
Each of these functions
converts, formats, and prints its
trailing arguments
under control of a
.IR format 
string.
The
format
contains two types of objects:
plain characters, which are simply copied to the
output stream,
and conversion specifications,
each of which results in fetching of
zero or more
arguments.
The results are undefined if there are arguments of the
wrong type or too few
arguments for the format.
If the format is exhausted while
arguments remain, the excess
is ignored.
.PP
Each conversion specification has the following format:
.IP
.B "% [flags] verb
.PP
The verb is a single character and each flag is a single character or a
(decimal) numeric string.
Up to two numeric strings may be used;
the first is called
.IR width ,
the second
.IR precision .
A period can be used to separate them, and if the period is
present then
.I width
and
.I precision
are taken to be zero if missing, otherwise they are `omitted'.
Either or both of the numbers may be replaced with the character
.BR * ,
meaning that the actual number will be obtained from the argument list
as an integer.
The flags and numbers are arguments to
the
.I verb
described below.
.PP
The numeric verbs
.BR d ,
.BR i ,
.BR u ,
.BR o ,
.BR b ,
.BR x ,
and
.B X
format their arguments in decimal, decimal,
unsigned decimal, octal, binary, hexadecimal, and upper case hexadecimal.
Each interprets the flags
.BR 0 ,
.BR h ,
.BR hh ,
.BR l ,
.BR + ,
.BR - ,
.BR , ,
and
.B #
to mean pad with zeros,
short, byte, long, always print a sign, left justified, commas every three digits,
and alternate format.
Also, a space character in the flag
position is like
.BR + ,
but prints a space instead of a plus sign for non-negative values.
If neither
short nor long is specified,
then the argument is an
.BR int .
If an unsigned verb is specified,
then the argument is interpreted as a
positive number and no sign is output;
space and
.B +
flags are ignored for unsigned verbs.
If two
.B l
flags are given,
then the argument is interpreted as a
.B vlong
(usually an 8-byte, sometimes a 4-byte integer).
If
.I precision
is not omitted, the number is padded on the left with zeros
until at least
.I precision
digits appear.
If
.I precision
is explicitly 0, and the number is 0,
no digits are generated, and alternate formatting
does not apply.
Then, if alternate format is specified,
for
.B o
conversion, the number is preceded by a
.B 0
if it doesn't already begin with one.
For non-zero numbers and
.B x
conversion, the number is preceded by
.BR 0x ;
for
.B X
conversion, the number is preceded by
.BR 0X .
Finally, if
.I width
is not omitted, the number is padded on the left (or right, if
left justification is specified) with enough blanks to
make the field at least
.I width
characters long.
.PP
The floating point verbs
.BR f ,
.BR e ,
.BR E ,
.BR g ,
and
.B G
take a
.B double
argument.
Each interprets the flags
.BR 0 ,
.BR L
.BR + ,
.BR - ,
and
.B #
to mean pad with zeros,
long double argument,
always print a sign,
left justified,
and
alternate format.
.I Width
is the minimum field width and,
if the converted value takes up less than
.I width
characters, it is padded on the left (or right, if `left justified')
with spaces.
.I Precision
is the number of digits that are converted after the decimal place for
.BR e ,
.BR E ,
and
.B f
conversions,
and
.I precision
is the maximum number of significant digits for
.B g
and
.B G
conversions.
The 
.B f
verb produces output of the form
.RB [ - ] digits [ .digits\fR].
.B E
conversion appends an exponent
.BR E [ - ] digits ,
and
.B e
conversion appends an exponent
.BR e [ - ] digits .
The
.B g
verb will output the argument in either
.B e
or
.B f
with the goal of producing the smallest output.
Also, trailing zeros are omitted from the fraction part of
the output, and a trailing decimal point appears only if it is followed
by a digit.
The
.B G
verb is similar, but uses
.B E
format instead of
.BR e .
When alternate format is specified, the result will always contain a decimal point,
and for
.B g
and
.B G
conversions, trailing zeros are not removed.
.PP
The
.B s
verb copies a string
(pointer to
.BR char )
to the output.
The number of characters copied
.RI ( n )
is the minimum
of the size of the string and
.IR precision .
These
.I n
characters are justified within a field of
.I width
characters as described above.
If a
.I precision
is given, it is safe for the string not to be nul-terminated
as long as it is at least
.I precision
characters (not bytes!) long.
The
.B S
verb is similar, but it interprets its pointer as an array
of runes (see
.IR utf (7));
the runes are converted to
.SM UTF
before output.
.PP
The
.B c
verb copies a single
.B char
(promoted to
.BR int )
justified within a field of
.I width
characters as described above.
The
.B C
verb is similar, but works on runes.
.PP
The
.B p
verb formats a pointer value.
At the moment, it is a synonym for
.BR x ,
but that will change if pointers and integers are different sizes.
.PP
The
.B r
verb takes no arguments; it copies the error string returned by a call to
.IR strerror (3)
with an argument of
.IR errno.
.PP
Custom verbs may be installed using
.IR fmtinstall (3).
.SH EXAMPLE
This function prints an error message with a variable
number of arguments and then quits.
.IP
.EX
.ta 6n +6n +6n
void fatal(char *msg, ...)
{
	char buf[1024], *out;
	va_list arg;

	out = seprint(buf, buf+sizeof buf, "Fatal error: ");
	va_start(arg, msg);
	out = vseprint(out, buf+sizeof buf, msg, arg);
	va_end(arg);
	write(2, buf, out-buf);
	exit(1);
}
.EE
.SH SOURCE
.B http://swtch.com/plan9port/unix
.SH SEE ALSO
.IR fmtinstall (3),
.IR fprintf (3),
.IR utf (7)
.SH DIAGNOSTICS
Routines that write to a file descriptor or call
.IR malloc
set
.IR errstr .
.SH BUGS
The formatting is close to that specified for ANSI
.IR fprintf (3);
the main difference is that
.B b
and
.B r
are not in ANSI and some
.B C9X
verbs and syntax are missing.
Also, and distinctly not a bug,
.I print
and friends generate
.SM UTF
rather than
.SM ASCII.
.PP
There is no
.IR runeprint ,
.IR runefprint ,
etc. because runes are byte-order dependent and should not be written directly to a file; use the
UTF output of
.I print
or
.I fprint
instead.
Also,
.I sprint
is deprecated for safety reasons; use
.IR snprint ,
.IR seprint ,
or
.I smprint
instead.
Safety also precludes the existence of
.IR runesprint .
Added lib/libfmt/print.c.


























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
 * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */
#include <stdarg.h>
#include "plan9.h"
#include "fmt.h"
#include "fmtdef.h"

int
print(const char *fmt, ...)
{
	int n;
	va_list args;

	va_start(args, fmt);
	n = vfprint(1, fmt, args);
	va_end(args);
	return n;
}
Added lib/libfmt/runefmtstr.c.
























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
 * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */
#include <stdarg.h>
#include <stdlib.h>
#include "plan9.h"
#include "fmt.h"
#include "fmtdef.h"

Rune*
runefmtstrflush(Fmt *f)
{
	if(f->start == nil)
		return nil;
	*(Rune*)f->to = '\0';
	f->to = f->start;
	return f->start;
}
Added lib/libfmt/runeseprint.c.




























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
 * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */
#include <stdarg.h>
#include <string.h>
#include "plan9.h"
#include "fmt.h"
#include "fmtdef.h"

Rune*
runeseprint(Rune *buf, Rune *e, const char *fmt, ...)
{
	Rune *p;
	va_list args;

	va_start(args, fmt);
	p = runevseprint(buf, e, fmt, args);
	va_end(args);
	return p;
}
Added lib/libfmt/runesmprint.c.




























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
 * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */
#include <stdarg.h>
#include <string.h>
#include "plan9.h"
#include "fmt.h"
#include "fmtdef.h"

Rune*
runesmprint(const char *fmt, ...)
{
	va_list args;
	Rune *p;

	va_start(args, fmt);
	p = runevsmprint(fmt, args);
	va_end(args);
	return p;
}
Added lib/libfmt/runesnprint.c.






























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
 * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */
#include <stdarg.h>
#include <string.h>
#include "plan9.h"
#include "fmt.h"
#include "fmtdef.h"

int
runesnprint(Rune *buf, int len, const char *fmt, ...)
{
	int n;
	va_list args;

	va_start(args, fmt);
	n = runevsnprint(buf, len, fmt, args);
	va_end(args);
	return n;
}

Added lib/libfmt/runesprint.c.




























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
 * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */
#include <stdarg.h>
#include <string.h>
#include "plan9.h"
#include "fmt.h"
#include "fmtdef.h"

int
runesprint(Rune *buf, const char *fmt, ...)
{
	int n;
	va_list args;

	va_start(args, fmt);
	n = runevsnprint(buf, 256, fmt, args);
	va_end(args);
	return n;
}
Added lib/libfmt/runevseprint.c.


















































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
 * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */
#include <stdarg.h>
#include <string.h>
#include "plan9.h"
#include "fmt.h"
#include "fmtdef.h"

Rune*
runevseprint(Rune *buf, Rune *e, const char *fmt, va_list args)
{
	Fmt f;

	if(e <= buf)
		return nil;
	f.runes = 1;
	f.start = buf;
	f.to = buf;
	f.stop = e - 1;
	f.flush = nil;
	f.farg = nil;
	f.nfmt = 0;
	va_copy(f.args,args);
	fmtlocaleinit(&f, nil, nil, nil);
	dofmt(&f, fmt);
	va_end(f.args);
	*(Rune*)f.to = '\0';
	return (Rune*)f.to;
}

Added lib/libfmt/runevsmprint.c.
























































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
 * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */
/*
 * Plan 9 port version must include libc.h in order to
 * get Plan 9 debugging malloc, which sometimes returns
 * different pointers than the standard malloc.
 */
#include <stdlib.h>
#include <string.h>
#include "plan9.h"
#include "fmt.h"
#include "fmtdef.h"

static int
runeFmtStrFlush(Fmt *f)
{
	Rune *s;
	int n;

	if(f->start == nil)
		return 0;
	n = (uintptr_t)f->farg;
	n *= 2;
	s = (Rune*)f->start;
	f->start = realloc(s, sizeof(Rune)*n);
	if(f->start == nil){
		f->farg = nil;
		f->to = nil;
		f->stop = nil;
		free(s);
		return 0;
	}
	f->farg = (void*)(uintptr_t)n;
	f->to = (Rune*)f->start + ((Rune*)f->to - s);
	f->stop = (Rune*)f->start + n - 1;
	return 1;
}

int
runefmtstrinit(Fmt *f)
{
	int n;

	memset(f, 0, sizeof *f);
	f->runes = 1;
	n = 32;
	f->start = malloc(sizeof(Rune)*n);
	if(f->start == nil)
		return -1;
	f->to = f->start;
	f->stop = (Rune*)f->start + n - 1;
	f->flush = runeFmtStrFlush;
	f->farg = (void*)(uintptr_t)n;
	f->nfmt = 0;
	fmtlocaleinit(f, nil, nil, nil);
	return 0;
}

/*
 * print into an allocated string buffer
 */
Rune*
runevsmprint(const char *fmt, va_list args)
{
	Fmt f;
	int n;

	if(runefmtstrinit(&f) < 0)
		return nil;
	va_copy(f.args,args);
	n = dofmt(&f, fmt);
	va_end(f.args);
	if(f.start == nil)
		return nil;
	if(n < 0){
		free(f.start);
		return nil;
	}
	*(Rune*)f.to = '\0';
	return (Rune*)f.start;
}
Added lib/libfmt/runevsnprint.c.
















































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
 * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */
#include <stdarg.h>
#include <string.h>
#include "plan9.h"
#include "fmt.h"
#include "fmtdef.h"

int
runevsnprint(Rune *buf, int len, const char *fmt, va_list args)
{
	Fmt f;

	if(len <= 0)
		return -1;
	f.runes = 1;
	f.start = buf;
	f.to = buf;
	f.stop = buf + len - 1;
	f.flush = nil;
	f.farg = nil;
	f.nfmt = 0;
	va_copy(f.args,args);
	fmtlocaleinit(&f, nil, nil, nil);
	dofmt(&f, fmt);
	va_end(f.args);
	*(Rune*)f.to = '\0';
	return (Rune*)f.to - buf;
}
Added lib/libfmt/seprint.c.


























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
 * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */
#include <stdarg.h>
#include "plan9.h"
#include "fmt.h"
#include "fmtdef.h"

char*
seprint(char *buf, char *e, const char *fmt, ...)
{
	char *p;
	va_list args;

	va_start(args, fmt);
	p = vseprint(buf, e, fmt, args);
	va_end(args);
	return p;
}
Added lib/libfmt/smprint.c.


























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
 * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */
#include <stdarg.h>
#include "plan9.h"
#include "fmt.h"
#include "fmtdef.h"

char*
smprint(const char *fmt, ...)
{
	va_list args;
	char *p;

	va_start(args, fmt);
	p = vsmprint(fmt, args);
	va_end(args);
	return p;
}
Added lib/libfmt/snprint.c.




























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
 * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */
#include <stdarg.h>
#include "plan9.h"
#include "fmt.h"
#include "fmtdef.h"

int
snprint(char *buf, int len, const char *fmt, ...)
{
	int n;
	va_list args;

	va_start(args, fmt);
	n = vsnprint(buf, len, fmt, args);
	va_end(args);
	return n;
}

Added lib/libfmt/sprint.c.
















































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
 * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */
#include <stdarg.h>
#include "plan9.h"
#include "fmtdef.h"

int
sprint(char *buf, const char *fmt, ...)
{
	int n;
	uint len;
	va_list args;

	len = 1<<30;  /* big number, but sprint is deprecated anyway */
	/*
	 * on PowerPC, the stack is near the top of memory, so
	 * we must be sure not to overflow a 32-bit pointer.
	 *
	 * careful!  gcc-4.2 assumes buf+len < buf can never be true and
	 * optimizes the test away.  casting to uintptr works around this bug.
	 */
	if((uintptr_t)buf+len < (uintptr_t)buf)
		len = -(uintptr_t)buf-1;

	va_start(args, fmt);
	n = vsnprint(buf, len, fmt, args);
	va_end(args);
	return n;
}
Added lib/libfmt/strtod.c.








































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
 * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */
#include <stdlib.h>
#include <math.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "plan9.h"
#include "fmt.h"
#include "fmtdef.h"

static ulong
umuldiv(ulong a, ulong b, ulong c)
{
	double d;

	d = ((double)a * (double)b) / (double)c;
	if(d >= 4294967295.)
		d = 4294967295.;
	return (ulong)d;
}

/*
 * This routine will convert to arbitrary precision
 * floating point entirely in multi-precision fixed.
 * The answer is the closest floating point number to
 * the given decimal number. Exactly half way are
 * rounded ala ieee rules.
 * Method is to scale input decimal between .500 and .999...
 * with external power of 2, then binary search for the
 * closest mantissa to this decimal number.
 * Nmant is is the required precision. (53 for ieee dp)
 * Nbits is the max number of bits/word. (must be <= 28)
 * Prec is calculated - the number of words of fixed mantissa.
 */
enum
{
	Nbits	= 28,				/* bits safely represented in a ulong */
	Nmant	= 53,				/* bits of precision required */
	Prec	= (Nmant+Nbits+1)/Nbits,	/* words of Nbits each to represent mantissa */
	Sigbit	= 1<<(Prec*Nbits-Nmant),	/* first significant bit of Prec-th word */
	Ndig	= 1500,
	One	= (ulong)(1<<Nbits),
	Half	= (ulong)(One>>1),
	Maxe	= 310,

	Fsign	= 1<<0,		/* found - */
	Fesign	= 1<<1,		/* found e- */
	Fdpoint	= 1<<2,		/* found . */

	S0	= 0,		/* _		_S0	+S1	#S2	.S3 */
	S1,			/* _+		#S2	.S3 */
	S2,			/* _+#		#S2	.S4	eS5 */
	S3,			/* _+.		#S4 */
	S4,			/* _+#.#	#S4	eS5 */
	S5,			/* _+#.#e	+S6	#S7 */
	S6,			/* _+#.#e+	#S7 */
	S7			/* _+#.#e+#	#S7 */
};

static	int	xcmp(char*, char*);
static	int	fpcmp(char*, ulong*);
static	void	frnorm(ulong*);
static	void	divascii(char*, int*, int*, int*);
static	void	mulascii(char*, int*, int*, int*);

typedef	struct	Tab	Tab;
struct	Tab
{
	int	bp;
	int	siz;
	char*	cmp;
};

double
fmtstrtod(const char *as, char **aas)
{
	int na, ex, dp, bp, c, i, flag, state;
	ulong low[Prec], hig[Prec], mid[Prec];
	double d;
	char *s, a[Ndig];

	flag = 0;	/* Fsign, Fesign, Fdpoint */
	na = 0;		/* number of digits of a[] */
	dp = 0;		/* na of decimal point */
	ex = 0;		/* exonent */

	state = S0;
	for(s=(char*)as;; s++) {
		c = *s;
		if(c >= '0' && c <= '9') {
			switch(state) {
			case S0:
			case S1:
			case S2:
				state = S2;
				break;
			case S3:
			case S4:
				state = S4;
				break;

			case S5:
			case S6:
			case S7:
				state = S7;
				ex = ex*10 + (c-'0');
				continue;
			}
			if(na == 0 && c == '0') {
				dp--;
				continue;
			}
			if(na < Ndig-50)
				a[na++] = c;
			continue;
		}
		switch(c) {
		case '\t':
		case '\n':
		case '\v':
		case '\f':
		case '\r':
		case ' ':
			if(state == S0)
				continue;
			break;
		case '-':
			if(state == S0)
				flag |= Fsign;
			else
				flag |= Fesign;
		case '+':
			if(state == S0)
				state = S1;
			else
			if(state == S5)
				state = S6;
			else
				break;	/* syntax */
			continue;
		case '.':
			flag |= Fdpoint;
			dp = na;
			if(state == S0 || state == S1) {
				state = S3;
				continue;
			}
			if(state == S2) {
				state = S4;
				continue;
			}
			break;
		case 'e':
		case 'E':
			if(state == S2 || state == S4) {
				state = S5;
				continue;
			}
			break;
		}
		break;
	}

	/*
	 * clean up return char-pointer
	 */
	switch(state) {
	case S0:
		if(xcmp(s, "nan") == 0) {
			if(aas != nil)
				*aas = s+3;
			goto retnan;
		}
	case S1:
		if(xcmp(s, "infinity") == 0) {
			if(aas != nil)
				*aas = s+8;
			goto retinf;
		}
		if(xcmp(s, "inf") == 0) {
			if(aas != nil)
				*aas = s+3;
			goto retinf;
		}
	case S3:
		if(aas != nil)
			*aas = (char*)as;
		goto ret0;	/* no digits found */
	case S6:
		s--;		/* back over +- */
	case S5:
		s--;		/* back over e */
		break;
	}
	if(aas != nil)
		*aas = s;

	if(flag & Fdpoint)
	while(na > 0 && a[na-1] == '0')
		na--;
	if(na == 0)
		goto ret0;	/* zero */
	a[na] = 0;
	if(!(flag & Fdpoint))
		dp = na;
	if(flag & Fesign)
		ex = -ex;
	dp += ex;
	if(dp < -Maxe){
		errno = ERANGE;
		goto ret0;	/* underflow by exp */
	} else
	if(dp > +Maxe)
		goto retinf;	/* overflow by exp */

	/*
	 * normalize the decimal ascii number
	 * to range .[5-9][0-9]* e0
	 */
	bp = 0;		/* binary exponent */
	while(dp > 0)
		divascii(a, &na, &dp, &bp);
	while(dp < 0 || a[0] < '5')
		mulascii(a, &na, &dp, &bp);

	/* close approx by naive conversion */
	mid[0] = 0;
	mid[1] = 1;
	for(i=0; (c=a[i]) != '\0'; i++) {
		mid[0] = mid[0]*10 + (c-'0');
		mid[1] = mid[1]*10;
		if(i >= 8)
			break;
	}
	low[0] = umuldiv(mid[0], One, mid[1]);
	hig[0] = umuldiv(mid[0]+1, One, mid[1]);
	for(i=1; i<Prec; i++) {
		low[i] = 0;
		hig[i] = One-1;
	}

	/* binary search for closest mantissa */
	for(;;) {
		/* mid = (hig + low) / 2 */
		c = 0;
		for(i=0; i<Prec; i++) {
			mid[i] = hig[i] + low[i];
			if(c)
				mid[i] += One;
			c = mid[i] & 1;
			mid[i] >>= 1;
		}
		frnorm(mid);

		/* compare */
		c = fpcmp(a, mid);
		if(c > 0) {
			c = 1;
			for(i=0; i<Prec; i++)
				if(low[i] != mid[i]) {
					c = 0;
					low[i] = mid[i];
				}
			if(c)
				break;	/* between mid and hig */
			continue;
		}
		if(c < 0) {
			for(i=0; i<Prec; i++)
				hig[i] = mid[i];
			continue;
		}

		/* only hard part is if even/odd roundings wants to go up */
		c = mid[Prec-1] & (Sigbit-1);
		if(c == Sigbit/2 && (mid[Prec-1]&Sigbit) == 0)
			mid[Prec-1] -= c;
		break;	/* exactly mid */
	}

	/* normal rounding applies */
	c = mid[Prec-1] & (Sigbit-1);
	mid[Prec-1] -= c;
	if(c >= Sigbit/2) {
		mid[Prec-1] += Sigbit;
		frnorm(mid);
	}
	goto out;

ret0:
	return 0;

retnan:
	return __NaN();

retinf:
	/*
	 * Unix strtod requires these.  Plan 9 would return Inf(0) or Inf(-1). */
	errno = ERANGE;
	if(flag & Fsign)
		return -HUGE_VAL;
	return HUGE_VAL;

out:
	d = 0;
	for(i=0; i<Prec; i++)
		d = d*One + mid[i];
	if(flag & Fsign)
		d = -d;
	d = ldexp(d, bp - Prec*Nbits);
	if(d == 0){	/* underflow */
		errno = ERANGE;
	}
	return d;
}

static void
frnorm(ulong *f)
{
	int i, c;

	c = 0;
	for(i=Prec-1; i>0; i--) {
		f[i] += c;
		c = f[i] >> Nbits;
		f[i] &= One-1;
	}
	f[0] += c;
}

static int
fpcmp(char *a, ulong* f)
{
	ulong tf[Prec];
	int i, d, c;

	for(i=0; i<Prec; i++)
		tf[i] = f[i];

	for(;;) {
		/* tf *= 10 */
		for(i=0; i<Prec; i++)
			tf[i] = tf[i]*10;
		frnorm(tf);
		d = (tf[0] >> Nbits) + '0';
		tf[0] &= One-1;

		/* compare next digit */
		c = *a;
		if(c == 0) {
			if('0' < d)
				return -1;
			if(tf[0] != 0)
				goto cont;
			for(i=1; i<Prec; i++)
				if(tf[i] != 0)
					goto cont;
			return 0;
		}
		if(c > d)
			return +1;
		if(c < d)
			return -1;
		a++;
	cont:;
	}
}

static void
divby(char *a, int *na, int b)
{
	int n, c;
	char *p;

	p = a;
	n = 0;
	while(n>>b == 0) {
		c = *a++;
		if(c == 0) {
			while(n) {
				c = n*10;
				if(c>>b)
					break;
				n = c;
			}
			goto xx;
		}
		n = n*10 + c-'0';
		(*na)--;
	}
	for(;;) {
		c = n>>b;
		n -= c<<b;
		*p++ = c + '0';
		c = *a++;
		if(c == 0)
			break;
		n = n*10 + c-'0';
	}
	(*na)++;
xx:
	while(n) {
		n = n*10;
		c = n>>b;
		n -= c<<b;
		*p++ = c + '0';
		(*na)++;
	}
	*p = 0;
}

static	Tab	tab1[] =
{
	 1,  0, "",
	 3,  1, "7",
	 6,  2, "63",
	 9,  3, "511",
	13,  4, "8191",
	16,  5, "65535",
	19,  6, "524287",
	23,  7, "8388607",
	26,  8, "67108863",
	27,  9, "134217727",
};

static void
divascii(char *a, int *na, int *dp, int *bp)
{
	int b, d;
	Tab *t;

	d = *dp;
	if(d >= (int)(nelem(tab1)))
		d = (int)(nelem(tab1))-1;
	t = tab1 + d;
	b = t->bp;
	if(memcmp(a, t->cmp, t->siz) > 0)
		d--;
	*dp -= d;
	*bp += b;
	divby(a, na, b);
}

static void
mulby(char *a, char *p, char *q, int b)
{
	int n, c;

	n = 0;
	*p = 0;
	for(;;) {
		q--;
		if(q < a)
			break;
		c = *q - '0';
		c = (c<<b) + n;
		n = c/10;
		c -= n*10;
		p--;
		*p = c + '0';
	}
	while(n) {
		c = n;
		n = c/10;
		c -= n*10;
		p--;
		*p = c + '0';
	}
}

static	Tab	tab2[] =
{
	 1,  1, "",				/* dp = 0-0 */
	 3,  3, "125",
	 6,  5, "15625",
	 9,  7, "1953125",
	13, 10, "1220703125",
	16, 12, "152587890625",
	19, 14, "19073486328125",
	23, 17, "11920928955078125",
	26, 19, "1490116119384765625",
	27, 19, "7450580596923828125",		/* dp 8-9 */
};

static void
mulascii(char *a, int *na, int *dp, int *bp)
{
	char *p;
	int d, b;
	Tab *t;

	d = -*dp;
	if(d >= (int)(nelem(tab2)))
		d = (int)(nelem(tab2))-1;
	t = tab2 + d;
	b = t->bp;
	if(memcmp(a, t->cmp, t->siz) < 0)
		d--;
	p = a + *na;
	*bp -= b;
	*dp += d;
	*na += d;
	mulby(a, p+d, p, b);
}

static int
xcmp(char *a, char *b)
{
	int c1, c2;

	while((c1 = *b++) != '\0') {
		c2 = *a++;
		if(isupper(c2))
			c2 = tolower(c2);
		if(c1 != c2)
			return 1;
	}
	return 0;
}
Added lib/libfmt/test.c.






























































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
 * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */
#include <stdio.h>
#include <stdarg.h>
#include <utf.h>
#include "plan9.h"
#include "fmt.h"
#include "fmtdef.h"

int
main(int argc, char *argv[])
{
	quotefmtinstall();
	print("hello world\n");
	print("x: %x\n", 0x87654321);
	print("u: %u\n", 0x87654321);
	print("d: %d\n", 0x87654321);
	print("s: %s\n", "hi there");
	print("q: %q\n", "hi i'm here");
	print("c: %c\n", '!');
	print("g: %g %g %g\n", 3.14159, 3.14159e10, 3.14159e-10);
	print("e: %e %e %e\n", 3.14159, 3.14159e10, 3.14159e-10);
	print("f: %f %f %f\n", 3.14159, 3.14159e10, 3.14159e-10);
	print("smiley: %C\n", (Rune)0x263a);
	print("%g %.18g\n", 2e25, 2e25);
	print("%2.18g\n", 1.0);
	print("%2.18f\n", 1.0);
	print("%f\n", 3.1415927/4);
	print("%d\n", 23);
	print("%i\n", 23);
	print("%0.10d\n", 12345);

	/* test %4$d formats */
	print("%3$d %4$06d %2$d %1$d\n", 444, 333, 111, 222);
	print("%3$d %4$06d %2$d %1$d\n", 444, 333, 111, 222);
	print("%3$d %4$*5$06d %2$d %1$d\n", 444, 333, 111, 222, 20);
	print("%3$hd %4$*5$06d %2$d %1$d\n", 444, 333, (short)111, 222, 20);
	print("%3$lld %4$*5$06d %2$d %1$d\n", 444, 333, 111LL, 222, 20);

	/* test %'d formats */
	print("%'d %'d %'d\n", 1, 2222, 33333333);
	print("%'019d\n", 0);
	print("%08d %08d %08d\n", 1, 2222, 33333333);
	print("%'08d %'08d %'08d\n", 1, 2222, 33333333);
	print("%'x %'X %'b\n", 0x11111111, 0xabcd1234, 12345);
	print("%'lld %'lld %'lld\n", 1LL, 222222222LL, 3333333333333LL);
	print("%019lld %019lld %019lld\n", 1LL, 222222222LL, 3333333333333LL);
	print("%'019lld %'019lld %'019lld\n", 1LL, 222222222LL, 3333333333333LL);
	print("%'020lld %'020lld %'020lld\n", 1LL, 222222222LL, 3333333333333LL);
	print("%'llx %'llX %'llb\n", 0x111111111111LL, 0xabcd12345678LL, 112342345LL);
	return 0;
}
Added lib/libfmt/test2.c.


















>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
#include <stdarg.h>
#include <utf.h>
#include <fmt.h>

int
main(int argc, char **argv)
{
	print("%020.10d\n", 100);
}
Added lib/libfmt/test3.c.








































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <u.h>
#include <libc.h>
#include <stdio.h>

void
test(char *fmt, ...)
{
	va_list arg;
	char fmtbuf[100], stdbuf[100];

	va_start(arg, fmt);
	vsnprint(fmtbuf, sizeof fmtbuf, fmt, arg);
	va_end(arg);

	va_start(arg, fmt);
	vsnprint(stdbuf, sizeof stdbuf, fmt, arg);
	va_end(arg);

	if(strcmp(fmtbuf, stdbuf) != 0)
		print("fmt %s: fmt=\"%s\" std=\"%s\"\n", fmt, fmtbuf, stdbuf);

	print("fmt %s: %s\n", fmt, fmtbuf);
}


int
main(int argc, char *argv[])
{
	test("%f", 3.14159);
	test("%f", 3.14159e10);
	test("%f", 3.14159e-10);

	test("%e", 3.14159);
	test("%e", 3.14159e10);
	test("%e", 3.14159e-10);

	test("%g", 3.14159);
	test("%g", 3.14159e10);
	test("%g", 3.14159e-10);

	test("%g", 2e25);
	test("%.18g", 2e25);

	test("%2.18g", 1.0);
	test("%2.18f", 1.0);
	test("%f", 3.1415927/4);

	test("%20.10d", 12345);
	test("%0.10d", 12345);

	return 0;
}
Added lib/libfmt/vfprint.c.


































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
 * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */
#include <stdarg.h>
#include "plan9.h"
#include "fmt.h"
#include "fmtdef.h"

int
vfprint(int fd, const char *fmt, va_list args)
{
	Fmt f;
	char buf[256];
	int n;

	fmtfdinit(&f, fd, buf, sizeof(buf));
	va_copy(f.args,args);
	n = dofmt(&f, fmt);
	va_end(f.args);
	if(n > 0 && __fmtFdFlush(&f) == 0)
		return -1;
	return n;
}
Added lib/libfmt/vseprint.c.
















































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
 * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */
#include <stdarg.h>
#include "plan9.h"
#include "fmt.h"
#include "fmtdef.h"

char*
vseprint(char *buf, char *e, const char *fmt, va_list args)
{
	Fmt f;

	if(e <= buf)
		return nil;
	f.runes = 0;
	f.start = buf;
	f.to = buf;
	f.stop = e - 1;
	f.flush = 0;
	f.farg = nil;
	f.nfmt = 0;
	va_copy(f.args,args);
	fmtlocaleinit(&f, nil, nil, nil);
	dofmt(&f, fmt);
	va_end(f.args);
	*(char*)f.to = '\0';
	return (char*)f.to;
}

Added lib/libfmt/vsmprint.c.


















































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
 * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */
/*
 * Plan 9 port version must include libc.h in order to
 * get Plan 9 debugging malloc, which sometimes returns
 * different pointers than the standard malloc.
 */
#include <stdlib.h>
#include <string.h>
#include "plan9.h"
#include "fmt.h"
#include "fmtdef.h"

static int
fmtStrFlush(Fmt *f)
{
	char *s;
	int n;

	if(f->start == nil)
		return 0;
	n = (uintptr_t)f->farg;
	n *= 2;
	s = (char*)f->start;
	f->start = realloc(s, n);
	if(f->start == nil){
		f->farg = nil;
		f->to = nil;
		f->stop = nil;
		free(s);
		return 0;
	}
	f->farg = (void*)(uintptr_t)n;
	f->to = (char*)f->start + ((char*)f->to - s);
	f->stop = (char*)f->start + n - 1;
	return 1;
}

int
fmtstrinit(Fmt *f)
{
	int n;

	memset(f, 0, sizeof *f);
	f->runes = 0;
	n = 32;
	f->start = malloc(n);
	if(f->start == nil)
		return -1;
	f->to = f->start;
	f->stop = (char*)f->start + n - 1;
	f->flush = fmtStrFlush;
	f->farg = (void*)(uintptr_t)n;
	f->nfmt = 0;
	fmtlocaleinit(f, nil, nil, nil);
	return 0;
}

/*
 * print into an allocated string buffer
 */
char*
vsmprint(const char *fmt, va_list args)
{
	Fmt f;
	int n;

	if(fmtstrinit(&f) < 0)
		return nil;
	va_copy(f.args,args);
	n = dofmt(&f, fmt);
	va_end(f.args);
	if(n < 0){
		free(f.start);
		return nil;
	}
	return fmtstrflush(&f);
}
Added lib/libfmt/vsnprint.c.
















































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
 * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */
#include <stdlib.h>
#include <stdarg.h>
#include "plan9.h"
#include "fmt.h"
#include "fmtdef.h"

int
vsnprint(char *buf, int len, const char *fmt, va_list args)
{
	Fmt f;

	if(len <= 0)
		return -1;
	f.runes = 0;
	f.start = buf;
	f.to = buf;
	f.stop = buf + len - 1;
	f.flush = 0;
	f.farg = nil;
	f.nfmt = 0;
	va_copy(f.args,args);
	fmtlocaleinit(&f, nil, nil, nil);
	dofmt(&f, fmt);
	va_end(f.args);
	*(char*)f.to = '\0';
	return (char*)f.to - buf;
}
Added lib/libregexp/Makefile.


































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
ROOT= ../..
include $(ROOT)/mk/hdr.mk

VERSION=2.0
TARG=libregexp9

OBJ=\
	regcomp\
	regerror\
	regexec\
	regsub\
	regaux\
	rregexec\
	rregsub

include $(ROOT)/mk/lib.mk

Added lib/libregexp/NOTICE.


















































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/*
 * The authors of this software is Rob Pike.
 *		Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
 * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
*/

This is a Unix port of the Plan 9 regular expression library.

Please send comments about the packaging
to Russ Cox <rsc@swtch.com>.


----

This software is also made available under the Lucent Public License
version 1.02; see http://plan9.bell-labs.com/plan9dist/license.html

Added lib/libregexp/README.










>
>
>
>
>
1
2
3
4
5
This software was packaged for Unix by Russ Cox.
Please send comments to rsc@swtch.com.

http://swtch.com/plan9port/unix

Added lib/libregexp/regaux.c.
































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
#include "plan9.h"
#include "regexp9.h"
#include "regcomp.h"


/*
 *  save a new match in mp
 */
extern void
_renewmatch(Resub *mp, int ms, Resublist *sp)
{
	int i;

	if(mp==0 || ms<=0)
		return;
	if(mp[0].s.sp==0 || sp->m[0].s.sp<mp[0].s.sp ||
	   (sp->m[0].s.sp==mp[0].s.sp && sp->m[0].e.ep>mp[0].e.ep)){
		for(i=0; i<ms && i<NSUBEXP; i++)
			mp[i] = sp->m[i];
		for(; i<ms; i++)
			mp[i].s.sp = mp[i].e.ep = 0;
	}
}

/*
 * Note optimization in _renewthread:
 * 	*lp must be pending when _renewthread called; if *l has been looked
 *		at already, the optimization is a bug.
 */
extern Relist*
_renewthread(Relist *lp,	/* _relist to add to */
	Reinst *ip,		/* instruction to add */
	int ms,
	Resublist *sep)		/* pointers to subexpressions */
{
	Relist *p;

	for(p=lp; p->inst; p++){
		if(p->inst == ip){
			if(sep->m[0].s.sp < p->se.m[0].s.sp){
				if(ms > 1)
					p->se = *sep;
				else
					p->se.m[0] = sep->m[0];
			}
			return 0;
		}
	}
	p->inst = ip;
	if(ms > 1)
		p->se = *sep;
	else
		p->se.m[0] = sep->m[0];
	(++p)->inst = 0;
	return p;
}

/*
 * same as renewthread, but called with
 * initial empty start pointer.
 */
extern Relist*
_renewemptythread(Relist *lp,	/* _relist to add to */
	Reinst *ip,		/* instruction to add */
	int ms,
	char *sp)		/* pointers to subexpressions */
{
	Relist *p;

	for(p=lp; p->inst; p++){
		if(p->inst == ip){
			if(sp < p->se.m[0].s.sp) {
				if(ms > 1)
					memset(&p->se, 0, sizeof(p->se));
				p->se.m[0].s.sp = sp;
			}
			return 0;
		}
	}
	p->inst = ip;
	if(ms > 1)
		memset(&p->se, 0, sizeof(p->se));
	p->se.m[0].s.sp = sp;
	(++p)->inst = 0;
	return p;
}

extern Relist*
_rrenewemptythread(Relist *lp,	/* _relist to add to */
	Reinst *ip,		/* instruction to add */
	int ms,
	Rune *rsp)		/* pointers to subexpressions */
{
	Relist *p;

	for(p=lp; p->inst; p++){
		if(p->inst == ip){
			if(rsp < p->se.m[0].s.rsp) {
				if(ms > 1)
					memset(&p->se, 0, sizeof(p->se));
				p->se.m[0].s.rsp = rsp;
			}
			return 0;
		}
	}
	p->inst = ip;
	if(ms > 1)
		memset(&p->se, 0, sizeof(p->se));
	p->se.m[0].s.rsp = rsp;
	(++p)->inst = 0;
	return p;
}
Added lib/libregexp/regcomp.c.






























































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
#include <setjmp.h>
#include <stdlib.h>
#include "plan9.h"
#include "regexp9.h"
#include "regcomp.h"

enum {
	FALSE,
	TRUE,
};

/*
 * Parser Information
 */
typedef struct Node Node;
struct Node
{
	Reinst*	first;
	Reinst*	last;
};

#define	NSTACK	20
static	Node	andstack[NSTACK];
static	Node	*andp;
static	int	atorstack[NSTACK];
static	int*	atorp;
static	int	cursubid;		/* id of current subexpression */
static	int	subidstack[NSTACK];	/* parallel to atorstack */
static	int*	subidp;
static	int	lastwasand;	/* Last token was operand */
static	int	nbra;
static	char*	exprp;		/* pointer to next character in source expression */
static	int	lexdone;
static	int	nclass;
static	Reclass*classp;
static	Reinst*	freep;
static	int	errors;
static	Rune	yyrune;		/* last lex'd rune */
static	Reclass*yyclassp;	/* last lex'd class */

/* predeclared crap */
static	void	operator(int);
static	void	pushand(Reinst*, Reinst*);
static	void	pushator(int);
static	void	evaluntil(int);
static	int	bldcclass(void);

static jmp_buf regkaboom;

static	void
rcerror(char *s)
{
	errors++;
	regerror(s);
	longjmp(regkaboom, 1);
}

static	Reinst*
newinst(int t)
{
	freep->type = t;
	freep->u2.left = 0;
	freep->u1.right = 0;
	return freep++;
}

static	void
operand(int t)
{
	Reinst *i;

	if(lastwasand)
		operator(CAT);	/* catenate is implicit */
	i = newinst(t);

	if(t == CCLASS || t == NCCLASS)
		i->u1.cp = yyclassp;
	if(t == RUNE)
		i->u1.r = yyrune;

	pushand(i, i);
	lastwasand = TRUE;
}

static	void
operator(int t)
{
	if(t==RBRA && --nbra<0)
		rcerror("unmatched right paren");
	if(t==LBRA){
		if(++cursubid >= NSUBEXP)
			rcerror ("too many subexpressions");
		nbra++;
		if(lastwasand)
			operator(CAT);
	} else
		evaluntil(t);
	if(t != RBRA)
		pushator(t);
	lastwasand = FALSE;
	if(t==STAR || t==QUEST || t==PLUS || t==RBRA)
		lastwasand = TRUE;	/* these look like operands */
}

static	void
regerr2(char *s, int c)
{
	char buf[100];
	char *cp = buf;
	while(*s)
		*cp++ = *s++;
	*cp++ = c;
	*cp = '\0';
	rcerror(buf);
}

static	void
cant(char *s)
{
	char buf[100];
	strcpy(buf, "can't happen: ");
	strcat(buf, s);
	rcerror(buf);
}

static	void
pushand(Reinst *f, Reinst *l)
{
	if(andp >= &andstack[NSTACK])
		cant("operand stack overflow");
	andp->first = f;
	andp->last = l;
	andp++;
}

static	void
pushator(int t)
{
	if(atorp >= &atorstack[NSTACK])
		cant("operator stack overflow");
	*atorp++ = t;
	*subidp++ = cursubid;
}

static	Node*
popand(int op)
{
	Reinst *inst;

	if(andp <= &andstack[0]){
		regerr2("missing operand for ", op);
		inst = newinst(NOP);
		pushand(inst,inst);
	}
	return --andp;
}

static	int
popator(void)
{
	if(atorp <= &atorstack[0])
		cant("operator stack underflow");
	--subidp;
	return *--atorp;
}

static	void
evaluntil(int pri)
{
	Node *op1, *op2;
	Reinst *inst1, *inst2;

	while(pri==RBRA || atorp[-1]>=pri){
		switch(popator()){
		default:
			rcerror("unknown operator in evaluntil");
			break;
		case LBRA:		/* must have been RBRA */
			op1 = popand('(');
			inst2 = newinst(RBRA);
			inst2->u1.subid = *subidp;
			op1->last->u2.next = inst2;
			inst1 = newinst(LBRA);
			inst1->u1.subid = *subidp;
			inst1->u2.next = op1->first;
			pushand(inst1, inst2);
			return;
		case OR:
			op2 = popand('|');
			op1 = popand('|');
			inst2 = newinst(NOP);
			op2->last->u2.next = inst2;
			op1->last->u2.next = inst2;
			inst1 = newinst(OR);
			inst1->u1.right = op1->first;
			inst1->u2.left = op2->first;
			pushand(inst1, inst2);
			break;
		case CAT:
			op2 = popand(0);
			op1 = popand(0);
			op1->last->u2.next = op2->first;
			pushand(op1->first, op2->last);
			break;
		case STAR:
			op2 = popand('*');
			inst1 = newinst(OR);
			op2->last->u2.next = inst1;
			inst1->u1.right = op2->first;
			pushand(inst1, inst1);
			break;
		case PLUS:
			op2 = popand('+');
			inst1 = newinst(OR);
			op2->last->u2.next = inst1;
			inst1->u1.right = op2->first;
			pushand(op2->first, inst1);
			break;
		case QUEST:
			op2 = popand('?');
			inst1 = newinst(OR);
			inst2 = newinst(NOP);
			inst1->u2.left = inst2;
			inst1->u1.right = op2->first;
			op2->last->u2.next = inst2;
			pushand(inst1, inst2);
			break;
		}
	}
}

static	Reprog*
optimize(Reprog *pp)
{
	Reinst *inst, *target;
	int size;
	Reprog *npp;
	Reclass *cl;
	int diff;

	/*
	 *  get rid of NOOP chains
	 */
	for(inst=pp->firstinst; inst->type!=END; inst++){
		target = inst->u2.next;
		while(target->type == NOP)
			target = target->u2.next;
		inst->u2.next = target;
	}

	/*
	 *  The original allocation is for an area larger than
	 *  necessary.  Reallocate to the actual space used
	 *  and then relocate the code.
	 */
	size = sizeof(Reprog) + (freep - pp->firstinst)*sizeof(Reinst);
	npp = realloc(pp, size);
	if(npp==0 || npp==pp)
		return pp;
	diff = (char *)npp - (char *)pp;
	freep = (Reinst *)((char *)freep + diff);
	for(inst=npp->firstinst; inst<freep; inst++){
		switch(inst->type){
		case OR:
		case STAR:
		case PLUS:
		case QUEST:
			*(char **)&inst->u1.right += diff;
			break;
		case CCLASS:
		case NCCLASS:
			*(char **)&inst->u1.right += diff;
			cl = inst->u1.cp;
			*(char **)&cl->end += diff;
			break;
		}
		*(char **)&inst->u2.left += diff;
	}
	*(char **)&npp->startinst += diff;
	return npp;
}

#ifdef	DEBUG
static	void
dumpstack(void){
	Node *stk;
	int *ip;

	print("operators\n");
	for(ip=atorstack; ip<atorp; ip++)
		print("0%o\n", *ip);
	print("operands\n");
	for(stk=andstack; stk<andp; stk++)
		print("0%o\t0%o\n", stk->first->type, stk->last->type);
}

static	void
dump(Reprog *pp)
{
	Reinst *l;
	Rune *p;

	l = pp->firstinst;
	do{
		print("%d:\t0%o\t%d\t%d", l-pp->firstinst, l->type,
			l->u2.left-pp->firstinst, l->u1.right-pp->firstinst);
		if(l->type == RUNE)
			print("\t%C\n", l->u1.r);
		else if(l->type == CCLASS || l->type == NCCLASS){
			print("\t[");
			if(l->type == NCCLASS)
				print("^");
			for(p = l->u1.cp->spans; p < l->u1.cp->end; p += 2)
				if(p[0] == p[1])
					print("%C", p[0]);
				else
					print("%C-%C", p[0], p[1]);
			print("]\n");
		} else
			print("\n");
	}while(l++->type);
}
#endif

static	Reclass*
newclass(void)
{
	if(nclass >= NCLASS)
		regerr2("too many character classes; limit", NCLASS+'0');
	return &(classp[nclass++]);
}

static	int
nextc(Rune *rp)
{
	if(lexdone){
		*rp = 0;
		return 1;
	}
	exprp += chartorune(rp, exprp);
	if(*rp == '\\'){
		exprp += chartorune(rp, exprp);
		return 1;
	}
	if(*rp == 0)
		lexdone = 1;
	return 0;
}

static	int
lex(int literal, int dot_type)
{
	int quoted;

	quoted = nextc(&yyrune);
	if(literal || quoted){
		if(yyrune == 0)
			return END;
		return RUNE;
	}

	switch(yyrune){
	case 0:
		return END;
	case '*':
		return STAR;
	case '?':
		return QUEST;
	case '+':
		return PLUS;
	case '|':
		return OR;
	case '.':
		return dot_type;
	case '(':
		return LBRA;
	case ')':
		return RBRA;
	case '^':
		return BOL;
	case '$':
		return EOL;
	case '[':
		return bldcclass();
	}
	return RUNE;
}

static int
bldcclass(void)
{
	int type;
	Rune r[NCCRUNE];
	Rune *p, *ep, *np;
	Rune rune;
	int quoted;

	/* we have already seen the '[' */
	type = CCLASS;
	yyclassp = newclass();

	/* look ahead for negation */
	/* SPECIAL CASE!!! negated classes don't match \n */
	ep = r;
	quoted = nextc(&rune);
	if(!quoted && rune == '^'){
		type = NCCLASS;
		quoted = nextc(&rune);
		*ep++ = '\n';
		*ep++ = '\n';
	}

	/* parse class into a set of spans */
	for(; ep<&r[NCCRUNE];){
		if(rune == 0){
			rcerror("malformed '[]'");
			return 0;
		}
		if(!quoted && rune == ']')
			break;
		if(!quoted && rune == '-'){
			if(ep == r){
				rcerror("malformed '[]'");
				return 0;
			}
			quoted = nextc(&rune);
			if((!quoted && rune == ']') || rune == 0){
				rcerror("malformed '[]'");
				return 0;
			}
			*(ep-1) = rune;
		} else {
			*ep++ = rune;
			*ep++ = rune;
		}
		quoted = nextc(&rune);
	}

	/* sort on span start */
	for(p = r; p < ep; p += 2){
		for(np = p; np < ep; np += 2)
			if(*np < *p){
				rune = np[0];
				np[0] = p[0];
				p[0] = rune;
				rune = np[1];
				np[1] = p[1];
				p[1] = rune;
			}
	}

	/* merge spans */
	np = yyclassp->spans;
	p = r;
	if(r == ep)
		yyclassp->end = np;
	else {
		np[0] = *p++;
		np[1] = *p++;
		for(; p < ep; p += 2)
			if(p[0] <= np[1]){
				if(p[1] > np[1])
					np[1] = p[1];
			} else {
				np += 2;
				np[0] = p[0];
				np[1] = p[1];
			}
		yyclassp->end = np+2;
	}

	return type;
}

static	Reprog*
regcomp1(char *s, int literal, int dot_type)
{
	int token;
	Reprog *volatile pp;

	/* get memory for the program */
	pp = malloc(sizeof(Reprog) + 6*sizeof(Reinst)*strlen(s));
	if(pp == 0){
		regerror("out of memory");
		return 0;
	}
	freep = pp->firstinst;
	classp = pp->class;
	errors = 0;

	if(setjmp(regkaboom))
		goto out;

	/* go compile the sucker */
	lexdone = 0;
	exprp = s;
	nclass = 0;
	nbra = 0;
	atorp = atorstack;
	andp = andstack;
	subidp = subidstack;
	lastwasand = FALSE;
	cursubid = 0;

	/* Start with a low priority operator to prime parser */
	pushator(START-1);
	while((token = lex(literal, dot_type)) != END){
		if((token&0300) == OPERATOR)
			operator(token);
		else
			operand(token);
	}

	/* Close with a low priority operator */
	evaluntil(START);

	/* Force END */
	operand(END);
	evaluntil(START);
#ifdef DEBUG
	dumpstack();
#endif
	if(nbra)
		rcerror("unmatched left paren");
	--andp;	/* points to first and only operand */
	pp->startinst = andp->first;
#ifdef DEBUG
	dump(pp);
#endif
	pp = optimize(pp);
#ifdef DEBUG
	print("start: %d\n", andp->first-pp->firstinst);
	dump(pp);
#endif
out:
	if(errors){
		free(pp);
		pp = 0;
	}
	return pp;
}

extern	Reprog*
regcomp(char *s)
{
	return regcomp1(s, 0, ANY);
}

extern	Reprog*
regcomplit(char *s)
{
	return regcomp1(s, 1, ANY);
}

extern	Reprog*
regcompnl(char *s)
{
	return regcomp1(s, 0, ANYNL);
}
Added lib/libregexp/regerror.c.






























>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdlib.h>
#include <plan9.h>
#include <regexp9.h>

void
regerror(char *s)
{
	char buf[132];

	strcpy(buf, "regerror: ");
	strcat(buf, s);
	strcat(buf, "\n");
	write(2, buf, strlen(buf));
	exits("regerr");
}
Added lib/libregexp/regexec.c.
















































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
#include <stdlib.h>
#include "plan9.h"
#include "regexp9.h"
#include "regcomp.h"


/*
 *  return	0 if no match
 *		>0 if a match
 *		<0 if we ran out of _relist space
 */
static int
regexec1(Reprog *progp,	/* program to run */
	char *bol,	/* string to run machine on */
	Resub *mp,	/* subexpression elements */
	int ms,		/* number of elements at mp */
	Reljunk *j
)
{
	int flag=0;
	Reinst *inst;
	Relist *tlp;
	char *s;
	int i, checkstart;
	Rune r, *rp, *ep;
	int n;
	Relist* tl;		/* This list, next list */
	Relist* nl;
	Relist* tle;		/* ends of this and next list */
	Relist* nle;
	int match;
	char *p;

	match = 0;
	checkstart = j->starttype;
	if(mp)
		for(i=0; i<ms; i++) {
			mp[i].s.sp = 0;
			mp[i].e.ep = 0;
		}
	j->relist[0][0].inst = 0;
	j->relist[1][0].inst = 0;

	/* Execute machine once for each character, including terminal NUL */
	s = j->starts;
	do{
		/* fast check for first char */
		if(checkstart) {
			switch(j->starttype) {
			case RUNE:
				p = utfrune(s, j->startchar);
				if(p == 0 || s == j->eol)
					return match;
				s = p;
				break;
			case BOL:
				if(s == bol)
					break;
				p = utfrune(s, '\n');
				if(p == 0 || s == j->eol)
					return match;
				s = p;
				break;
			}
		}
		r = *(uchar*)s;
		if(r < Runeself)
			n = 1;
		else
			n = chartorune(&r, s);

		/* switch run lists */
		tl = j->relist[flag];
		tle = j->reliste[flag];
		nl = j->relist[flag^=1];
		nle = j->reliste[flag];
		nl->inst = 0;

		/* Add first instruction to current list */
		if(match == 0)
			_renewemptythread(tl, progp->startinst, ms, s);

		/* Execute machine until current list is empty */
		for(tlp=tl; tlp->inst; tlp++){	/* assignment = */
			for(inst = tlp->inst; ; inst = inst->u2.next){
				switch(inst->type){
				case RUNE:	/* regular character */
					if(inst->u1.r == r){
						if(_renewthread(nl, inst->u2.next, ms, &tlp->se)==nle)
							return -1;
					}
					break;
				case LBRA:
					tlp->se.m[inst->u1.subid].s.sp = s;
					continue;
				case RBRA:
					tlp->se.m[inst->u1.subid].e.ep = s;
					continue;
				case ANY:
					if(r != '\n')
						if(_renewthread(nl, inst->u2.next, ms, &tlp->se)==nle)
							return -1;
					break;
				case ANYNL:
					if(_renewthread(nl, inst->u2.next, ms, &tlp->se)==nle)
							return -1;
					break;
				case BOL:
					if(s == bol || *(s-1) == '\n')
						continue;
					break;
				case EOL:
					if(s == j->eol || r == 0 || r == '\n')
						continue;
					break;
				case CCLASS:
					ep = inst->u1.cp->end;
					for(rp = inst->u1.cp->spans; rp < ep; rp += 2)
						if(r >= rp[0] && r <= rp[1]){
							if(_renewthread(nl, inst->u2.next, ms, &tlp->se)==nle)
								return -1;
							break;
						}
					break;
				case NCCLASS:
					ep = inst->u1.cp->end;
					for(rp = inst->u1.cp->spans; rp < ep; rp += 2)
						if(r >= rp[0] && r <= rp[1])
							break;
					if(rp == ep)
						if(_renewthread(nl, inst->u2.next, ms, &tlp->se)==nle)
							return -1;
					break;
				case OR:
					/* evaluate right choice later */
					if(_renewthread(tlp, inst->u1.right, ms, &tlp->se) == tle)
						return -1;
					/* efficiency: advance and re-evaluate */
					continue;
				case END:	/* Match! */
					match = 1;
					tlp->se.m[0].e.ep = s;
					if(mp != 0)
						_renewmatch(mp, ms, &tlp->se);
					break;
				}
				break;
			}
		}
		if(s == j->eol)
			break;
		checkstart = j->starttype && nl->inst==0;
		s += n;
	}while(r);
	return match;
}

static int
regexec2(Reprog *progp,	/* program to run */
	char *bol,	/* string to run machine on */
	Resub *mp,	/* subexpression elements */
	int ms,		/* number of elements at mp */
	Reljunk *j
)
{
	int rv;
	Relist *relist0, *relist1;

	/* mark space */
	relist0 = malloc(BIGLISTSIZE*sizeof(Relist));
	if(relist0 == nil)
		return -1;
	relist1 = malloc(BIGLISTSIZE*sizeof(Relist));
	if(relist1 == nil){
		free(relist1);
		return -1;
	}
	j->relist[0] = relist0;
	j->relist[1] = relist1;
	j->reliste[0] = relist0 + BIGLISTSIZE - 2;
	j->reliste[1] = relist1 + BIGLISTSIZE - 2;

	rv = regexec1(progp, bol, mp, ms, j);
	free(relist0);
	free(relist1);
	return rv;
}

extern int
regexec(Reprog *progp,	/* program to run */
	char *bol,	/* string to run machine on */
	Resub *mp,	/* subexpression elements */
	int ms)		/* number of elements at mp */
{
	Reljunk j;
	Relist relist0[LISTSIZE], relist1[LISTSIZE];
	int rv;

	/*
 	 *  use user-specified starting/ending location if specified
	 */
	j.starts = bol;
	j.eol = 0;
	if(mp && ms>0){
		if(mp->s.sp)
			j.starts = mp->s.sp;
		if(mp->e.ep)
			j.eol = mp->e.ep;
	}
	j.starttype = 0;
	j.startchar = 0;
	if(progp->startinst->type == RUNE && progp->startinst->u1.r < Runeself) {
		j.starttype = RUNE;
		j.startchar = progp->startinst->u1.r;
	}
	if(progp->startinst->type == BOL)
		j.starttype = BOL;

	/* mark space */
	j.relist[0] = relist0;
	j.relist[1] = relist1;
	j.reliste[0] = relist0 + nelem(relist0) - 2;
	j.reliste[1] = relist1 + nelem(relist1) - 2;

	rv = regexec1(progp, bol, mp, ms, &j);
	if(rv >= 0)
		return rv;
	rv = regexec2(progp, bol, mp, ms, &j);
	if(rv >= 0)
		return rv;
	return -1;
}
Added lib/libregexp/regexp9.3.
























































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
.deEX
.ift .ft5
.nf
..
.deEE
.ft1
.fi
..
.TH REGEXP9 3
.SH NAME
regcomp, regcomplit, regcompnl, regexec, regsub, rregexec, rregsub, regerror \- regular expression
.SH SYNOPSIS
.B #include <utf.h>
.br
.B #include <fmt.h>
.br
.B #include <regexp9.h>
.PP
.ta \w'\fLRegprog 'u
.B
Reprog	*regcomp(char *exp)
.PP
.B
Reprog	*regcomplit(char *exp)
.PP
.B
Reprog	*regcompnl(char *exp)
.PP
.nf
.B
int  regexec(Reprog *prog, char *string, Resub *match, int msize)
.PP
.nf
.B
void regsub(char *source, char *dest, int dlen, Resub *match, int msize)
.PP
.nf
.B
int  rregexec(Reprog *prog, Rune *string, Resub *match, int msize)
.PP
.nf
.B
void rregsub(Rune *source, Rune *dest, int dlen, Resub *match, int msize)
.PP
.B
void regerror(char *msg)
.SH DESCRIPTION
.I Regcomp
compiles a
regular expression and returns
a pointer to the generated description.
The space is allocated by
.IR malloc (3)
and may be released by
.IR free .
Regular expressions are exactly as in
.IR regexp9 (7).
.PP
.I Regcomplit
is like
.I regcomp
except that all characters are treated literally.
.I Regcompnl
is like
.I regcomp
except that the
.B .
metacharacter matches all characters, including newlines.
.PP
.I Regexec
matches a null-terminated
.I string
against the compiled regular expression in
.IR prog .
If it matches,
.I regexec
returns
.B 1
and fills in the array
.I match
with character pointers to the substrings of
.I string
that correspond to the
parenthesized subexpressions of 
.IR exp :
.BI match[ i ].sp
points to the beginning and
.BI match[ i ].ep
points just beyond
the end of the
.IR i th
substring.
(Subexpression
.I i
begins at the
.IR i th
left parenthesis, counting from 1.)
Pointers in
.B match[0]
pick out the substring that corresponds to
the whole regular expression.
Unused elements of
.I match
are filled with zeros.
Matches involving
.LR * ,
.LR + ,
and 
.L ?
are extended as far as possible.
The number of array elements in 
.I match
is given by
.IR msize .
The structure of elements of
.I match 
is:
.IP
.EX
typedef struct {
	union {
	   char *sp;
	   Rune *rsp;
	};
	union {
	   char *ep;
	   Rune *rep;
	};
} Resub;
.EE
.LP
If
.B match[0].sp
is nonzero on entry,
.I regexec
starts matching at that point within
.IR string .
If
.B match[0].ep
is nonzero on entry,
the last character matched is the one
preceding that point.
.PP
.I Regsub
places in
.I dest
a substitution instance of
.I source
in the context of the last
.I regexec
performed using
.IR match .
Each instance of
.BI \e n\f1,
where
.I n
is a digit, is replaced by the
string delimited by
.BI match[ n ].sp
and
.BI match[ n ].ep\f1.
Each instance of 
.L &
is replaced by the string delimited by
.B match[0].sp
and
.BR match[0].ep .
The substitution will always be null terminated and
trimmed to fit into dlen bytes.
.PP
.IR Regerror ,
called whenever an error is detected in
.IR regcomp ,
writes the string
.I msg
on the standard error file and exits.
.I Regerror
can be replaced to perform
special error processing.
If the user supplied
.I regerror
returns rather than exits,
.I regcomp
will return 0. 
.PP
.I Rregexec
and
.I rregsub
are variants of 
.I regexec
and
.I regsub
that use strings of
.B Runes
instead of strings of
.BR chars .
With these routines, the 
.I rsp
and
.I rep
fields of the
.I match
array elements should be used.
.SH SOURCE
.B http://swtch.com/plan9port/unix
.SH "SEE ALSO"
.IR grep (1)
.SH DIAGNOSTICS
.I Regcomp
returns 
.B 0
for an illegal expression
or other failure.
.I Regexec
returns 0
if
.I string
is not matched.
.SH BUGS
There is no way to specify or match a NUL character; NULs terminate patterns and strings.
Added lib/libregexp/regexp9.7.


























































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
.deEX
.ift .ft5
.nf
..
.deEE
.ft1
.fi
..
.TH REGEXP9 7
.SH NAME
regexp \- Plan 9 regular expression notation
.SH DESCRIPTION
This manual page describes the regular expression
syntax used by the Plan 9 regular expression library
.IR regexp9 (3).
It is the form used by
.IR egrep (1)
before
.I egrep
got complicated.
.PP
A 
.I "regular expression"
specifies
a set of strings of characters.
A member of this set of strings is said to be
.I matched
by the regular expression.  In many applications
a delimiter character, commonly
.LR / ,
bounds a regular expression.
In the following specification for regular expressions
the word `character' means any character (rune) but newline.
.PP
The syntax for a regular expression
.B e0
is
.IP
.EX
e3:  literal | charclass | '.' | '^' | '$' | '(' e0 ')'

e2:  e3
  |  e2 REP

REP: '*' | '+' | '?'

e1:  e2
  |  e1 e2

e0:  e1
  |  e0 '|' e1
.EE
.PP
A
.B literal
is any non-metacharacter, or a metacharacter
(one of
.BR .*+?[]()|\e^$ ),
or the delimiter
preceded by 
.LR \e .
.PP
A
.B charclass
is a nonempty string
.I s
bracketed
.BI [ \|s\| ]
(or
.BI [^ s\| ]\fR);
it matches any character in (or not in)
.IR s .
A negated character class never
matches newline.
A substring 
.IB a - b\f1,
with
.I a
and
.I b
in ascending
order, stands for the inclusive
range of
characters between
.I a
and
.IR b .
In 
.IR s ,
the metacharacters
.LR - ,
.LR ] ,
an initial
.LR ^ ,
and the regular expression delimiter
must be preceded by a
.LR \e ;
other metacharacters 
have no special meaning and
may appear unescaped.
.PP
A 
.L .
matches any character.
.PP
A
.L ^
matches the beginning of a line;
.L $
matches the end of the line.
.PP
The 
.B REP
operators match zero or more
.RB ( * ),
one or more
.RB ( + ),
zero or one
.RB ( ? ),
instances respectively of the preceding regular expression 
.BR e2 .
.PP
A concatenated regular expression,
.BR "e1\|e2" ,
matches a match to 
.B e1
followed by a match to
.BR e2 .
.PP
An alternative regular expression,
.BR "e0\||\|e1" ,
matches either a match to
.B e0
or a match to
.BR e1 .
.PP
A match to any part of a regular expression
extends as far as possible without preventing
a match to the remainder of the regular expression.
.SH "SEE ALSO
.IR regexp9 (3)
Added lib/libregexp/regsub.c.






























































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include "plan9.h"
#include "regexp9.h"

/* substitute into one string using the matches from the last regexec() */
extern	void
regsub(char *sp,	/* source string */
	char *dp,	/* destination string */
	int dlen,
	Resub *mp,	/* subexpression elements */
	int ms)		/* number of elements pointed to by mp */
{
	char *ssp, *ep;
	int i;

	ep = dp+dlen-1;
	while(*sp != '\0'){
		if(*sp == '\\'){
			switch(*++sp){
			case '0':
			case '1':
			case '2':
			case '3':
			case '4':
			case '5':
			case '6':
			case '7':
			case '8':
			case '9':
				i = *sp-'0';
				if(mp[i].s.sp != 0 && mp!=0 && ms>i)
					for(ssp = mp[i].s.sp;
					     ssp < mp[i].e.ep;
					     ssp++)
						if(dp < ep)
							*dp++ = *ssp;
				break;
			case '\\':
				if(dp < ep)
					*dp++ = '\\';
				break;
			case '\0':
				sp--;
				break;
			default:
				if(dp < ep)
					*dp++ = *sp;
				break;
			}
		}else if(*sp == '&'){
			if(mp[0].s.sp != 0 && mp!=0 && ms>0)
			if(mp[0].s.sp != 0)
				for(ssp = mp[0].s.sp;
				     ssp < mp[0].e.ep; ssp++)
					if(dp < ep)
						*dp++ = *ssp;
		}else{
			if(dp < ep)
				*dp++ = *sp;
		}
		sp++;
	}
	*dp = '\0';
}
Added lib/libregexp/rregexec.c.








































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
#include "plan9.h"
#include "regexp9.h"
#include "regcomp.h"

/*
 *  return	0 if no match
 *		>0 if a match
 *		<0 if we ran out of _relist space
 */
static int
rregexec1(Reprog *progp,	/* program to run */
	Rune *bol,		/* string to run machine on */
	Resub *mp,		/* subexpression elements */
	int ms,			/* number of elements at mp */
	Reljunk *j)
{
	int flag=0;
	Reinst *inst;
	Relist *tlp;
	Rune *s;
	int i, checkstart;
	Rune r, *rp, *ep;
	Relist* tl;		/* This list, next list */
	Relist* nl;
	Relist* tle;		/* ends of this and next list */
	Relist* nle;
	int match;
	Rune *p;

	match = 0;
	checkstart = j->startchar;
	if(mp)
		for(i=0; i<ms; i++) {
			mp[i].s.rsp = 0;
			mp[i].e.rep = 0;
		}
	j->relist[0][0].inst = 0;
	j->relist[1][0].inst = 0;

	/* Execute machine once for each character, including terminal NUL */
	s = j->rstarts;
	do{

		/* fast check for first char */
		if(checkstart) {
			switch(j->starttype) {
			case RUNE:
				p = runestrchr(s, j->startchar);
				if(p == 0 || p == j->reol)
					return match;
				s = p;
				break;
			case BOL:
				if(s == bol)
					break;
				p = runestrchr(s, '\n');
				if(p == 0 || s == j->reol)
					return match;
				s = p+1;
				break;
			}
		}

		r = *s;

		/* switch run lists */
		tl = j->relist[flag];
		tle = j->reliste[flag];
		nl = j->relist[flag^=1];
		nle = j->reliste[flag];
		nl->inst = 0;

		/* Add first instruction to current list */
		_rrenewemptythread(tl, progp->startinst, ms, s);

		/* Execute machine until current list is empty */
		for(tlp=tl; tlp->inst; tlp++){
			for(inst=tlp->inst; ; inst = inst->u2.next){
				switch(inst->type){
				case RUNE:	/* regular character */
					if(inst->u1.r == r)
						if(_renewthread(nl, inst->u2.next, ms, &tlp->se)==nle)
							return -1;
					break;
				case LBRA:
					tlp->se.m[inst->u1.subid].s.rsp = s;
					continue;
				case RBRA:
					tlp->se.m[inst->u1.subid].e.rep = s;
					continue;
				case ANY:
					if(r != '\n')
						if(_renewthread(nl, inst->u2.next, ms, &tlp->se)==nle)
							return -1;
					break;
				case ANYNL:
					if(_renewthread(nl, inst->u2.next, ms, &tlp->se)==nle)
							return -1;
					break;
				case BOL:
					if(s == bol || *(s-1) == '\n')
						continue;
					break;
				case EOL:
					if(s == j->reol || r == 0 || r == '\n')
						continue;
					break;
				case CCLASS:
					ep = inst->u1.cp->end;
					for(rp = inst->u1.cp->spans; rp < ep; rp += 2)
						if(r >= rp[0] && r <= rp[1]){
							if(_renewthread(nl, inst->u2.next, ms, &tlp->se)==nle)
								return -1;
							break;
						}
					break;
				case NCCLASS:
					ep = inst->u1.cp->end;
					for(rp = inst->u1.cp->spans; rp < ep; rp += 2)
						if(r >= rp[0] && r <= rp[1])
							break;
					if(rp == ep)
						if(_renewthread(nl, inst->u2.next, ms, &tlp->se)==nle)
							return -1;
					break;
				case OR:
					/* evaluate right choice later */
					if(_renewthread(tlp, inst->u1.right, ms, &tlp->se) == tle)
						return -1;
					/* efficiency: advance and re-evaluate */
					continue;
				case END:	/* Match! */
					match = 1;
					tlp->se.m[0].e.rep = s;
					if(mp != 0)
						_renewmatch(mp, ms, &tlp->se);
					break;
				}
				break;
			}
		}
		if(s == j->reol)
			break;
		checkstart = j->startchar && nl->inst==0;
		s++;
	}while(r);
	return match;
}

static int
rregexec2(Reprog *progp,	/* program to run */
	Rune *bol,	/* string to run machine on */
	Resub *mp,	/* subexpression elements */
	int ms,		/* number of elements at mp */
	Reljunk *j
)
{
	Relist relist0[5*LISTSIZE], relist1[5*LISTSIZE];

	/* mark space */
	j->relist[0] = relist0;
	j->relist[1] = relist1;
	j->reliste[0] = relist0 + nelem(relist0) - 2;
	j->reliste[1] = relist1 + nelem(relist1) - 2;

	return rregexec1(progp, bol, mp, ms, j);
}

extern int
rregexec(Reprog *progp,	/* program to run */
	Rune *bol,	/* string to run machine on */
	Resub *mp,	/* subexpression elements */
	int ms)		/* number of elements at mp */
{
	Reljunk j;
	Relist relist0[LISTSIZE], relist1[LISTSIZE];
	int rv;

	/*
 	 *  use user-specified starting/ending location if specified
	 */
	j.rstarts = bol;
	j.reol = 0;
	if(mp && ms>0){
		if(mp->s.sp)
			j.rstarts = mp->s.rsp;
		if(mp->e.ep)
			j.reol = mp->e.rep;
	}
	j.starttype = 0;
	j.startchar = 0;
	if(progp->startinst->type == RUNE && progp->startinst->u1.r < Runeself) {
		j.starttype = RUNE;
		j.startchar = progp->startinst->u1.r;
	}
	if(progp->startinst->type == BOL)
		j.starttype = BOL;

	/* mark space */
	j.relist[0] = relist0;
	j.relist[1] = relist1;
	j.reliste[0] = relist0 + nelem(relist0) - 2;
	j.reliste[1] = relist1 + nelem(relist1) - 2;

	rv = rregexec1(progp, bol, mp, ms, &j);
	if(rv >= 0)
		return rv;
	rv = rregexec2(progp, bol, mp, ms, &j);
	if(rv >= 0)
		return rv;
	return -1;
}
Added lib/libregexp/rregsub.c.






























































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include "plan9.h"
#include "regexp9.h"

/* substitute into one string using the matches from the last regexec() */
extern	void
rregsub(Rune *sp,	/* source string */
	Rune *dp,	/* destination string */
	int dlen,
	Resub *mp,	/* subexpression elements */
	int ms)		/* number of elements pointed to by mp */
{
	Rune *ssp, *ep;
	int i;

	ep = dp+(dlen/sizeof(Rune))-1;
	while(*sp != '\0'){
		if(*sp == '\\'){
			switch(*++sp){
			case '0':
			case '1':
			case '2':
			case '3':
			case '4':
			case '5':
			case '6':
			case '7':
			case '8':
			case '9':
				i = *sp-'0';
				if(mp[i].s.rsp != 0 && mp!=0 && ms>i)
					for(ssp = mp[i].s.rsp;
					     ssp < mp[i].e.rep;
					     ssp++)
						if(dp < ep)
							*dp++ = *ssp;
				break;
			case '\\':
				if(dp < ep)
					*dp++ = '\\';
				break;
			case '\0':
				sp--;
				break;
			default:
				if(dp < ep)
					*dp++ = *sp;
				break;
			}
		}else if(*sp == '&'){
			if(mp[0].s.rsp != 0 && mp!=0 && ms>0)
			if(mp[0].s.rsp != 0)
				for(ssp = mp[0].s.rsp;
				     ssp < mp[0].e.rep; ssp++)
					if(dp < ep)
						*dp++ = *ssp;
		}else{
			if(dp < ep)
				*dp++ = *sp;
		}
		sp++;
	}
	*dp = '\0';
}
Added lib/libregexp/test.c.




























































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include "plan9.h"
#include <regexp9.h>

struct x
{
	char *re;
	char *s;
	Reprog *p;
};

struct x t[] = {
	{ "^[^!@]+$", "/bin/upas/aliasmail '&'", 0 },
	{ "^local!(.*)$", "/mail/box/\\1/mbox", 0 },
	{ "^plan9!(.*)$", "\\1", 0 },
	{ "^helix!(.*)$", "\\1", 0 },
	{ "^([^!]+)@([^!@]+)$", "\\2!\\1", 0 },
	{ "^(uk\\.[^!]*)(!.*)$", "/bin/upas/uk2uk '\\1' '\\2'", 0 },
	{ "^[^!]*\\.[^!]*!.*$", "inet!&", 0 },
	{ "^\xE2\x98\xBA$", "smiley", 0 },
	{ "^(coma|research|pipe|pyxis|inet|hunny|gauss)!(.*)$", "/mail/lib/qmail '\\s' 'net!\\1' '\\2'", 0 },
	{ "^.*$", "/mail/lib/qmail '\\s' 'net!research' '&'", 0 },
	{ 0, 0, 0 },
};

main(int ac, char **av)
{
	Resub rs[10];
	char dst[128];
	int n;
	struct x *tp;

	for(tp = t; tp->re; tp++)
		tp->p = regcomp(tp->re);


	for(tp = t; tp->re; tp++){
		print("%s VIA %s", av[1], tp->re);
		memset(rs, 0, sizeof rs);
		if(regexec(tp->p, av[1], rs, 10)){
			regsub(tp->s, dst, sizeof dst, rs, 10);
			print(" sub %s -> %s", tp->s, dst);
		}
		print("\n");
	}
	exit(0);
}
Added lib/libregexp/test2.c.








































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include "plan9.h"
#include <regexp9.h>


main(int ac, char **av)
{
	Resub rs[10];
	Reprog *p;
	char *s;
	int i;

	p = regcomp("[^a-z]");
	s = "\n";
	if(regexec(p, s, rs, 10))
		print("%s %lux %lux %lux\n", s, s, rs[0].sp, rs[0].ep);
	s = "0";
	if(regexec(p, s, rs, 10))
		print("%s %lux %lux %lux\n", s, s, rs[0].sp, rs[0].ep);
	exit(0);
}
Added lib/libstuff/Makefile.






























































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
ROOT= ../..
include $(ROOT)/mk/hdr.mk

TARG=libstuff

PACKAGES += $(X11PACKAGES) xext xrandr xrender xinerama

OBJ=\
    	buffer		\
	clientutil	\
	client_readconfig	\
	event/buttonpress	\
	event/buttonrelease	\
	event/clientmessage	\
	event/configurenotify	\
	event/configurerequest	\
	event/destroynotify	\
	event/enternotify	\
	event/event		\
	event/expose		\
	event/focusin		\
	event/focusout		\
	event/ixp		\
	event/keypress		\
	event/keyrelease	\
	event/leavenotify	\
	event/mapnotify		\
	event/maprequest	\
	event/mappingnotify	\
	event/motionnotify	\
	event/propertynotify	\
	event/reparentnotify	\
	event/selection		\
	event/selectionclear	\
	event/selectionrequest	\
	event/unmapnotify	\
	event/xtime		\
	fmt/blprint		\
	fmt/bvlprint		\
	fmt/fmtbuf		\
	fmt/localefmt		\
	fmt/localelen		\
	fmt/lprint		\
	fmt/vlprint		\
	geom/get_sticky		\
	geom/quadrant		\
	geom/rect_contains_p	\
	geom/rect_haspoint_p	\
	geom/rect_intersect_p	\
	geom/rect_intersection	\
	init_screens	\
	map		\
	printevent	\
	util/_die	\
	util/closeexec	\
	util/comm	\
	util/doublefork	\
	util/emalloc	\
	util/emallocz	\
	util/erealloc	\
	util/estrdup	\
	util/estrndup	\
	util/fatal	\
	util/freelater	\
	util/getbase	\
	util/getint	\
	util/getlong	\
	util/getulong	\
	util/grep	\
	util/join	\
	util/max	\
	util/mfatal	\
	util/min	\
	util/nsec	\
	util/pathsearch	\
	util/refree	\
	util/reinit	\
	util/spawn3	\
	util/spawn3l	\
	util/stokenize	\
	util/strcasestr	\
	util/strend	\
	util/strlcat	\
	util/strlcatprint	\
	util/sxprint	\
	util/tokenize	\
	util/trim	\
	util/uniq	\
	util/unmask	\
	util/unquote	\
	util/utflcpy	\
	util/vector	\
	util/vsxprint	\
	x11/convpts	\
	x11/errors	\
	x11/ignored_xerrors	\
	x11/freestringlist	\
	x11/initdisplay	\
	x11/selection	\
	x11/sendevent	\
	x11/sendmessage	\
	x11/sync	\
	x11/x11		\
	x11/xatom	\
	x11/xft		\
	x11/colors/loadcolor	\
	x11/colors/parsecolor	\
	x11/colors/xftcolor	\
	x11/drawing/border	\
	x11/drawing/drawline	\
	x11/drawing/drawpoly	\
	x11/drawing/drawstring	\
	x11/drawing/fill	\
	x11/drawing/fillpoly	\
	x11/drawing/setgccol	\
	x11/focus/getfocus	\
	x11/focus/setfocus	\
	x11/geometry/XRect	\
	x11/geometry/addpt	\
	x11/geometry/divpt	\
	x11/geometry/eqpt	\
	x11/geometry/eqrect	\
	x11/geometry/insetrect	\
	x11/geometry/mulpt	\
	x11/geometry/rectaddpt	\
	x11/geometry/rectsetorigin	\
	x11/geometry/rectsubpt	\
	x11/geometry/subpt	\
	x11/images/allocimage	\
	x11/images/copyimage	\
	x11/images/freeimage	\
	x11/images/xftdrawable	\
	x11/insanity/gravitate	\
	x11/insanity/gethints	\
	x11/insanity/sethints	\
	x11/insanity/sizehint	\
	x11/keyboard/grabkeyboard	\
	x11/keyboard/ungrabkeyboard	\
	x11/keys/keycode	\
	x11/keys/parsekey	\
	x11/mouse/grabpointer	\
	x11/mouse/pointerscreen	\
	x11/mouse/querypointer	\
	x11/mouse/translate	\
	x11/mouse/ungrabpointer	\
	x11/mouse/warppointer	\
	x11/properties/changeprop_char	\
	x11/properties/changeprop_long	\
	x11/properties/changeprop_short	\
	x11/properties/changeprop_string	\
	x11/properties/changeprop_textlist	\
	x11/properties/changeprop_ulong	\
	x11/properties/changeproperty	\
	x11/properties/delproperty	\
	x11/properties/getprop	\
	x11/properties/getprop_long	\
	x11/properties/getprop_string	\
	x11/properties/getprop_textlist	\
	x11/properties/getproperty	\
	x11/properties/strlistdup	\
	x11/properties/windowname	\
	x11/shape/setshapemask	\
	x11/text/freefont	\
	x11/text/labelh	\
	x11/text/loadfont	\
	x11/text/textextents_l	\
	x11/text/textwidth	\
	x11/text/textwidth_l	\
	x11/windows/configwin	\
	x11/windows/createwindow	\
	x11/windows/createwindow_rgba	\
	x11/windows/createwindow_visual	\
	x11/windows/destroywindow	\
	x11/windows/findwin	\
	x11/windows/getwinrect	\
	x11/windows/lowerwin	\
	x11/windows/mapwin	\
	x11/windows/movewin	\
	x11/windows/raisewin	\
	x11/windows/reparentwindow	\
	x11/windows/reshapewin	\
	x11/windows/selectinput	\
	x11/windows/setborder	\
	x11/windows/sethandler	\
	x11/windows/setwinattr	\
	x11/windows/unmapwin	\
	x11/windows/window	\
	xext

include $(ROOT)/mk/lib.mk

Added lib/libstuff/buffer.c.
















>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
/* Copyright ©2008-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */

char	buffer[8092];
char*	_buffer;
char*	const _buf_end = buffer + sizeof buffer;

Added lib/libstuff/client_readconfig.c.
















































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/* Copyright ©2009-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include <ixp.h>
#include <stuff/clientutil.h>
#include <stuff/util.h>
#include <stuff/x.h>
#include <stdio.h>

void
client_readconfig(CTuple *norm, CTuple *focus, Font **font) {

	if(norm)
		loadcolor(norm, readctl("/ctl", "normcolors "), nil);
	if(focus)
		loadcolor(focus, readctl("/ctl", "focuscolors "), nil);
	*font = loadfont(readctl("/ctl", "font "));
	if(!*font)
		fatal("Can't load font %q", readctl("/ctl", "font "));
	sscanf(readctl("/ctl", "fontpad "), "%d %d %d %d",
	       &(*font)->pad.min.x, &(*font)->pad.max.x,
	       &(*font)->pad.min.x, &(*font)->pad.max.y);
}

Added lib/libstuff/clientutil.c.




















































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
/* Copyright ©2009-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#define IXP_NO_P9_
#define IXP_P9_STRUCTS
#define CLIENTEXTERN
#include <stdlib.h>
#include <string.h>
#include <ixp.h>
#include <stuff/clientutil.h>
#include <stuff/util.h>

char*
readctl(char *ctlname, char *key) {
	static char	ctlfile[128];
	static char 	ctl[1024];
	static char*	ectl;
	IxpCFid *fid;
	char *s, *p;
	int nkey, n;

	if(strcmp(ctlname, ctlfile)) {
		strncpy(ctlfile, ctlname, sizeof ctlfile);
		fid = ixp_open(client, ctlfile, OREAD);
		n = ixp_read(fid, ctl, sizeof ctl - 1);
		ectl = ctl + n;
		ixp_close(fid);
	}

	nkey = strlen(key);
	p = ctl - 1;
	do {
		p++;
		if(!strncmp(p, key, nkey)) {
			p += nkey;
			s = strchr(p, '\n');
			n = (s ? s : ectl) - p;
			s = freelater(emalloc(n + 1));
			s[n] = '\0';
			return memcpy(s, p, n);
		}
	} while((p = strchr(p, '\n')));
	return "";
}

void
client_init(char* address) {
	IXP_ASSERT_VERSION;

	if(address == nil)
		address = getenv("WMII_ADDRESS");
	if(address && *address)
		client = ixp_mount(address);
	else
		client = ixp_nsmount("wmii");
	if(client == nil)
		fatal("can't mount wmii filesystem: %r\n");
}
Added lib/libstuff/event/buttonpress.c.




























>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* Copyright ©2006-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "event.h"

void
event_buttonpress(XButtonPressedEvent *ev) {
	Window *w;

	if((w = findwin(ev->window)))
		event_handle(w, bdown, ev);
	else
		XAllowEvents(display, ReplayPointer, ev->time);
}
Added lib/libstuff/event/buttonrelease.c.
























>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
/* Copyright ©2006-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "event.h"

void
event_buttonrelease(XButtonPressedEvent *ev) {
	Window *w;

	if((w = findwin(ev->window)))
		event_handle(w, bup, ev);
}
Added lib/libstuff/event/clientmessage.c.
























>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
/* Copyright ©2006-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "event.h"

void
event_clientmessage(XClientMessageEvent *ev) {
	Window *w;

	if((w = findwin(ev->window)))
		event_handle(w, message, ev);
}
Added lib/libstuff/event/configurenotify.c.




























>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* Copyright ©2006-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "event.h"

void
event_configurenotify(XConfigureEvent *ev) {
	Window *w;

	if(!ev->send_event)
		event_lastconfigure = ev->serial;
	if((w = findwin(ev->window)))
		event_handle(w, config, ev);
}
Added lib/libstuff/event/configurerequest.c.
















































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/* Copyright ©2006-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "event.h"

void
event_configurerequest(XConfigureRequestEvent *ev) {
	XWindowChanges wc;
	Window *w;

	if((w = findwin(ev->window)))
		event_handle(w, configreq, ev);
	else{
		wc.x = ev->x;
		wc.y = ev->y;
		wc.width = ev->width;
		wc.height = ev->height;
		wc.border_width = ev->border_width;
		wc.sibling = ev->above;
		wc.stack_mode = ev->detail;
		XConfigureWindow(display, ev->window, ev->value_mask, &wc);
	}
}

Added lib/libstuff/event/destroynotify.c.




























>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* Copyright ©2006-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "event.h"

void
event_destroynotify(XDestroyWindowEvent *ev) {
	Window *w;

	if(!ev->send_event)
		event_lastconfigure = ev->serial;
	if((w = findwin(ev->window)))
		event_handle(w, destroy, ev);
}
Added lib/libstuff/event/enternotify.c.


































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/* Copyright ©2006-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "event.h"

void
event_enternotify(XCrossingEvent *ev) {
	Window *w;

	if(!ev->send_event)
		event_xtime = ev->time;
	if(ev->mode != NotifyNormal)
		return;

	if((w = findwin(ev->window)))
		event_handle(w, enter, ev);
}
Added lib/libstuff/event/event.c.


































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
/* Copyright ©2006-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "event.h"

typedef bool (*Handler)(Window*, void*, XEvent*);
void	(*event_debug)(XEvent*);
long	event_lastconfigure;
long	event_xtime;
bool	event_looprunning;

EventHandler event_handler[LASTEvent] = {
	[ButtonPress] =		(EventHandler)event_buttonpress,
	[ButtonRelease] =	(EventHandler)event_buttonrelease,
	[ClientMessage] =	(EventHandler)event_clientmessage,
	[ConfigureNotify] =	(EventHandler)event_configurenotify,
	[ConfigureRequest] =	(EventHandler)event_configurerequest,
	[DestroyNotify] =	(EventHandler)event_destroynotify,
	[EnterNotify] =		(EventHandler)event_enternotify,
	[Expose] =		(EventHandler)event_expose,
	[FocusIn] =		(EventHandler)event_focusin,
	[FocusOut] =		(EventHandler)event_focusout,
	[KeyPress] =		(EventHandler)event_keypress,
	[KeyRelease] =		(EventHandler)event_keyrelease,
	[LeaveNotify] =		(EventHandler)event_leavenotify,
	[MapNotify] =		(EventHandler)event_mapnotify,
	[MapRequest] =		(EventHandler)event_maprequest,
	[MappingNotify] =	(EventHandler)event_mappingnotify,
	[MotionNotify] =	(EventHandler)event_motionnotify,
	[PropertyNotify] =	(EventHandler)event_propertynotify,
	[ReparentNotify] =	(EventHandler)event_reparentnotify,
	[SelectionClear] =	(EventHandler)event_selectionclear,
	[SelectionNotify] =	(EventHandler)event_selection,
	[UnmapNotify] =		(EventHandler)event_unmapnotify,
};

void
_event_handle(Window *w, ulong offset, XEvent *event) {
	Handler f;
	HandlersLink *l;

	if(w->handler && (f = structmember(w->handler, Handler, offset)))
		if(!f(w, w->aux, event))
			return;

	for(l=w->handler_link; l; l=l->next)
		if((f = structmember(l->handler, Handler, offset)))
			if(!f(w, l->aux, event))
				return;
}

void
event_dispatch(XEvent *e) {
	if(event_debug)
		event_debug(e);

	if(e->type < nelem(event_handler)) {
		if(event_handler[e->type])
			event_handler[e->type](e);
	}else
		xext_event(e);
}

void
event_check(void) {
	XEvent ev;

	while(XPending(display)) {
		XNextEvent(display, &ev);
		event_dispatch(&ev);
	}
}

void
event_loop(void) {
	XEvent ev;

	event_looprunning = true;
	while(event_looprunning) {
		XNextEvent(display, &ev);
		event_dispatch(&ev);
	}
}

uint
event_flush(long event_mask, bool dispatch) {
	XEvent ev;
	uint n = 0;

	while(XCheckMaskEvent(display, event_mask, &ev)) {
		if(dispatch)
			event_dispatch(&ev);
		n++;
	}
	return n;
}

static int
findenter(Display *d, XEvent *e, XPointer v) {
	long *l;

	USED(d);
	l = (long*)v;
	if(*l)
		return false;
	if(e->type == EnterNotify)
		return true;
	if(e->type == MotionNotify)
		(*l)++;
	return false;
}

/* This isn't perfect. If there were motion events in the queue
 * before this was called, then it flushes nothing. If we don't
 * check for them, we might lose a legitamate enter event.
 */
uint
event_flushenter(void) {
	XEvent e;
	long l;
	int n;

	l = 0;
	n = 0;
	while(XCheckIfEvent(display, &e, findenter, (void*)&l))
		n++;
	return n;
}

Added lib/libstuff/event/event.h.


















>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
#include <stuff/x.h>

typedef void (*EventHandler)(XEvent*);

#define handle(w, fn, ev) \
	BLOCK(if((w)->handler->fn) (w)->handler->fn((w), ev))

extern EventHandler event_handler[LASTEvent];

Added lib/libstuff/event/expose.c.
























>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
/* Copyright ©2006-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "event.h"

void
event_expose(XExposeEvent *ev) {
	Window *w;

	if(ev->count == 0 && (w = findwin(ev->window)))
		event_handle(w, expose, ev);
}
Added lib/libstuff/event/focusin.c.






















































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/* Copyright ©2006-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "event.h"

void
event_focusin(XFocusChangeEvent *ev) {
	Window *w;

	/* Yes, we're focusing in on nothing, here. */
	if(ev->detail == NotifyDetailNone) {
		/* FIXME: Do something. */
		return;
	}

	if(!((ev->detail == NotifyNonlinear)
	   ||(ev->detail == NotifyNonlinearVirtual)
	   ||(ev->detail == NotifyVirtual)
	   ||(ev->detail == NotifyInferior)
	   ||(ev->detail == NotifyAncestor)))
		return;
	if((ev->mode == NotifyWhileGrabbed))
		return;

	if((w = findwin(ev->window)))
		event_handle(w, focusin, ev);
}
Added lib/libstuff/event/focusout.c.






































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/* Copyright ©2006-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "event.h"

void
event_focusout(XFocusChangeEvent *ev) {
	Window *w;

	if(!((ev->detail == NotifyNonlinear)
	   ||(ev->detail == NotifyNonlinearVirtual)
	   ||(ev->detail == NotifyVirtual)
	   ||(ev->detail == NotifyInferior)
	   ||(ev->detail == NotifyAncestor)))
		return;

	if((w = findwin(ev->window)))
		event_handle(w, focusout, ev);
}
Added lib/libstuff/event/ixp.c.
















































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/* Copyright ©2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include <ixp.h>
#include "event.h"

void
event_preselect(IxpServer *s) {
	USED(s);
	event_check();
	XFlush(display);
}

void
event_fdready(IxpConn *c) {
	USED(c);
	event_check();
}

void
event_fdclosed(IxpConn *c) {

	c->srv->running = false;
}
Added lib/libstuff/event/keypress.c.




























>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* Copyright ©2006-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "event.h"

void
event_keypress(XKeyEvent *ev) {
	Window *w;

	if(!ev->send_event)
		event_xtime = ev->time;
	if((w = findwin(ev->window)))
		event_handle(w, kdown, ev);
}
Added lib/libstuff/event/keyrelease.c.




























>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* Copyright ©2006-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "event.h"

void
event_keyrelease(XKeyEvent *ev) {
	Window *w;

	if(!ev->send_event)
		event_xtime = ev->time;
	if((w = findwin(ev->window)))
		event_handle(w, kup, ev);
}
Added lib/libstuff/event/leavenotify.c.




























>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* Copyright ©2006-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "event.h"

void
event_leavenotify(XCrossingEvent *ev) {
	Window *w;

	if(!ev->send_event)
		event_xtime = ev->time;
	if((w = findwin(ev->window)))
		event_handle(w, leave, ev);
}
Added lib/libstuff/event/mapnotify.c.
































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/* Copyright ©2006-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "event.h"

void
event_mapnotify(XMapEvent *ev) {
	Window *w;

	if(!ev->send_event)
		event_lastconfigure = ev->serial;
	if((w = findwin(ev->event)))
		event_handle(w, map, ev);
	if(ev->send_event && (w = findwin(ev->event)))
		event_handle(w, map, ev);
}
Added lib/libstuff/event/mappingnotify.c.






















>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
/* Copyright ©2006-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "event.h"

void
event_mappingnotify(XMappingEvent *ev) {

	/* Why do you need me to tell you this? */
	XRefreshKeyboardMapping(ev);
}
Added lib/libstuff/event/maprequest.c.
























>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
/* Copyright ©2006-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "event.h"

void
event_maprequest(XMapRequestEvent *ev) {
	Window *w;

	if((w = findwin(ev->parent)))
		event_handle(w, mapreq, ev);
}
Added lib/libstuff/event/motionnotify.c.




























>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* Copyright ©2006-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "event.h"

void
event_motionnotify(XMotionEvent *ev) {
	Window *w;

	if(!ev->send_event)
		event_xtime = ev->time;
	if((w = findwin(ev->window)))
		event_handle(w, motion, ev);
}
Added lib/libstuff/event/propertynotify.c.




























>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* Copyright ©2006-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "event.h"

void
event_propertynotify(XPropertyEvent *ev) {
	Window *w;

	if(!ev->send_event)
		event_xtime = ev->time;
	if((w = findwin(ev->window)))
		event_handle(w, property, ev);
}
Added lib/libstuff/event/reparentnotify.c.






































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/* Copyright ©2006-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "event.h"

void
event_reparentnotify(XReparentEvent *ev) {
	Window *target, *w;

	if(!ev->send_event)
		event_lastconfigure = ev->serial;
	w = nil;
	if((target = findwin(ev->window)) && (w = findwin(ev->parent)))
		target->parent = w;
	if((w = findwin(ev->event)))
		event_handle(w, reparent, ev);
	if(ev->send_event && target)
		event_handle(target, reparent, ev);
}
Added lib/libstuff/event/selection.c.






























>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/* Copyright ©2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "event.h"

void
event_selection(XSelectionEvent *ev) {
	Window *w;

	if(!ev->send_event)
		event_xtime = ev->time;
	if((w = findwin(ev->requestor)))
		event_handle(w, selection, ev);
}

Added lib/libstuff/event/selectionclear.c.




























>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* Copyright ©2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "event.h"

void
event_selectionclear(XSelectionClearEvent *ev) {
	Window *w;

	if(!ev->send_event)
		event_xtime = ev->time;
	if((w = findwin(ev->window)))
		event_handle(w, selectionclear, ev);
}
Added lib/libstuff/event/selectionrequest.c.






























>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/* Copyright ©2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "event.h"

void
event_selectionrequest(XSelectionRequestEvent *ev) {
	Window *w;

	if(!ev->send_event)
		event_xtime = ev->time;
	if((w = findwin(ev->owner)))
		event_handle(w, selectionrequest, ev);
}

Added lib/libstuff/event/unmapnotify.c.












































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/* Copyright ©2006-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "event.h"

void
event_unmapnotify(XUnmapEvent *ev) {
	Window *w;

	if(!ev->send_event)
		event_lastconfigure = ev->serial;
	if((w = findwin(ev->window))) {
		if(!ev->send_event)
			w->mapped = false;
		if(!ev->send_event && ev->event == ev->window)
			w->unmapped--;
		if(ev->send_event && ev->event != ev->window)
			event_handle(w, unmap, ev);
	}
	if((w = findwin(ev->event)))
		event_handle(w, unmap, ev);
}
Added lib/libstuff/event/xtime.c.




































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/* Copyright ©2006-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include <stuff/x.h>

static int
findtime(Display *d, XEvent *e, XPointer v) {
	Window *w;

	w = (Window*)v;
	if(e->type == PropertyNotify && e->xproperty.window == w->xid) {
		event_xtime = e->xproperty.time;
		return true;
	}
	return false;
}

long
event_updatextime(void) {
	Window *w;
	WinAttr wa;
	XEvent e;
	long l;

	w = createwindow(&scr.root, Rect(0, 0, 1, 1), 0, InputOnly, &wa, 0);

	XSelectInput(display, w->xid, PropertyChangeMask);
	changeprop_long(w, "ATOM", "ATOM", &l, 0);
	XIfEvent(display, &e, findtime, (void*)w);

	destroywindow(w);
	return event_xtime;
}

Added lib/libstuff/fmt/blprint.c.






































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/* Copyright ©2010 Kris Maglione <maglione.k at Gmail>
 * Copyright ©2002 by Lucent Technologies.
 * See LICENSE file for license details.
 */
#include "fmtdef.h"
#include <bio.h>

int
Blprint(Biobuf *bp, const char *fmt, ...)
{
	va_list arg;
	int n;

	va_start(arg, fmt);
	n = Bvlprint(bp, fmt, arg);
	va_end(arg);
	return n;
}

Added lib/libstuff/fmt/bvlprint.c.




















































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
/* Copyright ©2010 Kris Maglione <maglione.k at Gmail>
 * Copyright ©2002 by Lucent Technologies.
 * See LICENSE file for license details.
 */
#include "fmtdef.h"
#include <bio.h>

static int
fmtBlflush(Fmt *f)
{
	mbstate_t state;
	Biobuf *bp;
	Rune *rp, *rend;
	int res;

	bp = f->farg;
	rend = f->to;
	state = (mbstate_t){0};
	for(rp=(Rune*)f->start; rp < rend; rp++) {
		if(MB_LEN_MAX + bp->ocount > 0 && Bflush(bp) < 0)
			return 0;

		res = wcrtomb((char*)bp->ebuf + bp->ocount, *rp, &state);
		if(res == -1)
			Bputc(bp, '?');
		else
			bp->ocount += res;
	}
	f->to = f->start;
	return 1;
}

int
Bvlprint(Biobuf *bp, const char *fmt, va_list args)
{
	Fmt f;
	Rune buf[256];
	int res;

	if(utf8locale())
		return Bvprint(bp, fmt, args);

	f.runes = 1;
	f.start = (char*)buf;
	f.to = (char*)buf;
	f.stop = (char*)(buf + nelem(buf) - 1);
	f.flush = fmtBlflush;
	f.farg = bp;
	f.nfmt = 0;

	va_copy(f.args, args);
	res = dofmt(&f, fmt);
	va_end(f.args);
	if(res > 0 && fmtBlflush(&f) == 0)
		return -1;
	return res;
}

Added lib/libstuff/fmt/fmtbuf.c.






























>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include "fmtdef.h"

Fmt
fmtbuf(char *buf, int len) {
	Fmt f;

	f.runes = 0;
	f.start = buf;
	f.to = buf;
	f.stop = buf + len - 1;
	f.flush = 0;
	f.farg = nil;
	f.nfmt = 0;
	return f;
}
Added lib/libstuff/fmt/fmtdef.h.




















>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
#include <stuff/util.h>
#include <langinfo.h>
#include <limits.h>
#include <string.h>
#include <wchar.h>

extern void* __fmtflush(Fmt *f, void *t, int len);
extern int __fmtpad(Fmt *f, int n);
extern int __rfmtpad(Fmt *f, int n);

Added lib/libstuff/fmt/localefmt.c.






















































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
/* Copyright ©2010 Kris Maglione <maglione.k at Gmail>
 * Copyright ©2002 by Lucent Technologies.
 * See LICENSE file for license details.
 */
#include "fmtdef.h"

static bool
pad(Fmt *f, int len) {
	if(f->flags & FmtWidth) {
		if(f->runes)
			return __rfmtpad(f, f->width - len) >= 0;
		return __fmtpad(f, f->width - len) >= 0;
	}
	return true;
}

int
localefmt(Fmt *f) {
	mbstate_t state;
	Rune *rp, *rend;
	char *sp, *send, *str, *end;
	Rune r;
	wchar_t w;
	int res, count, rlen;

	str = va_arg(f->args, char*);

	if(utf8locale()) {
		/* We handle precision in bytes, fmtstrcpy in characters */
		if(f->flags & FmtPrec)
			f->prec = utfnlen(str, f->prec);
		return fmtstrcpy(f, str);
	}

	end = 0;
	if(f->flags & FmtPrec)
		end = str + f->prec;

	if(!(f->flags & FmtLeft) && !pad(f, localelen(str, end)))
		return -1;

	sp = f->to;
	send = f->stop;
	rp = (Rune*)f->to;
	rend = (Rune*)f->stop;

	count = 0;
	for(state = (mbstate_t){0}; ; str += res) {
		switch((res = mbrtowc(&w, str, end ? end - str : MB_LEN_MAX, &state))) {
		case 0:
		case -2:
			break;
		case -1:
			w = Runesync;
			res = 1;
			/* Fallthrough. */
		default:
			count++;
			if(w > Runemax)
				w = Runesync;
			r = w;
			// print("%d %C\n", res, r);
			if(f->runes) {
				if(rp >= rend) {
					// print("flush\n");
					rp = (Rune*)__fmtflush(f, rp, sizeof *rp);
					rend = (Rune*)f->stop;
					if(rp == nil)
						return -1;
				}
				*rp++ = r;
			}else {
				if(sp + UTFmax > send && sp + (rlen = runelen(r)) > send) {
					// print("flush %d\n", rlen);
					sp = __fmtflush(f, sp, rlen);
					send = f->stop;
					if(sp == nil)
						return -1;
				}
				if(r < Runeself)
					*sp++ = (char)r;
				else
					sp += runetochar(sp, &r);
			}
			continue;
		}
		if(f->runes) {
			f->nfmt += rp - (Rune*)f->to;
			f->to = (char*)rp;
		}else {
			f->nfmt += sp - (char*)f->to;
			f->to = sp;
		}
		break;
	}

	if((f->flags & FmtLeft) && !pad(f, count))
		return -1;

	return 0;
}

void
localefmtinstall(void) {
	fmtinstall('L', localefmt);
}

Added lib/libstuff/fmt/localelen.c.


































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/* Copyright ©2010 Kris Maglione <maglione.k at Gmail>
 * Copyright ©2002 by Lucent Technologies.
 * See LICENSE file for license details.
 */
#include "fmtdef.h"

int
localelen(char *str, char *end) {
	mbstate_t state;
	size_t n, res;

	if(utf8locale()) {
		if(end)
			return utfnlen(str, end - str);
		return utflen(str);
	}

	state = (mbstate_t){0};
	n = 0;
	for(n=0;;)
		switch((res = mbrtowc(nil, str, end ? end - str : MB_LEN_MAX, &state))) {
		case -1:
			return -1;
		case 0:
		case -2:
			return n;
		default:
			n++;
			str += res;
		}
	return n; /* Not reached. */
}

Added lib/libstuff/fmt/lprint.c.


































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/* Copyright ©2010 Kris Maglione <maglione.k at Gmail>
 * Copyright ©2002 by Lucent Technologies.
 * See LICENSE file for license details.
 */
#include "fmtdef.h"

int
lprint(int fd, const char *fmt, ...) {
	va_list ap;
	int res;

	va_start(ap, fmt);
	res = vlprint(fd, fmt, ap);
	va_end(ap);
	return res;
}

Added lib/libstuff/fmt/vlprint.c.
























































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
/* Copyright ©2010 Kris Maglione <maglione.k at Gmail>
 * Copyright ©2002 by Lucent Technologies.
 * See LICENSE file for license details.
 */
#include "fmtdef.h"
#include <unistd.h>

static int
fmtlfdflush(Fmt *f) {
	mbstate_t state;
	char buf[256];
	Rune *rp, *rend;
	char *sp, *send;
	int res;

	sp = buf;
	send = buf + sizeof buf - UTFmax;
	rend = f->to;
	state = (mbstate_t){0};
	for(rp=(Rune*)f->start; rp < rend; rp++) {
		res = wcrtomb(sp, *rp, &state);
		if(res == -1)
			*sp++ = '?'; /* Fixme? */
		else
			sp += res;
		if(sp >= send || rp == rend - 1) {
			if(write((uintptr_t)f->farg, buf, sp - buf) != sp - buf)
				return 0;
			sp = buf;
		}
	}
	f->to = f->start;
	return 1;
}

int
vlprint(int fd, const char *fmt, va_list args) {
	Fmt f;
	Rune buf[256];
	int res;

	if(utf8locale())
		return vfprint(fd, fmt, args);

	f.runes = 1;
	f.start = (char*)buf;
	f.to = (char*)buf;
	f.stop = (char*)(buf + nelem(buf) - 1);
	f.flush = fmtlfdflush;
	f.farg = (void*)(uintptr_t)fd;
	f.nfmt = 0;

	va_copy(f.args, args);
	res = dofmt(&f, fmt);
	va_end(f.args);
	if(res > 0 && fmtlfdflush(&f) == 0)
		return -1;
	return res;
}

Added lib/libstuff/geom/get_sticky.c.
















































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/* Copyright ©2006-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include <stuff/geom.h>

Align
get_sticky(Rectangle src, Rectangle dst) {
	Align corner;

	corner = 0;
	if(src.min.x != dst.min.x
	&& src.max.x == dst.max.x)
		corner |= East;
	else
		corner |= West;

	if(src.min.y != dst.min.y
	&& src.max.y == dst.max.y)
		corner |= South;
	else
		corner |= North;

	return corner;
}
Added lib/libstuff/geom/quadrant.c.


































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/* Copyright ©2006-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include <stuff/geom.h>

Align
quadrant(Rectangle r, Point pt) {
	Align ret;

	pt = subpt(pt, r.min);
	ret = East  * (pt.x >= Dx(r) * .7)
	    | West  * (pt.x <= Dx(r) * .3)
	    | South * (pt.y >= Dy(r) * .7)
	    | North * (pt.y <= Dy(r) * .3);

	return ret;
}
Added lib/libstuff/geom/rect_contains_p.c.
























>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
/* Copyright ©2006-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include <stuff/geom.h>

bool
rect_contains_p(Rectangle r, Rectangle r2) {
	return r2.min.x >= r.min.x
	    && r2.max.x <= r.max.x
	    && r2.min.y >= r.min.y
	    && r2.max.y <= r.max.y;
}
Added lib/libstuff/geom/rect_haspoint_p.c.




















>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
/* Copyright ©2006-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include <stuff/geom.h>

bool
rect_haspoint_p(Rectangle r, Point pt) {
	return (pt.x >= r.min.x) && (pt.x < r.max.x)
	    && (pt.y >= r.min.y) && (pt.y < r.max.y);
}
Added lib/libstuff/geom/rect_intersect_p.c.
























>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
/* Copyright ©2006-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include <stuff/geom.h>

bool
rect_intersect_p(Rectangle r, Rectangle r2) {
	return r.min.x <= r2.max.x
	    && r.max.x >= r2.min.x
	    && r.min.y <= r2.max.y
	    && r.max.y >= r2.min.y;
}
Added lib/libstuff/geom/rect_intersection.c.


































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/* Copyright ©2006-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include <stuff/geom.h>
#include <stuff/util.h>

Rectangle
rect_intersection(Rectangle r, Rectangle r2) {
	Rectangle ret;

	/* ret != canonrect(ret) ≡ no intersection. */
	ret.min.x = max(r.min.x, r2.min.x);
	ret.max.x = min(r.max.x, r2.max.x);
	ret.min.y = max(r.min.y, r2.min.y);
	ret.max.y = min(r.max.y, r2.max.y);
	return ret;
}
Added lib/libstuff/init_screens.c.


















>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
/* Copyright ©2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */

void init_screens(void);
void
init_screens(void) {
}

Added lib/libstuff/map.c.






































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
/* Written by Kris Maglione */
/* Public domain */
#include <assert.h>
#include <string.h>
#include <stuff/util.h>

/* Edit s/^([a-zA-Z].*)\n([a-z].*) {/\1 \2;/g  x/^([^a-zA-Z]|static|$)/-+d  s/ (\*map|val|*str)//g */

struct MapEnt {
	ulong		hash;
	const char*	key;
	void*		val;
	MapEnt*		next;
};

MapEnt *NM;

/* By Dan Bernstein. Public domain. */
static ulong
hash(const char *str) {
	ulong h;

	h = 5381;
	while (*str != '\0') {
		h += h << 5; /* h *= 33 */
		h ^= *str++;
	}
	return h;
}

static void
insert(Map *m, MapEnt **e, ulong val, const char *key) {
	MapEnt *te;

	m->nmemb++;
	te = emallocz(sizeof *te);
	te->hash = val;
	te->key = key;
	te->next = *e;
	*e = te;
}

static MapEnt**
map_getp(Map *map, ulong val, int create) {
	MapEnt **e;

	e = &map->bucket[val%map->nhash];
	for(; *e; e = &(*e)->next)
		if((*e)->hash >= val) break;
	if(*e == nil || (*e)->hash != val) {
		if(create)
			insert(map, e, val, nil);
		else
			e = &NM;
	}
	return e;
}

static MapEnt**
hash_getp(Map *map, const char *str, int create) {
	MapEnt **e;
	ulong h;
	int cmp;

	h = hash(str);
	e = map_getp(map, h, create);
	if(*e && (*e)->key == nil)
		(*e)->key = estrdup(str);
	else {
		SET(cmp);
		for(; *e; e = &(*e)->next)
			if((*e)->hash > h || (cmp = strcmp((*e)->key, str)) >= 0)
				break;
		if(*e == nil || (*e)->hash > h || cmp > 0)
			if(create)
				insert(map, e, h, estrdup(str));
	}
	return e;
}

void**
map_get(Map *map, ulong val, bool create) {
	MapEnt *e;

	e = *map_getp(map, val, create);
	return e ? &e->val : nil;
}

void**
hash_get(Map *map, const char *str, bool create) {
	MapEnt *e;

	e = *hash_getp(map, str, create);
	return e ? &e->val : nil;
}

void*
map_rm(Map *map, ulong val) {
	MapEnt **e, *te;
	void *ret;

	ret = nil;
	e = map_getp(map, val, 0);
	if(*e) {
		te = *e;
		ret = te->val;
		*e = te->next;
		assert(map->nmemb-- > 0);
		free(te);
	}
	return ret;
}

void*
hash_rm(Map *map, const char *str) {
	MapEnt **e, *te;
	void *ret;

	ret = nil;
	e = hash_getp(map, str, 0);
	if(*e) {
		te = *e;
		ret = te->val;
		*e = te->next;
		assert(map->nmemb-- > 0);
		free((void*)(uintptr_t)te->key);
		free(te);
	}
	return ret;
}

Added lib/libstuff/printevent.c.




























































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
/*
 * Original code posted to comp.sources.x
 * Modifications by Russ Cox <rsc@swtch.com>.
 * Further modifications by Kris Maglione <maglione.k at Gmail>
 */

/*
 * Path: uunet!wyse!mikew From: mikew@wyse.wyse.com (Mike Wexler) Newsgroups:
 * comp.sources.x Subject: v02i056:  subroutine to print events in human
 * readable form, Part01/01 Message-ID: <1935@wyse.wyse.com> Date: 22 Dec 88
 * 19:28:25 GMT Organization: Wyse Technology, San Jose Lines: 1093 Approved:
 * mikew@wyse.com
 *
 * Submitted-by: richsun!darkstar!ken Posting-number: Volume 2, Issue 56
 * Archive-name: showevent/part01
 *
 *
 * There are times during debugging when it would be real useful to be able to
 * print the fields of an event in a human readable form.  Too many times I
 * found myself scrounging around in section 8 of the Xlib manual looking for
 * the valid fields for the events I wanted to see, then adding printf's to
 * display the numeric values of the fields, and then scanning through X.h
 * trying to decode the cryptic detail and state fields.  After playing with
 * xev, I decided to write a couple of standard functions that I could keep
 * in a library and call on whenever I needed a little debugging verbosity.
 * The first function, GetType(), is useful for returning the string
 * representation of the type of an event.  The second function, ShowEvent(),
 * is used to display all the fields of an event in a readable format.  The
 * functions are not complicated, in fact, they are mind-numbingly boring -
 * but that's just the point nobody wants to spend the time writing functions
 * like this, they just want to have them when they need them.
 *
 * A simple, sample program is included which does little else but to
 * demonstrate the use of these two functions.  These functions have saved me
 * many an hour during debugging and I hope you find some benefit to these.
 * If you have any comments, suggestions, improvements, or if you find any
 * blithering errors you can get it touch with me at the following location:
 *
 * ken@richsun.UUCP
 */

#include <stdarg.h>
#include <bio.h>
#include <stuff/x.h>
#include <stuff/util.h>
#include "printevent.h"
#define Window XWindow
#define unmask _unmask

#define nil ((void*)0)

typedef struct Pair Pair;

struct Pair {
	int key;
	char *val;
};

static char* sep = " ";

static char *
search(Pair *lst, int key, char *(*def)(int)) {
	for(; lst->val; lst++)
		if(lst->key == key)
			return lst->val;
	return def(key);
}

static char*
unmask(Pair *list, uint val)
{
	Pair  *p;
	char *s, *end;
	int n;

	buffer[0] = '\0';
	end = buffer + sizeof buffer;
	s = buffer;

	n = 0;
	s = utfecpy(s, end, "(");
	for (p = list; p->val; p++)
		if (val & p->key) {
			if(n++)
				s = utfecpy(s, end, "|");
			s = utfecpy(s, end, p->val);
		}
	utfecpy(s, end, ")");

	return buffer;
}

static char *
strhex(int key) {
	sprint(buffer, "0x%x", key);
	return buffer;
}

static char *
strdec(int key) {
	sprint(buffer, "%d", key);
	return buffer;
}

static char *
strign(int key) {
	USED(key);

	return "?";
}

/******************************************************************************/
/**** Miscellaneous routines to convert values to their string equivalents ****/
/******************************************************************************/

static void
TInt(Fmt *b, va_list *ap) {
	fmtprint(b, "%d", va_arg(*ap, int));
}

static void
TWindow(Fmt *b, va_list *ap) {
	Window w;

	w = va_arg(*ap, Window);
	fmtprint(b, "0x%ux", (uint)w);
}

static void
TData(Fmt *b, va_list *ap) {
	long *l;
	int i;

	l = va_arg(*ap, long*);
	fmtprint(b, "{");
	for (i = 0; i < 5; i++) {
		if(i > 0)
			fmtprint(b, ", ");
		fmtprint(b, "0x%08lx", l[i]);
	}
	fmtprint(b, "}");
}

/* Returns the string equivalent of a timestamp */
static void
TTime(Fmt *b, va_list *ap) {
	ldiv_t	d;
	ulong   msec;
	ulong   sec;
	ulong   min;
	ulong   hr;
	ulong   day;
	Time time;

	time = va_arg(*ap, Time);

	msec = time/1000;
	d = ldiv(msec, 60);
	msec = time-msec*1000;

	sec = d.rem;
	d = ldiv(d.quot, 60);
	min = d.rem;
	d = ldiv(d.quot, 24);
	hr = d.rem;
	day = d.quot;

#ifdef notdef
	sprintf(buffer, "%lu day%s %02lu:%02lu:%02lu.%03lu",
		day, day == 1 ? "" : "(s)", hr, min, sec, msec);
#endif

	fmtprint(b, "%ludd_%ludh_%ludm_%lud.%03luds", day, hr, min, sec, msec);
}

/* Returns the string equivalent of a boolean parameter */
static void
TBool(Fmt *b, va_list *ap) {
	static Pair list[] = {
		{True, "True"},
		{False, "False"},
		{0, nil},
	};
	Bool key;

	key = va_arg(*ap, Bool);
	fmtprint(b, "%s", search(list, key, strign));
}

/* Returns the string equivalent of a property notify state */
static void
TPropState(Fmt *b, va_list *ap) {
	static Pair list[] = {
		{PropertyNewValue, "PropertyNewValue"},
		{PropertyDelete, "PropertyDelete"},
		{0, nil},
	};
	uint key;

	key = va_arg(*ap, uint);
	fmtprint(b, "%s", search(list, key, strign));
}

/* Returns the string equivalent of a visibility notify state */
static void
TVis(Fmt *b, va_list *ap) {
	static Pair list[] = {
		{VisibilityUnobscured, "VisibilityUnobscured"},
		{VisibilityPartiallyObscured, "VisibilityPartiallyObscured"},
		{VisibilityFullyObscured, "VisibilityFullyObscured"},
		{0, nil},
	};
	int key;

	key = va_arg(*ap, int);
	fmtprint(b, "%s", search(list, key, strign));
}

/* Returns the string equivalent of a mask of buttons and/or modifier keys */
static void
TModState(Fmt *b, va_list *ap) {
	static Pair list[] = {
		{Button1Mask, "Button1Mask"},
		{Button2Mask, "Button2Mask"},
		{Button3Mask, "Button3Mask"},
		{Button4Mask, "Button4Mask"},
		{Button5Mask, "Button5Mask"},
		{ShiftMask, "ShiftMask"},
		{LockMask, "LockMask"},
		{ControlMask, "ControlMask"},
		{Mod1Mask, "Mod1Mask"},
		{Mod2Mask, "Mod2Mask"},
		{Mod3Mask, "Mod3Mask"},
		{Mod4Mask, "Mod4Mask"},
		{Mod5Mask, "Mod5Mask"},
		{0, nil},
	};
	uint state;

	state = va_arg(*ap, uint);
	fmtprint(b, "%s", unmask(list, state));
}

/* Returns the string equivalent of a mask of configure window values */
static void
TConfMask(Fmt *b, va_list *ap) {
	static Pair list[] = {
		{CWX, "CWX"},
		{CWY, "CWY"},
		{CWWidth, "CWWidth"},
		{CWHeight, "CWHeight"},
		{CWBorderWidth, "CWBorderWidth"},
		{CWSibling, "CWSibling"},
		{CWStackMode, "CWStackMode"},
		{0, nil},
	};
	uint valuemask;

	valuemask = va_arg(*ap, uint);
	fmtprint(b, "%s", unmask(list, valuemask));
}

/* Returns the string equivalent of a motion hint */
#if 0
static void
IsHint(Fmt *b, va_list *ap) {
	static Pair list[] = {
		{NotifyNormal, "NotifyNormal"},
		{NotifyHint, "NotifyHint"},
		{0, nil},
	};
	char key;

	key = va_arg(*ap, char);
	fmtprint(b, "%s", search(list, key, strign));
}
#endif

/* Returns the string equivalent of an id or the value "None" */
static void
TIntNone(Fmt *b, va_list *ap) {
	static Pair list[] = {
		{None, "None"},
		{0, nil},
	};
	int key;

	key = va_arg(*ap, int);
	fmtprint(b, "%s", search(list, key, strhex));
}

/* Returns the string equivalent of a colormap state */
static void
TColMap(Fmt *b, va_list *ap) {
	static Pair list[] = {
		{ColormapInstalled, "ColormapInstalled"},
		{ColormapUninstalled, "ColormapUninstalled"},
		{0, nil},
	};
	int key;

	key = va_arg(*ap, int);
	fmtprint(b, "%s", search(list, key, strign));
}

/* Returns the string equivalent of a crossing detail */
static void
TXing(Fmt *b, va_list *ap) {
	static Pair list[] = {
		{NotifyAncestor, "NotifyAncestor"},
		{NotifyInferior, "NotifyInferior"},
		{NotifyVirtual, "NotifyVirtual"},
		{NotifyNonlinear, "NotifyNonlinear"},
		{NotifyNonlinearVirtual, "NotifyNonlinearVirtual"},
		{0, nil},
	};
	int key;

	key = va_arg(*ap, int);
	fmtprint(b, "%s", search(list, key, strign));
}

/* Returns the string equivalent of a focus change detail */
static void
TFocus(Fmt *b, va_list *ap) {
	static Pair list[] = {
		{NotifyAncestor, "NotifyAncestor"},
		{NotifyInferior, "NotifyInferior"},
		{NotifyVirtual, "NotifyVirtual"},
		{NotifyNonlinear, "NotifyNonlinear"},
		{NotifyNonlinearVirtual, "NotifyNonlinearVirtual"},
		{NotifyPointer, "NotifyPointer"},
		{NotifyPointerRoot, "NotifyPointerRoot"},
		{NotifyDetailNone, "NotifyDetailNone"},
		{0, nil},
	};
	int key;

	key = va_arg(*ap, int);
	fmtprint(b, "%s", search(list, key, strign));
}

/* Returns the string equivalent of a configure detail */
static void
TConfDetail(Fmt *b, va_list *ap) {
	static Pair list[] = {
		{Above, "Above"},
		{Below, "Below"},
		{TopIf, "TopIf"},
		{BottomIf, "BottomIf"},
		{Opposite, "Opposite"},
		{0, nil},
	};
	int key;

	key = va_arg(*ap, int);
	fmtprint(b, "%s", search(list, key, strign));
}

/* Returns the string equivalent of a grab mode */
static void
TGrabMode(Fmt *b, va_list *ap) {
	static Pair list[] = {
		{NotifyNormal, "NotifyNormal"},
		{NotifyGrab, "NotifyGrab"},
		{NotifyUngrab, "NotifyUngrab"},
		{NotifyWhileGrabbed, "NotifyWhileGrabbed"},
		{0, nil},
	};
	int key;

	key = va_arg(*ap, int);
	fmtprint(b, "%s", search(list, key, strign));
}

/* Returns the string equivalent of a mapping request */
static void
TMapping(Fmt *b, va_list *ap) {
	static Pair list[] = {
		{MappingModifier, "MappingModifier"},
		{MappingKeyboard, "MappingKeyboard"},
		{MappingPointer, "MappingPointer"},
		{0, nil},
	};
	int key;

	key = va_arg(*ap, int);
	fmtprint(b, "%s", search(list, key, strign));
}

/* Returns the string equivalent of a stacking order place */
static void
TPlace(Fmt *b, va_list *ap) {
	static Pair list[] = {
		{PlaceOnTop, "PlaceOnTop"},
		{PlaceOnBottom, "PlaceOnBottom"},
		{0, nil},
	};
	int key;

	key = va_arg(*ap, int);
	fmtprint(b, "%s", search(list, key, strign));
}

/* Returns the string equivalent of a major code */
static void
TMajor(Fmt *b, va_list *ap) {
	static char *list[] = { XMajors };
	char *s;
	uint key;

	key = va_arg(*ap, uint);
	s = "<nil>";
	if(key < nelem(list))
		s = list[key];
	fmtprint(b, "%s", s);
}

static char*
eventtype(int key) {
	static Pair list[] = {
		{ButtonPress, "ButtonPress"},
		{ButtonRelease, "ButtonRelease"},
		{CirculateNotify, "CirculateNotify"},
		{CirculateRequest, "CirculateRequest"},
		{ClientMessage, "ClientMessage"},
		{ColormapNotify, "ColormapNotify"},
		{ConfigureNotify, "ConfigureNotify"},
		{ConfigureRequest, "ConfigureRequest"},
		{CreateNotify, "CreateNotify"},
		{DestroyNotify, "DestroyNotify"},
		{EnterNotify, "EnterNotify"},
		{Expose, "Expose"},
		{FocusIn, "FocusIn"},
		{FocusOut, "FocusOut"},
		{GraphicsExpose, "GraphicsExpose"},
		{GravityNotify, "GravityNotify"},
		{KeyPress, "KeyPress"},
		{KeyRelease, "KeyRelease"},
		{KeymapNotify, "KeymapNotify"},
		{LeaveNotify, "LeaveNotify"},
		{MapNotify, "MapNotify"},
		{MapRequest, "MapRequest"},
		{MappingNotify, "MappingNotify"},
		{MotionNotify, "MotionNotify"},
		{NoExpose, "NoExpose"},
		{PropertyNotify, "PropertyNotify"},
		{ReparentNotify, "ReparentNotify"},
		{ResizeRequest, "ResizeRequest"},
		{SelectionClear, "SelectionClear"},
		{SelectionNotify, "SelectionNotify"},
		{SelectionRequest, "SelectionRequest"},
		{UnmapNotify, "UnmapNotify"},
		{VisibilityNotify, "VisibilityNotify"},
		{0, nil},
	};
	return search(list, key, strdec);
}
/* Returns the string equivalent the keycode contained in the key event */
static void
TKeycode(Fmt *b, va_list *ap) {
	KeySym keysym_str;
	XKeyEvent *ev;
	char *keysym_name;

	ev = va_arg(*ap, XKeyEvent*);

	XLookupString(ev, buffer, sizeof buffer, &keysym_str, nil);

	if (keysym_str == NoSymbol)
		keysym_name = "NoSymbol";
	else
		keysym_name = XKeysymToString(keysym_str);
	if(keysym_name == nil)
		keysym_name = "(no name)";

	fmtprint(b, "%ud (keysym 0x%x \"%s\")", (int)ev->keycode,
			(int)keysym_str, keysym_name);
}

/* Returns the string equivalent of an atom or "None" */
static void
TAtom(Fmt *b, va_list *ap) {

	fmtstrcpy(b, atomname(va_arg(*ap, Atom)));
}

#define _(m) #m, ev->m
#define TEnd nil
typedef void (*Tfn)(Fmt*, va_list*);

static int
pevent(Fmt *fmt, void *e, ...) {
	va_list ap;
	Tfn fn;
	XAnyEvent *ev;
	char *key;
	int n;

	ev = e;
	fmtprint(fmt, "%3ld %-20s ", ev->serial, eventtype(ev->type));
	if(ev->send_event)
		fmtstrcpy(fmt, "(sendevent) ");

	n = 0;
	va_start(ap, e);
	for(;;) {
		fn = va_arg(ap, Tfn);
		if(fn == TEnd)
			break;

		if(n++ != 0)
			fmtprint(fmt, "%s", sep);

		key = va_arg(ap, char*);
		fmtprint(fmt, "%s=", key);
		fn(fmt, &ap);
	}
	va_end(ap);
	return 0;
}

/*****************************************************************************/
/*** Routines to print out readable values for the field of various events ***/
/*****************************************************************************/

static int
VerbMotion(Fmt *fmt, XEvent *e) {
	XMotionEvent *ev = &e->xmotion;

	return 	pevent(fmt, ev,
		TWindow, _(window),
		TWindow, _(root),
		TWindow, _(subwindow),
		TTime, _(time),
		TInt, _(x), TInt, _(y),
		TInt, _(x_root), TInt, _(y_root),
		TModState, _(state),
		TBool, _(same_screen),
		TEnd
	);
    //fprintf(stderr, "is_hint=%s%s", IsHint(ev->is_hint), sep);
}

static int
VerbButton(Fmt *fmt, XEvent *e) {
	XButtonEvent *ev = &e->xbutton;

	return 	pevent(fmt, ev,
		TWindow, _(window),
		TWindow, _(root),
		TWindow, _(subwindow),
		TTime, _(time),
		TInt, _(x), TInt, _(y),
		TInt, _(x_root), TInt, _(y_root),
		TModState, _(state),
		TInt, _(button),
		TBool, _(same_screen),
		TEnd
	);
}

static int
VerbColormap(Fmt *fmt, XEvent *e) {
	XColormapEvent *ev = &e->xcolormap;

	return 	pevent(fmt, ev,
		TWindow, _(window),
		TIntNone, _(colormap),
		TBool, _(new),
		TColMap, _(state),
		TEnd
	);
}

static int
VerbCrossing(Fmt *fmt, XEvent *e) {
	XCrossingEvent *ev = &e->xcrossing;

	return 	pevent(fmt, ev,
		TWindow, _(window),
		TWindow, _(root),
		TWindow, _(subwindow),
		TTime, _(time),
		TInt, _(x), TInt, _(y),
		TInt, _(x_root), TInt, _(y_root),
		TGrabMode, _(mode),
		TXing, _(detail),
		TBool, _(same_screen),
		TBool, _(focus),
		TModState, _(state),
		TEnd
	);
}

static int
VerbExpose(Fmt *fmt, XEvent *e) {
	XExposeEvent *ev = &e->xexpose;

	return 	pevent(fmt, ev,
		TWindow, _(window),
		TInt, _(x), TInt, _(y),
		TInt, _(width), TInt, _(height),
		TInt, _(count),
		TEnd
	);
}

static int
VerbGraphicsExpose(Fmt *fmt, XEvent *e) {
	XGraphicsExposeEvent *ev = &e->xgraphicsexpose;

	return 	pevent(fmt, ev,
		TWindow, _(drawable),
		TInt, _(x), TInt, _(y),
		TInt, _(width), TInt, _(height),
		TMajor, _(major_code),
		TInt, _(minor_code),
		TEnd
	);
}

static int
VerbNoExpose(Fmt *fmt, XEvent *e) {
	XNoExposeEvent *ev = &e->xnoexpose;

	return 	pevent(fmt, ev,
		TWindow, _(drawable),
		TMajor, _(major_code),
		TInt, _(minor_code),
		TEnd
	);
}

static int
VerbFocus(Fmt *fmt, XEvent *e) {
	XFocusChangeEvent *ev = &e->xfocus;

	return 	pevent(fmt, ev,
		TWindow, _(window),
		TGrabMode, _(mode),
		TFocus, _(detail),
		TEnd
	);
}

static int
VerbKeymap(Fmt *fmt, XEvent *e) {
	XKeymapEvent *ev = &e->xkeymap;
	int i;

	fmtprint(fmt, "window=0x%x%s", (int)ev->window, sep);
	fmtprint(fmt, "key_vector=");
	for (i = 0; i < 32; i++)
		fmtprint(fmt, "%02x", ev->key_vector[i]);
	fmtprint(fmt, "\n");
	return 0;
}

static int
VerbKey(Fmt *fmt, XEvent *e) {
	XKeyEvent *ev = &e->xkey;

	return 	pevent(fmt, ev,
		TWindow, _(window),
		TWindow, _(root),
		TWindow, _(subwindow),
		TTime, _(time),
		TInt, _(x), TInt, _(y),
		TInt, _(x_root), TInt, _(y_root),
		TModState, _(state),
		TKeycode, "keycode", ev,
		TBool, _(same_screen),
		TEnd
	);
}

static int
VerbProperty(Fmt *fmt, XEvent *e) {
	XPropertyEvent *ev = &e->xproperty;

	return 	pevent(fmt, ev,
		TWindow, _(window),
		TAtom, _(atom),
		TTime, _(time),
		TPropState, _(state),
		TEnd
	);
}

static int
VerbResizeRequest(Fmt *fmt, XEvent *e) {
	XResizeRequestEvent *ev = &e->xresizerequest;

	return 	pevent(fmt, ev,
		TWindow, _(window),
		TInt, _(width), TInt, _(height),
		TEnd
	);
}

static int
VerbCirculate(Fmt *fmt, XEvent *e) {
	XCirculateEvent *ev = &e->xcirculate;

	return 	pevent(fmt, ev,
		TWindow, _(event),
		TWindow, _(window),
		TPlace, _(place),
		TEnd
	);
}

static int
VerbConfigure(Fmt *fmt, XEvent *e) {
	XConfigureEvent *ev = &e->xconfigure;

	return 	pevent(fmt, ev,
		TWindow, _(event),
		TWindow, _(window),
		TInt, _(x), TInt, _(y),
		TInt, _(width), TInt, _(height),
		TInt, _(border_width),
		TIntNone, _(above),
		TBool, _(override_redirect),
		TEnd
	);
}

static int
VerbCreateWindow(Fmt *fmt, XEvent *e) {
	XCreateWindowEvent *ev = &e->xcreatewindow;

	return 	pevent(fmt, ev,
		TWindow, _(parent),
		TWindow, _(window),
		TInt, _(x), TInt, _(y),
		TInt, _(width), TInt, _(height),
		TInt, _(border_width),
		TBool, _(override_redirect),
		TEnd
	);
}

static int
VerbDestroyWindow(Fmt *fmt, XEvent *e) {
	XDestroyWindowEvent *ev = &e->xdestroywindow;

	return 	pevent(fmt, ev,
		TWindow, _(event),
		TWindow, _(window),
		TEnd
	);
}

static int
VerbGravity(Fmt *fmt, XEvent *e) {
	XGravityEvent *ev = &e->xgravity;

	return 	pevent(fmt, ev,
		TWindow, _(event),
		TWindow, _(window),
		TInt, _(x), TInt, _(y),
		TEnd
	);
}

static int
VerbMap(Fmt *fmt, XEvent *e) {
	XMapEvent *ev = &e->xmap;

	return 	pevent(fmt, ev,
		TWindow, _(event),
		TWindow, _(window),
		TBool, _(override_redirect),
		TEnd
	);
}

static int
VerbReparent(Fmt *fmt, XEvent *e) {
	XReparentEvent *ev = &e->xreparent;

	return 	pevent(fmt, ev,
		TWindow, _(event),
		TWindow, _(window),
		TWindow, _(parent),
		TInt, _(x), TInt, _(y),
		TBool, _(override_redirect),
		TEnd
	);
}

static int
VerbUnmap(Fmt *fmt, XEvent *e) {
	XUnmapEvent *ev = &e->xunmap;

	return 	pevent(fmt, ev,
		TWindow, _(event),
		TWindow, _(window),
		TBool, _(from_configure),
		TEnd
	);
}

static int
VerbCirculateRequest(Fmt *fmt, XEvent *e) {
	XCirculateRequestEvent *ev = &e->xcirculaterequest;

	return 	pevent(fmt, ev,
		TWindow, _(parent),
		TWindow, _(window),
		TPlace, _(place),
		TEnd
	);
}

static int
VerbConfigureRequest(Fmt *fmt, XEvent *e) {
	XConfigureRequestEvent *ev = &e->xconfigurerequest;

	return 	pevent(fmt, ev,
		TWindow, _(parent),
		TWindow, _(window),
		TInt, _(x), TInt, _(y),
		TInt, _(width), TInt, _(height),
		TInt, _(border_width),
		TIntNone, _(above),
		TConfDetail, _(detail),
		TConfMask, _(value_mask),
		TEnd
	);
}

static int
VerbMapRequest(Fmt *fmt, XEvent *e) {
	XMapRequestEvent *ev = &e->xmaprequest;

	return 	pevent(fmt, ev,
		TWindow, _(parent),
		TWindow, _(window),
		TEnd
	);
}

static int
VerbClient(Fmt *fmt, XEvent *e) {
	XClientMessageEvent *ev = &e->xclient;

	return 	pevent(fmt, ev,
		TWindow, _(window),
		TAtom, _(message_type),
		TInt, _(format),
		TData, "data (as longs)", &ev->data,
		TEnd
	);
}

static int
VerbMapping(Fmt *fmt, XEvent *e) {
	XMappingEvent *ev = &e->xmapping;

	return 	pevent(fmt, ev,
		TWindow, _(window),
		TMapping, _(request),
		TWindow, _(first_keycode),
		TWindow, _(count),
		TEnd
	);
}

static int
VerbSelectionClear(Fmt *fmt, XEvent *e) {
	XSelectionClearEvent *ev = &e->xselectionclear;

	return 	pevent(fmt, ev,
		TWindow, _(window),
		TAtom, _(selection),
		TTime, _(time),
		TEnd
	);
}

static int
VerbSelection(Fmt *fmt, XEvent *e) {
	XSelectionEvent *ev = &e->xselection;

	return 	pevent(fmt, ev,
		TWindow, _(requestor),
		TAtom, _(selection),
		TAtom, _(target),
		TAtom, _(property),
		TTime, _(time),
		TEnd
	);
}

static int
VerbSelectionRequest(Fmt *fmt, XEvent *e) {
	XSelectionRequestEvent *ev = &e->xselectionrequest;

	return 	pevent(fmt, ev,
		TWindow, _(owner),
		TWindow, _(requestor),
		TAtom, _(selection),
		TAtom, _(target),
		TAtom, _(property),
		TTime, _(time),
		TEnd
	);
}

static int
VerbVisibility(Fmt *fmt, XEvent *e) {
	XVisibilityEvent *ev = &e->xvisibility;

	return 	pevent(fmt, ev,
		TWindow, _(window),
		TVis, _(state),
		TEnd
	);
}

/******************************************************************************/
/**************** Print the values of all fields for any event ****************/
/******************************************************************************/

typedef struct Handler Handler;
struct Handler {
	int key;
	int (*fn)(Fmt*, XEvent*);
};

int
fmtevent(Fmt *fmt) {
	XEvent *e;
	XAnyEvent *ev;
	/*
		fprintf(stderr, "type=%s%s", eventtype(e->xany.type), sep);
		fprintf(stderr, "serial=%lu%s", ev->serial, sep);
		fprintf(stderr, "send_event=%s%s", TorF(ev->send_event), sep);
		fprintf(stderr, "display=0x%p%s", ev->display, sep);
	*/
	static Handler fns[] = {
		{MotionNotify, VerbMotion},
		{ButtonPress, VerbButton},
		{ButtonRelease, VerbButton},
		{ColormapNotify, VerbColormap},
		{EnterNotify, VerbCrossing},
		{LeaveNotify, VerbCrossing},
		{Expose, VerbExpose},
		{GraphicsExpose, VerbGraphicsExpose},
		{NoExpose, VerbNoExpose},
		{FocusIn, VerbFocus},
		{FocusOut, VerbFocus},
		{KeymapNotify, VerbKeymap},
		{KeyPress, VerbKey},
		{KeyRelease, VerbKey},
		{PropertyNotify, VerbProperty},
		{ResizeRequest, VerbResizeRequest},
		{CirculateNotify, VerbCirculate},
		{ConfigureNotify, VerbConfigure},
		{CreateNotify, VerbCreateWindow},
		{DestroyNotify, VerbDestroyWindow},
		{GravityNotify, VerbGravity},
		{MapNotify, VerbMap},
		{ReparentNotify, VerbReparent},
		{UnmapNotify, VerbUnmap},
		{CirculateRequest, VerbCirculateRequest},
		{ConfigureRequest, VerbConfigureRequest},
		{MapRequest, VerbMapRequest},
		{ClientMessage, VerbClient},
		{MappingNotify, VerbMapping},
		{SelectionClear, VerbSelectionClear},
		{SelectionNotify, VerbSelection},
		{SelectionRequest, VerbSelectionRequest},
		{VisibilityNotify, VerbVisibility},
		{0, nil},
	};
	Handler *p;

	e = va_arg(fmt->args, XEvent*);
	ev = &e->xany;

	for (p = fns; p->fn; p++)
		if (p->key == ev->type)
			return p->fn(fmt, e);
	return 1;
}

Added lib/libstuff/printevent.h.
















































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
int fmtevent(Fmt *fmt);

enum {
	X_CreateWindow = 1,
	X_ChangeWindowAttributes,
	X_GetWindowAttributes,
	X_DestroyWindow,
	X_DestroySubwindows,
	X_ChangeSaveSet,
	X_ReparentWindow,
	X_MapWindow,
	X_MapSubwindows,
	X_UnmapWindow,
	X_UnmapSubwindows,
	X_ConfigureWindow,
	X_CirculateWindow,
	X_GetGeometry,
	X_QueryTree,
	X_InternAtom,
	X_GetAtomName,
	X_ChangeProperty,
	X_DeleteProperty,
	X_GetProperty,
	X_ListProperties,
	X_SetSelectionOwner,
	X_GetSelectionOwner,
	X_ConvertSelection,
	X_SendEvent,
	X_GrabPointer,
	X_UngrabPointer,
	X_GrabButton,
	X_UngrabButton,
	X_ChangeActivePointerGrab,
	X_GrabKeyboard,
	X_UngrabKeyboard,
	X_GrabKey,
	X_UngrabKey,
	X_AllowEvents,
	X_GrabServer,
	X_UngrabServer,
	X_QueryPointer,
	X_GetMotionEvents,
	X_TranslateCoords,
	X_WarpPointer,
	X_SetInputFocus,
	X_GetInputFocus,
	X_QueryKeymap,
	X_OpenFont,
	X_CloseFont,
	X_QueryFont,
	X_QueryTextExtents,
	X_ListFonts,
	X_ListFontsWithInfo,
	X_SetFontPath,
	X_GetFontPath,
	X_CreatePixmap,
	X_FreePixmap,
	X_CreateGC,
	X_ChangeGC,
	X_CopyGC,
	X_SetDashes,
	X_SetClipRectangles,
	X_FreeGC,
	X_ClearArea,
	X_CopyArea,
	X_CopyPlane,
	X_PolyPoint,
	X_PolyLine,
	X_PolySegment,
	X_PolyRectangle,
	X_PolyArc,
	X_FillPoly,
	X_PolyFillRectangle,
	X_PolyFillArc,
	X_PutImage,
	X_GetImage,
	X_PolyText8,
	X_PolyText16,
	X_ImageText8,
	X_ImageText16,
	X_CreateColormap,
	X_FreeColormap,
	X_CopyColormapAndFree,
	X_InstallColormap,
	X_UninstallColormap,
	X_ListInstalledColormaps,
	X_AllocColor,
	X_AllocNamedColor,
	X_AllocColorCells,
	X_AllocColorPlanes,
	X_FreeColors,
	X_StoreColors,
	X_StoreNamedColor,
	X_QueryColors,
	X_LookupColor,
	X_CreateCursor,
	X_CreateGlyphCursor,
	X_FreeCursor,
	X_RecolorCursor,
	X_QueryBestSize,
	X_QueryExtension,
	X_ListExtensions,
	X_ChangeKeyboardMapping,
	X_GetKeyboardMapping,
	X_ChangeKeyboardControl,
	X_GetKeyboardControl,
	X_Bell,
	X_ChangePointerControl,
	X_GetPointerControl,
	X_SetScreenSaver,
	X_GetScreenSaver,
	X_ChangeHosts,
	X_ListHosts,
	X_SetAccessControl,
	X_SetCloseDownMode,
	X_KillClient,
	X_RotateProperties,
	X_ForceScreenSaver,
	X_SetPointerMapping,
	X_GetPointerMapping,
	X_SetModifierMapping,
	X_GetModifierMapping,
	X_NoOperation,
};

#define XMajors \
	"<nil>",\
	"CreateWindow",\
	"ChangeWindowAttributes",\
	"GetWindowAttributes",\
	"DestroyWindow",\
	"DestroySubwindows",\
	"ChangeSaveSet",\
	"ReparentWindow",\
	"MapWindow",\
	"MapSubwindows",\
	"UnmapWindow",\
	"UnmapSubwindows",\
	"ConfigureWindow",\
	"CirculateWindow",\
	"GetGeometry",\
	"QueryTree",\
	"InternAtom",\
	"GetAtomName",\
	"ChangeProperty",\
	"DeleteProperty",\
	"GetProperty",\
	"ListProperties",\
	"SetSelectionOwner",\
	"GetSelectionOwner",\
	"ConvertSelection",\
	"SendEvent",\
	"GrabPointer",\
	"UngrabPointer",\
	"GrabButton",\
	"UngrabButton",\
	"ChangeActivePointerGrab",\
	"GrabKeyboard",\
	"UngrabKeyboard",\
	"GrabKey",\
	"UngrabKey",\
	"AllowEvents",\
	"GrabServer",\
	"UngrabServer",\
	"QueryPointer",\
	"GetMotionEvents",\
	"TranslateCoords",\
	"WarpPointer",\
	"SetInputFocus",\
	"GetInputFocus",\
	"QueryKeymap",\
	"OpenFont",\
	"CloseFont",\
	"QueryFont",\
	"QueryTextExtents",\
	"ListFonts",\
	"ListFontsWithInfo",\
	"SetFontPath",\
	"GetFontPath",\
	"CreatePixmap",\
	"FreePixmap",\
	"CreateGC",\
	"ChangeGC",\
	"CopyGC",\
	"SetDashes",\
	"SetClipRectangles",\
	"FreeGC",\
	"ClearArea",\
	"CopyArea",\
	"CopyPlane",\
	"PolyPoint",\
	"PolyLine",\
	"PolySegment",\
	"PolyRectangle",\
	"PolyArc",\
	"FillPoly",\
	"PolyFillRectangle",\
	"PolyFillArc",\
	"PutImage",\
	"GetImage",\
	"PolyText8",\
	"PolyText16",\
	"ImageText8",\
	"ImageText16",\
	"CreateColormap",\
	"FreeColormap",\
	"CopyColormapAndFree",\
	"InstallColormap",\
	"UninstallColormap",\
	"ListInstalledColormaps",\
	"AllocColor",\
	"AllocNamedColor",\
	"AllocColorCells",\
	"AllocColorPlanes",\
	"FreeColors",\
	"StoreColors",\
	"StoreNamedColor",\
	"QueryColors",\
	"LookupColor",\
	"CreateCursor",\
	"CreateGlyphCursor",\
	"FreeCursor",\
	"RecolorCursor",\
	"QueryBestSize",\
	"QueryExtension",\
	"ListExtensions",\
	"ChangeKeyboardMapping",\
	"GetKeyboardMapping",\
	"ChangeKeyboardControl",\
	"GetKeyboardControl",\
	"Bell",\
	"ChangePointerControl",\
	"GetPointerControl",\
	"SetScreenSaver",\
	"GetScreenSaver",\
	"ChangeHosts",\
	"ListHosts",\
	"SetAccessControl",\
	"SetCloseDownMode",\
	"KillClient",\
	"RotateProperties",\
	"ForceScreenSaver",\
	"SetPointerMapping",\
	"GetPointerMapping",\
	"SetModifierMapping",\
	"GetModifierMapping",\
	"NoOperation",\

Added lib/libstuff/util/_die.c.




















































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/* Written by Kris Maglione <maglione.k at Gmail> */
/* Public domain */
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>
#include <fmt.h>
#include "util.h"

void
_die(char *file, int line, char *msg, ...) {
	va_list ap;

	va_start(ap, msg);
	fprint(2, "%s: dieing at %s:%d: %s\n",
		argv0, file, line,
		vsxprint(msg, ap));
	va_end(ap);

	kill(getpid(), SIGABRT);
	abort(); /* Adds too many frames:
		  *  _die()
		  *  abort()
		  *  raise(SIGABRT)
		  *  kill(getpid(), SIGABRT)
		  */
}
Added lib/libstuff/util/closeexec.c.
























>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
/* Copyright ©2008-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include <unistd.h>
#include <fcntl.h>
#include "util.h"

void
closeexec(int fd) {
	if(fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
		fatal("can't set %d close on exec: %r", fd);
}
Added lib/libstuff/util/comm.c.




















































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
/* Copyright ©2008-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include <string.h>
#include "util.h"
#include <stuff/x11.h>


char**
comm(int cols, char **toka, char **tokb) {
	Vector_ptr vec;
	char **ret;
	int cmp;

	vector_pinit(&vec);
	while(*toka || *tokb) {
		if(!*toka)
			cmp = 1;
		else if(!*tokb)
			cmp = -1;
		else
			cmp = strcmp(*toka, *tokb);
		if(cmp < 0) {
			if(cols & CLeft)
				vector_ppush(&vec, *toka);
			toka++;
		}else if(cmp > 0) {
			if(cols & CRight)
				vector_ppush(&vec, *tokb);
			tokb++;
		}else {
			if(cols & CCenter)
				vector_ppush(&vec, *toka);
			toka++;
			tokb++;
		}
	}
	vector_ppush(&vec, nil);
	ret = strlistdup((char**)vec.ary);
	free(vec.ary);
	return ret;
}
Added lib/libstuff/util/doublefork.c.




























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/* Copyright ©2008-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include <sys/wait.h>
#include <unistd.h>
#include "util.h"

int
doublefork(void) {
	pid_t pid;
	int status;

	switch(pid=fork()) {
	case -1:
		fatal("Can't fork(): %r");
	case 0:
		switch(pid=fork()) {
		case -1:
			fatal("Can't fork(): %r");
		case 0:
			return 0;
		default:
			exit(0);
		}
	default:
		waitpid(pid, &status, 0);
		return pid;
	}
	/* NOTREACHED */
}
Added lib/libstuff/util/emalloc.c.






















>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
/* Written by Kris Maglione <maglione.k at Gmail> */
/* Public domain */
#include "util.h"

void *
emalloc(uint size) {
	void *ret = malloc(size);
	if(!ret)
		mfatal("malloc", size);
	return ret;
}
Added lib/libstuff/util/emallocz.c.






















>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
/* Written by Kris Maglione <maglione.k at Gmail> */
/* Public domain */
#include <string.h>
#include "util.h"

void*
emallocz(uint size) {
	void *ret = emalloc(size);
	memset(ret, 0, size);
	return ret;
}
Added lib/libstuff/util/erealloc.c.






















>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
/* Written by Kris Maglione <maglione.k at Gmail> */
/* Public domain */
#include "util.h"

void *
erealloc(void *ptr, uint size) {
	void *ret = realloc(ptr, size);
	if(!ret)
		mfatal("realloc", size);
	return ret;
}
Added lib/libstuff/util/estrdup.c.
























>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
/* Written by Kris Maglione <maglione.k at Gmail> */
/* Public domain */
#include <string.h>
#include "util.h"

char*
estrdup(const char *str) {
	void *ret = strdup(str);
	if(!ret)
		mfatal("strdup", strlen(str));
	return ret;
}
Added lib/libstuff/util/estrndup.c.






























>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/* Written by Kris Maglione <maglione.k at Gmail> */
/* Public domain */
#include <string.h>
#include "util.h"

char*
estrndup(const char *str, uint len) {
	char *ret;

	len = min(len, strlen(str));
	ret = emalloc(len + 1);
	memcpy(ret, str, len);
	ret[len] = '\0';
	return ret;
}
Added lib/libstuff/util/fatal.c.














































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/* Written by Kris Maglione <maglione.k at Gmail> */
/* Public domain */
#include <fmt.h>
#include "util.h"

typedef struct VFmt VFmt;
struct VFmt {
	const char *fmt;
	va_list args;
};

#ifdef VARARGCK
# pragma varargck type "V" VFmt*
#endif

static int
Vfmt(Fmt *f) {
	VFmt *vf;
	int i;

	vf = va_arg(f->args, VFmt*);
	i = fmtvprint(f, vf->fmt, vf->args);
	return i;
}

void
fatal(const char *fmt, ...) {
	VFmt fp;

	fmtinstall('V', Vfmt);
	fmtinstall('', Vfmt);

	fp.fmt = fmt;
	va_start(fp.args, fmt);
	fprint(2, "%s: fatal: %V\n", argv0, &fp);
	va_end(fp.args);

	exit(1);
}
Added lib/libstuff/util/freelater.c.






























>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/* Written by Kris Maglione <maglione.k at Gmail> */
/* Public domain */
#include "util.h"

void*
freelater(void *p) {
	static char*	obj[16];
	static long	nobj;
	int id;

	id = nobj++ % nelem(obj);
	free(obj[id]);
	obj[id] = p;
	return p;
}
Added lib/libstuff/util/getbase.c.






















































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/* Copyright ©2008-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include <ctype.h>
#include <string.h>
#include <stuff/util.h>

#define strbcmp(str, const) (strncmp((str), (const), sizeof(const)-1))
int
getbase(const char **s, long *sign) {
	const char *p;
	int ret;

	ret = 10;
	*sign = 1;
	if(**s == '-') {
		*sign = -1;
		*s += 1;
	}else if(**s == '+')
		*s += 1;

	p = *s;
	if(!strbcmp(p, "0x")) {
		*s += 2;
		ret = 16;
	}
	else if(isdigit(p[0])) {
		if(p[1] == 'r') {
			*s += 2;
			ret = p[0] - '0';
		}
		else if(isdigit(p[1]) && p[2] == 'r') {
			*s += 3;
			ret = 10*(p[0]-'0') + (p[1]-'0');
		}
	}
	else if(p[0] == '0') {
		ret = 8;
	}
	if(ret != 10 && (**s == '-' || **s == '+'))
		*sign = 0;
	return ret;
}
Added lib/libstuff/util/getint.c.




























>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* Copyright ©2008-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include <stuff/util.h>

bool
getint(const char *s, int *ret) {
	long l;
	bool res;

	res = getlong(s, &l);
	*ret = l;
	return res;
}
Added lib/libstuff/util/getlong.c.














































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/* Copyright ©2008-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include <string.h>
#include <stuff/util.h>

bool
getlong(const char *s, long *ret) {
	const char *end;
	char *rend;
	int base;
	long sign;

	if(s == nil)
		return false;
	end = s+strlen(s);
	base = getbase(&s, &sign);
	if(sign == 0)
		return false;

	*ret = sign * strtol(s, &rend, base);
	return (end == rend);
}
Added lib/libstuff/util/getulong.c.














































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/* Copyright ©2008-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include <string.h>
#include <stuff/util.h>

bool
getulong(const char *s, ulong *ret) {
	const char *end;
	char *rend;
	int base;
	long sign;

	if(s == nil)
		return false;
	end = s+strlen(s);
	base = getbase(&s, &sign);
	if(sign < 1)
		return false;

	*ret = strtoul(s, &rend, base);
	return (end == rend);
}
Added lib/libstuff/util/grep.c.












































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/* Copyright ©2008-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "util.h"


void
grep(char **list, Reprog *re, int flags) {
	char **p, **q;
	int res;

	q = list;
	for(p=q; *p; p++) {
		res = 0;
		if(re)
			res = regexec(re, *p, nil, 0);
		if(res && !(flags & GInvert)
		|| !res && (flags & GInvert))
			*q++ = *p;
	}
	*q = nil;
}
Added lib/libstuff/util/join.c.






















































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/* Copyright ©2008-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include <fmt.h>
#include "util.h"

char*
join(char **list, char *sep, Fmt *f) {
	Fmt fmt;
	char **p;

	if(f == nil) {
		f = &fmt;
		if(fmtstrinit(f) < 0)
			abort();
	}

	for(p=list; *p; p++) {
		if(p != list)
			fmtstrcpy(f, sep);
		fmtstrcpy(f, *p);
	}

	if(f != &fmt)
		return nil;
	return fmtstrflush(f);
}
Added lib/libstuff/util/max.c.




















>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
/* Written by Kris Maglione <maglione.k at Gmail> */
/* Public domain */
#include "util.h"

int
max(int a, int b) {
	if(a > b)
		return a;
	return b;
}
Added lib/libstuff/util/mfatal.c.


































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/* Written by Kris Maglione <maglione.k at Gmail> */
/* Public domain */
#include <string.h>
#include <unistd.h>
#include "util.h"

/* Can't malloc */
void
mfatal(char *name, uint size) {
	const char
		couldnot[] = ": fatal: Could not ",
		paren[] = "() ",
		bytes[] = " bytes\n";
	char buf[1024];
	char sizestr[8];
	int i;

	i = sizeof sizestr;
	do {
		sizestr[--i] = '0' + (size%10);
		size /= 10;
	} while(size > 0);

	strlcat(buf, argv0, sizeof buf);
	strlcat(buf, couldnot, sizeof buf);
	strlcat(buf, name, sizeof buf);
	strlcat(buf, paren, sizeof buf);
	strlcat(buf, sizestr+i, sizeof buf);
	strlcat(buf, bytes, sizeof buf);
	write(2, buf, strlen(buf));

	exit(1);
}
Added lib/libstuff/util/min.c.




















>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
/* Written by Kris Maglione <maglione.k at Gmail> */
/* Public domain */
#include "util.h"

int
min(int a, int b) {
	if(a < b)
		return a;
	return b;
}
Added lib/libstuff/util/nsec.c.


























>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
/* Written by Kris Maglione <maglione.k at Gmail> */
/* Public domain */
#include "util.h"
#include <sys/time.h>

uvlong
nsec(void) {
	struct timeval tv;

	gettimeofday(&tv, nil);
	return (uvlong)tv.tv_sec * 1000000000 + (uvlong)tv.tv_usec * 1000;
}

Added lib/libstuff/util/pathsearch.c.
































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/* Copyright ©2008-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include <string.h>
#include <unistd.h>
#include <fmt.h>
#include "util.h"

char*
pathsearch(const char *path, const char *file, bool slashok) {
	char *orig, *p, *s;

	if(!slashok && strchr(file, '/') > file)
		file = sxprint("%s/%s", getcwd(buffer, sizeof buffer), file);
	else if(!strncmp(file, "./",  2))
		file = sxprint("%s/%s", getcwd(buffer, sizeof buffer), file+2);
	if(file[0] == '/') {
		if(access(file, X_OK))
			return strdup(file);
		return nil;
	}

	orig = estrdup(path ? path : getenv("PATH"));
	for(p=orig; (s=strtok(p, ":")); p=nil) {
		s = smprint("%s/%s", s, file);
		if(!access(s, X_OK))
			break;
		free(s);
	}
	free(orig);
	return s;
}
Added lib/libstuff/util/refree.c.


























>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
/* Copyright ©2008-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "util.h"

void
refree(Regex *r) {

	free(r->regex);
	free(r->regc);
	r->regex = nil;
	r->regc = nil;
}
Added lib/libstuff/util/reinit.c.
































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/* Copyright ©2008-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "util.h"


void
reinit(Regex *r, char *regx) {

	refree(r);

	if(regx[0] != '\0') {
		r->regex = estrdup(regx);
		r->regc = regcomp(regx);
	}
}
Added lib/libstuff/util/spawn3.c.






















































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/* Copyright ©2008-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include <errno.h>
#include <unistd.h>
#include "util.h"

int
spawn3(int fd[3], const char *file, char *argv[]) {
	/* Some ideas from Russ Cox's libthread port. */
	int p[2];
	int pid;

	if(pipe(p) < 0)
		return -1;
	closeexec(p[1]);

	switch(pid = doublefork()) {
	case 0:
		dup2(fd[0], 0);
		dup2(fd[1], 1);
		dup2(fd[2], 2);

		execvp(file, argv);
		write(p[1], &errno, sizeof errno);
		exit(1);
		break;
	default:
		close(p[1]);
		if(read(p[0], &errno, sizeof errno) == sizeof errno)
			pid = -1;
		close(p[0]);
		break;
	case -1: /* can't happen */
		break;
	}

	close(fd[0]);
	/* These could fail if any of these was also a previous fd. */
	close(fd[1]);
	close(fd[2]);
	return pid;
}
Added lib/libstuff/util/spawn3l.c.
























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/* Copyright ©2008-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include <fmt.h>
#include "util.h"

int
spawn3l(int fd[3], const char *file, ...) {
	va_list ap;
	char **argv;
	int i, n;

	va_start(ap, file);
	for(n=0; va_arg(ap, char*); n++)
		;
	va_end(ap);

	argv = emalloc((n+1) * sizeof *argv);
	va_start(ap, file);
	quotefmtinstall();
	for(i=0; i <= n; i++)
		argv[i] = va_arg(ap, char*);
	va_end(ap);

	i = spawn3(fd, file, argv);
	free(argv);
	return i;
}
Added lib/libstuff/util/stokenize.c.












































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/* Written by Kris Maglione <maglione.k at Gmail> */
/* Public domain */
#include <string.h>
#include "util.h"

uint
stokenize(char *res[], uint reslen, char *str, char *delim) {
	char *s;
	uint i;

	i = 0;
	s = str;
	while(i < reslen && *s) {
		while(*s && strchr(delim, *s))
			*(s++) = '\0';
		if(*s)
			res[i++] = s;
		while(*s && !strchr(delim, *s))
			s++;
	}
	return i;
}
Added lib/libstuff/util/strcasestr.c.


















































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/* Written by Kris Maglione <maglione.k at Gmail> */
/* Public domain */
#include <ctype.h>
#include <stdint.h>
#include <string.h>
#include <strings.h>
#include "util.h"

/* TODO: Make this UTF-8 compliant. */
char*
strcasestr(const char *dst, const char *src) {
	int len, dc, sc;

	if(src[0] == '\0')
		return (char*)(uintptr_t)dst;

	len = strlen(src) - 1;
	sc  = tolower(src[0]);
	for(; (dc = *dst); dst++) {
		dc = tolower(dc);
		if(sc == dc && (len == 0 || !strncasecmp(dst+1, src+1, len)))
			return (char*)(uintptr_t)dst;
	}
	return nil;
}
Added lib/libstuff/util/strend.c.


























>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
/* Copyright ©2008-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include <string.h>
#include <stuff/util.h>

char*
strend(char *s, int n) {
	int len;

	len = strlen(s);
	return s + max(0, len - n);
}
Added lib/libstuff/util/strlcat.c.




















































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/* Written by Kris Maglione <maglione.k at Gmail> */
/* Public domain */
#include "util.h"

uint
strlcat(char *dst, const char *src, uint size) {
	const char *s;
	char *d;
	int n, len;

	d = dst;
	s = src;
	n = size;
	while(n-- > 0 && *d != '\0')
		d++;
	len = n;

	while(*s != '\0') {
		if(n-- > 0)
			*d++ = *s;
		s++;
	}
	if(len > 0)
		*d = '\0';
	return size - n - 1;
}
Added lib/libstuff/util/strlcatprint.c.






































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/* Copyright ©2008-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include <string.h>
#include <fmt.h>
#include "util.h"

int
strlcatprint(char *buf, int len, const char *fmt, ...) {
	va_list ap;
	int buflen;
	int ret;

	va_start(ap, fmt);
	buflen = strlen(buf);
	ret = vsnprint(buf+buflen, len-buflen, fmt, ap);
	va_end(ap);
	return ret;
}
Added lib/libstuff/util/sxprint.c.




























>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* Written by Kris Maglione <maglione.k at Gmail> */
/* Public domain */
#include "util.h"

char*
sxprint(const char *fmt, ...) {
	va_list ap;
	char *ret;

	va_start(ap, fmt);
	ret = vsxprint(fmt, ap);
	va_end(ap);
	return ret;
}
Added lib/libstuff/util/tokenize.c.










































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/* Written by Kris Maglione <maglione.k at Gmail> */
/* Public domain */
#include "util.h"

uint
tokenize(char *res[], uint reslen, char *str, char delim) {
	char *s;
	uint i;

	i = 0;
	s = str;
	while(i < reslen && *s) {
		while(*s == delim)
			*(s++) = '\0';
		if(*s)
			res[i++] = s;
		while(*s && *s != delim)
			s++;
	}
	return i;
}
Added lib/libstuff/util/trim.c.






































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/* Written by Kris Maglione <maglione.k at Gmail> */
/* Public domain */
#include "util.h"

void
trim(char *str, const char *chars) {
	const char *r;
	char *p, *q;

	for(p=str, q=str; *p; p++) {
		for(r=chars; *r; r++)
			if(*p == *r)
				break;
		if(!*r)
			*q++ = *p;
	}
	*q = '\0';
}

Added lib/libstuff/util/uniq.c.




































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* Copyright ©2008-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include <string.h>
#include "util.h"

void
uniq(char **toks) {
	char **p, **q;

	q = toks;
	if(*q == nil)
		return;
	for(p=q+1; *p; p++)
		if(strcmp(*q, *p))
			*++q = *p;
	*++q = nil;
}
Added lib/libstuff/util/unmask.c.










































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/* Copyright ©2008-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include <fmt.h>
#include "util.h"

int
unmask(Fmt *f, long mask, char **table, long sep) {
	int i, nfmt;

	nfmt = f->nfmt;
	for(i=0; table[i]; i++)
		if(*table[i] && (mask & (1<<i))) {
			if(f->nfmt > nfmt)
				fmtrune(f, sep);
			if(fmtstrcpy(f, table[i]))
				return -1;
		}
	return 0;
}

Added lib/libstuff/util/unquote.c.












































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/* Copyright ©2008-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "util.h"

int
unquote(char *buf, char *toks[], int ntoks) {
	char *s, *t;
	bool inquote;
	int n;

	n = 0;
	s = buf;
	while(*s && n < ntoks) {
		while(*s && utfrune(" \t\r\n", *s))
			s++;
		inquote = false;
		toks[n] = s;
		t = s;
		while(*s && (inquote || !utfrune(" \t\r\n", *s))) {
			if(*s == '\'') {
				if(inquote && s[1] == '\'')
					*t++ = *s++;
				else
					inquote = !inquote;
			}
			else
				*t++ = *s;
			s++;
		}
		if(*s)
			s++;
		*t = '\0';
		if(s != toks[n])
			n++;
	}
	return n;
}
Added lib/libstuff/util/utflcpy.c.






















>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
/* Written by Kris Maglione <maglione.k at Gmail> */
/* Public domain */
#include "util.h"

int
utflcpy(char *to, const char *from, int l) {
	char *p;

	p = utfecpy(to, to+l, from);
	return p-to;
}
Added lib/libstuff/util/util.h.










>
>
>
>
>
1
2
3
4
5
/* Written by Kris Maglione <maglione.k at Gmail> */
/* Public domain */
#include <stuff/util.h>

void	mfatal(char*, uint);
Added lib/libstuff/util/vector.c.


































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/* Copyright ©2008-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include <string.h>
#include "util.h"

/* Blech. */
#define VECTOR(type, nam, c) \
void                                                                    \
vector_##c##init(Vector_##nam *v) {                                     \
	memset(v, 0, sizeof *v);                                        \
}                                                                       \
									\
void                                                                    \
vector_##c##free(Vector_##nam *v) {                                     \
	free(v->ary);                                                   \
	memset(v, 0, sizeof *v);                                        \
}                                                                       \
									\
void                                                                    \
vector_##c##push(Vector_##nam *v, type val) {                           \
	if(v->n == v->size) {                                           \
		if(v->size == 0)                                        \
			v->size = 2;                                    \
		v->size <<= 2;                                          \
		v->ary = erealloc(v->ary, v->size * sizeof *v->ary);    \
	}                                                               \
	v->ary[v->n++] = val;                                           \
}                                                                       \

VECTOR(long, long, l)
VECTOR(Rectangle, rect, r)
VECTOR(void*, ptr, p)
Added lib/libstuff/util/vsxprint.c.


























>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
/* Written by Kris Maglione <maglione.k at Gmail> */
/* Public domain */
#include <fmt.h>
#include "util.h"

char*
vsxprint(const char *fmt, va_list ap) {
	char *s;

	s = vsmprint(fmt, ap);
	freelater(s);
	return s;
}
Added lib/libstuff/x11/colors/loadcolor.c.














































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include <string.h>
#include "../x11.h"

int
loadcolor(CTuple *c, const char *str, const char *end) {
	char buf[128];
	char *toks[4];

	utflcpy(buf, str, end ? min(end - str + 1, sizeof buf) : sizeof buf);
	if(3 > stokenize(toks, nelem(toks), buf, " \t\r\n"))
		return 0;

	if(!(parsecolor(toks[0], &c->fg)
	   && parsecolor(toks[1], &c->bg)
	   && parsecolor(toks[2], &c->border)))
		return 0;

	snprint(c->colstr, sizeof c->colstr, "%L %L %L", c->fg, c->bg, c->border);
	return toks[2] + strlen(toks[2]) - buf;
}
Added lib/libstuff/x11/colors/parsecolor.c.




































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

ulong
pixelvalue(Window *w, Color *c) {
	XColor xc;

	if(w->visual == nil || w->visual->class != TrueColor) {
		if(c->pixel != ~0UL)
			return c->pixel;
		xc.red = c->red;
		xc.green = c->green;
		xc.blue = c->blue;
		XAllocColor(display, w->colormap, &xc);
		return c->pixel = xc.pixel;
	}
	if(w->depth == 32 || c->alpha == 0)
		return (((ulong)c->alpha & 0xff00) << 16)
		     | (((ulong)c->red & 0xff00) << 8)
		     | (((ulong)c->green & 0xff00) << 0)
		     | (((ulong)c->blue & 0xff00) >> 8);
	else
		return ((ulong)c->red * 0xffff / c->alpha & 0xff00) << 8
		     | ((ulong)c->green * 0xffff / c->alpha & 0xff00) << 0
		     | ((ulong)c->blue * 0xffff / c->alpha & 0xff00) >> 8;
}

bool
parsecolor(const char *name, Color *ret) {
	ret->pixel = ~0UL;
	return XRenderParseColor(display, (char*)(uintptr_t)name, (XRenderColor*)ret);
}
Added lib/libstuff/x11/colors/xftcolor.c.


























>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

XftColor*
xftcolor(Image *i, Color *c) {
	XftColor *xc;

	xc = emallocz(sizeof *c);
	*xc = (XftColor){ pixelvalue(i, c), c->red, c->green, c->blue, c->alpha };
	return freelater(xc);
}
Added lib/libstuff/x11/convpts.c.


































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "x11.h"

XPoint*
convpts(Point *pt, int np) {
	XPoint *rp;
	int i;

	rp = emalloc(np * sizeof *rp);
	for(i = 0; i < np; i++) {
		rp[i].x = pt[i].x;
		rp[i].y = pt[i].y;
	}
	return rp;
}
Added lib/libstuff/x11/drawing/border.c.






































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

void
border(Image *dst, Rectangle r, int w, Color *col) {
	if(w == 0)
		return;

	r = insetrect(r, w/2);
	r.max.x -= w%2;
	r.max.y -= w%2;

	XSetLineAttributes(display, dst->gc, w, LineSolid, CapButt, JoinMiter);
	setgccol(dst, col);
	XDrawRectangle(display, dst->xid, dst->gc,
		       r.min.x, r.min.y, Dx(r), Dy(r));
}
Added lib/libstuff/x11/drawing/drawline.c.






















>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

void
drawline(Image *dst, Point p1, Point p2, int cap, int w, Color *col) {
	XSetLineAttributes(display, dst->gc, w, LineSolid, cap, JoinMiter);
	setgccol(dst, col);
	XDrawLine(display, dst->xid, dst->gc, p1.x, p1.y, p2.x, p2.y);
}
Added lib/libstuff/x11/drawing/drawpoly.c.






























>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

void
drawpoly(Image *dst, Point *pt, int np, int cap, int w, Color *col) {
	XPoint *xp;

	xp = convpts(pt, np);
	XSetLineAttributes(display, dst->gc, w, LineSolid, cap, JoinMiter);
	setgccol(dst, col);
	XDrawLines(display, dst->xid, dst->gc, xp, np, CoordModeOrigin);
	free(xp);
}
Added lib/libstuff/x11/drawing/drawstring.c.








































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include <string.h>
#include "../x11.h"

uint
fillstring(Image *dst, Font *font,
	   Rectangle r, Align align,
	   const char *text, CTuple *col, int borderw) {

	fill(dst, r, &col->bg);
	if(borderw)
		border(dst, r, borderw, &col->border);
	return drawstring(dst, font, r, align, text, &col->fg);
}

uint
drawstring(Image *dst, Font *font,
	   Rectangle r, Align align,
	   const char *text, Color *col) {
	Rectangle tr;
	char *buf;
	uint x, y, width, height, len;
	int shortened;

	shortened = 0;

	len = strlen(text);
	buf = emalloc(len+1);
	memcpy(buf, text, len+1);

	r.max.y -= font->pad.min.y;
	r.min.y += font->pad.max.y;

	height = font->ascent + font->descent;
	y = r.min.y + Dy(r) / 2 - height / 2 + font->ascent;

	width = Dx(r) - font->pad.min.x - font->pad.max.x - (font->height & ~1);

	r.min.x += font->pad.min.x;
	r.max.x -= font->pad.max.x;

	/* shorten text if necessary */
	tr = ZR;
	while(len > 0) {
		tr = textextents_l(font, buf, len + min(shortened, 3), nil);
		if(Dx(tr) <= width)
			break;
		while(len > 0 && (buf[--len]&0xC0) == 0x80)
			buf[len] = '.';
		buf[len] = '.';
		shortened++;
	}

	if(len == 0 || Dx(tr) > width)
		goto done;

	/* mark shortened info in the string */
	if(shortened)
		len += min(shortened, 3);

	switch (align) {
	case East:
		x = r.max.x - (tr.max.x + (font->height / 2));
		break;
	case Center:
		x = r.min.x + (Dx(r) - Dx(tr)) / 2 - tr.min.x;
		break;
	default:
		x = r.min.x + (font->height / 2) - tr.min.x;
		break;
	}

	setgccol(dst, col);
	switch(font->type) {
	case FFontSet:
		Xutf8DrawString(display, dst->xid,
				font->font.set, dst->gc,
				x, y,
				buf, len);
		break;
	case FXft:
		xft->drawstring(xftdrawable(dst), xftcolor(dst, col),
				font->font.xft,
				x, y, buf, len);
		break;
	case FX11:
		XSetFont(display, dst->gc, font->font.x11->fid);
		XDrawString(display, dst->xid, dst->gc,
			    x, y, buf, len);
		break;
	default:
		die("Invalid font type.");
	}

done:
	free(buf);
	return Dx(tr);
}
Added lib/libstuff/x11/drawing/fill.c.






















>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

void
fill(Image *dst, Rectangle r, Color *col) {
	setgccol(dst, col);
	XFillRectangle(display, dst->xid, dst->gc,
		r.min.x, r.min.y, Dx(r), Dy(r));
}
Added lib/libstuff/x11/drawing/fillpoly.c.




























>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

void
fillpoly(Image *dst, Point *pt, int np, Color *col) {
	XPoint *xp;

	xp = convpts(pt, np);
	setgccol(dst, col);
	XFillPolygon(display, dst->xid, dst->gc, xp, np, Complex, CoordModeOrigin);
	free(xp);
}
Added lib/libstuff/x11/drawing/setgccol.c.


















>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

void
setgccol(Image *dst, Color *c) {
	XSetForeground(display, dst->gc, pixelvalue(dst, c));
}
Added lib/libstuff/x11/errors.c.


















































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "x11.h"

extern ErrorCode ignored_xerrors[];
static bool	_trap_errors;
static long	nerrors;

int
errorhandler(Display *dpy, XErrorEvent *error) {
	ErrorCode *e;

	USED(dpy);

	if(_trap_errors) {
		nerrors++;
		return 0;
	}

	e = ignored_xerrors;
	if(e)
	for(; e->rcode || e->ecode; e++)
		if((e->rcode == 0 || e->rcode == error->request_code)
		&& (e->ecode == 0 || e->ecode == error->error_code))
			return 0;

	fprint(2, "%s: fatal error: Xrequest code=%d, Xerror code=%d\n",
			argv0, error->request_code, error->error_code);
	return xlib_errorhandler(display, error); /* calls exit() */
}

int
traperrors(bool enable) {

	sync();
	_trap_errors = enable;
	if (enable)
		nerrors = 0;
	return nerrors;
}
Added lib/libstuff/x11/focus/getfocus.c.


























>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

XWindow
getfocus(void) {
	XWindow ret;
	int revert;

	XGetInputFocus(display, &ret, &revert);
	return ret;
}
Added lib/libstuff/x11/focus/setfocus.c.


















>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

void
setfocus(Window *w, int mode) {
	XSetInputFocus(display, w->xid, mode, CurrentTime);
}
Added lib/libstuff/x11/freestringlist.c.


















>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "x11.h"

void
freestringlist(char *list[]) {
	XFreeStringList(list);
}
Added lib/libstuff/x11/geometry/XRect.c.






























>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

XRectangle
XRect(Rectangle r) {
	XRectangle xr;

	xr.x = r.min.x;
	xr.y = r.min.y;
	xr.width = Dx(r);
	xr.height = Dy(r);
	return xr;
}
Added lib/libstuff/x11/geometry/addpt.c.






















>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

Point
addpt(Point p, Point q) {
	p.x += q.x;
	p.y += q.y;
	return p;
}
Added lib/libstuff/x11/geometry/divpt.c.






















>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

Point
divpt(Point p, Point q) {
	p.x /= q.x;
	p.y /= q.y;
	return p;
}
Added lib/libstuff/x11/geometry/eqpt.c.


















>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

int
eqpt(Point p, Point q) {
	return p.x==q.x && p.y==q.y;
}
Added lib/libstuff/x11/geometry/eqrect.c.




















>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

int
eqrect(Rectangle a, Rectangle b) {
	return a.min.x==b.min.x && a.max.x==b.max.x
	    && a.min.y==b.min.y && a.max.y==b.max.y;
}
Added lib/libstuff/x11/geometry/insetrect.c.


























>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

Rectangle
insetrect(Rectangle r, int n) {
	r.min.x += n;
	r.min.y += n;
	r.max.x -= n;
	r.max.y -= n;
	return r;
}
Added lib/libstuff/x11/geometry/mulpt.c.






















>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

Point
mulpt(Point p, Point q) {
	p.x *= q.x;
	p.y *= q.y;
	return p;
}
Added lib/libstuff/x11/geometry/rectaddpt.c.


























>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

Rectangle
rectaddpt(Rectangle r, Point p) {
	r.min.x += p.x;
	r.max.x += p.x;
	r.min.y += p.y;
	r.max.y += p.y;
	return r;
}
Added lib/libstuff/x11/geometry/rectsetorigin.c.






























>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

Rectangle
rectsetorigin(Rectangle r, Point p) {
	Rectangle ret;

	ret.min.x = p.x;
	ret.min.y = p.y;
	ret.max.x = p.x + Dx(r);
	ret.max.y = p.y + Dy(r);
	return ret;
}
Added lib/libstuff/x11/geometry/rectsubpt.c.


























>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

Rectangle
rectsubpt(Rectangle r, Point p) {
	r.min.x -= p.x;
	r.max.x -= p.x;
	r.min.y -= p.y;
	r.max.y -= p.y;
	return r;
}
Added lib/libstuff/x11/geometry/subpt.c.






















>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

Point
subpt(Point p, Point q) {
	p.x -= q.x;
	p.y -= q.y;
	return p;
}
Added lib/libstuff/x11/ignored_xerrors.c.


















>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "x11.h"

ErrorCode ignored_xerrors[] = {
	{ 0, },
};

Added lib/libstuff/x11/images/allocimage.c.










































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

Image*
allocimage(int w, int h, int depth) {
	Image *img;

	img = emallocz(sizeof *img);
	img->type = WImage;
	img->xid = XCreatePixmap(display, scr.root.xid, w, h, depth);
	img->gc = XCreateGC(display, img->xid, 0, nil);
	img->colormap = scr.colormap;
	img->visual = scr.visual;
	if(depth == 32)
		img->visual = scr.visual32;
	img->depth = depth;
	img->r = Rect(0, 0, w, h);
	return img;
}
Added lib/libstuff/x11/images/copyimage.c.


























>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

void
copyimage(Image *dst, Rectangle r, Image *src, Point p) {
	XCopyArea(display,
		  src->xid, dst->xid,
		  dst->gc,
		  r.min.x, r.min.y, Dx(r), Dy(r),
		  p.x, p.y);
}
Added lib/libstuff/x11/images/freeimage.c.




































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

void
freeimage(Image *img) {
	if(img == nil)
		return;

	assert(img->type == WImage);

	if(img->xft)
		xft->drawdestroy(img->xft);
	XFreePixmap(display, img->xid);
	XFreeGC(display, img->gc);
	free(img);
}
Added lib/libstuff/x11/images/xftdrawable.c.






















>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

XftDraw*
xftdrawable(Image *img) {
	if(img->xft == nil)
		img->xft = xft->drawcreate(display, img->xid, img->visual, img->colormap);
	return img->xft;
}
Added lib/libstuff/x11/initdisplay.c.






























































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "x11.h"

int	(*xlib_errorhandler) (Display*, XErrorEvent*);

Map	windowmap;
Map	atommap;
Map	atomnamemap;
static MapEnt*	wbucket[137];
static MapEnt*	abucket[137];
static MapEnt*	anamebucket[137];

static int
Afmt(Fmt *f) {

	return fmtstrcpy(f, atomname(va_arg(f->args, Atom)));
}

static int
Lfmt(Fmt *f) {
	Color c;

#define fix(c, m) (ushort)((c.alpha ? (ulong)c.m * 0xffff / c.alpha : 0) >> 8)
	c = va_arg(f->args, Color);
	return fmtprint(f, c.alpha < 0xff00 ? "rgba:%02uhx/%02uhx/%02uhx/%02uhx"
					    : "#%02uhx%02uhx%02uhx",
			fix(c, red), fix(c, green), fix(c, blue), c.alpha >> 8);
#undef fix
}

static int
Pfmt(Fmt *f) {
	Point p;

	p = va_arg(f->args, Point);
	return fmtprint(f, "(%d,%d)", p.x, p.y);
}

static int
Rfmt(Fmt *f) {
	Rectangle r;

	r = va_arg(f->args, Rectangle);
	return fmtprint(f, "%P+%dx%d", r.min, Dx(r), Dy(r));
}

static int
Wfmt(Fmt *f) {
	Window *w;

	w = va_arg(f->args, Window*);
	if(w == nil)
		return fmtstrcpy(f, "<nil>");
	return fmtprint(f, "0x%ulx", w->xid);
}

void
initdisplay(void) {
	display = XOpenDisplay(nil);
	if(display == nil)
		fatal("Can't open display");
	scr.screen = DefaultScreen(display);
	scr.colormap = DefaultColormap(display, scr.screen);
	scr.visual = DefaultVisual(display, scr.screen);
	scr.gc = DefaultGC(display, scr.screen);
	scr.depth = DefaultDepth(display, scr.screen);

	scr.root.xid = RootWindow(display, scr.screen);
	scr.root.visual = scr.visual;
	scr.root.r = Rect(0, 0,
			  DisplayWidth(display, scr.screen),
			  DisplayHeight(display, scr.screen));
	scr.rect = scr.root.r;

	scr.root.parent = &scr.root;

	scr.xim = XOpenIM(display, nil, nil, nil);

	windowmap.bucket = wbucket;
	windowmap.nhash = nelem(wbucket);
	atommap.bucket = abucket;
	atommap.nhash = nelem(abucket);
	atomnamemap.bucket = anamebucket;
	atomnamemap.nhash = nelem(anamebucket);

	fmtinstall('A', Afmt);
	fmtinstall('L', Lfmt);
	fmtinstall('R', Rfmt);
	fmtinstall('P', Pfmt);
	fmtinstall('W', Wfmt);

	xlib_errorhandler = XSetErrorHandler(errorhandler);
}
Added lib/libstuff/x11/insanity/gethints.c.


































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include <string.h>
#include "../x11.h"

void
gethints(Window *w) {
	XSizeHints xs;
	XWMHints *wmh;
	WinHints *h;
	Point p;
	long size;

	if(w->hints == nil)
		w->hints = emalloc(sizeof *h);

	h = w->hints;
	*h = ZWinHints;

	wmh = XGetWMHints(display, w->xid);
	if(wmh) {
		if(wmh->flags & WindowGroupHint)
			h->group = wmh->window_group;
		free(wmh);
	}

	if(!XGetWMNormalHints(display, w->xid, &xs, &size))
		return;

	if(xs.flags & PMinSize) {
		h->min.x = xs.min_width;
		h->min.y = xs.min_height;
	}
	if(xs.flags & PMaxSize) {
		h->max.x = xs.max_width;
		h->max.y = xs.max_height;
	}

	/* Goddamn buggy clients. */
	if(h->max.x < h->min.x)
		h->max.x = h->min.x;
	if(h->max.y < h->min.y)
		h->max.y = h->min.y;

	h->base = h->min;
	if(xs.flags & PBaseSize) {
		p.x = xs.base_width;
		p.y = xs.base_height;
		h->base = p;
		h->baspect = p;
	}

	if(xs.flags & PResizeInc) {
		h->inc.x = max(xs.width_inc, 1);
		h->inc.y = max(xs.height_inc, 1);
	}

	if(xs.flags & PAspect) {
		h->aspect.min.x = xs.min_aspect.x;
		h->aspect.min.y = xs.min_aspect.y;
		h->aspect.max.x = xs.max_aspect.x;
		h->aspect.max.y = xs.max_aspect.y;
	}

	h->position = (xs.flags & (USPosition|PPosition)) != 0;

	if(!(xs.flags & PWinGravity))
		xs.win_gravity = NorthWestGravity;
	p = ZP;
	switch (xs.win_gravity) {
	case EastGravity:
	case CenterGravity:
	case WestGravity:
		p.y = 1;
		break;
	case SouthEastGravity:
	case SouthGravity:
	case SouthWestGravity:
		p.y = 2;
		break;
	}
	switch (xs.win_gravity) {
	case NorthGravity:
	case CenterGravity:
	case SouthGravity:
		p.x = 1;
		break;
	case NorthEastGravity:
	case EastGravity:
	case SouthEastGravity:
		p.x = 2;
		break;
	}
	h->grav = p;
	h->gravstatic = (xs.win_gravity == StaticGravity);
}
Added lib/libstuff/x11/insanity/gravitate.c.






































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

Rectangle
gravitate(Rectangle rc, Rectangle rf, Point grav) {
	Point d;

	/* Get delta between frame and client rectangles */
	d = subpt(subpt(rf.max, rf.min),
		  subpt(rc.max, rc.min));

	/* Divide by 2 and apply gravity */
	d = divpt(d, Pt(2, 2));
	d = mulpt(d, grav);

	return rectsubpt(rc, d);
}
Added lib/libstuff/x11/insanity/sethints.c.


































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"
#include <string.h>

const WinHints ZWinHints = {
	.inc = {1, 1},
	.max = {INT_MAX, INT_MAX},
};

typedef struct GravityMap	GravityMap;

struct GravityMap {
	Point	point;
	int	gravity;
};

static GravityMap gravity_map[] = {
	{ {0, 0}, NorthWestGravity },
	{ {0, 1}, WestGravity },
	{ {0, 2}, SouthWestGravity },

	{ {1, 0}, NorthGravity },
	{ {1, 1}, CenterGravity },
	{ {1, 2}, SouthGravity },

	{ {2, 0}, NorthEastGravity },
	{ {2, 1}, EastGravity },
	{ {2, 2}, SouthEastGravity },
};

void
sethints(Window *w, WinHints *h) {
	XSizeHints xhints = { 0, };
	int i;

	/* TODO: Group hint */

	if(w->hints == nil)
		w->hints = emalloc(sizeof *h);

	*w->hints = *h;

	if(!eqpt(h->min, ZP)) {
		xhints.flags |= PMinSize;
		xhints.min_width = h->min.x;
		xhints.min_height = h->min.y;
	}
	if(!eqpt(h->max, Pt(INT_MAX, INT_MAX))) {
		xhints.flags |= PMaxSize;
		xhints.max_width = h->max.x;
		xhints.max_height = h->max.y;
	}

	if(!eqpt(h->base, ZP)) {
		xhints.flags |= PBaseSize;
		xhints.base_width  = h->baspect.x;
		xhints.base_height = h->baspect.y;
	}

	if(!eqrect(h->aspect, ZR)) {
		xhints.flags |= PAspect;

		xhints.base_width  = h->baspect.x;
		xhints.base_height = h->baspect.y;

		xhints.min_aspect.x = h->aspect.min.x;
		xhints.min_aspect.y = h->aspect.min.y;

		xhints.max_aspect.x = h->aspect.max.x;
		xhints.max_aspect.y = h->aspect.max.y;
	}

	if(!eqpt(h->inc, Pt(1, 1))) {
		xhints.flags |= PResizeInc;
		xhints.width_inc  = h->inc.x;
		xhints.height_inc = h->inc.y;
	}

	/* USPosition is probably an evil assumption, but it holds in our use cases. */
	if(h->position)
		xhints.flags |= USPosition | PPosition;

	xhints.flags |= PWinGravity;
	if(h->gravstatic)
		xhints.win_gravity = StaticGravity;
	else
		for(i=0; i < nelem(gravity_map); i++)
			if(h->grav.x == gravity_map[i].point.x &&
			   h->grav.y == gravity_map[i].point.y) {
				xhints.win_gravity = gravity_map[i].gravity;
				break;
			}

	XSetWMNormalHints(display, w->xid, &xhints);
}
Added lib/libstuff/x11/insanity/sizehint.c.




















































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

Rectangle
sizehint(WinHints *h, Rectangle r) {
	Point p, aspect, origin;

	if(h == nil)
		return r;

	origin = r.min;
	r = rectsubpt(r, origin);

	/* Min/max */
	r.max.x = max(r.max.x, h->min.x);
	r.max.y = max(r.max.y, h->min.y);
	r.max.x = min(r.max.x, h->max.x);
	r.max.y = min(r.max.y, h->max.y);

	/* Increment */
	p = subpt(r.max, h->base);
	r.max.x -= p.x % h->inc.x;
	r.max.y -= p.y % h->inc.y;

	/* Aspect */
	p = subpt(r.max, h->baspect);
	p.y = max(p.y, 1);

	aspect = h->aspect.min;
	if(p.x * aspect.y / p.y < aspect.x)
		r.max.y = h->baspect.y
			+ p.x * aspect.y / aspect.x;

	aspect = h->aspect.max;
	if(p.x * aspect.y / p.y > aspect.x)
		r.max.x = h->baspect.x
		        + p.y * aspect.x / aspect.y;

	return rectaddpt(r, origin);
}
Added lib/libstuff/x11/keyboard/grabkeyboard.c.
























>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

int
grabkeyboard(Window *w) {

	return XGrabKeyboard(display, w->xid, true /* owner events */,
		GrabModeAsync, GrabModeAsync, CurrentTime
		) == GrabSuccess;
}
Added lib/libstuff/x11/keyboard/ungrabkeyboard.c.


















>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

void
ungrabkeyboard(void) {
	XUngrabKeyboard(display, CurrentTime);
}
Added lib/libstuff/x11/keys/keycode.c.


















>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

KeyCode
keycode(const char *name) {
	return XKeysymToKeycode(display, XStringToKeysym(name));
}
Added lib/libstuff/x11/keys/parsekey.c.


































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

char *modkey_names[] = {
	"Shift",
	"",
	"Control",
	"Mod1",
	"Mod2",
	"Mod3",
	"Mod4",
	"Mod5",
	nil
};

bool
parsekey(char *str, int *mask, char **key) {
	static char *keys[16];
	int i, j, nkeys;

	*mask = 0;
	nkeys = tokenize(keys, nelem(keys), str, '-');
	for(i=0; i < nkeys; i++) {
		for(j=0; modkey_names[j]; j++)
			if(!strcasecmp(modkey_names[j], keys[i])) {
				*mask |= 1 << j;
				goto next;
			}
		break;
	next: continue;
	}
	if(key) {
		if(nkeys)
			*key = keys[i];
		return i == nkeys - 1;
	}
	else
		return i == nkeys;
}

int
numlockmask(void) {
	static int masks[] = {
		ShiftMask, LockMask, ControlMask, Mod1Mask,
		Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask
	};
	XModifierKeymap *modmap;
	KeyCode kcode;
	int i, max, numlock;

	numlock = 0;
	modmap = XGetModifierMapping(display);
	kcode = keycode("Num_Lock");
	if(kcode && modmap && modmap->max_keypermod > 0) {
		max = nelem(masks) * modmap->max_keypermod;
		for(i = 0; i < max && !numlock; i++)
			if(modmap->modifiermap[i] == kcode)
				numlock = masks[i / modmap->max_keypermod];
	}
	XFreeModifiermap(modmap);
	return numlock;
}

int
fmtkey(Fmt *f) {
	XKeyEvent *ev;
	char *key;
	int nfmt;

	ev = va_arg(f->args, XKeyEvent*);
	key = XKeysymToString(XKeycodeToKeysym(display, ev->keycode, 0));

	nfmt = f->nfmt;
	unmask(f, ev->state, modkey_names, '-');
	if(f->nfmt > nfmt)
		fmtrune(f, '-');
	return fmtstrcpy(f, key);
}

Added lib/libstuff/x11/mouse/grabpointer.c.
































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

int
grabpointer(Window *w, Window *confine, Cursor cur, int mask) {
	XWindow cw;

	cw = None;
	if(confine)
		cw = confine->xid;
	return XGrabPointer(display, w->xid, false /* owner events */, mask,
		GrabModeAsync, GrabModeAsync, cw, cur, CurrentTime
		) == GrabSuccess;
}
Added lib/libstuff/x11/mouse/pointerscreen.c.






























>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

int
pointerscreen(void) {
	XWindow win;
	Point pt;
	uint ui;
	int i;

	return XQueryPointer(display, scr.root.xid, &win, &win, &i, &i,
			     &pt.x, &pt.y, &ui);
}
Added lib/libstuff/x11/mouse/querypointer.c.






























>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

Point
querypointer(Window *w) {
	XWindow win;
	Point pt;
	uint ui;
	int i;

	XQueryPointer(display, w->xid, &win, &win, &i, &i, &pt.x, &pt.y, &ui);
	return pt;
}
Added lib/libstuff/x11/mouse/translate.c.




























>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

Point
translate(Window *src, Window *dst, Point sp) {
	Point pt;
	XWindow w;

	XTranslateCoordinates(display, src->xid, dst->xid, sp.x, sp.y,
			      &pt.x, &pt.y, &w);
	return pt;
}
Added lib/libstuff/x11/mouse/ungrabpointer.c.


















>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

void
ungrabpointer(void) {
	XUngrabPointer(display, CurrentTime);
}
Added lib/libstuff/x11/mouse/warppointer.c.














































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

void
warppointer(Point pt) {
	/* Nasty kludge for xephyr, xnest. */
	static int havereal = -1;
	static char* real;

	if(havereal == -1) {
		real = getenv("REALDISPLAY");
		havereal = real != nil;
	}
	if(havereal)
		system(sxprint("DISPLAY=%s wiwarp %d %d", real, pt.x, pt.y));

	XWarpPointer(display,
		/* src, dest w */ None, scr.root.xid,
		/* src_rect */	0, 0, 0, 0,
		/* target */	pt.x, pt.y);
}
Added lib/libstuff/x11/properties/changeprop_char.c.


















>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

void
changeprop_char(Window *w, const char *prop, const char *type, const char data[], int len) {
	changeproperty(w, prop, type, 8, (const uchar*)data, len);
}
Added lib/libstuff/x11/properties/changeprop_long.c.


















>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

void
changeprop_long(Window *w, const char *prop, const char *type, long data[], int len) {
	changeproperty(w, prop, type, 32, (uchar*)data, len);
}
Added lib/libstuff/x11/properties/changeprop_short.c.


















>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

void
changeprop_short(Window *w, const char *prop, const char *type, short data[], int len) {
	changeproperty(w, prop, type, 16, (uchar*)data, len);
}
Added lib/libstuff/x11/properties/changeprop_string.c.




















>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include <string.h>
#include "../x11.h"

void
changeprop_string(Window *w, const char *prop, const char *string) {
	changeprop_char(w, prop, "UTF8_STRING", string, strlen(string));
}
Added lib/libstuff/x11/properties/changeprop_textlist.c.
















































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include <string.h>
#include "../x11.h"

void
changeprop_textlist(Window *w, const char *prop, const char *type, char *data[]) {
	char **p, *s, *t;
	int len, n;

	len = 0;
	for(p=data; *p; p++)
		len += strlen(*p) + 1;
	s = emalloc(len);
	t = s;
	for(p=data; *p; p++) {
		n = strlen(*p) + 1;
		memcpy(t, *p, n);
		t += n;
	}
	changeprop_char(w, prop, type, s, len - 1);
	free(s);
}
Added lib/libstuff/x11/properties/changeprop_ulong.c.


















>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

void
changeprop_ulong(Window *w, const char *prop, const char *type, ulong data[], int len) {
	changeproperty(w, prop, type, 32, (uchar*)data, len);
}
Added lib/libstuff/x11/properties/changeproperty.c.






















>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

void
changeproperty(Window *w, const char *prop, const char *type,
	       int width, const uchar data[], int n) {
	XChangeProperty(display, w->xid, xatom(prop), xatom(type), width,
			PropModeReplace, data, n);
}
Added lib/libstuff/x11/properties/delproperty.c.


















>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

void
delproperty(Window *w, const char *prop) {
	XDeleteProperty(display, w->xid, xatom(prop));
}
Added lib/libstuff/x11/properties/getprop.c.
























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

ulong
getprop(Window *w, const char *prop, const char *type, Atom *actual, int *format,
	ulong offset, uchar **ret, ulong length) {
	Atom typea;
	ulong n, extra;
	int status;

	typea = (type ? xatom(type) : 0L);

	status = XGetWindowProperty(display, w->xid,
		xatom(prop), offset, length, false /* delete */,
		typea, actual, format, &n, &extra, ret);

	if(status != Success) {
		*ret = nil;
		return 0;
	}
	if(n == 0) {
		free(*ret);
		*ret = nil;
	}
	return n;
}
Added lib/libstuff/x11/properties/getprop_long.c.




















































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

ulong
getprop_long(Window *w, const char *prop, const char *type,
	     ulong offset, long **ret, ulong length) {
	Atom actual;
	ulong n;
	int format;

	n = getprop(w, prop, type, &actual, &format, offset, (uchar**)ret, length);
	if(n == 0 || format == 32 && xatom(type) == actual)
		return n;
	free(*ret);
	*ret = 0;
	return 0;
}

ulong
getprop_ulong(Window *w, const char *prop, const char *type,
	      ulong offset, ulong **ret, ulong length) {
	return getprop_long(w, prop, type, offset, (long**)ret, length);
}

Added lib/libstuff/x11/properties/getprop_string.c.






































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

char*
getprop_string(Window *w, const char *name) {
	char **list, *str;
	int n;

	str = nil;

	n = getprop_textlist(w, name, &list);
	if(n > 0)
		str = estrdup(*list);
	freestringlist(list);

	return str;
}
Added lib/libstuff/x11/properties/getprop_textlist.c.












































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

int
getprop_textlist(Window *w, const char *name, char **ret[]) {
	XTextProperty prop;
	char **list;
	int n;

	*ret = nil;
	n = 0;

	XGetTextProperty(display, w->xid, &prop, xatom(name));
	if(prop.nitems > 0) {
		if(Xutf8TextPropertyToTextList(display, &prop, &list, &n) == Success)
			*ret = list;
		XFree(prop.value);
	}
	return n;
}
Added lib/libstuff/x11/properties/getproperty.c.
























>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

ulong
getproperty(Window *w, char *prop, char *type, Atom *actual,
	    ulong offset, uchar **ret, ulong length) {
	int format;

	return getprop(w, prop, type, actual, &format, offset, ret, length);
}
Added lib/libstuff/x11/properties/strlistdup.c.


























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include <string.h>
#include "../x11.h"

char**
strlistdup(char *list[]) {
	char **p;
	char *q;
	int i, m, n;

	n = 0;
	m = 0;
	for(p=list; *p; p++, n++)
		m += strlen(*p) + 1;

	p = malloc((n+1) * sizeof(*p) + m);
	q = (char*)&p[n+1];

	for(i=0; i < n; i++) {
		p[i] = q;
		m = strlen(list[i]) + 1;
		memcpy(q, list[i], m);
		q += m;
	}
	p[n] = nil;
	return p;
}
Added lib/libstuff/x11/properties/windowname.c.


































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

char*
windowname(Window *w) {
	char *str;

	str = getprop_string(w, "_NET_WM_NAME");
	if(str == nil)
		str = getprop_string(w, "WM_NAME");
	if(str == nil)
		str = estrdup("");
	return str;
}

Added lib/libstuff/x11/selection.c.






























































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
/* Copyright ©2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "x11.h"

static Handlers handlers;

typedef struct Data	Data;

struct Data {
	long	selection;
	void	(*callback)(void*, char*);
	void*	aux;
};

static bool
_getselection(Window *w, long selection, char *type) {
	XConvertSelection(display, selection, xatom(type),
			  selection, w->xid, CurrentTime);
	return true;
}

void
getselection(char *selection, void (*callback)(void*, char*), void *aux) {
	Window *w;
	Data *d;

	d = emallocz(sizeof *d);
	d->selection = xatom(selection);
	d->callback = callback;
	d->aux = aux;

	w = createwindow(&scr.root, Rect(0, 0, 1, 1), 0, InputOnly, nil, 0);
	w->aux = d;
	sethandler(w, &handlers);

	_getselection(w, d->selection, "UTF8_STRING");
}

static bool
selection_event(Window *w, void *aux, XSelectionEvent *ev) {
	Data *d;
	char **ret;

	d = aux;
	if(ev->property == None && ev->target != xatom("STRING"))
		return _getselection(w, d->selection, "STRING");
	else if(ev->property == None)
		d->callback(d->aux, nil);
	else {
		getprop_textlist(w, atomname(ev->property), &ret);
		delproperty(w, atomname(ev->property));
		d->callback(d->aux, ret ? *ret : nil);
		free(ret);
	}
	destroywindow(w);
	return false;
}

static Handlers handlers = {
	.selection = selection_event,
};

Added lib/libstuff/x11/sendevent.c.




















>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "x11.h"

void
sendevent(Window *w, bool propagate, long mask, void *e) {
	XSendEvent(display, w->xid, propagate, mask, e);
}

Added lib/libstuff/x11/sendmessage.c.
















































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "x11.h"
#include <string.h>

void
sendmessage(Window *w, const char *name, long l0, long l1, long l2, long l3, long l4) {

	clientmessage(w, name, NoEventMask, 32, (ClientMessageData){ .l = { l0, l1, l2, l3, l4 } });
}

void
clientmessage(Window *w, const char *name, long mask, int format, ClientMessageData data) {
	XClientMessageEvent e;

	e.type = ClientMessage;
	e.window = w->xid;
	e.message_type = xatom(name);
	e.format = format;
	bcopy(&data, &e.data, sizeof(data));
	sendevent(w, false, mask, (XEvent*)&e);
}

Added lib/libstuff/x11/shape/setshapemask.c.






















>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

void
setshapemask(Window *dst, Image *src, Point pt) {
	/* Assumes that we have the shape extension... */
	XShapeCombineMask (display, dst->xid,
		ShapeBounding, pt.x, pt.y, src->xid, ShapeSet);
}
Added lib/libstuff/x11/sync.c.


















>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "x11.h"

void
sync(void) {
	XSync(display, false);
}
Added lib/libstuff/x11/text/freefont.c.














































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

void
freefont(Font *f) {
	switch(f->type) {
	case FFontSet:
		XFreeFontSet(display, f->font.set);
		break;
	case FXft:
		xft->fontclose(display, f->font.xft);
		break;
	case FX11:
		XFreeFont(display, f->font.x11);
		break;
	default:
		break;
	}
	free(f->name);
	free(f);
}
Added lib/libstuff/x11/text/labelh.c.


















>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

uint
labelh(Font *font) {
	return max(font->height + font->descent + font->pad.min.y + font->pad.max.y, 1);
}
Added lib/libstuff/x11/text/loadfont.c.












































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include <string.h>
#include "../x11.h"

Font*
loadfont(const char *name) {
	XFontStruct **xfonts;
	char **missing, **font_names;
	Biobuf *b;
	Font *f;
	int n, i;

	missing = nil;
	f = emallocz(sizeof *f);
	f->name = estrdup(name);
	if(!strncmp(f->name, "xft:", 4)) {
		f->type = FXft;

		if(!havexft())
			goto error;

		f->font.xft = xft->fontopen(display, scr.screen, f->name + 4);
		if(!f->font.xft)
			f->font.xft = xft->fontopenname(display, scr.screen, f->name + 4);
		if(!f->font.xft)
			goto error;

		f->ascent = f->font.xft->ascent;
		f->descent = f->font.xft->descent;
	}else {
		f->font.set = XCreateFontSet(display, name, &missing, &n, nil);
		if(missing) {
			if(false) {
				b = Bfdopen(dup(2), O_WRONLY);
				Bprint(b, "%s: note: missing fontset%s for '%s':", argv0,
						(n > 1 ? "s" : ""), name);
				for(i = 0; i < n; i++)
					Bprint(b, "%s %s", (i ? "," : ""), missing[i]);
				Bprint(b, "\n");
				Bterm(b);
			}
			freestringlist(missing);
		}

		if(f->font.set) {
			f->type = FFontSet;
			XFontsOfFontSet(f->font.set, &xfonts, &font_names);
			f->ascent = xfonts[0]->ascent;
			f->descent = xfonts[0]->descent;
		}else {
			f->type = FX11;
			f->font.x11 = XLoadQueryFont(display, name);
			if(!f->font.x11)
				goto error;

			f->ascent = f->font.x11->ascent;
			f->descent = f->font.x11->descent;
		}
	}
	f->height = f->ascent + f->descent;
	return f;

error:
	fprint(2, "%s: cannot load font: %s\n", argv0, name);
	f->type = 0;
	freefont(f);
	return nil;
}
Added lib/libstuff/x11/text/textextents_l.c.




































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

Rectangle
textextents_l(Font *font, const char *text, uint len, int *offset) {
	Rectangle rect;
	XRectangle r;
	XGlyphInfo i;
	int unused;

	if(!offset)
		offset = &unused;

	switch(font->type) {
	case FFontSet:
		*offset = Xutf8TextExtents(font->font.set, text, len, &r, nil);
		return Rect(r.x, -r.y - r.height, r.x + r.width, -r.y);
	case FXft:
		xft->textextents(display, font->font.xft, text, len, &i);
		*offset = i.xOff;
		return Rect(-i.x, i.y - i.height, -i.x + i.width, i.y);
	case FX11:
		rect = ZR;
		rect.max.x = XTextWidth(font->font.x11, text, len);
		rect.max.y = font->ascent;
		*offset = rect.max.x;
		return rect;
	default:
		die("Invalid font type");
		return ZR; /* shut up ken */
	}
}
Added lib/libstuff/x11/text/textwidth.c.




















>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include <string.h>
#include "../x11.h"

uint
textwidth(Font *font, const char *text) {
	return textwidth_l(font, text, strlen(text));
}
Added lib/libstuff/x11/text/textwidth_l.c.
























>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

uint
textwidth_l(Font *font, const char *text, uint len) {
	Rectangle r;

	r = textextents_l(font, text, len, nil);
	return Dx(r);
}
Added lib/libstuff/x11/windows/configwin.c.












































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

void
configwin(Window *w, Rectangle r, int border) {
	XWindowChanges wc;

	if(eqrect(r, w->r) && border == w->border)
		return;

	wc.x = r.min.x - border;
	wc.y = r.min.y - border;
	wc.width = Dx(r);
	wc.height = Dy(r);
	wc.border_width = border;
	XConfigureWindow(display, w->xid, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc);

	w->r = r;
	w->border = border;
}
Added lib/libstuff/x11/windows/createwindow.c.


















>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

Window*
createwindow(Window *parent, Rectangle r, int depth, uint class, WinAttr *wa, int valmask) {
	return createwindow_visual(parent, r, depth, scr.visual, class, wa, valmask);
}
Added lib/libstuff/x11/windows/createwindow_rgba.c.




































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

Window*
createwindow_rgba(Window *parent, Rectangle r, WinAttr *wa, int valmask) {
	WinAttr attr;

	if(scr.visual32 == nil)
		return createwindow(parent, r, scr.depth, InputOutput, wa, valmask);

	attr = wa ? *wa : (WinAttr){0};
	valmask |= CWBorderPixel | CWColormap;
	attr.border_pixel = 0;
	attr.colormap = scr.colormap32;
	return createwindow_visual(parent, r, 32, scr.visual32, InputOutput, &attr, valmask);
}
Added lib/libstuff/x11/windows/createwindow_visual.c.










































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"
#include <string.h>
#include <unistd.h>

static char hostname[HOST_NAME_MAX + 1];
static long pid;

Window*
createwindow_visual(Window *parent, Rectangle r,
		    int depth, Visual *vis, uint class,
		    WinAttr *wa, int valmask) {
	Window *w;
	WinAttr wa_empty;

	assert(parent->type == WWindow);

	if(wa == nil)
		wa = &wa_empty;

	w = emallocz(sizeof *w);
	w->visual = vis;
	w->type = WWindow;
	w->parent = parent;
	if(valmask & CWColormap)
		w->colormap = wa->colormap;
	if(valmask & CWEventMask)
		w->eventmask = wa->event_mask;

	w->xid = XCreateWindow(display, parent->xid, r.min.x, r.min.y, Dx(r), Dy(r),
				0 /* border */, depth, class, vis, valmask, wa);
#if 0
	print("createwindow_visual(%W, %R, %d, %p, %ud, %p, %x) = %W\n",
			parent, r, depth, vis, class, wa, valmask, w);
#endif
	if(class != InputOnly)
		w->gc = XCreateGC(display, w->xid, 0, nil);

	if(pid == 0)
		pid = getpid();
	changeprop_long(w, "_NET_WM_PID", "CARDINAL", &pid, 1);

	if(!hostname[0])
		gethostname(hostname, sizeof(hostname) - 1);
	if(hostname[0])
		changeprop_char(w, "WM_CLIENT_MACHINE", "STRING", hostname, strlen(hostname));

	w->r = r;
	w->depth = depth;
	return w;
}
Added lib/libstuff/x11/windows/destroywindow.c.


















































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

void
cleanupwindow(Window *w) {
	assert(w->type == WWindow);
	sethandler(w, nil);
	while(w->handler_link)
		pophandler(w, w->handler_link->handler);
	free(w->hints);
	free(w->dnd);
	if(w->xft)
		xft->drawdestroy(w->xft);
	if(w->gc)
		XFreeGC(display, w->gc);
}

void
destroywindow(Window *w) {
	cleanupwindow(w);
	XDestroyWindow(display, w->xid);
	free(w);
}
Added lib/libstuff/x11/windows/findwin.c.






































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"
#include <assert.h>

Window*
findwin(XWindow xw) {
	Window *w;
	void **e;

	e = map_get(&windowmap, (ulong)xw, false);
	if(e) {
		w = *e;
		assert(w->xid == xw);
		return w;
	}
	return nil;
}
Added lib/libstuff/x11/windows/getwinrect.c.






























>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

Rectangle
getwinrect(Window *w) {
	XWindowAttributes wa;
	Point p;

	if(!XGetWindowAttributes(display, w->xid, &wa))
		return ZR;
	p = translate(w, &scr.root, ZP);
	return rectaddpt(Rect(0, 0, wa.width, wa.height), p);
}
Added lib/libstuff/x11/windows/lowerwin.c.




















>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

void
lowerwin(Window *w) {
	assert(w->type == WWindow);
	XLowerWindow(display, w->xid);
}
Added lib/libstuff/x11/windows/mapwin.c.






























>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

int
mapwin(Window *w) {
	assert(w->type == WWindow);
	if(!w->mapped) {
		XMapWindow(display, w->xid);
		w->mapped = 1;
		return 1;
	}
	return 0;
}
Added lib/libstuff/x11/windows/movewin.c.


























>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

void
movewin(Window *w, Point pt) {
	Rectangle r;

	assert(w->type == WWindow);
	r = rectsetorigin(w->r, pt);
	reshapewin(w, r);
}
Added lib/libstuff/x11/windows/raisewin.c.




















>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

void
raisewin(Window *w) {
	assert(w->type == WWindow);
	XRaiseWindow(display, w->xid);
}
Added lib/libstuff/x11/windows/reparentwindow.c.
























>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

void
reparentwindow(Window *w, Window *par, Point p) {
	assert(w->type == WWindow);
	XReparentWindow(display, w->xid, par->xid, p.x, p.y);
	w->r = rectsubpt(w->r, w->r.min);
	w->r = rectaddpt(w->r, p);
}
Added lib/libstuff/x11/windows/reshapewin.c.
























>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

void
reshapewin(Window *w, Rectangle r) {
	assert(w->type == WWindow);
	assert(Dx(r) > 0 && Dy(r) > 0); /* Rather than an X error. */

	configwin(w, r, w->border);
}
Added lib/libstuff/x11/windows/selectinput.c.




















>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

void
selectinput(Window *w, long mask) {
	w->eventmask = mask;
	XSelectInput(display, w->xid, mask);
}
Added lib/libstuff/x11/windows/setborder.c.




























>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

void
setborder(Window *w, int width, Color *col) {

	assert(w->type == WWindow);
	if(width)
		XSetWindowBorder(display, w->xid, pixelvalue(w, col));
	if(width != w->border)
		configwin(w, w->r, width);
}
Added lib/libstuff/x11/windows/sethandler.c.




















































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

static void
updatemap(Window *w) {
	void **e;

	assert(w->type == WWindow);
	assert((w->prev != nil && w->next != nil) || w->next == w->prev);

	if(w->handler == nil && w->handler_link == nil)
		map_rm(&windowmap, (ulong)w->xid);
	else {
		e = map_get(&windowmap, (ulong)w->xid, true);
		*e = w;
	}
}

Handlers*
sethandler(Window *w, Handlers *new) {
	Handlers *old;

	old = w->handler;
	w->handler = new;

	updatemap(w);
	return old;
}

static HandlersLink*	free_link;

void
pushhandler(Window *w, Handlers *new, void *aux) {
	HandlersLink *l;
	int i;

	if(free_link == nil) {
		l = emalloc(16 * sizeof *l);
		for(i=0; i < 16; i++) {
			l[i].next = free_link;
			free_link = l;
		}
	}
	l = free_link;
	free_link = l->next;

	/* TODO: Maybe: pophandler(w, new); */

	l->next = w->handler_link;
	l->handler = new;
	l->aux = aux;
	w->handler_link = l;

	updatemap(w);
}

bool
pophandler(Window *w, Handlers *old) {
	HandlersLink **lp;
	HandlersLink *l;

	for(lp=&w->handler_link; (l=*lp); lp=&l->next)
		if(l->handler == old) {
			*lp = l->next;
			l->next = free_link;
			free_link = l;
			updatemap(w);
			return true;
		}
	return false;
}

Added lib/libstuff/x11/windows/setwinattr.c.




















>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

void
setwinattr(Window *w, WinAttr *wa, int valmask) {
	assert(w->type == WWindow);
	XChangeWindowAttributes(display, w->xid, valmask, wa);
}
Added lib/libstuff/x11/windows/unmapwin.c.
































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

int
unmapwin(Window *w) {
	assert(w->type == WWindow);
	if(w->mapped) {
		XUnmapWindow(display, w->xid);
		w->mapped = 0;
		w->unmapped++;
		return 1;
	}
	return 0;
}
Added lib/libstuff/x11/windows/window.c.




























>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "../x11.h"

Window*
window(XWindow xw) {
	Window *w;

	w = emallocz(sizeof *w);
	w->type = WWindow;
	w->xid = xw;
	return freelater(w);
}
Added lib/libstuff/x11/x11.c.
























>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "x11.h"

const Point	ZP = {0, 0};
const Rectangle	ZR = {{0, 0}, {0, 0}};

const Window	_pointerwin = { .xid = PointerRoot };
Window*		const pointerwin = (Window*)&_pointerwin;


Added lib/libstuff/x11/x11.h.


















































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */

#define _X11_VISIBLE
#define pointerwin __pointerwin
#include <assert.h>
#include <limits.h>
#include <math.h>
#include <strings.h>
#include <unistd.h>
#include <bio.h>
#include <stuff/x.h>
#include <stuff/util.h>
#undef  pointerwin

extern int	(*xlib_errorhandler) (Display*, XErrorEvent*);

void	configwin(Window*, Rectangle, int);
XPoint*	convpts(Point*, int);
int	errorhandler(Display*, XErrorEvent*);
void	setgccol(Image*, Color*);
XftColor*	xftcolor(Image*, Color*);
XftDraw*	xftdrawable(Image*);

Added lib/libstuff/x11/xatom.c.






































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "x11.h"

Atom
xatom(const char *name) {
	void **e, **f;

	e = hash_get(&atommap, name, true);
	if(*e == nil) {
		*e = (void*)XInternAtom(display, name, false);
		f = map_get(&atomnamemap, (ulong)*e, true);
		if(*f == nil)
			*f = (void*)(uintptr_t)name;
	}
	return (Atom)*e;
}

char*
atomname(ulong atom) {
	void **e;

	e = map_get(&atomnamemap, atom, true);
	if(*e == nil) {
		*e = XGetAtomName(display, atom);
		if(*e == nil) {
			map_rm(&atomnamemap, atom);
			return nil;
		}
		*hash_get(&atommap, *e, true) = (void*)atom;
	}
	return *e;
}

Added lib/libstuff/x11/xft.c.














































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/* Copyright ©2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include <stuff/x.h>
#include <stuff/util.h>

Xft	*xft;

#ifdef HAVE_RTLD
#include <dlfcn.h>

bool
havexft(void) {
	void *libxft;

	if(xft == nil) {
		libxft = dlopen("libXft.so", RTLD_LAZY);
		if(libxft == nil)
			return false;
		xft = emalloc(sizeof *xft);
		*(void**)(uintptr_t)&xft->drawcreate   = dlsym(libxft, "XftDrawCreate");
		*(void**)(uintptr_t)&xft->drawdestroy  = dlsym(libxft, "XftDrawDestroy");
		*(void**)(uintptr_t)&xft->fontopen     = dlsym(libxft, "XftFontOpenXlfd");
		*(void**)(uintptr_t)&xft->fontopenname = dlsym(libxft, "XftFontOpenName");
		*(void**)(uintptr_t)&xft->fontclose    = dlsym(libxft, "XftFontClose");
		*(void**)(uintptr_t)&xft->textextents  = dlsym(libxft, "XftTextExtentsUtf8");
		*(void**)(uintptr_t)&xft->drawstring   = dlsym(libxft, "XftDrawStringUtf8");
	}
	return xft && xft->drawcreate && xft->drawdestroy && xft->fontopen
		   && xft->fontopenname && xft->fontclose && xft->textextents && xft->drawstring;
}

#else
bool
havexft(void) {
	return false;
}
#endif

Added lib/libstuff/xext.c.








































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
/* Copyright ©2008-2010 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#define _X11_VISIBLE
#include <stuff/x.h>
#include <stuff/util.h>
#include <X11/extensions/Xrandr.h>
#include <X11/extensions/Xrender.h>
#include <X11/extensions/Xinerama.h>

#if RANDR_MAJOR < 1
#  error XRandR versions less than 1.0 are not supported
#endif

static void	randr_screenchange(XRRScreenChangeNotifyEvent*);
static bool	randr_event_p(XEvent *e);
static void	randr_init(void);
static void	render_init(void);
static void	xinerama_init(void);

typedef void		(*EvHandler)(XEvent*);
static EvHandler	randr_handlers[RRNumberEvents];

bool	have_RandR;
bool	have_render;
bool	have_xinerama;
int	randr_eventbase;
Visual*	render_visual;

static void
handle(XEvent *e, EvHandler h[], int base) {

	if(h[e->type-base])
		h[e->type-base](e);
}

void
xext_init(void) {
	randr_init();
	render_init();
	xinerama_init();
}

void
xext_event(XEvent *e) {

	if(randr_event_p(e))
		handle(e, randr_handlers, randr_eventbase);
}

static void
randr_init(void) {
	int errorbase, major, minor;

	have_RandR = XRRQueryExtension(display, &randr_eventbase, &errorbase);
	if(have_RandR)
		if(XRRQueryVersion(display, &major, &minor) && major < 1)
			have_RandR = false;
	if(have_RandR)
		XRRSelectInput(display, scr.root.xid, RRScreenChangeNotifyMask);
}

static bool
randr_event_p(XEvent *e) {
	return have_RandR
	    && (uint)e->type - randr_eventbase < RRNumberEvents;
}

static void
randr_screenchange(XRRScreenChangeNotifyEvent *ev) {

	XRRUpdateConfiguration((XEvent*)ev);
	if(ev->rotation*90 % 180)
		scr.rect = Rect(0, 0, ev->width, ev->height);
	else
		scr.rect = Rect(0, 0, ev->height, ev->width);
	init_screens();
}

static EvHandler randr_handlers[] = {
	[RRScreenChangeNotify] = (EvHandler)randr_screenchange,
};

/* Ripped most graciously from ecore_x. XRender documentation
 * is sparse.
 */
static void
render_init(void) {
	XVisualInfo *vip;
	XVisualInfo vi;
	int base, i, n;

	have_render = XRenderQueryExtension(display, &base, &base);
	if(!have_render)
		return;

	vi.class = TrueColor;
	vi.depth = 32;
	vi.screen = scr.screen;
	vip = XGetVisualInfo(display, VisualClassMask
				    | VisualDepthMask
				    | VisualScreenMask,
				    &vi, &n);
	for(i=0; i < n; i++)
		if(render_argb_p(vip[i].visual)) {
			render_visual = vip[i].visual;
			scr.visual32 = render_visual;
			break;
		}
	XFree(vip);
	if(render_visual)
		scr.colormap32 = XCreateColormap(display, scr.root.xid, render_visual, AllocNone);
}

bool
render_argb_p(Visual *v) {
	XRenderPictFormat *f;

	if(!have_render)
		return false;
	f = XRenderFindVisualFormat(display, v);
	return f
	    && f->type == PictTypeDirect
	    && f->direct.alphaMask;
}

static void
xinerama_init(void) {
	int base;

	have_xinerama = XineramaQueryExtension(display, &base, &base);
}

static bool
xinerama_active(void) {
	return have_xinerama && XineramaIsActive(display);
}

Rectangle*
xinerama_screens(int *np) {
	static Rectangle *rects;
	XineramaScreenInfo *res;
	int i, n;

	if(!xinerama_active()) {
		*np = 1;
		return &scr.rect;
	}

	free(rects);
	res = XineramaQueryScreens(display, &n);
	rects = emalloc(n * sizeof *rects);
	for(i=0; i < n; i++) {
		rects[i].min.x = res[i].x_org;
		rects[i].min.y = res[i].y_org;
		rects[i].max.x = res[i].x_org + res[i].width;
		rects[i].max.y = res[i].y_org + res[i].height;
	}
	XFree(res);

	*np = n;
	return rects;
}

Added lib/libutf/Makefile.




























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
ROOT= ../..
include $(ROOT)/mk/hdr.mk

VERSION=2.0
TARG=libutf

OBJ=\
	rune\
	runestrcat\
	runestrchr\
	runestrcmp\
	runestrcpy\
	runestrdup\
	runestrlen\
	runestrecpy\
	runestrncat\
	runestrncmp\
	runestrncpy\
	runestrrchr\
	runestrstr\
	runetype\
	utfecpy\
	utflen\
	utfnlen\
	utfrrune\
	utfrune\
	utfutf

include $(ROOT)/mk/lib.mk

Added lib/libutf/NOTICE.


















































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *		Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
 * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
*/

This is a Unix port of the Plan 9 formatted I/O package.

Please send comments about the packaging
to Russ Cox <rsc@swtch.com>.


----

This software is also made available under the Lucent Public License
version 1.02; see http://plan9.bell-labs.com/plan9dist/license.html

Added lib/libutf/README.










>
>
>
>
>
1
2
3
4
5
This software was packaged for Unix by Russ Cox.
Please send comments to rsc@swtch.com.

http://swtch.com/plan9port/unix

Added lib/libutf/isalpharune.3.


















































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
.deEX
.ift .ft5
.nf
..
.deEE
.ft1
.fi
..
.TH ISALPHARUNE 3
.SH NAME
isalpharune, islowerrune, isspacerune, istitlerune, isupperrune, tolowerrune, totitlerune, toupperrune \- Unicode character classes and cases
.SH SYNOPSIS
.B #include <utf.h>
.PP
.B
int isalpharune(Rune c)
.PP
.B
int islowerrune(Rune c)
.PP
.B
int isspacerune(Rune c)
.PP
.B
int istitlerune(Rune c)
.PP
.B
int isupperrune(Rune c)
.PP
.B
Rune tolowerrune(Rune c)
.PP
.B
Rune totitlerune(Rune c)
.PP
.B
Rune toupperrune(Rune c)
.SH DESCRIPTION
These routines examine and operate on Unicode characters,
in particular a subset of their properties as defined in the Unicode standard.
Unicode defines some characters as alphabetic and specifies three cases:
upper, lower, and title.
Analogously to
.IR isalpha (3)
for
.SM ASCII\c
,
these routines
test types and modify cases for Unicode characters.
The names are self-explanatory.
.PP
The case-conversion routines return the character unchanged if it has no case.
.SH SOURCE
.B http://swtch.com/plan9port/unix
.SH "SEE ALSO
.IR isalpha (3) ,
.IR "The Unicode Standard" .
Added lib/libutf/libutf.a.

cannot compute difference between binary files

Added lib/libutf/rune.3.




































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
.deEX
.ift .ft5
.nf
..
.deEE
.ft1
.fi
..
.TH RUNE 3
.SH NAME
runetochar, chartorune, runelen, runenlen, fullrune, utfecpy, utflen, utfnlen, utfrune, utfrrune, utfutf \- rune/UTF conversion
.SH SYNOPSIS
.ta \w'\fLchar*xx'u
.B #include <utf.h>
.PP
.B
int	runetochar(char *s, Rune *r)
.PP
.B
int	chartorune(Rune *r, char *s)
.PP
.B
int	runelen(long r)
.PP
.B
int	runenlen(Rune *r, int n)
.PP
.B
int	fullrune(char *s, int n)
.PP
.B
char*	utfecpy(char *s1, char *es1, char *s2)
.PP
.B
int	utflen(char *s)
.PP
.B
int	utfnlen(char *s, long n)
.PP
.B
char*	utfrune(char *s, long c)
.PP
.B
char*	utfrrune(char *s, long c)
.PP
.B
char*	utfutf(char *s1, char *s2)
.SH DESCRIPTION
These routines convert to and from a
.SM UTF
byte stream and runes.
.PP
.I Runetochar
copies one rune at
.I r
to at most
.B UTFmax
bytes starting at
.I s
and returns the number of bytes copied.
.BR UTFmax ,
defined as
.B 3
in
.BR <libc.h> ,
is the maximum number of bytes required to represent a rune.
.PP
.I Chartorune
copies at most
.B UTFmax
bytes starting at
.I s
to one rune at
.I r
and returns the number of bytes copied.
If the input is not exactly in
.SM UTF
format,
.I chartorune
will convert to 0x80 and return 1.
.PP
.I Runelen
returns the number of bytes
required to convert
.I r
into
.SM UTF.
.PP
.I Runenlen
returns the number of bytes
required to convert the
.I n
runes pointed to by
.I r
into
.SM UTF.
.PP
.I Fullrune
returns 1 if the string
.I s
of length
.I n
is long enough to be decoded by
.I chartorune
and 0 otherwise.
This does not guarantee that the string
contains a legal
.SM UTF
encoding.
This routine is used by programs that
obtain input a byte at
a time and need to know when a full rune
has arrived.
.PP
The following routines are analogous to the
corresponding string routines with
.B utf
substituted for
.B str
and
.B rune
substituted for
.BR chr .
.PP
.I Utfecpy
copies UTF sequences until a null sequence has been copied, but writes no 
sequences beyond
.IR es1 .
If any sequences are copied,
.I s1
is terminated by a null sequence, and a pointer to that sequence is returned.
Otherwise, the original
.I s1
is returned.
.PP
.I Utflen
returns the number of runes that
are represented by the
.SM UTF
string
.IR s .
.PP
.I Utfnlen
returns the number of complete runes that
are represented by the first
.I n
bytes of
.SM UTF
string
.IR s .
If the last few bytes of the string contain an incompletely coded rune,
.I utfnlen
will not count them; in this way, it differs from
.IR utflen ,
which includes every byte of the string.
.PP
.I Utfrune
.RI ( utfrrune )
returns a pointer to the first (last)
occurrence of rune
.I c
in the
.SM UTF
string
.IR s ,
or 0 if
.I c
does not occur in the string.
The NUL byte terminating a string is considered to
be part of the string
.IR s .
.PP
.I Utfutf
returns a pointer to the first occurrence of
the
.SM UTF
string
.I s2
as a
.SM UTF
substring of
.IR s1 ,
or 0 if there is none.
If
.I s2
is the null string,
.I utfutf
returns
.IR s1 .
.SH SOURCE
.B http://swtch.com/plan9port/unix
.SH SEE ALSO
.IR utf (7),
.IR tcs (1)
Added lib/libutf/rune.c.


















































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
 * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */
#include <stdarg.h>
#include <string.h>
#include "plan9.h"
#include "utf.h"

enum
{
	Bit1	= 7,
	Bitx	= 6,
	Bit2	= 5,
	Bit3	= 4,
	Bit4	= 3,
	Bit5	= 2,

	T1	= ((1<<(Bit1+1))-1) ^ 0xFF,	/* 0000 0000 */
	Tx	= ((1<<(Bitx+1))-1) ^ 0xFF,	/* 1000 0000 */
	T2	= ((1<<(Bit2+1))-1) ^ 0xFF,	/* 1100 0000 */
	T3	= ((1<<(Bit3+1))-1) ^ 0xFF,	/* 1110 0000 */
	T4	= ((1<<(Bit4+1))-1) ^ 0xFF,	/* 1111 0000 */
	T5	= ((1<<(Bit5+1))-1) ^ 0xFF,	/* 1111 1000 */

	Rune1	= (1<<(Bit1+0*Bitx))-1,		/* 0000 0000 0000 0000 0111 1111 */
	Rune2	= (1<<(Bit2+1*Bitx))-1,		/* 0000 0000 0000 0111 1111 1111 */
	Rune3	= (1<<(Bit3+2*Bitx))-1,		/* 0000 0000 1111 1111 1111 1111 */
	Rune4	= (1<<(Bit4+3*Bitx))-1,		/* 0011 1111 1111 1111 1111 1111 */

	Maskx	= (1<<Bitx)-1,			/* 0011 1111 */
	Testx	= Maskx ^ 0xFF,			/* 1100 0000 */

	Bad	= Runeerror
};

int
chartorune(Rune *rune, const char *str)
{
	int c, c1, c2, c3;
	long l;

	/*
	 * one character sequence
	 *	00000-0007F => T1
	 */
	c = *(uchar*)str;
	if(c < Tx) {
		*rune = c;
		return 1;
	}

	/*
	 * two character sequence
	 *	0080-07FF => T2 Tx
	 */
	c1 = *(uchar*)(str+1) ^ Tx;
	if(c1 & Testx)
		goto bad;
	if(c < T3) {
		if(c < T2)
			goto bad;
		l = ((c << Bitx) | c1) & Rune2;
		if(l <= Rune1)
			goto bad;
		*rune = l;
		return 2;
	}

	/*
	 * three character sequence
	 *	0800-FFFF => T3 Tx Tx
	 */
	c2 = *(uchar*)(str+2) ^ Tx;
	if(c2 & Testx)
		goto bad;
	if(c < T4) {
		l = ((((c << Bitx) | c1) << Bitx) | c2) & Rune3;
		if(l <= Rune2)
			goto bad;
		*rune = l;
		return 3;
	}

	/*
	 * four character sequence
	 *	10000-10FFFF => T4 Tx Tx Tx
	 */
	if(UTFmax >= 4) {
		c3 = *(uchar*)(str+3) ^ Tx;
		if(c3 & Testx)
			goto bad;
		if(c < T5) {
			l = ((((((c << Bitx) | c1) << Bitx) | c2) << Bitx) | c3) & Rune4;
			if(l <= Rune3)
				goto bad;
			if(l > Runemax)
				goto bad;
			*rune = l;
			return 4;
		}
	}

	/*
	 * bad decoding
	 */
bad:
	*rune = Bad;
	return 1;
}

int
runetochar(char *str, const Rune *rune)
{
	long c;

	/*
	 * one character sequence
	 *	00000-0007F => 00-7F
	 */
	c = *rune;
	if(c <= Rune1) {
		str[0] = c;
		return 1;
	}

	/*
	 * two character sequence
	 *	00080-007FF => T2 Tx
	 */
	if(c <= Rune2) {
		str[0] = T2 | (c >> 1*Bitx);
		str[1] = Tx | (c & Maskx);
		return 2;
	}

	/*
	 * three character sequence
	 *	00800-0FFFF => T3 Tx Tx
	 */
	if(c > Runemax)
		c = Runeerror;
	if(c <= Rune3) {
		str[0] = T3 |  (c >> 2*Bitx);
		str[1] = Tx | ((c >> 1*Bitx) & Maskx);
		str[2] = Tx |  (c & Maskx);
		return 3;
	}
	
	/*
	 * four character sequence
	 *	010000-1FFFFF => T4 Tx Tx Tx
	 */
	str[0] = T4 |  (c >> 3*Bitx);
	str[1] = Tx | ((c >> 2*Bitx) & Maskx);
	str[2] = Tx | ((c >> 1*Bitx) & Maskx);
	str[3] = Tx |  (c & Maskx);
	return 4;
}

int
runelen(long c)
{
	Rune rune;
	char str[10];

	rune = c;
	return runetochar(str, &rune);
}

int
runenlen(const Rune *r, int nrune)
{
	int nb, c;

	nb = 0;
	while(nrune--) {
		c = *r++;
		if(c <= Rune1)
			nb++;
		else
		if(c <= Rune2)
			nb += 2;
		else
		if(c <= Rune3 || c > Runemax)
			nb += 3;
		else
			nb += 4;
	}
	return nb;
}

int
fullrune(const char *str, int n)
{
	int c;

	if(n <= 0)
		return 0;
	c = *(uchar*)str;
	if(c < Tx)
		return 1;
	if(c < T3)
		return n >= 2;
	if(UTFmax == 3 || c < T4)
		return n >= 3;
	return n >= 4;
}
Added lib/libutf/runestrcat.3.




















































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
.deEX
.ift .ft5
.nf
..
.deEE
.ft1
.fi
..
.TH RUNESTRCAT 3
.SH NAME
runestrcat, 
runestrncat,
runestrcmp,
runestrncmp,
runestrcpy,
runestrncpy,
runestrecpy,
runestrlen,
runestrchr,
runestrrchr,
runestrdup,
runestrstr \- rune string operations
.SH SYNOPSIS
.B #include <u.h>
.br
.B #include <libc.h>
.PP
.ta \w'\fLRune* \fP'u
.B
Rune*	runestrcat(Rune *s1, Rune *s2)
.PP
.B
Rune*	runestrncat(Rune *s1, Rune *s2, long n)
.PP
.B
int	runestrcmp(Rune *s1, Rune *s2)
.PP
.B
int	runestrncmp(Rune *s1, Rune *s2, long n)
.PP
.B
Rune*	runestrcpy(Rune *s1, Rune *s2)
.PP
.B
Rune*	runestrncpy(Rune *s1, Rune *s2, long n)
.PP
.B
Rune*	runestrecpy(Rune *s1, Rune *es1, Rune *s2)
.PP
.B
long	runestrlen(Rune *s)
.PP
.B
Rune*	runestrchr(Rune *s, Rune c)
.PP
.B
Rune*	runestrrchr(Rune *s, Rune c)
.PP
.B
Rune*	runestrdup(Rune *s)
.PP
.B
Rune*	runestrstr(Rune *s1, Rune *s2)
.SH DESCRIPTION
These functions are rune string analogues of
the corresponding functions in 
.IR strcat (3).
.SH SOURCE
.B http://swtch.com/plan9port/unix
.SH SEE ALSO
.IR rune (3),
.IR strcat (3)
.SH BUGS
The outcome of overlapping moves varies among implementations.
Added lib/libutf/runestrcat.c.
















































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
 * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */
#include <stdarg.h>
#include <string.h>
#include <plan9.h>

Rune*
runestrcat(Rune *s1, const Rune *s2)
{

	runestrcpy(runestrchr(s1, 0), s2);
	return s1;
}
Added lib/libutf/runestrchr.c.






































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
 * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */
#include <stdarg.h>
#include <string.h>
#include "plan9.h"
#include "utf.h"

Rune*
runestrchr(const Rune *s, Rune c)
{
	Rune c0 = c;
	Rune c1;

	if(c == 0) {
		while(*s++)
			;
		return (Rune*)s-1;
	}

	while(c1 = *s++)
		if(c1 == c0)
			return (Rune*)s-1;
	return 0;
}
Added lib/libutf/runestrcmp.c.






































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
 * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */
#include <stdarg.h>
#include <string.h>
#include "plan9.h"
#include "utf.h"

int
runestrcmp(const Rune *s1, const Rune *s2)
{
	Rune c1, c2;

	for(;;) {
		c1 = *s1++;
		c2 = *s2++;
		if(c1 != c2) {
			if(c1 > c2)
				return 1;
			return -1;
		}
		if(c1 == 0)
			return 0;
	}
}
Added lib/libutf/runestrcpy.c.
























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
 * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */
#include <stdarg.h>
#include <string.h>
#include "plan9.h"
#include "utf.h"

Rune*
runestrcpy(Rune *s1, const Rune *s2)
{
	Rune *os1;

	os1 = s1;
	while(*s1++ = *s2++)
		;
	return os1;
}
Added lib/libutf/runestrdup.c.






















































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
 * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */
#include <stdlib.h>
#include <plan9.h>

Rune*
runestrdup(const Rune *s)
{
	Rune *ns;

	ns = malloc(sizeof(Rune)*(runestrlen(s) + 1));
	if(ns == 0)
		return 0;

	return runestrcpy(ns, s);
}
Added lib/libutf/runestrecpy.c.
































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
 * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */
#include <stdarg.h>
#include <string.h>
#include "plan9.h"
#include "utf.h"

Rune*
runestrecpy(Rune *s1, Rune *es1, const Rune *s2)
{
	if(s1 >= es1)
		return s1;

	while(*s1++ = *s2++){
		if(s1 == es1){
			*--s1 = '\0';
			break;
		}
	}
	return s1;
}
Added lib/libutf/runestrlen.c.










































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
 * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */
#include <plan9.h>

long
runestrlen(const Rune *s)
{

	return runestrchr(s, 0) - s;
}
Added lib/libutf/runestrncat.c.
































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
 * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */
#include <stdarg.h>
#include <string.h>
#include "plan9.h"
#include "utf.h"

Rune*
runestrncat(Rune *s1, const Rune *s2, long n)
{
	Rune *os1;

	os1 = s1;
	s1 = runestrchr(s1, 0);
	while(*s1++ = *s2++)
		if(--n < 0) {
			s1[-1] = 0;
			break;
		}
	return os1;
}
Added lib/libutf/runestrncmp.c.










































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
 * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */
#include <stdarg.h>
#include <string.h>
#include "plan9.h"
#include "utf.h"

int
runestrncmp(const Rune *s1, const Rune *s2, long n)
{
	Rune c1, c2;

	while(n > 0) {
		c1 = *s1++;
		c2 = *s2++;
		n--;
		if(c1 != c2) {
			if(c1 > c2)
				return 1;
			return -1;
		}
		if(c1 == 0)
			break;
	}
	return 0;
}
Added lib/libutf/runestrncpy.c.


































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
 * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */
#include <stdarg.h>
#include <string.h>
#include "plan9.h"
#include "utf.h"

Rune*
runestrncpy(Rune *s1, const Rune *s2, long n)
{
	int i;
	Rune *os1;

	os1 = s1;
	for(i = 0; i < n; i++)
		if((*s1++ = *s2++) == 0) {
			while(++i < n)
				*s1++ = 0;
			return os1;
		}
	return os1;
}
Added lib/libutf/runestrrchr.c.




























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
 * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */
#include <stdarg.h>
#include <string.h>
#include "plan9.h"
#include "utf.h"

Rune*
runestrrchr(const Rune *s, Rune c)
{
	const Rune *r;

	if(c == 0)
		return runestrchr(s, 0);
	r = 0;
	while(s = runestrchr(s, c))
		r = s++;
	return (Rune*)r;
}
Added lib/libutf/runestrstr.c.


























































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
 * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */
#include <stdarg.h>
#include <string.h>
#include "plan9.h"
#include "utf.h"

/*
 * Return pointer to first occurrence of s2 in s1,
 * 0 if none
 */
Rune*
runestrstr(const Rune *s1, const Rune *s2)
{
	const Rune *pa, *pb;
	Rune *p;
	int c0, c;

	c0 = *s2;
	if(c0 == 0)
		return (Rune*)s1;
	s2++;
	for(p=runestrchr(s1, c0); p; p=runestrchr(p+1, c0)) {
		pa = p;
		for(pb=s2;; pb++) {
			c = *pb;
			if(c == 0)
				return p;
			if(c != *++pa)
				break;
		}
	}
	return 0;
}
Added lib/libutf/runetype.c.






























































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
 * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */
#include <stdarg.h>
#include <string.h>
#include "plan9.h"
#include "utf.h"

/*
 * alpha ranges -
 *	only covers ranges not in lower||upper
 */
static
Rune	__alpha2[] =
{
	0x00d8,	0x00f6,	/* Ø - ö */
	0x00f8,	0x01f5,	/* ø - ǵ */
	0x0250,	0x02a8,	/* ɐ - ʨ */
	0x038e,	0x03a1,	/* Ύ - Ρ */
	0x03a3,	0x03ce,	/* Σ - ώ */
	0x03d0,	0x03d6,	/* ϐ - ϖ */
	0x03e2,	0x03f3,	/* Ϣ - ϳ */
	0x0490,	0x04c4,	/* Ґ - ӄ */
	0x0561,	0x0587,	/* ա - և */
	0x05d0,	0x05ea,	/* א - ת */
	0x05f0,	0x05f2,	/* װ - ײ */
	0x0621,	0x063a,	/* ء - غ */
	0x0640,	0x064a,	/* ـ - ي */
	0x0671,	0x06b7,	/* ٱ - ڷ */
	0x06ba,	0x06be,	/* ں - ھ */
	0x06c0,	0x06ce,	/* ۀ - ێ */
	0x06d0,	0x06d3,	/* ې - ۓ */
	0x0905,	0x0939,	/* अ - ह */
	0x0958,	0x0961,	/* क़ - ॡ */
	0x0985,	0x098c,	/* অ - ঌ */
	0x098f,	0x0990,	/* এ - ঐ */
	0x0993,	0x09a8,	/* ও - ন */
	0x09aa,	0x09b0,	/* প - র */
	0x09b6,	0x09b9,	/* শ - হ */
	0x09dc,	0x09dd,	/* ড় - ঢ় */
	0x09df,	0x09e1,	/* য় - ৡ */
	0x09f0,	0x09f1,	/* ৰ - ৱ */
	0x0a05,	0x0a0a,	/* ਅ - ਊ */
	0x0a0f,	0x0a10,	/* ਏ - ਐ */
	0x0a13,	0x0a28,	/* ਓ - ਨ */
	0x0a2a,	0x0a30,	/* ਪ - ਰ */
	0x0a32,	0x0a33,	/* ਲ - ਲ਼ */
	0x0a35,	0x0a36,	/* ਵ - ਸ਼ */
	0x0a38,	0x0a39,	/* ਸ - ਹ */
	0x0a59,	0x0a5c,	/* ਖ਼ - ੜ */
	0x0a85,	0x0a8b,	/* અ - ઋ */
	0x0a8f,	0x0a91,	/* એ - ઑ */
	0x0a93,	0x0aa8,	/* ઓ - ન */
	0x0aaa,	0x0ab0,	/* પ - ર */
	0x0ab2,	0x0ab3,	/* લ - ળ */
	0x0ab5,	0x0ab9,	/* વ - હ */
	0x0b05,	0x0b0c,	/* ଅ - ଌ */
	0x0b0f,	0x0b10,	/* ଏ - ଐ */
	0x0b13,	0x0b28,	/* ଓ - ନ */
	0x0b2a,	0x0b30,	/* ପ - ର */
	0x0b32,	0x0b33,	/* ଲ - ଳ */
	0x0b36,	0x0b39,	/* ଶ - ହ */
	0x0b5c,	0x0b5d,	/* ଡ଼ - ଢ଼ */
	0x0b5f,	0x0b61,	/* ୟ - ୡ */
	0x0b85,	0x0b8a,	/* அ - ஊ */
	0x0b8e,	0x0b90,	/* எ - ஐ */
	0x0b92,	0x0b95,	/* ஒ - க */
	0x0b99,	0x0b9a,	/* ங - ச */
	0x0b9e,	0x0b9f,	/* ஞ - ட */
	0x0ba3,	0x0ba4,	/* ண - த */
	0x0ba8,	0x0baa,	/* ந - ப */
	0x0bae,	0x0bb5,	/* ம - வ */
	0x0bb7,	0x0bb9,	/* ஷ - ஹ */
	0x0c05,	0x0c0c,	/* అ - ఌ */
	0x0c0e,	0x0c10,	/* ఎ - ఐ */
	0x0c12,	0x0c28,	/* ఒ - న */
	0x0c2a,	0x0c33,	/* ప - ళ */
	0x0c35,	0x0c39,	/* వ - హ */
	0x0c60,	0x0c61,	/* ౠ - ౡ */
	0x0c85,	0x0c8c,	/* ಅ - ಌ */
	0x0c8e,	0x0c90,	/* ಎ - ಐ */
	0x0c92,	0x0ca8,	/* ಒ - ನ */
	0x0caa,	0x0cb3,	/* ಪ - ಳ */
	0x0cb5,	0x0cb9,	/* ವ - ಹ */
	0x0ce0,	0x0ce1,	/* ೠ - ೡ */
	0x0d05,	0x0d0c,	/* അ - ഌ */
	0x0d0e,	0x0d10,	/* എ - ഐ */
	0x0d12,	0x0d28,	/* ഒ - ന */
	0x0d2a,	0x0d39,	/* പ - ഹ */
	0x0d60,	0x0d61,	/* ൠ - ൡ */
	0x0e01,	0x0e30,	/* ก - ะ */
	0x0e32,	0x0e33,	/* า - ำ */
	0x0e40,	0x0e46,	/* เ - ๆ */
	0x0e5a,	0x0e5b,	/* ๚ - ๛ */
	0x0e81,	0x0e82,	/* ກ - ຂ */
	0x0e87,	0x0e88,	/* ງ - ຈ */
	0x0e94,	0x0e97,	/* ດ - ທ */
	0x0e99,	0x0e9f,	/* ນ - ຟ */
	0x0ea1,	0x0ea3,	/* ມ - ຣ */
	0x0eaa,	0x0eab,	/* ສ - ຫ */
	0x0ead,	0x0eae,	/* ອ - ຮ */
	0x0eb2,	0x0eb3,	/* າ - ຳ */
	0x0ec0,	0x0ec4,	/* ເ - ໄ */
	0x0edc,	0x0edd,	/* ໜ - ໝ */
	0x0f18,	0x0f19,	/* ༘ - ༙ */
	0x0f40,	0x0f47,	/* ཀ - ཇ */
	0x0f49,	0x0f69,	/* ཉ - ཀྵ */
	0x10d0,	0x10f6,	/* ა - ჶ */
	0x1100,	0x1159,	/* ᄀ - ᅙ */
	0x115f,	0x11a2,	/* ᅟ - ᆢ */
	0x11a8,	0x11f9,	/* ᆨ - ᇹ */
	0x1e00,	0x1e9b,	/* Ḁ - ẛ */
	0x1f50,	0x1f57,	/* ὐ - ὗ */
	0x1f80,	0x1fb4,	/* ᾀ - ᾴ */
	0x1fb6,	0x1fbc,	/* ᾶ - ᾼ */
	0x1fc2,	0x1fc4,	/* ῂ - ῄ */
	0x1fc6,	0x1fcc,	/* ῆ - ῌ */
	0x1fd0,	0x1fd3,	/* ῐ - ΐ */
	0x1fd6,	0x1fdb,	/* ῖ - Ί */
	0x1fe0,	0x1fec,	/* ῠ - Ῥ */
	0x1ff2,	0x1ff4,	/* ῲ - ῴ */
	0x1ff6,	0x1ffc,	/* ῶ - ῼ */
	0x210a,	0x2113,	/* ℊ - ℓ */
	0x2115,	0x211d,	/* ℕ - ℝ */
	0x2120,	0x2122,	/* ℠ - ™ */
	0x212a,	0x2131,	/* K - ℱ */
	0x2133,	0x2138,	/* ℳ - ℸ */
	0x3041,	0x3094,	/* ぁ - ゔ */
	0x30a1,	0x30fa,	/* ァ - ヺ */
	0x3105,	0x312c,	/* ㄅ - ㄬ */
	0x3131,	0x318e,	/* ㄱ - ㆎ */
	0x3192,	0x319f,	/* ㆒ - ㆟ */
	0x3260,	0x327b,	/* ㉠ - ㉻ */
	0x328a,	0x32b0,	/* ㊊ - ㊰ */
	0x32d0,	0x32fe,	/* ㋐ - ㋾ */
	0x3300,	0x3357,	/* ㌀ - ㍗ */
	0x3371,	0x3376,	/* ㍱ - ㍶ */
	0x337b,	0x3394,	/* ㍻ - ㎔ */
	0x3399,	0x339e,	/* ㎙ - ㎞ */
	0x33a9,	0x33ad,	/* ㎩ - ㎭ */
	0x33b0,	0x33c1,	/* ㎰ - ㏁ */
	0x33c3,	0x33c5,	/* ㏃ - ㏅ */
	0x33c7,	0x33d7,	/* ㏇ - ㏗ */
	0x33d9,	0x33dd,	/* ㏙ - ㏝ */
	0x4e00,	0x9fff,	/* 一 - 鿿 */
	0xac00,	0xd7a3,	/* 가 - 힣 */
	0xf900,	0xfb06,	/* 豈 - st */
	0xfb13,	0xfb17,	/* ﬓ - ﬗ */
	0xfb1f,	0xfb28,	/* ײַ - ﬨ */
	0xfb2a,	0xfb36,	/* שׁ - זּ */
	0xfb38,	0xfb3c,	/* טּ - לּ */
	0xfb40,	0xfb41,	/* נּ - סּ */
	0xfb43,	0xfb44,	/* ףּ - פּ */
	0xfb46,	0xfbb1,	/* צּ - ﮱ */
	0xfbd3,	0xfd3d,	/* ﯓ - ﴽ */
	0xfd50,	0xfd8f,	/* ﵐ - ﶏ */
	0xfd92,	0xfdc7,	/* ﶒ - ﷇ */
	0xfdf0,	0xfdf9,	/* ﷰ - ﷹ */
	0xfe70,	0xfe72,	/* ﹰ - ﹲ */
	0xfe76,	0xfefc,	/* ﹶ - ﻼ */
	0xff66,	0xff6f,	/* ヲ - ッ */
	0xff71,	0xff9d,	/* ア - ン */
	0xffa0,	0xffbe,	/* ᅠ - ᄒ */
	0xffc2,	0xffc7,	/* ᅡ - ᅦ */
	0xffca,	0xffcf,	/* ᅧ - ᅬ */
	0xffd2,	0xffd7,	/* ᅭ - ᅲ */
	0xffda,	0xffdc,	/* ᅳ - ᅵ */
};

/*
 * alpha singlets -
 *	only covers ranges not in lower||upper
 */
static
Rune	__alpha1[] =
{
	0x00aa,	/* ª */
	0x00b5,	/* µ */
	0x00ba,	/* º */
	0x03da,	/* Ϛ */
	0x03dc,	/* Ϝ */
	0x03de,	/* Ϟ */
	0x03e0,	/* Ϡ */
	0x06d5,	/* ە */
	0x09b2,	/* ল */
	0x0a5e,	/* ਫ਼ */
	0x0a8d,	/* ઍ */
	0x0ae0,	/* ૠ */
	0x0b9c,	/* ஜ */
	0x0cde,	/* ೞ */
	0x0e4f,	/* ๏ */
	0x0e84,	/* ຄ */
	0x0e8a,	/* ຊ */
	0x0e8d,	/* ຍ */
	0x0ea5,	/* ລ */
	0x0ea7,	/* ວ */
	0x0eb0,	/* ະ */
	0x0ebd,	/* ຽ */
	0x1fbe,	/* ι */
	0x207f,	/* ⁿ */
	0x20a8,	/* ₨ */
	0x2102,	/* ℂ */
	0x2107,	/* ℇ */
	0x2124,	/* ℤ */
	0x2126,	/* Ω */
	0x2128,	/* ℨ */
	0xfb3e,	/* מּ */
	0xfe74,	/* ﹴ */
};

/*
 * space ranges
 */
static
Rune	__space2[] =
{
	0x0009,	0x000a,	/* tab and newline */
	0x0020,	0x0020,	/* space */
	0x00a0,	0x00a0,	/*   */
	0x2000,	0x200b,	/*   - ​ */
	0x2028,	0x2029,	/* 
 - 
 */
	0x3000,	0x3000,	/*   */
	0xfeff,	0xfeff,	/*  */
};

/*
 * lower case ranges
 *	3rd col is conversion excess 500
 */
static
Rune	__toupper2[] =
{
	0x0061,	0x007a, 468,	/* a-z A-Z */
	0x00e0,	0x00f6, 468,	/* à-ö À-Ö */
	0x00f8,	0x00fe, 468,	/* ø-þ Ø-Þ */
	0x0256,	0x0257, 295,	/* ɖ-ɗ Ɖ-Ɗ */
	0x0258,	0x0259, 298,	/* ɘ-ə Ǝ-Ə */
	0x028a,	0x028b, 283,	/* ʊ-ʋ Ʊ-Ʋ */
	0x03ad,	0x03af, 463,	/* έ-ί Έ-Ί */
	0x03b1,	0x03c1, 468,	/* α-ρ Α-Ρ */
	0x03c3,	0x03cb, 468,	/* σ-ϋ Σ-Ϋ */
	0x03cd,	0x03ce, 437,	/* ύ-ώ Ύ-Ώ */
	0x0430,	0x044f, 468,	/* а-я А-Я */
	0x0451,	0x045c, 420,	/* ё-ќ Ё-Ќ */
	0x045e,	0x045f, 420,	/* ў-џ Ў-Џ */
	0x0561,	0x0586, 452,	/* ա-ֆ Ա-Ֆ */
	0x1f00,	0x1f07, 508,	/* ἀ-ἇ Ἀ-Ἇ */
	0x1f10,	0x1f15, 508,	/* ἐ-ἕ Ἐ-Ἕ */
	0x1f20,	0x1f27, 508,	/* ἠ-ἧ Ἠ-Ἧ */
	0x1f30,	0x1f37, 508,	/* ἰ-ἷ Ἰ-Ἷ */
	0x1f40,	0x1f45, 508,	/* ὀ-ὅ Ὀ-Ὅ */
	0x1f60,	0x1f67, 508,	/* ὠ-ὧ Ὠ-Ὧ */
	0x1f70,	0x1f71, 574,	/* ὰ-ά Ὰ-Ά */
	0x1f72,	0x1f75, 586,	/* ὲ-ή Ὲ-Ή */
	0x1f76,	0x1f77, 600,	/* ὶ-ί Ὶ-Ί */
	0x1f78,	0x1f79, 628,	/* ὸ-ό Ὸ-Ό */
	0x1f7a,	0x1f7b, 612,	/* ὺ-ύ Ὺ-Ύ */
	0x1f7c,	0x1f7d, 626,	/* ὼ-ώ Ὼ-Ώ */
	0x1f80,	0x1f87, 508,	/* ᾀ-ᾇ ᾈ-ᾏ */
	0x1f90,	0x1f97, 508,	/* ᾐ-ᾗ ᾘ-ᾟ */
	0x1fa0,	0x1fa7, 508,	/* ᾠ-ᾧ ᾨ-ᾯ */
	0x1fb0,	0x1fb1, 508,	/* ᾰ-ᾱ Ᾰ-Ᾱ */
	0x1fd0,	0x1fd1, 508,	/* ῐ-ῑ Ῐ-Ῑ */
	0x1fe0,	0x1fe1, 508,	/* ῠ-ῡ Ῠ-Ῡ */
	0x2170,	0x217f, 484,	/* ⅰ-ⅿ Ⅰ-Ⅿ */
	0x24d0,	0x24e9, 474,	/* ⓐ-ⓩ Ⓐ-Ⓩ */
	0xff41,	0xff5a, 468,	/* a-z A-Z */
};

/*
 * lower case singlets
 *	2nd col is conversion excess 500
 */
static
Rune	__toupper1[] =
{
	0x00ff, 621,	/* ÿ Ÿ */
	0x0101, 499,	/* ā Ā */
	0x0103, 499,	/* ă Ă */
	0x0105, 499,	/* ą Ą */
	0x0107, 499,	/* ć Ć */
	0x0109, 499,	/* ĉ Ĉ */
	0x010b, 499,	/* ċ Ċ */
	0x010d, 499,	/* č Č */
	0x010f, 499,	/* ď Ď */
	0x0111, 499,	/* đ Đ */
	0x0113, 499,	/* ē Ē */
	0x0115, 499,	/* ĕ Ĕ */
	0x0117, 499,	/* ė Ė */
	0x0119, 499,	/* ę Ę */
	0x011b, 499,	/* ě Ě */
	0x011d, 499,	/* ĝ Ĝ */
	0x011f, 499,	/* ğ Ğ */
	0x0121, 499,	/* ġ Ġ */
	0x0123, 499,	/* ģ Ģ */
	0x0125, 499,	/* ĥ Ĥ */
	0x0127, 499,	/* ħ Ħ */
	0x0129, 499,	/* ĩ Ĩ */
	0x012b, 499,	/* ī Ī */
	0x012d, 499,	/* ĭ Ĭ */
	0x012f, 499,	/* į Į */
	0x0131, 268,	/* ı I */
	0x0133, 499,	/* ij IJ */
	0x0135, 499,	/* ĵ Ĵ */
	0x0137, 499,	/* ķ Ķ */
	0x013a, 499,	/* ĺ Ĺ */
	0x013c, 499,	/* ļ Ļ */
	0x013e, 499,	/* ľ Ľ */
	0x0140, 499,	/* ŀ Ŀ */
	0x0142, 499,	/* ł Ł */
	0x0144, 499,	/* ń Ń */
	0x0146, 499,	/* ņ Ņ */
	0x0148, 499,	/* ň Ň */
	0x014b, 499,	/* ŋ Ŋ */
	0x014d, 499,	/* ō Ō */
	0x014f, 499,	/* ŏ Ŏ */
	0x0151, 499,	/* ő Ő */
	0x0153, 499,	/* œ Œ */
	0x0155, 499,	/* ŕ Ŕ */
	0x0157, 499,	/* ŗ Ŗ */
	0x0159, 499,	/* ř Ř */
	0x015b, 499,	/* ś Ś */
	0x015d, 499,	/* ŝ Ŝ */
	0x015f, 499,	/* ş Ş */
	0x0161, 499,	/* š Š */
	0x0163, 499,	/* ţ Ţ */
	0x0165, 499,	/* ť Ť */
	0x0167, 499,	/* ŧ Ŧ */
	0x0169, 499,	/* ũ Ũ */
	0x016b, 499,	/* ū Ū */
	0x016d, 499,	/* ŭ Ŭ */
	0x016f, 499,	/* ů Ů */
	0x0171, 499,	/* ű Ű */
	0x0173, 499,	/* ų Ų */
	0x0175, 499,	/* ŵ Ŵ */
	0x0177, 499,	/* ŷ Ŷ */
	0x017a, 499,	/* ź Ź */
	0x017c, 499,	/* ż Ż */
	0x017e, 499,	/* ž Ž */
	0x017f, 200,	/* ſ S */
	0x0183, 499,	/* ƃ Ƃ */
	0x0185, 499,	/* ƅ Ƅ */
	0x0188, 499,	/* ƈ Ƈ */
	0x018c, 499,	/* ƌ Ƌ */
	0x0192, 499,	/* ƒ Ƒ */
	0x0199, 499,	/* ƙ Ƙ */
	0x01a1, 499,	/* ơ Ơ */
	0x01a3, 499,	/* ƣ Ƣ */
	0x01a5, 499,	/* ƥ Ƥ */
	0x01a8, 499,	/* ƨ Ƨ */
	0x01ad, 499,	/* ƭ Ƭ */
	0x01b0, 499,	/* ư Ư */
	0x01b4, 499,	/* ƴ Ƴ */
	0x01b6, 499,	/* ƶ Ƶ */
	0x01b9, 499,	/* ƹ Ƹ */
	0x01bd, 499,	/* ƽ Ƽ */
	0x01c5, 499,	/* Dž DŽ */
	0x01c6, 498,	/* dž DŽ */
	0x01c8, 499,	/* Lj LJ */
	0x01c9, 498,	/* lj LJ */
	0x01cb, 499,	/* Nj NJ */
	0x01cc, 498,	/* nj NJ */
	0x01ce, 499,	/* ǎ Ǎ */
	0x01d0, 499,	/* ǐ Ǐ */
	0x01d2, 499,	/* ǒ Ǒ */
	0x01d4, 499,	/* ǔ Ǔ */
	0x01d6, 499,	/* ǖ Ǖ */
	0x01d8, 499,	/* ǘ Ǘ */
	0x01da, 499,	/* ǚ Ǚ */
	0x01dc, 499,	/* ǜ Ǜ */
	0x01df, 499,	/* ǟ Ǟ */
	0x01e1, 499,	/* ǡ Ǡ */
	0x01e3, 499,	/* ǣ Ǣ */
	0x01e5, 499,	/* ǥ Ǥ */
	0x01e7, 499,	/* ǧ Ǧ */
	0x01e9, 499,	/* ǩ Ǩ */
	0x01eb, 499,	/* ǫ Ǫ */
	0x01ed, 499,	/* ǭ Ǭ */
	0x01ef, 499,	/* ǯ Ǯ */
	0x01f2, 499,	/* Dz DZ */
	0x01f3, 498,	/* dz DZ */
	0x01f5, 499,	/* ǵ Ǵ */
	0x01fb, 499,	/* ǻ Ǻ */
	0x01fd, 499,	/* ǽ Ǽ */
	0x01ff, 499,	/* ǿ Ǿ */
	0x0201, 499,	/* ȁ Ȁ */
	0x0203, 499,	/* ȃ Ȃ */
	0x0205, 499,	/* ȅ Ȅ */
	0x0207, 499,	/* ȇ Ȇ */
	0x0209, 499,	/* ȉ Ȉ */
	0x020b, 499,	/* ȋ Ȋ */
	0x020d, 499,	/* ȍ Ȍ */
	0x020f, 499,	/* ȏ Ȏ */
	0x0211, 499,	/* ȑ Ȑ */
	0x0213, 499,	/* ȓ Ȓ */
	0x0215, 499,	/* ȕ Ȕ */
	0x0217, 499,	/* ȗ Ȗ */
	0x0253, 290,	/* ɓ Ɓ */
	0x0254, 294,	/* ɔ Ɔ */
	0x025b, 297,	/* ɛ Ɛ */
	0x0260, 295,	/* ɠ Ɠ */
	0x0263, 293,	/* ɣ Ɣ */
	0x0268, 291,	/* ɨ Ɨ */
	0x0269, 289,	/* ɩ Ɩ */
	0x026f, 289,	/* ɯ Ɯ */
	0x0272, 287,	/* ɲ Ɲ */
	0x0283, 282,	/* ʃ Ʃ */
	0x0288, 282,	/* ʈ Ʈ */
	0x0292, 281,	/* ʒ Ʒ */
	0x03ac, 462,	/* ά Ά */
	0x03cc, 436,	/* ό Ό */
	0x03d0, 438,	/* ϐ Β */
	0x03d1, 443,	/* ϑ Θ */
	0x03d5, 453,	/* ϕ Φ */
	0x03d6, 446,	/* ϖ Π */
	0x03e3, 499,	/* ϣ Ϣ */
	0x03e5, 499,	/* ϥ Ϥ */
	0x03e7, 499,	/* ϧ Ϧ */
	0x03e9, 499,	/* ϩ Ϩ */
	0x03eb, 499,	/* ϫ Ϫ */
	0x03ed, 499,	/* ϭ Ϭ */
	0x03ef, 499,	/* ϯ Ϯ */
	0x03f0, 414,	/* ϰ Κ */
	0x03f1, 420,	/* ϱ Ρ */
	0x0461, 499,	/* ѡ Ѡ */
	0x0463, 499,	/* ѣ Ѣ */
	0x0465, 499,	/* ѥ Ѥ */
	0x0467, 499,	/* ѧ Ѧ */
	0x0469, 499,	/* ѩ Ѩ */
	0x046b, 499,	/* ѫ Ѫ */
	0x046d, 499,	/* ѭ Ѭ */
	0x046f, 499,	/* ѯ Ѯ */
	0x0471, 499,	/* ѱ Ѱ */
	0x0473, 499,	/* ѳ Ѳ */
	0x0475, 499,	/* ѵ Ѵ */
	0x0477, 499,	/* ѷ Ѷ */
	0x0479, 499,	/* ѹ Ѹ */
	0x047b, 499,	/* ѻ Ѻ */
	0x047d, 499,	/* ѽ Ѽ */
	0x047f, 499,	/* ѿ Ѿ */
	0x0481, 499,	/* ҁ Ҁ */
	0x0491, 499,	/* ґ Ґ */
	0x0493, 499,	/* ғ Ғ */
	0x0495, 499,	/* ҕ Ҕ */
	0x0497, 499,	/* җ Җ */
	0x0499, 499,	/* ҙ Ҙ */
	0x049b, 499,	/* қ Қ */
	0x049d, 499,	/* ҝ Ҝ */
	0x049f, 499,	/* ҟ Ҟ */
	0x04a1, 499,	/* ҡ Ҡ */
	0x04a3, 499,	/* ң Ң */
	0x04a5, 499,	/* ҥ Ҥ */
	0x04a7, 499,	/* ҧ Ҧ */
	0x04a9, 499,	/* ҩ Ҩ */
	0x04ab, 499,	/* ҫ Ҫ */
	0x04ad, 499,	/* ҭ Ҭ */
	0x04af, 499,	/* ү Ү */
	0x04b1, 499,	/* ұ Ұ */
	0x04b3, 499,	/* ҳ Ҳ */
	0x04b5, 499,	/* ҵ Ҵ */
	0x04b7, 499,	/* ҷ Ҷ */
	0x04b9, 499,	/* ҹ Ҹ */
	0x04bb, 499,	/* һ Һ */
	0x04bd, 499,	/* ҽ Ҽ */
	0x04bf, 499,	/* ҿ Ҿ */
	0x04c2, 499,	/* ӂ Ӂ */
	0x04c4, 499,	/* ӄ Ӄ */
	0x04c8, 499,	/* ӈ Ӈ */
	0x04cc, 499,	/* ӌ Ӌ */
	0x04d1, 499,	/* ӑ Ӑ */
	0x04d3, 499,	/* ӓ Ӓ */
	0x04d5, 499,	/* ӕ Ӕ */
	0x04d7, 499,	/* ӗ Ӗ */
	0x04d9, 499,	/* ә Ә */
	0x04db, 499,	/* ӛ Ӛ */
	0x04dd, 499,	/* ӝ Ӝ */
	0x04df, 499,	/* ӟ Ӟ */
	0x04e1, 499,	/* ӡ Ӡ */
	0x04e3, 499,	/* ӣ Ӣ */
	0x04e5, 499,	/* ӥ Ӥ */
	0x04e7, 499,	/* ӧ Ӧ */
	0x04e9, 499,	/* ө Ө */
	0x04eb, 499,	/* ӫ Ӫ */
	0x04ef, 499,	/* ӯ Ӯ */
	0x04f1, 499,	/* ӱ Ӱ */
	0x04f3, 499,	/* ӳ Ӳ */
	0x04f5, 499,	/* ӵ Ӵ */
	0x04f9, 499,	/* ӹ Ӹ */
	0x1e01, 499,	/* ḁ Ḁ */
	0x1e03, 499,	/* ḃ Ḃ */
	0x1e05, 499,	/* ḅ Ḅ */
	0x1e07, 499,	/* ḇ Ḇ */
	0x1e09, 499,	/* ḉ Ḉ */
	0x1e0b, 499,	/* ḋ Ḋ */
	0x1e0d, 499,	/* ḍ Ḍ */
	0x1e0f, 499,	/* ḏ Ḏ */
	0x1e11, 499,	/* ḑ Ḑ */
	0x1e13, 499,	/* ḓ Ḓ */
	0x1e15, 499,	/* ḕ Ḕ */
	0x1e17, 499,	/* ḗ Ḗ */
	0x1e19, 499,	/* ḙ Ḙ */
	0x1e1b, 499,	/* ḛ Ḛ */
	0x1e1d, 499,	/* ḝ Ḝ */
	0x1e1f, 499,	/* ḟ Ḟ */
	0x1e21, 499,	/* ḡ Ḡ */
	0x1e23, 499,	/* ḣ Ḣ */
	0x1e25, 499,	/* ḥ Ḥ */
	0x1e27, 499,	/* ḧ Ḧ */
	0x1e29, 499,	/* ḩ Ḩ */
	0x1e2b, 499,	/* ḫ Ḫ */
	0x1e2d, 499,	/* ḭ Ḭ */
	0x1e2f, 499,	/* ḯ Ḯ */
	0x1e31, 499,	/* ḱ Ḱ */
	0x1e33, 499,	/* ḳ Ḳ */
	0x1e35, 499,	/* ḵ Ḵ */
	0x1e37, 499,	/* ḷ Ḷ */
	0x1e39, 499,	/* ḹ Ḹ */
	0x1e3b, 499,	/* ḻ Ḻ */
	0x1e3d, 499,	/* ḽ Ḽ */
	0x1e3f, 499,	/* ḿ Ḿ */
	0x1e41, 499,	/* ṁ Ṁ */
	0x1e43, 499,	/* ṃ Ṃ */
	0x1e45, 499,	/* ṅ Ṅ */
	0x1e47, 499,	/* ṇ Ṇ */
	0x1e49, 499,	/* ṉ Ṉ */
	0x1e4b, 499,	/* ṋ Ṋ */
	0x1e4d, 499,	/* ṍ Ṍ */
	0x1e4f, 499,	/* ṏ Ṏ */
	0x1e51, 499,	/* ṑ Ṑ */
	0x1e53, 499,	/* ṓ Ṓ */
	0x1e55, 499,	/* ṕ Ṕ */
	0x1e57, 499,	/* ṗ Ṗ */
	0x1e59, 499,	/* ṙ Ṙ */
	0x1e5b, 499,	/* ṛ Ṛ */
	0x1e5d, 499,	/* ṝ Ṝ */
	0x1e5f, 499,	/* ṟ Ṟ */
	0x1e61, 499,	/* ṡ Ṡ */
	0x1e63, 499,	/* ṣ Ṣ */
	0x1e65, 499,	/* ṥ Ṥ */
	0x1e67, 499,	/* ṧ Ṧ */
	0x1e69, 499,	/* ṩ Ṩ */
	0x1e6b, 499,	/* ṫ Ṫ */
	0x1e6d, 499,	/* ṭ Ṭ */
	0x1e6f, 499,	/* ṯ Ṯ */
	0x1e71, 499,	/* ṱ Ṱ */
	0x1e73, 499,	/* ṳ Ṳ */
	0x1e75, 499,	/* ṵ Ṵ */
	0x1e77, 499,	/* ṷ Ṷ */
	0x1e79, 499,	/* ṹ Ṹ */
	0x1e7b, 499,	/* ṻ Ṻ */
	0x1e7d, 499,	/* ṽ Ṽ */
	0x1e7f, 499,	/* ṿ Ṿ */
	0x1e81, 499,	/* ẁ Ẁ */
	0x1e83, 499,	/* ẃ Ẃ */
	0x1e85, 499,	/* ẅ Ẅ */
	0x1e87, 499,	/* ẇ Ẇ */
	0x1e89, 499,	/* ẉ Ẉ */
	0x1e8b, 499,	/* ẋ Ẋ */
	0x1e8d, 499,	/* ẍ Ẍ */
	0x1e8f, 499,	/* ẏ Ẏ */
	0x1e91, 499,	/* ẑ Ẑ */
	0x1e93, 499,	/* ẓ Ẓ */
	0x1e95, 499,	/* ẕ Ẕ */
	0x1ea1, 499,	/* ạ Ạ */
	0x1ea3, 499,	/* ả Ả */
	0x1ea5, 499,	/* ấ Ấ */
	0x1ea7, 499,	/* ầ Ầ */
	0x1ea9, 499,	/* ẩ Ẩ */
	0x1eab, 499,	/* ẫ Ẫ */
	0x1ead, 499,	/* ậ Ậ */
	0x1eaf, 499,	/* ắ Ắ */
	0x1eb1, 499,	/* ằ Ằ */
	0x1eb3, 499,	/* ẳ Ẳ */
	0x1eb5, 499,	/* ẵ Ẵ */
	0x1eb7, 499,	/* ặ Ặ */
	0x1eb9, 499,	/* ẹ Ẹ */
	0x1ebb, 499,	/* ẻ Ẻ */
	0x1ebd, 499,	/* ẽ Ẽ */
	0x1ebf, 499,	/* ế Ế */
	0x1ec1, 499,	/* ề Ề */
	0x1ec3, 499,	/* ể Ể */
	0x1ec5, 499,	/* ễ Ễ */
	0x1ec7, 499,	/* ệ Ệ */
	0x1ec9, 499,	/* ỉ Ỉ */
	0x1ecb, 499,	/* ị Ị */
	0x1ecd, 499,	/* ọ Ọ */
	0x1ecf, 499,	/* ỏ Ỏ */
	0x1ed1, 499,	/* ố Ố */
	0x1ed3, 499,	/* ồ Ồ */
	0x1ed5, 499,	/* ổ Ổ */
	0x1ed7, 499,	/* ỗ Ỗ */
	0x1ed9, 499,	/* ộ Ộ */
	0x1edb, 499,	/* ớ Ớ */
	0x1edd, 499,	/* ờ Ờ */
	0x1edf, 499,	/* ở Ở */
	0x1ee1, 499,	/* ỡ Ỡ */
	0x1ee3, 499,	/* ợ Ợ */
	0x1ee5, 499,	/* ụ Ụ */
	0x1ee7, 499,	/* ủ Ủ */
	0x1ee9, 499,	/* ứ Ứ */
	0x1eeb, 499,	/* ừ Ừ */
	0x1eed, 499,	/* ử Ử */
	0x1eef, 499,	/* ữ Ữ */
	0x1ef1, 499,	/* ự Ự */
	0x1ef3, 499,	/* ỳ Ỳ */
	0x1ef5, 499,	/* ỵ Ỵ */
	0x1ef7, 499,	/* ỷ Ỷ */
	0x1ef9, 499,	/* ỹ Ỹ */
	0x1f51, 508,	/* ὑ Ὑ */
	0x1f53, 508,	/* ὓ Ὓ */
	0x1f55, 508,	/* ὕ Ὕ */
	0x1f57, 508,	/* ὗ Ὗ */
	0x1fb3, 509,	/* ᾳ ᾼ */
	0x1fc3, 509,	/* ῃ ῌ */
	0x1fe5, 507,	/* ῥ Ῥ */
	0x1ff3, 509,	/* ῳ ῼ */
};

/*
 * upper case ranges
 *	3rd col is conversion excess 500
 */
static
Rune	__tolower2[] =
{
	0x0041,	0x005a, 532,	/* A-Z a-z */
	0x00c0,	0x00d6, 532,	/* À-Ö à-ö */
	0x00d8,	0x00de, 532,	/* Ø-Þ ø-þ */
	0x0189,	0x018a, 705,	/* Ɖ-Ɗ ɖ-ɗ */
	0x018e,	0x018f, 702,	/* Ǝ-Ə ɘ-ə */
	0x01b1,	0x01b2, 717,	/* Ʊ-Ʋ ʊ-ʋ */
	0x0388,	0x038a, 537,	/* Έ-Ί έ-ί */
	0x038e,	0x038f, 563,	/* Ύ-Ώ ύ-ώ */
	0x0391,	0x03a1, 532,	/* Α-Ρ α-ρ */
	0x03a3,	0x03ab, 532,	/* Σ-Ϋ σ-ϋ */
	0x0401,	0x040c, 580,	/* Ё-Ќ ё-ќ */
	0x040e,	0x040f, 580,	/* Ў-Џ ў-џ */
	0x0410,	0x042f, 532,	/* А-Я а-я */
	0x0531,	0x0556, 548,	/* Ա-Ֆ ա-ֆ */
	0x10a0,	0x10c5, 548,	/* Ⴀ-Ⴥ ა-ჵ */
	0x1f08,	0x1f0f, 492,	/* Ἀ-Ἇ ἀ-ἇ */
	0x1f18,	0x1f1d, 492,	/* Ἐ-Ἕ ἐ-ἕ */
	0x1f28,	0x1f2f, 492,	/* Ἠ-Ἧ ἠ-ἧ */
	0x1f38,	0x1f3f, 492,	/* Ἰ-Ἷ ἰ-ἷ */
	0x1f48,	0x1f4d, 492,	/* Ὀ-Ὅ ὀ-ὅ */
	0x1f68,	0x1f6f, 492,	/* Ὠ-Ὧ ὠ-ὧ */
	0x1f88,	0x1f8f, 492,	/* ᾈ-ᾏ ᾀ-ᾇ */
	0x1f98,	0x1f9f, 492,	/* ᾘ-ᾟ ᾐ-ᾗ */
	0x1fa8,	0x1faf, 492,	/* ᾨ-ᾯ ᾠ-ᾧ */
	0x1fb8,	0x1fb9, 492,	/* Ᾰ-Ᾱ ᾰ-ᾱ */
	0x1fba,	0x1fbb, 426,	/* Ὰ-Ά ὰ-ά */
	0x1fc8,	0x1fcb, 414,	/* Ὲ-Ή ὲ-ή */
	0x1fd8,	0x1fd9, 492,	/* Ῐ-Ῑ ῐ-ῑ */
	0x1fda,	0x1fdb, 400,	/* Ὶ-Ί ὶ-ί */
	0x1fe8,	0x1fe9, 492,	/* Ῠ-Ῡ ῠ-ῡ */
	0x1fea,	0x1feb, 388,	/* Ὺ-Ύ ὺ-ύ */
	0x1ff8,	0x1ff9, 372,	/* Ὸ-Ό ὸ-ό */
	0x1ffa,	0x1ffb, 374,	/* Ὼ-Ώ ὼ-ώ */
	0x2160,	0x216f, 516,	/* Ⅰ-Ⅿ ⅰ-ⅿ */
	0x24b6,	0x24cf, 526,	/* Ⓐ-Ⓩ ⓐ-ⓩ */
	0xff21,	0xff3a, 532,	/* A-Z a-z */
};

/*
 * upper case singlets
 *	2nd col is conversion excess 500
 */
static
Rune	__tolower1[] =
{
	0x0100, 501,	/* Ā ā */
	0x0102, 501,	/* Ă ă */
	0x0104, 501,	/* Ą ą */
	0x0106, 501,	/* Ć ć */
	0x0108, 501,	/* Ĉ ĉ */
	0x010a, 501,	/* Ċ ċ */
	0x010c, 501,	/* Č č */
	0x010e, 501,	/* Ď ď */
	0x0110, 501,	/* Đ đ */
	0x0112, 501,	/* Ē ē */
	0x0114, 501,	/* Ĕ ĕ */
	0x0116, 501,	/* Ė ė */
	0x0118, 501,	/* Ę ę */
	0x011a, 501,	/* Ě ě */
	0x011c, 501,	/* Ĝ ĝ */
	0x011e, 501,	/* Ğ ğ */
	0x0120, 501,	/* Ġ ġ */
	0x0122, 501,	/* Ģ ģ */
	0x0124, 501,	/* Ĥ ĥ */
	0x0126, 501,	/* Ħ ħ */
	0x0128, 501,	/* Ĩ ĩ */
	0x012a, 501,	/* Ī ī */
	0x012c, 501,	/* Ĭ ĭ */
	0x012e, 501,	/* Į į */
	0x0130, 301,	/* İ i */
	0x0132, 501,	/* IJ ij */
	0x0134, 501,	/* Ĵ ĵ */
	0x0136, 501,	/* Ķ ķ */
	0x0139, 501,	/* Ĺ ĺ */
	0x013b, 501,	/* Ļ ļ */
	0x013d, 501,	/* Ľ ľ */
	0x013f, 501,	/* Ŀ ŀ */
	0x0141, 501,	/* Ł ł */
	0x0143, 501,	/* Ń ń */
	0x0145, 501,	/* Ņ ņ */
	0x0147, 501,	/* Ň ň */
	0x014a, 501,	/* Ŋ ŋ */
	0x014c, 501,	/* Ō ō */
	0x014e, 501,	/* Ŏ ŏ */
	0x0150, 501,	/* Ő ő */
	0x0152, 501,	/* Œ œ */
	0x0154, 501,	/* Ŕ ŕ */
	0x0156, 501,	/* Ŗ ŗ */
	0x0158, 501,	/* Ř ř */
	0x015a, 501,	/* Ś ś */
	0x015c, 501,	/* Ŝ ŝ */
	0x015e, 501,	/* Ş ş */
	0x0160, 501,	/* Š š */
	0x0162, 501,	/* Ţ ţ */
	0x0164, 501,	/* Ť ť */
	0x0166, 501,	/* Ŧ ŧ */
	0x0168, 501,	/* Ũ ũ */
	0x016a, 501,	/* Ū ū */
	0x016c, 501,	/* Ŭ ŭ */
	0x016e, 501,	/* Ů ů */
	0x0170, 501,	/* Ű ű */
	0x0172, 501,	/* Ų ų */
	0x0174, 501,	/* Ŵ ŵ */
	0x0176, 501,	/* Ŷ ŷ */
	0x0178, 379,	/* Ÿ ÿ */
	0x0179, 501,	/* Ź ź */
	0x017b, 501,	/* Ż ż */
	0x017d, 501,	/* Ž ž */
	0x0181, 710,	/* Ɓ ɓ */
	0x0182, 501,	/* Ƃ ƃ */
	0x0184, 501,	/* Ƅ ƅ */
	0x0186, 706,	/* Ɔ ɔ */
	0x0187, 501,	/* Ƈ ƈ */
	0x018b, 501,	/* Ƌ ƌ */
	0x0190, 703,	/* Ɛ ɛ */
	0x0191, 501,	/* Ƒ ƒ */
	0x0193, 705,	/* Ɠ ɠ */
	0x0194, 707,	/* Ɣ ɣ */
	0x0196, 711,	/* Ɩ ɩ */
	0x0197, 709,	/* Ɨ ɨ */
	0x0198, 501,	/* Ƙ ƙ */
	0x019c, 711,	/* Ɯ ɯ */
	0x019d, 713,	/* Ɲ ɲ */
	0x01a0, 501,	/* Ơ ơ */
	0x01a2, 501,	/* Ƣ ƣ */
	0x01a4, 501,	/* Ƥ ƥ */
	0x01a7, 501,	/* Ƨ ƨ */
	0x01a9, 718,	/* Ʃ ʃ */
	0x01ac, 501,	/* Ƭ ƭ */
	0x01ae, 718,	/* Ʈ ʈ */
	0x01af, 501,	/* Ư ư */
	0x01b3, 501,	/* Ƴ ƴ */
	0x01b5, 501,	/* Ƶ ƶ */
	0x01b7, 719,	/* Ʒ ʒ */
	0x01b8, 501,	/* Ƹ ƹ */
	0x01bc, 501,	/* Ƽ ƽ */
	0x01c4, 502,	/* DŽ dž */
	0x01c5, 501,	/* Dž dž */
	0x01c7, 502,	/* LJ lj */
	0x01c8, 501,	/* Lj lj */
	0x01ca, 502,	/* NJ nj */
	0x01cb, 501,	/* Nj nj */
	0x01cd, 501,	/* Ǎ ǎ */
	0x01cf, 501,	/* Ǐ ǐ */
	0x01d1, 501,	/* Ǒ ǒ */
	0x01d3, 501,	/* Ǔ ǔ */
	0x01d5, 501,	/* Ǖ ǖ */
	0x01d7, 501,	/* Ǘ ǘ */
	0x01d9, 501,	/* Ǚ ǚ */
	0x01db, 501,	/* Ǜ ǜ */
	0x01de, 501,	/* Ǟ ǟ */
	0x01e0, 501,	/* Ǡ ǡ */
	0x01e2, 501,	/* Ǣ ǣ */
	0x01e4, 501,	/* Ǥ ǥ */
	0x01e6, 501,	/* Ǧ ǧ */
	0x01e8, 501,	/* Ǩ ǩ */
	0x01ea, 501,	/* Ǫ ǫ */
	0x01ec, 501,	/* Ǭ ǭ */
	0x01ee, 501,	/* Ǯ ǯ */
	0x01f1, 502,	/* DZ dz */
	0x01f2, 501,	/* Dz dz */
	0x01f4, 501,	/* Ǵ ǵ */
	0x01fa, 501,	/* Ǻ ǻ */
	0x01fc, 501,	/* Ǽ ǽ */
	0x01fe, 501,	/* Ǿ ǿ */
	0x0200, 501,	/* Ȁ ȁ */
	0x0202, 501,	/* Ȃ ȃ */
	0x0204, 501,	/* Ȅ ȅ */
	0x0206, 501,	/* Ȇ ȇ */
	0x0208, 501,	/* Ȉ ȉ */
	0x020a, 501,	/* Ȋ ȋ */
	0x020c, 501,	/* Ȍ ȍ */
	0x020e, 501,	/* Ȏ ȏ */
	0x0210, 501,	/* Ȑ ȑ */
	0x0212, 501,	/* Ȓ ȓ */
	0x0214, 501,	/* Ȕ ȕ */
	0x0216, 501,	/* Ȗ ȗ */
	0x0386, 538,	/* Ά ά */
	0x038c, 564,	/* Ό ό */
	0x03e2, 501,	/* Ϣ ϣ */
	0x03e4, 501,	/* Ϥ ϥ */
	0x03e6, 501,	/* Ϧ ϧ */
	0x03e8, 501,	/* Ϩ ϩ */
	0x03ea, 501,	/* Ϫ ϫ */
	0x03ec, 501,	/* Ϭ ϭ */
	0x03ee, 501,	/* Ϯ ϯ */
	0x0460, 501,	/* Ѡ ѡ */
	0x0462, 501,	/* Ѣ ѣ */
	0x0464, 501,	/* Ѥ ѥ */
	0x0466, 501,	/* Ѧ ѧ */
	0x0468, 501,	/* Ѩ ѩ */
	0x046a, 501,	/* Ѫ ѫ */
	0x046c, 501,	/* Ѭ ѭ */
	0x046e, 501,	/* Ѯ ѯ */
	0x0470, 501,	/* Ѱ ѱ */
	0x0472, 501,	/* Ѳ ѳ */
	0x0474, 501,	/* Ѵ ѵ */
	0x0476, 501,	/* Ѷ ѷ */
	0x0478, 501,	/* Ѹ ѹ */
	0x047a, 501,	/* Ѻ ѻ */
	0x047c, 501,	/* Ѽ ѽ */
	0x047e, 501,	/* Ѿ ѿ */
	0x0480, 501,	/* Ҁ ҁ */
	0x0490, 501,	/* Ґ ґ */
	0x0492, 501,	/* Ғ ғ */
	0x0494, 501,	/* Ҕ ҕ */
	0x0496, 501,	/* Җ җ */
	0x0498, 501,	/* Ҙ ҙ */
	0x049a, 501,	/* Қ қ */
	0x049c, 501,	/* Ҝ ҝ */
	0x049e, 501,	/* Ҟ ҟ */
	0x04a0, 501,	/* Ҡ ҡ */
	0x04a2, 501,	/* Ң ң */
	0x04a4, 501,	/* Ҥ ҥ */
	0x04a6, 501,	/* Ҧ ҧ */
	0x04a8, 501,	/* Ҩ ҩ */
	0x04aa, 501,	/* Ҫ ҫ */
	0x04ac, 501,	/* Ҭ ҭ */
	0x04ae, 501,	/* Ү ү */
	0x04b0, 501,	/* Ұ ұ */
	0x04b2, 501,	/* Ҳ ҳ */
	0x04b4, 501,	/* Ҵ ҵ */
	0x04b6, 501,	/* Ҷ ҷ */
	0x04b8, 501,	/* Ҹ ҹ */
	0x04ba, 501,	/* Һ һ */
	0x04bc, 501,	/* Ҽ ҽ */
	0x04be, 501,	/* Ҿ ҿ */
	0x04c1, 501,	/* Ӂ ӂ */
	0x04c3, 501,	/* Ӄ ӄ */
	0x04c7, 501,	/* Ӈ ӈ */
	0x04cb, 501,	/* Ӌ ӌ */
	0x04d0, 501,	/* Ӑ ӑ */
	0x04d2, 501,	/* Ӓ ӓ */
	0x04d4, 501,	/* Ӕ ӕ */
	0x04d6, 501,	/* Ӗ ӗ */
	0x04d8, 501,	/* Ә ә */
	0x04da, 501,	/* Ӛ ӛ */
	0x04dc, 501,	/* Ӝ ӝ */
	0x04de, 501,	/* Ӟ ӟ */
	0x04e0, 501,	/* Ӡ ӡ */
	0x04e2, 501,	/* Ӣ ӣ */
	0x04e4, 501,	/* Ӥ ӥ */
	0x04e6, 501,	/* Ӧ ӧ */
	0x04e8, 501,	/* Ө ө */
	0x04ea, 501,	/* Ӫ ӫ */
	0x04ee, 501,	/* Ӯ ӯ */
	0x04f0, 501,	/* Ӱ ӱ */
	0x04f2, 501,	/* Ӳ ӳ */
	0x04f4, 501,	/* Ӵ ӵ */
	0x04f8, 501,	/* Ӹ ӹ */
	0x1e00, 501,	/* Ḁ ḁ */
	0x1e02, 501,	/* Ḃ ḃ */
	0x1e04, 501,	/* Ḅ ḅ */
	0x1e06, 501,	/* Ḇ ḇ */
	0x1e08, 501,	/* Ḉ ḉ */
	0x1e0a, 501,	/* Ḋ ḋ */
	0x1e0c, 501,	/* Ḍ ḍ */
	0x1e0e, 501,	/* Ḏ ḏ */
	0x1e10, 501,	/* Ḑ ḑ */
	0x1e12, 501,	/* Ḓ ḓ */
	0x1e14, 501,	/* Ḕ ḕ */
	0x1e16, 501,	/* Ḗ ḗ */
	0x1e18, 501,	/* Ḙ ḙ */
	0x1e1a, 501,	/* Ḛ ḛ */
	0x1e1c, 501,	/* Ḝ ḝ */
	0x1e1e, 501,	/* Ḟ ḟ */
	0x1e20, 501,	/* Ḡ ḡ */
	0x1e22, 501,	/* Ḣ ḣ */
	0x1e24, 501,	/* Ḥ ḥ */
	0x1e26, 501,	/* Ḧ ḧ */
	0x1e28, 501,	/* Ḩ ḩ */
	0x1e2a, 501,	/* Ḫ ḫ */
	0x1e2c, 501,	/* Ḭ ḭ */
	0x1e2e, 501,	/* Ḯ ḯ */
	0x1e30, 501,	/* Ḱ ḱ */
	0x1e32, 501,	/* Ḳ ḳ */
	0x1e34, 501,	/* Ḵ ḵ */
	0x1e36, 501,	/* Ḷ ḷ */
	0x1e38, 501,	/* Ḹ ḹ */
	0x1e3a, 501,	/* Ḻ ḻ */
	0x1e3c, 501,	/* Ḽ ḽ */
	0x1e3e, 501,	/* Ḿ ḿ */
	0x1e40, 501,	/* Ṁ ṁ */
	0x1e42, 501,	/* Ṃ ṃ */
	0x1e44, 501,	/* Ṅ ṅ */
	0x1e46, 501,	/* Ṇ ṇ */
	0x1e48, 501,	/* Ṉ ṉ */
	0x1e4a, 501,	/* Ṋ ṋ */
	0x1e4c, 501,	/* Ṍ ṍ */
	0x1e4e, 501,	/* Ṏ ṏ */
	0x1e50, 501,	/* Ṑ ṑ */
	0x1e52, 501,	/* Ṓ ṓ */
	0x1e54, 501,	/* Ṕ ṕ */
	0x1e56, 501,	/* Ṗ ṗ */
	0x1e58, 501,	/* Ṙ ṙ */
	0x1e5a, 501,	/* Ṛ ṛ */
	0x1e5c, 501,	/* Ṝ ṝ */
	0x1e5e, 501,	/* Ṟ ṟ */
	0x1e60, 501,	/* Ṡ ṡ */
	0x1e62, 501,	/* Ṣ ṣ */
	0x1e64, 501,	/* Ṥ ṥ */
	0x1e66, 501,	/* Ṧ ṧ */
	0x1e68, 501,	/* Ṩ ṩ */
	0x1e6a, 501,	/* Ṫ ṫ */
	0x1e6c, 501,	/* Ṭ ṭ */
	0x1e6e, 501,	/* Ṯ ṯ */
	0x1e70, 501,	/* Ṱ ṱ */
	0x1e72, 501,	/* Ṳ ṳ */
	0x1e74, 501,	/* Ṵ ṵ */
	0x1e76, 501,	/* Ṷ ṷ */
	0x1e78, 501,	/* Ṹ ṹ */
	0x1e7a, 501,	/* Ṻ ṻ */
	0x1e7c, 501,	/* Ṽ ṽ */
	0x1e7e, 501,	/* Ṿ ṿ */
	0x1e80, 501,	/* Ẁ ẁ */
	0x1e82, 501,	/* Ẃ ẃ */
	0x1e84, 501,	/* Ẅ ẅ */
	0x1e86, 501,	/* Ẇ ẇ */
	0x1e88, 501,	/* Ẉ ẉ */
	0x1e8a, 501,	/* Ẋ ẋ */
	0x1e8c, 501,	/* Ẍ ẍ */
	0x1e8e, 501,	/* Ẏ ẏ */
	0x1e90, 501,	/* Ẑ ẑ */
	0x1e92, 501,	/* Ẓ ẓ */
	0x1e94, 501,	/* Ẕ ẕ */
	0x1ea0, 501,	/* Ạ ạ */
	0x1ea2, 501,	/* Ả ả */
	0x1ea4, 501,	/* Ấ ấ */
	0x1ea6, 501,	/* Ầ ầ */
	0x1ea8, 501,	/* Ẩ ẩ */
	0x1eaa, 501,	/* Ẫ ẫ */
	0x1eac, 501,	/* Ậ ậ */
	0x1eae, 501,	/* Ắ ắ */
	0x1eb0, 501,	/* Ằ ằ */
	0x1eb2, 501,	/* Ẳ ẳ */
	0x1eb4, 501,	/* Ẵ ẵ */
	0x1eb6, 501,	/* Ặ ặ */
	0x1eb8, 501,	/* Ẹ ẹ */
	0x1eba, 501,	/* Ẻ ẻ */
	0x1ebc, 501,	/* Ẽ ẽ */
	0x1ebe, 501,	/* Ế ế */
	0x1ec0, 501,	/* Ề ề */
	0x1ec2, 501,	/* Ể ể */
	0x1ec4, 501,	/* Ễ ễ */
	0x1ec6, 501,	/* Ệ ệ */
	0x1ec8, 501,	/* Ỉ ỉ */
	0x1eca, 501,	/* Ị ị */
	0x1ecc, 501,	/* Ọ ọ */
	0x1ece, 501,	/* Ỏ ỏ */
	0x1ed0, 501,	/* Ố ố */
	0x1ed2, 501,	/* Ồ ồ */
	0x1ed4, 501,	/* Ổ ổ */
	0x1ed6, 501,	/* Ỗ ỗ */
	0x1ed8, 501,	/* Ộ ộ */
	0x1eda, 501,	/* Ớ ớ */
	0x1edc, 501,	/* Ờ ờ */
	0x1ede, 501,	/* Ở ở */
	0x1ee0, 501,	/* Ỡ ỡ */
	0x1ee2, 501,	/* Ợ ợ */
	0x1ee4, 501,	/* Ụ ụ */
	0x1ee6, 501,	/* Ủ ủ */
	0x1ee8, 501,	/* Ứ ứ */
	0x1eea, 501,	/* Ừ ừ */
	0x1eec, 501,	/* Ử ử */
	0x1eee, 501,	/* Ữ ữ */
	0x1ef0, 501,	/* Ự ự */
	0x1ef2, 501,	/* Ỳ ỳ */
	0x1ef4, 501,	/* Ỵ ỵ */
	0x1ef6, 501,	/* Ỷ ỷ */
	0x1ef8, 501,	/* Ỹ ỹ */
	0x1f59, 492,	/* Ὑ ὑ */
	0x1f5b, 492,	/* Ὓ ὓ */
	0x1f5d, 492,	/* Ὕ ὕ */
	0x1f5f, 492,	/* Ὗ ὗ */
	0x1fbc, 491,	/* ᾼ ᾳ */
	0x1fcc, 491,	/* ῌ ῃ */
	0x1fec, 493,	/* Ῥ ῥ */
	0x1ffc, 491,	/* ῼ ῳ */
};

/*
 * title characters are those between
 * upper and lower case. ie DZ Dz dz
 */
static
Rune	__totitle1[] =
{
	0x01c4, 501,	/* DŽ Dž */
	0x01c6, 499,	/* dž Dž */
	0x01c7, 501,	/* LJ Lj */
	0x01c9, 499,	/* lj Lj */
	0x01ca, 501,	/* NJ Nj */
	0x01cc, 499,	/* nj Nj */
	0x01f1, 501,	/* DZ Dz */
	0x01f3, 499,	/* dz Dz */
};

static Rune*
bsearch(Rune c, Rune *t, int n, int ne)
{
	Rune *p;
	int m;

	while(n > 1) {
		m = n/2;
		p = t + m*ne;
		if(c >= p[0]) {
			t = p;
			n = n-m;
		} else
			n = m;
	}
	if(n && c >= t[0])
		return t;
	return 0;
}

Rune
tolowerrune(Rune c)
{
	Rune *p;

	p = bsearch(c, __tolower2, nelem(__tolower2)/3, 3);
	if(p && c >= p[0] && c <= p[1])
		return c + p[2] - 500;
	p = bsearch(c, __tolower1, nelem(__tolower1)/2, 2);
	if(p && c == p[0])
		return c + p[1] - 500;
	return c;
}

Rune
toupperrune(Rune c)
{
	Rune *p;

	p = bsearch(c, __toupper2, nelem(__toupper2)/3, 3);
	if(p && c >= p[0] && c <= p[1])
		return c + p[2] - 500;
	p = bsearch(c, __toupper1, nelem(__toupper1)/2, 2);
	if(p && c == p[0])
		return c + p[1] - 500;
	return c;
}

Rune
totitlerune(Rune c)
{
	Rune *p;

	p = bsearch(c, __totitle1, nelem(__totitle1)/2, 2);
	if(p && c == p[0])
		return c + p[1] - 500;
	return c;
}

int
islowerrune(Rune c)
{
	Rune *p;

	p = bsearch(c, __toupper2, nelem(__toupper2)/3, 3);
	if(p && c >= p[0] && c <= p[1])
		return 1;
	p = bsearch(c, __toupper1, nelem(__toupper1)/2, 2);
	if(p && c == p[0])
		return 1;
	return 0;
}

int
isupperrune(Rune c)
{
	Rune *p;

	p = bsearch(c, __tolower2, nelem(__tolower2)/3, 3);
	if(p && c >= p[0] && c <= p[1])
		return 1;
	p = bsearch(c, __tolower1, nelem(__tolower1)/2, 2);
	if(p && c == p[0])
		return 1;
	return 0;
}

int
isalpharune(Rune c)
{
	Rune *p;

	if(isupperrune(c) || islowerrune(c))
		return 1;
	p = bsearch(c, __alpha2, nelem(__alpha2)/2, 2);
	if(p && c >= p[0] && c <= p[1])
		return 1;
	p = bsearch(c, __alpha1, nelem(__alpha1), 1);
	if(p && c == p[0])
		return 1;
	return 0;
}

int
istitlerune(Rune c)
{
	return isupperrune(c) && islowerrune(c);
}

int
isspacerune(Rune c)
{
	Rune *p;

	p = bsearch(c, __space2, nelem(__space2)/2, 2);
	if(p && c >= p[0] && c <= p[1])
		return 1;
	return 0;
}
Added lib/libutf/utf.7.






































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
.deEX
.ift .ft5
.nf
..
.deEE
.ft1
.fi
..
.TH UTF 7
.SH NAME
UTF, Unicode, ASCII, rune \- character set and format
.SH DESCRIPTION
The Plan 9 character set and representation are
based on the Unicode Standard and on the ISO multibyte
.SM UTF-8
encoding (Universal Character
Set Transformation Format, 8 bits wide).
The Unicode Standard represents its characters in 16
bits;
.SM UTF-8
represents such
values in an 8-bit byte stream.
Throughout this manual,
.SM UTF-8
is shortened to
.SM UTF.
.PP
In Plan 9, a
.I rune
is a 16-bit quantity representing a Unicode character.
Internally, programs may store characters as runes.
However, any external manifestation of textual information,
in files or at the interface between programs, uses a
machine-independent, byte-stream encoding called
.SM UTF.
.PP
.SM UTF
is designed so the 7-bit
.SM ASCII
set (values hexadecimal 00 to 7F),
appear only as themselves
in the encoding.
Runes with values above 7F appear as sequences of two or more
bytes with values only from 80 to FF.
.PP
The
.SM UTF
encoding of the Unicode Standard is backward compatible with
.SM ASCII\c
:
programs presented only with
.SM ASCII
work on Plan 9
even if not written to deal with
.SM UTF,
as do
programs that deal with uninterpreted byte streams.
However, programs that perform semantic processing on
.SM ASCII
graphic
characters must convert from
.SM UTF
to runes
in order to work properly with non-\c
.SM ASCII
input.
See
.IR rune (3).
.PP
Letting numbers be binary,
a rune x is converted to a multibyte
.SM UTF
sequence
as follows:
.PP
01.   x in [00000000.0bbbbbbb] → 0bbbbbbb
.br
10.   x in [00000bbb.bbbbbbbb] → 110bbbbb, 10bbbbbb
.br
11.   x in [bbbbbbbb.bbbbbbbb] → 1110bbbb, 10bbbbbb, 10bbbbbb
.br
.PP
Conversion 01 provides a one-byte sequence that spans the
.SM ASCII
character set in a compatible way.
Conversions 10 and 11 represent higher-valued characters
as sequences of two or three bytes with the high bit set.
Plan 9 does not support the 4, 5, and 6 byte sequences proposed by X-Open.
When there are multiple ways to encode a value, for example rune 0,
the shortest encoding is used.
.PP
In the inverse mapping,
any sequence except those described above
is incorrect and is converted to rune hexadecimal 0080.
.SH "SEE ALSO"
.IR ascii (1),
.IR tcs (1),
.IR rune (3),
.IR "The Unicode Standard" .
Added lib/libutf/utfecpy.c.








































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
 * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */
#include <stdarg.h>
#include <string.h>
#include "plan9.h"
#include "utf.h"

char*
utfecpy(char *to, char *e, const char *from)
{
	char *end;

	if(to >= e)
		return to;
	end = memccpy(to, from, '\0', e - to);
	if(end == nil){
		end = e-1;
		while(end>to && (*--end&0xC0)==0x80)
			;
		*end = '\0';
	}else{
		end--;
	}
	return end;
}
Added lib/libutf/utflen.c.










































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
 * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */
#include <stdarg.h>
#include <string.h>
#include "plan9.h"
#include "utf.h"

int
utflen(const char *s)
{
	int c;
	long n;
	Rune rune;

	n = 0;
	for(;;) {
		c = *(uchar*)s;
		if(c < Runeself) {
			if(c == 0)
				return n;
			s++;
		} else
			s += chartorune(&rune, s);
		n++;
	}
}
Added lib/libutf/utfnlen.c.


















































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
 * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */
#include <stdarg.h>
#include <string.h>
#include "plan9.h"
#include "utf.h"

int
utfnlen(const char *s, long m)
{
	int c;
	long n;
	Rune rune;
	const char *es;

	es = s + m;
	for(n = 0; s < es; n++) {
		c = *(uchar*)s;
		if(c < Runeself){
			if(c == '\0')
				break;
			s++;
			continue;
		}
		if(!fullrune(s, es-s))
			break;
		s += chartorune(&rune, s);
	}
	return n;
}
Added lib/libutf/utfrrune.c.


























































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
 * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */
#include <stdarg.h>
#include <string.h>
#include "plan9.h"
#include "utf.h"

char*
utfrrune(const char *s, long c)
{
	long c1;
	Rune r;
	const char *s1;

	if(c < Runesync)		/* not part of utf sequence */
		return strrchr(s, c);

	s1 = 0;
	for(;;) {
		c1 = *(uchar*)s;
		if(c1 < Runeself) {	/* one byte rune */
			if(c1 == 0)
				return (char*)s1;
			if(c1 == c)
				s1 = s;
			s++;
			continue;
		}
		c1 = chartorune(&r, s);
		if(r == c)
			s1 = s;
		s += c1;
	}
}
Added lib/libutf/utfrune.c.
























































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
 * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */
#include <stdarg.h>
#include <string.h>
#include "plan9.h"
#include "utf.h"

char*
utfrune(const char *s, long c)
{
	long c1;
	Rune r;
	int n;

	if(c < Runesync)		/* not part of utf sequence */
		return strchr(s, c);

	for(;;) {
		c1 = *(uchar*)s;
		if(c1 < Runeself) {	/* one byte rune */
			if(c1 == 0)
				return 0;
			if(c1 == c)
				return (char*)s;
			s++;
			continue;
		}
		n = chartorune(&r, s);
		if(r == c)
			return (char*)s;
		s += n;
	}
}
Added lib/libutf/utfutf.c.


















































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/*
 * The authors of this software are Rob Pike and Ken Thompson.
 *              Copyright (c) 2002 by Lucent Technologies.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
 * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */
#include <stdarg.h>
#include <string.h>
#include "plan9.h"
#include "utf.h"


/*
 * Return pointer to first occurrence of s2 in s1,
 * 0 if none
 */
char*
utfutf(const char *s1, const char *s2)
{
	const char *p;
	long f, n1, n2;
	Rune r;

	n1 = chartorune(&r, s2);
	f = r;
	if(f <= Runesync)		/* represents self */
		return strstr(s1, s2);

	n2 = strlen(s2);
	for(p=s1; p=utfrune(p, f); p+=n1)
		if(strncmp(p, s2, n2) == 0)
			return (char*)p;
	return 0;
}
Added lib/libwmii_hack/Makefile.


































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
ROOT= ../..
include $(ROOT)/mk/hdr.mk

hack.o hack.o_pic: util.c hack.h x11.h

CFLAGS += $(INCX11)
SOLDFLAGS += $(LIBX11)

TARG =	libwmii_hack
# Can't just link libstuff here. We need PIC objects.
OBJ =	hack	\
	../libstuff/util/getbase	\
	../libstuff/util/getlong	\
	../libstuff/util/tokenize

include $(ROOT)/mk/so.mk

Added lib/libwmii_hack/hack.c.










































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
/* Copyright ©2008 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include "hack.h"
#include <dlfcn.h>
#include <stdbool.h>
#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>

#include "util.c"
#include "x11.c"

enum {
	Timeout = 10,
};

static void*	xlib;

static long	transient;
static Atom	types[32];
static long	ntypes;
static char**	tags;
static long	pid;
static long	stime;
static char	hostname[256];
static long	starttime;

typedef Window (*mapfn)(Display*, Window);

static Window (*mapwindow)(Display*, Window);
static Window (*mapraised)(Display*, Window);

static void
init(Display *d) { /* Hrm... assumes one display... */
	char *toks[nelem(types)];
	char *s, *p;
	long n;
	int i;

	xlib = dlopen("libX11.so", RTLD_GLOBAL | RTLD_LAZY);
	if(xlib == nil)
		return;
	mapwindow = (mapfn)(uintptr_t)dlsym(xlib, "XMapWindow");
	mapraised = (mapfn)(uintptr_t)dlsym(xlib, "XMapRaised");

	unsetenv("LD_PRELOAD");

	if((s = getenv("WMII_HACK_TRANSIENT"))) {
		if(getlong(s, &n))
			transient = n;
		unsetenv("WMII_HACK_TRANSIENT");
	}
	if((s = getenv("WMII_HACK_TYPE"))) {
		s = strdup(s);
		unsetenv("WMII_HACK_TYPE");

		n = tokenize(toks, nelem(toks), s, ',');
		for(i=0; i < n; i++) {
			for(p=toks[i]; *p; p++)
				if(*p >= 'a' && *p <= 'z')
					*p += 'A' - 'a';
			toks[i] = smprint("_NET_WM_WINDOW_TYPE_%s", toks[i]);
		}
		XInternAtoms(d, toks, n, false, types);
		ntypes = n;
		for(i=0; i < n; i++)
			free(toks[i]);
		free(s);
	}
	if((s = getenv("WMII_HACK_TAGS"))) {
		s = strdup(s);
		unsetenv("WMII_HACK_TAGS");

		n = tokenize(toks, nelem(toks)-1, s, '+');
		tags = strlistdup(toks, n);
		free(s);
	}
	if((s = getenv("WMII_HACK_TIME"))) {
		getlong(s, &stime);
		unsetenv("WMII_HACK_TIME");
	}

	pid = getpid();
	gethostname(hostname, sizeof hostname - 1);
}

static void
setprops(Display *d, Window w) {
	long *l;

	if(!xlib)
		init(d);

	if(getprop_long(d, w, "_NET_WM_PID", "CARDINAL", 0L, &l, 1L))
		free(l);
	else {
		changeprop_long(d, w, "_NET_WM_PID", "CARDINAL", &pid, 1);
		changeprop_string(d, w, "WM_CLIENT_MACHINE", hostname);
	}

	/* Kludge. */
	if(starttime == 0)
		starttime = time(0);
	else if(time(0) > starttime + Timeout)
		return;

	if(transient)
		changeprop_long(d, w, "WM_TRANSIENT_FOR", "WINDOW", &transient, 1);
	if(ntypes)
		changeprop_long(d, w, "_NET_WM_WINDOW_TYPE", "ATOM", (long*)types, ntypes);
	if(tags)
		changeprop_textlist(d, w, "_WMII_TAGS", "UTF8_STRING", tags);
	if(stime)
		changeprop_long(d, w, "_WMII_LAUNCH_TIME", "CARDINAL", &stime, 1);
}

int
XMapWindow(Display *d, Window w) {

	setprops(d, w);
	return mapwindow(d, w);
}

int
XMapRaised(Display *d, Window w) {

	setprops(d, w);
	return mapraised(d, w);
}

Added lib/libwmii_hack/hack.h.
























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

typedef unsigned long	ulong;
typedef unsigned int	uint;
typedef unsigned char	uchar;

#define _XOPEN_SOURCE 600
#define IXP_P9_STRUCTS
#define IXP_NO_P9_
#include <assert.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stuff/util.h>
#include <X11/Xlib.h>

#define strdup my_strdup
#define smprint my_smprint
#define vsmprint my_vsmprint

static char*	smprint(const char*, ...);
static char*	vsmprint(const char*, va_list);
static char*	strdup(const char*);

#define nil ((void*)0)
#define nelem(ary) (sizeof(ary) / sizeof(*ary))

Added lib/libwmii_hack/util.c.






















































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <ctype.h>
#include <stdio.h>
#include <string.h>

static char*
vsmprint(const char *fmt, va_list ap) {
	va_list al;
	char *buf = "";
	int n;

	va_copy(al, ap);
	n = vsnprintf(buf, 0, fmt, al);
	va_end(al);

	buf = malloc(++n);
	if(buf)
		vsnprintf(buf, n, fmt, ap);
	return buf;
}

static char*
smprint(const char *fmt, ...) {
	va_list ap;
	char *ret;

	va_start(ap, fmt);
	ret = vsmprint(fmt, ap);
	va_end(ap);
	return ret;
}

static char*
strdup(const char *s) {
	char *ret;
	int len;

	len = strlen(s)+1;
	ret = malloc(len);
	if(ret)
		memcpy(ret, s, len);
	return ret;
}

Added lib/libwmii_hack/x11.c.










































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
/* Copyright ©2007 Kris Maglione <maglione.k at Gmail>
 * See LICENSE file for license details.
 */
#include <assert.h>
#include "x11.h"

/* Misc */
static Atom
xatom(Display *display, char *name) {
	/* Blech. I don't trust Xlib's cacheing.
	MapEnt *e;

	e = hash_get(&amap, name, 1);
	if(e->val == nil)
		e->val = (void*)XInternAtom(display, name, False);
	return (Atom)e->val;
	*/
	return XInternAtom(display, name, False);
}
/* Properties */
#if 0
static void
delproperty(Display *display, Window w, char *prop) {
	XDeleteProperty(display, w, xatom(display, prop));
}
#endif

static void
changeproperty(Display *display, Window w, char *prop, char *type, int width, uchar data[], int n) {
	XChangeProperty(display, w, xatom(display, prop), xatom(display, type), width, PropModeReplace, data, n);
}

static void
changeprop_string(Display *display, Window w, char *prop, char *string) {
	changeprop_char(display, w, prop, "UTF8_STRING", string, strlen(string));
}

static void
changeprop_char(Display *display, Window w, char *prop, char *type, char data[], int len) {
	changeproperty(display, w, prop, type, 8, (uchar*)data, len);
}

#if 0
static void
changeprop_short(Display *display, Window w, char *prop, char *type, short data[], int len) {
	changeproperty(display, w, prop, type, 16, (uchar*)data, len);
}
#endif

static void
changeprop_long(Display *display, Window w, char *prop, char *type, long data[], int len) {
	changeproperty(display, w, prop, type, 32, (uchar*)data, len);
}

static void
changeprop_textlist(Display *display, Window w, char *prop, char *type, char *data[]) {
	char **p, *s, *t;
	int len, n;

	len = 0;
	for(p=data; *p; p++)
		len += strlen(*p) + 1;
	s = malloc(len);
	if(s == nil)
		return;
	t = s;
	for(p=data; *p; p++) {
		n = strlen(*p) + 1;
		memcpy(t, *p, n);
		t += n;
	}
	changeprop_char(display, w, prop, type, s, len);
	free(s);
}

#if 0
static void
freestringlist(char *list[]) {
	XFreeStringList(list);
}
#endif

static ulong
getprop(Display *display, Window w, char *prop, char *type, Atom *actual, int *format, ulong offset, uchar **ret, ulong length) {
	Atom typea;
	ulong n, extra;
	int status;

	typea = (type ? xatom(display, type) : 0L);

	status = XGetWindowProperty(display, w,
		xatom(display, prop), offset, length, False /* delete */,
		typea, actual, format, &n, &extra, ret);

	if(status != Success) {
		*ret = nil;
		return 0;
	}
	if(n == 0) {
		free(*ret);
		*ret = nil;
	}
	return n;
}

#if 0
static ulong
getproperty(Display *display, Window w, char *prop, char *type, Atom *actual, ulong offset, uchar **ret, ulong length) {
	int format;

	return getprop(display, w, prop, type, actual, &format, offset, ret, length);
}
#endif

static ulong
getprop_long(Display *display, Window w, char *prop, char *type, ulong offset, long **ret, ulong length) {
	Atom actual;
	ulong n;
	int format;

	n = getprop(display, w, prop, type, &actual, &format, offset, (uchar**)ret, length);
	if(n == 0 || format == 32 && xatom(display, type) == actual)
		return n;
	free(*ret);
	*ret = 0;
	return 0;
}

#ifdef notdef
static char**
strlistdup(char *list[], int n) {
	char **p, *q;
	int i, m;

	for(i=0, m=0; i < n; i++)
		m += strlen(list[i])+1;

	p = malloc((n+1)*sizeof(char*) + m);
	if(p == nil)
		return nil;
	q = (char*)&p[n+1];

	for(i=0; i < n; i++) {
		p[i] = q;
		m = strlen(list[i])+1;
		memcpy(q, list[i], m);
		q += m;
	}
	p[n] = nil;
	return p;
}
#endif

static char**
strlistdup(char *list[], int n) {
	char **p, *q;
	int i, m;

	m = 0;
	for(i=0; i < n; i++)
		m += strlen(list[i]) + 1;

	p = malloc((n+1) * sizeof(*p) + m);
	q = (char*)&p[n+1];

	for(i=0; i < n; i++) {
		p[i] = q;
		m = strlen(list[i]) + 1;
		memcpy(q, list[i], m);
		q += m;
	}
	p[n] = nil;
	return p;
}

#if 0
static int
getprop_textlist(Display *display, Window w, char *name, char **ret[]) {
	XTextProperty prop;
	char **list;
	int n;

	n = 0;

	XGetTextProperty(display, w, &prop, xatom(display, name));
	if(prop.nitems > 0) {
		if(Xutf8TextPropertyToTextList(display, &prop, &list, &n) == Success) {
			*ret = strlistdup(list, n);
			XFreeStringList(list);
		}
		XFree(prop.value);
	}
	return n;
}
#endif

#if 0
static char*
getprop_string(Display *display, Window w, char *name) {
	char **list, *str;
	int n;

	str = nil;

	n = getprop_textlist(display, w, name, &list);
	if(n > 0)
		str = strdup(*list);
	freestringlist(list);

	return str;
}
#endif

Added lib/libwmii_hack/x11.h.




































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>

static void	changeprop_char(Display*, Window, char*, char*, char[], int);
static void	changeprop_long(Display*, Window, char*, char*, long[], int);
/* static void	changeprop_short(Display*, Window, char*, char*, short[], int); */
static void	changeprop_string(Display*, Window, char*, char*);
static void	changeprop_textlist(Display*, Window, char*, char*, char*[]);
static void	changeproperty(Display*, Window, char*, char*, int width, uchar*, int);
/* static void	delproperty(Display*, Window, char*); */
/* static void	freestringlist(char**); */
static ulong	getprop_long(Display*, Window, char*, char*, ulong, long**, ulong);
/* static char*	getprop_string(Display*, Window, char*); */
/* static int	getprop_textlist(Display*, Window, char*, char**[]); */
/* static ulong	getproperty(Display*, Window, char*, char*, Atom*, ulong, uchar**, ulong); */
static Atom	xatom(Display*, char*);

Added man/Makefile.
































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
ROOT=..
include $(ROOT)/mk/hdr.mk
include $(ROOT)/mk/wmii.mk

MANPAGES = wmii.1	   \
	   wmiir.1	   \
	   wmii9menu.1\
	   wihack.1   \
	   wimenu.1   \
	   wistrut.1  \
	   witray.1

$(TARG): Makefile $(ROOT)/mk/wmii.mk header.t2t

include $(ROOT)/mk/man.mk

Added man/header.t2t.


































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
%!target: man
%!encoding: UTF-8

% Special formatting for certain constructs. They, unfortunately
% have to be post-processed. The _italic_ hack is necessary for
% italicising things like /_foo_/, which txt2tags will ignore.
% The others need to work in ``` lines.
%!preproc(man): \bPROVISIONAL\b  **PROVISIONAL**
%!postproc(man): (<.*?>)         \\fI\1\\fR
%!postproc(man): \b_(.*?)_       \\fI\1\\fR
%!postproc(man): \\e_            _
%!postproc(man): `(.*?)`         \\fB\1\\fR
%!postproc(man): \[(.*?)\]       [\\fI\1\\fR]
%!postproc(man): \+$             \n.P
%!postproc(man): (\$[a-zA-Z_]+)  \\fB\1\\fR
%!postproc(man): (\${[a-zA-Z_]+)(.*?)} \\fB\1\\fR\2\\fB}\\fR

%!postproc(html): (<.*?>)   (:arg \1:)
%!postproc(html): \b_(.*?)_ (:emph \1:)
%!postproc(html): `(.*?)`   (:code \1:)
%!postproc(html): \+$       <br/>

%!postproc(html) \(:(\w+)\s*(.*):\) <span class="\1">\2</span>

% Well, it seems that txt2tags isn't particularly well suited
% to troff output. These two hacks make multi-level definition
% lists possible.
%!postproc(man): ^\s*>>$ .RS 8
%!postproc(man): ^\s*<<$ .RS -8

%!postproc(html): ^\s*>>$
%!postproc(html): ^\s*<<$

Added man/mkfile.


















>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
TARG = `{bmake -VTARG}

default:V: all

all:V: $TARG

%.1: %.tex
	latex2man -M $stem.tex $stem.1

Added man/wihack.1.






































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
.TH "WIMENU" 1 "May, 2010" "wmii-@VERSION@"

.SH NAME
.P
wihack \- The wmii window hack

.SH SYNOPSIS
.P
wihack \fI[\-transient \fI<window>\fR]\fR \fI[\-type \fI<ewmh window type>\fR]\fR \fI[\-tags \fI<tags>\fR]\fR \fI<program>\fR

.SH DESCRIPTION
.P
\fBwihack\fR is a program which alters the windows created by an
arbitrary program. It has the name \fBwihack\fR because it is just that:
a hack. It uses LD_PRELOAD to override certain Xlib calls and add
properties to newly created windows.

.SH ARGUMENTS
.TP
\-transient \fI<window>\fR
Marks created windows as transient for a given \fI<window>\fR.
This causes the new window to open in the floating layer of
the same tags as \fI<window>\fR.
.TP
\-type \fI<ewmh window type>\fR
Sets the EWMH window type of the created window to the type
given. \fBwmii\fR understands the following types:

.RS 8
.TP
dialog
Opens in the floating layer.
.TP
dock
.TP
menu
.TP
toolbar
Automatically opens in the floating layer. Does not
have a window border or titlebar.
.TP
splash
Automatically floats and does not automatically
receive focus.
.RS -8
.TP
\-tags \fI<tags>\fR
The created window opens on the given tags.

.SH BUGS
.P
It is a hack.

.P
It doesn't work for setuid programs.

.P
It doesn't work for non\-Xlib programs.

.SH SEE ALSO
.P
wmii(1)


.\" man code generated by txt2tags 2.5 (http://txt2tags.sf.net)
.\" cmdline: txt2tags -o- wihack.man1

Added man/wihack.man1.




















































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
WIMENU
wmii-@VERSION@
May, 2010

%!includeconf: header.t2t

= NAME =

wihack - The wmii window hack

= SYNOPSIS =

wihack [-transient <window>] [-type <ewmh window type>] [-tags <tags>] <program>

= DESCRIPTION =

`wihack` is a program which alters the windows created by an
arbitrary program. It has the name `wihack` because it is just that:
a hack. It uses LD_PRELOAD to override certain Xlib calls and add
properties to newly created windows.

= ARGUMENTS =

: -transient <window>
        Marks created windows as transient for a given <window>.
        This causes the new window to open in the floating layer of
        the same tags as <window>.
: -type <ewmh window type>
        Sets the EWMH window type of the created window to the type
        given. `wmii` understands the following types:

        >>
        : dialog
                Opens in the floating layer.
        : dock
        : menu
        : toolbar
                Automatically opens in the floating layer. Does not
                have a window border or titlebar.
        : splash
                Automatically floats and does not automatically
                receive focus.
        <<
: -tags <tags>
        The created window opens on the given tags.
:
= BUGS =

It is a hack.

It doesn't work for setuid programs.

It doesn't work for non-Xlib programs.

= SEE ALSO =

wmii(1)

Added man/wimenu.1.


















































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
.TH "WIMENU" 1 "Oct, 2009" "wmii-@VERSION@"


.SH NAME

.P
wimenu \- The wmii menu program

.SH SYNOPSIS

.P
wimenu [\fI\-i\fR] [\fI\-h \fI<history file>\fR\fR] [\fI\-n \fI<history count>\fR\fR] [\fI\-p \fI<prompt>\fR\fR] 
.P
wimenu \-v

.SH DESCRIPTION

.P
\fBwimenu\fR is \fBwmii\fR's standard menu program. It's used
extensively by \fBwmii\fR and related programs to prompt the user
for input. The standard configuration uses it to launch
programs, select views, and perform standard actions. It
supports basic item completion and history searching.

.SH BASIC ARGUMENTS

.P
Normal use of \fBwimenu\fR shouldn't require any arguments other than the
following. More advanced options are documented below.

.TP
\-h \fI<history file>\fR
Causes \fBwimenu\fR to read its command history from
\fI<history file>\fR and to append its result to that file if
\fI\-n\fR is given.
.TP
\-i
Causes matching of completion items to be performed in a
case insensitive manner.
.TP
\-n \fI<count>\fR
Write at most \fI<count>\fR items back to the history file.
The file is never modified unless this option is
provided. Duplicates are filtered out within a 20 item
sliding window before this limit is imposed.
.TP
\-p \fI<prompt>\fR
The string \fI<prompt>\fR will be show before the input field
when the menu is opened.
.TP
\-r \fI<rows>\fR
Display completion items as a vertical list, one per
row, rather than a horizontal list, side\-by\-side. A
maximum of \fI<rows>\fR rows will be displayed.

.SH ADVANCED ARGUMENTS

.TP
\-a
The address at which to connect to \fBwmii\fR.
.TP
\-K
Prevents \fBwimenu\fR from initializing its default key
bindings. WARNING: If you do this, be sure to bind a key
with the Accept or Reject action, or you will have no way
to exit \fBwimenu\fR.
.TP
\-k \fI<key file>\fR
Key bindings will be read from \fI<key file>\fR. Bindings
appear as:

\fI<key>\fR [\fIaction\fR] [\fIargs\fR]

where \fI<key>\fR is a key name, similar to the format used by
wmii. For action and args, please refer to the default
bindings, provided in the source distribution under
cmd/menu/keys.txt, or use strings(1) on the \fBwimenu\fR
executable (this level of customization is reserved for the
determined).
.TP
\-s \fI<screen>\fR
Suggests that the menu open on Xinerama screen \fI<screen>\fR.
.TP
\-S \fI<command separator>\fR

.RS
Causes each input item to be split at the first occurance of
\fI<command sep>\fR. The text to the left of the separator is displayed
as a menu option, and the text to the right is displayed when a
selection is made.
.RE

.SH KEY BINDINGS

.P
\fBwimenu\fR's default key bindings are based largely on the
movement keys of vi and the standard UNIX shell input bindings.

.TP
Return, C\-j, C\-m
Accept the input, and select the first matching
completion if the cursor is at the end of the input.
.TP
S\-Return, C\-S\-j, C\-S\-m
Accept the input literally.
.TP
Esc, C\-[
Quit without returning any output, and exit with
non\-zero status.

.TP
A\-p
Paste the PRIMARY selection.

.TP
Left, C\-b
Move backward one character.
.TP
Right, C\-f
Move forward one character.

.TP
A\-b
Move backward one word.
.TP
A\-f
Move forward one word.

.TP
C\-a
Move to the beginning of the line.
.TP
C\-e
Move to the end of the line.

.TP
C\-p, Up
Move backward through the input history.
.TP
C\-n, Down
Move forward through the input history.

.TP
Backspace, C\-h
Delete the previous character.
.TP
C\-Backspace, C\-w
Delete the previous word.
.TP
C\-u
Delete the previous portion of the line.

.TP
Tab, C\-i¸ A\-l
Select the next completion.
.TP
S\-Tab, C\-S\-i, A\-h
Select the previous completion.
.TP
PageUp, A\-k
Select the previous completion page.
.TP
PageDown, A\-j
Select the next completion page.
.TP
Home, A\-g
Select the first completion page.
.TP
End, A\-S\-g
Select the last completion page.

.SH CUSTOM COMPLETION

.P
Custom, multipart completion data may be proveded by an
external application. When the standard input is not a TTY,
processing of a set of completions stops at every blank line.
After the first new line or EOF, \fBwimenu\fR displays the first
set of menu items, and waits for further input. The completion
items may be replaced by writing out a new set, again followed
by a new line. Every set following the first must begin with a
line containing a single decimal number specifying where the
new completion results are to be spliced into the input. When
an item is selected, text from this position to the position
of the caret is replaced.

.SS ARGUMENTS

.TP
\-c
Prints the contents of the input buffer each time the
user inputs a character, as such:

\fI<text before caret>\fR\en\fI<text after caret>\fR\en

.SS EXAMPLE

.P
Let's assume that a script would like to provide a menu with
completions first for a command name, then for arguments
to that command. Given three commands and argument sets,

.TP
foo

.RS
1, 2, 3
.RE

.TP
bar

.RS
4, 5, 6
.RE

.TP
baz

.RS
7, 8, 9
.RE

.P
the following script provides the appropriate completions:

.nf
#!/bin/sh -f

rm fifo
mkfifo fifo

# Open wimenu with a fifo as its stdin
wimenu -c <fifo | awk '
	BEGIN {
		# Define the completion results
		cmds = "foo\enbar\enbaz\en"
		cmd[\fI"foo"\fR] = "1\en2\en3\en"
		cmd[\fI"bar"\fR] = "4\en5\en6\en"
		cmd[\fI"baz"\fR] = "7\en8\en9\en"

		# Print the first set of completions to wimenu’s fifo
		fifo = "fifo"
		print cmds >fifo; fflush(fifo)
	}

        { print; fflush() }

	# Push out a new set of completions
	function update(str, opts) {
		print length(str) >fifo # Print the length of the preceding string
		print opts >fifo        # and the options themself
		fflush(fifo)
	}

	# Ensure correct argument count with trailing spaces
	/ $/ { $0 = $0 "#"; }

	{ # Process the input and provide the completions
		if (NF == 1)
			update("", cmds)        # The first arg, command choices
		else
			update($1 " ", cmd[\fI$1\fR]) # The second arg, command arguments
		# Skip the trailing part of the command
		getline rest
	}
\&' | tail -1
.fi


.P
In theory, this facility can be used for myriad purposes,
including hijacking the programmable completion facilities of
most shells. See also the provided examples[\fI1\fR].

.SH ENVIRONMENT

.TP
\fB$WMII_ADDRESS\fR
The address at which to connect to wmii.
.TP
\fB$NAMESPACE\fR
The namespace directory to use if no address is
provided.

.SH SEE ALSO

.P
wmii(1), wmiir(1), wistrug(1), wmii9menu(1), dmenu(1)

.P
[\fI1\fR] http://www.suckless.org/wiki/wmii/tips/9p_tips 
.P
[\fI2\fR] @EXAMPLES@

.\" man code generated by txt2tags 2.6 (http://txt2tags.org)
.\" cmdline: txt2tags -o- wimenu.man1
Added man/wimenu.man1.


















































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
WIMENU
wmii-@VERSION@
Oct, 2009

%!includeconf: header.t2t

= NAME =

wimenu - The wmii menu program

= SYNOPSIS =

wimenu [-i] [-h <history file>] [-n <history count>] [-p <prompt>] +
wimenu -v

= DESCRIPTION =

`wimenu` is `wmii`'s standard menu program. It's used
extensively by `wmii` and related programs to prompt the user
for input. The standard configuration uses it to launch
programs, select views, and perform standard actions. It
supports basic item completion and history searching.

= BASIC ARGUMENTS =

Normal use of `wimenu` shouldn't require any arguments other than the
following. More advanced options are documented below.

: -h <history file>
        Causes `wimenu` to read its command history from
        <history file> and to append its result to that file if
        _-n_ is given.
: -i
        Causes matching of completion items to be performed in a
        case insensitive manner.
: -n <count>
        Write at most <count> items back to the history file.
        The file is never modified unless this option is
        provided. Duplicates are filtered out within a 20 item
        sliding window before this limit is imposed.
: -p <prompt>
        The string <prompt> will be show before the input field
        when the menu is opened.
: -r <rows>
        Display completion items as a vertical list, one per
        row, rather than a horizontal list, side-by-side. A
        maximum of <rows> rows will be displayed.
:

= ADVANCED ARGUMENTS =

: -a
        The address at which to connect to `wmii`.
: -K
        Prevents `wimenu` from initializing its default key
        bindings. WARNING: If you do this, be sure to bind a key
        with the Accept or Reject action, or you will have no way
        to exit `wimenu`.
: -k <key file>
        Key bindings will be read from <key file>. Bindings
        appear as:

                <key> [action] [args]

        where <key> is a key name, similar to the format used by
        wmii. For action and args, please refer to the default
        bindings, provided in the source distribution under
        cmd/menu/keys.txt, or use strings(1) on the `wimenu`
        executable (this level of customization is reserved for the
        determined).
: -s <screen>
        Suggests that the menu open on Xinerama screen <screen>.
: -S <command separator>
	Causes each input item to be split at the first occurance of
	<command sep>. The text to the left of the separator is displayed
	as a menu option, and the text to the right is displayed when a
	selection is made.

= KEY BINDINGS =

`wimenu`'s default key bindings are based largely on the
movement keys of vi and the standard UNIX shell input bindings.

: Return, C-j, C-m
        Accept the input, and select the first matching
        completion if the cursor is at the end of the input.
: S-Return, C-S-j, C-S-m
        Accept the input literally.
: Esc, C-[
        Quit without returning any output, and exit with
        non-zero status.

: A-p
        Paste the PRIMARY selection.

: Left, C-b
        Move backward one character.
: Right, C-f
        Move forward one character.

: A-b
        Move backward one word.
: A-f
        Move forward one word.

: C-a
        Move to the beginning of the line.
: C-e
        Move to the end of the line.

: C-p, Up
        Move backward through the input history.
: C-n, Down
        Move forward through the input history.

: Backspace, C-h
        Delete the previous character.
: C-Backspace, C-w
        Delete the previous word.
: C-u
        Delete the previous portion of the line.

: Tab, C-i¸ A-l
        Select the next completion.
: S-Tab, C-S-i, A-h
        Select the previous completion.
: PageUp, A-k
        Select the previous completion page.
: PageDown, A-j
        Select the next completion page.
: Home, A-g
        Select the first completion page.
: End, A-S-g
        Select the last completion page.
:
= CUSTOM COMPLETION =

Custom, multipart completion data may be proveded by an
external application. When the standard input is not a TTY,
processing of a set of completions stops at every blank line.
After the first new line or EOF, `wimenu` displays the first
set of menu items, and waits for further input. The completion
items may be replaced by writing out a new set, again followed
by a new line. Every set following the first must begin with a
line containing a single decimal number specifying where the
new completion results are to be spliced into the input. When
an item is selected, text from this position to the position
of the caret is replaced.

== ARGUMENTS ==

: -c
        Prints the contents of the input buffer each time the
        user inputs a character, as such:

                <text before caret>\n<text after caret>\n
:

== EXAMPLE ==

Let's assume that a script would like to provide a menu with
completions first for a command name, then for arguments
to that command. Given three commands and argument sets,

: foo
	1, 2, 3
: bar
	4, 5, 6
: baz
	7, 8, 9

the following script provides the appropriate completions:

```
#!/bin/sh -f

rm fifo
mkfifo fifo

# Open wimenu with a fifo as its stdin
wimenu -c <fifo | awk '
	BEGIN {
		# Define the completion results
		cmds = "foo\nbar\nbaz\n"
		cmd["foo"] = "1\n2\n3\n"
		cmd["bar"] = "4\n5\n6\n"
		cmd["baz"] = "7\n8\n9\n"

		# Print the first set of completions to wimenu’s fifo
		fifo = "fifo"
		print cmds >fifo; fflush(fifo)
	}

        { print; fflush() }

	# Push out a new set of completions
	function update(str, opts) {
		print length(str) >fifo # Print the length of the preceding string
		print opts >fifo        # and the options themself
		fflush(fifo)
	}

	# Ensure correct argument count with trailing spaces
	/ $/ { $0 = $0 "#"; }

	{ # Process the input and provide the completions
		if (NF == 1)
			update("", cmds)        # The first arg, command choices
		else
			update($1 " ", cmd[$1]) # The second arg, command arguments
		# Skip the trailing part of the command
		getline rest
	}
' | tail -1
```

In theory, this facility can be used for myriad purposes,
including hijacking the programmable completion facilities of
most shells. See also the provided examples[1].

= ENVIRONMENT =

: $WMII_ADDRESS
        The address at which to connect to wmii.
: $NAMESPACE
        The namespace directory to use if no address is
        provided.
:
= SEE ALSO =
wmii(1), wmiir(1), wistrug(1), wmii9menu(1), dmenu(1)

[1] http://www.suckless.org/wiki/wmii/tips/9p_tips +
[2] @EXAMPLES@
Added man/wistrut.1.










































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
.TH "WISTRUT" 1 "May, 2010" "wmii-@VERSION@"

.SH NAME
.P
wistrut \- The wmii EWMH strut manager

.SH SYNOPSIS
.P
wistrut \fI[\-HV]\fR \fI<window|class>\fR... 
.P
wistrut \-v

.SH DESCRIPTION
.P
\fBwistrut\fR automatically sets EWMH struts on windows for programs
which don't provide such functionality. This allows you to leave
utility windows onscreen without obscuring managed clients. Instead,
whatever part of the screen is occupied by the window will be left
free by wmii, provided it is less than half of the screen width or
height. Struts are automatically updated when the managed windows
are moved or resized, and are only applied if the window is touching
an edge of the screen.

.P
\fBwistrut\fR may be used with any EWMH compatible window manager.

.SH ARGUMENTS
.P
All non\-option arguments constitute window IDs or regular
expressions. In the latter case, the any window whose
\fI<name>\fR:\fI<class>\fR:\fI<title>\fR (as used in wmii's colrules and tagrules)
will be managed.

.TP
\-H

.RS
Only set horizontal struts. Normally, \fBwistrut\fR locates
struts in the direction of the narrowest dimension of the
window, provided it is touching a screen edge. With this
option set, they will always be allocated on either the left
or right of the screen. Never the top or bottom.
.RE
.TP
\-V

.RS
Only set vertical struts. See \-H.
.RE
.TP
\-v

.RS
Display version information.
.RE

.SH BUGS
.P
There is no way to remove struts from a window other than to move it
away from the edge of the screen and kill \fBwistrut\fR.

.SH SEE ALSO
.P
wmii(1)


.\" man code generated by txt2tags 2.5 (http://txt2tags.sf.net)
.\" cmdline: txt2tags -o- wistrut.man1

Added man/wistrut.man1.














































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
WISTRUT
wmii-@VERSION@
May, 2010

%!includeconf: header.t2t

= NAME =

wistrut - The wmii EWMH strut manager

= SYNOPSIS =

wistrut [-HV] <window|class>... +
wistrut -v

= DESCRIPTION =

`wistrut` automatically sets EWMH struts on windows for programs
which don't provide such functionality. This allows you to leave
utility windows onscreen without obscuring managed clients. Instead,
whatever part of the screen is occupied by the window will be left
free by wmii, provided it is less than half of the screen width or
height. Struts are automatically updated when the managed windows
are moved or resized, and are only applied if the window is touching
an edge of the screen.

`wistrut` may be used with any EWMH compatible window manager.

= ARGUMENTS =

All non-option arguments constitute window IDs or regular
expressions. In the latter case, the any window whose
<name>:<class>:<title> (as used in wmii's colrules and tagrules)
will be managed.

: -H
	Only set horizontal struts. Normally, `wistrut` locates
	struts in the direction of the narrowest dimension of the
	window, provided it is touching a screen edge. With this
	option set, they will always be allocated on either the left
	or right of the screen. Never the top or bottom.
: -V
	Only set vertical struts. See -H.
: -v
	Display version information.

= BUGS =

There is no way to remove struts from a window other than to move it
away from the edge of the screen and kill `wistrut`.

= SEE ALSO =

wmii(1)

Added man/witray.1.




































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
.TH "WITRAY" 1 "May, 2010" "wmii-@VERSION@"


.SH NAME

.P
witray \- The wmii system tray

.SH SYNOPSIS

.P
witray [\fI\-a \fI<address>\fR\fR] [\fI\-NESW\fR] [\fI\-HV\fR] [\fI\-p \fI<padding>\fR\fR] [\fI\-s \fI<iconsize>\fR\fR] \-t (tags) 
.P
witray \-v

.SH DESCRIPTION

.P
\fBwitray\fR is a simple system tray program meant to be used with wmii.
It supports the Freedesktop System Tray Protocol Specification
common among all newer applications and desktop environments. The
tray is only visible when applications require it.  While there are
many standalone system tray applications, this one is explicitly
tailored to wmii. It's simple, stays out of the user's way, is easy
to configure, and generally Just Works.

.SH ARGUMENTS

.TP
\-a
The address at which to connect to \fBwmii\fR.
.TP
\-N \-E \-S \-W

.RS
Sets the screen edge where \fBwitray\fR is to appear to the
\fINorth\fR, \fISouth\fR, \fIEast\fR, or \fIWest\fR edge, respectively.
Options may be combined, for instance *\-NE* gives the _North
East_ corner of the screen. If no options are given,
\fBwitray\fR opens to the West of whichever part of the screen
the wmii bar occupies.
.RE

.TP
\-H \-V

.RS
Specifies whether icons are to be aligned horizontally or
vertically, respectively. Also determines from which edge of
the screen windows are shunted to make room for the tray.
.RE

.TP
\-n
Do not replace an already running system tray.
.TP
\-p \fI<padding>\fR

.RS
Sets the padding between icons and around the edge of the
tray. In pixels.
.RE

.TP
\-s \fI<iconsize>\fR

.RS
Sets the size of the icons, in pixels. If not provided,
icons are sized so that the tray is the same height as the
\fBwmii\fR bar.
.RE

.TP
\-t \fI<tags>\fR

.RS
The tags on which to open. Default: \fI/./\fR
.RE

.TP
\-v

.RS
Display version information.
.RE

.SH CAVEATS

.P
\fBwitray\fR is not XRandR aware.

.SH SEE ALSO

.P
wmii(1), wimenu(1)

.\" man code generated by txt2tags 2.6 (http://txt2tags.org)
.\" cmdline: txt2tags -o- witray.man1
Added man/witray.man1.




























































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
WITRAY
wmii-@VERSION@
May, 2010

%!includeconf: header.t2t

= NAME =

witray - The wmii system tray

= SYNOPSIS =

witray [-a <address>] [-NESW] [-HV] [-p <padding>] [-s <iconsize>] [-t tags] +
witray -v

= DESCRIPTION =

`witray` is a simple system tray program meant to be used with wmii.
It supports the Freedesktop System Tray Protocol Specification
common among all newer applications and desktop environments. The
tray is only visible when applications require it.  While there are
many standalone system tray applications, this one is explicitly
tailored to wmii. It's simple, stays out of the user's way, is easy
to configure, and generally Just Works.

= ARGUMENTS =

: -a
        The address at which to connect to `wmii`.
: -N -E -S -W
	Sets the screen edge where `witray` is to appear to the
	_North_, _South_, _East_, or _West_ edge, respectively.
	Options may be combined, for instance *-NE* gives the _North
	East_ corner of the screen. If no options are given,
	`witray` opens to the West of whichever part of the screen
	the wmii bar occupies.
: -H -V
	Specifies whether icons are to be aligned horizontally or
	vertically, respectively. Also determines from which edge of
	the screen windows are shunted to make room for the tray.
: -n
        Do not replace an already running system tray.
: -p <padding>
	Sets the padding between icons and around the edge of the
	tray. In pixels.
: -s <iconsize>
	Sets the size of the icons, in pixels. If not provided,
	icons are sized so that the tray is the same height as the
	`wmii` bar.
: -t <tags>
	The tags on which to open. Default: _/./_
: -v
	Display version information.

= CAVEATS =

`witray` is not XRandR aware.

= SEE ALSO =

wmii(1), wimenu(1)

Added man/wmii.1.








































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
.TH "WMII" 1 "2010 June" "wmii-@VERSION@"

.SH NAME
.P
wmii \- Window Manager Improved²

.SH SYNOPSIS
.P
wmii \fI[\-a \fI<address>\fR]\fR \fI[\-r \fI<wmiirc>\fR]\fR 
.P
wmii \-v

.SH DESCRIPTION
.SS Overview
.P
\fBwmii\fR is a dynamic window manager for X11. In contrast to
static window management the user rarely has to think about how
to organize windows, no matter what he is doing or how many
applications are used at the same time.  The window manager
adapts to the current environment and fits to the needs of the
user, rather than forcing him to use a preset, fixed layout and
trying to shoehorn all windows and applications into it.

.P
\fBwmii\fR supports classic and tiled window management with
extended keyboard and mouse control. Classic window management
arranges windows in a floating layer in which tyen can be moved
and resized freely. Tiled window management arranges windows in
vertical columns.  Each column holds an arbitrary number
arbitrary windows and arranges them vertically in a
non\-overlapping manner. They can then be moved and resized,
among and within columns, at will.

.P
\fBwmii\fR provides a virtual filesystem which represents the
internal state similar to the procfs of Unix operating systems.
Modifying this virtual filesystem results in changing the state
of the window manager. The virtual filesystem service can be
accessed through 9P\-capable client programs, like
wmiir(1).  This allows simple and powerful remote control
of the core window manager.

.SS Command Line Arguments
.TP
\-a \fI<address>\fR
Specifies the address on which \fBwmii\fR should listen for
connections. The address takes the form
\fB\fI<protocol>\fR!\fI<address>\fR\fR. The default is of the form:

.nf
      unix!/tmp/ns.\fB$USER\fR.\fB${DISPLAY\fR%.0\fB}\fR/wmii
.fi

which opens a unix socket per Plan 9 Port conventions. To
open a TCP socket, listening at port 4332 on the loopback
interface, use:

.nf
      tcp!localhost!4332
.fi

\fB$WMII_NAMESPACE\fR is automatically set to this value.

.TP
\-r \fI<wmiirc>\fR
Specifies which rc script to run. If \fI<wmiirc>\fR consists of a
single argument, \fB$WMII_CONFPATH\fR is searched before \fB$PATH\fR.
Otherwise, it is passed to the shell for evaluation. The
environment variables \fB$WMII_ADDRESS\fR and \fB$WMII_CONFPATH\fR are
preset for the script.

.SS Terminology
.TP
Display
A running X server instance consisting of input
devices and screens.
.TP
Screen
A physical or virtual (Xinerama or Xnest(1))
screen of an X display.
.TP
Window
A (rectangular) drawable X object which is
displayed on a screen, usually an application window.
.TP
Client
An application window surrounded by a frame window
containing a border and a titlebar.
.TP
Floating layer
A screen layer of \fBwmii\fR on top of all other layers,
where clients are arranged in a classic (floating)
manner.  They can be resized or moved freely.
.TP
Managed layer
A screen layer of \fBwmii\fR underneath the floating layer,
where clients are arranged in a non\-overlapping
(managed) manner.  Here, the window manager dynamically
assigns each client a size and position.  The managed
layer consists of columns.
.TP
Tag
Alphanumeric strings which can be assigned to a
client. This provides a mechanism to group clients with
similar properties. Clients can have one tag, e.g.
\fIwork\fR, or several tags, e.g.  \fIwork+mail\fR.
Tags are separated with the \fI+\fR character.
.TP
View
A set of clients containing a specific tag, quite
similar to a workspace in other window managers. It
consists of the floating and managed layers.
.TP
Column
A column is a screen area which arranges clients
vertically in a non\-overlapping way. Clients can be
moved and resized between and within columns freely.
.TP
Bar
The bar at the bottom of the screen displays a label
for each view and allows the creation of arbitrary
user\-defined labels.
.TP
Event
An event is a message which can be read from a
special file in the filesystem of \fBwmii\fR, such as a
mouse button press, a key press, or a message written by
a different 9P\-client.


.SS Basic window management
.P
Running a raw \fBwmii\fR process without a wmiirc(1) script provides
basic window management capabilities.  However, to use it
effectively, remote control through its filesystem interface is
necessary.  Without such a script, it is only possible to move
and resize clients with the mouse, but not to change their tags
or to switch views.  Other interactions, such as customizing the
style, killing or retagging clients, and grabbing keys, cannot
be achieved without accessing the filesystem.

.P
The filesystem can be accessed by connecting to the \fIaddress\fR
of \fBwmii\fR with any 9P\-capable client, such as wmiir(1)

.SS Actions
.P
The default configuration provides for a special menu of
actions. These consist of either shell scripts in \fB$WMII_CONFPATH\fR
or action definitions included in wmiirc.

.P
Here is a list of the default actions:

.TS
tab(^); ll.
 exec^Replace the window manager with another program
 quit^Leave the window manager nicely
 rehash^Refresh the program list
 showkeys^Display a list of key bindings recognized by wmii
 status^Periodically print date and load average to the bar
 welcome^Display a welcome message that contains the wmii tutorial
.TE

.SS Default Key Bindings
.P
All of the provided \fBwmiirc\fR scripts accept at least the following key
bindings. They should also provide a \fBshowkeys\fR action to open a
key binding quick\-reference.

.SS Moving Around
.TS
tab(^); ll.
 \fBKey\fR^\fBAction\fR
 Mod\-h^Move to a window to the \fIleft\fR of the one currently focused
 Mod\-l^Move to a window to the \fIright\fR of the one currently focused
 Mod\-j^Move to the window \fIbelow\fR the one currently focused
 Mod\-k^Move to a window \fIabove\fR the one currently focused
 Mod\-space^Toggle between the managed and floating layers
 Mod\-t \fI<tag>\fR^Move to the view of the given \fI<tag>\fR
 Mod\-n^Move to the next view
 Mod\-b^Move to the previous view
 Mod\-\fI\fI[0\-9]\fR\fR^Move to the view with the given number
.TE

.SS Moving Things Around
.TS
tab(^); ll.
 \fBKey\fR^\fBAction\fR
 Mod\-Shift\-h^Move the current window \fIwindow\fR to a column on the \fIleft\fR
 Mod\-Shift\-l^Move the current window to a column on the \fIright\fR
 Mod\-Shift\-j^Move the current window below the window beneath it.
 Mod\-Shift\-k^Move the current window above the window above it.
 Mod\-Shift\-space^Toggle the current window between the managed and floating layer
 Mod\-Shift\-t \fI<tag>\fR^Move the current window to the view of the given \fI<tag>\fR
 Mod\-Shift\-\fI\fI[0\-9]\fR\fR^Move the current window to the view with the given number
.TE

.SS Miscellaneous
.TS
tab(^); ll.
 \fBKey\fR^\fBAction\fR
 Mod\-m^Switch the current column to \fImax mode\fR
 Mod\-s^Switch the current column to \fIstack mode\fR
 Mod\-d^Switch the current column to \fIdefault mode\fR
 Mod\-Shift\-c^\fBKill\fR the selected client
 Mod\-p \fI<program>\fR^\fBExecute\fR \fI<program>\fR
 Mod\-a \fI<action>\fR^\fBExecute\fR the named <action
 Mod\-Enter^\fBExecute\fR an \fB@TERMINAL@\fR
.TE

.SH Configuration
.P
If you feel the need to change the default configuration, then
customize (as described above) the \fBwmiirc\fR action.  This
action is executed at the end of the \fBwmii\fR script and does
all the work of setting up the window manager, the key bindings,
the bar labels, etc.

.SS Filesystem
.P
Most aspects of \fBwmii\fR are controlled via the filesystem.  It is
usually accessed via the wmiir(1) command, but it can be
accessed by any 9P, including plan9port's 9P\fI[1]\fR, and can be
mounted natively on Linux via v9fs\fI[1]\fR, and on Inferno (which man
run on top of Linux). All data in the filesystem, including
filenames, is UTF\-8 encoded. However, when accessed via
wmiir(1), text is automatically translated to and from your
locale encoding.

.P
The filesystem is, as are many other 9P filesystems, entirely
synthetic. The files exist only in memory, and are not written
to disk. They are generally initiated on wmii startup via a
script such as wmiirc. Several files are used to issue commands,
others simply act as if they were ordinary files (their contents
are updated and returned exactly as written), though writing
them has side\-effects (such as changing key bindings). A
description of the filesystem layout and control commands
follows.

.SS Hierarchy
.TP
/
Global control files
.TP
/client/\fI*\fR/
Client control files
.TP
/tag/\fI*\fR/
View control files
.TP
/lbar/, /rbar/
Files representing the contents of the bottom bar


.SS The / Hierarchy
.TP
colrules
The \fIcolrules\fR file contains a list of
rules which affect the width of newly created columns.
Rules have the form:

.nf
      /\fI<regex>\fR/ -> \fI<width>\fR\fI[+\fI<width>\fR]\fR*
.fi

Where,

.nf
      \fI<width>\fR := \fI<percent of screen>\fR | \fI<pixels>\fRpx
.fi

When a new column, \fI<n>\fR, is created on a view whose name
matches \fI<regex>\fR, it is given the \fI<n>\fRth supplied \fI<width>\fR.
If there is no \fI<n>\fRth width, it is given 1/\fI<ncol>\fRth of the
screen.

.TP
rules
\fBPROVISIONAL\fR

The \fIrules\fR file contains a list of rules that may be used
to automatically set properties of new clients. Rules are
specified as:

.nf
      /\fI<regex>\fR/ \fI<key>\fR=\fI<value>\fR ...
.fi

where each \fI<key>\fR represents a command in the clients \fIctl\fR
file, and each \fI<value>\fR represents the value to assign to it.
The rules are applied when the client is first started and
the contents of the \fIprops\fR file match the regular
expression \fI<regex>\fR.

Additionally, the following keys are accepted and have
special meaning:

.RS 8
.TP
continue
Normally, when a matching rule is encountered, rule
matching stops. When the continue key is provided
(with any value), matching continues at the next
rule.
.TP
force\-tags=\fI<tags>\fR
Like \fItags\fR, but overrides any settings obtained
obtained from the client's group or from the
\fB_WMII_TAGS\fR window property.
.RS -8
.TP
keys
The \fIkeys\fR file contains a list of keys which
\fBwmii\fR will grab. Whenever these key combinations
are pressed, the string which represents them are
written to '/event' as: Key \fI<string>\fR
.TP
event
The \fIevent\fR file never returns EOF while
\fBwmii\fR is running. It stays open and reports events
as they occur. Included among them are:
.RS 8
.TP
\fI[Not]\fRUrgent \fI<client>\fR \fI[Manager|Client]\fR
\fI<client>\fR's urgent hint has been set or
unset. The second arg is \fI[Client]\fR if it's
been set by the client, and \fI[Manager]\fR if
it's been set by \fBwmii\fR via a control
message.
.TP
\fI[Not]\fRUrgentTag \fI<tag>\fR \fI[Manager|Client]\fR
A client on \fI<tag>\fR has had its urgent hint
set, or the last urgent client has had its
urgent hint unset.
.TP
Client\fI<Click|MouseDown>\fR \fI<client>\fR \fI<button>\fR
A client's titlebar has either been clicked or
has a button pressed over it.
.TP
\fI[Left|Right]\fRBar\fI[Click|MouseDown]\fR \fI<button>\fR \fI<bar>\fR
A left or right bar has been clicked or has a
button pressed over it.
.RS -8

For a more comprehensive list of available events, see
\fIwmii.pdf\fR\fI[2]\fR

.TP
ctl
The \fIctl\fR file takes a number of messages to
change global settings such as color and font, which can
be viewed by reading it. It also takes the following
commands:
.RS 8
.TP
quit
Quit \fBwmii\fR
.TP
exec \fI<prog>\fR
Replace \fBwmii\fR with \fI<prog>\fR
.TP
spawn \fI<prog>\fR
Spawn a new program, as if by the \fI\-r\fR flag.
.RS -8


.SS The /client/ Hierarchy
.P
Each directory under '/client/' represents an X11 client.
Each directory is named for the X window id of the window the
client represents, in the form that most X utilities recognize.
The one exception is the special 'sel' directory, which
represents the currently selected client.

.TP
ctl
When read, the 'ctl' file returns the X window id
of the client. The following commands may be written to
it:
.RS 8
.TP
allow \fI<flags>\fR
The set of unusual actions the client is allowed to
perform, in the same format as the tag set.
.RS 8
.TP
activate
The client is allowed to activate
itself – that is, focus its window and,
as the case may require, uncollapse it
and select a tag it resides on. This
flag must be set on a client if you wish
it able to activate itself from the
system tray.
.RS -8
.TP
floating \fI<on | off | always | never>\fR
Defines whether this client is likely to float when
attached to a new view. Ordinarilly, the value
changes automatically whenever the window is
moved between the floating and managed layers.
However, setting a value of \fIalways\fR or \fInever\fR
overrides this behavior. Additionally, dialogs,
menus, docks, and splash screens will always
float unless this value is set to \fInever\fR.
.TP
fullscreen \fI<on | off | toggle>\fR
Sets the client's fullscreen state.
.TP
group \fI<group id>\fR
The client's group ID, or 0 if not part of a group.
Clients tend to open with the same tags and in the
same columns as the last active member of their
group. Setting this property is only useful when
done via the rules file.
.TP
kill
Close the client's window.
.TP
pid
Read\-only value of the PID of the program that
owns the window, if the value is available and
the process is on the same machine as wmii.
.TP
slay
Forcibly kill the client's connection to the X
server, closing all of its windows. Kill the parent
process if the client's PID is available.
.TP
tags \fI<tags>\fR
The client's tags. The same as the tags file.
.TP
urgent \fI<on | off | toggle>\fR
Set or unset the client's urgent hint.
.RS -8

.TP
label
Set or read a client's label (title).
.TP
props
Returns a clients class and label as:
\fI<instance>\fR:\fI<class>\fR:\fI<label>\fR.
.TP
tags
Set or read a client's tags. Tags are separated by
\fB+\fR, \fB\-\fR, or \fB^\fR. Tags beginning with \fB+\fR are
added, while those beginning with \fB\-\fR are removed and
those beginning with \fB^\fR are toggled.  If the tag
string written begins with \fB+\fR, \fB^\fR, or \fB\-\fR, the
written tags are added to or removed from the client's
set, otherwise the set is overwritten.


.SS The /tag/ Hierarchy
.P
Each directory under '/tag/' represents a view, containing
all of the clients with the given tag applied. The special
\&'sel' directory represents the currently selected tag.

.TP
ctl
The 'ctl' file can be read to retrieve the name
of the tag the directory represents, or written with the
following commands:
.RS 8
.TP
select
Select a client:
select \fI[left|right|up|down]\fR 
.P
select \fI[\fI<row number>\fR|sel]\fR \fI[\fI<frame number>\fR]\fR 
.P
select client \fI<client>\fR
.TP
send
Send a client somewhere:
.RS 8
.TP
send \fI[\fI<client>\fR|sel]\fR \fI[up|down|left|right]\fR
.TP
send \fI[\fI<client>\fR|sel]\fR \fI<area>\fR
Send \fI<client>\fR to the \fIn\fRth \fI<area>\fR
.TP
send \fI[\fI<client>\fR|sel]\fR toggle
Toggle \fI<client>\fR between the floating and managed layer.
.RS -8
.TP
swap
Swap a client with another. Same syntax as send.

.TP
grow
Grow or shrink a client.

.nf
     grow \fI<frame>\fR \fI<direction>\fR \fI[\fI<amount>\fR]\fR
.fi

.TP
nudge
Nudge a client in a given direction.

.nf
     grow \fI<frame>\fR \fI<direction>\fR \fI[\fI<amount>\fR]\fR
.fi

.RS -8
Where the arguments are defined as follows:
.RS 8
.TP
area
Selects a column or the floating area.

.nf
     area        ::= \fI<area_spec>\fR | \fI<screen_spec>\fR:\fI<area_spec>\fR
.fi

When \fI<screen_spec>\fR is omitted and \fI<area_spec>\fR is not "sel",
0 is assumed. "sel" by itself represents the selected client no
matter which screen it is on.

.nf
     area_spec   ::= "~" | \fI<number>\fR | "sel"
.fi

Where "~" represents the floating area and \fI<number>\fR represents a column
index, starting at one.

.nf
     screen_spec ::= \fI<number>\fR
.fi

Where \fI<number>\fR representes the 0\-based Xinerama screen number.

.TP
frame
Selects a client window.

.nf
     frame ::= \fI<area>\fR \fI<index>\fR | \fI<area>\fR sel | client \fI<window-id>\fR
.fi

Where \fI<index>\fR represents the nth frame of \fI<area>\fR or \fI<window\-id>\fR is
the X11 window id of the given client.

.TP
amount
The amount to grow or nudge something.

.nf
     amount ::= \fI<number>\fR | \fI<number>\fRpx
.fi

If "px" is given, \fI<number>\fR is interperated as an exact pixel count.
Otherwise, it's interperated as a "reasonable" amount, which is
usually either the height of a window's title bar, or its sizing
increment (as defined by X11) in a given direction.
.RS -8
.TP
index
Read for a description of the contents of a tag.


.SS The /rbar/, /lbar/ Hierarchy
.P
The files under '/rbar/' and '/lbar/' represent the
items of the bar at the bottom of the screen. Files under
\&'/lbar/' appear on the left side of the bar, while those
under '/rbar/' appear on the right, with the leftmost item
occupying all extra available space. The items are sorted
lexicographically.

.P
The files may be read or written to obtain or alter the colors
and text of the bars. The format is similar to the various \fIctl\fR
files and should be self explanitory.

.SH FILES
.TP
/tmp/ns.\fB$USER\fR.\fB${DISPLAY\fR%.0\fB}\fR/wmii
The wmii socket file which provides a 9P service.
.TP
@GLOBALCONF@
Global action directory.
.TP
@LOCALCONF@
User\-specific action directory. Actions are first searched here.


.SH ENVIRONMENT
.TP
\fB$HOME\fR, \fB$DISPLAY\fR
See the section \fBFILES\fR above.

.P
The following variables are set and exported within \fBwmii\fR and
thus can be used in actions:

.TP
\fB$WMII_ADDRESS\fR
The address on which \fBwmii\fR is listening.
.TP
\fB$WMII_CONFPATH\fR
The path that wmii searches for its configuration
scripts and actions.
.TP
\fB$NAMESPACE\fR
The namespace directory to use if no address is provided.

.SH SEE ALSO
.P
wimenu(1), wmii9menu(1), witray(1), wmiir(1), wihack(1) 
.P
@DOCDIR@/wmii.pdf
@DOCDIR@/FAQ

.P
\fI[1]\fR http://www.suckless.org/wiki/wmii/tips/9p_tips 
.P
\fI[2]\fR @DOCDIR@/wmii.pdf


.\" man code generated by txt2tags 2.5 (http://txt2tags.sf.net)
.\" cmdline: txt2tags -o- wmii.man1

Added man/wmii.man1.






































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
WMII
wmii-@VERSION@
2010 June

%!includeconf: header.t2t

= NAME =

wmii - Window Manager Improved²

= SYNOPSIS =

wmii [-a <address>] [-r <wmiirc>] +
wmii -v

= DESCRIPTION =

== Overview ==

`wmii` is a dynamic window manager for X11. In contrast to
static window management the user rarely has to think about how
to organize windows, no matter what he is doing or how many
applications are used at the same time.  The window manager
adapts to the current environment and fits to the needs of the
user, rather than forcing him to use a preset, fixed layout and
trying to shoehorn all windows and applications into it.

`wmii` supports classic and tiled window management with
extended keyboard and mouse control. Classic window management
arranges windows in a floating layer in which tyen can be moved
and resized freely. Tiled window management arranges windows in
vertical columns.  Each column holds an arbitrary number
arbitrary windows and arranges them vertically in a
non-overlapping manner. They can then be moved and resized,
among and within columns, at will.

`wmii` provides a virtual filesystem which represents the
internal state similar to the procfs of Unix operating systems.
Modifying this virtual filesystem results in changing the state
of the window manager. The virtual filesystem service can be
accessed through 9P-capable client programs, like
wmiir(1).  This allows simple and powerful remote control
of the core window manager.

== Command Line Arguments ==

: -a <address>
        Specifies the address on which `wmii` should listen for
        connections. The address takes the form
        `<protocol>!<address>`. The default is of the form:

```     unix!/tmp/ns.$USER.${DISPLAY%.0}/wmii
        which opens a unix socket per Plan 9 Port conventions. To
        open a TCP socket, listening at port 4332 on the loopback
        interface, use:

```     tcp!localhost!4332
        $WMII_NAMESPACE is automatically set to this value.

: -r <wmiirc>
        Specifies which rc script to run. If <wmiirc> consists of a
        single argument, $WMII_CONFPATH is searched before $PATH.
        Otherwise, it is passed to the shell for evaluation. The
        environment variables $WMII_ADDRESS and $WMII_CONFPATH are
        preset for the script.
:
== Terminology ==

: Display
        A running X server instance consisting of input
        devices and screens.
: Screen
        A physical or virtual (Xinerama or Xnest(1))
        screen of an X display.
: Window
        A (rectangular) drawable X object which is
        displayed on a screen, usually an application window.
: Client
        An application window surrounded by a frame window
        containing a border and a titlebar.
: Floating layer
        A screen layer of `wmii` on top of all other layers,
        where clients are arranged in a classic (floating)
        manner.  They can be resized or moved freely.
: Managed layer
        A screen layer of `wmii` underneath the floating layer,
        where clients are arranged in a non-overlapping
        (managed) manner.  Here, the window manager dynamically
        assigns each client a size and position.  The managed
        layer consists of columns.
: Tag
        Alphanumeric strings which can be assigned to a
        client. This provides a mechanism to group clients with
        similar properties. Clients can have one tag, e.g.
        _work_, or several tags, e.g.  _work+mail_.
        Tags are separated with the _+_ character.
: View
        A set of clients containing a specific tag, quite
        similar to a workspace in other window managers. It
        consists of the floating and managed layers.
: Column
        A column is a screen area which arranges clients
        vertically in a non-overlapping way. Clients can be
        moved and resized between and within columns freely.
: Bar
        The bar at the bottom of the screen displays a label
        for each view and allows the creation of arbitrary
        user-defined labels.
: Event
        An event is a message which can be read from a
        special file in the filesystem of `wmii`, such as a
        mouse button press, a key press, or a message written by
        a different 9P-client.
:

== Basic window management ==

Running a raw `wmii` process without a wmiirc(1) script provides
basic window management capabilities.  However, to use it
effectively, remote control through its filesystem interface is
necessary.  Without such a script, it is only possible to move
and resize clients with the mouse, but not to change their tags
or to switch views.  Other interactions, such as customizing the
style, killing or retagging clients, and grabbing keys, cannot
be achieved without accessing the filesystem.

The filesystem can be accessed by connecting to the //address//
of `wmii` with any 9P-capable client, such as wmiir(1)

== Actions ==

The default configuration provides for a special menu of
actions. These consist of either shell scripts in $WMII_CONFPATH
or action definitions included in wmiirc.

Here is a list of the default actions:

| exec     | Replace the window manager with another program
| quit     | Leave the window manager nicely
| rehash   | Refresh the program list
| showkeys | Display a list of key bindings recognized by wmii
| status   | Periodically print date and load average to the bar
| welcome  | Display a welcome message that contains the wmii tutorial

== Default Key Bindings ==

All of the provided `wmiirc` scripts accept at least the following key
bindings. They should also provide a `showkeys` action to open a
key binding quick-reference.

=== Moving Around ===

|| Key          | Action
| Mod-h         | Move to a window to the _left_ of the one currently focused
| Mod-l         | Move to a window to the _right_ of the one currently focused
| Mod-j         | Move to the window _below_ the one currently focused
| Mod-k         | Move to a window _above_ the one currently focused
| Mod-space     | Toggle between the managed and floating layers
| Mod-t <tag>   | Move to the view of the given <tag>
| Mod-n         | Move to the next view
| Mod-b         | Move to the previous view
| Mod-//[0-9]// | Move to the view with the given number

=== Moving Things Around ===

|| Key                 | Action
|  Mod-Shift-h         | Move the current window _window_ to a column on the _left_
|  Mod-Shift-l         | Move the current window to a column on the _right_
|  Mod-Shift-j         | Move the current window below the window beneath it.
|  Mod-Shift-k         | Move the current window above the window above it.
|  Mod-Shift-space     | Toggle the current window between the managed and floating layer
|  Mod-Shift-t <tag>   | Move the current window to the view of the given <tag>
|  Mod-Shift-//[0-9]// | Move the current window to the view with the given number

=== Miscellaneous ===

|| Key               | Action
|  Mod-m             | Switch the current column to _max mode_
|  Mod-s             | Switch the current column to _stack mode_
|  Mod-d             | Switch the current column to _default mode_
|  Mod-Shift-c       | `Kill` the selected client
|  Mod-p <program>   | `Execute` <program>
|  Mod-a <action>    | `Execute` the named <action
|  Mod-Enter         | `Execute` an `@TERMINAL@`

= Configuration =

If you feel the need to change the default configuration, then
customize (as described above) the `wmiirc` action.  This
action is executed at the end of the `wmii` script and does
all the work of setting up the window manager, the key bindings,
the bar labels, etc.

== Filesystem ==

Most aspects of `wmii` are controlled via the filesystem.  It is
usually accessed via the wmiir(1) command, but it can be
accessed by any ``9P``, including plan9port's 9P[1], and can be
mounted natively on Linux via v9fs[1], and on Inferno (which man
run on top of Linux). All data in the filesystem, including
filenames, is UTF-8 encoded. However, when accessed via
wmiir(1), text is automatically translated to and from your
locale encoding.

The filesystem is, as are many other 9P filesystems, entirely
synthetic. The files exist only in memory, and are not written
to disk. They are generally initiated on wmii startup via a
script such as wmiirc. Several files are used to issue commands,
others simply act as if they were ordinary files (their contents
are updated and returned exactly as written), though writing
them has side-effects (such as changing key bindings). A
description of the filesystem layout and control commands
follows.

== Hierarchy ==

: /
        Global control files
: /client/_*_/
        Client control files
: /tag/_*_/
        View control files
: /lbar/, /rbar/
        Files representing the contents of the bottom bar
:

== The / Hierarchy ==

: colrules
        The _colrules_ file contains a list of
        rules which affect the width of newly created columns.
        Rules have the form:

```     /<regex>/ -> <width>[+<width>]*
        Where,

```     <width> := <percent of screen> | <pixels>px
        When a new column, <n>, is created on a view whose name
        matches <regex>, it is given the <n>th supplied <width>.
        If there is no <n>th width, it is given 1/<ncol>th of the
        screen.

: rules
        PROVISIONAL

        The _rules_ file contains a list of rules that may be used
        to automatically set properties of new clients. Rules are
        specified as:

```     /<regex>/ <key>=<value> ...
        where each <key> represents a command in the clients _ctl_
        file, and each <value> represents the value to assign to it.
        The rules are applied when the client is first started and
        the contents of the _props_ file match the regular
        expression <regex>.

        Additionally, the following keys are accepted and have
        special meaning:

        >>
        : continue
                Normally, when a matching rule is encountered, rule
                matching stops. When the continue key is provided
                (with any value), matching continues at the next
                rule.
        : force-tags=<tags>
                Like _tags_, but overrides any settings obtained
                obtained from the client's group or from the
                **\_WMII_TAGS** window property.
        <<
: keys
        The _keys_ file contains a list of keys which
        `wmii` will grab. Whenever these key combinations
        are pressed, the string which represents them are
        written to '/event' as: Key <string>
: event
        The _event_ file never returns EOF while
        `wmii` is running. It stays open and reports events
        as they occur. Included among them are:
        >>
        : [Not]Urgent <client> [Manager|Client]
                <client>'s urgent hint has been set or
                unset. The second arg is [Client] if it's
                been set by the client, and [Manager] if
                it's been set by `wmii` via a control
                message.
        : [Not]UrgentTag <tag> [Manager|Client]
                A client on <tag> has had its urgent hint
                set, or the last urgent client has had its
                urgent hint unset.
        : Client<Click|MouseDown> <client> <button>
                A client's titlebar has either been clicked or
                has a button pressed over it.
        : [Left|Right]Bar[Click|MouseDown] <button> <bar>
                A left or right bar has been clicked or has a
                button pressed over it.
        :
        <<

        For a more comprehensive list of available events, see
        _wmii.pdf_[2]

: ctl
        The _ctl_ file takes a number of messages to
        change global settings such as color and font, which can
        be viewed by reading it. It also takes the following
        commands:
        >>
        : quit
                Quit `wmii`
        : exec <prog>
                Replace `wmii` with <prog>
        : spawn <prog>
                Spawn a new program, as if by the _-r_ flag.
        :
        <<
:

== The /client/ Hierarchy ==

Each directory under '/client/' represents an X11 client.
Each directory is named for the X window id of the window the
client represents, in the form that most X utilities recognize.
The one exception is the special 'sel' directory, which
represents the currently selected client.

: ctl
        When read, the 'ctl' file returns the X window id
        of the client. The following commands may be written to
        it:
        >>
        : allow <flags>
                The set of unusual actions the client is allowed to
                perform, in the same format as the tag set.
                >>
                : activate
                        The client is allowed to activate
                        itself – that is, focus its window and,
                        as the case may require, uncollapse it
                        and select a tag it resides on. This
                        flag must be set on a client if you wish
                        it able to activate itself from the
                        system tray.
                <<
        : floating <on | off | always | never>
                Defines whether this client is likely to float when
                attached to a new view. Ordinarilly, the value
                changes automatically whenever the window is
                moved between the floating and managed layers.
                However, setting a value of _always_ or _never_
                overrides this behavior. Additionally, dialogs,
                menus, docks, and splash screens will always
                float unless this value is set to _never_.
        : fullscreen <on | off | toggle>
                Sets the client's fullscreen state.
        : group <group id>
                The client's group ID, or 0 if not part of a group.
                Clients tend to open with the same tags and in the
                same columns as the last active member of their
                group. Setting this property is only useful when
                done via the rules file.
        : kill
                Close the client's window.
        : pid
                Read-only value of the PID of the program that
                owns the window, if the value is available and
                the process is on the same machine as wmii.
        : slay
                Forcibly kill the client's connection to the X
                server, closing all of its windows. Kill the parent
                process if the client's PID is available.
        : tags <tags>
                The client's tags. The same as the tags file.
        : urgent <on | off | toggle>
                Set or unset the client's urgent hint.
        <<

: label
        Set or read a client's label (title).
: props
        Returns a clients class and label as:
        <instance>:<class>:<label>.
: tags
        Set or read a client's tags. Tags are separated by
        **+**, **-**, or **^**. Tags beginning with **+** are
        added, while those beginning with **-** are removed and
        those beginning with **^** are toggled.  If the tag
        string written begins with **+**, **^**, or **-**, the
        written tags are added to or removed from the client's
        set, otherwise the set is overwritten.
:

== The /tag/ Hierarchy ==

Each directory under '/tag/' represents a view, containing
all of the clients with the given tag applied. The special
'sel' directory represents the currently selected tag.

: ctl
        The 'ctl' file can be read to retrieve the name
        of the tag the directory represents, or written with the
        following commands:
        >>
        : select
                Select a client:
                    select [left|right|up|down] +
                    select [<row number>|sel] [<frame number>] +
                    select client <client>
        : send
                Send a client somewhere:
                >>
                : send [<client>|sel] [up|down|left|right]
                : send [<client>|sel] <area>
                    Send <client> to the _n_th <area>
                : send [<client>|sel] toggle
                    Toggle <client> between the floating and managed layer.
                <<
        : swap
                Swap a client with another. Same syntax as send.

        : grow
                Grow or shrink a client.

```    grow <frame> <direction> [<amount>]
        : nudge
                Nudge a client in a given direction.

```    grow <frame> <direction> [<amount>]
        :
        <<
        Where the arguments are defined as follows:
        >>
        : area
            Selects a column or the floating area.

```    area        ::= <area_spec> | <screen_spec>:<area_spec>
            When <screen_spec> is omitted and <area_spec> is not "sel",
            0 is assumed. "sel" by itself represents the selected client no
            matter which screen it is on.

```    area_spec   ::= "~" | <number> | "sel"
            Where "~" represents the floating area and <number> represents a column
            index, starting at one.

```    screen_spec ::= <number>
            Where <number> representes the 0-based Xinerama screen number.

        : frame
            Selects a client window.

```    frame ::= <area> <index> | <area> sel | client <window-id>
            Where <index> represents the nth frame of <area> or <window-id> is
            the X11 window id of the given client.

        : amount
            The amount to grow or nudge something.

```    amount ::= <number> | <number>px
            If "px" is given, <number> is interperated as an exact pixel count.
            Otherwise, it's interperated as a "reasonable" amount, which is
            usually either the height of a window's title bar, or its sizing
            increment (as defined by X11) in a given direction.
        <<
: index
        Read for a description of the contents of a tag.
:


== The /rbar/, /lbar/ Hierarchy ==

The files under '/rbar/' and '/lbar/' represent the
items of the bar at the bottom of the screen. Files under
'/lbar/' appear on the left side of the bar, while those
under '/rbar/' appear on the right, with the leftmost item
occupying all extra available space. The items are sorted
lexicographically.

The files may be read or written to obtain or alter the colors
and text of the bars. The format is similar to the various _ctl_
files and should be self explanitory.

= FILES =

: /tmp/ns.$USER.${DISPLAY%.0}/wmii
        The wmii socket file which provides a 9P service.
: @GLOBALCONF@
        Global action directory.
: @LOCALCONF@
        User-specific action directory. Actions are first searched here.
:

= ENVIRONMENT =

: $HOME, $DISPLAY
        See the section **FILES** above.
:
The following variables are set and exported within `wmii` and
thus can be used in actions:

: $WMII_ADDRESS
        The address on which `wmii` is listening.
: $WMII_CONFPATH
        The path that wmii searches for its configuration
        scripts and actions.
: $NAMESPACE
        The namespace directory to use if no address is provided.
:
= SEE ALSO =
wimenu(1), wmii9menu(1), witray(1), wmiir(1), wihack(1) +
@DOCDIR@/wmii.pdf
@DOCDIR@/FAQ

[1] http://www.suckless.org/wiki/wmii/tips/9p_tips +
[2] @DOCDIR@/wmii.pdf

Added man/wmii9menu.1.




































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
.TH "WMII9MENU" 1 "Oct, 2009" "wmii-@VERSION@"

.SH NAME
.P
wmii9menu \- The wmii menu program

.SH SYNOPSIS
.P
wmii9menu \fI[\-a \fI<address>\fR]\fR \fI[\-i \fI<initial>\fR]\fR \fI<item>\fR\fI[:\fI<command>\fR]\fR...
wmii9menu \-v

.SH DESCRIPTION
.P
\fBwmii9menu\fR is \fBwmii\fR's standard clickable menu program. It's used
extensively by \fBwmii\fR and related programs to display clickable
menus, namely for window titlebars and bar items. The name, along
with the code, derives from the 9menu program, which in turn derives
its name from Plan 9's style of clickable menus.

.SH ARGUMENTS
.TP
\-a
The address at which to connect to \fBwmii\fR.
.TP
\-i \fI<initial>\fR

.RS
If \fI<initial>\fR is listed among the other items on the command
line, it is selected at startup, and the menu is positioned
so that the mouse pointer is centered on said item.
.RE
.P
:

.SH USAGE
.P
\fBwmii9menu\fR is invoked with a list of arguments, each of which is
displayed as a menu item. The first \fI:\fR in the item name, and any
text following it, is stripped. The menu is opened such that the
mouse pointer is centered on the selected item. If a mouse button is
depressed when the menu opens, then releasing it will confirm the
selection. Otherwise, a mouse press will do the same. When a
selection is made, \fBwmii9menu\fR prints the result. If the selected
item initially contained a \fI:\fR, the text following it is printed.
Otherwise, the item text itself is printed.

.SH ENVIRONMENT
.TP
\fB$WMII_ADDRESS\fR
The address at which to connect to wmii.
.TP
\fB$NAMESPACE\fR
The namespace directory to use if no address is
provided.

.SH SEE ALSO
.P
wmii(1), wmiir(1), wimenu(1)

.P
\fI[1]\fR http://www.suckless.org/wiki/wmii/tips/9p_tips


.\" man code generated by txt2tags 2.5 (http://txt2tags.sf.net)
.\" cmdline: txt2tags -o- wmii9menu.man1

Added man/wmii9menu.man1.




















































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
WMII9MENU
wmii-@VERSION@
Oct, 2009

%!includeconf: header.t2t

= NAME =

wmii9menu - The wmii menu program

= SYNOPSIS =

wmii9menu [-a <address>] [-i <initial>] <item>[:<command>]...
wmii9menu -v

= DESCRIPTION =

`wmii9menu` is `wmii`'s standard clickable menu program. It's used
extensively by `wmii` and related programs to display clickable
menus, namely for window titlebars and bar items. The name, along
with the code, derives from the 9menu program, which in turn derives
its name from Plan 9's style of clickable menus.

= ARGUMENTS =

: -a
        The address at which to connect to `wmii`.
: -i <initial>
	If <initial> is listed among the other items on the command
	line, it is selected at startup, and the menu is positioned
	so that the mouse pointer is centered on said item.
:

= USAGE =

`wmii9menu` is invoked with a list of arguments, each of which is
displayed as a menu item. The first _:_ in the item name, and any
text following it, is stripped. The menu is opened such that the
mouse pointer is centered on the selected item. If a mouse button is
depressed when the menu opens, then releasing it will confirm the
selection. Otherwise, a mouse press will do the same. When a
selection is made, `wmii9menu` prints the result. If the selected
item initially contained a _:_, the text following it is printed.
Otherwise, the item text itself is printed.

= ENVIRONMENT =

: $WMII_ADDRESS
        The address at which to connect to wmii.
: $NAMESPACE
        The namespace directory to use if no address is
        provided.
:
= SEE ALSO =
wmii(1), wmiir(1), wimenu(1)

[1] http://www.suckless.org/wiki/wmii/tips/9p_tips

Added man/wmiir.1.


















































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
.TH "wmiir" 1 "Oct, 2009" "wmii-@VERSION@"

.SH NAME
.P
wmiir \- The wmii 9P filesystem client

.SH SYNOPSIS
.P
wmiir \fI[\-a \fI<address>\fR]\fR \fI[\-b]\fR {create | ls \fI[\-dlp]\fR | read | remove | write} \fI<file>\fR 
.P
wmiir \fI[\-a \fI<address>\fR]\fR \fI[\-b]\fR xwrite \fI<file>\fR \fI<data>\fR ... 
.P
wmiir \-v

.SH DESCRIPTION
.P
\fBwmiir\fR is a simple 9P filesystem client which ships with \fBwmii\fR, and connects
to its virtual filesystem by default. \fBwmiir\fR is most often used to query and
issue commands to \fBwmii\fR, both from the command line and from its \fBsh\fR\-based
configuration scripts.

.P
Since the default encoding of 9P filesystems is UTF\-8, \fBwmiir\fR
assumes that all data read and written is text data and
translates to or from your locale character encoding as
necessary. When working with non\-text data in a non\-UTF\-8
locale, the \fI\-b\fR flag should be specified to disable this
behavior.

.SH ARGUMENTS
.TP
\-a
The address at which to connect to \fBwmii\fR.
.TP
\-b

.RS
With the \fI\-b\fR flag, data that you intend to read or
write is treated as binary data.
.RE
.P
:

.SH COMMANDS
.P
The following commands deal with 9P filesystems.

.TP
create \fI<file>\fR
Creates a new file or directory in the filesystem. Permissions and
file type are inferred by \fBwmii\fR. The contents of the standard input
are written to the new file.
.TP
ls \fI[\-dlp]\fR \fI<path>\fR
Lists the contents of \fI<path>\fR.

Flags:
.RS 8
.TP
\-d
Don't list the contents of directories.
.TP
\-l
Long output. For each file, list its permissions, owner,
group, size (bytes), mtime, and name.
.TP
\-p
Print the full path to each file.
.RS -8
.TP
read \fI<file>\fR
Reads the entire contents of a file from the filesystem. Blocks until
interrupted or EOF is received.

Synonyms: \fBcat\fR
.TP
remove \fI<path>\fR
Removes \fI<path>\fR from the filesystem.

Synonyms: \fBrm\fR
.TP
write \fI<file>\fR
Writes the contents of the standard input to \fI<file>\fR.
.TP
xwrite \fI<file>\fR \fI<data>\fR ...
Writes each argument after \fI<file>\fR to the latter.


.P
Additionally, wmiir provides the following utility commands relevant
to scripting wmii:

.TP
namespace

.RS
Prints the current wmii namespace directory, usually
equivalent to /tmp/ns.\fB$USER\fR.\fB${DISPLAY\fR%.0\fB}\fR, but possibly
different depending on the value of \fB$NAMESPACE\fR and
\fB$WMII_NAMESPACE\fR.
.RE

.RS
Synonyms: \fBns\fR
.RE
.TP
setsid \fI[\-0 \fI<argv0>\fR]\fR \fI[\-f]\fR \fI<command>\fR

.RS
Executes the given command after setting the session id (see
setsid(2)). If \fI\-0\fR is given, the command is run with the
given value as argv\fI[0]\fR. For instance, to run sh as a login
shell, one might run
.RE

.nf
         wmiir setsid -0 -sh sh
.fi

.RS
If \fI\-f\fR is given, wmiir will fork into the background before
executing the command.
.RE
.TP
proglist \fI[\-\-]\fR \fI<directory>\fR ...

.RS
Lists all executable commands in the given directories.
.RE

.SH ENVIRONMENT
.TP
\fB$WMII_ADDRESS\fR
The address at which to connect to wmii.
.TP
\fB$NAMESPACE\fR
The namespace directory to use if no address is
provided.


.SH SEE ALSO
.P
wmii(1), libixp\fI[2]\fR

.P
\fI[1]\fR http://www.suckless.org/wiki/wmii/tips/9p_tips 
.P
\fI[2]\fR http://libs.suckless.org/libixp


.\" man code generated by txt2tags 2.5 (http://txt2tags.sf.net)
.\" cmdline: txt2tags -o- wmiir.man1

Added man/wmiir.man1.






























































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
wmiir
wmii-@VERSION@
Oct, 2009

%!includeconf: header.t2t

= NAME =

wmiir - The wmii 9P filesystem client

= SYNOPSIS =

wmiir [-a <address>] [-b] {create | ls [-dlp] | read | remove | write} <file> +
wmiir [-a <address>] [-b] xwrite <file> <data> ... +
wmiir -v

= DESCRIPTION =

`wmiir` is a simple 9P filesystem client which ships with `wmii`, and connects
to its virtual filesystem by default. `wmiir` is most often used to query and
issue commands to `wmii`, both from the command line and from its `sh`-based
configuration scripts.

Since the default encoding of 9P filesystems is UTF-8, `wmiir`
assumes that all data read and written is text data and
translates to or from your locale character encoding as
necessary. When working with non-text data in a non-UTF-8
locale, the _-b_ flag should be specified to disable this
behavior.

= ARGUMENTS =

: -a
        The address at which to connect to `wmii`.
: -b
	With the _-b_ flag, data that you intend to read or
	write is treated as binary data.
:
= COMMANDS =

The following commands deal with 9P filesystems.

: create <file>
        Creates a new file or directory in the filesystem. Permissions and
        file type are inferred by `wmii`. The contents of the standard input
        are written to the new file.
: ls [-dlp] <path>
        Lists the contents of <path>.

        Flags:
        >>
        : -d
                Don't list the contents of directories.
        : -l
                Long output. For each file, list its permissions, owner,
                group, size (bytes), mtime, and name.
        : -p
                Print the full path to each file.
        <<
: read <file>
        Reads the entire contents of a file from the filesystem. Blocks until
        interrupted or EOF is received.

        Synonyms: `cat`
: remove <path>
        Removes <path> from the filesystem.

        Synonyms: `rm`
: write <file>
        Writes the contents of the standard input to <file>.
: xwrite <file> <data> ...
        Writes each argument after <file> to the latter.
:

Additionally, wmiir provides the following utility commands relevant
to scripting wmii:

: namespace
	Prints the current wmii namespace directory, usually
	equivalent to /tmp/ns.$USER.${DISPLAY%.0}, but possibly
	different depending on the value of $NAMESPACE and
	$WMII_NAMESPACE.

	Synonyms: `ns`
: setsid [-0 <argv0>] [-f] <command>
	Executes the given command after setting the session id (see
	setsid(2)). If _-0_ is given, the command is run with the
	given value as argv[0]. For instance, to run sh as a login
	shell, one might run

```        wmiir setsid -0 -sh sh
	If _-f_ is given, wmiir will fork into the background before
	executing the command.
: proglist [--] <directory> ...
	Lists all executable commands in the given directories.

= ENVIRONMENT =

: $WMII_ADDRESS
        The address at which to connect to wmii.
: $NAMESPACE
        The namespace directory to use if no address is
        provided.
:

= SEE ALSO =
wmii(1), libixp[2]

[1] http://www.suckless.org/wiki/wmii/tips/9p_tips +
[2] http://libs.suckless.org/libixp

Added mk/common.mk.
































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
all:

install: all simpleinstall
uninstall: simpleuninstall

DOCDIR = $(DOC)
simpleinstall:
	for f in $(DOCS); do \
		$(INSTALL) 0644 $$f $(DOCDIR) $$f; \
	done
	for f in $(TEXT); do \
		$(INSTALL) 0644 $$f $(DIR) $$f; \
	done
	for f in $(BINARY); do \
		$(INSTALL) -b 0644 $$f $(DIR) $$f; \
	done
	for f in $(EXECS); do \
		$(INSTALL) 0755 $$f $(DIR) $$f; \
	done

simpleuninstall:
	for f in $(DOCS); do \
		$(UNINSTALL) $$f $(DOCDIR) $$f; \
	done
	for f in $(TEXT); do \
		$(UNINSTALL) $$f $(DIR) $$f; \
	done
	for f in $(BINARY); do \
		$(UNINSTALL) -b $$f $(DIR) $$f; \
	done
	for f in $(EXECS); do \
		$(UNINSTALL) -b $$f $(DIR) $$f; \
	done

cleandep:
	echo CLEANDEP
	rm .depend 2>/dev/null || true

tags:
	files=; \
	for f in $(OBJ); do \
		[ -f "$$f.c" ] && files="$$files $$f.c"; \
	done; \
	echo CTAGS $$files $(TAGFILES); \
	if [ -n "$$files" ]; then $(DEBUG) $(CTAGS) $$files $(TAGFILES); fi

.PHONY: all options clean dist install uninstall depend cleandep tags
.PHONY: simpleuninstall simpleinstall
Added mk/dir.mk.






































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
MKSUBDIR = \
	set -e;							\
	targ=$@; targ=$${targ\#d};				\
	for i in $$dirs; do					\
		export $(SUBMAKE_EXPORT) BASE=$(BASE)$$i/;	\
		if [ ! -d $$i ]; then				\
			echo Skipping nonexistent directory: $$i 1>&2;	\
		else						\
			echo MAKE $$targ $$BASE;		\
			(cd $$i && $(MAKE) $$targ) || exit $?;	\
		fi;						\
	done

dall:
	+dirs="$(DIRS)"; $(MKSUBDIR)
dclean:
	+dirs="$(DIRS)"; $(MKSUBDIR)
dinstall:
	+dirs="$(INSTDIRS)"; $(MKSUBDIR)
duninstall:
	+dirs="$(INSTDIRS)"; $(MKSUBDIR)
ddepend:
	+dirs="$(DIRS)"; $(MKSUBDIR)
dtags:
	+dirs="$(DIRS)"; $(MKSUBDIR)

all: dall
clean: dclean
install: dinstall
uninstall: duninstall
depend: ddepend
tags: dtags

INSTDIRS = $(DIRS)

Added mk/gcc.mk.
























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
DEBUGCFLAGS = \
	-g \
	-O0 \
	-fno-builtin \
	-fno-inline \
	-fno-omit-frame-pointer \
	-fno-optimize-sibling-calls \
	-fno-unroll-loops
CFLAGS += \
	-std=c99 \
	-pedantic \
	-pipe \
	-fno-strict-aliasing \
	-Wall \
	-Wimplicit \
	-Wmissing-prototypes \
	-Wno-comment \
	-Wno-missing-braces \
	-Wno-parentheses \
	-Wno-sign-compare \
	-Wno-switch \
	-Wpointer-arith \
	-Wreturn-type \
	-Wstrict-prototypes \
	-Wtrigraphs
MKDEP = cpp -M
SOCFLAGS += -fPIC

Added mk/hdr.mk.












































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
DIR =
DIRS =
DOC =
DOCDIR =
DOCS =
EXECS =
HFILES =
INCLUDES =
LIB =
LIBS =
OBJ =
OFILES =
OFILES_PIC =
PACKAGES =
PROG =
SO =
TAGFILES =
TARG =
TEXT =

FILTER = cat

EXCFLAGS = $(INCLUDES) -D_XOPEN_SOURCE=600

COMPILE_FLAGS = $(EXCFLAGS) $(CFLAGS)
COMPILE    = $(SHELL) $(ROOT)/util/compile "$(CC)" "$(PACKAGES)" "$(COMPILE_FLAGS)"
COMPILEPIC = $(SHELL) $(ROOT)/util/compile "$(CC)" "$(PACKAGES)" "$(COMPILE_FLAGS) $(SOCFLAGS)"

LINK   = $(SHELL) $(ROOT)/util/link "$(LD)" "$(PACKAGES)" "$(LIBS) $(LDFLAGS)"
LINKSO = $(SHELL) $(ROOT)/util/link "$(LD)" "$(PACKAGES)" "$(SOLDFLAGS) $(LIBS) $(SHARED)"

CLEANNAME=$(SHELL) $(ROOT)/util/cleanname

SOEXT=so
TAGFILES=

CTAGS=ctags --fields=+S --c-kinds=+px

PACKAGES = 

# Try to find a sane shell. /bin/sh is a last resort, because it's
# usually bash on Linux, which means it's painfully slow.
SHELLSEARCH = for sh in /bin/dash /bin/ksh /bin/sh; do \
	      if test -x $$sh; then echo $$sh; exit; fi; done

BINSH:= $(shell $(SHELLSEARCH))
BINSH!= $(SHELLSEARCH)
SHELL := $(BINSH)
.SHELL: name=sh path=$(SHELL)

all:

include $(ROOT)/config.mk
sinclude $(ROOT)/config.local.mk
sinclude $(shell echo .)depend

.SILENT:
.SUFFIXES: .$(SOEXT) .1 .3 .awk .build .c .clean .depend .install .man1 .man3 .o .o_pic .out .pdf .py .rc .sh .uninstall

.c.depend:
	echo MKDEP $<
	$(DEBUG) eval "$(MKDEP) $(COMPILE_FLAGS)" $< | sed '1s|.*:|$(<:%.c=%.o):|' >>.depend

.sh.depend .rc.depend .1.depend .3.depend .awk.depend:
	:

.c.o:
	$(COMPILE) $@ $<
.c.o_pic:
	$(COMPILEPIC) $@ $<

.o.out:
	$(LINK) $@ $<
.c.out:
	$(COMPILE) $(<:.c=.o) $<
	$(LINK) $@ $(<:.c=.o)

.rc.out .awk.out .sh.out:
	echo FILTER $(BASE)$<
	[ -n "$(<:%.sh=)" ] || $(BINSH) -n $<
	set -e; \
	$(DEBUG) $(FILTER) $< >$@; \
	$(DEBUG) chmod 0755 $@

.man1.1 .man3.3:
	echo TXT2TAGS $(BASE)$<
	$(DEBUG) txt2tags -o- $< >$@

DEBUG = _debug() { [ -n "$$noisycc" ] && echo >&2 $$@ || true; "$$@"; }; _debug

INSTALL= _install() { set -e; \
		 dashb=$$1; [ $$1 = -b ] && shift; \
		 d=$(DESTDIR)$$3; f=$$d/$$(basename $$4); \
		 if [ ! -d $$d ]; then echo MKDIR $$3; mkdir -p $$d; fi; \
		 echo INSTALL $$($(CLEANNAME) $(BASE)$$2); \
		 $(DEBUG) rm -f $$f; \
		 if [ "$$dashb" = -b ]; \
		 then $(DEBUG) cp -f $$2 $$f; \
		 else $(DEBUG) $(FILTER) <$$2 >$$f; \
		 fi; \
		 $(DEBUG) chmod $$1 $$f; \
	 }; _install
UNINSTALL= _uninstall() { set -e; \
	           echo UNINSTALL $$($(CLEANNAME) $(BASE)$$1); \
		   $(DEBUG) rm -f $(DESTDIR)$$2/$$(basename $$3); \
	   }; _uninstall

.out.install:
	$(INSTALL) -b 0755 $< $(BIN) $*
.out.uninstall:
	$(UNINSTALL) $< $(BIN) $*

.a.install .$(SOEXT).install:
	$(INSTALL) -b 0644 $< $(LIBDIR) $<
.a.uninstall .$(SOEXT).uninstall:
	$(UNINSTALL) $< $(LIBDIR) $<

.h.install:
	$(INSTALL) 0644 $< $(INCLUDE) $<
.h.uninstall:
	$(UNINSTALL) $< $(INCLUDE) $<

.pdf.install:
	$(INSTALL) -b 0644 $< $(DOC) $<
.pdf.uninstall:
	$(UNINSTALL) $< $(DOC) $<

INSTALMAN=   _installman()   { man=$${1\#\#*.}; $(INSTALL) 0644 $$1 $(MAN)/man$$man $$1; }; _installman
UNINSTALLMAN=_uninstallman() { man=$${1\#\#*.}; $(UNINSTALL) $$1 $(MAN)/man$$man $$1; }; _uninstallman
MANSECTIONS=1 2 3 4 5 6 7 8 9
$(MANSECTIONS:%=.%.install):
	$(INSTALMAN) $<
$(MANSECTIONS:%=.%.uninstall):
	$(UNINSTALLMAN) $<

.out.clean:
	echo CLEAN $$($(CLEANNAME) $(BASE)$<)
	rm -f $< || true 2>/dev/null
	rm -f $*.o || true 2>/dev/null
.o.clean .o_pic.clean:
	echo CLEAN $$($(CLEANNAME) $(BASE)$<)
	rm -f $< || true 2>/dev/null

printinstall:
clean:
install: printinstall
depend: cleandep

include $(ROOT)/mk/common.mk

Added mk/lib.mk.
































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
PTARG = $(ROOT)/lib/$(TARG)
LIB = $(PTARG).a
OFILES = $(OBJ:=.o)

all: $(HFILES) $(LIB)

install: $(PTARG).install
uninstall: $(PTARG).uninstall
clean: libclean
depend: $(OBJ:=.depend)

libclean:
	for i in $(LIB) $(OFILES); do \
		[ -e $$i ] && \
		echo CLEAN $$($(CLEANNAME) $(BASE)$$i); \
		rm -f $$i; \
	done 2>/dev/null || true

printinstall:
	echo 'Install directories:'
	echo '	Lib: $(LIBDIR)'

$(LIB): $(OFILES)
	echo AR $$($(CLEANNAME) $(BASE)/$@)
	mkdir $(ROOT)/lib 2>/dev/null || true
	$(AR) $@ $(OFILES)

SOMKSH=case "$(MAKESO)" in 1|[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]) echo $(ROOT)/mk/so.mk;; *) echo /dev/null;; esac
SOMK:=$(shell $(SOMKSH))
SOMK!=$(SOMKSH)
include $(SOMK)

Added mk/man.mk.


























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

targ = for k in $(MANPAGES); do echo $$k | sed 's/ .*//'; done
TARG:= $(shell $(targ))
TARG!= $(targ)

all: $(TARG)
install: $(TARG:.1=.install) $(TARG:.3=.install) maninstall
uninstall: $(TARG:.1=.uninstall) $(TARG:.3=.uninstall) manuninstall

.PHONY: maninstall manuninstall

MANLOOP = \
	set -ef; \
	for k in $(MANPAGES); do \
		set -- $$k; \
		real=$$1; shift; \
		for targ; do \
			_ $$real $(MAN)/man$${real\#\#*.}/$$targ; \
		done; \
	done
maninstall:
	_() { echo LN $$1 $${2##*/}; ln -sf $$1 $(DESTDIR)$$2; }; $(MANLOOP)
manuninstall:
	_() { echo RM $${2##*/}; rm -f $(DESTDIR)$$2; }; $(MANLOOP)

printinstall:
	echo 'Install directories:'
	echo '	Man: $(MAN)'

Added mk/many.mk.








































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
PROGS = $(TARG:=.out)

all: $(OFILES) $(PROGS)

install: $(TARG:=.install)
uninstall: $(TARG:=.uninstall)
depend: $(OFILES:.o=.depend) $(TARG:=.depend)
clean: manyclean

printinstall:
	echo 'Install directories:'
	echo '	Bin: $(BIN)'

manyclean:
	for i in $(TARG:=.o) $(TARG:=.out) $(OFILES); do \
		[ -e $$i ] && \
		echo CLEAN $$($(CLEANNAME) $(BASE)$$i); \
		rm -f $$i; \
	done 2>/dev/null || true

Added mk/one.mk.




















































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
PROG = $(TARG).out
OFILES = $(OBJ:=.o)

all: $(PROG)

install: $(TARG).install
uninstall: $(TARG).uninstall
clean: oneclean
depend: $(OBJ:=.depend)

printinstall:
	echo 'Install directories:'
	echo '	Bin: $(BIN)'

oneclean:
	for i in $(PROG) $(OFILES); do \
		[ -e $$i ] && \
		echo CLEAN $$($(CLEANNAME) $(BASE)$$i); \
		rm -f $$i; \
	done 2>/dev/null || true

$(OFILES): $(HFILES)

$(PROG): $(OFILES) $(LIB)
	$(LINK) $@ $(OFILES) $(LIB)

Added mk/python.mk.




























>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# all: pybuild
install: $(PYMODULES:%=%.install)
clean: pyclean

.py.install:
	echo PYTHON install $* $(PYPREFIX)
	DESTDIR=$(DESTDIR); \
	$(DEBUG) $(PYTHON) $< install -c --root=$${DESTDIR:-/} $(PYPREFIX)
pyclean:
	echo CLEAN build/
	rm -rf build

.PHONY: pybuild pyclean
Added mk/so.mk.


























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
SOPTARG = $(ROOT)/lib/$(TARG)
SO = $(SOPTARG).$(SOEXT)
SONAME = $(TARG).$(SOEXT)
OFILES_PIC = $(OBJ:=.o_pic)

all: $(HFILES) $(SO)

install: $(SOPTARG).install
uninstall: $(SOPTARG).uninstall
clean: soclean
depend: $(OBJ:=.depend)

soclean:
	for i in $(SO) $(OFILES_PIC); do \
		[ -e $$i ] && \
		echo CLEAN $$($(CLEANNAME) $(BASE)$$i); \
		rm -f $$i; \
	done 2>/dev/null || true

printsoinstall:
	echo 'Install directories:'
	echo '	Lib: $(LIBDIR)'

printinstall: printsoinstall

$(SO): $(OFILES_PIC)
	mkdir $(ROOT)/lib 2>/dev/null || true
	$(LINKSO) $@ $(OFILES_PIC)

Added mk/wmii.mk.
















































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

VERS = hg$$(hg identify -n)
VERS = $$(test -n "$$WMII_HGVERSION" && echo $$WMII_HGVERSION || \
          echo -n "hg$$(hg id -n 2>/dev/null)")

WMII_HGVERSION = $(VERS)
WMII_HGVERSION:= $(shell echo $(VERS))
WMII_HGVERSION!= echo $(VERS)

VERSION = $(WMII_HGVERSION)
COPYRIGHT = ©2010 Kris Maglione

CONFDIR = wmii-hg
LOCALCONF = ~/.$(CONFDIR)
GLOBALCONF = $(ETC)/$(CONFDIR)

.MAKE.EXPORTED += WMII_HGVERSION
SUBMAKE_EXPORT = WMII_HGVERSION=$(WMII_HGVERSION)

LIBS9 = $(ROOT)/lib/libstuff.a $(ROOT)/lib/libregexp9.a $(ROOT)/lib/libbio.a $(ROOT)/lib/libfmt.a $(ROOT)/lib/libutf.a

CFLAGS += '-DVERSION=\"$(VERSION)\"' '-DCOPYRIGHT=\"$(COPYRIGHT)\"' \
	  '-DCONFDIR=\"$(CONFDIR)\"' '-DCONFPREFIX=\"$(ETC)\"' \
	  '-DLOCALCONF=\"$(LOCALCONF)\"' '-DGLOBALCONF=\"$(GLOBALCONF)\"' \
	  -DIXP_NEEDAPI=129

FILTER = sed "s|@ALTDOC@|$(DOC)/alternative_wmiircs|g; \
	      s|@BINSH@|$(BINSH)|g; \
	      s|@CONFDIR@|$(CONFDIR)|g; \
	      s|@CONFPREFIX@|$(ETC)|g; \
	      s|@DOCDIR@|$(DOC)|g; \
	      s|@EXAMPLES@|$(DOC)/examples|g; \
	      s|@GLOBALCONF@|$(GLOBALCONF)|g; \
	      s|@LIBDIR@|$(LIBDIR)|g; \
	      s|@LOCALCONF@|$(LOCALCONF)|g; \
	      s|@PYTHON@|$(PYTHON)|g; \
	      s|@TERMINAL@|$(TERMINAL)|g; \
	      s|@VERSION@|$(VERSION)|g; \
	      /^@@/d;"

Added rc/Makefile.


















>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
ROOT=..
include $(ROOT)/mk/hdr.mk
include $(ROOT)/mk/wmii.mk

BIN = $(GLOBALCONF)
TARG =  wmiirc    \
	welcome

include $(ROOT)/mk/many.mk
Added rc/sh.wmii.


































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
#!sh
# WMII Configuration
load std string regex expr echo

argv0 = $0

#mount -Aa {os dial unix!/tmp/ns.kris.:1/wmii >[1=0]} / || raise mount
#mount -Aa {styxmon {os rc -c 'dial $WMII_ADDRESS' >[1=0]}} / || raise mount
mount -Aa {os rc -c 'dial $WMII_ADDRESS' >[1=0]} / || raise mount

{`{read} && echo halt>/dev/sysctl}&

MODKEY=Mod1
UP=k
DOWN=j
LEFT=h
RIGHT=l

WMII_FONT=fixed
WMII_NORMCOLORS=('#222222' '#5FBF77' '#2A7F3F')
WMII_FOCUSCOLORS=('#ffffff' '#153F1F' '#2A7F3F')
WMII_BACKGROUND='#333333'

WMII_TERM=urxvt

fn wmiimenu {
	(nf nb nil sf sb nil) := ($WMII_NORMCOLORS $WMII_FOCUSCOLORS)
	os -d ${hostenv HOME} (
		dmenu -b -fn $WMII_FONT
		-nf $nf -nb $nb -sf $sf -sb $sb)
}

fn 9menu {
	os -d ${hostenv HOME} (
		wmii9menu -font ${hd ${split , $WMII_FONT}}
		-^(nf nb br)^$WMII_NORMCOLORS
		-^(sf sb br)^$WMII_FOCUSCOLORS $*)
}

# Column Rules
echo '/./ -> 60+40' >/colrules

# Tagging Rules
echo '
/Firefox/ -> www
/XMMS.*/ -> ~
/MPlayer.*/ -> ~
/.*/ -> sel
' >/tagrules

subfn seq {
	result=${expr $* seq}
}

subfn hostenv {
	arg := $"*
	result="{os rc -c 'echo -n $'^$arg </dev/null}
}

subfn ftl {
	result=${tl $*}
	result=$"result
}

subfn lines {
	ifs := "{echo}
	arg := $*
	result = `{$arg}
}

fn 'fn?' {
	args := $*
	~ ("{rescue '*' {} {whatis $args >[2]/dev/null}}
	   'load std; fn '*)
}

fn ifx {
	(pred fn val args) := $*
	if {$pred $val} {$fn $val $args}
}

fn dofn {
	ifx 'fn?' {$*} $*
}

fn run_command {
	os -b -d ${hostenv HOME} $* &
}

fn dprint {
	arg := $*
	or {~ $#debug 0} {~ $debug '' 0} { echo $arg }
}

subfn config_whatis {
	result=${lines {os rc -c 'PATH=$WMII_CONFPATH which $*' $* </dev/null} $*}
}

# Status Bar Info
fn status {
	echo ${re mg '[0-9]+\.[0-9]+' "{os uptime}} '|' `{date}
}

for(i in Key Event Action) {
	'{fn $i { fn '$i'-$1 ${tl $*} }}'
}

# Events
Event Start {
	if {~ $1 wmiirc} {
		rm -f $progs_file
		exit
	}
}

Event Key {
	dprint Key-$1
	Key-$1 $1
}

Event CreateTag { echo $WMII_NORMCOLORS $* > /lbar/$"* }
Event DestroyTag { rm /lbar/$"* }
Event FocusTag { echo $WMII_FOCUSCOLORS $* > /lbar/$"* }
Event UnfocusTag { echo $WMII_NORMCOLORS $* > /lbar/$"* }
Event UrgentTag { echo '*'${ftl $*} > /lbar/${ftl $*} }
Event NotUrgentTag { echo ${tl $*} > /lbar/${ftl $*} }

Event LeftBarClick {
	(button name) := $*
	if {~ $button 1} { echo view $name >/ctl }
}
Event LeftBarMouseDown {
	(button name) := $*
	if {~ $button 3} { echo view "{9menu ${lines read_tags}} >/ctl & }
}
lastcmd=''
Event ClientMouseDown {
	(client button) := $*
	if {~ $button 3} {
		lastcmd = `{9menu -initial $lastcmd Nop Delete Fullscreen}
		if{~ $#lastcmd 0} {lastcmd=''}
		cmp := {~ $lastcmd $*}
		if {$cmp Nop} {
		} {$cmp Delete} { echo kill >/client/$client/ctl
		} {$cmp Fullscreen} { echo Fullscreen toggle >/client/$client/ctl
		}
	}
}

# Actions
Action quit { echo quit >>/ctl }
Action rehash {
	flag x -
	proglist ${hostenv PATH} >$progs_file
}
Action status {
	flag x -
	if {rm /rbar/status >[2]/dev/null} { sleep 1 }
	echo $WMII_NORMCOLORS >/rbar/status
	while {status >/rbar/status} { sleep 1 }
}

ifx {ftest -x $*} {run $*} $home/.wmii-3.5/sh.wmii.local
fn Key { ifx {! 'fn?' $*} {fn $*} Key-$1 ${tl $*} }

fn Action {
	(action args) := $*
	or {dofn Action-$action $args} {
		ifx {! ~ $#* 0} {run_command $*} ${config_whatis $action} $args
	}
}

# Key Bindings
Key $MODKEY-Control-t {
	if { ~ `{wc -l /keys} 0 1} {
		initkeys
		echo grabmod $MODKEY >/ctl
	} {
		echo $MODKEY-Control-t >/keys
		echo grabmod Mod3 >/ctl
	}
}

Key $MODKEY-$LEFT { echo select left >/tag/sel/ctl }
Key $MODKEY-$RIGHT { echo select right >/tag/sel/ctl }
Key $MODKEY-$UP { echo select up >/tag/sel/ctl }
Key $MODKEY-$DOWN { echo select down >/tag/sel/ctl }

Key $MODKEY-Shift-$LEFT { echo send sel left >/tag/sel/ctl }
Key $MODKEY-Shift-$RIGHT { echo send sel right >/tag/sel/ctl }
Key $MODKEY-Shift-$DOWN { echo send sel down >/tag/sel/ctl }
Key $MODKEY-Shift-$UP { echo send sel up >/tag/sel/ctl }

Key $MODKEY-space { echo select toggle >/tag/sel/ctl }
Key $MODKEY-Shift-space { echo send sel toggle >/tag/sel/ctl }

Key $MODKEY-d { echo colmode sel default >/tag/sel/ctl }
Key $MODKEY-s { echo colmode sel stack >/tag/sel/ctl }
Key $MODKEY-m { echo colmode sel max >/tag/sel/ctl }

Key $MODKEY-f { echo Fullscreen toggle >/client/sel/ctl }

Key $MODKEY-Shift-c { echo kill >/client/sel/ctl }

Key $MODKEY-a { Action `{actionlist | wmiimenu} & }
Key $MODKEY-p { run_command rc -c "{wmiimenu <$progs_file} & }
Key $MODKEY-Return { run_command $WMII_TERM & }
Key $MODKEY-t { echo view `{read_tags | wmiimenu} >/ctl & }
Key $MODKEY-Shift-t {
	sel := "{cat /client/sel/ctl}
	read_tags | wmiimenu >/client/$sel/tags
}

Key $MODKEY-^${seq 0 9} { echo view ${tl ${splitr $1 -}} >/ctl }
Key Shift-$MODKEY-${seq 0 9} { echo ${tl ${splitr $1 -}} >/client/sel/tags}

# Functions
fn proglist {
	os find ${split : $"*} -maxdepth 1 -type f </dev/null | sed 's,.*/,,' | sort | uniq
	#for(d in /n/local^${split : $"*}) {
	#	fs filter {mode -d} $d
	#} | sed 's,.*/,,' | sort | uniq
}

fn getfuns {
	ls -p /env | sed -n 's/^fn-' ^ $1 ^ '-//p'
}

fn actionlist {
	{	rescue '*' {} {
			proglist ${hostenv WMII_CONFPATH}
		}
	 	getfuns Action
	} | sort | uniq
}

fn initkeys {
	getfuns Key >/keys
}

fn read_tags {
	ls -p /tag | grep -v '^sel$'
}

# WM Configuration
{
	echo grabmod $MODKEY
	echo border 2
	echo font $WMII_FONT
	echo focuscolors $WMII_FOCUSCOLORS
	echo normcolors $WMII_NORMCOLORS
} >/ctl

# Misc Setup
os xsetroot -solid $WMII_BACKGROUND </dev/null

dofn Local-Overrides

Action status &
progs_file=/tmp/proglist.${pid}
Action rehash &

# Tag Bar Setup
seltag=${lines sed 1q /tag/sel/ctl}
comm -13 ${pipe from {read_tags}} ${pipe from {ls -p /lbar/*}} |
	getlines { rm /lbar/$line }
read_tags | getlines {
	if {~ $line $seltag} {
		echo $WMII_FOCUSCOLORS $line
	} {
		echo $WMII_NORMCOLORS $line
	} >/lbar/$line
}

# Keygrab Setup
initkeys

echo Start wmiirc >/event

# Event Loop
getlines {
	(event args) := ${split ' 	' $line}
	dprint Event-$event: $args
	rescue '*' { dprint Exception: $exception } {
		dofn Event-$event $args
	} </dev/null
	dprint loop
} </event

Added rc/welcome.sh.


























































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#!/bin/sh
# display a welcome message that contains the wmii tutorial

xmessage -file - <<'EOF'
Welcome to wmii, the non-wimp environment of the Suckless Project.

This is a small step by step tutorial, intended to make you a
little bit familiar with wmii. For a more detailed walkthrough,
see @DOCDIR@/wmii.pdf.

From here on, keypresses will be described such that M-a refers to
pressing your modifier and a at the same time. The default modifier
key, hereafter $MODKEY, is the Windows(R) key, but it may also be Alt.

Let's go!

 * Start two @TERMINAL@s by pressing M-Return twice.
 * Switch between the three windows: M-j, M-k,
   M*h, M-l
   If you prefer to use the mouse, then just move the pointer to
   the desired window.
 * Try the other column modes: M-s for stack mode,
   M*m for max mode Press M-d to return to default
   mode.
 * Create a new column with: M-Shift-l
   This moves the client rightwards.
 * Tag the selected client with another tag: M-Shift-2
   IMPORTANT: before you do the next step, note that you
     can select the current tag with M-1.
 * Select the new tag: M-2
 * Select the floating area: M-Space
 * Open the programs menu: M-p
   Type 'xclock' and press Enter.
 * Move the xclock window: Hold $MODKEY, left-click on the
   window and move the cursor around.
 * Resize the xclock window: Hold $MODKEY, right-click the
   window and move the cursor around.
 * Kill the selected client (the xclock window) with: M-Shift-c
 * Open the actions menu: M-a
   Show the list of key bindings by selecting 'showkeys'
 * We'll now have a look at the internal filesystem used by
   wmii.  Executing
   	wmiir ls /
   in the shell of the terminal will list all the files in the
   root directory.
   Entries ending with / are directories.
   If you are curious, you can now dig deeper into the
   directory trees. For instance,
   	wmiir ls /rbar/
   will show you the content of the right half of the bar.

We hope that these steps gave you an idea of how wmii works.
You can reread them at any time by pressing M-a and
selecting 'welcome'.

You should now take a look at the wmii(1) man page.  A FAQ is
available at <http://wmii.suckless.org>.

Further documentation, including alternative configuration
possibilities, is provided in @DOCDIR@.
EOF
Added rc/wmiirc.sh.














































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
#!@BINSH@ -f
# Configure wmii
wmiiscript=wmiirc # For wmii.sh
. wmii.sh


# Configuration Variables
MODKEY=Mod4
UP=k
DOWN=j
LEFT=h
RIGHT=l

# Bars
noticetimeout=5
noticebar=/rbar/!notice

# Colors tuples: "<text> <background> <border>"
export WMII_NORMCOLORS='#000000 #c1c48b #81654f'
export WMII_FOCUSCOLORS='#000000 #81654f #000000'

export WMII_BACKGROUND='#333333'
export WMII_FONT='-*-fixed-medium-r-*-*-13-*-*-*-*-*-*-*'

set -- $(echo $WMII_NORMCOLORS $WMII_FOCUSCOLORS)
export WMII_TERM="@TERMINAL@"

# Ask about MODKEY on first run
    if ! test -d "${WMII_CONFPATH%%:*}"; then
        mkdir "${WMII_CONFPATH%%:*}"
        res=$(wihack -type DIALOG xmessage -nearmouse -buttons Windows,Alt -print -fn $WMII_FONT \
              "Welcome to wmii,$wi_newline$wi_newline" \
              "Most of wmii's default key bindings make use of the$wi_newline" \
              "Windows key, or equivalent. For keyboards lacking such$wi_newline" \
              "a key, many users change this to the Alt key.$wi_newline$wi_newline" \
              "Which would you prefer?")
        [ "$res" = "Alt" ] && MODKEY=Mod1
        echo "MODKEY=$MODKEY" >"${WMII_CONFPATH%%:*}/wmiirc_local"
        chmod +x "${WMII_CONFPATH%%:*}/wmiirc_local"
    fi

# Menu history
hist="${WMII_CONFPATH%%:*}/history"
histnum=5000

# Column Rules
wmiir write /colrules <<!
    /gimp/ -> 17+83+41
    /.*/ -> 62+38 # Golden Ratio
!

# Tagging Rules
wmiir write /rules <<!
    # Apps with system tray icons like to their main windows
    # Give them permission.
    /^Pidgin:/ allow=+activate

    # MPlayer and VLC don't float by default, but should.
    /MPlayer|VLC/ floating=on

    # ROX puts all of its windows in the same group, so they open
    # with the same tags.  Disable grouping for ROX Filer.
    /^ROX-Filer:/ group=0
!

# Status Bar Info
status() {
	echo -n label $(uptime | sed 's/.*://; s/, / /g') '|' $(date)
}

# Generic overridable startup details
startup() { witray & }

wi_runconf -s wmiirc_local
startup

echo colors $WMII_NORMCOLORS | wmiir create $noticebar

# Event processing
wi_events <<'!'
# Events
Event CreateTag
	echo colors "$WMII_NORMCOLORS$wi_newline" label "$@" | wmiir create "/lbar/$@"
Event DestroyTag
	wmiir remove "/lbar/$@"
Event FocusTag
	wmiir xwrite "/lbar/$@" colors "$WMII_FOCUSCOLORS"
Event UnfocusTag
	wmiir xwrite "/lbar/$@" colors "$WMII_NORMCOLORS"
Event UrgentTag
	shift
	wmiir xwrite "/lbar/$@" label "*$@"
Event NotUrgentTag
	shift
	wmiir xwrite "/lbar/$@" label "$@"
Event LeftBarClick LeftBarDND
	shift
	wmiir xwrite /ctl view "$@"
Event Unresponsive
	{
		client=$1; shift
		msg="The following client is not responding. What would you like to do?$wi_newline"
		resp=$(wihack -transient $client \
			      xmessage -nearmouse -buttons Kill,Wait -print \
			      -fn "${WMII_FONT%%,*}" "$msg $(wmiir read /client/sel/label)")
		if [ "$resp" = Kill ]; then
			wmiir xwrite /client/$client/ctl slay &
		fi
	}&
Event Notice
	wmiir xwrite $noticebar label $wi_arg

	kill $xpid 2>/dev/null # Let's hope this isn't reused...
	{ sleep $noticetimeout; wmiir xwrite $noticebar label; }&
	xpid = $!

# Menus
Menu Client-3-Delete
	wmiir xwrite /client/$1/ctl kill
Menu Client-3-Kill
	wmiir xwrite /client/$1/ctl slay
Menu Client-3-Fullscreen
	wmiir xwrite /client/$1/ctl Fullscreen on
Event ClientMouseDown
	wi_fnmenu Client $2 $1 &

Menu LBar-3-Delete
	tag=$1; clients=$(wmiir read "/tag/$tag/index" | awk '/[^#]/{print $2}')
	for c in $clients; do
		if [ "$tag" = "$(wmiir read /client/$c/tags)" ]
		then wmiir xwrite /client/$c/ctl kill
		else wmiir xwrite /client/$c/ctl tags -$tag
		fi
		[ "$tag" = "$(wi_seltag)" ] &&
			wmiir xwrite /ctl view $(wi_tags | wi_nexttag)
	done
Event LeftBarMouseDown
	wi_fnmenu LBar "$@" &

# Actions
Action showkeys
	echo "$KeysHelp" | xmessage -file - -fn ${WMII_FONT%%,*}
Action quit
	wmiir xwrite /ctl quit
Action exec
	wmiir xwrite /ctl exec "$@"
Action rehash
	wi_proglist $PATH >$progsfile
Action status
	set +xv
	if wmiir remove /rbar/status 2>/dev/null; then
		sleep 2
	fi
	echo colors "$WMII_NORMCOLORS" | wmiir create /rbar/status
	while status | wmiir write /rbar/status; do
		sleep 1
	done

# Key Bindings
KeyGroup Moving around
Key $MODKEY-$LEFT   # Select the client to the left
	wmiir xwrite /tag/sel/ctl select left
Key $MODKEY-$RIGHT  # Select the client to the right
	wmiir xwrite /tag/sel/ctl select right
Key $MODKEY-$UP     # Select the client above
	wmiir xwrite /tag/sel/ctl select up
Key $MODKEY-$DOWN   # Select the client below
	wmiir xwrite /tag/sel/ctl select down

Key $MODKEY-space   # Toggle between floating and managed layers
	wmiir xwrite /tag/sel/ctl select toggle

KeyGroup Moving through stacks
Key $MODKEY-Control-$UP    # Select the stack above
	wmiir xwrite /tag/sel/ctl select up stack
Key $MODKEY-Control-$DOWN  # Select the stack below
	wmiir xwrite /tag/sel/ctl select down stack

KeyGroup Moving clients around
Key $MODKEY-Shift-$LEFT   # Move selected client to the left
	wmiir xwrite /tag/sel/ctl send sel left
Key $MODKEY-Shift-$RIGHT  # Move selected client to the right
	wmiir xwrite /tag/sel/ctl send sel right
Key $MODKEY-Shift-$UP     # Move selected client up
	wmiir xwrite /tag/sel/ctl send sel up
Key $MODKEY-Shift-$DOWN   # Move selected client down
	wmiir xwrite /tag/sel/ctl send sel down

Key $MODKEY-Shift-space   # Toggle selected client between floating and managed layers
	wmiir xwrite /tag/sel/ctl send sel toggle

KeyGroup Client actions
Key $MODKEY-f # Toggle selected client's fullsceen state
	wmiir xwrite /client/sel/ctl Fullscreen toggle
Key $MODKEY-Shift-c # Close client
	wmiir xwrite /client/sel/ctl kill

KeyGroup Changing column modes
Key $MODKEY-d # Set column to default mode
	wmiir xwrite /tag/sel/ctl colmode sel default-max
Key $MODKEY-s # Set column to stack mode
	wmiir xwrite /tag/sel/ctl colmode sel stack-max
Key $MODKEY-m # Set column to max mode
	wmiir xwrite /tag/sel/ctl colmode sel stack+max

KeyGroup Running programs
Key $MODKEY-a      # Open wmii actions menu
	action $(wi_actions | wimenu -h "${hist}.actions" -n $histnum) &
Key $MODKEY-p      # Open program menu
	eval wmiir setsid "$(wimenu -h "${hist}.progs" -n $histnum <$progsfile)" &

Key $MODKEY-Return # Launch a terminal
	eval wmiir setsid $WMII_TERM &

KeyGroup Other
Key $MODKEY-Control-t # Toggle all other key bindings
	case $(wmiir read /keys | wc -l | tr -d ' \t\n') in
	0|1)
		echo -n "$Keys" | wmiir write /keys
		wmiir xwrite /ctl grabmod $MODKEY;;
	*)
		wmiir xwrite /keys $MODKEY-Control-t
		wmiir xwrite /ctl grabmod Mod3;;
	esac

KeyGroup Tag actions
Key $MODKEY-t       # Change to another tag
	wmiir xwrite /ctl view $(wi_tags | wimenu -h "${hist}.tags" -n 50) &
Key $MODKEY-Shift-t # Retag the selected client
	# Assumes left-to-right order of evaluation
	wmiir xwrite /client/$(wi_selclient)/ctl tags $(wi_tags | wimenu -h "${hist}.tags" -n 50) &
Key $MODKEY-n	    # Move to the next tag
	wmiir xwrite /ctl view $(wi_tags | wi_nexttag)
Key $MODKEY-b	    # Move to the previous tag
	wmiir xwrite /ctl view $(wi_tags | sort -r | wi_nexttag)
!
	for i in 0 1 2 3 4 5 6 7 8 9; do
		wi_events <<!
Key $MODKEY-$i		 # Move to the numbered view
	wmiir xwrite /ctl view "$i"
Key $MODKEY-Shift-$i     # Retag selected client with the numbered tag
	wmiir xwrite /client/sel/ctl tags "$i"
!
done
wi_events -e

# WM Configuration
wmiir write /ctl <<!
	font $WMII_FONT
	focuscolors $WMII_FOCUSCOLORS
	normcolors $WMII_NORMCOLORS
	grabmod $MODKEY
	border 1
!
xsetroot -solid "$WMII_BACKGROUND" &

# Misc
progsfile="$(wmiir namespace)/.proglist"
action status &
wi_proglist $PATH >$progsfile &

# Setup Tag Bar
IFS="$wi_newline"
wmiir rm $(wmiir ls -p /lbar) >/dev/null
seltag=$(wmiir read /tag/sel/ctl | sed 1q)
unset IFS
wi_tags | while read tag
do
	if [ "$tag" = "$seltag" ]; then
		echo colors "$WMII_FOCUSCOLORS"
		echo label $tag
	else
		echo colors "$WMII_NORMCOLORS"
		echo label $tag
	fi | wmiir create "/lbar/$tag"
done

wi_eventloop

Added test/Makefile.




























>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
ROOT=..
include $(ROOT)/mk/hdr.mk

TARG =	grav

OFILES = ../cmd/util.o     \
	 ../cmd/wmii/map.o \
	 ../cmd/wmii/x11.o

LDFLAGS += $(OFILES) -lfmt -lutf -lbio $(LIBX11) -lXext
CFLAGS += $(INCX11)

include $(ROOT)/mk/many.mk

Added test/event.






































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/bin/rc

wm=''
if(~ $1 -d) {
	wm = '&wm/wm wm/logon</dev/null'
	shift
}

inferno '{$home/wmii/test/event.dis $*'$wm'}' $* &
inf = $apid

. 9.rc

fn sigint sigterm {exit}
fn sigexit {/bin/kill $apid}

while(! ~ `{read </dev/tty} q)
	true

Added test/event.b.








































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
implement Event;

include "sys.m";
	sys: Sys;
	OREAD, OWRITE: import Sys;
	print, sprint, read, open: import sys;
include "draw.m";
include "string.m";
	str: String;
include "bufio.m";
	bufio: Bufio;
	Iobuf: import bufio;
include "lists.m";
	lists: Lists;
	append, reverse: import lists;
include "regex.m";
	regex: Regex;
	Re: import regex;
include "sh.m";
	sh: Sh;

Event: module
{
	init: fn(nil: ref Draw->Context, argv: list of string);
};

line: chan of string;

suicide()
{
	fd := open(sprint("/proc/%d/pgrp", sys->pctl(0, nil)), OWRITE);
	sys->fprint(fd, "kill");
}

buflines(in, out: chan of string)
{
	lines: list of string;
	for(;;) {
		if(lines == nil)
			lines = <-in :: nil;
		alt {
		l := <-in =>
			lines = append(lines, l);
		out <-= hd lines =>
			if(hd lines == nil)
				suicide();
			lines = tl lines;
		}
	}
}

readlines(c: chan of string, fd: ref sys->FD)
{
	out := chan of string;

	spawn buflines(out, c);

	b := bufio->fopen(fd, OREAD);
	while((s := b.gets('\n')) != nil)
		out <-= s;
	out <-= nil;
}

readfile(file: string): (string, int)
{
	fd := open(file, OREAD);
	if(fd == nil)
		return ("", 0);

	ret := "";
	buf := array[512] of byte;
	while((n := read(fd, buf, len buf)) > 0)
		ret += string buf[:n];
	return (ret, 1);
}

ishex(s: string): int
{
	if(len s < 3 || s[0:2] != "0x")
		return 0;
	s = s[2:];
	(nil, end) := str->toint(s, 16);
	return end == nil;
}

init(draw: ref Draw->Context, argv: list of string)
{
	sys = load Sys Sys->PATH;
	str = load String String->PATH;
	bufio = load Bufio Bufio->PATH;
	lists = load Lists "/dis/lib/lists.dis";
	regex = load Regex Regex->PATH;
	sh = load Sh Sh->PATH;

	sys->pctl(sys->NEWPGRP, nil);

	sh->system(draw, "mount -A {os rc -c 'exec dial $WMII_ADDRESS' >[1=0]} /mnt/wmii &");

	line = chan of string;
	spawn readlines(line, sys->fildes(0));

	relist: list of ref (Re, int);

	argv = tl argv;
	for(; argv != nil; argv = tl argv) {
		vflag := 0;
		if(hd argv == "-v") {
			argv = tl argv;
			vflag = 1;
		}
		(re, err) := regex->compile(hd argv, 0);
		if(err != nil)
			raise sprint("bad regex %q: %s", hd argv, err);
		relist = ref (re, vflag) :: relist;
	}

	relist = reverse(relist);

line:	for(;;) {
		lin := <-line;
		if(lin == nil)
			break;
		l := str->unquoted(lin);
		if(l == nil)
			continue;

		(evn, end) := str->toint(hd l, 10);
		if(end == nil) {
			for(rel := relist; rel != nil; rel = tl relist) {
				(re, vflag) := *(hd rel);
				match := regex->execute(re, lin);
				if((match == nil) != vflag)
					continue line;
			}
			print("%s", lin);
			for(; l != nil; l = tl l) {
				(k, v) := str->splitstrr(hd l, "=");
				if(ishex(v)) {
					(name, ok) := readfile(sprint("/mnt/wmii/client/%s/props", v));
					if(ok)
						print("%d	%s%s\n", evn, k, name);
				}
			}
		}else
			print("%s", lin);
	}
}

Added test/grav.c.
































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#if 0
	set -e
	name=grav
	root=..
	obj=$root/cmd
	lib=$root/lib
	inc=$root/include
	cc -I$inc -I/usr/local/include \
		-o o.$name \
		-Wall \
		$name.c \
		$obj/util.o \
		$obj/wmii/map.o \
		$obj/wmii/x11.o \
		-L$lib -lfmt -lutf -lbio \
		-L/usr/local/lib -lX11 -lXext \

	exec o.$name
#endif
#include <fmt.h>
#include <stdarg.h>
#include <stdbool.h>
#include <unistd.h>
#include <stuff/util.h>
#include <stuff/x11.h>

char buffer[8196];
void debug() {}

static Window*	win;

static char*	gravity[] = {
	[NorthEastGravity] = "NorthEastGravity",
	[NorthWestGravity] = "NorthWestGravity",
	[SouthEastGravity] = "SouthEastGravity",
	[SouthWestGravity] = "SouthWestGravity",
	[StaticGravity]    = "StaticGravity",
};

static void
draw(Window *w) {
	Rectangle r;

	r = w->r;
	r = rectsubpt(r, r.min);

	fill(w, r, 0UL);
	border(w, Rect(3, 3, 97, 97), 2, ~0UL);
	border(w, Rect(8, 8, 92, 92), 2, ~0UL);
	sync();
}

static void
setgravity(Window *w, long gravity) {
	XSizeHints wmh;

	wmh.flags = PWinGravity;
	wmh.win_gravity = gravity;
	XSetWMNormalHints(display, w->w, &wmh);
}

static void
config(Window *w, long grav, Point p) {
	Rectangle r;

	r = rectsetorigin(Rect(0, 0, 100, 100), p);

	print("%s: %R\n", gravity[grav], r);

	setgravity(w, grav);
	w->r = ZR; /* Kludge. */
	reshapewin(w, r);
	draw(w);
	sleep(1);
}

int
main(void) {
	XSizeHints wmh;
	WinAttr wa;
	XEvent ev;

	initdisplay();

	/* Kludge the bar height. */
	scr.rect.max.y -= 14;

	wa.background_pixmap = ParentRelative;
	wa.event_mask = ExposureMask|StructureNotifyMask;
	win = createwindow(&scr.root,
				Rect(0, 0, 100, 100), scr.depth, InputOutput,
				&wa, CWEventMask | CWBackPixmap);
	XSelectInput(display, win->w, ExposureMask);

	wmh.flags = PMinSize|PMaxSize|USPosition;
	wmh.min_width = wmh.max_width = 100;
	wmh.min_height = wmh.max_height = 100;
	XSetWMNormalHints(display, win->w, &wmh);

	mapwin(win);
	raisewin(win);
	XMaskEvent(display, ExposureMask, &ev);

	draw(win);
	sleep(2);

	config(win, StaticGravity, Pt(0, 0));

	config(win, NorthWestGravity,
		    Pt(0,
		       0));

	config(win, NorthEastGravity,
		    Pt(Dx(scr.rect) - 100,
		       0));

	config(win, SouthEastGravity,
		    Pt(Dx(scr.rect) - 100,
		       Dy(scr.rect) - 100));

	config(win, SouthWestGravity,
		    Pt(0,
		       Dy(scr.rect) - 100));

	sleep(1);
	return 0;
}

Added test/mkfile.






















>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
default:V: all

TARG=\
	event.dis\

all:V: $TARG

nuke:V: rm *.dis *.sbl

%.dis:	%.b
	limbo -gw $stem.b
Added util/cleanname.




































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/bin/sh -f

echo "$@" |
	awk -F'/+' '{
		delete a
		n = 0
		for(i = 1; i <= NF; i++) {
			if($i == ".." && n > 0 && a[n] != "..")
				n--
			else if($i != "" && $i != ".")
				a[++n] = $i
		}
		s = ""
		for(i = 1; i <= n; i++)
			s = s "/" a[i]
		print substr(s, 2)
	}'

Added util/compile.


























































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#!/bin/sh -f

CC=$1
PACKAGES=$2
CFLAGS=$3; shift 3

[ -n "$PACKAGES" ] && CFLAGS="$CFLAGS $(pkg-config --cflags $PACKAGES)"

outfile="$1"; shift
bin="$(echo $0 | sed 's,/[^/]*$,,')"
xtmp=/tmp/cc.$$.$USER.out

# Derived from Russ Cox's 9c in plan9port.

echo CC $($bin/cleanname ${BASE}$outfile)
[ -n "$noisycc" ] && echo $CC -o $outfile $CFLAGS $@
eval '$CC -o $outfile '"$CFLAGS"' $@ >$xtmp 2>&1'
status=$?
[ $status -eq 0 ] || echo $CC -o $outfile $CFLAGS $@ >&2

base=$(echo $BASE | sed 's/,/\\,/g')
re='\([^[:space:]/][^[:space:]]*\..:[0-9]\)'

undup() { # GCC is crap.
	awk '
	function shift() {
		for(n=1; n<=3; n++)
			if(2*n <= nl)
			for(i=1; i<=n; i++) {
				if(l[i] != l[i+n])
					break;
				if(i == n) {
					for(i=1; i<=n; i++)
						print l[i]
					nl -= 2*n;
					for(i=1; i<=nl; i++)
						l[i] = l[i+2*n];
					return;
				}
			}
		if(nl == 0)
			return
		print l[1]
		for(i=1; i<nl; i++)
			l[i] = l[i+1]
		nl--
	}
	BEGIN{
		nl=0
		maxl=6
	}
	tolower($0) ~ /: (error|note): .?each undeclared identifier|: error: for each function it appears|is dangerous, better use|is almost always misused|: in function |: at top level:|support .long long.|use of c99 long long|iso c forbids conversion|warning:.*warn_unused_result/ {
		next
	}
	$1 == "warning:" {
		t = $2 " " $1
		sub(/^[^ ]+ [^ ]+ /, "")
		$0 = t " " $0
	}
	{
		sub(/\[/, ": [", $1)
		if(nl == maxl)
			shift()
		l[++nl] = $0
	}
	END{
		while(nl > 0)
			shift();
	}'
}

cat $xtmp | sed "s,^$re,$base&,g; s,\([[:space:]]\)$re,\1$base\2,g" |
	sed 's/ .first use in this function.$//; s/\"\([^\"][^\"]*\)\", line \([0-9][0-9]*\)/\1:\2/g' |
	undup >&2
rm -f $xtmp
exit $status

Added util/genchangelog.




























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#!/bin/sh
set -ef

ifs="$(echo)"
name=$1
vers=$2
dist=$3
desc="Upstream build"
auth="Kris Maglione <kris@suckless.org>"
date=$(date +'%a, %d %b %Y %T %z')
if hg root >/dev/null 2>&1; then
	t() { hg log -r . --template "{$@}"; }
	vers=$(t rev)
	desc=$(t desc)
	auth=$(t author)
	date=$(t 'date|rfc822date')
fi

cat >debian/changelog.new  <<!
$name ($vers) $dist; urgency=low

  * $desc

 -- $auth  $date

$(cat debian/changelog)
!

mv debian/changelog.new debian/changelog

Added util/genconfig.
























































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
#!/bin/sh -f

# What I wouldn't do for rc...
# Well... it's better than m4shgmake.

CONFIG="$ROOT/config.mk"
CONFSTR='# Generated by "make config"'

# ==================== The Messy Part ====================

#XXX Ignores lines ending in \
parseconfig() {
	cat <<'!' | sed "s/CONFSTR/$CONFSTR/"
	/^CONFSTR/ {exit}
	/^( *#|	)/ {next}
	function fixup() {
		sub(/ #.*/, "")
		sub(/^ */, "")
		gsub(/'/, "'\"'\"'")
		sub(/[	 ]*\+?=[	 ]*/, "='")
		sub(/[	]*$/, "'")
		var = $0
		val = $0
		sub(/\+?=.*/, "", var)
		sub(/^[^=]+=/, "", val)
	}
	/\+=/ && !/^	/ {
		fixup()
		if(!set[var]) {
			print var"=''"
			append[var]++
		}
		print var"=\"$" var " \"" val
		print var"_orig=\"$" var "\""
		next
	}
	/=/ && !/^	/ {
		fixup()
		delete append[var]
		set[var]++

		print var"="val
		print var"_orig=\"$" var "\""
	}
	END{
		for(v in set)
			print v "_append=''"
		for(v in append)
			print v "_append=true"
	}
!
}

findinc() {
	var="$1"; file="$2"; shift 2
	for d in "$@"; do
		if [ -d "$d" -a -f "$d/$file" ]; then
			eval "$var=\"-I$d\""
			break; fi; done
}
soext=.so
aext=.a
findlib() {
	var="$1"; lib="$2"; shift 2
	for d in "$@"; do
		if [ -d "$d" -a -f "$d/$lib.so" -o -f "$d/lib$lib.a" ]; then
			_libdir="$d"; _libname="$lib"
			eval "$var=\"-L$d -l$lib\""
			break; fi; done
}

expand() {
	_expand="$@"; _expand_old=''
	while [ "$_expand" != "$_expand_old" ]; do
		_expand_old="$_expand"
		eval "_expand=\"$_expand\""; done
	echo -n "$_expand"
}

cfg() {
	CFG="$CFG
$@"
}

equals() {
	if [ -z "$append" ]; then
		echo -n "=";
	else
		echo -n "+="; fi
}

prompt() {
	var=$1; shift
	eval "def=\$$var; orig=\$${var}_orig; append=\$${var}_append"

	unset val
	if [ -z "$def" -o -n "$force" ]; then
		echo "$@"
		echo -n "$var[$def]$(equals) "
		read val
		echo
	fi

	if [ -z "$val" ]; then
		val="$def"; fi
	if [ "$(expand $val)" != "$(expand "$orig")" ]; then
		cfg "$var$(equals)$val"; fi
}

# The messy sed... Turns $(VAR) into ${VAR}, but not $$(VAR) into $${VAR}
eval "$(sed 's/\([^$]\)\$(\([A-Za-z_]*\))/\1${\2}/g' <"$CONFIG" | awk "`parseconfig`")"
CFG="$(sed -n "/^$CONFSTR/q; p" "$CONFIG"; echo "$CONFSTR")"

# ==================== The Fun Part ====================

cat <<!
Configuring for the wmii build.

You'll be prompted for a number of settings which wmii needs to build,
install, and run properly. In most cases, the defaults will be sufficient,
in which case, you may simply press return to accept them.

!

# Guess...
AWKPATH=$(which awk 2>/dev/null)

prompt AWKPATH "Full path to your system's 'awk' program"
prompt PLAN9 "Path of a Plan 9 Port or 9base installation"

force=1
prompt PREFIX Installation prefix

echo
echo "Compilation details (if you don't understand these, just leave the defaults)"
echo
prompt CC C object compiler
prompt LD 'Linker (this should normally not be "ld")'

set -- $CC
if $1 -v 2>&1 | grep 'gcc version' >/dev/null; then
	echo -n You appear to be using gcc. Is this correct? '[yes] '
	while :; do
		read resp
		case "$resp" in
		[Yy][Ee][Ss]|'')
			cfg 'include $(ROOT)/mk/gcc.mk'
			break;;
		[Nn][Oo])
			cfg 'CFLAGS=""'
			# Not perfect. Botches system cflags, but we
			# need to ditch the gcc ones.
			break;;
		*)
			echo -n 'Please answer yes or no: '
		esac
	done
	echo
fi

prompt INCPATH Search path for include files
prompt LIBS Libraries to be linked with every executable

prompt CFLAGS Flags for the C compiler
prompt LDFLAGS Flags for the linker
case $(uname -s) in
	[Dd]arwin) cfg 'STATIC=""';;
	*) prompt STATIC Extra linker flags to produce a static executable;;
esac
unset force

# Make some guesses

# Extract the -L paths from ldflags.
ldlibs="$(awk 'BEGIN{
		for(i=1; i <= ARGC; i++)
			if(ARGV[i] ~ /-L/)
				print ":" substr(ARGV[i], 3)
	}' $LDFLAGS)"
# Turn include paths into ../lib paths.
inclibs="$(expand "$INCPATH"|sed 's,/include:,/lib:,g; s,/include$,/lib,')"
# Lace them all together, and expand them.
libs="$(expand "$LIBDIR:$ldlibs:$inclibs:/usr/local/lib:/opt/local/lib")"

LIBIXP=${LIBIXP%/libixp.a}
INCPATH="$INCPATH:/usr/local/include:/opt/local/include"
incpath="$(expand "$INCPATH")"

oifs="$IFS"; IFS=:
findinc INCX11 X11/Xlib.h $incpath \
	/usr/X11R6/include /usr/x11/include /usr/x11/include /usr/X11/include \
	/usr/openwin/include /opt/x11/include /opt/X11/include
findinc INCICONV iconv.h $incpath

findlib LIBX11 X11 $libs \
	/usr/X11R6/lib /usr/X11/lib /usr/openwin/lib /usr/x11/lib \
	/opt/X11 /opt/x11 /usr/local/lib /opt/local/lib
findlib LIBICONV iconv $libs

if [ -d "$ROOT/libixp" ]; then
	_libdir="$ROOT/lib"; else
	soext=.a findlib LIBIXP ixp "$ROOT/lib" $libs; fi
LIBIXP="$_libdir/libixp.a"
IFS="$oifs"

# Back to prompting
echo Library paths...
prompt INCX11 Compiler flags to find X11 includes
prompt LIBX11 Linker flags to link against libX11

# Find out if the system libc includes iconv...
#   GNU systems tend to make /usr/lib/libc.so or /lib/libc.so
# linker scripts rather than libraries, so I can't parse them.
# As a kludge, I ask ldd what actual libc.so /bin/sh links to,
# which is pretty reliable.  It's very unlikely that anyone will
# actually be prompted for this.
#   I suppose the auto*hell way would be to just compile
# something against libc. Perhaps another day.
if nm -D $(ldd /bin/sh | awk '$1 ~ /^libc\.so/ {print $3}') |
	awk -ve=1 '$2 == "T" && $3 == "iconv" {e=0; exit}; END {exit e}'
then
	echo
	echo "Your system's libc appears to include iconv."
	echo
else
	prompt LIBICONV Linker flags to link agains libiconv \
		'(may be left blank if iconv is part of your system libc)'
fi
prompt INCICONV Compiler flags to find iconv.h
prompt LIBIXP Path to libixp.a

echo Writing config.mk
echo "$CFG
" >config.mk
echo Done.

Added util/link.
















































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#!/bin/sh -f

LD=$1
PACKAGES=$2
LDFLAGS=$3; shift 3

[ -n "$PACKAGES" ] && LDFLAGS="$LDFLAGS $(pkg-config --libs $PACKAGES)"

outfile="$1"; shift
bin="$(echo $0 | sed 's,/[^/]*$,,')"

# Derived from Russ Cox's 9l in plan9port.
ofiles=""
args=""
for i
do
	case "$i" in
	*.[ao]|*.o_pic)
		ofiles="$ofiles $i"
		;;
	*)
		args="$args $i"
		;;
	esac
done

xtmp=/tmp/ld.$$.$USER.out

echo LD "$($bin/cleanname ${BASE}$outfile)"
[ -n "$noisycc" ] && echo $LD -o $outfile $ofiles $LDFLAGS $args
$LD -o $outfile $ofiles $LDFLAGS $args >$xtmp 2>&1
status=$?
[ $status -eq 0 ] || echo $LD -o $outfile $ofiles $LDFLAGS $args >&2

sed 's/.*: In function `[^:]*: *//' $xtmp | egrep . |
egrep -v 'is almost always misused|is dangerous, better use|in statically linked applications requires at runtime'
rm -f $xtmp

exit $status