[Pkg-wmaker-commits] [wmpinboard] 06/30: Imported Upstream version 0.9.2

Doug Torrance dtorrance-guest at moszumanska.debian.org
Wed Jan 27 23:31:37 UTC 2016


This is an automated email from the git hooks/post-receive script.

dtorrance-guest pushed a commit to branch master
in repository wmpinboard.

commit 3456106465c1b700f0bac5d95aac91046aab66ad
Author: Doug Torrance <dtorrance at piedmont.edu>
Date:   Tue Jan 26 22:34:01 2016 -0500

    Imported Upstream version 0.9.2
---
 CREDITS           |    9 +
 ChangeLog         |  105 +++
 INSTALL           |   59 ++
 Imakefile         |   44 ++
 LICENSE           |  339 ++++++++++
 README            |    2 +
 TODO              |   33 +
 bbar.xpm          |   55 ++
 bbar_lc.xpm       |   50 ++
 bbar_llc.xpm      |   42 ++
 features.h        |   53 ++
 getopt.c          | 1004 +++++++++++++++++++++++++++++
 getopt.h          |  134 ++++
 getopt1.c         |  187 ++++++
 misc.c            |  110 ++++
 misc.h            |   18 +
 notes.c           |  726 +++++++++++++++++++++
 notes.h           |   54 ++
 pinboard.xpm      |  136 ++++
 pinboard_lc.xpm   |   86 +++
 pinboard_llc.xpm  |   80 +++
 wmpb-convert.pl   |   39 ++
 wmpinboard.1      | 1014 +++++++++++++++++++++++++++++
 wmpinboard.c      | 1831 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 wmpinboard.h      |   75 +++
 wmpinboard.lsm    |   11 +
 wmpinboard.lsm.in |   11 +
 wmpinboard.pod    |  867 +++++++++++++++++++++++++
 xmisc.c           |  368 +++++++++++
 xmisc.h           |   32 +
 30 files changed, 7574 insertions(+)

diff --git a/CREDITS b/CREDITS
new file mode 100644
index 0000000..8bef9fa
--- /dev/null
+++ b/CREDITS
@@ -0,0 +1,9 @@
+ wmpinboard credits
+--------------------
+ - written by Marco G"otze, <mailto:gomar at mindless.com>
+ - closer looks at other WM dock apps as well as the Xlib documentation were
+   of some help
+ - the images were created using The GIMP
+ - wmpinboard uses XIC-related keyboard handling code taken from RXVT's source
+ - thanks to various people for making suggestions and pointing out bugs
+
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..10dde6d
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,105 @@
+09/01/99: v0.9.2
+  - implemented EMACS-style C-[npfb] cursor movement
+  - added option to compile with even less colors
+  - back to getopt, this time distributing its source along with the program's
+  - fixed all non-ANSI-conforming segments of code I could find, hopefully
+    enhancing portability
+08/15/99: v0.9.1 -- announced
+  - implemented IPC among multiple instances, preventing multiple interactive
+    instances from running simultaneously, and allowing for deletion and
+    addition of notes via the command line
+  - ^C/R now result in a visual selection, too
+  - made intermediate `=' optional for parameterized long command line options
+  - slight changes to assignment of some colors to groups
+  - bug fixes (double click selection at first column; cursor display vs.
+    selection)
+07/31/99: v0.9 -- announced
+  - dropped the extensive README file in favor of a full-blown man page
+  - added X clipboard support (via mouse, keyboard; see the man page)
+  - left mouse button can now be used to drag notes without raising them
+  - added possibility to place new notes before editing them by dragging them
+    "off" the "TO DO" label
+  - added command line options "--dump" and "--dump-raw" to list all notes'
+    contents
+  - added simulated wear & tear of notes (can be disabled at compile time)
+  - "--version" now outputs information about compile-time options
+  - made it possible to compile wmpinboard in a fashion optimized for low
+    color depths (looks rather ugly, but at least it will _work_)
+  - slight changes to the palette of available note colors
+  - changed behavior of [Backspace] when pressed on the very last character
+    (now just clears that if it isn't blank); an entire (intermediate) line can
+    now be removed via ^Y or ^Z
+  - fixed a problem related to AfterStep swallowing
+
+06/06/99: v0.8.5
+  - different animation upon note destruction
+  - reverted from getopt() to manual command line parsing due to compatibility
+    issues on non-GNU systems
+05/18/99: v0.8.4 -- announced
+  - in edit mode, note color can now be changed via ([Shift]-)[Tab]
+  - switched from manual command line parsing to getopt functions (causing
+    some changes to the set of parameters, but retaining backward
+    compatibility)
+04/19/99: v0.8.3 -- announced
+  - only minor (internal) modifications
+03/06/99: v0.8.2
+  - added some eye candy (i.e., superfluous animations, can be disabled)
+  - minor internal changes
+02/23/99: v0.8.1 -- announced
+  - added options to explicitly set/suppress WithdrawnState for better
+    AfterStep Wharf compatibility; now tries to autodetect Window Maker
+  - internal optimizations: tidied up the code and reduced memory usage
+  - fixed a few small buglets
+02/20/99: v0.8 -- announced
+  - made some principal changes to the user interface in edit mode (right-
+    clicking on the triangle now pops up a panel from which further actions
+    can be chosen)
+  - added drawing capabilities: you can now add simple sketches to notes
+    (using the same color as the text)
+  - uses alternate mouse cursors where appropriate
+  - now remembers for each note where you left the cursor after editing it the
+    previous time; the text cursor can now be placed using the mouse
+  - [Shift]-[Up/Down] now cycles through notes of *roughly* the same color
+  - some minor changes ([Esc], allow for fonts <= 7x10, adjustable timeout,
+    modified some command line parameters' formats, minor modification of
+    behavior in "-c" mode)
+  - rewrote about half of the README file to fit the altered user interface and
+    generally make it more instructive
+
+01/10/99: v0.7 -- announced
+  - major redesign concerning character input and output, i.e.:
+     - switched from character images to real X fonts (any 8bit 6x10 font
+       should work)
+     - support for input and displaying of local characters (including
+       appropriate handling of dead keys)
+  - switched to a binary data file format
+  - added experimental optional "click to focus" emulation mode
+  - made note timeout optional (command line parameter)
+
+01/06/99: v0.6 -- unreleased
+  - major changes to the program's overall appearance
+  - notes now are 10*6 (-1) in size, and there may be up to 20 of them
+  - notes can no longer be removed by dragging them off the board
+  - right-clicking the triangle in edit mode now clears the note on first click
+  - changes to the keyboard handling (low-level; plus, added insert mode,
+    deleting of entire lines, and the ability to cycle through notes in edit
+    mode)
+
+01/03/99: v0.5.5 -- announced
+  - just some minor changes to the documentation
+01/01/99: v0.5.4
+  - modified keyboard handling ([End]) and edit mode note coloring
+  - changed the set of colors available for notes
+  - in edit mode, both the left and right mouse buttons can now be used to
+    switch colors (in opposite directions)
+12/31/98: v0.5.3
+  - fixed keyboard handling to avoid blocking of WM accelerators
+  - slight modification of the X window initialization procedure to improve
+    docking behavior in some circumstances
+12/30/98: v0.5.2
+  - fixed a bug introduced in v0.5.1 (note display timeout)
+12/29/98: v0.5.1 -- announced
+  - fixed a bug raising a note's icon to the top when left-clicking on it
+  - minor code optimizations
+12/26/98: v0.5 -- initial release (unannounced)
+
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..b9ce659
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,59 @@
+ wmpinboard
+============
+
+ License
+---------
+
+wmpinboard is distributed under the terms of the GNU General Public license,
+revision 2, or--at your choice--any later revision.  A copy of said document
+is included with this source package.
+
+ Requirements
+--------------
+
+ - an ANSI-compliant C compiler (preferably, GCC or one of its descendants)
+ - X11R6 including header files (installed separately as "xdevel" or similar
+   by some distributions)
+ - the XPM library
+ - wmpinboard is recommended to be run on a hi-color (15bit or above) display
+
+wmpinboard has reportedly been successfully compiled on the following
+platforms:
+
+ - Linux 2.0.x through 2.2.x, various setups
+ - FreeBSD 3.1
+ - Solaris 2.6
+
+Reports about other platforms are always welcome.
+
+ Installation
+--------------
+
+First, cd to the directory this file resides in.  There, do:
+
+  $ xmkmf -a
+  $ make
+
+Then, as the root user, do:
+
+  $ make install
+
+This should build and install the program.  Unless you edit either 
+Makefile, both the binary and the man page will be installed under 
+"/usr/local".  Alternatively, you can manually copy the files to 
+destinations of your choice.
+
+Note: You might want to check the file "features.h" and modify it to 
+adjust some compile-time settings to your preferences before compiling 
+the program.
+
+ Upgrading from earlier versions
+---------------------------------
+
+If you're upgrading to wmpinboard 0.7+ from an earlier version, existing notes
+will get lost due to some major changes concerning its data file format.
+You can avoid this by running the included PERL script as the user in
+question beforehand:
+
+  $ perl /path/to/wmpinboard.app/wmpb-convert.pl
+
diff --git a/Imakefile b/Imakefile
new file mode 100644
index 0000000..0c2e1ad
--- /dev/null
+++ b/Imakefile
@@ -0,0 +1,44 @@
+DEPLIBS = $(DEPXLIB)
+#CFLAGS += -Wall -ansi -pedantic
+
+LOCAL_LIBRARIES = $(XPMLIB) $(XLIB)
+
+SRCS = getopt.c getopt1.c misc.c xmisc.c notes.c wmpinboard.c
+OBJS = getopt.o getopt1.o misc.o xmisc.o notes.o wmpinboard.o
+
+ComplexProgramTarget(wmpinboard)
+
+dist: all wmpinboard.lsm wmpinboard.1
+	chmod 0644 *
+	chmod 0755 wmpinboard .
+	strip wmpinboard
+
+clean::
+	$(RM) $(OBJS)
+	$(RM) -rf .xvpics
+
+distclean: clean
+	$(RM) Makefile
+
+realclean: distclean
+	$(RM) wmpinboard.lsm wmpinboard.1
+
+bindistclean: dist
+	$(RM) *.[cho] *.{in,pod,xpm} Makefile* Imakefile INSTALL
+	$(RM) -rf .xvpics
+
+wmpinboard.lsm: wmpinboard.h wmpinboard.lsm.in
+	sed `grep VERSION wmpinboard.h|head -1|perl -ne '/([\d.]+).*?\((\d+)\s+(\w+)\s+(\d+)/; print "s/__VERSION__/$$1/;s/__DATE__/@{[uc sprintf \"%02d%s%02d\", $$2, $$3, $$4]}/"'` wmpinboard.lsm.in >wmpinboard.lsm
+
+wmpinboard.1: wmpinboard.h wmpinboard.pod
+	pod2man --release=v`grep VERSION wmpinboard.h|head -1|perl -ne '/[\d.]+/; print $$&'` --center=wmpinboard wmpinboard.pod >wmpinboard.1
+
+all::
+	@test -e ./ReleaseNotes && cat ReleaseNotes || true
+
+install::
+	$(INSTALL) -m 0644 wmpinboard.1 $(DESTDIR)/man/man1
+
+DESTDIR = /usr/local
+BINDIR  = /bin
+
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..a43ea21
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,339 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                          675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+

+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+

+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+

+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+

+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+

+	Appendix: How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19yy name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/README b/README
new file mode 100644
index 0000000..803f212
--- /dev/null
+++ b/README
@@ -0,0 +1,2 @@
+For extensive information on how to use the program, see the 
+wmpinboard(1) man page included with this package.
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..8b22efc
--- /dev/null
+++ b/TODO
@@ -0,0 +1,33 @@
+ wmpinboard
+============
+
+ Things TO DO
+--------------
+
+ Things perhaps TO DO
+----------------------
+
+ - add command line parameter to change the board image on the fly
+ - alarm feature???
+
+ Things I'm NOT going TO DO
+----------------------------
+
+ - Make wmpinboard pop up any external windows.  Why?
+
+     See the man page, section "FREQUENTLY ASKED QUESTIONS".
+
+ - Add an alarm feature.  (currently under re-consideration...)
+
+     After some contemplation I've come to the conclusion that adding
+     something like this is really beyond the scope of wmpinboard.
+
+ - Render rough sketches of the notes' contents in pinboard view.
+
+     Judging by my personal experience, the notes' texts tend to cover them
+     uniformly due to the limitation in size, so doing this would invariably
+     result in just a mess of dark blobs.
+
+
+Further suggestions?  Mail to <gomar at mindless.com>.
+
diff --git a/bbar.xpm b/bbar.xpm
new file mode 100644
index 0000000..03bc28e
--- /dev/null
+++ b/bbar.xpm
@@ -0,0 +1,55 @@
+/* XPM */
+static char * bbar_xpm[] = {
+"58 30 22 1",
+" 	c None",
+".	c #000000",
+"+	c #EFD183",
+"@	c #6E3A1C",
+"#	c #D2AA5C",
+"$	c #AEFA04",
+"%	c #7F6602",
+"&	c #FF0569",
+"*	c #FADA04",
+"=	c #6B9600",
+"-	c #9E0EDC",
+";	c #FA0604",
+">	c #FE821C",
+",	c #F2EE04",
+"'	c #F48989",
+")	c #C16C6C",
+"!	c #894E4E",
+"~	c #F73D88",
+"{	c #87003A",
+"]	c #62A6EF",
+"^	c #3E6599",
+"/	c #2A4668",
+"..........................................................",
+".+++++++++++++ at +++++++++++++@+++++++++++++ at ++++++++++++++.",
+".+############@+############@+############@+############@.",
+".+#$###......#@+####%.%#####@+##########&#@+#.....#....#@.",
+".+#$$###.....#@+####.*.#####@+###.###.#&&#@+#.$$$.#.=$.#@.",
+".+#-$$###....#@+###%*.######@+##.#.##.&&##@+#.$$$.#.$$.#@.",
+".+#--$$#.....#@+###.*%##..##@+#.###.#&&###@+#.$$=.#.$$.#@.",
+".+#;--$...#..#@+##%*.##.##.#@+#.###.&&..##@+#....##.$$.#@.",
+".+#;;-...###.#@+##.*%#.####. at +#....&&.##.#@+######.=$$.#@.",
+".+#>;...$$####@+#%*.##.####. at +#.##&&#.##.#@+#....#.=$$.#@.",
+".+#>>..--$$###@+#.*%###.##.#@+#.#&&.#.##.#@+#.$=.##.$$.#@.",
+".+#,>>;;--$$##@+#..#####..##@+#.&&#.#...##@+#.$$=.#.=$.#@.",
+".+#,,>>;;--$$#@+#.......##.#@+#&&#########@+#.....##...#@.",
+".+############@+###########. at +############@+############@.",
+".@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@.",
+".+++++++++++++ at +++++++++++++@+++++++++++++ at ++++++++++++++.",
+".+############@+####''''''##@+############@+############@.",
+".+#$$--;;>>,,#@+###'))))))!#@+#&#...######@+##########~#@.",
+".+##$$--;;>>,#@+###'))))))!#@+#&&###.#####@+#########~{#@.",
+".+###$$--..>>#@+###'))))))!#@+#.&&###.####@+########~{##@.",
+".+####$$...;>#@+##'))))))!##@+#.#&&#..####@+########~###@.",
+".+#.###...-;;#@+##'))))))!##@+#.##&&&.####@+#######~{###@.",
+".+#..#...$--;#@+##'))))))!##@+##.#.#&&####@+#~~###~~####@.",
+".+#.....#$$--#@+##]^^))))!##@+###...#&&###@+#~~~~~~{####@.",
+".+#....###$$-#@+#]^^^^^^/###@+###.####&&##@+###~~~{#####@.",
+".+#.....###$$#@+#]^^^^^^/###@+##.######&&#@+####~~{#####@.",
+".+#......###$#@+#]^^^^^^/###@+##........&#@+#####{######@.",
+".+############@+##//////####@+############@+############@.",
+".@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@.",
+".........................................................."};
diff --git a/bbar_lc.xpm b/bbar_lc.xpm
new file mode 100644
index 0000000..dada84c
--- /dev/null
+++ b/bbar_lc.xpm
@@ -0,0 +1,50 @@
+/* XPM */
+static char * bbar_xpm[] = {
+"58 30 17 1",
+" 	c None",
+".	c #D6FE04",
+"+	c #9A6E2C",
+"@	c #020204",
+"#	c #6E3A1C",
+"$	c #F9DB04",
+"%	c #562E1C",
+"&	c #DEC67C",
+"*	c #6A361C",
+"=	c #66321C",
+"-	c #565654",
+";	c #6AE624",
+">	c #F22E94",
+",	c #FEFEFC",
+"'	c #E80D23",
+")	c #0A76EC",
+"!	c #0ACAEC",
+"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@",
+"@&&&&&&&&&&&&&#&&&&&&&&&&&&&#&&&&&&&&&&&&&#&&&&&&&&&&&&&&@",
+"@&&&&&&&&&&&&&#&&&&&&&&&&&&&#&&&&&&&&&&&&&#&&&&&&&&&&&&&#@",
+"@&&.&&&@@@@@@&#&&&&&+ at +&&&&&#&&&&&&&&&&&>&#&&@@@@@&@@@@&#@",
+"@&&..&&&@@@@@&#&&&&&@$@&&&&&#&&&&@&&&@&>>&#&&@...@&@;.@&#@",
+"@&&'..&&&@@@@&#&&&&+$@&&&&&&#&&&@&@&&@>>&&#&&@...@&@..@&#@",
+"@&&''..&@@@@@&#&&&&@$+&&@@&&#&&@&&&@&>>&&&#&&@..;@&@..@&#@",
+"@&&'''.@@@&@@&#&&&+$@&&@&&@&#&&@&&&@>>@@&&#&&@@@@&&@..@&#@",
+"@&&>>'@@@&&&@&#&&&@$+&@&&&&@#&&@@@@>>@&&@&#&&&&&&&@;..@&#@",
+"@&&+>@@@..&&&&#&&+$@&&@&&&&@#&&@&&>>&@&&@&#&&@@@@&@;..@&#@",
+"@&&++@@''..&&&#&&@$+&&&@&&@&#&&@&>>@&@&&@&#&&@.;@&&@..@&#@",
+"@&&$++>>''..&&#&&@@&&&&&@@&&#&&@>>&@&@@@&&#&&@..;@&@;.@&#@",
+"@&&$$++>'''..&#&&@@@@@@@&&@&#&&>>&&&&&&&&&#&&@@@@@&&@@@&#@",
+"@&&&&&&&&&&&&&#&&&&&&&&&&&&@#&&&&&&&&&&&&&#&&&&&&&&&&&&&#@",
+"@########################################################@",
+"@&&&&&&&&&&&&&#&&&&&&&&&&&&&#&&&&&&&&&&&&&#&&&&&&&&&&&&&&@",
+"@&&&&&&&&&&&&&#&&&&&>+>+>+&&#&&&&&&&&&&&&&#&&&&&&&&&&&&&#@",
+"@&&..'''>++$$&#&&&&>+>+>+>#&#&&>&@@@&&&&&&#&&&&&&&&&&&>&#@",
+"@&&&..''>>++$&#&&&&+>+>+>+#&#&&>>&&&@&&&&&#&&&&&&&&&&>%&#@",
+"@&&&&..''@@++&#&&&&>+>+>+>#&#&&@>>&&&@&&&&#&&&&&&&&&>%&&#@",
+"@&&&&&..@@@>+&#&&&>+>+>+>#&&#&&@&>>&@@&&&&#&&&&&&&&&>&&&#@",
+"@&&@&&&@@@'>>&#&&&+>+>+>+#&&#&&@&&>>>@&&&&#&&&&&&&&>%&&&#@",
+"@&&@@&@@@.'''&#&&&>+>+>+>#&&#&&&@&@&>>&&&&#&&>>&&&>>&&&&#@",
+"@&&@@@@@&..''&#&&&!-)>+>+#&&#&&&&@@@&>>&&&#&&>>>>>>%&&&&#@",
+"@&&@@@@&&&..'&#&&!-)-)-)-&&&#&&&&@&&&&>>&&#&&&&>>>%&&&&&#@",
+"@&&@@@@@&&&..&#&&!)-)-)-)&&&#&&&@&&&&&&>>&#&&&&&>>%&&&&&#@",
+"@&&@@@@@@&&&.&#&&!-)-)-)-&&&#&&&@@@@@@@@>&#&&&&&&%&&&&&&#@",
+"@&&&&&&&&&&&&&#&&&)-)-)-&&&&#&&&&&&&&&&&&&#&&&&&&&&&&&&&#@",
+"@########################################################@",
+"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"};
diff --git a/bbar_llc.xpm b/bbar_llc.xpm
new file mode 100644
index 0000000..9884b01
--- /dev/null
+++ b/bbar_llc.xpm
@@ -0,0 +1,42 @@
+/* XPM */
+static char * bbar_xpm[] = {
+"58 30 9 1",
+" 	c None",
+".	c #020204",
+"+	c #DEC67C",
+"@	c #6E3A1C",
+"#	c #9A6E2C",
+"$	c #E80D23",
+"%	c #F9DB04",
+"*	c #565654",
+"=	c #0A76EC",
+"..........................................................",
+".+++++++++++++ at +++++++++++++@+++++++++++++ at ++++++++++++++.",
+".+++++++++++++ at +++++++++++++@+++++++++++++ at +++++++++++++@.",
+".++++++......+ at +++++#.#+++++ at +++++++++++$+ at ++.....+....+@.",
+".+++++++.....+ at +++++.%.+++++ at ++++.+++.+$$+ at ++.%%%.+.%%.+ at .",
+".++$+++++....+ at ++++#%.++++++ at +++.+.++.$$++ at ++.%%%.+.%%.+ at .",
+".++$$+++.....+ at ++++.%#++..++ at ++.+++.+$$+++ at ++.%%%.+.%%.+ at .",
+".++$$$+...+..+ at +++#%.++.++.+ at ++.+++.$$..++ at ++....++.%%.+ at .",
+".++%%$...+++.+ at +++.%#+.++++. at ++....$$.++.+ at +++++++.%%%.+ at .",
+".++#%...++++++ at ++#%.++.++++. at ++.++$$+.++.+ at ++....+.%%%.+ at .",
+".++##..$$+++++ at ++.%#+++.++.+ at ++.+$$.+.++.+ at ++.%%.++.%%.+ at .",
+".++%##%%$$++++ at ++..+++++..++@++.$$+.+...++ at ++.%%%.+.%%.+ at .",
+".++%%##%$$$+++ at ++.......++.+@++$$+++++++++ at ++.....++...+@.",
+".+++++++++++++ at ++++++++++++.@+++++++++++++ at +++++++++++++@.",
+".@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@.",
+".+++++++++++++ at +++++++++++++@+++++++++++++ at ++++++++++++++.",
+".+++++++++++++ at +++++$#$#$#++ at +++++++++++++@+++++++++++++ at .",
+".++++$$$%##%%+ at ++++$#$#$#$@+ at ++$+...++++++ at +++++++++++$+ at .",
+".+++++$$%%##%+ at ++++#$#$#$#@+ at ++$$+++.+++++ at ++++++++++$*+ at .",
+".++++++$$..##+ at ++++$#$#$#$@+ at ++.$$+++.++++ at +++++++++$*++ at .",
+".+++++++...%#+ at +++$#$#$#$@++ at ++.+$$+..++++ at +++++++++$+++ at .",
+".++.+++...$%%+ at +++#$#$#$#@++ at ++.++$$$.++++ at ++++++++$*+++ at .",
+".++..+...+$$$+ at +++$#$#$#$@++ at +++.+.+$$++++ at ++$$+++$$++++ at .",
+".++.....+++$$+ at +++#*=$#$#@++ at ++++...+$$+++ at ++$$$$$$*++++ at .",
+".++....+++++$+ at ++#*=*=*=*+++ at ++++.++++$$++ at ++++$$$*+++++ at .",
+".++.....++++++ at ++#=*=*=*=+++ at +++.++++++$$+ at +++++$$*+++++ at .",
+".++......+++++ at ++#*=*=*=*+++ at +++........$+ at ++++++*++++++ at .",
+".+++++++++++++ at +++=*=*=*++++ at +++++++++++++@+++++++++++++ at .",
+".@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@.",
+".........................................................."};
diff --git a/features.h b/features.h
new file mode 100644
index 0000000..47a71dd
--- /dev/null
+++ b/features.h
@@ -0,0 +1,53 @@
+/*
+ *  Copyright (C) 1998,9 by Marco G"otze.
+ *
+ *  This code is part of the wmpinboard source package, which is
+ *  distributed under the terms of the GNU GPL2.
+ */
+
+#ifndef FEATURES_H_INCLUDED
+#define FEATURES_H_INCLUDED
+
+/******************** START OF USER-EDITABLE SECTION *************************/
+
+/*
+ * this defines the amount of idle time after which edit mode is terminated;
+ * the value is interpreted as seconds and has to be greater than or equal to
+ * 5, or else the default value of 60s will be used
+ */
+#define TIMEOUT 60
+
+/*
+ * #undef this to disable simulated wear & tear of notes
+ */
+#define CREASES
+
+/*
+ * #define one of these to build a version of wmpinboard that's optimized for
+ * low color depths (palette modes)--but be warned: this won't look pretty;
+ * implicitly #undef's CREASES (neither is recommended, let alone LOWESTCOLOR)
+ */
+#undef LOWCOLOR
+#undef LOWESTCOLOR
+
+/********************* END OF USER-EDITABLE SECTION **************************/
+
+#if TIMEOUT < 5 && TIMEOUT != 0
+#undef TIMEOUT
+#define TIMEOUT 60
+#endif
+
+#ifdef LOWESTCOLOR
+#define LOWCOLOR
+#endif
+
+#ifdef LOWCOLOR
+#undef CREASES
+#endif
+
+#define DCLICK_LIMIT 500  /* ms */
+
+#define FUNSTUFF
+
+#endif  /* FEATURES_H_INCLUDED */
+
diff --git a/getopt.c b/getopt.c
new file mode 100644
index 0000000..0f41fb6
--- /dev/null
+++ b/getopt.c
@@ -0,0 +1,1004 @@
+/* Getopt for GNU.
+   NOTE: getopt is now part of the C library, so if you don't know what
+   "Keep this file name-space clean" means, talk to roland at gnu.ai.mit.edu
+   before changing it!
+
+   Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97
+   Free Software Foundation, Inc.
+
+   This file is part of the GNU C Library.  Its master source is NOT part of
+   the C library, however.  The master source lives in /gd/gnu/lib.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+

+/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>.
+   Ditto for AIX 3.2 and <stdlib.h>.  */
+#ifndef _NO_PROTO
+#define _NO_PROTO
+#endif
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if !defined (__STDC__) || !__STDC__
+/* This is a separate conditional since some stdc systems
+   reject `defined (const)'.  */
+#ifndef const
+#define const
+#endif
+#endif
+
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+   actually compiling the library itself.  This code is part of the GNU C
+   Library, but also included in many other GNU distributions.  Compiling
+   and linking in this code is a waste when using the GNU C library
+   (especially if it is a shared library).  Rather than having every GNU
+   program understand `configure --with-gnu-libc' and omit the object files,
+   it is simpler to just do this in the source for each such file.  */
+
+#define GETOPT_INTERFACE_VERSION 2
+#if !defined (_LIBC) && defined (__GLIBC__) && __GLIBC__ >= 2
+#include <gnu-versions.h>
+#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
+#define ELIDE_CODE
+#endif
+#endif
+
+#ifndef ELIDE_CODE
+
+/* This needs to come after some library #include
+   to get __GNU_LIBRARY__ defined.  */
+#ifdef	__GNU_LIBRARY__
+/* Don't include stdlib.h for non-GNU C libraries because some of them
+   contain conflicting prototypes for getopt.  */
+#include <stdlib.h>
+#include <unistd.h>
+#endif /* GNU C library.  */
+
+#ifdef VMS
+#include <unixlib.h>
+#if HAVE_STRING_H - 0
+#include <string.h>
+#endif
+#endif
+
+#if defined (WIN32) && !defined (__CYGWIN32__)
+/* It's not Unix, really.  See?  Capital letters.  */
+#include <windows.h>
+#define getpid() GetCurrentProcessId()
+#endif
+
+#ifndef _
+/* This is for other GNU distributions with internationalized messages.
+   When compiling libc, the _ macro is predefined.  */
+#ifdef HAVE_LIBINTL_H
+#include <libintl.h>
+#define _(msgid)	gettext (msgid)
+#else
+#define _(msgid)	(msgid)
+#endif
+#endif
+
+/* This version of `getopt' appears to the caller like standard Unix `getopt'
+   but it behaves differently for the user, since it allows the user
+   to intersperse the options with the other arguments.
+
+   As `getopt' works, it permutes the elements of ARGV so that,
+   when it is done, all the options precede everything else.  Thus
+   all application programs are extended to handle flexible argument order.
+
+   Setting the environment variable POSIXLY_CORRECT disables permutation.
+   Then the behavior is completely standard.
+
+   GNU application programs can use a third alternative mode in which
+   they can distinguish the relative order of options and other arguments.  */
+
+#include "getopt.h"
+
+/* For communication from `getopt' to the caller.
+   When `getopt' finds an option that takes an argument,
+   the argument value is returned here.
+   Also, when `ordering' is RETURN_IN_ORDER,
+   each non-option ARGV-element is returned here.  */
+
+char *optarg = NULL;
+
+/* Index in ARGV of the next element to be scanned.
+   This is used for communication to and from the caller
+   and for communication between successive calls to `getopt'.
+
+   On entry to `getopt', zero means this is the first call; initialize.
+
+   When `getopt' returns -1, this is the index of the first of the
+   non-option elements that the caller should itself scan.
+
+   Otherwise, `optind' communicates from one call to the next
+   how much of ARGV has been scanned so far.  */
+
+/* 1003.2 says this must be 1 before any call.  */
+int optind = 1;
+
+/* Formerly, initialization of getopt depended on optind==0, which
+   causes problems with re-calling getopt as programs generally don't
+   know that. */
+
+int __getopt_initialized = 0;
+
+/* The next char to be scanned in the option-element
+   in which the last option character we returned was found.
+   This allows us to pick up the scan where we left off.
+
+   If this is zero, or a null string, it means resume the scan
+   by advancing to the next ARGV-element.  */
+
+static char *nextchar;
+
+/* Callers store zero here to inhibit the error message
+   for unrecognized options.  */
+
+int opterr = 1;
+
+/* Set to an option character which was unrecognized.
+   This must be initialized on some systems to avoid linking in the
+   system's own getopt implementation.  */
+
+int optopt = '?';
+
+/* Describe how to deal with options that follow non-option ARGV-elements.
+
+   If the caller did not specify anything,
+   the default is REQUIRE_ORDER if the environment variable
+   POSIXLY_CORRECT is defined, PERMUTE otherwise.
+
+   REQUIRE_ORDER means don't recognize them as options;
+   stop option processing when the first non-option is seen.
+   This is what Unix does.
+   This mode of operation is selected by either setting the environment
+   variable POSIXLY_CORRECT, or using `+' as the first character
+   of the list of option characters.
+
+   PERMUTE is the default.  We permute the contents of ARGV as we scan,
+   so that eventually all the non-options are at the end.  This allows options
+   to be given in any order, even with programs that were not written to
+   expect this.
+
+   RETURN_IN_ORDER is an option available to programs that were written
+   to expect options and other ARGV-elements in any order and that care about
+   the ordering of the two.  We describe each non-option ARGV-element
+   as if it were the argument of an option with character code 1.
+   Using `-' as the first character of the list of option characters
+   selects this mode of operation.
+
+   The special argument `--' forces an end of option-scanning regardless
+   of the value of `ordering'.  In the case of RETURN_IN_ORDER, only
+   `--' can cause `getopt' to return -1 with `optind' != ARGC.  */
+
+static enum
+{
+	REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
+}
+ordering;
+
+/* Value of POSIXLY_CORRECT environment variable.  */
+static char *posixly_correct;
+

+#ifdef	__GNU_LIBRARY__
+/* We want to avoid inclusion of string.h with non-GNU libraries
+   because there are many ways it can cause trouble.
+   On some systems, it contains special magic macros that don't work
+   in GCC.  */
+#include <string.h>
+#define	my_index	strchr
+#else
+
+/* Avoid depending on library functions or files
+   whose names are inconsistent.  */
+
+char *getenv();
+
+static char *
+     my_index(str, chr)
+     const char *str;
+     int chr;
+{
+	while (*str)
+	{
+		if (*str == chr)
+			return (char *) str;
+		str++;
+	}
+	return 0;
+}
+
+/* If using GCC, we can safely declare strlen this way.
+   If not using GCC, it is ok not to declare it.  */
+#ifdef __GNUC__
+/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h.
+   That was relevant to code that was here before.  */
+#if !defined (__STDC__) || !__STDC__
+/* gcc with -traditional declares the built-in strlen to return int,
+   and has done so at least since version 2.4.5. -- rms.  */
+extern int strlen(const char *);
+
+#endif /* not __STDC__ */
+#endif /* __GNUC__ */
+
+#endif /* not __GNU_LIBRARY__ */
+

