[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, <t) && localtime_r(&t, <)) {
+ 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