+/* Handle permutation of arguments.  */
+
+/* Describe the part of ARGV that contains non-options that have
+   been skipped.  `first_nonopt' is the index in ARGV of the first of them;
+   `last_nonopt' is the index after the last of them.  */
+
+static int first_nonopt;
+static int last_nonopt;
+
+#ifdef _LIBC
+/* Bash 2.0 gives us an environment variable containing flags
+   indicating ARGV elements that should not be considered arguments.  */
+
+static const char *nonoption_flags;
+static int nonoption_flags_len;
+
+static int original_argc;
+static char *const *original_argv;
+
+/* Make sure the environment variable bash 2.0 puts in the environment
+   is valid for the getopt call we must make sure that the ARGV passed
+   to getopt is that one passed to the process.  */
+static void store_args(int argc, char *const *argv) __attribute__((unused));
+     static void
+          store_args(int argc, char *const *argv)
+{
+	/* XXX This is no good solution.  We should rather copy the args so
+	   that we can compare them later.  But we must not use malloc(3).  */
+	original_argc = argc;
+	original_argv = argv;
+}
+text_set_element(__libc_subinit, store_args);
+#endif
+
+/* Exchange two adjacent subsequences of ARGV.
+   One subsequence is elements [first_nonopt,last_nonopt)
+   which contains all the non-options that have been skipped so far.
+   The other is elements [last_nonopt,optind), which contains all
+   the options processed since those non-options were skipped.
+
+   `first_nonopt' and `last_nonopt' are relocated so that they describe
+   the new indices of the non-options in ARGV after they are moved.  */
+
+#if defined (__STDC__) && __STDC__
+static void exchange(char **);
+
+#endif
+
+static void
+     exchange(argv)
+     char **argv;
+{
+	int bottom = first_nonopt;
+	int middle = last_nonopt;
+	int top = optind;
+	char *tem;
+
+	/* Exchange the shorter segment with the far end of the longer segment.
+	   That puts the shorter segment into the right place.
+	   It leaves the longer segment in the right place overall,
+	   but it consists of two parts that need to be swapped next.  */
+
+	while (top > middle && middle > bottom)
+	{
+		if (top - middle > middle - bottom)
+		{
+			/* Bottom segment is the short one.  */
+			int len = middle - bottom;
+			register int i;
+
+			/* Swap it with the top part of the top segment.  */
+			for (i = 0; i < len; i++)
+			{
+				tem = argv[bottom + i];
+				argv[bottom + i] = argv[top - (middle - bottom) + i];
+				argv[top - (middle - bottom) + i] = tem;
+			}
+			/* Exclude the moved bottom segment from further swapping.  */
+			top -= len;
+		}
+		else
+		{
+			/* Top segment is the short one.  */
+			int len = top - middle;
+			register int i;
+
+			/* Swap it with the bottom part of the bottom segment.  */
+			for (i = 0; i < len; i++)
+			{
+				tem = argv[bottom + i];
+				argv[bottom + i] = argv[middle + i];
+				argv[middle + i] = tem;
+			}
+			/* Exclude the moved top segment from further swapping.  */
+			bottom += len;
+		}
+	}
+
+	/* Update records for the slots the non-options now occupy.  */
+
+	first_nonopt += (optind - last_nonopt);
+	last_nonopt = optind;
+}
+
+/* Initialize the internal data when the first call is made.  */
+
+#if defined (__STDC__) && __STDC__
+static const char *_getopt_initialize(int, char *const *, const char *);
+
+#endif
+static const char *
+     _getopt_initialize(argc, argv, optstring)
+     int argc;
+     char *const *argv;
+     const char *optstring;
+{
+	/* Start processing options with ARGV-element 1 (since ARGV-element 0
+	   is the program name); the sequence of previously skipped
+	   non-option ARGV-elements is empty.  */
+
+	first_nonopt = last_nonopt = optind = 1;
+
+	nextchar = NULL;
+
+	posixly_correct = getenv("POSIXLY_CORRECT");
+
+	/* Determine how to handle the ordering of options and nonoptions.  */
+
+	if (optstring[0] == '-')
+	{
+		ordering = RETURN_IN_ORDER;
+		++optstring;
+	}
+	else if (optstring[0] == '+')
+	{
+		ordering = REQUIRE_ORDER;
+		++optstring;
+	}
+	else if (posixly_correct != NULL)
+		ordering = REQUIRE_ORDER;
+	else
+		ordering = PERMUTE;
+
+#ifdef _LIBC
+	if (posixly_correct == NULL
+	    && argc == original_argc && argv == original_argv)
+	{
+		/* Bash 2.0 puts a special variable in the environment for each
+		   command it runs, specifying which ARGV elements are the results of
+		   file name wildcard expansion and therefore should not be
+		   considered as options.  */
+		char var[100];
+
+		sprintf(var, "_%d_GNU_nonoption_argv_flags_", getpid());
+		nonoption_flags = getenv(var);
+		if (nonoption_flags == NULL)
+			nonoption_flags_len = 0;
+		else
+			nonoption_flags_len = strlen(nonoption_flags);
+	}
+	else
+		nonoption_flags_len = 0;
+#endif
+
+	return optstring;
+}
+

+/* Scan elements of ARGV (whose length is ARGC) for option characters
+   given in OPTSTRING.
+
+   If an element of ARGV starts with '-', and is not exactly "-" or "--",
+   then it is an option element.  The characters of this element
+   (aside from the initial '-') are option characters.  If `getopt'
+   is called repeatedly, it returns successively each of the option characters
+   from each of the option elements.
+
+   If `getopt' finds another option character, it returns that character,
+   updating `optind' and `nextchar' so that the next call to `getopt' can
+   resume the scan with the following option character or ARGV-element.
+
+   If there are no more option characters, `getopt' returns -1.
+   Then `optind' is the index in ARGV of the first ARGV-element
+   that is not an option.  (The ARGV-elements have been permuted
+   so that those that are not options now come last.)
+
+   OPTSTRING is a string containing the legitimate option characters.
+   If an option character is seen that is not listed in OPTSTRING,
+   return '?' after printing an error message.  If you set `opterr' to
+   zero, the error message is suppressed but we still return '?'.
+
+   If a char in OPTSTRING is followed by a colon, that means it wants an arg,
+   so the following text in the same ARGV-element, or the text of the following
+   ARGV-element, is returned in `optarg'.  Two colons mean an option that
+   wants an optional arg; if there is text in the current ARGV-element,
+   it is returned in `optarg', otherwise `optarg' is set to zero.
+
+   If OPTSTRING starts with `-' or `+', it requests different methods of
+   handling the non-option ARGV-elements.
+   See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
+
+   Long-named options begin with `--' instead of `-'.
+   Their names may be abbreviated as long as the abbreviation is unique
+   or is an exact match for some defined option.  If they have an
+   argument, it follows the option name in the same ARGV-element, separated
+   from the option name by a `=', or else the in next ARGV-element.
+   When `getopt' finds a long-named option, it returns 0 if that option's
+   `flag' field is nonzero, the value of the option's `val' field
+   if the `flag' field is zero.
+
+   The elements of ARGV aren't really const, because we permute them.
+   But we pretend they're const in the prototype to be compatible
+   with other systems.
+
+   LONGOPTS is a vector of `struct option' terminated by an
+   element containing a name which is zero.
+
+   LONGIND returns the index in LONGOPT of the long-named option found.
+   It is only valid when a long-named option has been found by the most
+   recent call.
+
+   If LONG_ONLY is nonzero, '-' as well as '--' can introduce
+   long-named options.  */
+
+int
+    _getopt_internal(argc, argv, optstring, longopts, longind, long_only)
+     int argc;
+     char *const *argv;
+     const char *optstring;
+     const struct option *longopts;
+     int *longind;
+     int long_only;
+{
+	optarg = NULL;
+
+	if (!__getopt_initialized || optind == 0)
+	{
+		optstring = _getopt_initialize(argc, argv, optstring);
+		optind = 1;	/* Don't scan ARGV[0], the program name.  */
+		__getopt_initialized = 1;
+	}
+
+	/* Test whether ARGV[optind] points to a non-option argument.
+	   Either it does not have option syntax, or there is an environment flag
+	   from the shell indicating it is not an option.  The later information
+	   is only used when the used in the GNU libc.  */
+#ifdef _LIBC
+#define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0'	      \
+		     || (optind < nonoption_flags_len			      \
+			 && nonoption_flags[optind] == '1'))
+#else
+#define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0')
+#endif
+
+	if (nextchar == NULL || *nextchar == '\0')
+	{
+		/* Advance to the next ARGV-element.  */
+
+		/* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been
+		   moved back by the user (who may also have changed the arguments).  */
+		if (last_nonopt > optind)
+			last_nonopt = optind;
+		if (first_nonopt > optind)
+			first_nonopt = optind;
+
+		if (ordering == PERMUTE)
+		{
+			/* If we have just processed some options following some non-options,
+			   exchange them so that the options come first.  */
+
+			if (first_nonopt != last_nonopt && last_nonopt != optind)
+				exchange((char **) argv);
+			else if (last_nonopt != optind)
+				first_nonopt = optind;
+
+			/* Skip any additional non-options
+			   and extend the range of non-options previously skipped.  */
+
+			while (optind < argc && NONOPTION_P)
+				optind++;
+			last_nonopt = optind;
+		}
+
+		/* The special ARGV-element `--' means premature end of options.
+		   Skip it like a null option,
+		   then exchange with previous non-options as if it were an option,
+		   then skip everything else like a non-option.  */
+
+		if (optind != argc && !strcmp(argv[optind], "--"))
+		{
+			optind++;
+
+			if (first_nonopt != last_nonopt && last_nonopt != optind)
+				exchange((char **) argv);
+			else if (first_nonopt == last_nonopt)
+				first_nonopt = optind;
+			last_nonopt = argc;
+
+			optind = argc;
+		}
+
+		/* If we have done all the ARGV-elements, stop the scan
+		   and back over any non-options that we skipped and permuted.  */
+
+		if (optind == argc)
+		{
+			/* Set the next-arg-index to point at the non-options
+			   that we previously skipped, so the caller will digest them.  */
+			if (first_nonopt != last_nonopt)
+				optind = first_nonopt;
+			return -1;
+		}
+
+		/* If we have come to a non-option and did not permute it,
+		   either stop the scan or describe it to the caller and pass it by.  */
+
+		if (NONOPTION_P)
+		{
+			if (ordering == REQUIRE_ORDER)
+				return -1;
+			optarg = argv[optind++];
+			return 1;
+		}
+
+		/* We have found another option-ARGV-element.
+		   Skip the initial punctuation.  */
+
+		nextchar = (argv[optind] + 1
+			    + (longopts != NULL && argv[optind][1] == '-'));
+	}
+
+	/* Decode the current option-ARGV-element.  */
+
+	/* Check whether the ARGV-element is a long option.
+
+	   If long_only and the ARGV-element has the form "-f", where f is
+	   a valid short option, don't consider it an abbreviated form of
+	   a long option that starts with f.  Otherwise there would be no
+	   way to give the -f short option.
+
+	   On the other hand, if there's a long option "fubar" and
+	   the ARGV-element is "-fu", do consider that an abbreviation of
+	   the long option, just like "--fu", and not "-f" with arg "u".
+
+	   This distinction seems to be the most useful approach.  */
+
+	if (longopts != NULL
+	    && (argv[optind][1] == '-'
+		|| (long_only && (argv[optind][2] || !my_index(optstring, argv[optind][1])))))
+	{
+		char *nameend;
+		const struct option *p;
+		const struct option *pfound = NULL;
+		int exact = 0;
+		int ambig = 0;
+		int indfound = -1;
+		int option_index;
+
+		for (nameend = nextchar; *nameend && *nameend != '='; nameend++)
+			/* Do nothing.  */ ;
+
+		/* Test all long options for either exact match
+		   or abbreviated matches.  */
+		for (p = longopts, option_index = 0; p->name; p++, option_index++)
+			if (!strncmp(p->name, nextchar, nameend - nextchar))
+			{
+				if ((unsigned int) (nameend - nextchar)
+				    == (unsigned int) strlen(p->name))
+				{
+					/* Exact match found.  */
+					pfound = p;
+					indfound = option_index;
+					exact = 1;
+					break;
+				}
+				else if (pfound == NULL)
+				{
+					/* First nonexact match found.  */
+					pfound = p;
+					indfound = option_index;
+				}
+				else
+					/* Second or later nonexact match found.  */
+					ambig = 1;
+			}
+
+		if (ambig && !exact)
+		{
+			if (opterr)
+				fprintf(stderr, _("%s: option `%s' is ambiguous\n"),
+					argv[0], argv[optind]);
+			nextchar += strlen(nextchar);
+			optind++;
+			optopt = 0;
+			return '?';
+		}
+
+		if (pfound != NULL)
+		{
+			option_index = indfound;
+			optind++;
+			if (*nameend)
+			{
+				/* Don't test has_arg with >, because some C compilers don't
+				   allow it to be used on enums.  */
+				if (pfound->has_arg)
+					optarg = nameend + 1;
+				else
+				{
+					if (opterr)
+						if (argv[optind - 1][1] == '-')
+							/* --option */
+							fprintf(stderr,
+								_("%s: option `--%s' doesn't allow an argument\n"),
+								argv[0], pfound->name);
+						else
+							/* +option or -option */
+							fprintf(stderr,
+								_("%s: option `%c%s' doesn't allow an argument\n"),
+								argv[0], argv[optind - 1][0], pfound->name);
+
+					nextchar += strlen(nextchar);
+
+					optopt = pfound->val;
+					return '?';
+				}
+			}
+			else if (pfound->has_arg == 1)
+			{
+				if (optind < argc)
+					optarg = argv[optind++];
+				else
+				{
+					if (opterr)
+						fprintf(stderr,
+							_("%s: option `%s' requires an argument\n"),
+						 argv[0], argv[optind - 1]);
+					nextchar += strlen(nextchar);
+					optopt = pfound->val;
+					return optstring[0] == ':' ? ':' : '?';
+				}
+			}
+			nextchar += strlen(nextchar);
+			if (longind != NULL)
+				*longind = option_index;
+			if (pfound->flag)
+			{
+				*(pfound->flag) = pfound->val;
+				return 0;
+			}
+			return pfound->val;
+		}
+
+		/* Can't find it as a long option.  If this is not getopt_long_only,
+		   or the option starts with '--' or is not a valid short
+		   option, then it's an error.
+		   Otherwise interpret it as a short option.  */
+		if (!long_only || argv[optind][1] == '-'
+		    || my_index(optstring, *nextchar) == NULL)
+		{
+			if (opterr)
+			{
+				if (argv[optind][1] == '-')
+					/* --option */
+					fprintf(stderr, _("%s: unrecognized option `--%s'\n"),
+						argv[0], nextchar);
+				else
+					/* +option or -option */
+					fprintf(stderr, _("%s: unrecognized option `%c%s'\n"),
+					argv[0], argv[optind][0], nextchar);
+			}
+			nextchar = (char *) "";
+			optind++;
+			optopt = 0;
+			return '?';
+		}
+	}
+
+	/* Look at and handle the next short option-character.  */
+
+	{
+		char c = *nextchar++;
+		char *temp = my_index(optstring, c);
+
+		/* Increment `optind' when we start to process its last character.  */
+		if (*nextchar == '\0')
+			++optind;
+
+		if (temp == NULL || c == ':')
+		{
+			if (opterr)
+			{
+				if (posixly_correct)
+					/* 1003.2 specifies the format of this message.  */
+					fprintf(stderr, _("%s: illegal option -- %c\n"),
+						argv[0], c);
+				else
+					fprintf(stderr, _("%s: invalid option -- %c\n"),
+						argv[0], c);
+			}
+			optopt = c;
+			return '?';
+		}
+		/* Convenience. Treat POSIX -W foo same as long option --foo */
+		if (temp[0] == 'W' && temp[1] == ';')
+		{
+			char *nameend;
+			const struct option *p;
+			const struct option *pfound = NULL;
+			int exact = 0;
+			int ambig = 0;
+			int indfound = 0;
+			int option_index;
+
+			/* This is an option that requires an argument.  */
+			if (*nextchar != '\0')
+			{
+				optarg = nextchar;
+				/* If we end this ARGV-element by taking the rest as an arg,
+				   we must advance to the next element now.  */
+				optind++;
+			}
+			else if (optind == argc)
+			{
+				if (opterr)
+				{
+					/* 1003.2 specifies the format of this message.  */
+					fprintf(stderr, _("%s: option requires an argument -- %c\n"),
+						argv[0], c);
+				}
+				optopt = c;
+				if (optstring[0] == ':')
+					c = ':';
+				else
+					c = '?';
+				return c;
+			}
+			else
+				/* We already incremented `optind' once;
+				   increment it again when taking next ARGV-elt as argument.  */
+				optarg = argv[optind++];
+
+			/* optarg is now the argument, see if it's in the
+			   table of longopts.  */
+
+			for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++)
+				/* Do nothing.  */ ;
+
+			/* Test all long options for either exact match
+			   or abbreviated matches.  */
+			for (p = longopts, option_index = 0; p->name; p++, option_index++)
+				if (!strncmp(p->name, nextchar, nameend - nextchar))
+				{
+					if ((unsigned int) (nameend - nextchar) == strlen(p->name))
+					{
+						/* Exact match found.  */
+						pfound = p;
+						indfound = option_index;
+						exact = 1;
+						break;
+					}
+					else if (pfound == NULL)
+					{
+						/* First nonexact match found.  */
+						pfound = p;
+						indfound = option_index;
+					}
+					else
+						/* Second or later nonexact match found.  */
+						ambig = 1;
+				}
+			if (ambig && !exact)
+			{
+				if (opterr)
+					fprintf(stderr, _("%s: option `-W %s' is ambiguous\n"),
+						argv[0], argv[optind]);
+				nextchar += strlen(nextchar);
+				optind++;
+				return '?';
+			}
+			if (pfound != NULL)
+			{
+				option_index = indfound;
+				if (*nameend)
+				{
+					/* Don't test has_arg with >, because some C compilers don't
+					   allow it to be used on enums.  */
+					if (pfound->has_arg)
+						optarg = nameend + 1;
+					else
+					{
+						if (opterr)
+							fprintf(stderr, _("\
+%s: option `-W %s' doesn't allow an argument\n"),
+								argv[0], pfound->name);
+
+						nextchar += strlen(nextchar);
+						return '?';
+					}
+				}
+				else if (pfound->has_arg == 1)
+				{
+					if (optind < argc)
+						optarg = argv[optind++];
+					else
+					{
+						if (opterr)
+							fprintf(stderr,
+								_("%s: option `%s' requires an argument\n"),
+								argv[0], argv[optind - 1]);
+						nextchar += strlen(nextchar);
+						return optstring[0] == ':' ? ':' : '?';
+					}
+				}
+				nextchar += strlen(nextchar);
+				if (longind != NULL)
+					*longind = option_index;
+				if (pfound->flag)
+				{
+					*(pfound->flag) = pfound->val;
+					return 0;
+				}
+				return pfound->val;
+			}
+			nextchar = NULL;
+			return 'W';	/* Let the application handle it.   */
+		}
+		if (temp[1] == ':')
+		{
+			if (temp[2] == ':')
+			{
+				/* This is an option that accepts an argument optionally.  */
+				if (*nextchar != '\0')
+				{
+					optarg = nextchar;
+					optind++;
+				}
+				else
+					optarg = NULL;
+				nextchar = NULL;
+			}
+			else
+			{
+				/* This is an option that requires an argument.  */
+				if (*nextchar != '\0')
+				{
+					optarg = nextchar;
+					/* If we end this ARGV-element by taking the rest as an arg,
+					   we must advance to the next element now.  */
+					optind++;
+				}
+				else if (optind == argc)
+				{
+					if (opterr)
+					{
+						/* 1003.2 specifies the format of this message.  */
+						fprintf(stderr,
+							_("%s: option requires an argument -- %c\n"),
+							argv[0], c);
+					}
+					optopt = c;
+					if (optstring[0] == ':')
+						c = ':';
+					else
+						c = '?';
+				}
+				else
+					/* We already incremented `optind' once;
+					   increment it again when taking next ARGV-elt as argument.  */
+					optarg = argv[optind++];
+				nextchar = NULL;
+			}
+		}
+		return c;
+	}
+}
+
+int
+    getopt(argc, argv, optstring)
+     int argc;
+     char *const *argv;
+     const char *optstring;
+{
+	return _getopt_internal(argc, argv, optstring,
+				(const struct option *) 0,
+				(int *) 0,
+				0);
+}
+
+#endif /* Not ELIDE_CODE.  */
+

+#ifdef TEST
+
+/* Compile with -DTEST to make an executable for use in testing
+   the above definition of `getopt'.  */
+
+int
+    main(argc, argv)
+     int argc;
+     char **argv;
+{
+	int c;
+	int digit_optind = 0;
+
+	while (1)
+	{
+		int this_option_optind = optind ? optind : 1;
+
+		c = getopt(argc, argv, "abc:d:0123456789");
+		if (c == -1)
+			break;
+
+		switch (c)
+		{
+			case '0':
+			case '1':
+			case '2':
+			case '3':
+			case '4':
+			case '5':
+			case '6':
+			case '7':
+			case '8':
+			case '9':
+				if (digit_optind != 0 && digit_optind != this_option_optind)
+					printf("digits occur in two different argv-elements.\n");
+				digit_optind = this_option_optind;
+				printf("option %c\n", c);
+				break;
+
+			case 'a':
+				printf("option a\n");
+				break;
+
+			case 'b':
+				printf("option b\n");
+				break;
+
+			case 'c':
+				printf("option c with value `%s'\n", optarg);
+				break;
+
+			case '?':
+				break;
+
+			default:
+				printf("?? getopt returned character code 0%o ??\n", c);
+		}
+	}
+
+	if (optind < argc)
+	{
+		printf("non-option ARGV-elements: ");
+		while (optind < argc)
+			printf("%s ", argv[optind++]);
+		printf("\n");
+	}
+
+	exit(0);
+}
+
+#endif /* TEST */
diff --git a/getopt.h b/getopt.h
new file mode 100644
index 0000000..2fa12f7
--- /dev/null
+++ b/getopt.h
@@ -0,0 +1,134 @@
+/* Declarations for getopt.
+   Copyright (C) 1989,90,91,92,93,94,96,97 Free Software Foundation, Inc.
+
+   This file is part of the GNU C Library.  Its master source is NOT part of
+   the C library, however.  The master source lives in /gd/gnu/lib.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#ifndef _GETOPT_H
+#define _GETOPT_H 1
+
+#ifdef	__cplusplus
+extern "C"
+{
+#endif
+
+/* For communication from `getopt' to the caller.
+   When `getopt' finds an option that takes an argument,
+   the argument value is returned here.
+   Also, when `ordering' is RETURN_IN_ORDER,
+   each non-option ARGV-element is returned here.  */
+
+	extern char *optarg;
+
+/* Index in ARGV of the next element to be scanned.
+   This is used for communication to and from the caller
+   and for communication between successive calls to `getopt'.
+
+   On entry to `getopt', zero means this is the first call; initialize.
+
+   When `getopt' returns -1, this is the index of the first of the
+   non-option elements that the caller should itself scan.
+
+   Otherwise, `optind' communicates from one call to the next
+   how much of ARGV has been scanned so far.  */
+
+	extern int optind;
+
+/* Callers store zero here to inhibit the error message `getopt' prints
+   for unrecognized options.  */
+
+	extern int opterr;
+
+/* Set to an option character which was unrecognized.  */
+
+	extern int optopt;
+
+/* Describe the long-named options requested by the application.
+   The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
+   of `struct option' terminated by an element containing a name which is
+   zero.
+
+   The field `has_arg' is:
+   no_argument          (or 0) if the option does not take an argument,
+   required_argument    (or 1) if the option requires an argument,
+   optional_argument    (or 2) if the option takes an optional argument.
+
+   If the field `flag' is not NULL, it points to a variable that is set
+   to the value given in the field `val' when the option is found, but
+   left unchanged if the option is not found.
+
+   To have a long-named option do something other than set an `int' to
+   a compiled-in constant, such as set a value from `optarg', set the
+   option's `flag' field to zero and its `val' field to a nonzero
+   value (the equivalent single-letter option character, if there is
+   one).  For long options that have a zero `flag' field, `getopt'
+   returns the contents of the `val' field.  */
+
+	struct option
+	{
+#if defined (__STDC__) && __STDC__
+		const char *name;
+#else
+		char *name;
+#endif
+		/* has_arg can't be an enum because some compilers complain about
+		   type mismatches in all the code that assumes it is an int.  */
+		int has_arg;
+		int *flag;
+		int val;
+	};
+
+/* Names for the values of the `has_arg' field of `struct option'.  */
+
+#define	no_argument		0
+#define required_argument	1
+#define optional_argument	2
+
+#if defined (__STDC__) && __STDC__
+#ifdef __GNU_LIBRARY__
+/* Many other libraries have conflicting prototypes for getopt, with
+   differences in the consts, in stdlib.h.  To avoid compilation
+   errors, only prototype getopt for the GNU C library.  */
+	extern int getopt(int argc, char *const *argv, const char *shortopts);
+#else				/* not __GNU_LIBRARY__ */
+	extern int getopt();
+#endif				/* __GNU_LIBRARY__ */
+	extern int getopt_long(int argc, char *const *argv, const char *shortopts,
+			       const struct option *longopts, int *longind);
+	extern int getopt_long_only(int argc, char *const *argv,
+				    const char *shortopts,
+			       const struct option *longopts, int *longind);
+
+/* Internal only.  Users should not call this directly.  */
+	extern int _getopt_internal(int argc, char *const *argv,
+				    const char *shortopts,
+				const struct option *longopts, int *longind,
+				    int long_only);
+#else				/* not __STDC__ */
+	extern int getopt();
+	extern int getopt_long();
+	extern int getopt_long_only();
+
+	extern int _getopt_internal();
+#endif				/* __STDC__ */
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif				/* _GETOPT_H */
diff --git a/getopt1.c b/getopt1.c
new file mode 100644
index 0000000..fe414f3
--- /dev/null
+++ b/getopt1.c
@@ -0,0 +1,187 @@
+/* getopt_long and getopt_long_only entry points for GNU getopt.
+   Copyright (C) 1987,88,89,90,91,92,93,94,96,97 Free Software Foundation, Inc.
+
+   This file is part of the GNU C Library.  Its master source is NOT part of
+   the C library, however.  The master source lives in /gd/gnu/lib.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+

+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "getopt.h"
+
+#if !defined (__STDC__) || !__STDC__
+/* This is a separate conditional since some stdc systems
+   reject `defined (const)'.  */
+#ifndef const
+#define const
+#endif
+#endif
+
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+   actually compiling the library itself.  This code is part of the GNU C
+   Library, but also included in many other GNU distributions.  Compiling
+   and linking in this code is a waste when using the GNU C library
+   (especially if it is a shared library).  Rather than having every GNU
+   program understand `configure --with-gnu-libc' and omit the object files,
+   it is simpler to just do this in the source for each such file.  */
+
+#define GETOPT_INTERFACE_VERSION 2
+#if !defined (_LIBC) && defined (__GLIBC__) && __GLIBC__ >= 2
+#include <gnu-versions.h>
+#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
+#define ELIDE_CODE
+#endif
+#endif
+
+#ifndef ELIDE_CODE
+
+/* This needs to come after some library #include
+   to get __GNU_LIBRARY__ defined.  */
+#ifdef __GNU_LIBRARY__
+#include <stdlib.h>
+#endif
+
+#ifndef	NULL
+#define NULL 0
+#endif
+
+int
+    getopt_long(argc, argv, options, long_options, opt_index)
+     int argc;
+     char *const *argv;
+     const char *options;
+     const struct option *long_options;
+     int *opt_index;
+{
+	return _getopt_internal(argc, argv, options, long_options, opt_index, 0);
+}
+
+/* Like getopt_long, but '-' as well as '--' can indicate a long option.
+   If an option that starts with '-' (not '--') doesn't match a long option,
+   but does match a short option, it is parsed as a short option
+   instead.  */
+
+int
+    getopt_long_only(argc, argv, options, long_options, opt_index)
+     int argc;
+     char *const *argv;
+     const char *options;
+     const struct option *long_options;
+     int *opt_index;
+{
+	return _getopt_internal(argc, argv, options, long_options, opt_index, 1);
+}
+
+#endif /* Not ELIDE_CODE.  */
+

+#ifdef TEST
+
+#include <stdio.h>
+
+int
+    main(argc, argv)
+     int argc;
+     char **argv;
+{
+	int c;
+	int digit_optind = 0;
+
+	while (1)
+	{
+		int this_option_optind = optind ? optind : 1;
+		int option_index = 0;
+		static struct option long_options[] =
+		{
+			{"add", 1, 0, 0},
+			{"append", 0, 0, 0},
+			{"delete", 1, 0, 0},
+			{"verbose", 0, 0, 0},
+			{"create", 0, 0, 0},
+			{"file", 1, 0, 0},
+			{0, 0, 0, 0}
+		};
+
+		c = getopt_long(argc, argv, "abc:d:0123456789",
+				long_options, &option_index);
+		if (c == -1)
+			break;
+
+		switch (c)
+		{
+			case 0:
+				printf("option %s", long_options[option_index].name);
+				if (optarg)
+					printf(" with arg %s", optarg);
+				printf("\n");
+				break;
+
+			case '0':
+			case '1':
+			case '2':
+			case '3':
+			case '4':
+			case '5':
+			case '6':
+			case '7':
+			case '8':
+			case '9':
+				if (digit_optind != 0 && digit_optind != this_option_optind)
+					printf("digits occur in two different argv-elements.\n");
+				digit_optind = this_option_optind;
+				printf("option %c\n", c);
+				break;
+
+			case 'a':
+				printf("option a\n");
+				break;
+
+			case 'b':
+				printf("option b\n");
+				break;
+
+			case 'c':
+				printf("option c with value `%s'\n", optarg);
+				break;
+
+			case 'd':
+				printf("option d with value `%s'\n", optarg);
+				break;
+
+			case '?':
+				break;
+
+			default:
+				printf("?? getopt returned character code 0%o ??\n", c);
+		}
+	}
+
+	if (optind < argc)
+	{
+		printf("non-option ARGV-elements: ");
+		while (optind < argc)
+			printf("%s ", argv[optind++]);
+		printf("\n");
+	}
+
+	exit(0);
+}
+
+#endif /* TEST */
diff --git a/misc.c b/misc.c
new file mode 100644
index 0000000..ae9b0cb
--- /dev/null
+++ b/misc.c
@@ -0,0 +1,110 @@
+/*
+ *  Copyright (C) 1998,9 by Marco G"otze.
+ *
+ *  This code is part of the wmpinboard source package, which is
+ *  distributed under the terms of the GNU GPL2.
+ */
+
+#include <signal.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#include "wmpinboard.h"
+
+extern const char *rc_file_name;
+
+/*
+ * prints an error message and exits
+ */ 
+void
+die(const char *text)
+{ 
+  fprintf(stderr, "Fatal error: %s\n", text);
+  exit(EXIT_FAILURE);
+}
+
+/*
+ * allocs <amount> bytes and die()s if that fails
+ */
+void*
+smalloc(long amount)
+{
+  void *p = 0;
+
+  p = malloc(amount);
+  if (!p) die("Out of memory!");
+  return p;
+}
+
+/*
+ * returns a true value if a text string is to be considered empty
+ * (if width > 0, limit the check to width characters)
+ */
+int
+string_empty(char *ptr, int width)
+{
+  int i = 0;
+
+  while (*ptr && (!width || i++ < width))
+    if (*ptr++ != ' ') return 0;
+  return 1;
+}
+
+/*
+ * flushes a possibly running interactive instance, returns a true value if
+ * another instance *is* evidently running
+ */
+int
+flush_instance(int pid)
+{
+  struct stat buf;
+  time_t t;
+  char *s;
+  int i;
+  int running = 0;
+
+  if (pid != (int) getpid()) {  /* not fulfilled if file doesn't exist */
+    s = smalloc(strlen(getenv("HOME")) + strlen(rc_file_name) + 1);
+    strcpy(s, getenv("HOME"));
+    strcat(s, rc_file_name);
+    stat(s, &buf);
+    t = buf.st_mtime;
+    /* make that other instance confirm its presence by modifying the file */
+    if (kill(pid, SIGUSR1) != -1) {
+      for (i = 0; i < 3; i++) {  /* wait up to 3 secs for a modification */
+        stat(s, &buf);
+        if (buf.st_mtime != t) break;
+        sleep(1);
+      }
+      running = buf.st_mtime != t;
+      /* we assume that modifying the file is safe now */
+    }
+    free(s);
+  }
+  return running;
+}
+
+/*
+ * u()ncsarbmel sht eoctnnesto  fs<>
+ * m(solt ysudef roc notss' ,osw  eod'n todi  tnip-alec)
+ */
+char*
+csarbmel(const char *s)
+{
+  char ch, *t;
+  int i;
+
+  t = smalloc(strlen(s)+1);
+  strcpy(t, s);
+  for (i = 0; i+1 < strlen(t); i += 2) {
+    ch = t[i];
+    t[i] = t[i+1];
+    t[i+1] = ch;
+  }
+  return t;
+}
+
diff --git a/misc.h b/misc.h
new file mode 100644
index 0000000..73c605d
--- /dev/null
+++ b/misc.h
@@ -0,0 +1,18 @@
+/*
+ *  Copyright (C) 1998,9 by Marco G"otze.
+ *
+ *  This code is part of the wmpinboard source package, which is
+ *  distributed under the terms of the GNU GPL2.
+ */
+
+#ifndef MISC_H_INCLUDED
+#define MISC_H_INCLUDED
+
+void die(const char*);
+void *smalloc(long);
+int  string_empty(char*, int);
+int  flush_instance(int);
+char *csarbmel(const char *);
+
+#endif  /* MISC_H_INCLUDED */
+
diff --git a/notes.c b/notes.c
new file mode 100644
index 0000000..5996ff3
--- /dev/null
+++ b/notes.c
@@ -0,0 +1,726 @@
+/*
+ *  Copyright (C) 1998,9 by Marco G"otze.
+ *
+ *  This code is part of the wmpinboard source package, which is
+ *  distributed under the terms of the GNU GPL2.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+#include "features.h"
+#include "misc.h"
+#include "wmpinboard.h"
+#include "xmisc.h"
+#include "notes.h"
+
+/*
+ * adds a new note and returns its number (or -1 if notes limit exceeded)
+ */
+int
+add_note()
+{
+  int cols[C_NUM];
+  int rep = 20;
+  int i, j, n;
+
+  if (notes_count >= MAX_NOTES)
+    return -1;
+  n = notes_count++;
+
+  /* find a reasonable color */
+  for (i = 0; i < C_NUM; i++) cols[i] = 1;
+  for (i = 0; i < n; i++) cols[ndata[i].col] = 0;
+  for (i = j = 0; i < C_NUM; i++)
+    if (cols[i]) cols[j++] = i;
+  ndata[n].col = j ? cols[rand() % j] : rand() % C_NUM;
+
+  /* choose a random place to pin it, some pixels off any other note's origin
+     (possible if MAX_NOTES is sufficiently small) */
+  do {
+    ndata[n].x =  6 + rand() % 36;
+    ndata[n].y = 12 + rand() % 34;  /* avoid "TO DO" label */
+    j = 1;
+    for (i = 0; i < n; i++)
+      if (ndata[i].x - ndata[n].x < 5 && ndata[i].x - ndata[n].x > -5 &&
+          ndata[i].y - ndata[n].y < 5 && ndata[i].y - ndata[n].y > -5)
+      {
+        j = 0;
+        break;
+      }
+  } while (!j && --rep);
+
+  memset(ndata[n].text, 32, 59);
+  ndata[n].text[59] = '\0';
+  ndata[n].cursor = 0;
+  memset(ndata[n].sketch, 0, 512);
+  memset(ndata[n].creases, 0, 32);
+
+  return n;
+}
+
+/*
+ * removes a note
+ */
+void
+remove_note(int number)
+{
+  int i;
+
+  if (number >= notes_count) return;
+  /* in case memcpy() can't do overlapping copies... */
+  for (i = number; i < notes_count-1; i++)
+    memcpy(&ndata[i], &ndata[i+1], sizeof(data_t));
+  notes_count--;
+}
+
+/*
+ * moves a note on top of all others and returns the quote's new ID
+ */
+int
+raise_note(int number)
+{
+  data_t tmp;
+  int i;
+
+  if (number >= notes_count-1 || number < 0) return number;
+  memcpy(&tmp, &ndata[number], sizeof(data_t));
+  /* in case memcpy() can't do overlapping copies... */
+  for (i = number; i < notes_count-1; i++)
+    memcpy(&ndata[i], &ndata[i+1], sizeof(data_t));
+  memcpy(&ndata[notes_count-1], &tmp, sizeof(data_t));
+  return notes_count-1;
+}
+
+/*
+ * returns a value corresponding to how tilted a note is to be displayed
+ */
+int
+note_tilt(int note)
+{
+  return ndata[note].x < 14 ? 0 : (ndata[note].x < 36 ? 1 : 2);
+}
+
+/*
+ * returns true if the note in question is to be considered empty
+ */
+int
+note_empty(int note)
+{
+  char *ptr = ndata[note].sketch;
+  int i;
+
+  if (!string_empty(ndata[note].text, 0)) return 0;
+  for (i = 0; i < 511; i++, ptr++)  /* last byte ignored */
+    if (*ptr) return 0;
+  return 1;
+}
+
+/*
+ * creates templates for the icons suiting the specified note
+ */
+void
+color_notes(int note)
+{
+  XGetSubImage(display, app, 64, 0, 16, 48, ~0, ZPixmap, img, 64, 0);
+  if (ndata[note].sketch[511] & 1)
+    replace_color(img, 64, 0, 16, 48, C_EXTRA, palette[ndata[note].col].fg);
+  else
+    replace_color(img, 64, 0, 16, 48, C_EXTRA, palette[ndata[note].col].bg);
+  replace_color(img, 64, 0, 16, 48, C_INNER, palette[ndata[note].col].bg);
+#ifdef CREASES
+  render_wear(note);
+#endif
+}
+
+/*
+ * draws a note's miniature representation on the pinboard
+ */
+void
+pin_note(int note)
+{
+  merge_masked(img, 64, 16*note_tilt(note), ndata[note].x,
+    ndata[note].y, 16, 16, C_OUTER);
+}
+
+/*
+ * renders the pinboard with all of its notes excluding <exclude> if >= 0
+ */
+void
+render_pinboard(int exclude)
+{
+  int i;
+
+  XPutImage(display, app, normalGC, img, 0, 0, 0, 0, 64, 64);
+  for (i = 0; i < notes_count; i++)
+    if (i != exclude) {
+      color_notes(i);
+      pin_note(i);
+    }
+}
+
+/*
+ * if (<x>, <y>) is within a quote's area, returns the quote's ID, otherwise -1
+ */
+int
+selected_note(int x, int y)
+{
+  int i;
+
+  for (i = notes_count-1; i >= 0; i--)
+    if (x >= ndata[i].x && x < ndata[i].x+16 &&
+        y >= ndata[i].y && y < ndata[i].y+16 &&
+        XGetPixel(img, 128+x-ndata[i].x, note_tilt(i)*16+y-ndata[i].y) !=
+          C_OUTER)
+    {
+      return i;
+    }
+  return -1;
+}
+
+/*
+ * prints a letter identified by a note and a position in its text string, and
+ * overlays it by a possible sketch if sketch has a true value
+ */
+void
+print_letter(int note, int letter, int sketch)
+{
+  int a = 2+6*(letter%10), b = 2+10*(letter/10);
+
+
+  if (sketch) {
+    XFillRectangle(display, app, fillGC, a, b, 6, 10);
+#ifdef CREASES
+    render_edit_wear_area(note, a, b, 6, 10);
+#endif
+    draw_sketch_area(note, a, b, 6, 10);
+  }
+  XDrawString(display, app, fontGC, a, b + font->ascent,
+    &ndata[note].text[letter], 1);
+}
+
+/*
+ * prints a note's entire text *without* drawing a possibly existing sketch
+ */
+void
+print_text(int note)
+{
+  int i;
+
+  for (i = 0; i < 59; i++) print_letter(note, i, 0);
+}
+
+/*
+ * shifts part of a note's text to the right, inserting <ch> (step >= 1)
+ */
+void
+shift_string(int note, int at, int margin, int step, int ch)
+{
+  int i;
+
+  if (step < 1) return;
+
+  for (i = margin; i >= at+step; i--) {
+    ndata[note].text[i] = ndata[note].text[i-step];
+    print_letter(note, i, 1);
+  }
+  for (i = at; i < at+step; i++) {
+    ndata[note].text[i] = ch;
+    print_letter(note, i, 1);
+  }
+}
+
+/*
+ * draws a rectangular section of a sketch, according to the data saved along
+ * with a note
+ */
+void
+draw_sketch_area(int note, int x, int y, int w, int h)
+{
+  int i, j;
+
+  for (i = x; i < x+w; i++)
+    for (j = y; j < y+h; j++)
+      if (ndata[note].sketch[8*j + i/8] & (1<<(i%8)))
+        XDrawPoint(display, app, fontGC, i, j);
+}
+
+/*
+ * returns the number of the button bar's button corresponding to the given
+ * coordinates, or -1
+ */
+int
+bbar_button(int x, int y)
+{
+  int i;
+
+  for (i = 0; i < 8; i++)
+    if (x >= 4 + (i%4)*14 && x < 18 + (i%4)*14 && y >= 32 + (i/4)*14 &&
+      y < 46 + (i/4)*14)
+    {
+      return i;
+    }
+  return -1;
+}
+
+/*
+ * displays note <note> in edit mode view
+ */
+void
+render_note(int note)
+{
+  int i;
+
+  XSetForeground(display, fillGC, palette[ndata[note].col].bg);
+  XSetForeground(display, fontGC, BlackPixel(display, DefaultScreen(display)));
+  XSetBackground(display, fontGC, palette[ndata[note].col].bg);
+  set_mask(0);
+  XFillRectangle(display, app, fillGC, 0, 0, 64, 64);
+  XDrawRectangle(display, app, fontGC, 0, 0, 63, 63);
+  XSetForeground(display, fontGC, palette[ndata[note].col].fg);
+  for (i = 55; i < 62; i += 2)  /* draw triangle in the bottom right corner */
+    XDrawLine(display, app, fontGC, i, 62, 62, i);
+}
+
+/*
+ * prepares edit mode for note <note> (does NOT update the display)
+ */
+void
+init_edit_mode(int note)
+{
+#ifdef CREASES
+  XGCValues gcv;
+
+  gcv.foreground = palette[ndata[note].col].cr;
+  XChangeGC(display, creaseGC, GCForeground, &gcv);
+#endif
+  render_note(note);
+#ifdef CREASES
+  render_edit_wear(note);
+#endif
+  print_text(note);
+  draw_sketch(note);
+}
+
+/*
+ * converts <note>'s contents to what's referred to as "cooked" format (as
+ * opposed to raw data); returns a malloc()ed string that has to be freed
+ * later on!
+ */
+char*
+cook(int note, int pos, int len)
+{
+  char *s, *t;
+  int i;
+
+  s = smalloc(70);
+  for (t = s, i = pos; i < (pos+len > 59 ? 59 : pos+len); i++) {
+    if (!string_empty(&ndata[note].text[i], (i >= 50 ? 59 : 10*(i/10+1))-i))
+    {
+      if (t > s && !(i%10) && t[-1] != ' ' && t[-1] != '-')
+        *t++ = ' ';  /* replace virtual newlines by spaces */
+      if ((t > s && (ndata[note].text[i] != ' ' || t[-1] != ' ') &&
+        !(t[-1] == '-'
+        && string_empty(&ndata[note].text[10*(i/10)], i%10+1))) ||
+        (t == s && ndata[note].text[i] != ' '))
+      {
+        *t++ = ndata[note].text[i];
+      }
+    }
+  }
+  *t = '\0';
+  return s;
+}
+
+/*
+ * dumps all notes' contents on STDOUT (raw dump unless <cooked> is true)
+ */
+void
+dump_notes(int cooked)
+{
+  char *s;
+  int c = 0, i;
+
+#ifndef LOWESTCOLOR
+  for (; c < 4; c++)
+#endif
+    for (i = 0; i < notes_count; i++)
+      if (c_group[ndata[i].col] == c) {
+        s = cooked ? cook(i, 0, 59) : ndata[i].text;
+        printf("#%02d: %s\n", i, s);
+        if (cooked) free(s);
+      }
+}
+
+/*
+ * adds the (linear) number of the character (<x>, <y>) belongs to on a note;
+ * disallowing char #59 if <strict>
+ */
+int
+char_at(int x, int y, int strict)
+{
+  int i;
+
+  if (x < 2) x = 2;
+  if (x > 62) x = 62;  /* intentional! */
+  if (y > 61) y = 61;
+  if (y < 2) y = 2;
+  i = (x-2)/6 + ((y-2)/10)*10;
+  if (i > 59) i = 59;
+  return i;
+}
+
+#ifdef CREASES
+/*
+ * applies random wear & tear to a note (the default probability is multiplied
+ * by <factor>)
+ */
+void
+wear_note(int note)
+{
+  /* prefabricated crease patterns */
+  static const int pats = 8;
+  static const unsigned char pat[8][5][5] = {
+    { { 1,0,0,0,0 },
+      { 0,1,0,0,0 },
+      { 0,0,1,0,0 },
+      { 0,0,0,1,0 },
+      { 0,0,0,0,1 } },
+    { { 0,0,0,0,1 },
+      { 0,0,0,1,0 },
+      { 0,0,1,0,0 },
+      { 0,1,0,0,0 },
+      { 1,0,0,0,0 } },
+    { { 0,0,0,1,0 },
+      { 0,0,0,1,0 },
+      { 0,0,1,0,0 },
+      { 1,1,0,0,0 },
+      { 0,0,0,0,0 } },
+    { { 0,1,0,0,0 },
+      { 0,1,0,0,0 },
+      { 0,0,1,0,0 },
+      { 0,0,0,1,1 },
+      { 0,0,0,0,0 } },
+    { { 0,0,0,0,0 },
+      { 0,0,0,1,1 },
+      { 0,0,1,0,0 },
+      { 0,1,0,0,0 },
+      { 0,1,0,0,0 } },
+    { { 0,0,0,0,0 },
+      { 1,1,0,0,0 },
+      { 0,0,1,0,0 },
+      { 0,0,0,1,0 },
+      { 0,0,0,1,0 } },
+    { { 0,0,0,0,0 },
+      { 0,1,0,0,0 },
+      { 0,0,1,0,0 },
+      { 0,0,0,1,0 },
+      { 0,0,0,0,0 } },
+    { { 0,0,0,0,0 },
+      { 0,0,0,1,0 },
+      { 0,0,1,0,0 },
+      { 0,1,0,0,0 },
+      { 0,0,0,0,0 } }
+  };
+  int i, j, k = 0;
+
+  /* determine number of crease bits */
+  for (i = 0; i < 32; i++)
+   for (j = 0; j < 8; j++)
+     k += ndata[note].creases[i]>>(j) & 1;
+  /* don't cover more than 35% of a note's area with creases */
+  if (100*k/256 < 35 && !(rand() % (4+k/64))) {
+    int x, y;
+    int t = rand() % pats;  /* choose a prefab crease pattern to apply... */
+    int a = rand() % 11;    /* ...as well as its location */
+    int b = rand() % 11;
+
+    for (y = 0; y < 5; y++)
+      for (x = 0; x < 5; x++)
+        if (pat[t][y][x])
+          ndata[note].creases[2*(b+y) + (a+x<8)] |= 1<<(7-(a+x)%8);
+  }
+}
+
+/*
+ * makes an otherwise pre-rendered note look worn out
+ */
+void
+render_wear(int note)
+{
+  int x, y, z;
+
+  for (z = 0; z <= 48; z += 16)
+    for (y = 0; y < 16; y++)
+      for (x = 0; x < 16; x++)
+        if (ndata[note].creases[2*y+x/8]>>(7-x%8) & 1 &&
+          XGetPixel(img, 64+x, z+y) == palette[ndata[note].col].bg)
+        {
+          XPutPixel(img, 64+x, z+y, palette[ndata[note].col].cr);
+        }
+}
+
+/*
+ * renders wear & tear in edit mode in a specific window
+ */
+void
+render_edit_wear_area_win(Window win, int note, int a, int b, int w, int h)
+{
+#define m(x, y) (m[2*(y) + (x)/8]>>(7-(x)%8) & 1)
+#define l(x, y) (l[8*(y) + (x)/8]>>(7-(x)%8) & 1)
+  static unsigned char l[(64/8)*64];  /* high res crease map */
+  static unsigned char m[32];         /* low res crease map */
+  int x, y, z;
+
+  /* lazily render a high-res crease pattern */
+  if (memcmp(m, ndata[note].creases, sizeof(m))) {
+    memcpy(m, ndata[note].creases, sizeof(m));
+    memset(l, 0, sizeof(l));
+    /* now we have to somehow extrapolate a 64x64 crease pattern from a 16x16
+       sketch... */
+    for (z = 0; z < 3; z++)  /* three passes */
+      for (y = 2; y < 62; y++)
+        for (x = 2; x < 62; x++) {
+          int sx = x/4, sy = y/4;
+          int xx = x%4, yy = y%4;
+          int xi = (xx<<1)%3, yi = (yy<<1)%3;
+          int xd = xx<2 ? -1 : 1, yd = yy<2 ? -1 : 1;
+
+          if (z < 2 && (  /* during passes #0 and #1 */
+            (!z && xi && yi && m(sx, sy) &&
+              (m(sx+xd, sy+yd) || m(sx, sy+yd) || m(sx+xd, sy) ) &&
+              ((xd<0 && sx<2) || (xd>0 && sx>14) || (yd<0 && sy<2) ||
+               (yd>0 && sy>14) || m(sx+2*xd, sy+2*yd))
+            ) || (z && (
+              (!xi && !yi && l(x-xd, y-yd) && l(x+2*xd, y+2*yd)) ||
+              (!xi && yi && l(x-xd, y) && l(x+2*xd, y)) ||
+              (xi && !yi && l(x, y-yd) && l(x, y+2*yd))
+            ))))
+          {
+            l[8*y + x/8] |= 1<<(7-x%8);
+          }
+          /* during pass #2, remove isolated dots */
+          if (z == 2 && l(x, y) && !(l(x-1, y-1) || l(x, y-1) ||
+            l(x+1, y-1) || l(x-1, y) || l(x+1, y) || l(x-1, y+1) ||
+            l(x, y+1) || l(x+1, y+1)))
+          {
+            l[8*y + x/8] &= 0xff - (1<<(7-x%8));
+          }
+        }
+  }
+  for (y = b; y < b+h; y++)
+    for (x = a; x < a+w; x++)
+      if (x > 1 && y > 1 && x < 62 && y < 62 && x+y <= 116 && l(x, y))
+        XDrawPoint(display, win, creaseGC, x, y);
+#undef m
+#undef l
+}
+
+/* the algorithm previously used...
+void
+render_edit_wear_area_win(Window win, int note, int a, int b, int w, int h)
+{
+  static char m[16][16];
+  static unsigned char prev[32];
+  int x, y, i, j;
+
+  if (memcmp(prev, ndata[note].creases, 32)) {
+    memcpy(prev, ndata[note].creases, 32);
+    for (y = 0; y < 16; y++)
+      for (x = 0; x < 16; x++)
+        m[x][y] = prev[2*y + x/8]>>(7-x%8) & 1;
+  }
+  for (y = b; y < b+h; y++)
+    for (x = a; x < a+w; x++)
+      if (x > 1 && y > 1 && x < 62 && y < 62 && x+y <= 116) {
+        int sx = x/4, sy = y/4;
+        if (m[sx][sy]) {
+          int xx = x%4, yy = y%4;
+          int xi = xx&1 != (xx>>1)&1, yi = yy&1 != (yy>>1)&1;
+          int xd = xx<2 ? -1 : 1, yd = yy<2 ? -1 : 1;
+
+          if (
+              (m[sx+xd][sy+yd] &&
+                (
+                  (!xi && !yi && m[sx+xd][sy] && m[sx][sy+yd]) ||
+                  (!xi && yi && m[sx+xd][sy]) ||
+                  (xi && !yi && m[sx][sy+yd])
+                )
+              ) ||
+              (m[sx+xd][sy] + m[sx+xd][sy+yd] + m[sx][sy+yd] == 1)
+            )
+          {
+            XDrawPoint(display, win, creaseGC, x, y);
+          }
+        }
+      }
+}
+*/
+#endif
+
+#ifdef FUNSTUFF
+extern unsigned int state_bits;
+
+/*
+ * atek sparppoirta ecaitnoo  npsceai lcoacisno,sr terusnt ur efit ah tahppnede
+ */
+void
+check_occasion(int d, int m, int y)
+{
+  static const unsigned char sketch0[256] = {
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x87, 0x3d, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xe7, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xaf, 0x4d, 0x01,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xb3, 0x5d, 0x01,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x0d, 0x01,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0x1f, 0x01,
+    0x00, 0x00, 0x00, 0x00, 0x18, 0x78, 0x9f, 0x01,
+    0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0xfe, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x10, 0x60, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x30, 0xf0, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x60, 0xf0, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xe0, 0x01,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x3e, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x03, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
+  };
+  static const unsigned char sketch1[512] = {
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x01, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x03, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x03, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x03, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x07, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x01, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x04, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x04, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x0a, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0a, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x0a, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x18, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x1b, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x30, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x66, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x08, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x48, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0x61, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x97, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x90, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf8, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0xc0, 0x0d, 0xc0, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x80, 0x03,
+    0x00, 0x00, 0x00, 0x00, 0x60, 0x40, 0x80, 0x02,
+    0x00, 0x00, 0x00, 0x00, 0x20, 0x80, 0x03, 0x02,
+    0x00, 0x00, 0x00, 0x00, 0x20, 0x08, 0x56, 0x06,
+    0x00, 0x00, 0x00, 0x00, 0xe0, 0xf8, 0x1f, 0x06,
+    0x00, 0x00, 0x00, 0x00, 0xf8, 0x6f, 0xfd, 0x07,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01, 0x03,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x03, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x07, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
+  };
+  char *s;
+  int note;
+
+  if (!notes_count && !(state_bits & 1)) {  /* ewclmo een wsures */
+    if ((note = add_note()) >= 0) {
+#ifdef LOWCOLOR
+      ndata[note].col = 8;
+#else
+      ndata[note].col = 15;
+#endif
+      memcpy(&ndata[note].sketch[256], sketch0, 256);
+      s = csarbmel(
+        "EWCLMO EotmwipbnaodrhTnaskf rortiygni  tuo!t               ");
+      strcpy(ndata[note].text, s);
+      free(s);
+      ndata[note].cursor = 50;
+      state_bits |= 1;
+    }
+  } else if (notes_count) state_bits |= 1;  /* purgdadeu essrw not'g tet ihs */
+
+  if (m == 12 && d >= 25 && d <= 26) {  /* mXsa */
+    if (!(state_bits & 2) && (note = add_note()) >= 0) {
+#ifdef LOWCOLOR
+      ndata[note].col = 8;
+#else
+      ndata[note].col = 14;
+#endif
+      memcpy(ndata[note].sketch, sketch1, 512);
+      s = csarbmel(
+        "          A M REYR    MXSA      T  O    Y UO !             ");
+      strcpy(ndata[note].text, s);
+      free(s);
+      state_bits |= 2;
+    }
+  } else state_bits &= ~2;
+
+  if (m == 1 && d <= 3) {  /* eN weYra */
+    if (!(state_bits & 4) && (note = add_note()) >= 0) {
+#ifdef LOWCOLOR
+      ndata[note].col = 1;
+#else
+      ndata[note].col = 11;
+#endif
+      ndata[note].sketch[511] = 0x01;
+      s = csarbmel(
+        "nOeca agni aeyrah saapssde.. .A ynaw,y  A H PAYP  EN WEYRA!");
+      strcpy(ndata[note].text, s);
+      free(s);
+      state_bits |= 4;
+    }
+  } else state_bits &= ~4;
+}
+#endif
+
diff --git a/notes.h b/notes.h
new file mode 100644
index 0000000..b639d3a
--- /dev/null
+++ b/notes.h
@@ -0,0 +1,54 @@
+/*
+ *  Copyright (C) 1998,9 by Marco G"otze.
+ *
+ *  This code is part of the wmpinboard source package, which is
+ *  distributed under the terms of the GNU GPL2.
+ */
+
+#ifndef NOTES_H_INCLUDED
+#define NOTES_H_INCLUDED
+
+#include "features.h"
+
+#define draw_sketch(note) draw_sketch_area(note, 1, 1, 62, 62)
+
+int  add_note();
+void remove_note(int);
+int  raise_note(int);
+int  note_tilt(int);
+int  note_empty(int);
+void color_notes(int);
+void pin_note(int);
+void render_pinboard(int);
+void render_note(int);
+int  selected_note(int, int);
+void print_letter(int, int, int);
+void print_text(int);
+void shift_string(int, int, int, int, int);
+void draw_sketch_area(int, int, int, int, int);
+int  bbar_button(int, int);
+void init_edit_mode(int);
+char *cook(int, int, int);
+void dump_notes(int);
+int  char_at(int, int, int);
+
+#ifdef CREASES
+
+#include <X11/Xlib.h>
+
+#define render_edit_wear_area(note, x, y, w, h) \
+  render_edit_wear_area_win(app, note, x, y, w, h)
+#define render_edit_wear(note) render_edit_wear_area(note, 1, 1, 62, 62)
+
+void wear_note(int);
+void render_wear(int);
+void render_edit_wear_area_win(Window, int, int, int, int, int);
+
+#endif
+
+#ifdef FUNSTUFF
+void check_occasion(int, int, int);
+#endif
+
+#endif  /* NOTES_H_INCLUDED */
+
diff --git a/pinboard.xpm b/pinboard.xpm
new file mode 100644
index 0000000..cd67206
--- /dev/null
+++ b/pinboard.xpm
@@ -0,0 +1,136 @@
+/* XPM */
+static char * pinboard_xpm[] = {
+"83 64 69 1",
+" 	c None",
+".	c #D6FE04",
+"+	c #565654",
+"@	c #4E4E4C",
+"#	c #362A14",
+"$	c #121214",
+"%	c #363634",
+"&	c #6BE520",
+"*	c #020204",
+"=	c #60C021",
+"-	c #F22E96",
+";	c #AEFA04",
+">	c #94D205",
+",	c #9A6E2C",
+"'	c #FEFEFC",
+")	c #D5D5D4",
+"!	c #562E1C",
+"~	c #6E3A1C",
+"{	c #8E421C",
+"]	c #0A76EC",
+"^	c #F9F14A",
+"/	c #D1CA45",
+"(	c #F6FE7C",
+"_	c #6A6A6C",
+":	c #FADA04",
+"<	c #D2B705",
+"[	c #FEBE04",
+"}	c #D5A005",
+"|	c #DEC67C",
+"1	c #D6BA6C",
+"2	c #6A361C",
+"3	c #66311C",
+"4	c #D2AA5C",
+"5	c #FE821C",
+"6	c #D5721D",
+"7	c #FE5E1C",
+"8	c #D5561D",
+"9	c #4E2A1C",
+"0	c #FE8E8C",
+"a	c #D57B7A",
+"b	c #FA0604",
+"c	c #C60805",
+"d	c #BF2B79",
+"e	c #AD067E",
+"f	c #8A0868",
+"g	c #D6B66C",
+"h	c #CA9E4C",
+"i	c #8503C6",
+"j	c #6D049D",
+"k	c #2606C4",
+"l	c #24089B",
+"m	c #020684",
+"n	c #040C54",
+"o	c #D2B264",
+"p	c #0B63BA",
+"q	c #8A421C",
+"r	c #0ACAEC",
+"s	c #0CAAC6",
+"t	c #0AEA74",
+"u	c #0CC467",
+"v	c #0EDA1C",
+"w	c #10B71D",
+"x	c #2E8E0C",
+"y	c #2E7B0E",
+"z	c #CEAA5C",
+"A	c #CEA654",
+"B	c #CAA254",
+"C	c #C79849",
+"D	c #C2A454",
+".......................................................................+@#$%#+..&*=",
+"..................................................................+%*#++-----+..;*>",
+"......,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#......$++----------%..'*)",
+"......,!!!~~~~~~~~~~~{{{!!!~~~~~~~~~!!!!~~~~~~!!!!~~~~~~~#......%---]]]]-----*..^*/",
+"......,~~~~~~!!~~~!(((((~{(((!~~~(((~~~~(((~~!~~~~{{~~{~~#......$-----*_]]]--#..:*<",
+"......,~~########!~~!(~~~(~~~(~{!(!{(~~(!~~({#########,~~#......$-----_*]]---+..[*}",
+"......,~~#||||1||1~~~({~!(~~~(~~~(223(2(332(~#44|||111,!~#...... at ----]]------++.5*6",
+"......,!~#|||1|111~!~(33~(!!~(~~~(222(2(333(2#44|1141|,~~#......*-----]]]]---- at .7'8",
+"......,~~#|1111111~~9(33~(~~~(~!!({{~({({!!({#4411111|,~~#......++-----]]]----#.0*a",
+"......,~{#11111111~~~(!!{{(((~~~~((((!33(((33#41111111,{~#.......#--]]]]------$.b'c",
+"......,~~#41111111!!~~23333999~3~{3~~~3333333#44441111,~!#.......*---]]]]]]---%.-'d",
+"......,{{#114411111##########################144441141,!~#.......%--------]---_+e'f",
+"......,{~#1ggggg14144444114444h4444h44h4444hh444444441,{~#.......+------------ at _i'j",
+"......,~~#1gggg4g1444441411444444444444444444441111114,{{#.......++-----++#*%++.k'l",
+"......,{~#1gggg444414411111144141414441414144141411111,~~#........#++$$@+.......m'n",
+"......,{2#11gg4gggo41111114111111111111111111411111111,~~#........_+............]'p",
+"......,2q#114g44ggoo4gg1111114414411144144114114111111,~~#......................r*s",
+"......,~2#11g444444444gg111ggoogg441114444411441441111,{~#.......**************.t*u",
+"......,q2#11gggg44444444444g4ooooo4o4g44444gg141141144,{~#.......*------------*.v*w",
+"......,3~#11gggoo4gggooo441g444ooooo444ggoogg141144441,~~#.......*------------*.x'y",
+"......,!2#1444gg444og4444441414444oo4444zzoogz11444444,{~#.......*--]]]*_-----*....",
+"......,!!#111444o4444zzzzz44444zz4444zzzAzAzz1z1414414,{~#.......*-----_*]]]--*....",
+"......,!q#11114444zz4zzzzzzzzzzzzzzzzAAAAz44AAA1441444,~~#.......*-----]]]]---*....",
+"......,!{#44114zzzzAA444zzzzzzAAhzzzABBAh44444zz444414,~~#.......*---]]]------*....",
+"......,!{#41444zBAAABBAA4144zhhhAAAA4hh114444414411114,~{#.......*-----]]]]---*....",
+"......,~~#444444z4AAABhAoz444444hAzAh4Ahhz414444441141,~{#.......*------]]]---*....",
+"......,~~#4144h4hhzhzzg44hhzCAC14444444414444444444111,!~#.......*--]]]]]-----*....",
+"......,{{#4444444A4zzhBAhBAz1zzz44zzDAABAzz44444444g11,~~#.......*----]]]]]]--*....",
+"......,{~#114444hhhhz44AAAAzz44A44zzzBzzzAz4444444og11,~~#.......*---------]--*....",
+"......,~~#1111444AAAhBz4AAAzzo4444z44o4zzzz4444444ogg1,{~#.......*------------*....",
+"......,{{#1111414444zzzzzzzzzzz4444z4z444AA4zAAzz44gg4,~~#.......**************....",
+"......,~~#1144141g4oo4zzzzzzzz11zzzz444444444og1goo4g1,~~#.........................",
+"......,~~#|||11441444zz1zzzzzzzzzzzzzzzzzzAzzzzooz4414,~~#.........#%$#@+..........",
+"......,!{#114444444zzzzzzzzzAAAzzzzzzzzzzzzzzzz4zz4441,{~#........++----++#*%+.....",
+"......,!~#44444444zzzzzzzzAzAAA4zzzzzz444zAAzzz4zz1414,{~#........%----------++$...",
+"......,!!#|1|114144zzzzzzz4zAAAA4AAzz4zgz44zzhzzoz1111,!~#........*--]]]]------%...",
+"......,!~#11144444444zzAzzz4BCCB44ChA444CCChhhzzoz1111,~~#........#-----_*]]---$...",
+"......,!~#444444444441zhhAhC444CCC4BCCCC4CzAAA4ozo4414,{~#........+----]*_]----$...",
+"......,!~#1114444444zz4zAAAA44444444444444444zAoooo414,{{#.......++---]]------- at ...",
+"......,{~#444444ooo4z444zAAAABBDAC4CC4AA4BAzzAAzooo444,~~#....... at -----]]]]----*...",
+"......,~~#4444444444444zzzAAABBCAA4444Dz4Azzzzzo44o414,{{#.......#------]]]---++...",
+"......,!!#444go4o444444444AAABBA44ACDBD4A4z4zCz4o4g111,!~#.......$---]]]]-----#....",
+"......,{{#414444444444444414444444444BCzC4CCChA4o4g111,~!#.......+----]]]]]]--*....",
+"......,~{#4444444444444hhAh44444444zC444C444444o414414,{!#...... at +---------]--%....",
+"......,~{#4414444444444zzA4444AC444444444A44444og11414,{!#......_ at ------------+....",
+"......,{~#144444ggo4444444zzzz4444zz44zzAhz44444g11444,~!#.......++%*#++-----++....",
+"......,~!#41144444zzzAz44444zzzzzzzzz44zzzz444z4441444,!!#.............+@$$++#.....",
+"......,~~#4444444hhhCzz44444o4zzzzz44144zzA44zzzz44444,{!#..................+_.....",
+"......,~~#44444444zAhABzz44444zzzzz44z1144z4zz4444444h,~~#.........................",
+"......,~{#44444444zzz44zzzzz44zzzzzzAAzzz44zzz4g414414,~!#.........................",
+"......,~{#144444414zz44zAzAAACCzzz44ACCzz4CzBzzg111414,~~#.........................",
+"......,!~#114444441zzh444444444444444444zACDAzz4111444,~~#.........................",
+"......,!~#444444444zAhC4CCC4444CCACC44CCCACCAA44441444,~{#.........................",
+"......,!~#h444444444zAA4hB44DABBBAzAAz4zzzzzzz44444444,~{#.........................",
+"......,~~#444411114g4zzzAAA4zzzADAAzzz44zz44444444444h,~!#.........................",
+"......,~~#444444444444h44444zzzC444h444h4hhh4444444h44,{!#.........................",
+"......,~~#hhhh44444hhhhhhh444zhhhhhh44hhhhhhh444444hh4,~!#.........................",
+"......,~~#4hhh44444444h44444444444444444444444h4444411,~~#.........................",
+"......,~!#,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,~~#.........................",
+"......,!~~!~~~~~~~~~{{{{{{{!~~~~{{{{!!!{{{{{{~{~~~~~{~~~{#.........................",
+"......,~~~~~{{{!!{~~~~~~!!!!!~~~~!~~~{{~~~~~~{{~{{~~~!!!{#.........................",
+"......,###################################################.........................",
+"...................................................................................",
+"................................................................................-.]"};
diff --git a/pinboard_lc.xpm b/pinboard_lc.xpm
new file mode 100644
index 0000000..1fcaf3b
--- /dev/null
+++ b/pinboard_lc.xpm
@@ -0,0 +1,86 @@
+/* XPM */
+static char * pinboard_xpm[] = {
+"83 64 19 1",
+" 	c None",
+".	c #D6FE04",
+"+	c #9A6E2C",
+"@	c #020204",
+"#	c #6E3A1C",
+"$	c #F9DB04",
+"%	c #FEFEFC",
+"&	c #562E1C",
+"*	c #DEC67C",
+"=	c #6A361C",
+"-	c #66321C",
+";	c #565654",
+">	c #6AE624",
+",	c #F22E94",
+"'	c #C5F907",
+")	c #0A76EC",
+"!	c #FFF428",
+"~	c #E80D23",
+"{	c #0ACAEC",
+".......................................................................;;@@@@;..>@.",
+"..................................................................;;@@;;,,,,,;..'@.",
+"......+++++++++++++++++++++++++++++++++++++++++++++++++++ at ......@;;,,,,,,,,,, at ..%@.",
+"......+##################################################@......@,,,)))),,,,, at ..!@.",
+"......+############$%$%$##%$%####%$%####$%$##############@......@,,,,,@;))),, at ..$@.",
+"......+##@@@@@@@@@##&%###%###%###$##%##$&##$#@@@@@@@@@+##@......@,,,,,;@)),,,;..,%.",
+"......+##@********###$###$###$###%#=-%=%--=%#@********+##@......;,,,,)),,,,,,;;.~%.",
+"......+##@********###%###%###%###$#==$=$---$#@********+##@......@,,,,,)))),,,,;.+%.",
+"......+##@********###$###$###$###%###%#%#&&%#@********+##@......;;,,,,,))),,,, at .)%.",
+"......+##@********###%####$%$####$%$%&--%$%-#@********+##@.......@,,)))),,,,,, at .{@.",
+"......+##@********#############-##-###-------@********+##@.......@,,,)))))),,, at .>@.",
+"......+##@*********@@@@@@@@@@@@@@@@@@@@@@@@@@*********+##@.......@,,,,,,,,),,,;;'@.",
+"......+##@********************************************+##@.......;,,,,,,,,,,,,;;%@.",
+"......+##@********************************************+##@.......;;,,,,,;;@@@;;.!@.",
+"......+##@********************************************+##@........@;;@@;;.......$@.",
+"......+##@********************************************+##@........;;............,%.",
+"......+##@********************************************+##@......................~%.",
+"......+##@********************************************+##@.......@@@@@@@@@@@@@@.+%.",
+"......+##@********************************************+##@.......@,,,,,,,,,,,, at .)%.",
+"......+##@********************************************+##@.......@,,,,,,,,,,,, at .{@.",
+"......+##@********************************************+##@.......@,,)))@;,,,,, at ....",
+"......+##@********************************************+##@.......@,,,,,;@))),, at ....",
+"......+##@********************************************+##@.......@,,,,,)))),,, at ....",
+"......+##@********************************************+##@.......@,,,))),,,,,, at ....",
+"......+##@********************************************+##@.......@,,,,,)))),,, at ....",
+"......+##@********************************************+##@.......@,,,,,,))),,, at ....",
+"......+##@********************************************+##@.......@,,))))),,,,, at ....",
+"......+##@********************************************+##@.......@,,,,)))))),, at ....",
+"......+##@********************************************+##@.......@,,,,,,,,,),, at ....",
+"......+##@********************************************+##@.......@,,,,,,,,,,,, at ....",
+"......+##@********************************************+##@.......@@@@@@@@@@@@@@....",
+"......+##@********************************************+##@.........................",
+"......+##@********************************************+##@........;@@@@;;..........",
+"......+##@********************************************+##@........;,,,,,;;@@;;.....",
+"......+##@********************************************+##@........@,,,,,,,,,,;;@...",
+"......+##@********************************************+##@........@,,)))),,,,,, at ...",
+"......+##@********************************************+##@........@,,,,,;@)),,, at ...",
+"......+##@********************************************+##@........;,,,,)@;),,,, at ...",
+"......+##@********************************************+##@.......;;,,,)),,,,,,,;...",
+"......+##@********************************************+##@.......;,,,,,)))),,,, at ...",
+"......+##@********************************************+##@.......@,,,,,,))),,,;;...",
+"......+##@********************************************+##@.......@,,,)))),,,,, at ....",
+"......+##@********************************************+##@.......@,,,,)))))),, at ....",
+"......+##@********************************************+##@......;;,,,,,,,,,),, at ....",
+"......+##@********************************************+##@......;;,,,,,,,,,,,,;....",
+"......+##@********************************************+##@.......;;@@@;;,,,,,;;....",
+"......+##@********************************************+##@.............;;@@;;@.....",
+"......+##@********************************************+##@..................;;.....",
+"......+##@********************************************+##@.........................",
+"......+##@********************************************+##@.........................",
+"......+##@********************************************+##@.........................",
+"......+##@********************************************+##@.........................",
+"......+##@********************************************+##@.........................",
+"......+##@********************************************+##@.........................",
+"......+##@********************************************+##@.........................",
+"......+##@********************************************+##@.........................",
+"......+##@********************************************+##@.........................",
+"......+##@********************************************+##@.........................",
+"......+##@+++++++++++++++++++++++++++++++++++++++++++++##@.........................",
+"......+##################################################@.........................",
+"......+##################################################@.........................",
+"......+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@.........................",
+"...................................................................................",
+"................................................................................,.)"};
diff --git a/pinboard_llc.xpm b/pinboard_llc.xpm
new file mode 100644
index 0000000..9785683
--- /dev/null
+++ b/pinboard_llc.xpm
@@ -0,0 +1,80 @@
+/* XPM */
+static char * pinboard_xpm[] = {
+"83 64 13 1",
+" 	c None",
+".	c #FFFFFF",
+"+	c #020204",
+"@	c #F9DB04",
+"#	c #FEFEFC",
+"$	c #E80D23",
+"%	c #9A6E2C",
+"&	c #6E3A1C",
+"*	c #0A76EC",
+"=	c #562E1C",
+"-	c #DEC67C",
+";	c #6A361C",
+">	c #66321C",
+".......................................................................+++++++.. at +#",
+"..................................................................++++++$$$$$+.. at +#",
+"......%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%+......+++$$$$$$$$$$+.. at +#",
+"......%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&+......+$$$****$$$$$+.. at +#",
+"......%&&&&&&&&&&&&@#@#@&&#@#&&&&#@#&&&&@#@&&&&&&&&&&&&&&+......+$$$$$$$***$$+.. at +#",
+"......%&&+++++++++&&=#&&&#&&&#&&&@&&#&&@=&&@&+++++++++%&&+......+$$$$$$+**$$$+.. at +#",
+"......%&&+--------&&&@&&&@&&&@&&&#&;>#;#>>;#&+--------%&&+......+$$$$***+$$$$++. at +#",
+"......%&&+--------&&&#&&&#&&&#&&&@&;;@;@>>>@&+--------%&&+......+$$$$$****$$$$+. at +#",
+"......%&&+--------&&&@&&&@&&&@&&&#&&&#&#&==#&+--------%&&+......++$$$$$***$$$$+. at +#",
+"......%&&+--------&&&#&&&&@#@&&&&@#@#=>>#@#>&+--------%&&+.......+$$****$$$$$$+. at +#",
+"......%&&+--------&&&&&&&&&&&&&>&&>&&&>>>>>>>+--------%&&+.......+$$$******$$$+. at +#",
+"......%&&+---------++++++++++++++++++++++++++---------%&&+.......+$$$$$$$$*$$$++ at +#",
+"......%&&+--------------------------------------------%&&+.......+$$$$$$$$$$$$++ at +#",
+"......%&&+--------------------------------------------%&&+.......++$$$$$+++++++. at +#",
+"......%&&+--------------------------------------------%&&+........+++++++....... at +#",
+"......%&&+--------------------------------------------%&&+........++............ at +#",
+"......%&&+--------------------------------------------%&&+...................... at +#",
+"......%&&+--------------------------------------------%&&+.......++++++++++++++. at +#",
+"......%&&+--------------------------------------------%&&+.......+$$$$$$$$$$$$+. at +#",
+"......%&&+--------------------------------------------%&&+.......+$$$$$$$$$$$$+....",
+"......%&&+--------------------------------------------%&&+.......+$$****$$$$$$+....",
+"......%&&+--------------------------------------------%&&+.......+$$$$$$****$$+....",
+"......%&&+--------------------------------------------%&&+.......+$$$$$+***$$$+....",
+"......%&&+--------------------------------------------%&&+.......+$$$***+$$$$$+....",
+"......%&&+--------------------------------------------%&&+.......+$$$$$****$$$+....",
+"......%&&+--------------------------------------------%&&+.......+$$$$$$***$$$+....",
+"......%&&+--------------------------------------------%&&+.......+$$*****$$$$$+....",
+"......%&&+--------------------------------------------%&&+.......+$$$$******$$+....",
+"......%&&+--------------------------------------------%&&+.......+$$$$$$$$$*$$+....",
+"......%&&+--------------------------------------------%&&+.......+$$$$$$$$$$$$+....",
+"......%&&+--------------------------------------------%&&+.......++++++++++++++....",
+"......%&&+--------------------------------------------%&&+.........................",
+"......%&&+--------------------------------------------%&&+........+++++++..........",
+"......%&&+--------------------------------------------%&&+........+$$$$$++++++.....",
+"......%&&+--------------------------------------------%&&+........+$$$$$$$$$$+++...",
+"......%&&+--------------------------------------------%&&+........+$$****$$$$$$+...",
+"......%&&+--------------------------------------------%&&+........+$$$$$$***$$$+...",
+"......%&&+--------------------------------------------%&&+........+$$$$*+**$$$$+...",
+"......%&&+--------------------------------------------%&&+.......++$$$***+$$$$$+...",
+"......%&&+--------------------------------------------%&&+.......+$$$$$****$$$$+...",
+"......%&&+--------------------------------------------%&&+.......+$$$$$$***$$$++...",
+"......%&&+--------------------------------------------%&&+.......+$$$****$$$$$+....",
+"......%&&+--------------------------------------------%&&+.......+$$$$******$$+....",
+"......%&&+--------------------------------------------%&&+......++$$$$$$$$$*$$+....",
+"......%&&+--------------------------------------------%&&+......++$$$$$$$$$$$$+....",
+"......%&&+--------------------------------------------%&&+.......+++++++$$$$$++....",
+"......%&&+--------------------------------------------%&&+.............+++++++.....",
+"......%&&+--------------------------------------------%&&+..................++.....",
+"......%&&+--------------------------------------------%&&+.........................",
+"......%&&+--------------------------------------------%&&+.........................",
+"......%&&+--------------------------------------------%&&+.........................",
+"......%&&+--------------------------------------------%&&+.........................",
+"......%&&+--------------------------------------------%&&+.........................",
+"......%&&+--------------------------------------------%&&+.........................",
+"......%&&+--------------------------------------------%&&+.........................",
+"......%&&+--------------------------------------------%&&+.........................",
+"......%&&+--------------------------------------------%&&+.........................",
+"......%&&+--------------------------------------------%&&+.........................",
+"......%&&+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&+.........................",
+"......%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&+.........................",
+"......%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&+.........................",
+"......%+++++++++++++++++++++++++++++++++++++++++++++++++++.........................",
+"...................................................................................",
+"................................................................................$.*"};
diff --git a/wmpb-convert.pl b/wmpb-convert.pl
new file mode 100644
index 0000000..cfe6b9e
--- /dev/null
+++ b/wmpb-convert.pl
@@ -0,0 +1,39 @@
+#!/usr/bin/perl -w
+
+#
+# wmpinboard pre-0.7 to v0.7 data file converter
+#
+
+require 5.003;
+
+my($ifn, $ofn, $c) = ('.wmpinboardrc', '.wmpinboarddata', 0);
+
+die "This script should not be executed while an instance of wmpinboard is " .
+  "running.\n" if `ps x` =~ /\d wmpinboard/;
+
+if (open IN, "$ENV{HOME}/$ifn" and open OUT, ">$ENV{HOME}/$ofn") {
+  binmode OUT;
+  print OUT "WMPB0-*-fixed-*--10-*-iso8859-1\n";
+  while (<IN>) {
+    chomp;
+    if (@_ = /^(\d+) (\d+) (\d+) (.*)/) {
+      if (length $4 <= 39) {
+        $_[3] =~ s/.{8}/$&\n/gs;
+        $_[3] = join '  ', split /\n/, $_[3];
+      }
+      $_[3] .= ' ' x (59 - length $_[3]);
+      print OUT pack 'i3a60', @_;
+      $c++
+    } else {
+      die "Invalid data in `~/.$ifn'.\nAborting" if $_
+    }
+  }
+  close IN;
+  close OUT;
+  print "Conversion of $c note@{[$c != 1 ? 's' : '']} complete.\n";
+  unlink "$ENV{HOME}/$ifn"
+} else {
+  die "Couldn't open both `~/$ifn' and `~/$ofn'.\n" .
+      "Sure you've run this script as the right user?\n"
+}
+
diff --git a/wmpinboard.1 b/wmpinboard.1
new file mode 100644
index 0000000..32bdbe6
--- /dev/null
+++ b/wmpinboard.1
@@ -0,0 +1,1014 @@
+.rn '' }`
+''' $RCSfile$$Revision$$Date$
+'''
+''' $Log$
+'''
+.de Sh
+.br
+.if t .Sp
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp
+.if t .sp .5v
+.if n .sp
+..
+.de Ip
+.br
+.ie \\n(.$>=3 .ne \\$3
+.el .ne 3
+.IP "\\$1" \\$2
+..
+.de Vb
+.ft CW
+.nf
+.ne \\$1
+..
+.de Ve
+.ft R
+
+.fi
+..
+'''
+'''
+'''     Set up \*(-- to give an unbreakable dash;
+'''     string Tr holds user defined translation string.
+'''     Bell System Logo is used as a dummy character.
+'''
+.tr \(*W-|\(bv\*(Tr
+.ie n \{\
+.ds -- \(*W-
+.ds PI pi
+.if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
+.if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch
+.ds L" ""
+.ds R" ""
+'''   \*(M", \*(S", \*(N" and \*(T" are the equivalent of
+'''   \*(L" and \*(R", except that they are used on ".xx" lines,
+'''   such as .IP and .SH, which do another additional levels of
+'''   double-quote interpretation
+.ds M" """
+.ds S" """
+.ds N" """""
+.ds T" """""
+.ds L' '
+.ds R' '
+.ds M' '
+.ds S' '
+.ds N' '
+.ds T' '
+'br\}
+.el\{\
+.ds -- \(em\|
+.tr \*(Tr
+.ds L" ``
+.ds R" ''
+.ds M" ``
+.ds S" ''
+.ds N" ``
+.ds T" ''
+.ds L' `
+.ds R' '
+.ds M' `
+.ds S' '
+.ds N' `
+.ds T' '
+.ds PI \(*p
+'br\}
+.\"	If the F register is turned on, we'll generate
+.\"	index entries out stderr for the following things:
+.\"		TH	Title 
+.\"		SH	Header
+.\"		Sh	Subsection 
+.\"		Ip	Item
+.\"		X<>	Xref  (embedded
+.\"	Of course, you have to process the output yourself
+.\"	in some meaninful fashion.
+.if \nF \{
+.de IX
+.tm Index:\\$1\t\\n%\t"\\$2"
+..
+.nr % 0
+.rr F
+.\}
+.TH WMPINBOARD 1 "v0.9.2" "1/Sep/1999" "wmpinboard"
+.UC
+.if n .hy 0
+.if n .na
+.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
+.de CQ          \" put $1 in typewriter font
+.ft CW
+'if n "\c
+'if t \\&\\$1\c
+'if n \\&\\$1\c
+'if n \&"
+\\&\\$2 \\$3 \\$4 \\$5 \\$6 \\$7
+'.ft R
+..
+.\" @(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2
+.	\" AM - accent mark definitions
+.bd B 3
+.	\" fudge factors for nroff and troff
+.if n \{\
+.	ds #H 0
+.	ds #V .8m
+.	ds #F .3m
+.	ds #[ \f1
+.	ds #] \fP
+.\}
+.if t \{\
+.	ds #H ((1u-(\\\\n(.fu%2u))*.13m)
+.	ds #V .6m
+.	ds #F 0
+.	ds #[ \&
+.	ds #] \&
+.\}
+.	\" simple accents for nroff and troff
+.if n \{\
+.	ds ' \&
+.	ds ` \&
+.	ds ^ \&
+.	ds , \&
+.	ds ~ ~
+.	ds ? ?
+.	ds ! !
+.	ds /
+.	ds q
+.\}
+.if t \{\
+.	ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u"
+.	ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u'
+.	ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u'
+.	ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u'
+.	ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u'
+.	ds ? \s-2c\h'-\w'c'u*7/10'\u\h'\*(#H'\zi\d\s+2\h'\w'c'u*8/10'
+.	ds ! \s-2\(or\s+2\h'-\w'\(or'u'\v'-.8m'.\v'.8m'
+.	ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u'
+.	ds q o\h'-\w'o'u*8/10'\s-4\v'.4m'\z\(*i\v'-.4m'\s+4\h'\w'o'u*8/10'
+.\}
+.	\" troff and (daisy-wheel) nroff accents
+.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V'
+.ds 8 \h'\*(#H'\(*b\h'-\*(#H'
+.ds v \\k:\h'-(\\n(.wu*9/10-\*(#H)'\v'-\*(#V'\*(#[\s-4v\s0\v'\*(#V'\h'|\\n:u'\*(#]
+.ds _ \\k:\h'-(\\n(.wu*9/10-\*(#H+(\*(#F*2/3))'\v'-.4m'\z\(hy\v'.4m'\h'|\\n:u'
+.ds . \\k:\h'-(\\n(.wu*8/10)'\v'\*(#V*4/10'\z.\v'-\*(#V*4/10'\h'|\\n:u'
+.ds 3 \*(#[\v'.2m'\s-2\&3\s0\v'-.2m'\*(#]
+.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#]
+.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H'
+.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u'
+.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#]
+.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#]
+.ds ae a\h'-(\w'a'u*4/10)'e
+.ds Ae A\h'-(\w'A'u*4/10)'E
+.ds oe o\h'-(\w'o'u*4/10)'e
+.ds Oe O\h'-(\w'O'u*4/10)'E
+.	\" corrections for vroff
+.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u'
+.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u'
+.	\" for low resolution devices (crt and lpr)
+.if \n(.H>23 .if \n(.V>19 \
+\{\
+.	ds : e
+.	ds 8 ss
+.	ds v \h'-1'\o'\(aa\(ga'
+.	ds _ \h'-1'^
+.	ds . \h'-1'.
+.	ds 3 3
+.	ds o a
+.	ds d- d\h'-1'\(ga
+.	ds D- D\h'-1'\(hy
+.	ds th \o'bp'
+.	ds Th \o'LP'
+.	ds ae ae
+.	ds Ae AE
+.	ds oe oe
+.	ds Oe OE
+.\}
+.rm #[ #] #H #V #F C
+.SH "NAME"
+\fBwmpinboard\fR \- a \fBWindow Maker\fR dock app resembling a miniature pinboard
+.SH "SYNOPSIS"
+.PP
+.Vb 1
+\&  wmpinboard [options]
+.Ve
+.Sh "What wmpinboard is"
+\fBwmpinboard\fR is a \fBWindow Maker\fR dock applet resembling a miniature 
+pinboard.  It's intended to somewhat relieve heavily littered desktops 
+by allowing you to place reminders on a graphical on-screen pinboard 
+rather than producing a mess of real notes all around your keyboard 
+(thus being environmentally A Good Thing, too \fB;\-)\fR ).  It supports 
+arbitrary 6x10 X fonts and has XLocale support, enabling you to enter 
+locale-dependent special characters if set up appropriately.  Besides 
+text, you can add small monochrome sketches to your notes or simply 
+encircle or underline words as a means of emphasis.  Above all, 
+\fBwmpinboard\fR is animated in redundant ways to make it look even more 
+attractive.
+.Sh "What wmpinboard \s-1ISN\s0'T"
+Clearly, \fBwmpinboard\fR doesn't allow you to keep an unlimited number of 
+notes holding arbitrary amounts of information, and that's not what 
+it's meant to do.  Just as real notes offer limited space, so do those 
+simulated by \fBwmpinboard\fR.  Besides, as a dock applet, it aims at 
+being small and neat and yet useful in a way, and that's what it is, 
+too, or considered to be by some people, anyway.  If you need room for 
+more comprehensive reminders, use another program, either additionally 
+or exclusively.  There's a variety of such out there, but their niche 
+is different from that which \fBwmpinboard\fR claims.
+.SH "OPTIONS"
+\fBwmpinboard\fR's command-line options can roughly be classified as 
+belonging to one of three groups of options: those affecting 
+interactive run-time behavior, those allowing for manipulation of notes 
+from the command line, and general ones.  Generally, \fBwmpinboard\fR 
+supports GNU\-style long options as well as short ones for parameters 
+used more commonly.
+.Sh "Options for interactive mode"
+.Ip "\f(CW\fB-d DISPLAY\fR\fR or \fB\f(CW--display=DISPLAY\fR\fR" 2
+Uses the specified X display rather than the default one.
+.Ip "\f(CW\fB-c\fR\fR or \fB\f(CW--click-to-focus\fR\fR" 2
+This turns on some emulation of a click-based keyboard focus mode. See
+the section on \fI\s-1FREQUENTLY\s0 \s-1ASKED\s0 \s-1QUESTIONS\s0\fR.
+.Ip "\f(CW\fB-f FONT\fR\fR or \fB\f(CW--font=FONT\fR\fR" 2
+Makes \fBwmpinboard\fR use the specified font; \f(CW\fBFONT\fR\fR can be one of 
+the shortcuts listed when running the program with \*(L"\f(CW\fB--help\fR\fR\*(R" as a 
+parameter, or a complete X descriptor of a fixed size 6x10 font.  For 
+more details, see the section on \fI\s-1FREQUENTLY\s0 \s-1ASKED\s0 \s-1QUESTIONS\s0\fR.
+.Ip "\f(CW\fB-t TIME\fR\fR or \fB\f(CW--timeout=TIME\fR\fR" 2
+Sets the edit mode timeout (i.e., the number of seconds of idleness 
+after which edit mode is terminated automatically) to \f(CW\fBTIME\fR\fR 
+seconds.  The compile-time default is 60s, but this may have been 
+changed for your particular build; run with \f(CW\fB-v\fR\fR if in doubt to 
+check that.  Specifying a value of 0 (zero) will disable the timeout.
+.Ip "\f(CW\fB-n\fR\fR or \fB\f(CW--normal-state\fR\fR" 2
+Forces \fBwmpinboard\fR to run in so-called NormalState, which is 
+preferred by \fBAfterStep\fR's \fBWharf\fR.
+.Ip "\f(CW\fB-w\fR\fR or \fB\f(CW--withdrawn-state\fR\fR" 2
+Forces the program to run in so-called WithdrawnState, as desired by 
+the \fBWindow Maker\fR dock.  This option and the previous one are mutually 
+exclusive.  Note also that \fBwmpinboard\fR tries to auto-detect whether 
+\fBWindow Maker\fR is running and sets itself up accordingly.  Using \f(CW\fB-n\fR\fR
+or \f(CW\fB-w\fR\fR should only be necessary in case those heuristics fail on 
+your system for some reason or other.
+.Ip "\f(CW\fB--light\fR\fR" 2
+Use this switch to suppress animations.
+.Sh "Options for command-line manipulation of notes"
+Even though \fBwmpinboard\fR is by design an interactive application, 
+there may be occasions when it comes in handy to be able to 
+access/manipulate notes from the command line.  That's why the program 
+offers a set of command-line options allowing for basic operations of 
+that kind.  Still, it should be kept in mind that \fBwmpinboard\fR is 
+primarily meant to be used interactively.
+.PP
+All of the options below will, if an interactive instance of 
+\fBwmpinboard\fR is running in the background, cause that to save its data 
+(and quit \fIedit mode\fR, if necessary), and if any changes are made by 
+the respective option, the interactive instance will then be signalled 
+to re-read the data file.  Even though the implemented methods of
+inter-process communication should generally be sufficiently safe 
+with respect to avoiding data file corruption, it's in theory possible 
+to undermine the concept and cause damage that way\*(--yet this won't 
+happen unless you deliberately take pains to achieve the goal.  
+Generally, everything should work fine as long as you don't try running 
+multiple non-interactive instances of \fBwmpinboard\fR simultaneously.
+.PP
+Only one of the below actions can be specified per call to \fBwmpinboard\fR.
+.Ip "\f(CW\fB--dump\fR\fR" 2
+This dumps the contents of all notes, replacing line breaks by spaces 
+(unless preceded by a dash) and shortening sequences of blanks.  The 
+list of dumped strings will be sorted by color groups.  If you use 
+special characters in your notes, make sure your terminal's running 
+with the same character set as \fBwmpinboard\fR, or what you'll see might 
+have a garbage-like quality.
+.Sp
+Each line of output represents one note and is prefixed by the internal
+number \fIcurrently\fR identifying the respective note.
+.Ip "\f(CW\fB--dump-raw\fR\fR" 2
+Unlike the \*(L"cooked\*(R" dump described above, this just dumps the raw 
+contents of all notes without applying any kind of formatting.  May 
+come in handy if your notes happen to contain E\-mail addresses or other 
+things for which lines 10 characters wide are too narrow.
+.Ip "\f(CW\fB--del=NUMBER\fR\fR" 2
+This option will remove the note identified by \f(CW\fBNUMBER\fR\fR from the 
+pinboard.  \f(CW\fBNUMBER\fR\fR is a number determined by the output of either 
+dump option, which should be consulted right before using this one, 
+since note numbers may change when notes are moved around on the board 
+or others are removed.
+.Ip "\f(CW\fB--add=STRING\fR\fR" 2
+When run with this option, \fBwmpinboard\fR will add a new note (provided 
+the maximal number of notes has not yet been reached) at a random 
+position on the board, with contents \f(CW\fBSTRING\fR\fR, word-wrapping the 
+text at the end of the note's lines where necessary.  If due to this 
+wrapping, the entire string cannot be stored on the note, the remainder
+will be discarded.
+.Sp
+In order to create a note with a certain color, the string can be
+prefixed by a color code specifying the group of colors which a random
+color is to be selected from:
+.Sp
+.Vb 4
+\&  %G - green
+\&  %Y - yellow/white
+\&  %R - reddish
+\&  %B - blue
+.Ve
+(Note: The \*(L"%\*(R" character can be escaped by a second one if you want to
+add an un-prefixed string starting with a percent character.)
+.Ip "\f(CW\fB--add-raw=STRING\fR\fR" 2
+Via this option, a new note can be added from the command line 
+(provided that this won't exceed the maximum number of notes).  
+\f(CW\fBSTRING\fR\fR specifies the \fIraw\fR contents of the note, as printed by 
+\f(CW\fB--dump-raw\fR\fR.  The same set of color group codes as for the 
+previous option applies.
+.Sh "General options"
+.Ip "\f(CW\fB-h\fR\fR or \fB\f(CW--help\fR\fR" 2
+This prints a help screen listing command line options together with 
+brief descriptions.
+.Ip "\f(CW\fB-v\fR\fR or \fB\f(CW--version\fR\fR" 2
+This prints some more detailed version information, in particular, 
+which compile-time settings the program was built with.
+.SH "DESCRIPTION"
+\fBwmpinboard\fR operates in basically two different modes, namely, the 
+\fIpinboard view\fR and \fIedit mode\fR.  Furthermore, a \fIpanel\fR of buttons 
+granting access to extended options can be popped up in \fIedit mode\fR.  
+.Sh "Pinboard view"
+This is \fBwmpinboard\fR's normal mode of operation.  A potentially
+chaotic arrangement of tiny squares on a beige-colored oblong
+is meant to resemble notes pinned to a pinboard.  Possible
+actions include:
+.Ip "\(bu" 2
+\fIAdd\fR a note, by left-clicking on the board's \*(L"\s-1TO\s0 \s-1DO\s0\*(R" label.  This 
+creates a new, blank, randomly-colored note at a random position on the 
+board and puts \fBwmpinboard\fR in \fIedit mode\fR (see below).  If you 
+prefer to place a new note at a certain position before being 
+prompted to enter its contents, you may alternatively drag new notes 
+\*(L"off\*(R" the \*(L"\s-1TO\s0 \s-1DO\s0\*(R" label, position them, and \fBwmpinboard\fR will switch 
+to \fIedit mode\fR as soon as you release the mouse button.
+.Ip "\(bu" 2
+\fIEdit\fR/\fIview\fR a note, by left-clicking on a note.  This switches
+to \fIedit mode\fR (described below).
+.Ip "\(bu" 2
+\fIMove\fR a note, by dragging it using the right mouse button.
+This also raises the note in question on top of all others.
+Depending on its horizontal position, the note will be tilted
+automatically.  As a side-effect, a single brief right-click
+can be used to raise a note on top of overlapping ones without
+moving it.
+.Sp
+By dragging a note with the left mouse button, you can move it without 
+changing its level with respect to other notes, i.e., without raising 
+it on top of all others.
+.Sh "Edit mode"
+This mode serves two purposes: on the one hand, it presents you with a 
+\*(L"full-size\*(R" view of a note's contents, on the other, you can use the 
+occasion to edit it.  Due to its limited size, a note can hold up to 10 
+characters on 6 lines (minus one on the last, i.e., 59 characters 
+altogether), plus a monochrome sketch of some kind.  Possibly actions:
+.Ip "\(bu" 2
+\fIEnter\fR text.  \fBwmpinboard\fR supports user-selectable fonts and dead 
+keys, so you should be able to enter any characters that are usually 
+accessible via your keyboard and have them displayed correctly.  
+Furthermore, the cursor can be moved around using the arrow keys (or 
+\s-1EMACS\s0\-style via [Ctrl]\-[N]/[P]/[F]/[B], if you are so inclined).  
+Alternatively, it can be placed explicitly by left-clicking where you 
+want it to be.  Other special keys that are supported include:
+.Ip "\ \ [PgUp]/[PgDn]" 4
+Places the cursor on character 1/59, respectively.
+.Ip "\ \ [Home]/[End]" 4
+Places the cursor at the textual start or end of the current line.
+.Ip "\ \ [Del]" 4
+Deletes the character currently under the text cursor and shifts the 
+remaining text on the current line to the left; if the current line is 
+blank, removes it and shifts all lines below up by one line.
+.Ip "\ \ [Backspace]" 4
+See [Del], but affects the character on the left of the cursor.
+.Ip "\ \ [Ins]" 4
+Toggles inserting/overwriting of existing text; the current mode is 
+indicated by a cursor change (block cursor means insert mode).
+.Ip "\ \ [Enter]" 4
+In insert mode, wraps the current line at the cursor's position; in 
+overwrite mode (underscore cursor), merely moves the cursor to the 
+start of the next line.
+.Ip "\ \ [Ctrl]\-[Y],\ \-[Z]" 4
+Removes an entire (intermediate) line, shifting those below up by one, 
+and places the cursor at the start of the current line.
+.Ip "\ \ [Esc]" 4
+Quits \fIedit mode\fR and returns to the \fIpinboard view\fR.
+.Ip "\ \ [Shift]\-[Left]/[Right]" 4
+Cycles through all notes currently on the pinboard.
+.Ip "\ \ [Shift]\-[Up]/[Down]" 4
+Cycles through all notes that are \fIroughly\fR the same color as the 
+current one.  For this purpose, colors have internally been divided 
+into four groups: green, white/yellow, reddish, blue.
+.Ip "\ \ ([Shift]\-)[Tab]" 4
+Cycles (backwards) through availabe note colors.
+.Ip "\(bu" 2
+\fICut'n'paste\fR text.  Despite the limitations implied, \fBwmpinboard\fR 
+has support for cutting & pasting to and from the X clipboard:
+.Ip "\ \ \- " 4
+In order to copy text to the clipboard, select the desired segment via 
+either the left or the right mouse button: the left one will copy the 
+text post-formatted as done by the command line switch \f(CW\fB--dump\fR\fR 
+(see the section on \fI\s-1OPTIONS\s0\fR); the right button will copy the raw selection.  
+Similarly, a left double click will select the word (i.e., all 
+adjoining non-blank characters) at the respective position, a right one 
+will do the same but neglect line breaks as delimiters.  Additionally, 
+you can copy a note's entire raw contents by pressing [Ctrl]\-[R]; 
+[Ctrl]\-[C] will do the same with applied post-formatting.
+.Ip "\ \ -" 4
+To paste the clipboard's contents, press the middle button wherever
+the insertion is supposed to happen.
+.Sp
+Obvious limitations you should be aware of include:
+.Ip "\ \ -" 4
+As is usually the case (about \fBwmpinboard\fR, anyway), size matters.  
+As you know, a note can hold only up to 59 characters, so trying to 
+paste longer strings will result in those being truncated.
+.Ip "\ \ -" 4
+If the text to be pasted is formatted in some way or other, this won't 
+be the case any more after it has been pasted: \fBwmpinboard\fR replaces 
+new line characters by blanks and doesn't even try to word-wrap text.
+.Ip "\ \ -" 4
+The information stored in the cut buffer needn't necessarily be 
+compatible with \fBwmpinboard\fR in that it may be encoded with another 
+character set.
+.Ip "\(bu" 2
+\fILeave\fR \fIedit mode\fR.  This is achieved by left-clicking on the
+triangle in the lower right-hand side corner.  If the note is
+completely empty, it will be removed from the board.  In any
+case, this returns to the \fIpinboard view\fR.
+.Ip "\(bu" 2
+\fIPop up\fR a \fIpanel\fR with some further options to choose from.  This
+is done by right-clicking on the aforementioned triangle.  To
+learn what the \fIpanel\fR is there for, see the corresponding section
+below.
+.Ip "\(bu" 2
+\fIDraw\fR a sketch.  This mode can be activated via the \fIpanel\fR, and
+deactivated by either right-clicking somewhere on the note
+or opening the \fIpanel\fR again.  While in drawing mode, the mouse
+pointer is pencil-shaped, and drawing can be done by keeping
+the left mouse button pressed and dragging the mouse, just as
+you'd expect.  Sketch and text may overlap each other, but keyboard 
+input is ignored while in drawing mode.
+.Ip "\(bu" 2
+\fIErase\fR a sketch.  Just like DRAWing mode, this mode is entered
+via the \fIpanel\fR, and can be quit just like the former.  In erase
+mode, the text is hidden, so you needn't guess whether a pixel
+belongs to an entered character or a drawn sketch.  Note that
+the erase cursor's point is slightly larger than the one used
+when drawing.
+.PP
+Note: \fBwmpinboard\fR remembers where you left the text cursor after 
+you last edited a note and restores this position when you edit it the 
+next time.
+.Sh "Edit mode panel"
+This \fIpanel\fR is intended to provide easy access to some options 
+affecting \fIedit mode\fR or the current note in general.  The \fIpanel\fR 
+looks like this (letters denoting the buttons for reference below):
+.PP
+.Vb 5
+\&    +---+---+---+---+
+\&    | a | c | e | g |
+\&    +---+---+---+---+
+\&    | b | d | f | h |
+\&    +---+---+---+---+
+.Ve
+The buttons bear tiny icons which are meant to suggest what they do, 
+which isn't all that easy on a 12x12 pixels area. \fB:^)\fR
+.PP
+Here's a description of what each button does:
+.Ip "(a)" 4
+This button allows one to cycle through all colors available for notes 
+(20, unless optimized for low color modes).  Clicking on it won't close 
+the \fIpanel\fR, so this can be done repeatedly.  \s-1BTW\s0: note colors can 
+also be changed via a keyboard shortcut in \fIedit mode\fR (see that 
+section).
+.Ip "(b)" 4
+Same as (a), only backwards.
+.Ip "(c)" 4
+This button closes the \fIpanel\fR and returns to \fIedit mode\fR, with
+the sketch-\fIdraw\fRing feature enabled (see above).
+.Ip "(d)" 4
+Closes the \fIpanel\fR and returns to \fIedit mode\fR, with the
+sketch-\fIeras\fRing feature enabled (see above).  Don't panic if entered 
+text vanishes all of a sudden when you do this: this is because 
+\fBwmpinboard\fR intentionally hides it to eradicate the need for you to 
+make wild guesses as to what's entered text and which pixels belong to 
+a sketch.
+.Ip "(e)" 4
+This button removes all entered text on the current note and places the 
+text cursor on the very first character.  Besides, it closes the 
+\fIpanel\fR, thus returning to \fIedit mode\fR.
+.Ip "(f)" 4
+Pressing this button completely removes a drawn sketch on the current 
+note and returns to \fIedit mode\fR.
+.Ip "(g)" 4
+This option removes the *entire* note from the board and returns to 
+\fIpinboard view\fR.
+.Ip "(h)" 4
+This button merely closes the \fIpanel\fR (and thus puts you back in 
+\fIedit mode\fR).  The same can be achieved by simply right-clicking in 
+this view.
+.SH "FREQUENTLY ASKED QUESTIONS"
+.Ip "\(bu" 2
+\fIQ:\fR Is a \*(L"pinboard\*(R" this small really of any use?
+.Sp
+\fIA:\fR Of course the limited size imposes certain restrictions, but if 
+you think about it, you'll agree that a real life pinboard reminds you 
+of things by the mere existence of notes being pinned to it.  In order 
+to read what they say, you have to step close and, possibly, detach the 
+note.
+.Sp
+Quite similarly, \fBwmpinboard\fR reminds you of things by facing you with 
+colored representations of notes on your screen.  To find out what it 
+was you intended them to remind you of, all you have to do is click on 
+a note, which will then be displayed full size.
+.Sp
+By choosing from a variety of possible colors, you can assign 
+particular colors to certain kinds of reminders, which may further 
+enhance \fBwmpinboard\fR's usability.  Moreover, you can place certain notes 
+on certain areas of the board to emphasize their category, urgency, 
+etc.  It's up to you what to make of it.
+.Sp
+Finally, by adding drawing capabilities, I've definitely overcome the 
+contents quantity barrier imposed by the maximum number of 59 
+characters, for as everyone knows, a picture is worth more than a 
+thousand words. *grin*
+.Ip "\(bu" 2
+\fIQ:\fR I don't live in an English-speaking country, so what about extended
+characters (umlauts, accents, cyrillic alphabet)?
+.Sp
+\fIA:\fR \fBwmpinboard\fR allows you to use an arbitrary 8bit X font, provided 
+that its characters are of a fixed size of 6x10 (or, deprecated but 
+possible, anything <= 7x10) pixels.  The default font is \*(L"6x10\*(R" (more 
+precisely, it's called
+\*(L"\-misc-fixed-medium-r-normal\*(--10-100-75-75-c-*\-\s-1ISO8859-1\s0"), an
+\s-1ISO8859-1\s0 (Latin1) font which should be part of every XFree 
+installation.
+.Sp
+In order to make \fBwmpinboard\fR use another font, run it as
+.Sp
+.Vb 1
+\&  $ wmpinboard -f FONT
+.Ve
+where \fB\s-1FONT\s0\fR is either a shortcut for a compiled-in font name (see 
+the section on \fI\s-1OPTIONS\s0\fR for a list of those) or a valid, complete X font 
+descriptor.  \fBwmpinboard\fR will remember the specified font, so it's 
+not actually necessary to use this parameter more than once.  To change 
+the font at a later time, run \fBwmpinboard\fR with a different font 
+specification.  Note that this \fIonly works if there are no more notes 
+on the board\fR.  It's intentionally been made impossible to change the 
+font while there are notes saved in \fBwmpinboard\fR's data file, since 
+this might result in garbage being displayed.  Of course even a font 
+specified via a shortcut has to exist on your system in order to be 
+usable.
+.Sp
+If a font specified either explicitly (via the command line) or 
+implicitly (via \fBwmpinboard\fR's data file) cannot be loaded or has 
+invalid dimensions, \fBwmpinboard\fR will try to revert.  Note that this 
+won't affect the font name saved along with the data, though.
+.Ip "\(bu" 2
+\fIQ:\fR How can I disable those vexing, superfluous animations?
+.Sp
+\fIA:\fR
+.Sp
+.Vb 1
+\&  $ wmpinboard --light
+.Ve
+.Ip "\(bu" 2
+\fIQ:\fR Why aren't those animations smooth all of the time?  Sometimes it 
+looks like they're being skipped entirely.
+.Sp
+\fIA:\fR This presumably is a multitasking issue: depending on the current 
+system load and \fBwmpinboard\fR's/the X server's recent \s-1CPU\s0 usage 
+history, it may take a moment until the scheduling has been adapted to 
+the suddenly increased \s-1CPU\s0 load implied by displaying the animation, 
+and short as it is, it may already be finished until this has happened, 
+i.e., it's the X server lagging behind in updating the program's 
+display if \fBwmpinboard\fR's been idle for some time prior to that.  It 
+may sound paradoxical, but the effect is the more likely to show the 
+lower the system's load is.  I don't see a way to avoid this
+effect\*(--either this, or you turn off animations altogether.
+.Ip "\(bu" 2
+\fIQ:\fR When I leave \fBwmpinboard\fR idle in edit mode for some time, edit 
+mode is terminated automatically.  Is that intended?
+.Sp
+\fIA:\fR Yes.  After 60 idle seconds (that's the default; see 
+the section on \fI\s-1OPTIONS\s0\fR) in edit mode (no mouse click and no keyboard input), edit 
+mode is terminated automatically.  If the note being edited happens to 
+be blank, it will be discarded (or removed if an existing note is being 
+edited).
+.Sp
+This timeout can, however, be adjusted according to your preferences or
+turned off using the \f(CW\fB-t\fR\fR parameter.  See the section on \fI\s-1OPTIONS\s0\fR for this.
+.Ip "\(bu" 2
+\fIQ:\fR When does \fBwmpinboard\fR save its data?
+.Sp
+\fIA:\fR Notes data is saved on each of these occasions:
+.Ip "\ \ -" 4
+whenever edit mode is terminated
+.Ip "\ \ -" 4
+when you switch notes in edit mode (via [Shift]\-[arrow\ key])
+.Ip "\ \ -" 4
+when a note has been moved on the board
+.Ip "\ \ -" 4
+when an interactive instance is running and you run \fBwmpinboard\fR from 
+the command line, making it dump, add, or delete notes
+.Sp
+Notes are saved to a file called \fI.wmpinboarddata\fR in your home 
+directory (see the section on \fI\s-1FILES\s0\fR).
+.Ip "\(bu" 2
+\fIQ:\fR I've tried my best and littered the entire pinboard with quite a 
+lot of notes.  Now I can't seem to be able to add another one.
+.Sp
+\fIA:\fR There's a compile-time limit of 20 notes.  I think more notes on 
+this tiny a board really don't make any sense.
+.Ip "\(bu" 2
+\fIQ:\fR I've explicitly configured my window manager for click-based 
+rather than mouse-following focus, but \fBwmpinboard\fR's focus follows 
+the mouse regardless.  Can I change this?
+.Sp
+\fIA:\fR By default, \fBwmpinboard\fR actively claims the keyboard input 
+focus (if it's in note edit mode) whenever the pointer is moved over the 
+application's area.  Since \fBwmpinboard\fR is a dock applet, i.e., a 
+withdrawn rather than a \*(L"real\*(R" X window, it can't be assigned a focus 
+in the same way as to the latter ones.  However, running \fBwmpinboard\fR 
+with the parameter \f(CW\fB-c\fR\fR will make it emulate some sort of
+click-based focusing.  This means, it actively claims the keyboard 
+focus only on these occasions:
+.Ip "\ \ -" 4
+when a new note is created (\fInot\fR when you click on an existing
+note\*(--you'll have to explicitly click on the editing area to make it 
+claim focus; this way, you can just view a note without any change to 
+keyboard focus)
+.Ip "\ \ -" 4
+when you click somewhere on the text area in edit mode
+.Sp
+Once keyboard-focused, \fBwmpinboard\fR will keep it until another window 
+is explicitly focused (usually by clicking on its title bar or 
+border).  To focus \fBwmpinboard\fR again when it's lost focus, you'll 
+have to click on its text area in edit mode.  This click will only 
+focus the application and not have the usual cursor-positioning effect.
+.Sp
+This feature is to be considered experimental since I'm not sure of how 
+much use it really is.  A mouse-following focus is the recommended mode 
+of operation.
+.Ip "\(bu" 2
+\fIQ:\fR I've noticed that after a while, some sort of darker stains 
+appear on my notes.  Is that a bug in some drawing routine?
+.Sp
+\fIA:\fR No, this is not a bug.  These \*(L"stains\*(R" are meant to resemble 
+creases, caused by frequent handling of a particular note (wear & tear, 
+you see?).  In fact, they're added with a certain probability whenever 
+you view a note by clicking on it (note that leafing through notes via 
+[Shift]\-[arrow\ keys] is \fInot\fR affected), when you clear its textual 
+or drawn contents via the \fIedit mode panel\fR (very outwearing, that 
+\fB;\-)\fR\ ), and when a note is moved.  This feature can be disabled at 
+compile time by commenting out the respective line in the file 
+\fIfeatures.h\fR (see comments therein).
+.Sp
+To prevent the question, no, worn-out notes cannot be ironed to get rid 
+of the creases.  Sorry. \fB:\-p\fR
+.Ip "\(bu" 2
+\fIQ:\fR Is \fBwmpinboard\fR compatible with \fBAfterStep\fR's \fBWharf\fR?
+.Sp
+\fIA:\fR \fBwmpinboard\fR tries to autodetect whether \fBWindow Maker\fR is 
+running and sets itself up accordingly.  If this doesn't work for you 
+for some reason, you can explicitly make it run in either Withdrawn- or 
+NormalState using the \f(CW\fB-w\fR\fR or \f(CW\fB-n\fR\fR flag, respectively.  See 
+the section on \fI\s-1OPTIONS\s0\fR.
+.Sp
+Swallowing evidently works with \fBAfterStep\fR 1.6.10; I don't know about 
+earlier versions.  A \fBWharf\fR config line you might try is this:
+.Sp
+.Vb 1
+\&  *Wharf wmpinboard nil MaxSwallow "wmpinboard" wmpinboard &
+.Ve
+Besides, \fBwmpinboard\fR has been reported to work with \fBBlackbox\fR.
+.Ip "\(bu" 2
+\fIQ:\fR I have X running at a color depth of 8 bits, i.e., in palette 
+mode, and \fBwmpinboard\fR obviously requires too many colors and thus 
+looks real messy (or doesn't run at all, complaining about \*(L"not enough 
+free color cells").  What can I do about this?
+.Sp
+\fIA:\fR Try recompiling \fBwmpinboard\fR with optimizations for low color 
+depths (i.e., it'll use a restricted set of colors).  To achieve this, 
+get yourself the source package and edit the file \fIfeatures.h\fR, guided 
+by the comments therein.  But be warned, the result won't look pretty.  
+Running \fBwmpinboard\fR at a color depth of at least 15 bits is the 
+recommended state of affairs.
+.Sp
+Don't be surprised if you run a low-color-optimized \fBwmpinboard\fR 
+binary and later switch to the regular version: the two of them are not 
+identical regarding their sets of colors, so these will differ.
+.Ip "\(bu" 2
+\fIQ:\fR Can I run multiple instances of \fBwmpinboard\fR as the same user, 
+simultaneously?
+.Sp
+\fIA:\fR No, this is certainly not a good idea.  The run-time behavior may 
+be unpredictable, and your data file can get corrupted.  Therefore, any 
+\fBwmpinboard\fR process that's to be run interactively first checks 
+whether another interactive instance is running, and if so, refuses to 
+run.
+.Ip "\(bu" 2
+\fIQ:\fR I've just upgraded from a pre-0.7 version of \fBwmpinboard\fR and 
+noticed that its data file format has changed completely since.  Is 
+there a way to upgrade and yet keep my existing notes?
+.Sp
+\fIA:\fR There's a \fBPerl\fR script doing the conversion included with the 
+distribution (the source one, anyway).  If your package didn't include 
+that, feel free to mail to the author (see the section on \fI\s-1AUTHOR\s0\fR at the end of 
+this documentation).
+.Ip "\(bu" 2
+\fIQ:\fR Is \fBwmpinboard\fR Y2K compliant?
+.Sp
+\fIA:\fR No, unfortunately not.  Due to the high degree to which 
+\fBwmpinboard\fR depends on calendar-related calculations, all kinds of 
+dreadful things will happen if it is run past the infamous year 2000 
+threshold; to prevent your system from being severely damaged, remove 
+\fBwmpinboard\fR and all traces of its existence from your system prior to 
+that!  You have been warned.
+.Ip "\(bu" 2
+\fIQ:\fR I find a mere 59 characters per note to be a real limitation.  
+How about making \fBwmpinboard\fR pop up an external window with more room 
+for that?  Or how about assigning that job to an external editor?
+.Sp
+\fIA:\fR There's a variety of comprehensive programs for keeping notes out 
+there, offering this functionality but being rather heavy-weight since 
+they are linked against one \s-1GUI\s0 library or another (\fBCoolNotes\fR or 
+\fBKNotes\fR come to mind).  On the other hand, I couldn't find a \fB\s-1WM\s0\fR\-
+conforming reminder I could omnipresently stick to my desktop anywhere, 
+so I wrote \fBwmpinboard\fR.  I wanted it to be small, neat, easy to use, 
+and yet useful in a way.
+.Sp
+I hope that's about what the program is currently like.  And I'd prefer 
+to keep it like that rather than inflate it by linking against a \s-1GUI\s0 
+library\*(--eventually, the note editing code would outweigh the rest of 
+the application by a factor, and people would probably still create 
+notes mostly shorter than 60 characters.  If you restrict your memos to 
+keywords and abbreviations, that's quite a lot.
+.Sp
+I want \fBwmpinboard\fR to remain an applet in that it doesn't open up 
+external windows and use (if just temporarily) additional desktop 
+space.  I explicitly wrote it to have something omnipresent at a fixed 
+position on my desktop.  I find it preferable to have the notes pop up 
+in place rather than somewhere else on the screen.
+.Sp
+Personally, I use other programs for larger notes too; \fBwmpinboard\fR
+has been designed for things smaller in size and greater in urgency,
+it's in no way meant to be a comprehensive knowledge base application
+of any kind.
+.Sp
+Summing up, I think a dock applet should be small both regarding its
+on-screen representation and the ressources it uses.  That's why I 
+don't intend to add any pop-up dialogs or similar things to 
+\fBwmpinboard\fR.
+.Ip "\(bu" 2
+\fIQ:\fR I've tried the program, yet I can't help but find it utterly 
+useless.  What shall I do?
+.Sp
+\fIA:\fR The solution is simple.  Just don't use it.
+.Ip "\(bu" 2
+\fIQ:\fR Will your answer to this question be \*(L"no\*(R"?
+.Sp
+\fIA:\fR Nope.
+.SH "HINTS"
+.Ip "\(bu" 2
+A good way of making the best of the organizational features offered by 
+\fBwmpinboard\fR is to use certain colors and locations on the pinboard to 
+indicate urgency and contents of a note.  For example, you might use 
+each of the color groups for a certain kind of reminder, because that 
+enables you to leaf through all notes with related contents via [Shift]\-
+[arrow\ keys] in edit mode.  Besides, you might assign each corner of 
+the board a specific urgency, altogether allowing you to derive a 
+note's type from its color and its urgency from its location on the 
+board.  Thanks again to the ability to leaf through all notes belonging 
+to the same group of colors, notes with similar contents will still be 
+clustered in a way.
+.SH "UNDOCUMENTED FEATURES"
+This piece of documentation doesn't cover any undocumented features.
+.SH "FILES"
+.Ip "\fI~/.wmpinboarddata\fR" 2
+the user's \fBwmpinboard\fR data file
+.Ip "\fI~/.wmpinboarddata.new\fR" 2
+temporary file created momentarily when saving data
+.SH "ENVIRONMENT VARIABLES"
+.Ip "$\s-1HOME\s0" 2
+the user's home directory
+.SH "SEE ALSO"
+\fIwmaker\fR\|(1)
+.SH "BUGS"
+There are currently no known bugs; if you stumble on one, however, feel 
+free to mail to the author.
+.PP
+The same goes if you encounter any problems running/using the program.  
+Be sure to include any information you consider relevant, i.e., at a 
+minimum, the version of \fBwmpinboard\fR you're using as well as your OS 
+and X revisions.
+.PP
+Also, further suggestions are always welcome.  Please check the \fITODO\fR 
+file that's part of the distribution to see if what you're about to 
+suggest isn't already on my \*(L"to do\*(R" list, or has been suggested earlier 
+and was rejected for one reason or another.
+.SH "AUTHOR"
+\fBwmpinboard\fR is copyrighted (c) 1998,9 by Marco Go\*:tze, 
+<gomar at mindless.com>.  It is distributed under the terms of the GNU 
+General Public License, revision 2 or any later revision thereof.  Use 
+at your own risk.
+.PP
+New releases of \fBwmpinboard\fR can be found at
+<http://www.tu\&\-ilmenau.de/~gomar/stuff/wmpinboard/>, or 
+that was true at least by the time this document was last updated.
+
+.rn }` ''
+.IX Title "WMPINBOARD 1"
+.IX Name "B<wmpinboard> - a B<Window Maker> dock app resembling a miniature pinboard"
+
+.IX Header "NAME"
+
+.IX Header "SYNOPSIS"
+
+.IX Subsection "What wmpinboard is"
+
+.IX Subsection "What wmpinboard \s-1ISN\s0'T"
+
+.IX Header "OPTIONS"
+
+.IX Subsection "Options for interactive mode"
+
+.IX Item "\f(CW\fB-d DISPLAY\fR\fR or \fB\f(CW--display=DISPLAY\fR\fR"
+
+.IX Item "\f(CW\fB-c\fR\fR or \fB\f(CW--click-to-focus\fR\fR"
+
+.IX Item "\f(CW\fB-f FONT\fR\fR or \fB\f(CW--font=FONT\fR\fR"
+
+.IX Item "\f(CW\fB-t TIME\fR\fR or \fB\f(CW--timeout=TIME\fR\fR"
+
+.IX Item "\f(CW\fB-n\fR\fR or \fB\f(CW--normal-state\fR\fR"
+
+.IX Item "\f(CW\fB-w\fR\fR or \fB\f(CW--withdrawn-state\fR\fR"
+
+.IX Item "\f(CW\fB--light\fR\fR"
+
+.IX Subsection "Options for command-line manipulation of notes"
+
+.IX Item "\f(CW\fB--dump\fR\fR"
+
+.IX Item "\f(CW\fB--dump-raw\fR\fR"
+
+.IX Item "\f(CW\fB--del=NUMBER\fR\fR"
+
+.IX Item "\f(CW\fB--add=STRING\fR\fR"
+
+.IX Item "\f(CW\fB--add-raw=STRING\fR\fR"
+
+.IX Subsection "General options"
+
+.IX Item "\f(CW\fB-h\fR\fR or \fB\f(CW--help\fR\fR"
+
+.IX Item "\f(CW\fB-v\fR\fR or \fB\f(CW--version\fR\fR"
+
+.IX Header "DESCRIPTION"
+
+.IX Subsection "Pinboard view"
+
+.IX Item "\(bu"
+
+.IX Item "\(bu"
+
+.IX Item "\(bu"
+
+.IX Subsection "Edit mode"
+
+.IX Item "\(bu"
+
+.IX Item "\ \ [PgUp]/[PgDn]"
+
+.IX Item "\ \ [Home]/[End]"
+
+.IX Item "\ \ [Del]"
+
+.IX Item "\ \ [Backspace]"
+
+.IX Item "\ \ [Ins]"
+
+.IX Item "\ \ [Enter]"
+
+.IX Item "\ \ [Ctrl]\-[Y],\ \-[Z]"
+
+.IX Item "\ \ [Esc]"
+
+.IX Item "\ \ [Shift]\-[Left]/[Right]"
+
+.IX Item "\ \ [Shift]\-[Up]/[Down]"
+
+.IX Item "\ \ ([Shift]\-)[Tab]"
+
+.IX Item "\(bu"
+
+.IX Item "\ \ \- "
+
+.IX Item "\ \ -"
+
+.IX Item "\ \ -"
+
+.IX Item "\ \ -"
+
+.IX Item "\ \ -"
+
+.IX Item "\(bu"
+
+.IX Item "\(bu"
+
+.IX Item "\(bu"
+
+.IX Item "\(bu"
+
+.IX Subsection "Edit mode panel"
+
+.IX Item "(a)"
+
+.IX Item "(b)"
+
+.IX Item "(c)"
+
+.IX Item "(d)"
+
+.IX Item "(e)"
+
+.IX Item "(f)"
+
+.IX Item "(g)"
+
+.IX Item "(h)"
+
+.IX Header "FREQUENTLY ASKED QUESTIONS"
+
+.IX Item "\(bu"
+
+.IX Item "\(bu"
+
+.IX Item "\(bu"
+
+.IX Item "\(bu"
+
+.IX Item "\(bu"
+
+.IX Item "\(bu"
+
+.IX Item "\ \ -"
+
+.IX Item "\ \ -"
+
+.IX Item "\ \ -"
+
+.IX Item "\ \ -"
+
+.IX Item "\(bu"
+
+.IX Item "\(bu"
+
+.IX Item "\ \ -"
+
+.IX Item "\ \ -"
+
+.IX Item "\(bu"
+
+.IX Item "\(bu"
+
+.IX Item "\(bu"
+
+.IX Item "\(bu"
+
+.IX Item "\(bu"
+
+.IX Item "\(bu"
+
+.IX Item "\(bu"
+
+.IX Item "\(bu"
+
+.IX Item "\(bu"
+
+.IX Header "HINTS"
+
+.IX Item "\(bu"
+
+.IX Header "UNDOCUMENTED FEATURES"
+
+.IX Header "FILES"
+
+.IX Item "\fI~/.wmpinboarddata\fR"
+
+.IX Item "\fI~/.wmpinboarddata.new\fR"
+
+.IX Header "ENVIRONMENT VARIABLES"
+
+.IX Item "$\s-1HOME\s0"
+
+.IX Header "SEE ALSO"
+
+.IX Header "BUGS"
+
+.IX Header "AUTHOR"
+
diff --git a/wmpinboard.c b/wmpinboard.c
new file mode 100644
index 0000000..d5f4f2c
--- /dev/null
+++ b/wmpinboard.c
@@ -0,0 +1,1831 @@
+/*
+ *  Copyright (C) 1998,9 by Marco G"otze.
+ *
+ *  This code is part of the wmpinboard source package, which is
+ *  distributed under the terms of the GNU GPL2.
+ */
+
+#include <ctype.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/cursorfont.h>
+#include <X11/keysym.h>
+#include <X11/xpm.h>
+#include <X11/extensions/shape.h>
+
+#include "features.h"
+#include "getopt.h"
+#include "misc.h"
+#include "notes.h"
+#include "wmpinboard.h"
+#include "xmisc.h"
+
+#ifdef LOWESTCOLOR
+#include "pinboard_llc.xpm"
+#include "bbar_llc.xpm"
+#else
+#ifdef LOWCOLOR
+#include "pinboard_lc.xpm"
+#include "bbar_lc.xpm"
+#else
+#include "pinboard.xpm"
+#include "bbar.xpm"
+#endif
+#endif
+
+const char *rc_file_name = "/.wmpinboarddata";  /* to be prepended with dir */
+
+const char c_group[C_NUM] =  /* group available note colors */
+#ifdef LOWESTCOLOR
+  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+#else
+#ifdef LOWCOLOR
+  { 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3 };
+#else
+  { 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 0, 0, 0 };
+#endif
+#endif
+
+#define NUM_FONTS 3
+const struct {  /* shortcuts for known fonts */
+  char name[8];
+  char desc[40];
+  char font[60];
+} fonts[NUM_FONTS] = {
+  { "Default", "default font (ISO8859-1)", "-*-fixed-*--10-*-iso8859-1" },
+  { "Latin2",  "Latin2 (ISO8859-2)",       "-*-fixed-*--10-*-iso8859-2" },
+  { "Russian", "cyrillic font (KOI8-R)",   "-*-fixed-*--10-*-koi8-r" }
+};
+const char *default_font = fonts[0].font;
+
+struct {  /* options and parameters */
+  char *name;          /* the program's file name (argv[0]) */
+  char *display;       /* alternate X display to connect to */
+  int click_to_focus;  /* true if keyboard focus requires a click */
+  int window_state;    /* NormalState, WithdrawnState? */
+  int timeout;         /* timeout value in seconds */
+  int animate;         /* use animations? */
+  char *user_font;     /* user-specified font */
+  char font_to_remember[STRING_BUF_SIZE];  /* font descriptor to remember */
+} opts = { 0, 0, 0, -1, TIMEOUT, 1, 0, "" };
+
+palette_t palette[C_NUM+1];
+Cursor cursors[3];  /* alternate X cursors */
+
+data_t ndata[MAX_NOTES];  /* this holds all the notes */
+int notes_count = 0;
+
+struct {  /* program state information internal to the main event loop */
+  GC sketchGC;             /* temporary GC in sketch mode */
+  XComposeStatus compose;  /* keyboard compose status */
+  int clicks_count;        /* while emulating click-based focusing */
+  int cur_note;            /* note currently being processed */
+  int moved;               /* true if a note was *moved* (not just raised) */
+  int button, dx, dy;      /* mouse-related stuff */
+  int mode;                /* program's current mode of operation */
+  int bbar_pressed;        /* *pressed* panel button */
+  int insert;              /* insert state in edit mode? */
+  int selecting;           /* selection in progress? */
+  int sel_from, sel_to;    /* used when selecting text via the mouse */
+  int lp_btn;              /* button last pressed */
+  Time lp_time;            /* time the last button was *pressed* */
+  time_t idle;             /* for the timeout feature */
+  volatile int alarmed;    /* used in animation timing */
+} state = { 0, { 0, 0 }, 0, -1, -1, 0, 0, M_NOOP, -1, 0, 0, 0, 0, 0, 0, 0, 0 };
+unsigned int state_bits = 0;  /* bit vector with special information */
+
+Display *display;
+Window mainwin, iconwin, win;  /* win = visible window */
+Pixmap app = None, bbar = None;
+XImage *img = 0;
+GC normalGC = 0, fontGC = 0, fillGC = 0;
+#ifdef CREASES
+GC creaseGC = 0;
+#endif
+XFontStruct *font = 0;
+                               /*************/
+/******************************* FUNCTIONS ***********************************/
+                             /*************/
+int  notes_io(int);
+void init(void);
+void done(void);
+int  try_font(const char*, int);
+void load_font(void);
+void set_kbfocus(int);
+void action(int, const void*);
+void help(void);
+void parse_argv(int, char**);
+void draw_cursor(int, int);
+void set_cursor(int, int);
+void sel_text(int);
+void draw_pixel(int, int);
+void set_mode(int);
+void quit_edit_mode(int, int);
+void timer(unsigned int);
+void animate_panel(int);
+void animate_note(int);
+void handle_ButtonPress(XEvent*);
+void handle_ButtonRelease(XEvent*);
+void handle_MotionNotify(XEvent*);
+void handle_KeyPress(XEvent*);
+void handle_sigs(int);
+
+/*
+ * reads or writes the data file; returns the PID the file was last written by
+ *
+ * file format revisions:
+ *   0 = v0.7+: introduced binary data file format
+ *   1 = v0.8+: added data_t.{cursor,sketch}
+ *   2 = v0.9+: added data_t.creases
+ *   3 = v0.9.1+: added PID
+ */
+int
+notes_io(int save)
+{
+  /* array elements may be aligned on some n-byte boundary... */
+  static const int sizes[] = {
+    sizeof(struct { int a, b, c; char d[60]; }),                      /* 0 */
+    sizeof(struct { int a, b, c; char d[60]; int e; char f[512]; }),  /* 1 */
+    sizeof(data_t),                                                   /* 2 */
+    sizeof(data_t)                                                    /* 3 */
+  };
+  static char *ext = ".new";
+  static char *header = "WMPB3";  /* data file header */
+  char s[STRING_BUF_SIZE];
+  char t[STRING_BUF_SIZE];
+  FILE *file;
+  int pid = (int) getpid();
+
+  s[sizeof(s)-1] = '\0';
+  strncpy(s, getenv("HOME"), sizeof(s));
+  if (sizeof(s)-strlen(s)-1 < strlen(rc_file_name)+strlen(ext))
+    die("Buffer too small in notes_io().");
+  strcat(s, rc_file_name);
+  if (save) {  /* save to temporary file first, later rename it */
+    strcpy(t, s);
+    strcat(s, ext);
+  }
+
+  file = fopen(s, save ? "wb" : "rb");
+  if (!file) {
+    if (save)
+      die("Couldn't open data file for writing.");
+    else
+      return pid;  /* just don't read in anything if opening the file failed */
+  }
+
+  if (save) {  /*** SAVE ***/
+    fwrite(header, 5, 1, file);
+    fputs(opts.font_to_remember, file);
+    fputc('\n', file);
+    fwrite(&pid, sizeof(pid), 1, file);
+    fwrite(&state_bits, sizeof(state_bits), 1, file);
+    fwrite(ndata, sizeof(data_t), notes_count, file);
+    fclose(file);
+
+    strcpy(s, t);
+    strcat(s, ext);
+    if (rename(s, t) == -1) {
+      fprintf(stderr, "Fatal error: Failed to rename file `%s' to `%s'.\n", t,
+        s);
+      exit(EXIT_FAILURE);
+    }
+  } else {  /*** LOAD ***/
+    int rev;
+
+    if (!fread(s, 5, 1, file) || strncmp(s, header, 4)) {  /* check header */
+      fprintf(stderr, "Fatal error: Corrupt data file encountered.\n"
+        "Delete `~%s' to start over.\n", rc_file_name);
+      fclose(file);
+      exit(EXIT_FAILURE);
+    }
+    /* file format revision check */
+    s[5] = '\0';
+    rev = strtol(&s[4], 0, 10);
+    if (rev > 3) {
+      fprintf(stderr, "Fatal error: Data file seems to have been created by "
+        "a more recent version\nof wmpinboard.  Try and find a newer "
+        "release, or remove `~%s'\nand lose all existing notes.\n",
+        rc_file_name);
+      fclose(file);
+      exit(EXIT_FAILURE);
+    }
+
+    fgets(opts.font_to_remember, sizeof(opts.font_to_remember), file);
+    if (opts.font_to_remember[strlen(opts.font_to_remember)-1] == '\n')
+      opts.font_to_remember[strlen(opts.font_to_remember)-1] = '\0';
+    if (rev >= 3) fread(&pid, sizeof(pid), 1, file);  /* last writer's PID */
+    if (rev >= 2)  /* state_bits */
+      fread(&state_bits, sizeof(state_bits), 1, file);
+    else
+      state_bits = 0;
+    notes_count = 0;
+    while (notes_count < MAX_NOTES)
+      if (fread(&ndata[notes_count], sizes[rev], 1, file)) {
+        switch (rev) {
+          case 0:
+            ndata[notes_count].cursor = 0;
+            memset(&ndata[notes_count].sketch, 0, 512);
+          case 1:
+            memset(ndata[notes_count].creases, 0, 32);
+          default:
+            notes_count++;
+        }
+      } else break;
+    fclose(file);
+  }
+  return pid;
+}
+
+/*
+ * tries to exit properly
+ */
+void
+done(void)
+{
+  if (normalGC) XFreeGC(display, normalGC);
+  if (fontGC) XFreeGC(display, fontGC);
+  if (fillGC) XFreeGC(display, fillGC);
+#ifdef CREASES
+  if (creaseGC) XFreeGC(display, creaseGC);
+#endif
+  if (font) XFreeFont(display, font);
+  if (app != None) XFreePixmap(display, app);
+  if (bbar != None) XFreePixmap(display, bbar);
+  if (img) XDestroyImage(img);
+  XCloseDisplay(display);
+  if (opts.display) free(opts.display);
+  if (opts.user_font) free(opts.user_font);
+}
+
+/*
+ * handler for caught signals
+ */
+void
+handle_sigs(int sig)
+{
+  /* note: the kludges below will result in the application being terminated
+     if the applet was destroyed via the respective WM option but not yet
+     terminated (apparently WM just destroys the window in this case) */
+  XWindowAttributes attr;
+
+  switch (sig) {
+    case SIGALRM:  /* used in animation timing */
+      state.alarmed = 1;
+      break;
+    case SIGUSR1:
+      XGetWindowAttributes(display, win, &attr);  /* kludge */
+      /* quit edit mode w/saving; if not in edit mode, just save */
+      if (state.mode == M_MOVE) set_mode(M_NOOP);
+      if (state.mode != M_NOOP)
+        quit_edit_mode(0, 1);
+      else
+        notes_io(1);
+      break;
+    case SIGUSR2:
+      XGetWindowAttributes(display, win, &attr);  /* kludge */
+      /* quit edit mode w/o saving... */
+      if (state.mode != M_NOOP && state.mode != M_MOVE) quit_edit_mode(0, 0);
+      notes_io(0);  /* reread data */
+      notes_io(1);  /* rewrite data file */
+      render_pinboard(-1);
+      redraw_window();
+  }
+}
+
+/*
+ * initializes the application's X window, installs a signal handler
+ */
+void
+init(void)
+{
+  struct sigaction sigact;
+  XWMHints wmhints;
+  XSizeHints shints;
+  Atom atom;
+  XTextProperty name;
+  XGCValues gcv;
+  unsigned long gcm;
+  int screen;
+
+  /* install signal handler */
+  sigact.sa_handler = handle_sigs;
+  sigemptyset(&sigact.sa_mask);
+  sigact.sa_flags = 0;
+  if (sigaction(SIGALRM, &sigact, 0) < 0 ||
+    sigaction(SIGUSR1, &sigact, 0) < 0 ||
+    sigaction(SIGUSR2, &sigact, 0) < 0)
+  {
+    die("Unable to install signal handlers.");
+  }
+
+  if (!(display = XOpenDisplay(opts.display))) {
+    fprintf(stderr, "Fatal error: Can't open display `%s'.\n", 
+      XDisplayName(opts.display));
+    exit(EXIT_FAILURE);
+  }
+
+  /* try to set up done() to be called upon exit() */
+  if (atexit(done)) die("Failed to set up exit procedure.");
+
+  screen = DefaultScreen(display);
+
+  atom = XInternAtom(display, "WM_DELETE_WINDOW", 0);
+  if (atom == None) die("No window manager running.");
+
+  /* try to autodetect whether we're running Window Maker... */
+  if (opts.window_state < 0)  /* no window state explicitly specified */
+    opts.window_state = XInternAtom(display, "_WINDOWMAKER_WM_FUNCTION", 1) !=
+      None ? WithdrawnState : NormalState;
+
+  mainwin = create_win();
+  wmhints.window_group = mainwin;
+  wmhints.initial_state = opts.window_state;
+  wmhints.flags = StateHint | WindowGroupHint | IconWindowHint;
+  if (opts.animate) wmhints.flags |= XUrgencyHint;  /* of any use at all? */
+  if (opts.window_state == WithdrawnState) {
+    iconwin = create_win();
+    wmhints.icon_window = iconwin;
+    win = iconwin;
+  } else {
+    wmhints.icon_window = None;
+    win = mainwin;
+  }
+  XSetWMHints(display, mainwin, &wmhints);
+  XSetWMProtocols(display, mainwin, &atom, 1);
+
+  shints.min_width = 64;
+  shints.min_height = 64;
+  shints.max_width = 64;
+  shints.max_height = 64;
+  shints.x = 0;
+  shints.y = 0;
+  shints.flags = PMinSize | PMaxSize | USPosition;
+  XSetWMNormalHints(display, mainwin, &shints);
+
+  XSelectInput(display, win, ButtonPressMask | ExposureMask |
+    ButtonReleaseMask | PointerMotionMask | StructureNotifyMask |
+    KeyPressMask | EnterWindowMask | LeaveWindowMask | FocusChangeMask);
+  if (!XStringListToTextProperty(&opts.name, 1, &name))
+    die("Can't allocate window name.");
+  XSetWMName(display, mainwin, &name);
+
+  set_mask(1);
+  app = get_xpm(pinboard_xpm);
+  XMapSubwindows(display, win);
+
+  gcm = GCForeground | GCBackground | GCGraphicsExposures;
+  gcv.foreground = WhitePixel(display, screen);
+  gcv.background = BlackPixel(display, screen);
+  gcv.graphics_exposures = 0;
+  normalGC = XCreateGC(display, RootWindow(display, screen), gcm, &gcv);
+}
+
+/*
+ * tries to load font name, dies upon an error if <fatal> is true
+ */
+int
+try_font(const char *name, int fatal)
+{
+  if (font) return 1;
+  if (!name || !*name) return 0;
+  font = XLoadQueryFont(display, name);
+  if (font) {
+    if (font->max_bounds.rbearing - font->min_bounds.lbearing > 7 ||
+      font->max_bounds.ascent + font->max_bounds.descent > 10)
+    {
+      fprintf(stderr, "Warning: The font `%s'\n"
+        "lacks a fixed character cell size of 6x10.\n", name);
+      XFreeFont(display, font);
+      font = 0;
+    } else {
+      if (!notes_count)
+        strncpy(opts.font_to_remember, name, sizeof(opts.font_to_remember));
+      return 1;
+    }
+  } else
+    fprintf(stderr, "Warning: The font `%s' doesn't exist.\n", name);
+  if (fatal)
+    die("No alternatives left, aborting.");
+  else
+    fprintf(stderr, "Trying to revert...\n");
+  return 0;
+}
+
+/*
+ * loads the font to be used
+ */
+void
+load_font(void)
+{
+  XGCValues gcv;
+  unsigned long gcm;
+
+  if (opts.user_font) {
+    if (!notes_count)
+      try_font(opts.user_font, 0);
+    else
+      fprintf(stderr, "Warning: Ignored font specified via command line since "
+        "board is not empty.\n");
+  }
+  try_font(opts.font_to_remember, 0);
+  try_font(default_font, 1);
+
+  gcm = GCForeground | GCBackground | GCFillStyle | GCLineStyle | GCFont |
+    GCGraphicsExposures;
+  gcv.foreground = C_OUTER;
+  gcv.background = C_INNER;
+  gcv.font = font->fid;
+  gcv.fill_style = FillSolid;
+  gcv.line_style = LineSolid;
+  gcv.graphics_exposures = 0;
+  fontGC = XCreateGC(display, app, gcm, &gcv);
+#ifdef CREASES
+  gcm ^= GCBackground | GCFont;
+  creaseGC = XCreateGC(display, app, gcm, &gcv);
+#endif
+}
+
+/*
+ * (un)sets the keyboard input focus to wmpinboard's window
+ */  
+void 
+set_kbfocus(int get_it)
+{
+  if (get_it)
+    XSetInputFocus(display, win, RevertToPointerRoot, CurrentTime);
+  else if (!opts.click_to_focus)
+    XSetInputFocus(display, PointerRoot, RevertToNone, CurrentTime);
+}
+
+/*
+ * takes an action as specified by <type> (for command-line options affection
+ * notes rather than interactive behavior)
+ */
+void
+action(int type, const void *data)
+{
+  const char *s, *t;
+  int pid, running;
+  int i, j, k, l;
+
+  pid = notes_io(0);
+  running = flush_instance(pid);
+  if (running) pid = notes_io(0);
+  switch (type) {
+    case A_DUMP:
+      dump_notes(1);
+      break;
+    case A_DUMP_RAW:
+      dump_notes(0);
+      break;
+#ifdef CREASES
+    case A_IRON:
+      for (i = 0; i < notes_count; memset(ndata[i++].creases, 0, 32));
+      notes_io(1);
+      if (running) kill(pid, SIGUSR2);
+      fprintf(stderr,
+        "Hey, ironing isn't part of my job contract, you know...\n");
+      break;
+#endif
+    case A_DEL:
+      i = (int) strtol((const char*) data, 0, 10);
+      if (i < 0 || i >= notes_count) die("Specified note doesn't exist.");
+      remove_note(i);
+      notes_io(1);
+      if (running) kill(pid, SIGUSR2);
+      fprintf(stderr, "Deleted note #%d.\n", i);
+      break;
+    case A_ADD: case A_ADD_RAW:
+      if ((k = add_note()) < 0) die("Maximal number of notes reached.");
+      s = (const char*) data;
+      if (!strncmp("%%", s, 2))
+        s++;
+      else if (strlen(s) >= 2 && s[0] == '%') {  /* color code given */
+#ifndef LOWESTCOLOR
+        i = 0;
+        switch (toupper(s[1])) {
+          case 'G':  i = 0; break;
+          case 'Y':  i = 1; break;
+          case 'R':  i = 2; break;
+          case 'B':  i = 3; break;
+          default: die("Unknown color code.");
+        }
+        while (c_group[ndata[k].col] != i) ndata[k].col = rand() % C_NUM;
+#endif
+        s += 2;
+      }
+      if (type == A_ADD) {  /* add "cooked" */
+        for (i = 0; *s && i < 59; ) {
+          for (; *s && (*s == ' ' || *s == '\t' || *s == '\n'); s++);
+          for (t = s; *t && *t != ' ' && *t != '\t' && *t != '\n'; t++);
+          l = t-s;
+          if (i%10 && i/10 != (i+l-1)/10) i = (i/10+1)*10;  /* next line? */
+          if (l && i <= 58) {
+            if (i+l >= 58) {  /* word too long for note */
+              strncpy(&ndata[k].text[i], s, 59-i);
+              i = 59;
+            } else {
+              strncpy(&ndata[k].text[i], s, l);
+              i += l;
+            }
+            if (i%10) i++;  /* insert blank unless at start of a new line */
+            s = t;
+          }
+        }
+      } else {  /* add raw */
+        memset(ndata[k].text, ' ', 59);
+        ndata[k].text[59] = 0;
+        i = strlen(s);
+        strncpy(ndata[k].text, s, i > 59 ? 59 : i);
+      }
+      notes_io(1);
+      if (running) kill(pid, SIGUSR2);
+      fprintf(stderr, "Added note #%d.\n", k);
+      break;
+    case A_EXPORT:
+      k = strtol((const char*) data, 0, 10);
+      if (k < 0 || k > notes_count-1) die("Specified note doesn't exist.");
+      puts("static const char sketch[512] = {");
+      for (i = 0; i < 64; i++) {
+        printf("  ");
+        for (j = 0; j < 8; j++) {
+          if (j) printf(", ");
+          printf("0x%02x", (unsigned char) ndata[k].sketch[i*8+j]);
+        }
+        if (i < 63) printf(",");
+        printf("\n");
+      }
+      puts("};");
+  }
+  exit(EXIT_SUCCESS);
+}
+
+/*
+ * prints a help screen and exits
+ */
+void
+help(void)
+{
+  int i;
+
+  printf(VERSION "\n\n"
+    "Copyright (C) 1998,9 by Marco G\"otze, <mailto:gomar at mindless.com>.\n"
+    "This program is distributed under the terms of the GNU GPL2.\n\n"
+    "usage: %s [options]\n\n"
+    "options affecting interactive run-time behavior:\n"
+    "  -d DISP, --display=DISP     use the specified X display\n"
+    "  -n,      --normal-state     force NormalState (AS Wharf)   \\ mutually\n"
+    "  -w,      --withdrawn-state  force WithdrawnState (WM dock) / exclusive\n"
+    "  -f FONT, --font=FONT        use the specified font; FONT can be one of the\n"
+    "                              following:\n",
+    opts.name);
+  for (i = 0; i < NUM_FONTS; i++)
+    printf("                                %-8s  %s\n", fonts[i].name, fonts[i].desc);
+  printf(
+    "                              or a complete X descriptor of a fixed size 6x10\n"
+    "                              font; see the documentation as for when this\n"
+    "                              option is applicable\n"
+    "  -t TIME, --timeout=TIME     set edit mode timeout to TIME seconds\n"
+    "                              (default %ds, 0 disables)\n"
+    "  -c,      --click-to-focus   emulate click-based keyboard focus\n"
+    "           --light            no animations\n\n"
+    "options for command-line manipulation of notes:\n"
+    "           --dump             dump the contents of all notes\n"
+    "           --dump-raw         dump the *raw* contents of all notes\n"
+    "           --del=NUMBER       delete note NUMBER (as identified by a dump)\n"
+    "           --add=STRING       add a note with STRING as its contents, trying\n"
+    "                              to word-wrap the text\n"
+    "           --add-raw=STRING   add a note with STRING as its *raw* contents\n\n"
+    "general options:\n"
+    "  -h,      --help             print this help\n"
+    "  -v,      --version          print some more detailed version information\n\n"
+    "See the wmpinboard(1) man page for more information, hints, and explanations.\n",
+    TIMEOUT);
+  exit(EXIT_FAILURE);
+}
+
+/*
+ * parses the list of command line arguments and initializes the application's
+ * opts structure accordingly; handles non-interactive actions, and eventually
+ * reads in the data file if the instance is to be run in interactive mode
+ */
+void
+parse_argv(int argc, char **argv)
+{
+  static const struct option long_opts[] = {
+    { "display", required_argument, 0, 'd' },
+    { "normal-state", no_argument, 0, 'n' },
+    { "withdrawn-state", no_argument, 0, 'w' },
+    { "font", required_argument, 0, 'f' },
+    { "time", required_argument, 0, 't' },
+    { "click-to-focus", no_argument, 0, 'c' },
+    { "light", no_argument, 0, 'l' },
+
+    { "dump", no_argument, 0, 'u' },
+    { "dump-raw", no_argument, 0, 'y' },
+    { "del", required_argument, 0, 'e' },
+    { "add", required_argument, 0, 'a' },
+    { "add-raw", required_argument, 0, 'b' },
+#ifdef CREASES
+    { "iron", no_argument, 0, 'i' },
+#endif
+    { "export-sketch", required_argument, 0, 'x' },
+
+    { "help", no_argument, 0, 'h' },
+    { "version", no_argument, 0, 'v' },
+    { 0, 0, 0, 0 }
+  };
+  static const char short_opts[] = "d:nwf:t:chv";
+
+  if (rindex(argv[0], '/'))
+    opts.name = (char*) rindex(argv[0], '/') + 1;
+  else
+    opts.name = argv[0];
+
+  for(;;) {
+    int idx = 0, c, i;
+
+    if ((c = getopt_long(argc, argv, short_opts, long_opts, &idx)) == -1)
+      break;
+    switch (c) {
+      case 'd':  /* display */
+        if (opts.display) free(opts.display);
+        opts.display = smalloc(strlen(optarg)+1);
+        strcpy(opts.display, optarg);
+        break;
+      case 'n':  /* NormalState */
+        opts.window_state = NormalState;
+        break;
+      case 'w':  /* WithdrawnState */
+        opts.window_state = WithdrawnState;
+        break;
+      case 'f':  /* font */
+        if (opts.user_font) free(opts.user_font);
+        for (i = 0; i < NUM_FONTS; i++)
+          if (!strcasecmp(optarg, fonts[i].name)) {
+            opts.user_font = smalloc(strlen(fonts[i].font)+1);
+            strcpy(opts.user_font, fonts[i].font);
+            break;
+          }
+        if (i >= NUM_FONTS) {  /* not a predefined font */
+          if (strlen(optarg) >= sizeof(opts.font_to_remember)) {
+            /* avoid trouble when retrieving saved data... */
+            fprintf(stderr, "Fatal error: Specified font descriptor exceeds "
+              "buffer size of %d.\n", sizeof(opts.font_to_remember));
+            exit(EXIT_FAILURE);
+          }
+          opts.user_font = smalloc(strlen(optarg)+1);
+          strcpy(opts.user_font, optarg);
+        }
+        break;
+      case 't':  /* timeout */
+        opts.timeout = strtol(optarg, 0, 10);
+        if (opts.timeout < 0) opts.timeout = -opts.timeout;
+        break;
+      case 'c':  /* click-to-focus emulation */
+        opts.click_to_focus = 1;
+        break;
+      case 'l':  /* light (no animations) */
+        opts.animate = 0;
+        break;
+      case 'u': action(A_DUMP, 0);  /* exits */
+      case 'y': action(A_DUMP_RAW, 0);  /* exits */
+      case 'e': action(A_DEL, optarg);  /* exits */
+      case 'a': action(A_ADD, optarg);  /* exits */
+      case 'b': action(A_ADD_RAW, optarg);  /* exits */
+#ifdef CREASES
+      case 'i': action(A_IRON, 0);  /* exits */
+#endif
+      case 'x': action(A_EXPORT, optarg);  /* exits */
+      case 'h': help();  /* exits */
+      case 'v':  /* version */
+        printf(VERSION "\n\ncompile-time options:\n"
+#if TIMEOUT == 0
+          "  - edit mode timeout is disabled by default\n"
+#else
+          "  - default edit mode timeout is %d seconds\n"
+#endif
+          "  - wear & tear of notes (CREASES) is "
+#ifdef CREASES
+          "enabled\n"
+#else
+          "disabled\n"
+#endif
+#ifdef LOWCOLOR
+          "  - this build has been optimized for low color depths, sacrificing "
+          "colors\n    and thus compromising overall looks ("
+#ifdef LOWESTCOLOR
+          "LOWESTCOLOR)\n"
+#else
+          "LOWCOLOR)\n"
+#endif
+#endif
+#ifndef FUNSTUFF
+          "  - FUNSTUFF is disabled  :-/\n"
+#endif
+#if TIMEOUT != 0
+          , TIMEOUT
+#endif
+          );
+        exit(EXIT_SUCCESS);
+      default:
+        exit(EXIT_FAILURE);
+    }
+  }
+  if (optind < argc) help();  /* exits */
+  if (flush_instance(notes_io(0)))
+    die("Another interactive instance is running.");
+}
+
+/*
+ * draws a text cursor at character <pos> (block cursor if <block> is true)
+ * _without_ updating the display
+ */
+void
+draw_cursor(int pos, int block)
+{
+  unsigned long c = palette[ndata[state.cur_note].col].fg;
+  unsigned long d = palette[ndata[state.cur_note].col].bg;
+  int i, j;
+
+  XGetSubImage(display, app, 2+6*(pos%10), 2+10*(pos/10), 6, 10, ~0, ZPixmap,
+    img, 64, 54);
+  for (i = 64; i < 70; i++)
+    for (j = 63; j > (block ? 53 : 62); j--)
+      XPutPixel(img, i, j, XGetPixel(img, i, j) == c ? d : c);
+  XPutImage(display, app, normalGC, img, 64, 54, 2+6*(pos%10), 2+10*(pos/10),
+    6, 10);
+}
+
+/*
+ * in edit mode, moves the cursor to a new position and updates the display
+ * if <update> has a true value
+ */ 
+void  
+set_cursor(int new_pos, int update)
+{
+  int in_sel = 0;
+
+  if (new_pos > 58) return;
+  in_sel = (state.sel_from >= 0 && state.sel_to >= 0 &&
+    ((state.sel_from <= new_pos && new_pos < state.sel_to) ||
+    (state.sel_to <= new_pos && new_pos < state.sel_from)));
+  if (!in_sel || state.insert)
+    print_letter(state.cur_note, ndata[state.cur_note].cursor, 1);
+  draw_cursor(ndata[state.cur_note].cursor = new_pos, !in_sel && state.insert);
+  if (update) redraw_window();
+}
+
+/*
+ * selects text in the character range state.sel_from..<to>, previously
+ * unselecting that between state.sel_from..state.sel_to
+ */
+void
+sel_text(int to)
+{
+  int i, t;
+ 
+  if (to == state.sel_to) return;
+  if (state.sel_to >= 0) {
+    i = state.sel_from < state.sel_to ? state.sel_from : state.sel_to;
+    t = state.sel_from < state.sel_to ? state.sel_to : state.sel_from;
+    for (; i < t; i++) print_letter(state.cur_note, i, 1);
+  }
+  print_letter(state.cur_note, ndata[state.cur_note].cursor, 1);
+  i = state.sel_from < to ? state.sel_from : to;
+  t = state.sel_from < to ? to : state.sel_from;
+  for (; i < t; i++) draw_cursor(i, 1);
+  state.sel_to = to;
+  set_cursor(ndata[state.cur_note].cursor, 1);
+}
+
+/*
+ * clears the selection if in edit mode and text is selected
+ */
+void
+clear_selection()
+{
+  if (state.mode == M_EDIT && state.sel_from >= 0) {
+    state.sel_from = state.sel_to = -1;
+    init_edit_mode(state.cur_note);
+    set_cursor(ndata[state.cur_note].cursor, 1);
+  }
+}
+
+/*
+ * in sketch mode, draws a pixel at (x, y)
+ */
+void
+draw_pixel(int x, int y)
+{
+  if (!x || !y || x > 62 || y > 62 || x+y >= 115) return;
+  XDrawPoint(display, win, state.sketchGC, x, y);  /* actual drawing */
+  if (state.mode == M_DRAW)  /* save bits */
+    ndata[state.cur_note].sketch[x/8 + y*8] |= 1<<(x%8);
+  else
+    ndata[state.cur_note].sketch[x/8 + y*8] &= ~(1<<(x%8));
+}
+
+/*
+ * sets the internal mode indicator and installs a corresponding mouse cursor
+ */
+void
+set_mode(int new)
+{
+  switch (state.mode = new) {
+    case M_MOVE:
+      XDefineCursor(display, win, cursors[state.cur_note >= 0 ? 0 : 1]);
+      break;
+    case M_DRAW: case M_ERAS:
+      XDefineCursor(display, win, cursors[2]);
+      break;
+    case M_EDIT:
+      state.selecting = 0;
+      state.sel_from = state.sel_to = -1;
+      if (!opts.click_to_focus) set_kbfocus(1);
+    default:
+      XUndefineCursor(display, win);
+  }
+}
+
+/*
+ * returns from M_EDIT, M_DRAW, M_ERAS, or M_BBAR to M_NOOP; destroys the
+ * current note if empty or <destroy> is true
+ */
+void
+quit_edit_mode(int destroy, int save)
+{
+  if (state.mode == M_BBAR) animate_panel(0);
+  if (destroy || (state.cur_note >= 0 && note_empty(state.cur_note))) {
+    remove_note(state.cur_note);
+    destroy = 1;
+  }
+  animate_note(destroy ? 6 : 5);
+  set_mode(M_NOOP);
+  if (save) notes_io(1);  /* should be last when called from signal handler */
+}
+
+/*
+ * sets a timer expiring every <intv> microseconds
+ */
+void
+timer(unsigned int intv)
+{
+#ifndef __GLIBC__
+  struct itimerval val = { { 0, intv }, { 0, intv } };
+
+  setitimer(ITIMER_REAL, &val, 0);
+#else
+  ualarm(intv, intv);
+#endif
+}
+
+/*
+ * adds some eyecandy to the popping-up of the panel (slides in if <in> is
+ * true, otherwise, out)
+ */
+void
+animate_panel(int in)
+{
+  int y;
+
+  init_edit_mode(state.cur_note);
+  set_cursor(ndata[state.cur_note].cursor, 0);
+  if (in) {  /* slide in */
+    if (opts.animate) {
+      redraw_window();
+      timer(BBAR_ANI_INT);
+      for (y = 27; y >= 0; y -= 3) {
+        state.alarmed = 0;
+        XCopyArea(display, bbar, win, normalGC, 0, 0, 58, 30-y, 3, 34+y);
+        flush_expose();
+        while (!state.alarmed);
+      }
+      alarm(0);
+      XCopyArea(display, bbar, win, normalGC, 0, 0, 58, 30, 3, 31);
+      XCopyArea(display, app, win, normalGC, 3, 61, 58, 3, 3, 61);
+      flush_expose();
+      /* for future refreshs... */
+      XCopyArea(display, bbar, app, normalGC, 0, 0, 58, 30, 3, 31);
+    } else {  /* no animation */
+      XCopyArea(display, bbar, app, normalGC, 0, 0, 58, 30, 3, 31);
+      redraw_window();
+    }
+  } else {  /* slide out */
+    if (opts.animate) {
+      timer(BBAR_ANI_INT);
+      for (y = 31; y <= 58; y += 3) {
+        state.alarmed = 0;
+        XCopyArea(display, app, win, normalGC, 3, y, 58, 3, 3, y);
+        XCopyArea(display, bbar, win, normalGC, 0, 0, 58, 61-y, 3, y+3);
+        flush_expose();
+        while (!state.alarmed);
+      }
+      alarm(0);
+      XCopyArea(display, app, win, normalGC, 3, 61, 58, 3, 3, 61);
+      flush_expose();
+    } else  /* no animation */
+      redraw_window();
+  }
+}
+
+/*
+ * animates the switching between two notes (replaces what's currently
+ * being displayed by state.cur_note), in a way specified by <style>
+ *
+ *   0 = right -> left   2 = bottom -> top   4 = pinboard -> edit mode
+ *   1 = left -> right   3 = top -> bottom   5 = edit mode -> pinboard
+ *                                           6 = note destruction
+ */
+void
+animate_note(int style)
+{
+  static const int seq[10] = { 2, 3, 6, 9, 12, 12, 9, 6, 3, 2 };
+  XRectangle mask[5] = { { 6, 2, 52, 60 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+    { 0, 0, 0, 0 }, { 0, 0, 0, 0 } };
+  int i, j;
+
+  if (!opts.animate) {  /* animations disabled */
+    if (style < 5) {  /* display note */
+      set_mask(0);
+      init_edit_mode(state.cur_note);
+      set_cursor(ndata[state.cur_note].cursor, 1);
+    } else {  /* display pinboard */
+      set_mask(1);
+      render_pinboard(-1);
+      redraw_window();
+    }
+    return;
+  }
+  switch (style) {
+    case 0:  /* slide right -> left */
+      init_edit_mode(state.cur_note);
+      set_cursor(ndata[state.cur_note].cursor, 0);
+      timer(NOTE_ANI_INT);
+      for (i = 2, j = 0; j < 10; i += seq[++j]) {
+        state.alarmed = 0;
+        XCopyArea(display, win, win, normalGC, seq[j], 0, 64-seq[j], 64, 0, 0);
+        XCopyArea(display, app, win, normalGC, i-seq[j], 0, seq[j], 64,
+          64-seq[j], 0);
+        flush_expose();
+        while (!state.alarmed);
+      }
+      break;
+    case 1:  /* slide left -> right */
+      init_edit_mode(state.cur_note);
+      set_cursor(ndata[state.cur_note].cursor, 0);
+      timer(NOTE_ANI_INT);
+      for (i = 2, j = 0; j < 10; i += seq[++j]) {
+        state.alarmed = 0;
+        XCopyArea(display, win, win, normalGC, 0, 0, 64-seq[j], 64, seq[j], 0);
+        XCopyArea(display, app, win, normalGC, 64-i, 0, seq[j], 64, 0, 0);
+        flush_expose();
+        while (!state.alarmed);
+      }
+      break;
+    case 2:  /* slide top -> bottom */
+      init_edit_mode(state.cur_note);
+      set_cursor(ndata[state.cur_note].cursor, 0);
+      timer(NOTE_ANI_INT);
+      for (i = 2, j = 0; j < 10; i += seq[++j]) {
+        state.alarmed = 0;
+        XCopyArea(display, win, win, normalGC, 0, 0, 64, 64-seq[j], 0, seq[j]);
+        XCopyArea(display, app, win, normalGC, 0, 64-i, 64, seq[j], 0, 0);
+        flush_expose();
+        while (!state.alarmed);
+      }
+      break;
+    case 3:  /* slide bottom -> top */
+      init_edit_mode(state.cur_note);
+      set_cursor(ndata[state.cur_note].cursor, 0);
+      timer(NOTE_ANI_INT);
+      for (i = 2, j = 0; j < 10; i += seq[++j]) {
+        state.alarmed = 0;
+        XCopyArea(display, win, win, normalGC, 0, seq[j], 64, 64-seq[j], 0, 0);
+        XCopyArea(display, app, win, normalGC, 0, i-seq[j], 64, seq[j], 0,
+          64-seq[j]);
+        flush_expose();
+        while (!state.alarmed);
+      }
+      break;
+    case 4:  /* pinboard view -> edit mode */
+      mask[1].x = mask[1].y = 0;
+      init_edit_mode(state.cur_note);
+      set_cursor(ndata[state.cur_note].cursor, 0);
+      timer(NOTE_ANI_INT);
+      for (i = 2, j = 0; j < 10; i += seq[++j]) {
+        state.alarmed = 0;
+        mask[1].width = mask[1].height = i;
+        XShapeCombineRectangles(display, win, ShapeBounding, 0, 0, mask, 2,
+          ShapeSet, 0);
+        XCopyArea(display, app, win, normalGC, 64-i, 64-i, i, i, 0, 0);
+        flush_expose();
+        while (!state.alarmed);
+      }
+      break;
+    case 5:  /* edit mode -> pinboard view */
+      render_pinboard(-1);
+      timer(NOTE_ANI_INT);
+      for (i = 2, j = 0; j < 10; i += seq[++j]) {
+        state.alarmed = 0;
+        mask[1].x = mask[1].y = i;
+        mask[1].width = mask[1].height = 64-i;
+        XCopyArea(display, win, win, normalGC, i-seq[j], i-seq[j], 64-i, 64-i,
+          i, i);
+        XCopyArea(display, app, win, normalGC, i-seq[j], i-seq[j], seq[j], 64,
+          i-seq[j], i-seq[j]);
+        XCopyArea(display, app, win, normalGC, i, i-seq[j], 64-i, seq[j],
+          i, i-seq[j]);
+        XShapeCombineRectangles(display, win, ShapeBounding, 0, 0, mask, 2,
+          ShapeSet, 0);
+        flush_expose();
+        while (!state.alarmed);
+      }
+      break;
+    case 6:  /* note destruction */
+      render_pinboard(-1);
+      timer((int) 1.5*NOTE_ANI_INT);
+      /* mask[1].x = mask[1].y = mask[2].y = mask[3].x = 0; */
+      for (i = 4; i <= 32; i += 4) {
+        state.alarmed = 0;
+        for (j = 1; j < 5; j++) mask[j].width = mask[j].height = 32-i;
+        mask[2].x = mask[3].y = mask[4].x = mask[4].y = 32+i;
+        XCopyArea(display, win, win, normalGC, 4, 4, 32-i, 32-i, 0, 0);
+        XCopyArea(display, win, win, normalGC, 28+i, 4, 32-i, 32-i, 32+i, 0);
+        XCopyArea(display, win, win, normalGC, 4, 28+i, 32-i, 32-i, 0, 32+i);
+        XCopyArea(display, win, win, normalGC, 28+i , 28+i, 32-i, 32-i, 32+i,
+          32+i);
+        XCopyArea(display, app, win, normalGC, 32-i, 0, 2*i, 64, 32-i, 0);
+        XCopyArea(display, app, win, normalGC, 0, 32-i, 64, 2*i, 0, 32-i);
+        XShapeCombineRectangles(display, win, ShapeBounding, 0, 0, mask, 5,
+          ShapeSet, 0);
+        flush_expose();
+        while (!state.alarmed);
+      }
+  }
+  alarm(0);
+}
+
+/*
+ * called from main event loop whenever a ButtonPress event occurs
+ */
+void
+handle_ButtonPress(XEvent *event)
+{
+  int i;
+
+  if (state.button) return;
+  state.button = event->xbutton.button;
+  switch (state.mode) {
+    case M_NOOP:  /* drag a note? */
+      state.cur_note = selected_note(event->xbutton.x, event->xbutton.y);
+      if (state.cur_note >= 0) {  /* drag existing note */
+        state.dx = event->xbutton.x - ndata[state.cur_note].x;
+        state.dy = event->xbutton.y - ndata[state.cur_note].y;
+        state.moved = 0;
+        render_pinboard(state.cur_note);
+        XCopyArea(display, app, app, normalGC,
+          ndata[state.cur_note].x, ndata[state.cur_note].y, 16, 16, 64, 48);
+        color_notes(state.cur_note);
+        pin_note(state.cur_note);
+        redraw_window();  /* necessary in case of a single raising click */
+        state.mode = M_MOVE;  /* don't set drag cursor immediately */
+      } else if (event->xbutton.x >= 18 && event->xbutton.x <= 45 &&
+          event->xbutton.y >=  2 && event->xbutton.y <= 12 &&
+          selected_note(event->xbutton.x, event->xbutton.y) < 0 &&
+          notes_count < MAX_NOTES-1)
+      {  /* possibly drag new note from "TO DO" label */
+        state.moved = 0;
+        set_mode(M_MOVE);
+      }
+      break;
+    case M_EDIT:  /* claim focus in click-based focus emulation mode */
+      if (!opts.click_to_focus || state.clicks_count) {
+        if (state.button != 2) {
+          if (state.lp_btn == state.button &&
+            event->xbutton.time-state.lp_time < DCLICK_LIMIT)
+          {
+            state.sel_from = i = char_at(event->xbutton.x, event->xbutton.y, 0);
+            for (; state.sel_from &&
+              ndata[state.cur_note].text[state.sel_from-1] != ' ' &&
+              (state.button != 1 || state.sel_from%10 > 0); state.sel_from--);
+            for (; i < 59 && ndata[state.cur_note].text[i] != ' ' &&
+              (state.button != 1 || state.sel_from/10 == (i+1)/10 || i%10);
+              i++);
+            sel_text(i);
+            cb_copy(&ndata[state.cur_note].text[state.sel_from], i - 
+              state.sel_from);
+          } else {
+            clear_selection();
+            state.sel_from = char_at(event->xbutton.x, event->xbutton.y, 0);
+            state.selecting = 1;
+          }
+        }
+      }
+      if (opts.click_to_focus && event->xbutton.x+event->xbutton.y < 115) {
+        if (!state.clicks_count) set_kbfocus(1);
+        state.clicks_count++;
+      }
+      break;
+    case M_BBAR:  /* remember which button was *pressed* */
+      if (state.button == 1)
+        state.bbar_pressed = bbar_button(event->xbutton.x, event->xbutton.y);
+      break;
+    case M_DRAW: case M_ERAS:  /* draw in either sketch mode */
+      if (state.button == 1) {
+        draw_pixel(event->xbutton.x, event->xbutton.y);
+#ifdef CREASES
+        if (state.mode == M_ERAS) {
+          render_edit_wear_area_win(win, state.cur_note, event->xbutton.x,
+            event->xbutton.y, 1, 1);
+        }
+#endif
+      }
+  }
+  state.lp_btn = state.button;
+  state.lp_time = event->xbutton.time;
+}
+
+/*
+ * called from main event loop whenever a ButtonRelease event occurs
+ */
+void
+handle_ButtonRelease(XEvent *event)
+{
+  XGCValues gcv;
+  char *s;
+  int i;
+
+  if (event->xbutton.button != state.button) return;
+  switch (state.mode) {
+    case M_NOOP: case M_MOVE:  /* add new note or edit existing one? */
+      if (!state.moved && state.button == 1) {  /* left-click */
+        if (event->xbutton.x >= 18 && event->xbutton.x <= 45 &&
+          event->xbutton.y >=  2 && event->xbutton.y <= 12 &&
+          selected_note(event->xbutton.x, event->xbutton.y) < 0)
+        { /* add new note */
+          if ((state.cur_note = add_note()) >= 0) {
+            animate_note(4);
+            set_kbfocus(1);
+            set_mode(M_EDIT);
+          } else  /* beep if maximum number of notes reached */
+            XBell(display, 0);
+        } else if (state.cur_note >= 0 && state.cur_note ==
+          selected_note(event->xbutton.x, event->xbutton.y))
+        { /* open existing note in edit mode */
+#ifdef CREASES
+          wear_note(state.cur_note);
+#endif
+          animate_note(4);
+          state.clicks_count = 0;
+          set_mode(M_EDIT);
+        }
+      } else {  /* moved */
+        if (state.cur_note >= 0) {
+          if (string_empty(ndata[state.cur_note].text, 0)) {  /* new note */
+            animate_note(4);
+            set_kbfocus(1);
+            set_mode(M_EDIT);
+          } else {  /* existing note dragged via either left or right MB */
+#ifdef CREASES
+            wear_note(state.cur_note);
+#endif
+            notes_io(1);
+            if (state.button == 1) {  /* keep level */
+              render_pinboard(-1);
+              redraw_window();
+            } else {  /* raise */
+              state.cur_note = raise_note(state.cur_note);
+#ifdef CREASES
+              pin_note(state.cur_note);
+              redraw_window();
+#endif
+            }
+          }
+        }
+      }
+      if (state.mode != M_EDIT) set_mode(M_NOOP);
+      break;
+    case M_EDIT: case M_DRAW: case M_ERAS:
+      i = char_at(event->xbutton.x, event->xbutton.y, 0);
+      if (event->xbutton.x + event->xbutton.y >= 115 &&
+        (!state.selecting || state.sel_from == i))
+      {  /* clicked triangle? */
+        if (state.button > 1) {  /* open panel */
+          animate_panel(1);
+          if (!opts.click_to_focus) set_kbfocus(0);
+          set_mode(M_BBAR);
+        } else {  /* end edit mode */
+          if (state.mode == M_DRAW || state.mode == M_ERAS) {
+            XFreeGC(display, state.sketchGC);  /* free temporary GC */
+            if (M_ERAS) {  /* restore normal note display first */
+              print_text(state.cur_note);
+              draw_sketch(state.cur_note);
+              set_cursor(ndata[state.cur_note].cursor, 1);
+            }
+            set_mode(M_EDIT);
+          }
+          quit_edit_mode(0, 1);
+          if (!opts.click_to_focus) set_kbfocus(0);
+        }
+      } else {
+        if (state.mode == M_EDIT) {
+          if (!opts.click_to_focus || state.clicks_count > 1) {
+            if (state.button == 2) {
+              ndata[state.cur_note].cursor = char_at(event->xbutton.x,
+                event->xbutton.y, 1);
+              cb_paste(state.cur_note, state.insert);
+              init_edit_mode(state.cur_note);
+              set_cursor(ndata[state.cur_note].cursor, 1);
+            } else if (state.selecting && state.sel_from != i) {
+              sel_text(i);
+              if (state.sel_from > state.sel_to) {
+                i = state.sel_from;
+                state.sel_from = state.sel_to;
+                state.sel_to = i;
+              }
+              if (state.button == 1) {  /* copy cooked */
+                s = cook(state.cur_note, state.sel_from, state.sel_to -
+                  state.sel_from);
+                cb_copy(s, -1);
+                free(s);
+              } else {  /* copy raw */
+                cb_copy(&ndata[state.cur_note].text[state.sel_from],
+                  state.sel_to - state.sel_from);
+              }
+            } else
+              set_cursor(char_at(event->xbutton.x, event->xbutton.y, 1), 1);
+          }
+        } else if (state.button > 1) {  /* revert from sketch to edit mode */
+          XFreeGC(display, state.sketchGC);
+          init_edit_mode(state.cur_note);
+          set_cursor(ndata[state.cur_note].cursor, 1);
+          set_mode(M_EDIT);
+        }
+      }
+      state.selecting = 0;
+      break;
+    case M_BBAR:  /* actions in panel mode */
+      if (state.button > 1) {  /* close panel on right-click */
+        animate_panel(0);
+        set_mode(M_EDIT);
+      } else if (state.bbar_pressed == bbar_button(event->xbutton.x,
+        event->xbutton.y) && state.bbar_pressed >= 0)  /* clicked on panel? */
+      {
+        switch (state.bbar_pressed) {
+          case 0: case 4:  /* change note color */
+            i = ndata[state.cur_note].col;
+            if (state.bbar_pressed) {  /* previous color */
+              if (--ndata[state.cur_note].col < 0)
+                ndata[state.cur_note].col = C_NUM-1;
+            } else  if (++ndata[state.cur_note].col == C_NUM)
+                ndata[state.cur_note].col = 0;
+            init_edit_mode(state.cur_note);
+            set_cursor(ndata[state.cur_note].cursor, 0);
+            XCopyArea(display, bbar, app, normalGC, 0, 0, 58, 30, 3, 31);
+            redraw_window();
+            break;
+          case 1: case 5:  /* enter draw/erase mode */
+            gcv.foreground = state.bbar_pressed == 1 ?
+              palette[ndata[state.cur_note].col].fg :
+              palette[ndata[state.cur_note].col].bg;
+            state.sketchGC = XCreateGC(display, app, GCForeground, &gcv);
+            animate_panel(0);
+            render_note(state.cur_note);
+            if (state.bbar_pressed == 1) {  /* erase: hide text */
+              print_text(state.cur_note);
+              set_cursor(ndata[state.cur_note].cursor, 0);
+            }
+#ifdef CREASES
+            render_edit_wear(state.cur_note);
+#endif
+            draw_sketch(state.cur_note);
+            redraw_window();
+            set_mode(state.bbar_pressed == 1 ? M_DRAW : M_ERAS);
+            break;
+          case 2:  /* clear note's text */
+            memset(ndata[state.cur_note].text, 32, 59);
+#ifdef CREASES
+            wear_note(state.cur_note);
+            wear_note(state.cur_note);
+#endif
+            animate_panel(0);
+            init_edit_mode(state.cur_note);
+            set_cursor(0, 1);
+            set_mode(M_EDIT);
+            break;
+          case 6:  /* clear sketch */
+            memset(ndata[state.cur_note].sketch, 0, 511);  /* not last byte */
+#ifdef CREASES
+            wear_note(state.cur_note);
+            wear_note(state.cur_note);
+#endif
+            animate_panel(0);
+            init_edit_mode(state.cur_note);
+            set_cursor(ndata[state.cur_note].cursor, 1);
+            set_mode(M_EDIT);
+            break;
+          case 3:  /* remove note */
+            quit_edit_mode(1, 1);
+            if (!opts.click_to_focus) set_kbfocus(0);
+            break;
+          case 7:  /* close button bar */
+            animate_panel(0);
+            set_mode(M_EDIT);
+        }
+      }
+  }
+  state.button = 0;
+}
+
+/*
+ * called from main event loop whenever a MotionNotify event occurs
+ */
+void
+handle_MotionNotify(XEvent *event)
+{
+  int i, j;
+
+  switch (state.mode) {
+    case M_MOVE:  /* note's being dragged, update position */
+      if (state.cur_note >= 0) {  /* update note that's being moved's pos. */
+        /* the drag cursor isn't set immediately... */
+        if (!state.moved) {
+          set_mode(M_MOVE);
+          state.moved = 1;
+        }
+        i = ndata[state.cur_note].x;
+        j = ndata[state.cur_note].y;
+        ndata[state.cur_note].x = event->xbutton.x - state.dx;
+        ndata[state.cur_note].x = ndata[state.cur_note].x < 6 ? 6 :
+          ndata[state.cur_note].x;
+        ndata[state.cur_note].x = ndata[state.cur_note].x > 42 ? 42 :
+          ndata[state.cur_note].x;
+        ndata[state.cur_note].y = event->xbutton.y - state.dy;
+        ndata[state.cur_note].y = ndata[state.cur_note].y < 2 ? 2 :
+          ndata[state.cur_note].y;
+        ndata[state.cur_note].y = ndata[state.cur_note].y > 46 ? 46 :
+          ndata[state.cur_note].y;
+        XCopyArea(display, app, app, normalGC, 64, 48, 16, 16, i, j);
+        XCopyArea(display, app, app, normalGC, ndata[state.cur_note].x,
+          ndata[state.cur_note].y, 16, 16, 64, 48);
+        pin_note(state.cur_note);
+        redraw_window();
+        time(&state.idle);
+      } else if (!(event->xbutton.x >= 18 && event->xbutton.x <= 45 &&
+          event->xbutton.y >=  2 && event->xbutton.y <= 12 &&
+          selected_note(event->xbutton.x, event->xbutton.y) < 0))
+      { /* create note by dragging it "off" the "TO DO" label */
+        state.cur_note = add_note();
+        ndata[state.cur_note].x = event->xbutton.x-8;
+        ndata[state.cur_note].y = event->xbutton.y < 8 ? 0 :
+          event->xbutton.y-8;
+        state.dx = 8;
+        state.dy = event->xbutton.y - ndata[state.cur_note].y;
+        state.moved = 0;
+        XCopyArea(display, app, app, normalGC,
+          ndata[state.cur_note].x, ndata[state.cur_note].y, 16, 16, 64, 48);
+        color_notes(state.cur_note);
+        set_mode(M_MOVE);  /* update cursor */
+        pin_note(state.cur_note);
+        redraw_window();
+        time(&state.idle);
+      }
+      break;
+    case M_DRAW: case M_ERAS:  /* draw in either sketch mode */
+      if (state.button == 1) {
+        draw_pixel(event->xbutton.x, event->xbutton.y);
+        if (state.mode == M_ERAS) {  /* enlarge the cursor a bit when erasing */
+          draw_pixel(event->xbutton.x-1, event->xbutton.y);
+          draw_pixel(event->xbutton.x+1, event->xbutton.y);
+          draw_pixel(event->xbutton.x, event->xbutton.y-1);
+          draw_pixel(event->xbutton.x, event->xbutton.y+1);
+#ifdef CREASES
+          render_edit_wear_area_win(win, state.cur_note, event->xbutton.x-1,
+            event->xbutton.y, 3, 1);
+          render_edit_wear_area_win(win, state.cur_note, event->xbutton.x,
+            event->xbutton.y-1, 1, 3);
+#endif
+        }
+        time(&state.idle);
+      }
+      break;
+    case M_EDIT:
+      if (state.selecting) {
+        sel_text(char_at(event->xbutton.x, event->xbutton.y, 0));
+        time(&state.idle);
+      }
+      
+  }
+}
+
+/*
+ * called from main event loop whenever a KeyPress event occurs
+ */
+void
+handle_KeyPress(XEvent *event)
+{
+  KeySym ksym;
+  unsigned char ch[4];
+  char *s;
+  int i, j = 0;
+
+  if (state.mode != M_EDIT || event->xkey.state & 0xdfe8 ||
+    (InputContext && XFilterEvent(event, win)))
+  {
+    XSendEvent(display, RootWindow(display, DefaultScreen(display)), True,
+      KeyPressMask, event);
+    if (state.mode == M_EDIT) set_kbfocus(1);  /* make sure we keep focus */
+    return;
+  }
+  time(&state.idle);
+
+  clear_selection();
+  if (event->xkey.state & ControlMask) { /* [Ctrl]-anything -> special fn.s */
+    /* InputContext intentionally ignored here... */
+    XLookupString(&event->xkey, (char*) ch, sizeof(ch), &ksym, &state.compose);
+    switch (ksym) {
+      case XK_c: case XK_C:  /* copy cooked */
+        state.sel_from = 0;
+        sel_text(59);
+        s = cook(state.cur_note, 0, 59);
+        cb_copy(s, -1);
+        free(s);
+        return;
+      case XK_r: case XK_R:  /* copy raw */
+        state.sel_from = 0;
+        sel_text(59);
+        cb_copy(ndata[state.cur_note].text, -1);
+        return;
+      case XK_y: case XK_Y: case XK_z: case XK_Z:  /* zap line */
+        for (i = 10*(ndata[state.cur_note].cursor/10); i < 59; i++)
+          ndata[state.cur_note].text[i] =
+            i < 49 ? ndata[state.cur_note].text[i+10] : ' ';
+        init_edit_mode(state.cur_note);
+        set_cursor(10*(ndata[state.cur_note].cursor/10), 1);
+        return;
+      case XK_n: case XK_N:  /* EMACS-style down */
+        ksym = XK_Down;
+        break;
+      case XK_p: case XK_P:  /* EMACS-style up */
+        ksym = XK_Up;
+        break;
+      case XK_f: case XK_F:  /* EMACS-style right */
+        ksym = XK_Right;
+        break;
+      case XK_b: case XK_B:  /* EMACS-style left */
+        ksym = XK_Left;
+        break;
+      default:
+        return;
+    }
+  } else {
+    if (InputContext) {
+      j = XmbLookupString(InputContext, &event->xkey, (char*) ch, sizeof(ch),
+        &ksym, 0);
+    } else {
+      j = XLookupString(&event->xkey, (char*) ch, sizeof(ch), &ksym,
+        &state.compose);
+    }
+    if (!j && (ksym & 0xff00) == 0x0600) {  /* cyrillic keysyms */
+      ch[0] = (unsigned char) (ksym & 0x00ff);
+      ch[1] = '\0';
+      if (ch[0] >= 0xbf || ch[0] == 0xa3 || ch[0] == 0xb3)
+        j = 1;  /* filter extended KOI8 characters */
+    }
+  }
+  switch (ksym) {
+    case XK_Return: case XK_KP_Enter:
+      if (ndata[state.cur_note].cursor >= 50) break;
+      if (state.insert) {
+        shift_string(state.cur_note, 10*(ndata[state.cur_note].cursor/10+1), 58,
+          ndata[state.cur_note].cursor%10, ' ');
+        shift_string(state.cur_note, ndata[state.cur_note].cursor, 58,
+          10-ndata[state.cur_note].cursor%10, ' ');
+      }
+      set_cursor(10*(ndata[state.cur_note].cursor/10+1), 1);
+      break;
+    case XK_BackSpace: case 0xfd1a:
+      if (!ndata[state.cur_note].cursor) break;
+      if (ndata[state.cur_note].cursor == 58 &&
+        ndata[state.cur_note].text[58] != ' ')
+      { /* special behavior when on very last character */
+        ndata[state.cur_note].text[58] = ' ';
+        set_cursor(ndata[state.cur_note].cursor, 1);
+        break;
+      }
+      print_letter(state.cur_note, ndata[state.cur_note].cursor--, 1);
+    case XK_Delete: case XK_KP_Delete:
+      j = 1;  /* delete entire line if empty... */
+      if (((ksym != XK_BackSpace && ksym != 0xfd1a) ||
+        ndata[state.cur_note].cursor%10 == 9) &&
+        string_empty(&ndata[state.cur_note].text[10*
+          (ndata[state.cur_note].cursor/10)], 10))
+      {
+        ndata[state.cur_note].cursor = 10*(ndata[state.cur_note].cursor/10);
+        j = 10;
+      }
+      if (ksym == XK_BackSpace || ksym == 0xfd1a) {
+        if (ndata[state.cur_note].cursor%10 == 9 &&
+          ndata[state.cur_note].text[ndata[state.cur_note].cursor] == ' ')
+        {
+          for (; ndata[state.cur_note].text[ndata[state.cur_note].cursor-1] ==
+            ' ' && ndata[state.cur_note].cursor >
+            10*(ndata[state.cur_note].cursor/10);)
+          {
+            ndata[state.cur_note].text[ndata[state.cur_note].cursor--] = ' ';
+          }
+        }
+      }
+        
+      for (i = ndata[state.cur_note].cursor; i < (j == 1 &&
+        ndata[state.cur_note].cursor/10 < 5 ?
+        10*(ndata[state.cur_note].cursor/10+1)-j : 59-j); i++)
+      {
+        ndata[state.cur_note].text[i] = ndata[state.cur_note].text[i+j];
+        ndata[state.cur_note].text[i+j] = ' ';
+        print_letter(state.cur_note, i, 1);
+      }
+      if (ndata[state.cur_note].cursor%10 == 9 ||
+        ndata[state.cur_note].cursor == 58)
+      { /* exceptions to the loop */
+        ndata[state.cur_note].text[ndata[state.cur_note].cursor] = ' ';
+      }
+      if (j > 1)  /* only when lines have been shifted */
+        init_edit_mode(state.cur_note);
+      else
+        print_letter(state.cur_note, i, 1);
+      set_cursor(ndata[state.cur_note].cursor, 1);
+      break;
+    case XK_Up: case XK_KP_Up:
+      if ((event->xkey.state & 0x01) == 0x01) {  /* next note w/similar color */
+        for (i = state.cur_note == notes_count-1 ? 0 : state.cur_note+1;
+           i != state.cur_note;)
+        {
+           if (c_group[ndata[i].col] == c_group[ndata[state.cur_note].col])
+             break;
+           if (++i >= notes_count) i = 0;
+        }
+        if (i == state.cur_note) break;
+        if (note_empty(state.cur_note)) remove_note(state.cur_note);
+        notes_io(1);
+        state.cur_note = i;
+        animate_note(2);
+      } else  /* move ndata[state.cur_note].cursor */
+        if (ndata[state.cur_note].cursor > 9)
+          set_cursor(ndata[state.cur_note].cursor-10, 1);
+      break;
+    case XK_Down: case XK_KP_Down:
+      if ((event->xkey.state & 0x01) == 0x01) {  /* prev. note w/simil. color */
+        for (i = state.cur_note ? state.cur_note-1 : notes_count-1;
+          i != state.cur_note;)
+        {
+           if (c_group[ndata[i].col] == c_group[ndata[state.cur_note].col])
+             break;
+           if (--i < 0) i = notes_count-1;
+        }
+        if (i == state.cur_note) break;
+        if (note_empty(state.cur_note)) remove_note(state.cur_note);
+        notes_io(1);
+        state.cur_note = i;
+        animate_note(3);
+      } else  /* move ndata[state.cur_note].cursor */
+        if (ndata[state.cur_note].cursor < 49)
+          set_cursor(ndata[state.cur_note].cursor+10, 1);
+      break;
+    case XK_Left: case XK_KP_Left:
+      if ((event->xkey.state & 0x01) == 0x01) {  /* previous note */
+        if (notes_count == 1) break;
+        if (note_empty(state.cur_note)) remove_note(state.cur_note);
+        notes_io(1);
+        if (--state.cur_note < 0) state.cur_note = notes_count-1;
+        animate_note(1);
+      } else  /* move cursor */
+        if (ndata[state.cur_note].cursor)
+          set_cursor(ndata[state.cur_note].cursor-1, 1);
+      break;
+    case XK_Right: case XK_KP_Right:
+      if ((event->xkey.state & 0x01) == 0x01) {  /* next note */
+        if (notes_count == 1) break;
+        if (note_empty(state.cur_note)) {
+          remove_note(state.cur_note);
+          state.cur_note = notes_count-1;
+        }
+        notes_io(1);
+        if (++state.cur_note == notes_count) state.cur_note = 0;
+        animate_note(0);
+      } else  /* move cursor */
+        if (ndata[state.cur_note].cursor < 58)
+          set_cursor(ndata[state.cur_note].cursor+1, 1);
+      break;
+    case XK_Tab: case XK_ISO_Left_Tab: /* change color */
+      i = ndata[state.cur_note].col;
+      if ((ksym == XK_Tab && (event->xkey.state & 0x01) == 0x01) ||
+        ksym == XK_ISO_Left_Tab)
+      {  /* previous color */
+        if (--ndata[state.cur_note].col < 0)
+          ndata[state.cur_note].col = C_NUM-1;
+      } else  if (++ndata[state.cur_note].col == C_NUM)
+        ndata[state.cur_note].col = 0;
+      init_edit_mode(state.cur_note);
+      set_cursor(ndata[state.cur_note].cursor, 0);
+      redraw_window();
+      break;
+    case XK_Home: case XK_KP_Home:
+      set_cursor(10*(ndata[state.cur_note].cursor/10), 1);
+      break;
+    case XK_End: case XK_KP_End:
+      i = j = ndata[state.cur_note].cursor < 50 ?
+        10*(ndata[state.cur_note].cursor/10)+9 : 58;
+      if (ndata[state.cur_note].text[j] == ' ')
+        for (; i > 10*(j/10) && ndata[state.cur_note].text[i-1] == ' '; i--);
+      set_cursor(i, 1);
+      break;
+    case XK_Prior: case XK_KP_Prior:  /* page up */
+      set_cursor(0, 1);
+      break;
+    case XK_Next: case XK_KP_Next:  /* page down */
+      set_cursor(58, 1);
+      break;
+    case XK_Insert: case XK_KP_Insert:
+      state.insert = !state.insert;
+      set_cursor(ndata[state.cur_note].cursor, 1);
+      break;
+    case XK_Escape:
+      quit_edit_mode(0, 1);
+      if (!opts.click_to_focus) set_kbfocus(0);
+      break;
+    default:
+      if (j) {
+        if (state.insert)
+          shift_string(state.cur_note, ndata[state.cur_note].cursor,
+            ndata[state.cur_note].cursor < 50 ?
+            10*(ndata[state.cur_note].cursor/10)+9 : 58, 1, ch[0]);
+        else {
+          ndata[state.cur_note].text[ndata[state.cur_note].cursor] = ch[0];
+          print_letter(state.cur_note, ndata[state.cur_note].cursor, 1);
+        }
+        set_cursor(ndata[state.cur_note].cursor < 58 ?
+          ndata[state.cur_note].cursor+1 : ndata[state.cur_note].cursor, 1);
+      }
+  }
+}
+                                   /********/
+/*********************************** MAIN ************************************/
+                                 /********/
+int
+main(int argc, char **argv)
+{
+#ifdef FUNSTUFF
+  time_t tt = 0, t;
+  struct tm ltt, lt;
+#endif
+  XEvent event;
+  XGCValues gcv;
+  int i, j;
+
+  srand(time(0));
+  umask(077);  /* users' notes needn't be world-readable */
+
+  parse_argv(argc, argv);  /* evaluate command line parameters */
+  init();                  /* initialize X window */
+  XSetCommand(display, mainwin, argv, argc);
+  XMapWindow(display, mainwin);
+  init_xlocale();          /* initialize input context */
+
+  /* initialize internal images, palette, cursors */
+  bbar = get_xpm(bbar_xpm);
+  img = XGetImage(display, app, 0, 0, 83, 64, ~0, ZPixmap);
+  for (i = 0; i < C_NUM; i++) {
+    palette[i].bg = XGetPixel(img, 80, i);
+    palette[i].fg = XGetPixel(img, 81, i);
+    palette[i].cr = XGetPixel(img, 82, i);
+  }
+  palette[C_NUM].fg = XGetPixel(img, 80, 63);  /* C_INNER */
+  palette[C_NUM].bg = XGetPixel(img, 81, 63);  /* C_OUTER */
+  palette[C_NUM].cr = XGetPixel(img, 82, 63);  /* C_EXTRA */
+  gcv.foreground = C_OUTER;  /* dummy value */
+  gcv.graphics_exposures = 0;
+  fillGC = XCreateGC(display, app, GCForeground | GCGraphicsExposures, &gcv);
+  cursors[0] = XCreateFontCursor(display, XC_fleur);  /* when moving a note */
+  cursors[1] = XCreateFontCursor(display, XC_dotbox);  /* drag-create */
+  cursors[2] = XCreateFontCursor(display, XC_pencil);  /* for sketch mode */
+
+  load_font();
+  notes_io(1);  /* saves opts.font_to_remember and PID */
+  render_pinboard(-1);
+  redraw_window();
+  set_mode(M_NOOP);
+
+  for(;;) {  /*** MAIN EVENT LOOP ***/
+    while (XPending(display)) {
+      XNextEvent(display, &event);
+      switch (event.type) {
+        case Expose:
+          redraw_window();
+          break;
+        case ButtonPress:
+          handle_ButtonPress(&event);
+          time(&state.idle);
+          break;
+        case ButtonRelease:
+          if (event.xbutton.button == state.button)
+            handle_ButtonRelease(&event);
+          time(&state.idle);
+          break;
+        case MotionNotify:
+          handle_MotionNotify(&event);
+          break;
+        case EnterNotify:
+          if (state.mode == M_EDIT && !opts.click_to_focus) set_kbfocus(1);
+          state.clicks_count = 0;
+          break;
+        case LeaveNotify:
+          if (state.mode == M_EDIT && !opts.click_to_focus) set_kbfocus(0);
+          break;
+        case KeyPress:
+          handle_KeyPress(&event);
+          break;
+        case SelectionClear:
+          clear_selection();
+          break;
+        case SelectionRequest:
+          handle_SelectionRequest(&(event.xselectionrequest));
+      }
+    }
+    if (opts.timeout && (state.mode == M_EDIT || state.mode == M_BBAR ||
+      state.mode == M_DRAW || state.mode == M_ERAS) &&
+      time(0)-state.idle > opts.timeout)
+    {
+      quit_edit_mode(0, 1);  /* no set_kbfocus(0) here */
+    }
+#ifdef FUNSTUFF
+    if (state.mode == M_NOOP) {
+      time(&t);
+      if (t-tt > 10*60 && localtime_r(&tt, &ltt) && localtime_r(&t, &lt)) {
+        if (ltt.tm_mday != lt.tm_mday || ltt.tm_mon != lt.tm_mon ||
+          ltt.tm_year != lt.tm_year)
+        {
+          i = notes_count;
+          j = state_bits;
+          check_occasion(lt.tm_mday, 1+lt.tm_mon, 1900+lt.tm_year);
+          if (i != notes_count || j != state_bits) {  /* anything done? */
+            notes_io(1);
+            render_pinboard(-1);
+            redraw_window();
+          }
+        }
+        tt = t;
+      }
+    }
+#endif
+    usleep(state.mode == M_NOOP ? 100000L : 10000L);
+  }
+}
+
+/*
+                            CONCEPTUAL NOTES
+
+                                                      bbar =
+                                  show panel        +---------+
+ mainwin[, iconwin]        ,------------------------|::panel::| 30
+      (each) =             |       app =            |:::::::::|
+    +----------+           |  +----------+--+       +---------+
+    |          |           `->|          |::|           58
+    | visible  |    display   |   draw   |::| 48
+ 64 |   area   | <=========== |  buffer  |::|
+    |          |     64x64    |          +-++
+    |          |          ,-->|         <->|| 16
+    +----------+          |   +----------+-++
+         64               |        64    16\3
+                          |                 \ used when
+                          |                 moving a note     server-side
+  ........................|...............................................
+                          |
+              restore     |                                   client-side
+            ,----------->-+
+            |  board      |    draw
+     img =  |   16        +-<--------[bitfield]
+    +----------+-++  pin  |   sketch
+    |::::::::::| |------>-+
+    |:copy:of::| || notes |
+ 64 |:pristine:| ||       |
+    |:pinboard:+++|  copy |
+    |:::::::10{||---<--->-'
+    +----------++-+ character/write it back
+         64    6\13
+                 \                                            :: = const
+                  \ used to overlay a
+                character with the cursor
+*/
+
diff --git a/wmpinboard.h b/wmpinboard.h
new file mode 100644
index 0000000..5411c9c
--- /dev/null
+++ b/wmpinboard.h
@@ -0,0 +1,75 @@
+/*
+ *  Copyright (C) 1998,9 by Marco G"otze.
+ *
+ *  This code is part of the wmpinboard source package, which is
+ *  distributed under the terms of the GNU GPL2.
+ */
+
+#ifndef WMPINBOARD_H_INCLUDED
+#define WMPINBOARD_H_INCLUDED
+
+#include <X11/Xlib.h>
+
+#include "features.h"
+
+#define VERSION "wmpinboard 0.9.2 (1 Sep 99)"
+
+#define MAX_NOTES 20  /* maximal number of notes */
+
+#define STRING_BUF_SIZE 128  /* size of buffers for font descr. & suchlike */
+
+#define C_NUM 20                   /* number of colors */
+#define C_INNER palette[C_NUM].fg  /* mask color (interior of notes etc.) */
+#define C_OUTER palette[C_NUM].bg  /* mask color (exterior of notes etc.) */
+#define C_EXTRA palette[C_NUM].cr  /* additional mask color */
+
+/* program modes with respect to interactive behavior... */
+#define M_NOOP 0  /* "normal" mode (pinboard view) */
+#define M_EDIT 1  /* edit mode */
+#define M_MOVE 2  /* note being dragged (implies M_NOOP) */
+#define M_BBAR 3  /* button bar being displayed (implies M_EDIT) */
+#define M_DRAW 4  /* sketch mode, drawing (implies M_EDIT) */
+#define M_ERAS 5  /* sketch mode, erasing (implies M_EDIT) */
+
+/* command line actions */
+#define A_DUMP     0  /* "cooked" dump */
+#define A_DUMP_RAW 1  /* raw dump */
+#define A_IRON     2  /* "iron" notes */
+#define A_DEL      3  /* delete a now */
+#define A_ADD      4  /* add "cooked" */
+#define A_ADD_RAW  5  /* add raw */
+#define A_EXPORT   6  /* export sketch */
+
+#define BBAR_ANI_INT  2000L  /* usecs interval for panel animation */
+#define NOTE_ANI_INT 15000L  /* usecs interval for note animation */
+
+typedef struct {
+  int col;
+  int x, y;
+  char text[10*6];
+  int cursor;
+  char sketch[(64/8)*64];   /* bitfield; last byte used for other purposes... */
+  char creases[(16/8)*16];  /* dto. */
+} data_t;
+
+typedef struct {
+  unsigned long fg, bg, cr;  /* foreground, background, crease color */
+} palette_t;
+
+extern Display *display;
+extern Window win;
+extern XImage *img;
+extern Pixmap app;
+extern GC normalGC, fontGC, fillGC;
+#ifdef CREASES
+extern GC creaseGC;
+#endif
+extern XFontStruct *font;
+
+extern const char c_group[C_NUM];
+extern int notes_count;
+extern data_t ndata[MAX_NOTES];
+extern palette_t palette[C_NUM+1];
+
+#endif  /* WMPINBOARD_H_INCLUDED */
+
diff --git a/wmpinboard.lsm b/wmpinboard.lsm
new file mode 100644
index 0000000..f2554c1
--- /dev/null
+++ b/wmpinboard.lsm
@@ -0,0 +1,11 @@
+Begin3
+Title:          wmpinboard
+Version:        0.9.2
+Entered-date:   01SEP99
+Description:    Window Maker dock applet resembling a miniature pinboard
+Keywords:       WindowMaker WM X11 pinboard notes memo
+Author:         gomar at mindless.com (Marco Goetze)
+Primary-site:   http://www.tu-ilmenau.de/~gomar/stuff/wmpinboard/
+Copying-policy: GPL
+End
+
diff --git a/wmpinboard.lsm.in b/wmpinboard.lsm.in
new file mode 100644
index 0000000..78a59ac
--- /dev/null
+++ b/wmpinboard.lsm.in
@@ -0,0 +1,11 @@
+Begin3
+Title:          wmpinboard
+Version:        __VERSION__
+Entered-date:   __DATE__
+Description:    Window Maker dock applet resembling a miniature pinboard
+Keywords:       WindowMaker WM X11 pinboard notes memo
+Author:         gomar at mindless.com (Marco Goetze)
+Primary-site:   http://www.tu-ilmenau.de/~gomar/stuff/wmpinboard/
+Copying-policy: GPL
+End
+
diff --git a/wmpinboard.pod b/wmpinboard.pod
new file mode 100644
index 0000000..ade3769
--- /dev/null
+++ b/wmpinboard.pod
@@ -0,0 +1,867 @@
+=head1 NAME
+
+B<wmpinboard> - a B<Window Maker> dock app resembling a miniature pinboard
+
+=head1 SYNOPSIS
+
+  wmpinboard [options]
+
+=head2 What wmpinboard is
+
+B<wmpinboard> is a B<Window Maker> dock applet resembling a miniature 
+pinboard.  It's intended to somewhat relieve heavily littered desktops 
+by allowing you to place reminders on a graphical on-screen pinboard 
+rather than producing a mess of real notes all around your keyboard 
+(thus being environmentally A Good Thing, too B<;-)> ).  It supports 
+arbitrary 6x10 X fonts and has XLocale support, enabling you to enter 
+locale-dependent special characters if set up appropriately.  Besides 
+text, you can add small monochrome sketches to your notes or simply 
+encircle or underline words as a means of emphasis.  Above all, 
+B<wmpinboard> is animated in redundant ways to make it look even more 
+attractive.
+
+=head2 What wmpinboard ISN'T
+
+Clearly, B<wmpinboard> doesn't allow you to keep an unlimited number of 
+notes holding arbitrary amounts of information, and that's not what 
+it's meant to do.  Just as real notes offer limited space, so do those 
+simulated by B<wmpinboard>.  Besides, as a dock applet, it aims at 
+being small and neat and yet useful in a way, and that's what it is, 
+too, or considered to be by some people, anyway.  If you need room for 
+more comprehensive reminders, use another program, either additionally 
+or exclusively.  There's a variety of such out there, but their niche 
+is different from that which B<wmpinboard> claims.
+
+=head1 OPTIONS
+
+B<wmpinboard>'s command-line options can roughly be classified as 
+belonging to one of three groups of options: those affecting 
+interactive run-time behavior, those allowing for manipulation of notes 
+from the command line, and general ones.  Generally, B<wmpinboard> 
+supports GNU-style long options as well as short ones for parameters 
+used more commonly.
+
+=head2 Options for interactive mode
+
+=over 2
+
+=item C<B<-d DISPLAY>> or B<C<--display=DISPLAY>>
+
+Uses the specified X display rather than the default one.
+
+=item C<B<-c>> or B<C<--click-to-focus>>
+
+This turns on some emulation of a click-based keyboard focus mode. See
+L<"FREQUENTLY ASKED QUESTIONS">.
+
+=item C<B<-f FONT>> or B<C<--font=FONT>>
+
+Makes B<wmpinboard> use the specified font; C<B<FONT>> can be one of 
+the shortcuts listed when running the program with "C<B<--help>>" as a 
+parameter, or a complete X descriptor of a fixed size 6x10 font.  For 
+more details, see L<"FREQUENTLY ASKED QUESTIONS">.
+
+=item C<B<-t TIME>> or B<C<--timeout=TIME>>
+
+Sets the edit mode timeout (i.e., the number of seconds of idleness 
+after which edit mode is terminated automatically) to C<B<TIME>> 
+seconds.  The compile-time default is 60s, but this may have been 
+changed for your particular build; run with C<B<-v>> if in doubt to 
+check that.  Specifying a value of 0 (zero) will disable the timeout.
+
+=item C<B<-n>> or B<C<--normal-state>>
+
+Forces B<wmpinboard> to run in so-called NormalState, which is 
+preferred by B<AfterStep>'s B<Wharf>.
+
+=item C<B<-w>> or B<C<--withdrawn-state>>
+
+Forces the program to run in so-called WithdrawnState, as desired by 
+the B<Window Maker> dock.  This option and the previous one are mutually 
+exclusive.  Note also that B<wmpinboard> tries to auto-detect whether 
+B<Window Maker> is running and sets itself up accordingly.  Using C<B<-n>>
+or C<B<-w>> should only be necessary in case those heuristics fail on 
+your system for some reason or other.
+
+=item C<B<--light>>
+
+Use this switch to suppress animations.
+
+=back
+
+=head2 Options for command-line manipulation of notes
+
+Even though B<wmpinboard> is by design an interactive application, 
+there may be occasions when it comes in handy to be able to 
+access/manipulate notes from the command line.  That's why the program 
+offers a set of command-line options allowing for basic operations of 
+that kind.  Still, it should be kept in mind that B<wmpinboard> is 
+primarily meant to be used interactively.
+
+All of the options below will, if an interactive instance of 
+B<wmpinboard> is running in the background, cause that to save its data 
+(and quit I<edit mode>, if necessary), and if any changes are made by 
+the respective option, the interactive instance will then be signalled 
+to re-read the data file.  Even though the implemented methods of
+inter-process communication should generally be sufficiently safe 
+with respect to avoiding data file corruption, it's in theory possible 
+to undermine the concept and cause damage that way--yet this won't 
+happen unless you deliberately take pains to achieve the goal.  
+Generally, everything should work fine as long as you don't try running 
+multiple non-interactive instances of B<wmpinboard> simultaneously.
+
+Only one of the below actions can be specified per call to B<wmpinboard>.
+
+=over 2
+
+=item C<B<--dump>>
+
+This dumps the contents of all notes, replacing line breaks by spaces 
+(unless preceded by a dash) and shortening sequences of blanks.  The 
+list of dumped strings will be sorted by color groups.  If you use 
+special characters in your notes, make sure your terminal's running 
+with the same character set as B<wmpinboard>, or what you'll see might 
+have a garbage-like quality.
+
+Each line of output represents one note and is prefixed by the internal
+number I<currently> identifying the respective note.
+
+=item C<B<--dump-raw>>
+
+Unlike the "cooked" dump described above, this just dumps the raw 
+contents of all notes without applying any kind of formatting.  May 
+come in handy if your notes happen to contain E-mail addresses or other 
+things for which lines 10 characters wide are too narrow.
+
+=item C<B<--del=NUMBER>>
+
+This option will remove the note identified by C<B<NUMBER>> from the 
+pinboard.  C<B<NUMBER>> is a number determined by the output of either 
+dump option, which should be consulted right before using this one, 
+since note numbers may change when notes are moved around on the board 
+or others are removed.
+
+=item C<B<--add=STRING>>
+
+When run with this option, B<wmpinboard> will add a new note (provided 
+the maximal number of notes has not yet been reached) at a random 
+position on the board, with contents C<B<STRING>>, word-wrapping the 
+text at the end of the note's lines where necessary.  If due to this 
+wrapping, the entire string cannot be stored on the note, the remainder
+will be discarded.
+
+In order to create a note with a certain color, the string can be
+prefixed by a color code specifying the group of colors which a random
+color is to be selected from:
+
+  %G - green
+  %Y - yellow/white
+  %R - reddish
+  %B - blue
+
+(Note: The "%" character can be escaped by a second one if you want to
+add an un-prefixed string starting with a percent character.)
+
+=item C<B<--add-raw=STRING>>
+
+Via this option, a new note can be added from the command line 
+(provided that this won't exceed the maximum number of notes).  
+C<B<STRING>> specifies the I<raw> contents of the note, as printed by 
+C<B<--dump-raw>>.  The same set of color group codes as for the 
+previous option applies.
+
+=back
+
+=head2 General options
+
+=over 2
+
+=item C<B<-h>> or B<C<--help>>
+
+This prints a help screen listing command line options together with 
+brief descriptions.
+
+=item C<B<-v>> or B<C<--version>>
+
+This prints some more detailed version information, in particular, 
+which compile-time settings the program was built with.
+
+=back
+
+=head1 DESCRIPTION
+
+B<wmpinboard> operates in basically two different modes, namely, the 
+I<pinboard view> and I<edit mode>.  Furthermore, a I<panel> of buttons 
+granting access to extended options can be popped up in I<edit mode>.  
+
+=head2 Pinboard view
+
+This is B<wmpinboard>'s normal mode of operation.  A potentially
+chaotic arrangement of tiny squares on a beige-colored oblong
+is meant to resemble notes pinned to a pinboard.  Possible
+actions include:
+
+=over 2
+
+=item *
+
+I<Add> a note, by left-clicking on the board's "TO DO" label.  This 
+creates a new, blank, randomly-colored note at a random position on the 
+board and puts B<wmpinboard> in I<edit mode> (see below).  If you 
+prefer to place a new note at a certain position before being 
+prompted to enter its contents, you may alternatively drag new notes 
+"off" the "TO DO" label, position them, and B<wmpinboard> will switch 
+to I<edit mode> as soon as you release the mouse button.
+
+=item *
+
+I<Edit>/I<view> a note, by left-clicking on a note.  This switches
+to I<edit mode> (described below).
+
+=item *
+
+I<Move> a note, by dragging it using the right mouse button.
+This also raises the note in question on top of all others.
+Depending on its horizontal position, the note will be tilted
+automatically.  As a side-effect, a single brief right-click
+can be used to raise a note on top of overlapping ones without
+moving it.
+
+By dragging a note with the left mouse button, you can move it without 
+changing its level with respect to other notes, i.e., without raising 
+it on top of all others.
+
+=back
+
+=head2 Edit mode
+
+This mode serves two purposes: on the one hand, it presents you with a 
+"full-size" view of a note's contents, on the other, you can use the 
+occasion to edit it.  Due to its limited size, a note can hold up to 10 
+characters on 6 lines (minus one on the last, i.e., 59 characters 
+altogether), plus a monochrome sketch of some kind.  Possibly actions:
+
+=over 2
+
+=item *
+
+I<Enter> text.  B<wmpinboard> supports user-selectable fonts and dead 
+keys, so you should be able to enter any characters that are usually 
+accessible via your keyboard and have them displayed correctly.  
+Furthermore, the cursor can be moved around using the arrow keys (or 
+EMACS-style via [Ctrl]-[N]/[P]/[F]/[B], if you are so inclined).  
+Alternatively, it can be placed explicitly by left-clicking where you 
+want it to be.  Other special keys that are supported include:
+
+=over 2
+
+=item S<  [PgUp]/[PgDn]>
+
+Places the cursor on character 1/59, respectively.
+
+=item S<  [Home]/[End]>
+
+Places the cursor at the textual start or end of the current line.
+
+=item S<  [Del]>
+
+Deletes the character currently under the text cursor and shifts the 
+remaining text on the current line to the left; if the current line is 
+blank, removes it and shifts all lines below up by one line.
+
+=item S<  [Backspace]>
+
+See [Del], but affects the character on the left of the cursor.
+
+=item S<  [Ins]>
+
+Toggles inserting/overwriting of existing text; the current mode is 
+indicated by a cursor change (block cursor means insert mode).
+
+=item S<  [Enter]>
+
+In insert mode, wraps the current line at the cursor's position; in 
+overwrite mode (underscore cursor), merely moves the cursor to the 
+start of the next line.
+
+=item S<  [Ctrl]-[Y], -[Z]>
+
+Removes an entire (intermediate) line, shifting those below up by one, 
+and places the cursor at the start of the current line.
+
+=item S<  [Esc]>
+
+Quits I<edit mode> and returns to the I<pinboard view>.
+
+=item S<  [Shift]-[Left]/[Right]>
+
+Cycles through all notes currently on the pinboard.
+
+=item S<  [Shift]-[Up]/[Down]>
+
+Cycles through all notes that are I<roughly> the same color as the 
+current one.  For this purpose, colors have internally been divided 
+into four groups: green, white/yellow, reddish, blue.
+
+=item S<  ([Shift]-)[Tab]>
+
+Cycles (backwards) through availabe note colors.
+
+=back
+
+=item *
+
+I<Cut'n'paste> text.  Despite the limitations implied, B<wmpinboard> 
+has support for cutting & pasting to and from the X clipboard:
+
+=over 2
+
+=item S<  -> 
+
+In order to copy text to the clipboard, select the desired segment via 
+either the left or the right mouse button: the left one will copy the 
+text post-formatted as done by the command line switch C<B<--dump>> 
+(see L<"OPTIONS">); the right button will copy the raw selection.  
+Similarly, a left double click will select the word (i.e., all 
+adjoining non-blank characters) at the respective position, a right one 
+will do the same but neglect line breaks as delimiters.  Additionally, 
+you can copy a note's entire raw contents by pressing S<[Ctrl]-[R]>; 
+S<[Ctrl]-[C]> will do the same with applied post-formatting.
+
+=item S<  ->
+
+To paste the clipboard's contents, press the middle button wherever
+the insertion is supposed to happen.
+
+=back
+
+Obvious limitations you should be aware of include:
+
+=over 2
+
+=item S<  ->
+
+As is usually the case (about B<wmpinboard>, anyway), size matters.  
+As you know, a note can hold only up to 59 characters, so trying to 
+paste longer strings will result in those being truncated.
+
+=item S<  ->
+
+If the text to be pasted is formatted in some way or other, this won't 
+be the case any more after it has been pasted: B<wmpinboard> replaces 
+new line characters by blanks and doesn't even try to word-wrap text.
+
+=item S<  ->
+
+The information stored in the cut buffer needn't necessarily be 
+compatible with B<wmpinboard> in that it may be encoded with another 
+character set.
+
+=back
+
+=item *
+
+I<Leave> I<edit mode>.  This is achieved by left-clicking on the
+triangle in the lower right-hand side corner.  If the note is
+completely empty, it will be removed from the board.  In any
+case, this returns to the I<pinboard view>.
+
+=item *
+
+I<Pop up> a I<panel> with some further options to choose from.  This
+is done by right-clicking on the aforementioned triangle.  To
+learn what the I<panel> is there for, see the corresponding section
+below.
+
+=item *
+
+I<Draw> a sketch.  This mode can be activated via the I<panel>, and
+deactivated by either right-clicking somewhere on the note
+or opening the I<panel> again.  While in drawing mode, the mouse
+pointer is pencil-shaped, and drawing can be done by keeping
+the left mouse button pressed and dragging the mouse, just as
+you'd expect.  Sketch and text may overlap each other, but keyboard 
+input is ignored while in drawing mode.
+
+=item *
+
+I<Erase> a sketch.  Just like DRAWing mode, this mode is entered
+via the I<panel>, and can be quit just like the former.  In erase
+mode, the text is hidden, so you needn't guess whether a pixel
+belongs to an entered character or a drawn sketch.  Note that
+the erase cursor's point is slightly larger than the one used
+when drawing.
+
+=back
+
+Note: B<wmpinboard> remembers where you left the text cursor after 
+you last edited a note and restores this position when you edit it the 
+next time.
+
+=head2 Edit mode panel
+
+This I<panel> is intended to provide easy access to some options 
+affecting I<edit mode> or the current note in general.  The I<panel> 
+looks like this (letters denoting the buttons for reference below):
+
+    +---+---+---+---+
+    | a | c | e | g |
+    +---+---+---+---+
+    | b | d | f | h |
+    +---+---+---+---+
+
+The buttons bear tiny icons which are meant to suggest what they do, 
+which isn't all that easy on a 12x12 pixels area. B<:^)>
+
+Here's a description of what each button does:
+
+=over 4
+
+=item (a)
+
+This button allows one to cycle through all colors available for notes 
+(20, unless optimized for low color modes).  Clicking on it won't close 
+the I<panel>, so this can be done repeatedly.  BTW: note colors can 
+also be changed via a keyboard shortcut in I<edit mode> (see that 
+section).
+
+=item (b)
+
+Same as (a), only backwards.
+
+=item (c)
+
+This button closes the I<panel> and returns to I<edit mode>, with
+the sketch-I<draw>ing feature enabled (see above).
+
+=item (d)
+
+Closes the I<panel> and returns to I<edit mode>, with the
+sketch-I<eras>ing feature enabled (see above).  Don't panic if entered 
+text vanishes all of a sudden when you do this: this is because 
+B<wmpinboard> intentionally hides it to eradicate the need for you to 
+make wild guesses as to what's entered text and which pixels belong to 
+a sketch.
+
+=item (e)
+
+This button removes all entered text on the current note and places the 
+text cursor on the very first character.  Besides, it closes the 
+I<panel>, thus returning to I<edit mode>.
+
+=item (f)
+
+Pressing this button completely removes a drawn sketch on the current 
+note and returns to I<edit mode>.
+
+=item (g)
+
+This option removes the *entire* note from the board and returns to 
+I<pinboard view>.
+
+=item (h)
+
+This button merely closes the I<panel> (and thus puts you back in 
+I<edit mode>).  The same can be achieved by simply right-clicking in 
+this view.
+
+=back
+
+=head1 FREQUENTLY ASKED QUESTIONS
+
+=over 2
+
+=item *
+
+I<Q:> Is a "pinboard" this small really of any use?
+
+I<A:> Of course the limited size imposes certain restrictions, but if 
+you think about it, you'll agree that a real life pinboard reminds you 
+of things by the mere existence of notes being pinned to it.  In order 
+to read what they say, you have to step close and, possibly, detach the 
+note.
+
+Quite similarly, B<wmpinboard> reminds you of things by facing you with 
+colored representations of notes on your screen.  To find out what it 
+was you intended them to remind you of, all you have to do is click on 
+a note, which will then be displayed full size.
+
+By choosing from a variety of possible colors, you can assign 
+particular colors to certain kinds of reminders, which may further 
+enhance B<wmpinboard>'s usability.  Moreover, you can place certain notes 
+on certain areas of the board to emphasize their category, urgency, 
+etc.  It's up to you what to make of it.
+
+Finally, by adding drawing capabilities, I've definitely overcome the 
+contents quantity barrier imposed by the maximum number of 59 
+characters, for as everyone knows, a picture is worth more than a 
+thousand words. *grin*
+
+=item *
+
+I<Q:> I don't live in an English-speaking country, so what about extended
+characters (umlauts, accents, cyrillic alphabet)?
+
+I<A:> B<wmpinboard> allows you to use an arbitrary 8bit X font, provided 
+that its characters are of a fixed size of 6x10 (or, deprecated but 
+possible, anything <= 7x10) pixels.  The default font is "6x10" (more 
+precisely, it's called
+"-misc-fixed-medium-r-normal--10-100-75-75-c-*-ISO8859-1"), an
+ISO8859-1 (Latin1) font which should be part of every XFree 
+installation.
+
+In order to make B<wmpinboard> use another font, run it as
+
+  $ wmpinboard -f FONT
+
+where B<FONT> is either a shortcut for a compiled-in font name (see 
+L<"OPTIONS"> for a list of those) or a valid, complete X font 
+descriptor.  B<wmpinboard> will remember the specified font, so it's 
+not actually necessary to use this parameter more than once.  To change 
+the font at a later time, run B<wmpinboard> with a different font 
+specification.  Note that this I<only works if there are no more notes 
+on the board>.  It's intentionally been made impossible to change the 
+font while there are notes saved in B<wmpinboard>'s data file, since 
+this might result in garbage being displayed.  Of course even a font 
+specified via a shortcut has to exist on your system in order to be 
+usable.
+
+If a font specified either explicitly (via the command line) or 
+implicitly (via B<wmpinboard>'s data file) cannot be loaded or has 
+invalid dimensions, B<wmpinboard> will try to revert.  Note that this 
+won't affect the font name saved along with the data, though.
+
+=item *
+
+I<Q:> How can I disable those vexing, superfluous animations?
+
+I<A:>
+
+  $ wmpinboard --light
+
+=item *
+
+I<Q:> Why aren't those animations smooth all of the time?  Sometimes it 
+looks like they're being skipped entirely.
+
+I<A:> This presumably is a multitasking issue: depending on the current 
+system load and B<wmpinboard>'s/the X server's recent CPU usage 
+history, it may take a moment until the scheduling has been adapted to 
+the suddenly increased CPU load implied by displaying the animation, 
+and short as it is, it may already be finished until this has happened, 
+i.e., it's the X server lagging behind in updating the program's 
+display if B<wmpinboard>'s been idle for some time prior to that.  It 
+may sound paradoxical, but the effect is the more likely to show the 
+lower the system's load is.  I don't see a way to avoid this
+effect--either this, or you turn off animations altogether.
+
+=item *
+
+I<Q:> When I leave B<wmpinboard> idle in edit mode for some time, edit 
+mode is terminated automatically.  Is that intended?
+
+I<A:> Yes.  After 60 idle seconds (that's the default; see 
+L<"OPTIONS">) in edit mode (no mouse click and no keyboard input), edit 
+mode is terminated automatically.  If the note being edited happens to 
+be blank, it will be discarded (or removed if an existing note is being 
+edited).
+
+This timeout can, however, be adjusted according to your preferences or
+turned off using the C<B<-t>> parameter.  See L<"OPTIONS"> for this.
+
+=item *
+
+I<Q:> When does B<wmpinboard> save its data?
+
+I<A:> Notes data is saved on each of these occasions:
+
+=over 2
+
+=item S<  ->
+
+whenever edit mode is terminated
+
+=item S<  ->
+
+when you switch notes in edit mode (via [Shift]-[S<arrow key>])
+
+=item S<  ->
+
+when a note has been moved on the board
+
+=item S<  ->
+
+when an interactive instance is running and you run B<wmpinboard> from 
+the command line, making it dump, add, or delete notes
+
+=back
+
+Notes are saved to a file called F<.wmpinboarddata> in your home 
+directory (see L<"FILES">).
+
+=item *
+
+I<Q:> I've tried my best and littered the entire pinboard with quite a 
+lot of notes.  Now I can't seem to be able to add another one.
+
+I<A:> There's a compile-time limit of 20 notes.  I think more notes on 
+this tiny a board really don't make any sense.
+
+=item *
+
+I<Q:> I've explicitly configured my window manager for click-based 
+rather than mouse-following focus, but B<wmpinboard>'s focus follows 
+the mouse regardless.  Can I change this?
+
+I<A:> By default, B<wmpinboard> actively claims the keyboard input 
+focus (if it's in note edit mode) whenever the pointer is moved over the 
+application's area.  Since B<wmpinboard> is a dock applet, i.e., a 
+withdrawn rather than a "real" X window, it can't be assigned a focus 
+in the same way as to the latter ones.  However, running B<wmpinboard> 
+with the parameter C<B<-c>> will make it emulate some sort of
+click-based focusing.  This means, it actively claims the keyboard 
+focus only on these occasions:
+
+=over 2
+
+=item S<  ->
+
+when a new note is created (I<not> when you click on an existing
+note--you'll have to explicitly click on the editing area to make it 
+claim focus; this way, you can just view a note without any change to 
+keyboard focus)
+
+=item S<  ->
+
+when you click somewhere on the text area in edit mode
+
+=back
+
+Once keyboard-focused, B<wmpinboard> will keep it until another window 
+is explicitly focused (usually by clicking on its title bar or 
+border).  To focus B<wmpinboard> again when it's lost focus, you'll 
+have to click on its text area in edit mode.  This click will only 
+focus the application and not have the usual cursor-positioning effect.
+
+This feature is to be considered experimental since I'm not sure of how 
+much use it really is.  A mouse-following focus is the recommended mode 
+of operation.
+
+=item *
+
+I<Q:> I've noticed that after a while, some sort of darker stains 
+appear on my notes.  Is that a bug in some drawing routine?
+
+I<A:> No, this is not a bug.  These "stains" are meant to resemble 
+creases, caused by frequent handling of a particular note (wear & tear, 
+you see?).  In fact, they're added with a certain probability whenever 
+you view a note by clicking on it (note that leafing through notes via 
+[Shift]-[S<arrow keys>] is I<not> affected), when you clear its textual 
+or drawn contents via the I<edit mode panel> (very outwearing, that 
+S<B<;-)> )>, and when a note is moved.  This feature can be disabled at 
+compile time by commenting out the respective line in the file 
+F<features.h> (see comments therein).
+
+To prevent the question, no, worn-out notes cannot be ironed to get rid 
+of the creases.  Sorry. B<:-p>
+
+=item *
+
+I<Q:> Is B<wmpinboard> compatible with B<AfterStep>'s B<Wharf>?
+
+I<A:> B<wmpinboard> tries to autodetect whether B<Window Maker> is 
+running and sets itself up accordingly.  If this doesn't work for you 
+for some reason, you can explicitly make it run in either Withdrawn- or 
+NormalState using the C<B<-w>> or C<B<-n>> flag, respectively.  See 
+L<"OPTIONS">.
+
+Swallowing evidently works with B<AfterStep> 1.6.10; I don't know about 
+earlier versions.  A B<Wharf> config line you might try is this:
+
+  *Wharf wmpinboard nil MaxSwallow "wmpinboard" wmpinboard &
+
+Besides, B<wmpinboard> has been reported to work with B<Blackbox>.
+
+=item *
+
+I<Q:> I have X running at a color depth of 8 bits, i.e., in palette 
+mode, and B<wmpinboard> obviously requires too many colors and thus 
+looks real messy (or doesn't run at all, complaining about "not enough 
+free color cells").  What can I do about this?
+
+I<A:> Try recompiling B<wmpinboard> with optimizations for low color 
+depths (i.e., it'll use a restricted set of colors).  To achieve this, 
+get yourself the source package and edit the file F<features.h>, guided 
+by the comments therein.  But be warned, the result won't look pretty.  
+Running B<wmpinboard> at a color depth of at least 15 bits is the 
+recommended state of affairs.
+
+Don't be surprised if you run a low-color-optimized B<wmpinboard> 
+binary and later switch to the regular version: the two of them are not 
+identical regarding their sets of colors, so these will differ.
+
+=item *
+
+I<Q:> Can I run multiple instances of B<wmpinboard> as the same user, 
+simultaneously?
+
+I<A:> No, this is certainly not a good idea.  The run-time behavior may 
+be unpredictable, and your data file can get corrupted.  Therefore, any 
+B<wmpinboard> process that's to be run interactively first checks 
+whether another interactive instance is running, and if so, refuses to 
+run.
+
+=item *
+
+I<Q:> I've just upgraded from a pre-0.7 version of B<wmpinboard> and 
+noticed that its data file format has changed completely since.  Is 
+there a way to upgrade and yet keep my existing notes?
+
+I<A:> There's a B<Perl> script doing the conversion included with the 
+distribution (the source one, anyway).  If your package didn't include 
+that, feel free to mail to the author (see L<"AUTHOR"> at the end of 
+this documentation).
+
+=item *
+
+I<Q:> Is B<wmpinboard> Y2K compliant?
+
+I<A:> No, unfortunately not.  Due to the high degree to which 
+B<wmpinboard> depends on calendar-related calculations, all kinds of 
+dreadful things will happen if it is run past the infamous year 2000 
+threshold; to prevent your system from being severely damaged, remove 
+B<wmpinboard> and all traces of its existence from your system prior to 
+that!  You have been warned.
+
+=item *
+
+I<Q:> I find a mere 59 characters per note to be a real limitation.  
+How about making B<wmpinboard> pop up an external window with more room 
+for that?  Or how about assigning that job to an external editor?
+
+I<A:> There's a variety of comprehensive programs for keeping notes out 
+there, offering this functionality but being rather heavy-weight since 
+they are linked against one GUI library or another (B<CoolNotes> or 
+B<KNotes> come to mind).  On the other hand, I couldn't find a B<WM>-
+conforming reminder I could omnipresently stick to my desktop anywhere, 
+so I wrote B<wmpinboard>.  I wanted it to be small, neat, easy to use, 
+and yet useful in a way.
+
+I hope that's about what the program is currently like.  And I'd prefer 
+to keep it like that rather than inflate it by linking against a GUI 
+library--eventually, the note editing code would outweigh the rest of 
+the application by a factor, and people would probably still create 
+notes mostly shorter than 60 characters.  If you restrict your memos to 
+keywords and abbreviations, that's quite a lot.
+
+I want B<wmpinboard> to remain an applet in that it doesn't open up 
+external windows and use (if just temporarily) additional desktop 
+space.  I explicitly wrote it to have something omnipresent at a fixed 
+position on my desktop.  I find it preferable to have the notes pop up 
+in place rather than somewhere else on the screen.
+
+Personally, I use other programs for larger notes too; B<wmpinboard>
+has been designed for things smaller in size and greater in urgency,
+it's in no way meant to be a comprehensive knowledge base application
+of any kind.
+
+Summing up, I think a dock applet should be small both regarding its
+on-screen representation and the ressources it uses.  That's why I 
+don't intend to add any pop-up dialogs or similar things to 
+B<wmpinboard>.
+
+=item *
+
+I<Q:> I've tried the program, yet I can't help but find it utterly 
+useless.  What shall I do?
+
+I<A:> The solution is simple.  Just don't use it.
+
+=item *
+
+I<Q:> Will your answer to this question be "no"?
+
+I<A:> Nope.
+
+=back
+
+=head1 HINTS
+
+=over 2
+
+=item *
+
+A good way of making the best of the organizational features offered by 
+B<wmpinboard> is to use certain colors and locations on the pinboard to 
+indicate urgency and contents of a note.  For example, you might use 
+each of the color groups for a certain kind of reminder, because that 
+enables you to leaf through all notes with related contents via [Shift]-
+[S<arrow keys>] in edit mode.  Besides, you might assign each corner of 
+the board a specific urgency, altogether allowing you to derive a 
+note's type from its color and its urgency from its location on the 
+board.  Thanks again to the ability to leaf through all notes belonging 
+to the same group of colors, notes with similar contents will still be 
+clustered in a way.
+
+=back
+
+=head1 UNDOCUMENTED FEATURES
+
+This piece of documentation doesn't cover any undocumented features.
+
+=head1 FILES
+
+=over 2
+
+=item F<~/.wmpinboarddata>
+
+the user's B<wmpinboard> data file
+
+=item F<~/.wmpinboarddata.new>
+
+temporary file created momentarily when saving data
+
+=back
+
+=head1 ENVIRONMENT VARIABLES
+
+=over 2
+
+=item $HOME
+
+the user's home directory
+
+=back
+
+=head1 SEE ALSO
+
+wmaker(1)
+
+=head1 BUGS
+
+There are currently no known bugs; if you stumble on one, however, feel 
+free to mail to the author.
+
+The same goes if you encounter any problems running/using the program.  
+Be sure to include any information you consider relevant, i.e., at a 
+minimum, the version of B<wmpinboard> you're using as well as your OS 
+and X revisions.
+
+Also, further suggestions are always welcome.  Please check the F<TODO> 
+file that's part of the distribution to see if what you're about to 
+suggest isn't already on my "to do" list, or has been suggested earlier 
+and was rejected for one reason or another.
+
+=head1 AUTHOR
+
+B<wmpinboard> is copyrighted (c) 1998,9 by Marco GE<ouml>tze, 
+<gomar at mindless.com>.  It is distributed under the terms of the GNU 
+General Public License, revision 2 or any later revision thereof.  Use 
+at your own risk.
+
+New releases of B<wmpinboard> can be found at
+<http://www.tuZ<>-ilmenau.de/~gomar/stuff/wmpinboard/>, or 
+that was true at least by the time this document was last updated.
+
+=cut
+
diff --git a/xmisc.c b/xmisc.c
new file mode 100644
index 0000000..22f685f
--- /dev/null
+++ b/xmisc.c
@@ -0,0 +1,368 @@
+/*
+ *  Copyright (C) 1998,9 by Marco G"otze.
+ *
+ *  This code is part of the wmpinboard source package, which is
+ *  distributed under the terms of the GNU GPL2.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <stdarg.h>
+
+#include <X11/Xatom.h>
+#include <X11/Xlib.h>
+#include <X11/Xlocale.h>
+#include <X11/Xmd.h>
+#include <X11/xpm.h>
+#include <X11/extensions/shape.h>
+
+#include "features.h"
+#include "misc.h"
+#include "wmpinboard.h"
+#include "xmisc.h"
+
+typedef CARD32 Atom32;
+
+XIC InputContext = 0;
+static char *rs_inputMethod = 0;
+static char *rs_preeditType = 0;
+static unsigned char *cb_buffer = 0;
+
+/*
+ * replaces <o_col> by <n_col> in an XImage ([no]_col are XGetPixel return
+ * values!)
+ */
+void
+replace_color(XImage *img, int x, int y, int w, int h, unsigned long o_col,
+  unsigned long n_col)
+{
+  int i, j;
+
+  for (i = x; i < x+w; i++)
+    for (j = y; j < y+h; j++)
+      if (XGetPixel(img, i, j) == o_col)
+        XPutPixel(img, i, j, n_col);
+}
+
+/*
+ * puts <dest> on app, copying pixels only where <src>'s color is !=
+ * <mask_col>'s
+ */
+void
+merge_masked(XImage *src, int sx, int sy, int dx, int dy, int w, int h,
+  unsigned long mask_col)
+{
+  GC tempGC;
+  XGCValues gcv;
+  unsigned long p;
+  int i, j;
+
+  gcv.foreground = mask_col;  /* dummy value */
+  tempGC = XCreateGC(display, app, GCForeground, &gcv);
+  for (i = 0; i < w; i++)
+    for (j = 0; j < h; j++) {
+      p = XGetPixel(src, sx+i, sy+j);
+      if (p != mask_col) {
+        XSetForeground(display, tempGC, p);
+        XDrawPoint(display, app, tempGC, dx+i, dy+j);
+      }
+    }
+  XFreeGC(display, tempGC);
+}
+
+/*
+ * converts a pixmap structure to a Pixmap
+ */
+Pixmap
+get_xpm(char *pixmap_bytes[])
+{
+  Pixmap pic;
+  XpmAttributes attr;
+
+  attr.valuemask = XpmCloseness;
+  if (XpmCreatePixmapFromData(display, RootWindow(display,
+    DefaultScreen(display)), pixmap_bytes, &pic, 0, &attr) != XpmSuccess)
+  {
+    die("Not enough free color cells.");
+  }
+  return pic;
+}
+
+/*
+ * flushes Expose events
+ */
+void
+flush_expose(void)
+{
+  XEvent dummy;
+
+  while (XCheckTypedWindowEvent(display, win, Expose, &dummy));
+}
+
+/*
+ * redraws the application's window
+ */
+void
+redraw_window(void)
+{
+  flush_expose();
+  XCopyArea(display, app, win, normalGC, 0, 0, 64, 64, 0, 0);
+}
+
+/*
+ * sets either the app's shape mask for normal operation or reverts to none
+ * (edit mode)
+ */
+void
+set_mask(int set)
+{
+  static XRectangle none = { 0, 0, 64, 64 };
+  static XRectangle mask = { 6, 2, 52, 60 };
+
+  if (set)
+    XShapeCombineRectangles(display, win, ShapeBounding, 0, 0, &mask, 1,
+      ShapeSet, 0);
+  else
+    XShapeCombineRectangles(display, win, ShapeBounding, 0, 0, &none, 1,
+      ShapeSet, 0);
+}
+
+/*
+ * sets up XIC-orientated keyboard handling
+ * [stolen from the RXVT, minor adaptations]
+ */
+void
+init_xlocale(void)
+{
+  char *p, *s, buf[32], tmp[1024];
+  XIM xim = 0;
+  XIMStyle input_style = 0;
+  XIMStyles *xim_styles = 0;
+  int found;
+
+  InputContext = 0;
+  setlocale(LC_ALL, "");
+
+  if (!rs_inputMethod || !*rs_inputMethod) {
+    if ((p = XSetLocaleModifiers("@im=none")) && *p)
+      xim = XOpenIM(display, 0, 0, 0);
+  } else {
+    strcpy(tmp, rs_inputMethod);
+    for (s = tmp; *s;) {
+      char *end, *next_s;
+      
+      for (; *s && isspace(*s); s++);
+      if (!*s) break;
+      end = s;
+      for (; *end && (*end != ','); end++);
+      next_s = end--;
+      for (; (end >= s) && isspace(*end); end--);
+      *(end + 1) = '\0';
+      
+      if (*s) {
+        strcpy(buf, "@im=");
+        strcat(buf, s);
+        if ((p = XSetLocaleModifiers(buf)) && *p
+          && (xim = XOpenIM(display, 0, 0, 0)))
+        {
+          break;
+        }
+      }
+      if (!*next_s) break;
+      s = (next_s + 1);
+    }
+  }
+
+  if (!xim && (p = XSetLocaleModifiers("")) && *p)
+    xim = XOpenIM(display, 0, 0, 0);
+    
+  if (!xim) {
+#ifdef DEBUG_IC
+    fprintf(stderr, "Failed to open input method.\n");
+#endif
+    return;
+  }
+
+  if (XGetIMValues(xim, XNQueryInputStyle, &xim_styles, 0) || !xim_styles) {
+    XCloseIM(xim);
+#ifdef DEBUG_IC
+    fprintf(stderr, "Input method doesn't support any style.\n");
+#endif
+    return;
+  }
+
+  strcpy(tmp, (rs_preeditType ? rs_preeditType : "Root"));
+  for (found = 0, s = tmp; *s && !found;) {
+    unsigned short i;
+    char *end, *next_s;
+  
+    while (*s && isspace(*s)) s++;
+    if (!*s) break;
+    end = s;
+    while (*end && (*end != ',')) end++;
+    next_s = end--;
+    while ((end >= s) && isspace(*end)) *end-- = 0;
+  
+    if (!strcmp(s, "OverTheSpot"))
+      input_style = (XIMPreeditPosition | XIMStatusArea);
+    else if (!strcmp(s, "OffTheSpot"))
+      input_style = (XIMPreeditArea | XIMStatusArea);
+    else if (!strcmp(s, "Root"))
+      input_style = (XIMPreeditNothing | XIMStatusNothing);
+  
+    for (i = 0; i < xim_styles->count_styles; i++) {
+      if (input_style == xim_styles->supported_styles[i]) {
+        found = 1;
+         break;
+      }
+    }
+    s = next_s;
+  }
+  XFree(xim_styles);
+    
+  if (found == 0) {
+    XCloseIM(xim);
+#ifdef DEBUG_IC
+    fprintf(stderr, "Input method doesn't support my preedit type.\n");
+#endif
+    return;
+  }
+
+  if (input_style != (XIMPreeditNothing | XIMStatusNothing)) {
+    XCloseIM(xim);
+#ifdef DEBUG_IC
+    fprintf(stderr, "This program only supports the `Root' preedit type.\n");
+#endif
+    return;
+  }
+
+  InputContext = XCreateIC(xim, XNInputStyle, input_style,
+    XNClientWindow, win, XNFocusWindow, win, 0);
+
+  if (!InputContext) {
+    XCloseIM(xim);
+  }
+}
+
+/*
+ * creates a dock-app-tailored window
+ */
+Window
+create_win()
+{
+  Window win;
+  XClassHint hint;
+  XSetWindowAttributes attr;
+
+  win = XCreateSimpleWindow(display,
+    RootWindow(display, DefaultScreen(display)), 0, 0, 64, 64, 0, 0, 0);
+  hint.res_name = "wmpinboard";
+  hint.res_class = "WMPINBOARD";
+  XSetClassHint(display, win, &hint);
+  attr.background_pixmap = ParentRelative;
+  XChangeWindowAttributes(display, win, CWBackPixmap, &attr);
+  return win;
+}
+
+/*
+ * copies <text> to the X clipboard
+ */
+void
+cb_copy(const char *text, int len)
+{
+  int l;
+
+  if (!text) return;
+
+  if (cb_buffer) free(cb_buffer);
+  l = len < 0 ? strlen(text) : len;
+  cb_buffer = smalloc(l+1);
+  strncpy(cb_buffer, text, l);
+  cb_buffer[l] = 0;
+
+  XSetSelectionOwner(display, XA_PRIMARY, win, CurrentTime);
+  if (XGetSelectionOwner(display, XA_PRIMARY) != win)
+    fprintf(stderr, "Warning: Failed to set XA_PRIMARY ownership.\n");
+  XChangeProperty(display, DefaultRootWindow(display), XA_CUT_BUFFER0,
+    XA_STRING, 8, PropModeReplace, cb_buffer, l);
+}
+
+/*
+ * responds to a SelectionRequest event
+ * [once again, thanks to the RXVT source]
+ */
+void
+handle_SelectionRequest(XSelectionRequestEvent *rq)
+{
+  XEvent ev;
+  Atom32 target_list[2];
+  static Atom xa_targets = None;
+
+  if (xa_targets == None) xa_targets = XInternAtom(display, "TARGETS", 0);
+
+  ev.xselection.type = SelectionNotify;
+  ev.xselection.property = None;
+  ev.xselection.display = rq->display;
+  ev.xselection.requestor = rq->requestor;
+  ev.xselection.selection = rq->selection;
+  ev.xselection.target = rq->target;
+  ev.xselection.time = rq->time;
+
+  if (rq->target == xa_targets) {
+    target_list[0] = (Atom32) xa_targets;
+    target_list[1] = (Atom32) XA_STRING;
+    XChangeProperty(display, rq->requestor, rq->property, rq->target,
+      8*sizeof(target_list[0]), PropModeReplace,
+      (unsigned char*) target_list,
+      sizeof(target_list)/sizeof(target_list[0]));
+    ev.xselection.property = rq->property;
+  } else if (rq->target == XA_STRING) {
+    XChangeProperty(display, rq->requestor, rq->property, rq->target,
+      8, PropModeReplace, cb_buffer, strlen((char*) cb_buffer));
+      ev.xselection.property = rq->property;
+  }
+  XSendEvent(display, rq->requestor, 0, 0, &ev);
+}
+
+/*
+ * pastes the current contents of the clipboard into <note> at <pos>, inserting
+ * or overwriting depending on <ins>; moves the cursor
+ */
+void
+cb_paste(int note, int ins)
+{
+  unsigned long bytes_after, nitems;
+  unsigned char *data, *p;
+  Atom actual_type;
+  int actual_fmt;
+  char *q;
+  int pos = ndata[note].cursor;
+
+  if ((XGetWindowProperty(display, DefaultRootWindow(display),
+    XA_CUT_BUFFER0, 0, 64, 0, AnyPropertyType, &actual_type, &actual_fmt,
+    &nitems, &bytes_after, &data) != Success))
+  {
+    XFree(data);
+    return;
+  }
+  for (p = data; nitems-- && pos < 59; p++) {
+    if (ins)  /* shift right by one character till end of note's text */
+      for (q = &ndata[note].text[58]; q > &ndata[note].text[pos]; *q-- = q[-1]);
+    ndata[note].text[pos++] = *p == '\n' ? ' ' : *p;
+  }
+  ndata[note].cursor = pos > 58 ? 58 : pos;
+  XFree(data);
+}
+
+/*
+ * frees the copy made of a selected string
+ */
+void
+cb_clear()
+{
+  if (cb_buffer) free(cb_buffer);
+}
+
diff --git a/xmisc.h b/xmisc.h
new file mode 100644
index 0000000..52b5cea
--- /dev/null
+++ b/xmisc.h
@@ -0,0 +1,32 @@
+/*
+ *  Copyright (C) 1998,9 by Marco G"otze.
+ *
+ *  This code is part of the wmpinboard source package, which is
+ *  distributed under the terms of the GNU GPL2.
+ */
+
+#ifndef XMISC_H_INCLUDED
+#define XMISC_H_INCLUDED
+
+#include <X11/Xlib.h>
+
+#include "features.h"
+
+void replace_color(XImage*, int, int, int, int, unsigned long, unsigned long);
+void merge_masked(XImage*, int, int, int, int, int, int, unsigned long);
+
+Pixmap get_xpm(char**);
+void flush_expose(void);
+void redraw_window(void);
+void set_mask(int);
+void init_xlocale(void);
+Window create_win(void);
+void cb_copy(const char*, int);
+void handle_SelectionRequest(XSelectionRequestEvent *rq);
+void cb_paste(int, int);
+void cb_clear();
+
+extern XIC InputContext;
+
+#endif  /* XMISC_H_INCLUDED */
+

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-wmaker/wmpinboard.git



More information about the Pkg-wmaker-commits mailing list