[Pkg-wmaker-commits] [wmsystemtray] 01/34: Initial import

Doug Torrance dtorrance-guest at moszumanska.debian.org
Mon Aug 24 04:01:09 UTC 2015


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

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

commit a0e4e3e64a506bd76a2e525400f3053a2a5032b4
Author: Brad Jorsch <anomie at users.sourceforge.net>
Date:   Sat Jan 2 01:58:48 2010 +0000

    Initial import
    
    git-svn-id: https://svn.code.sf.net/p/wmsystemtray/code@1 8177d978-5354-4e5a-a197-9fd626d94383
---
 ChangeLog          |   5 +
 LICENSE            | 339 ++++++++++++++++++++++
 Makefile.am        |  17 ++
 b0rken/Makefile.am |   3 +
 b0rken/malloc.c    |  15 +
 bootstrap          |   6 +
 configure.ac       |  69 +++++
 fdtray.c           | 254 ++++++++++++++++
 fdtray.h           |   3 +
 m4/Makefile.am     |   2 +
 m4/xpm.m4          | 190 ++++++++++++
 wmsystemtray.1.in  | 124 ++++++++
 wmsystemtray.c     | 837 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 wmsystemtray.h     | 129 +++++++++
 wmsystemtray.xpm   |  42 +++
 15 files changed, 2035 insertions(+)

diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..6f028c3
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,5 @@
+wmsystemtray (1.0) unstable; urgency=low
+
+  * Initial release.
+
+ -- Brad Jorsch <anomie at users.sourceforge.net>  Fri, 01 Jan 2010 20:31:39 -0500
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..d511905
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,339 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    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 Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <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.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <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 Lesser General
+Public License instead of this License.
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..e145bf2
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,17 @@
+AUTOMAKE_OPTIONS = foreign
+ACLOCAL_AMFLAGS = -I m4
+
+SUBDIRS = b0rken m4
+
+bin_PROGRAMS = wmsystemtray
+
+wmsystemtray_SOURCES = fdtray.c fdtray.h wmsystemtray.c wmsystemtray.h wmsystemtray.xpm
+wmsystemtray_CFLAGS = @X_CFLAGS@ @libXmu_CFLAGS@ @libXmuu_CFLAGS@
+wmsystemtray_LDADD = b0rken/libb0rken.a @XLIBS@ @libXmu_LIBS@ @libXmuu_LIBS@
+
+man_MANS = wmsystemtray.1
+EXTRA_DIST = bootstrap $(man_MANS)
+
+reallyclean: distclean
+	-rm -rf autom4te.cache
+	-rm Makefile.in */Makefile.in aclocal.m4 config.h.in* config.guess config.sub configure install-sh ltmain.sh missing mkinstalldirs stamp-h.in
diff --git a/b0rken/Makefile.am b/b0rken/Makefile.am
new file mode 100644
index 0000000..4ff8f6e
--- /dev/null
+++ b/b0rken/Makefile.am
@@ -0,0 +1,3 @@
+noinst_LIBRARIES = libb0rken.a
+libb0rken_a_SOURCES =
+libb0rken_a_LIBADD = @LIBOBJS@
diff --git a/b0rken/malloc.c b/b0rken/malloc.c
new file mode 100644
index 0000000..67b7494
--- /dev/null
+++ b/b0rken/malloc.c
@@ -0,0 +1,15 @@
+/* Version of malloc that avoids the malloc(0) bug */
+
+#if HAVE_CONFIG_H
+# include "config.h"
+#endif
+#undef malloc
+
+#include <sys/types.h>
+
+void *malloc();
+
+void *rpl_malloc(size_t size){
+    if(size==0) size=1;
+    return malloc(size);
+}
diff --git a/bootstrap b/bootstrap
new file mode 100755
index 0000000..3073034
--- /dev/null
+++ b/bootstrap
@@ -0,0 +1,6 @@
+#! /bin/sh
+
+aclocal -I m4 \
+&& autoheader \
+&& automake --foreign --add-missing \
+&& autoconf
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..1bfa6cd
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,69 @@
+# Process this file with autoconf to produce a configure script.
+AC_INIT(wmsystemtray.c)
+AM_CONFIG_HEADER(config.h)
+AM_INIT_AUTOMAKE(wmsystemtray, 1.0)
+
+DATE=`date '+%B %e, %Y'`
+AC_SUBST(DATE)
+AC_SUBST(VERSION)
+
+# Checks for programs.
+AC_PROG_AWK
+AC_PROG_CC
+AC_PROG_CC_STDC
+AC_PROG_INSTALL
+AC_PROG_LN_S
+AC_PROG_RANLIB
+
+# Checks for libraries.
+PKG_CHECK_MODULES(libXmuu,xmuu,,[
+ PKG_CHECK_MODULES(libXmu,xmu)
+])
+
+# Checks for header files.
+AC_PATH_XTRA
+AC_FIND_XPM
+if test "$LINK_XPM" = ""; then
+    AC_MSG_ERROR(cannot find libxpm)
+fi
+AC_HEADER_STDC
+if test "$ac_cv_header_stdc" != "yes"; then AC_MSG_WARN(standard C headers not found); fi
+AC_CHECK_HEADERS([stdlib.h string.h unistd.h],, AC_MSG_WARN($ac_header not found))
+
+# Checks for typedefs, structures, and compiler characteristics.
+AC_C_CONST
+AC_C_INLINE
+AC_TYPE_SIZE_T
+AC_CHECK_TYPES([ssize_t])
+AC_CHECK_TYPES([sig_atomic_t],,,[[#include <signal.h>]])
+AH_BOTTOM(
+[#if !HAVE_SSIZE_T
+typedef signed ssize_t;
+#endif
+
+#if !HAVE_SIG_ATOMIC_T
+/* Just a guess */
+typedef int sig_atomic_t;
+#endif])
+
+# Compatability define
+AH_BOTTOM(
+[#ifndef __GNUC__
+#define  __attribute__(x)  /*NOTHING*/
+#endif])
+
+
+# Checks for library functions.
+AC_FUNC_MALLOC
+if test "$ac_cv_func_malloc_0_nonnull" != "yes"; then AC_MSG_WARN(malloc() doesn't seem to work); fi
+AC_CHECK_FUNCS([atexit dup2 mkdir select strchr strcspn strdup strerror strrchr strspn strstr strtol],, AC_MSG_WARN($ac_func doesn't seem to be available))
+
+CFLAGS="$CFLAGS \$(X_CFLAGS) \$(libnotify_CFLAGS)"
+LIBS="$LIBS"
+XLIBS="\$(X_PRE_LIBS) \$(X_LIBS) \$(LINK_XPM) -lX11 -lXext \$(X_EXTRA_LIBS) \$(libnotify_LIBS)"
+AC_SUBST(XLIBS)
+
+AC_CONFIG_FILES([Makefile wmsystemtray.1])
+AC_CONFIG_FILES([b0rken/Makefile])
+AC_CONFIG_FILES([m4/Makefile])
+AC_OUTPUT
diff --git a/fdtray.c b/fdtray.c
new file mode 100644
index 0000000..c56356b
--- /dev/null
+++ b/fdtray.c
@@ -0,0 +1,254 @@
+/* wmsystemtray
+ * Copyright © 2009  Brad Jorsch <anomie at users.sourceforge.net>
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+// http://standards.freedesktop.org/systemtray-spec/systemtray-spec-latest.html
+// http://standards.freedesktop.org/xembed-spec/xembed-spec-latest.html
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "wmsystemtray.h"
+#include "fdtray.h"
+
+static Atom net_system_tray_s;
+static Atom net_system_tray_opcode;
+static Atom net_system_tray_message_data;
+static Atom manager;
+static Atom xembed;
+static Atom xembed_info;
+static Time seltime;
+static int my_id;
+
+static Bool get_map(Window w){
+    Bool map = True;
+    Atom type;
+    int format;
+    unsigned long nitems, bytes_after;
+    unsigned char *data;
+    int ret = XGetWindowProperty(display, w, xembed_info, 0, 2, False, xembed_info, &type, &format, &nitems, &bytes_after, &data);
+    if(type == xembed_info && format == 32 && nitems >= 2){
+        map = (((long *)data)[1] & 1)?True:False;
+    }
+    if(ret == Success) XFree(data);
+    return map;
+}
+
+static void send_xembed_notify(Window w, Window parent){
+    XEvent ev;
+    ev.xclient.type = ClientMessage;
+    ev.xclient.window = w;
+    ev.xclient.message_type = xembed;
+    ev.xclient.format = 32;
+    ev.xclient.data.l[0] = get_X_time();
+    ev.xclient.data.l[1] = 0; // XEMBED_EMBEDDED_NOTIFY
+    ev.xclient.data.l[2] = 0;
+    ev.xclient.data.l[3] = parent;
+    ev.xclient.data.l[4] = 0; // version
+    void *v=catch_BadWindow_errors();
+    XSendEvent(display, w, False, NoEventMask, &ev);
+    if(uncatch_BadWindow_errors(v)){
+        warn(DEBUG_WARN, "Tray icon %lx is invalid", w);
+        icon_remove(w);
+    }
+}
+
+static void add(Window w){
+    warn(DEBUG_INFO, "fdtray: Dock request for window %lx", w);
+    int *data = malloc(sizeof(int));
+    if(!data){
+        warn(DEBUG_ERROR, "memory allocation failed");
+        return;
+    }
+    void *v=catch_BadWindow_errors();
+    XWithdrawWindow(display, w, screen);
+    XSelectInput(display, w, StructureNotifyMask|PropertyChangeMask);
+    Bool map = get_map(w);
+    if(uncatch_BadWindow_errors(v)){
+        warn(DEBUG_WARN, "fdtray: Dock request for invalid window %lx", w);
+        free(data);
+        return;
+    }
+    struct trayicon *icon = icon_add(my_id, w, data);
+    if(!icon){
+        free(data);
+        return;
+    }
+    icon_set_mapping(icon, map);
+}
+
+static void event(XEvent *ev){
+    struct trayicon *icon;
+    void *v;
+
+    switch(ev->type){
+      case SelectionClear:
+        if(ev->xselectionclear.selection == net_system_tray_s && ev->xselectionclear.window != selwindow){
+            warn(DEBUG_ERROR, "fdtray: Another application (window %lx) has forcibly taken the system tray registration", ev->xselectionclear.window);
+            exitapp = True;
+        }
+        break;
+
+      case PropertyNotify:
+        if(ev->xproperty.atom != xembed_info) break;
+        icon = icon_find(ev->xproperty.window);
+        if(!icon || icon->type!=my_id) break;
+        v = catch_BadWindow_errors();
+        Bool map = get_map(icon->w);
+        if(uncatch_BadWindow_errors(v)){
+            warn(DEBUG_WARN, "Tray icon %lx is invalid", icon->w);
+            icon_remove(icon->w);
+        } else {
+            icon_set_mapping(icon, map);
+        }
+        break;
+
+      case ConfigureNotify:
+        icon = icon_find(ev->xconfigure.window);
+        if(!icon || icon->type!=my_id) break;
+        v = catch_BadWindow_errors();
+        {
+            XWindowAttributes a;
+            XGetWindowAttributes(display, icon->w, &a);
+            if(a.width != iconsize || a.height != iconsize)
+                XResizeWindow(display, icon->w, iconsize, iconsize);
+        }
+        if(uncatch_BadWindow_errors(v)){
+            warn(DEBUG_WARN, "Tray icon %lx is invalid", icon->w);
+            icon_remove(icon->w);
+        }
+        break;
+
+      case ReparentNotify:
+        icon = icon_find(ev->xreparent.window);
+        if(!icon || icon->type!=my_id) break;
+        if(is_icon_parent(ev->xreparent.parent)){
+            send_xembed_notify(icon->w, ev->xreparent.parent);
+        } else {
+            warn(DEBUG_WARN, "Tray icon %lx was reparented, removing", icon->w);
+            icon_remove(icon->w);
+        }
+        break;
+
+      case DestroyNotify:
+        icon = icon_find(ev->xdestroywindow.window);
+        if(!icon || icon->type!=my_id) break;
+        warn(DEBUG_WARN, "Tray icon %lx was destroyed, removing", icon->w);
+        icon_remove(icon->w);
+        break;
+
+      case ClientMessage:
+        if(ev->xclient.message_type == net_system_tray_opcode){
+            switch(ev->xclient.data.l[1]){
+              case 0: // SYSTEM_TRAY_REQUEST_DOCK
+                add(ev->xclient.data.l[2]);
+                break;
+
+              case 1: // SYSTEM_TRAY_BEGIN_MESSAGE
+                icon = icon_find(ev->xclient.window);
+                if(!icon || icon->type!=my_id) break;
+                *(int *)icon->data = ev->xclient.data.l[4];
+                icon_begin_message(icon->w, ev->xclient.data.l[4], ev->xclient.data.l[3], ev->xclient.data.l[2]);
+                break;
+
+              case 2: // SYSTEM_TRAY_CANCEL_MESSAGE
+                icon = icon_find(ev->xclient.window);
+                if(!icon || icon->type!=my_id) break;
+                icon_cancel_message(icon->w, ev->xclient.data.l[2]);
+                break;
+            }
+        } else if(ev->xclient.message_type == net_system_tray_message_data){
+            icon = icon_find(ev->xclient.window);
+            if(!icon || icon->type!=my_id) break;
+            icon_message_data(icon->w, *(int *)icon->data, ev->xclient.data.b, 20);
+        }
+        break;
+    }
+}
+
+static void iremove(struct trayicon *icon){
+    warn(DEBUG_INFO, "fdtray: Reparenting %lx to the root window", icon->w);
+    void *v=catch_BadWindow_errors();
+    XSelectInput(display, icon->w, NoEventMask);
+    XUnmapWindow(display, icon->w);
+    XReparentWindow(display, icon->w, root, 0,0);
+    uncatch_BadWindow_errors(v);
+    free(icon->data);
+}
+
+static void closing(){
+    if(XGetSelectionOwner(display, net_system_tray_s) == selwindow){
+        warn(DEBUG_INFO, "fdtray: Releasing system tray selection");
+        // The ICCCM specifies "and the time specified as the timestamp that
+        // was used to acquire the selection", so that what we do here.
+        XSetSelectionOwner(display, net_system_tray_s, None, seltime);
+    }
+}
+
+static struct trayfuncs funcs = {
+    .handle_event = event,
+    .remove_icon = iremove,
+    .closing = closing,
+    .deinit = NULL
+};
+
+struct trayfuncs *fdtray_init(int id, int argc, char **argv){
+    char buf[50];
+    XEvent ev;
+
+    // Get the necessary atoms
+    my_id = id;
+    warn(DEBUG_DEBUG, "fdtray: Loading atoms");
+    snprintf(buf, sizeof(buf), "_NET_SYSTEM_TRAY_S%d", screen);
+    net_system_tray_s = XInternAtom(display, buf, False);
+    net_system_tray_opcode = XInternAtom(display, "_NET_SYSTEM_TRAY_OPCODE", False);
+    net_system_tray_message_data = XInternAtom(display, "_NET_SYSTEM_TRAY_MESSAGE_DATA", False);
+    manager = XInternAtom(display, "MANAGER", False);
+    xembed = XInternAtom(display, "_XEMBED", False);
+    xembed_info = XInternAtom(display, "_XEMBED_INFO", False);
+
+    // Try to grab the system tray selection. ICCCM specifies that we first
+    // check for an existing owner, then grab it with a non-CurrentTime
+    // timestamp, then check again if we now own int.
+    if(XGetSelectionOwner(display, net_system_tray_s)){
+        warn(DEBUG_WARN, "Another application is already running as the freedesktop.org protocol system tray");
+        return NULL;
+    }
+    warn(DEBUG_INFO, "fdtray: Grabbing system tray selection");
+    seltime = get_X_time();
+    XSetSelectionOwner(display, net_system_tray_s, selwindow, seltime);
+    if(XGetSelectionOwner(display, net_system_tray_s) != selwindow){
+        warn(DEBUG_WARN, "Failed to register as the freedesktop.org protocol system tray");
+        return NULL;
+    }
+
+    // Notify anyone who cares that we are now accepting tray icons
+    warn(DEBUG_INFO, "fdtray: Notifying clients that we are now the system tray");
+    ev.type = ClientMessage;
+    ev.xclient.window = root;
+    ev.xclient.message_type = manager;
+    ev.xclient.format = 32;
+    ev.xclient.data.l[0] = seltime;
+    ev.xclient.data.l[1] = net_system_tray_s;
+    ev.xclient.data.l[2] = selwindow;
+    ev.xclient.data.l[3] = 0;
+    ev.xclient.data.l[4] = 0;
+    XSendEvent(display, root, False, StructureNotifyMask, &ev);
+
+    return &funcs;
+}
diff --git a/fdtray.h b/fdtray.h
new file mode 100644
index 0000000..b342543
--- /dev/null
+++ b/fdtray.h
@@ -0,0 +1,3 @@
+#include "wmsystemtray.h"
+
+struct trayfuncs *fdtray_init(int id, int argc, char **argv);
diff --git a/m4/Makefile.am b/m4/Makefile.am
new file mode 100644
index 0000000..f871dd0
--- /dev/null
+++ b/m4/Makefile.am
@@ -0,0 +1,2 @@
+SUBDIRS =
+EXTRA_DIST = libpcre.m4 libwraster.m4 snprintf.m4 vsnprintf.m4 xpm.m4
diff --git a/m4/xpm.m4 b/m4/xpm.m4
new file mode 100644
index 0000000..4e065a6
--- /dev/null
+++ b/m4/xpm.m4
@@ -0,0 +1,190 @@
+dnl AC_FIND_XPM
+dnl ---------------
+dnl
+dnl Find Xpm libraries and headers.
+dnl Put Xpm include directory in xpm_includes,
+dnl put Xpm library directory in xpm_libraries,
+dnl and add appropriate flags to X_CFLAGS and X_LIBS.
+dnl
+dnl
+AC_DEFUN([AC_FIND_XPM],
+[
+AC_REQUIRE([AC_PATH_XTRA])
+xpm_includes=
+xpm_libraries=
+AC_ARG_WITH(xpm,
+[  --without-xpm           do not use the Xpm library])
+dnl Treat --without-xpm like
+dnl --without-xpm-includes --without-xpm-libraries.
+if test "$with_xpm" = "no"
+then
+xpm_includes=no
+xpm_libraries=no
+fi
+AC_ARG_WITH(xpm-includes,
+[  --with-xpm-includes=DIR Xpm include files are in DIR],
+xpm_includes="$withval")
+AC_ARG_WITH(xpm-libraries,
+[  --with-xpm-libraries=DIR
+                          Xpm libraries are in DIR],
+xpm_libraries="$withval")
+AC_MSG_CHECKING(for Xpm)
+#
+#
+# Search the include files.  Note that XPM can come in <X11/xpm.h> (as
+# in X11R6) or in <xpm.h> if installed locally.
+#
+if test "$xpm_includes" = ""; then
+AC_CACHE_VAL(ice_cv_xpm_includes,
+[
+ice_xpm_save_LIBS="$LIBS"
+ice_xpm_save_CFLAGS="$CFLAGS"
+ice_xpm_save_CPPFLAGS="$CPPFLAGS"
+ice_xpm_save_LDFLAGS="$LDFLAGS"
+#
+LIBS="$X_PRE_LIBS -lXpm -lXt -lX11 $X_EXTRA_LIBS $LIBS"
+CFLAGS="$X_CFLAGS $CFLAGS"
+CPPFLAGS="$X_CFLAGS $CPPFLAGS"
+LDFLAGS="$X_LIBS $LDFLAGS"
+#
+AC_TRY_COMPILE([
+#include <X11/xpm.h>
+],[int a;],
+[
+# X11/xpm.h is in the standard search path.
+ice_cv_xpm_includes=
+],
+[
+# X11/xpm.h is not in the standard search path.
+# Locate it and put its directory in `xpm_includes'
+#
+# /usr/include/Motif* are used on HP-UX (Motif).
+# /usr/include/X11* are used on HP-UX (X and Xaw).
+# /usr/dt is used on Solaris (Motif).
+# /usr/openwin is used on Solaris (X and Xaw).
+# Other directories are just guesses.
+for dir in "$x_includes" "${prefix}/include" /usr/include /usr/local/include \
+           /usr/include/Motif2.0 /usr/include/Motif1.2 /usr/include/Motif1.1 \
+           /usr/include/X11R6 /usr/include/X11R5 /usr/include/X11R4 \
+           /usr/dt/include /usr/openwin/include \
+           /usr/dt/*/include /opt/*/include /usr/include/Motif* \
+           /usr/*/include/X11R6 /usr/*/include/X11R5 /usr/*/include/X11R4 \
+	   "${prefix}"/*/include /usr/*/include /usr/local/*/include \
+	   "${prefix}"/include/* /usr/include/* /usr/local/include/*; do
+if test -f "$dir/X11/xpm.h" || test -f "$dir/xpm.h"; then
+ice_cv_xpm_includes="$dir"
+break
+fi
+done
+if test "$ice_cv_xpm_includes" = "/usr/include"; then
+ice_cv_xpm_includes=
+fi
+])
+#
+LIBS="$ice_xpm_save_LIBS"
+CFLAGS="$ice_xpm_save_CFLAGS"
+CPPFLAGS="$ice_xpm_save_CPPFLAGS"
+LDFLAGS="$ice_xpm_save_LDFLAGS"
+])
+xpm_includes="$ice_cv_xpm_includes"
+fi
+#
+#
+# Now for the libraries.
+#
+if test "$xpm_libraries" = ""; then
+AC_CACHE_VAL(ice_cv_xpm_libraries,
+[
+ice_xpm_save_LIBS="$LIBS"
+ice_xpm_save_CFLAGS="$CFLAGS"
+ice_xpm_save_CPPFLAGS="$CPPFLAGS"
+ice_xpm_save_LDFLAGS="$LDFLAGS"
+#
+LIBS="$X_PRE_LIBS -lXpm -lXt -lX11 $X_EXTRA_LIBS $LIBS"
+CFLAGS="$X_CFLAGS $CFLAGS"
+CPPFLAGS="$X_CFLAGS $CPPFLAGS"
+LDFLAGS="$X_LIBS $LDFLAGS"
+#
+#
+# We use XtToolkitInitialize() here since it takes no arguments
+# and thus also works with a C++ compiler.
+AC_TRY_LINK([
+#include <X11/Intrinsic.h>
+#include <X11/xpm.h>
+],[XtToolkitInitialize();],
+[
+# libxpm.a is in the standard search path.
+ice_cv_xpm_libraries=
+],
+[
+# libXpm.a is not in the standard search path.
+# Locate it and put its directory in `xpm_libraries'
+#
+#
+# /usr/lib/Motif* are used on HP-UX (Motif).
+# /usr/lib/X11* are used on HP-UX (X and Xpm).
+# /usr/dt is used on Solaris (Motif).
+# /usr/openwin is used on Solaris (X and Xpm).
+# Other directories are just guesses.
+for dir in "$x_libraries" "${prefix}/lib" /usr/lib /usr/local/lib \
+	   /usr/lib/Motif2.0 /usr/lib/Motif1.2 /usr/lib/Motif1.1 \
+	   /usr/lib/X11R6 /usr/lib/X11R5 /usr/lib/X11R4 /usr/lib/X11 \
+           /usr/dt/lib /usr/openwin/lib \
+	   /usr/dt/*/lib /opt/*/lib /usr/lib/Motif* \
+	   /usr/*/lib/X11R6 /usr/*/lib/X11R5 /usr/*/lib/X11R4 /usr/*/lib/X11 \
+	   "${prefix}"/*/lib /usr/*/lib /usr/local/*/lib \
+	   "${prefix}"/lib/* /usr/lib/* /usr/local/lib/*; do
+if test -d "$dir" && test "`ls $dir/libXpm.* 2> /dev/null`" != ""; then
+ice_cv_xpm_libraries="$dir"
+break
+fi
+done
+])
+#
+LIBS="$ice_xpm_save_LIBS"
+CFLAGS="$ice_xpm_save_CFLAGS"
+CPPFLAGS="$ice_xpm_save_CPPFLAGS"
+LDFLAGS="$ice_xpm_save_LDFLAGS"
+])
+#
+xpm_libraries="$ice_cv_xpm_libraries"
+fi
+#
+# Add Xpm definitions to X flags
+#
+if test "$xpm_includes" != "" && test "$xpm_includes" != "$x_includes" && test "$xpm_includes" != "no"
+then
+X_CFLAGS="-I$xpm_includes $X_CFLAGS"
+fi
+if test "$xpm_libraries" != "" && test "$xpm_libraries" != "$x_libraries" && test "$xpm_libraries" != "no"
+then
+case "$X_LIBS" in
+  *-R\ *) X_LIBS="-L$xpm_libraries -R $xpm_libraries $X_LIBS";;
+  *-R*)   X_LIBS="-L$xpm_libraries -R$xpm_libraries $X_LIBS";;
+  *)      X_LIBS="-L$xpm_libraries $X_LIBS";;
+esac
+fi
+#
+#
+xpm_libraries_result="$xpm_libraries"
+xpm_includes_result="$xpm_includes"
+
+if test "$xpm_libraries_result" != "no" && test "$xpm_includes_result" != "no"
+then AC_DEFINE(HAVE_XPM, 1, "Define if you have libxpm")
+     LINK_XPM="-lXpm"
+else LINK_XPM=""
+fi
+
+AC_SUBST(LINK_XPM)
+
+test "$xpm_libraries_result" = "" && 
+  xpm_libraries_result="in default path"
+test "$xpm_includes_result" = "" && 
+  xpm_includes_result="in default path"
+test "$xpm_libraries_result" = "no" && 
+  xpm_libraries_result="(none)"
+test "$xpm_includes_result" = "no" && 
+  xpm_includes_result="(none)"
+AC_MSG_RESULT(
+  [libraries $xpm_libraries_result, headers $xpm_includes_result])
+])dnl
diff --git a/wmsystemtray.1.in b/wmsystemtray.1.in
new file mode 100644
index 0000000..bd83813
--- /dev/null
+++ b/wmsystemtray.1.in
@@ -0,0 +1,124 @@
+.de Sh
+.br
+.if t .Sp
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.TH wmsystemtray 1x "@DATE@"
+.SH NAME
+wmsystemtray \- A freedesktop.org system tray as a Window Maker dock app
+.P
+.SH SYNOPSIS
+.B wmsystemtray
+.RB [\| options \|]
+.SH DESCRIPTION
+\fBwmsystemtray\fP is a system tray using the freedesktop.org system tray
+protocol designed as a Window Maker dock app. It has the ability to display
+more than one dock window to make room for more tray icons, and the ability to
+scroll through the icons if more are present than will fit.
+.SH OPTIONS
+.TP
+.BI "-display " <display\ name>
+Name of display to use.
+.TP
+.B --help
+Display usage information.
+.TP
+.BR -V ", " --version
+Display the version number and exit.
+.TP
+.BR -v ", " --verbose
+Print more messages to the console. May be repeated.
+.TP
+.BR -q ", " --quiet
+Print fewer messages to the console. May be repeated.
+.TP
+.BR -s ", " --small
+Use 16x16 tray icons, instead of 24x24. In other words, fit 9 icons instead of
+4 in one dock window.
+.TP
+.BI "-w " <n> "\fR,\fP --windows " <n>
+Specifies the number of dock windows to open. Each dock window is assigned a
+unique name (i.e. the first is wmsystemtray.wmsystemtray0, the second
+wmsystemtray.wmsystemtray1, and so on), so they can be reliably positioned in
+the dock or clip.
+.TP
+.B --id-windows
+Print the window's index number on the window, to make it easier to dock them
+in the right order. Note that if there are enough tray icons active, this
+number may be covered up.
+.TP
+.B --fill-rows
+Normally the first dock window is completely filled before any tray icons are
+placed in the second. This option fills the top row of each dock window before
+placing any icons in the second row, which may make more sense if you arrange
+the dock windows horizontally.
+.TP
+.BI "--arrows " <place>
+Normally, every dock window has both left and right scrolling arrows at the
+bottom. Specifying \fI--arrows horizontal\fP places the left arrow only on the
+first window and the right arrow only on the last, which may make more sense if
+the windows are arranged horizontally. \fI--arrows vertical\fP places the
+arrows only on the last window, which may make more sense if the windows are
+arranged vertically.
+.TP
+.BI "-c " <color> "\fR,\fP --fgcolor " <color>
+Specify a color for the page indicator and \fI--id-windows\fP indicators, instead of black. Colors may be specified in any format recognized by
+.B \%XParseColor
+In short, that's any color name in X's rgb.txt or an RGB color specified as
+"rgb:\fIrr\fP/\fIgg\fP/\fIgg\fP".
+.TP
+.BI "--bgcolor " <color>
+In non-Window Maker mode, specify the color for the window background. The
+default is to shape the window to fit the used area and to set
+\fIParentRelative\fP, which attempts to copy whatever is behind the window. 
+.TP
+.B --non-wmaker
+Activate non-Window Maker mode. This provides limited support for using the
+program in window managers that don't do Window Maker-style dockapps; the
+application still sizes itself as a 64x64 window, however, so you might be
+better served by a more traditional system tray.
+.SH DISPLAY
+The main portion of the dockapp has room for four 24x24 or nine 16x16 tray
+icons. At the bottom are left and right arrows for paging when more tray icons
+are available than can be displayed at once, with an indicator between showing
+the current "page" of icons and total number of pages currently available.
+
+In addition to left-clicking either scrolling arrows, the mouse's scroll wheel
+may be used on the bottom section to change pages.
+.SH BUGS
+The balloon message portion of the freedesktop.org protocol is not implemented
+at this time. I've heard that the official Gnome system tray doesn't implement
+this either, and most tray apps seem to directly use dbus desktop notifications
+service.
+
+Most of the Xembed specification is not implemented, as it is not needed here.
+For example, the only point to redirecting input focus is to allow the outer
+window to see input events (and then the outer window has to forward those
+events to the embeds). But since we don't really care, we can just let the
+icons get events directly. Similarly, we don't take focus or activation, and we
+don't do accellerators.
+.SH SPECIFICATIONS
+.UR http://standards.freedesktop.org/systemtray-spec/systemtray-spec-0.3.html
+freedesktop.org System Tray Protocol Specification
+.UE
+
+.UR http://standards.freedesktop.org/xembed-spec/xembed-spec-0.5.html
+freedesktop.org XEmbed Protocol Specification
+.UE
+.SH AUTHORS
+\fBwmsystemtray\fP was written by \fIBrad Jorsch
+<anomie at users.sourceforge.net>\fP.
+.P
+Email regarding wmsystemtray should be sent to
+\fIanomie at users.sourceforge.net\fP.
+.SH INSPIRATION
+When I finally decided to make use of some applications that work via system
+tray icons, I looked around for a tray for my preferred window manager. Some
+didn't integrate well (I didn't want a bar at the top or bottom of the screen),
+some dockapps couldn't handle more than 4 icons at all, some could do 4 icons
+with paging (and much crashing if any program was killed), some could do more
+than 4 icons by creating arbitrary numbers of app icons (but undockable,
+because they were created "as needed"). So I decided to write my own, combining the best features into a stable app.
diff --git a/wmsystemtray.c b/wmsystemtray.c
new file mode 100644
index 0000000..594d58a
--- /dev/null
+++ b/wmsystemtray.c
@@ -0,0 +1,837 @@
+/* wmsystemtray
+ * Copyright © 2009  Brad Jorsch <anomie at users.sourceforge.net>
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <stdarg.h>
+
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/xpm.h>
+#include <X11/extensions/shape.h>
+#include <X11/Xmu/SysUtil.h>
+
+#include "wmsystemtray.h"
+#include "wmsystemtray.xpm"
+#include "fdtray.h"
+
+/* Global variables */
+volatile sig_atomic_t exitapp=False;
+volatile sig_atomic_t testmessage=False;
+
+const char *PROGNAME=NULL;
+Bool nonwmaker=False;
+int iconsize=24;
+Bool need_update=False;
+
+static char *display_name=NULL;
+static char *geometry="";
+static int debug_level=DEBUG_WARN;
+static int icons_per_row=2;
+static int icons_per_col=2;
+static int num_windows=1;
+static Bool id_windows=False;
+static Bool point_messages=False;
+static int fill_style=0;
+static int arrow_style=0;
+static Bool no_messages=False;
+static int testmessagecount=0;
+
+Display *display=NULL;
+int screen=0;
+Window root=None;
+Window selwindow=None;
+static Atom _NET_WM_PING;
+static Atom WM_DELETE_WINDOW;
+static Atom WM_PROTOCOLS;
+
+static Pixmap pixmap;
+static Window *mainwin=NULL, *iconwin=NULL;
+static struct trayicon *icons=NULL;
+static int num_mapped_icons=0;
+static int current_page=0;
+static Window down_window=None;
+static int down_button=0;
+static long selwindow_mask = PropertyChangeMask;
+static GC gc10x20, gc5x8;
+static char *fgcolor = NULL, *bgcolor = NULL;
+
+#define NUM_TYPES 1
+static struct trayfuncs *types[NUM_TYPES];
+
+static struct trayfuncs *get_type(int id){
+    if(id < 0 || id >= NUM_TYPES){
+        warn(DEBUG_WARN, "Invalid tray icon type %d", id);
+        return NULL;
+    } else {
+        return types[id];
+    }
+}
+
+/* X functions */
+typedef int (*x_error_handler)(Display *, XErrorEvent *);
+x_error_handler orig_x_error_handler = NULL;
+static Bool x_error_occurred = False;
+static int ignore_BadWindow(Display *d, XErrorEvent *e){
+    if(e->error_code != BadWindow) orig_x_error_handler(d,e);
+    warn(DEBUG_INFO, "Ignoring BadWindow error");
+    x_error_occurred = True;
+    return 0;
+}
+
+void *catch_BadWindow_errors(){
+    x_error_occurred = False;
+    if(!orig_x_error_handler){
+        orig_x_error_handler = XSetErrorHandler(&ignore_BadWindow);
+        return (void *)orig_x_error_handler;
+    } else {
+        return (void *)XSetErrorHandler(&ignore_BadWindow);
+    }
+}
+
+Bool uncatch_BadWindow_errors(void *v){
+    XSync(display, False);
+    XSetErrorHandler((x_error_handler)v);
+    return x_error_occurred;
+}
+
+Time get_X_time(){
+    XEvent ev;
+    XChangeProperty(display, selwindow, XA_WM_CLASS, XA_STRING, 8, PropModeAppend, NULL, 0);
+    XWindowEvent(display, selwindow, PropertyChangeMask, &ev);
+    warn(DEBUG_DEBUG, "X time is %ld", ev.xproperty.time);
+    return ev.xproperty.time;
+}
+
+/* Icon handling */
+Bool is_icon_parent(Window w){
+    for(int i=0; i<num_windows; i++){
+        if(iconwin[i]==w) return True;
+    }
+    return False;
+}
+
+struct trayicon *icon_add(int type, Window w, void *data){
+    if(exitapp) return NULL;
+    if(!get_type(type)) return NULL;
+
+    struct trayicon *icon = malloc(sizeof(struct trayicon));
+    if(!icon){
+        warn(DEBUG_ERROR, "Memory allocation failed");
+        return NULL;
+    }
+    warn(DEBUG_DEBUG, "Adding tray icon %lx of type %d", w, type);
+    icon->type = type;
+    icon->w = w;
+    icon->data = data;
+    icon->parent = None;
+    icon->x = 0; icon->y = 0;
+    icon->mapped = False;
+    icon->next = NULL;
+    struct trayicon **p;
+    for(p = &icons; *p; p=&(*p)->next);
+    *p = icon;
+    need_update = True;
+    return icon;
+}
+
+void icon_remove(Window w){
+    struct trayicon *i, **n;
+    n = &icons;
+    while(*n){
+        i = *n;
+        if(i->w == w){
+            *n = i->next;
+            warn(DEBUG_DEBUG, "Removing tray icon %lx of type %d", i->w, i->type);
+            struct trayfuncs *funcs = get_type(i->type);
+            if(funcs && funcs->remove_icon) funcs->remove_icon(i);
+            if(i->mapped){
+                num_mapped_icons--;
+                need_update = True;
+            }
+            free(i);
+        } else {
+            n = &i->next;
+        }
+    }
+}
+
+struct trayicon *icon_find(Window w){
+    for(struct trayicon *i=icons; i; i=i->next){
+        if(i->w == w) return i;
+    }
+    return NULL;
+}
+
+Bool icon_set_mapping(struct trayicon *icon, Bool map){
+    if(icon->mapped == map) return True;
+
+    warn(DEBUG_DEBUG, "%sapping tray icon %lx", map?"M":"Unm", icon->w);
+    icon->mapped = map;
+    num_mapped_icons += map?1:-1;
+    need_update = True;
+    return True;
+}
+
+
+Bool icon_begin_message(Window w, int id, int length, int timeout){
+    warn(DEBUG_INFO, "begin_icon_message called for window %lx id %d (length=%d timeout=%d)", w, id, length, timeout);
+    // XXX
+    return False;
+}
+
+Bool icon_message_data(Window w, int id, char *data, int datalen){
+    warn(DEBUG_INFO, "icon_message_data called for window %lx id %d: %.*s", w, id, datalen, data);
+    // XXX
+    return False;
+}
+
+//static void icon_message_show(struct iconmessage *msg){
+//    // XXX
+//    // If point_messages is true, look up the icon and if it has x and y then set the corresponding hints
+//}
+
+void icon_cancel_message(Window w, int id){
+    warn(DEBUG_INFO, "cancel_icon_message called for window %lx id %d", w, id);
+    // XXX
+}
+
+
+/* Signal handler */
+static void sighandler(int i){
+    switch(i){
+      case SIGINT:
+      case SIGQUIT:
+      case SIGKILL: // Useless, but...
+      case SIGPIPE:
+      case SIGTERM:
+      case SIGABRT:
+        exitapp=True;
+        break;
+      case SIGHUP:
+        // Reload something?
+        break;
+      case SIGUSR1:
+        testmessage=True;
+        break;
+      case SIGUSR2:
+        break;
+    }
+    signal(i,sighandler);
+}
+
+/* Display functions */
+static void update(){
+    int i, j, x, y, w;
+    int icons_per_page=icons_per_row*icons_per_col*num_windows;
+    char buf[42];
+    Window dummy;
+    int pages;
+
+    need_update = False;
+
+redo:
+    pages = (num_mapped_icons-1)/icons_per_page+1;
+    if(current_page >= pages){
+        warn(DEBUG_DEBUG, "Page %d is empty!", current_page+1);
+        current_page=pages-1;
+    }
+    warn(DEBUG_DEBUG, "Updating display: page %d of %d", current_page+1, pages);
+
+    for(i = 0; i<num_windows; i++){
+        warn(DEBUG_DEBUG, "Drawing window %d (%lx)", i, iconwin[i]);
+        if(arrow_style==0 ||
+           (arrow_style==1 && i==0) ||
+           (arrow_style==2 && i==num_windows-1)){
+            y=(current_page==0)?0:(iconwin[i]==down_window && down_button==-1)?20:10;
+            XCopyArea(display, pixmap, iconwin[i], gc5x8, 0,y, 15,10, 4,52);
+        }
+        if(arrow_style==0 ||
+           (arrow_style==1 && i==num_windows-1) ||
+           (arrow_style==2 && i==num_windows-1)){
+            sprintf(buf, "%d/%d", current_page+1, pages);
+            x = strlen(buf);
+            XClearArea(display, iconwin[i], 19,52, 25,8, False);
+            XDrawString(display, iconwin[i], gc5x8, (current_page>8)?19:24,60, buf, x);
+            y=(current_page+1>=pages)?0:(iconwin[i]==down_window && down_button==1)?20:10;
+            XCopyArea(display, pixmap, iconwin[i], gc5x8, 15,y, 15,10, 45,52);
+        }
+        if(id_windows){
+            sprintf(buf, "%d", i);
+            XDrawString(display, iconwin[i], gc10x20, 8,50, buf, strlen(buf));
+        }
+    }
+
+    struct trayicon *icon = icons;
+    i=-current_page*icons_per_page;
+    while(icon){
+        void *v=catch_BadWindow_errors();
+        if(!icon->mapped || i<0 || i>=icons_per_page){
+            warn(DEBUG_DEBUG, "Tray icon %lx is not visible", icon->w);
+            if(icon->parent == None){
+                // Parent it somewhere
+                warn(DEBUG_DEBUG, "Reparenting %lx to %lx", icon->w, iconwin[0]);
+                XReparentWindow(display, icon->w, iconwin[0], 0, 0);
+                icon->parent = iconwin[0];
+            }
+            XUnmapWindow(display, icon->w);
+        } else {
+            j = i;
+            switch(fill_style){
+              case 0:
+                w = j / (icons_per_row * icons_per_col);
+                j = j % (icons_per_row * icons_per_col);
+                y = j / icons_per_row;
+                x = j % icons_per_row;
+                break;
+              case 1:
+                y = j / (icons_per_row * num_windows);
+                j = j % (icons_per_row * num_windows);
+                w = j / icons_per_row;
+                x = j % icons_per_row;
+                break;
+              default:
+                x=0; y=0; w=0;
+                break;
+            }
+            x=8+x*iconsize;
+            y=4+y*iconsize;
+            warn(DEBUG_DEBUG, "[%d] Tray icon %lx at %d %d,%d", i, icon->w, w, x, y);
+            if(icon->parent != iconwin[w]){
+                warn(DEBUG_DEBUG, "Reparenting %lx to %lx", icon->w, iconwin[w]);
+                XReparentWindow(display, icon->w, iconwin[w], x, y);
+                icon->parent = iconwin[w];
+            }
+            XMoveResizeWindow(display, icon->w, x, y, iconsize, iconsize);
+            XClearArea(display, icon->w, 0, 0, 0, 0, True);
+            XMapRaised(display, icon->w);
+            XTranslateCoordinates(display, icon->w, root, iconsize/2, iconsize/2, &icon->x, &icon->y, &dummy);
+        }
+        if(uncatch_BadWindow_errors(v)){
+            warn(DEBUG_INFO, "Tray icon %lx is invalid, removing and restarting layout", icon->w);
+            icon_remove(icon->w);
+            goto redo;
+        }
+        if(icon->mapped) i++;
+        icon = icon->next;
+    }
+
+    // XXX: if point_messages is true, can we re-point any notifications?
+}
+
+void selwindow_add_mask(long mask){
+    selwindow_mask |= mask;
+    warn(DEBUG_DEBUG, "selwindow mask is now %lx", selwindow_mask);
+    XSelectInput(display, selwindow, selwindow_mask);
+}
+
+static void create_dock_windows(int argc, char **argv){
+    XClassHint     *classHint;
+    XWMHints       *wmHints;
+    XSizeHints     *sizeHints;
+    Atom           wmProtocols[2];
+    char           hostname[256];
+    XTextProperty  machineName;
+    XRectangle     rects[2] = {
+        { .x = 8, .y = 4, .width = 48, .height = 48 },
+        { .x = 4, .y = 52, .width = 56, .height = 10 }
+    };
+    Atom           _NET_WM_PID;
+    XGCValues      gcv;
+    unsigned long  gcm;
+    char           buf[1024], *n;
+    int            err, dummy=0, pid;
+    Pixel          fgpix, bgpix;
+
+    pid = getpid();
+    warn(DEBUG_DEBUG, "My pid is %d", pid);
+
+    warn(DEBUG_INFO, "Opening display '%s'", XDisplayName(display_name));
+    if(!(display = XOpenDisplay(display_name)))
+        die("Can't open display %s", XDisplayName(display_name));
+    screen = DefaultScreen(display);
+    root = RootWindow(display, screen);
+
+    fgpix = BlackPixel(display, screen);
+    bgpix = WhitePixel(display, screen);
+    if(fgcolor != NULL){
+        warn(DEBUG_DEBUG, "Allocating colormap entry for fgcolor '%s'", fgcolor);
+        XColor color;
+        XWindowAttributes a;
+        XGetWindowAttributes(display, root, &a);
+        color.pixel = 0;
+        if(!XParseColor(display, a.colormap, fgcolor, &color)){
+            warn(DEBUG_ERROR, "Specified foreground color '%s' could not be parsed, using Black", fgcolor);
+            fgcolor = NULL;
+        } else if(!XAllocColor(display, a.colormap, &color)) {
+            warn(DEBUG_ERROR, "Cannot allocate colormap entry for foreground color '%s', using Black", fgcolor);
+            fgcolor = NULL;
+        } else {
+            fgpix = color.pixel;
+        }
+    }
+    if(bgcolor != NULL){
+        warn(DEBUG_DEBUG, "Allocating colormap entry for bgcolor '%s'", bgcolor);
+        XColor color;
+        XWindowAttributes a;
+        XGetWindowAttributes(display, root, &a);
+        color.pixel = 0;
+        if(!XParseColor(display, a.colormap, bgcolor, &color)){
+            warn(DEBUG_ERROR, "Specified background color '%s' could not be parsed, using default", bgcolor);
+            bgcolor = NULL;
+        } else if(!XAllocColor(display, a.colormap, &color)) {
+            warn(DEBUG_ERROR, "Cannot allocate colormap entry for background color '%s', using default", bgcolor);
+            bgcolor = NULL;
+        } else {
+            bgpix = color.pixel;
+        }
+    }
+
+    warn(DEBUG_DEBUG, "Interning atoms");
+    _NET_WM_PING = XInternAtom(display, "_NET_WM_PING", False);
+    _NET_WM_PID = XInternAtom(display, "_NET_WM_PID", False);
+    WM_PROTOCOLS = XInternAtom(display, "WM_PROTOCOLS", False);
+    WM_DELETE_WINDOW = XInternAtom(display, "WM_DELETE_WINDOW", False);
+
+    /* Load images */
+    warn(DEBUG_DEBUG, "Loading XPM");
+    err = XpmCreatePixmapFromData(display, root, wmsystemtray_xpm, &pixmap, NULL, NULL);
+    if(err != XpmSuccess) die("Could not load xpm (%d)", err);
+
+    /* Create hints */
+    warn(DEBUG_DEBUG, "Allocating window hints");
+    sizeHints = XAllocSizeHints();
+    classHint = XAllocClassHint();
+    wmHints = XAllocWMHints();
+    if(!sizeHints || !classHint || !wmHints) die("Memory allocation failed");
+
+    sizeHints->flags = USSize | USPosition | PSize | PBaseSize | PMinSize | PMaxSize;
+    sizeHints->x = 0;
+    sizeHints->y = 0;
+    sizeHints->width = 64;
+    sizeHints->height = 64;
+    sizeHints->base_width = sizeHints->width;
+    sizeHints->base_height = sizeHints->height;
+    sizeHints->min_width = 64;
+    sizeHints->min_height = 64;
+    sizeHints->max_width = 64;
+    sizeHints->max_height = 64;
+    warn(DEBUG_DEBUG, "Parsing geometry string '%s'", geometry);
+    XWMGeometry(display, screen, geometry, NULL, 1, sizeHints,
+                &sizeHints->x, &sizeHints->y, &sizeHints->width, &sizeHints->height, &dummy);
+    sizeHints->base_width = sizeHints->width;
+    sizeHints->base_height = sizeHints->height;
+    warn(DEBUG_DEBUG,"%d %d %d %d", sizeHints->x, sizeHints->y, sizeHints->base_width, sizeHints->base_height);
+
+    classHint->res_class = "wmsystemtray";
+    classHint->res_name = buf;
+
+    if(nonwmaker){
+        wmHints->flags = StateHint | WindowGroupHint;
+        wmHints->initial_state = NormalState;
+    } else {
+        wmHints->flags = StateHint | IconWindowHint | IconPositionHint | WindowGroupHint;
+        wmHints->initial_state = WithdrawnState;
+        wmHints->icon_x = sizeHints->x;
+        wmHints->icon_y = sizeHints->y;
+    }
+
+    wmProtocols[0] = _NET_WM_PING;
+    wmProtocols[1] = WM_DELETE_WINDOW;
+
+    machineName.encoding = XA_STRING;
+    machineName.format = 8;
+    machineName.nitems = XmuGetHostname(hostname, sizeof(hostname));
+    machineName.value = (unsigned char *) hostname;
+
+    /* Create windows */
+    warn(DEBUG_DEBUG, "Allocating space for %d windows", num_windows);
+    mainwin = malloc(num_windows * sizeof(*mainwin));
+    iconwin = nonwmaker?mainwin:malloc(num_windows * sizeof(*iconwin));
+    if(!mainwin || !iconwin) die("Memory allocation failed");
+
+    warn(DEBUG_DEBUG, "Creating selection window");
+    selwindow = XCreateSimpleWindow(display, root, -1,-1,1,1,0,0,0);
+    XSelectInput(display, selwindow, selwindow_mask);
+    strncpy(buf, "selwindow", sizeof(buf));
+    XSetClassHint(display, selwindow, classHint);
+    XStoreName(display, selwindow, PROGNAME);
+    XSetCommand(display, selwindow, argv, argc);
+    XSetWMProtocols(display, selwindow, wmProtocols, 2);
+    XSetWMClientMachine(display, selwindow, &machineName);
+    XChangeProperty(display, selwindow, _NET_WM_PID, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&pid, 1);
+    XShapeCombineRectangles(display, selwindow, ShapeBounding, 0, 0, NULL, 0, ShapeSet, YXBanded);
+
+    warn(DEBUG_DEBUG, "Creating GCs");
+    gcm = GCForeground | GCBackground | GCGraphicsExposures | GCFont;
+    gcv.foreground = fgpix;
+    gcv.background = bgpix;
+    gcv.graphics_exposures = True;
+    gcv.font = XLoadFont(display, "10x20");
+    gc10x20 = XCreateGC(display, root, gcm, &gcv);
+    gcv.font = XLoadFont(display, "5x8");
+    gc5x8 = XCreateGC(display, root, gcm, &gcv);
+
+    for(int i=0; i<num_windows; i++){
+        if(nonwmaker){
+            iconwin[i] = XCreateSimpleWindow(display, root, sizeHints->x, sizeHints->y, sizeHints->width, sizeHints->height, 1, fgpix, bgpix);
+        } else {
+            mainwin[i] = XCreateSimpleWindow(display, root, -1,-1,1,1,0,0,0);
+            iconwin[i] = XCreateSimpleWindow(display, mainwin[i], sizeHints->x, sizeHints->y, sizeHints->width, sizeHints->height, 1, fgpix, bgpix);
+        }
+        warn(DEBUG_DEBUG, "Dock window #%d is %lx", i, iconwin[i]);
+
+        XSetWMNormalHints(display, mainwin[i], sizeHints);
+        XSetWMNormalHints(display, iconwin[i], sizeHints);
+
+        snprintf(buf, sizeof(buf), "%s%d", PROGNAME, i);
+        XSetClassHint(display, mainwin[i], classHint);
+        XSetClassHint(display, iconwin[i], classHint);
+
+        XSelectInput(display, iconwin[i], ButtonPressMask | ExposureMask | ButtonReleaseMask | StructureNotifyMask | VisibilityChangeMask);
+
+        XStoreName(display, mainwin[i], PROGNAME);
+        XStoreName(display, iconwin[i], PROGNAME);
+        XSetCommand(display, mainwin[i], argv, argc);
+        XSetCommand(display, iconwin[i], argv, argc);
+
+        if(!nonwmaker) wmHints->icon_window = iconwin[i];
+        wmHints->window_group = mainwin[i];
+        XSetWMHints(display, mainwin[i], wmHints);
+
+        XSetWMProtocols(display, mainwin[i], wmProtocols, 2);
+        XSetWMProtocols(display, iconwin[i], wmProtocols, 2);
+
+        XSetWMClientMachine(display, mainwin[i], &machineName);
+        XChangeProperty(display, mainwin[i], _NET_WM_PID, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&pid, 1);
+        XSetWMClientMachine(display, iconwin[i], &machineName);
+        XChangeProperty(display, iconwin[i], _NET_WM_PID, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&pid, 1);
+
+        if(!nonwmaker || bgcolor == NULL){
+            XSetWindowBackgroundPixmap(display, iconwin[i], ParentRelative);
+            XShapeCombineRectangles(display, iconwin[i], ShapeBounding, 0, 0, rects, 2, ShapeSet, YXBanded);
+        }
+
+        XMapWindow(display, mainwin[i]);
+    }
+
+    warn(DEBUG_DEBUG, "Freeing X hints");
+    XFree(wmHints);
+    XFree(sizeHints);
+    XFree(classHint);
+
+    update();
+}
+
+static void cleanup(){
+    warn(DEBUG_INFO, "Cleaning up for exit");
+    for(int i=0; i<NUM_TYPES; i++){
+        if(types[i] && types[i]->closing) types[i]->closing();
+    }
+    warn(DEBUG_DEBUG, "Removing all icons");
+    while(icons) icon_remove(icons->w);
+    warn(DEBUG_DEBUG, "Deinitializing protocols");
+    for(int i=0; i<NUM_TYPES; i++){
+        if(types[i] && types[i]->deinit) types[i]->deinit();
+    }
+    warn(DEBUG_DEBUG, "Closing display");
+    if(mainwin) free(mainwin);
+    if(iconwin && !nonwmaker) free(iconwin);
+    if(display) XCloseDisplay(display);
+}
+
+/* Error and warning functions */
+void warn(int level, char *fmt, ...){
+    va_list ap;
+
+    if(debug_level<level) return;
+    va_start(ap, fmt);
+    fprintf(stderr, "%s: ", PROGNAME);
+    vfprintf(stderr, fmt, ap);
+    fprintf(stderr, "\n");
+    va_end(ap);
+}
+
+void die(char *fmt, ...){
+    va_list ap;
+
+    cleanup();
+
+    va_start(ap, fmt);
+    fprintf(stderr, "%s: ", PROGNAME);
+    vfprintf(stderr, fmt, ap);
+    fprintf(stderr, "\n");
+    va_end(ap);
+    exit(1);
+}
+
+static void usage(int exitcode) __attribute__ ((noreturn));
+static void usage(int exitcode){
+    fprintf(stderr, "USAGE: %s [<options>]\n", PROGNAME);
+    fprintf(stderr, "  -display <display>    X display to connect to\n");
+    fprintf(stderr, "  -geometry <geom>      Initial window geometry\n");
+    fprintf(stderr, "      --help            This message\n");
+    fprintf(stderr, "  -V, --version         Print the version number and exit\n");
+    fprintf(stderr, "  -v, --verbose         Print more messages (can be repeated)\n");
+    fprintf(stderr, "  -q, --quiet           Print fewer messages (can be repeated)\n");
+    fprintf(stderr, "  -s, --small           Use small (16x16) icons\n");
+    fprintf(stderr, "  -w, --windows <n>     Number of dock windows to create\n");
+    fprintf(stderr, "      --id-windows      Identify the individual windows\n");
+//    fprintf(stderr, "  -P, --point-messages  If an icon sends a popup message through the systray\n                        protocol, have the notification point to the icon");
+    fprintf(stderr, "      --fill-rows       Fill the top row of all windows first\n");
+    fprintf(stderr, "      --arrows <place>  How to place the arrows: all, horizontal, vertical\n");
+    fprintf(stderr, "  -c, --fgcolor <color> Text color used, default is black\n");
+    fprintf(stderr, "      --bgcolor <color> The window background color in non-Window Maker mode\n");
+    fprintf(stderr, "      --non-wmaker      Use in a non-Window Maker window manager\n");
+    fprintf(stderr, "\n");
+    exit(exitcode);
+}
+
+static int parse_args(int argc, char **argv){
+    char *c;
+
+    if((PROGNAME=strrchr(argv[0], '/'))==NULL || !PROGNAME[1])
+        PROGNAME=argv[0];
+    else PROGNAME++;
+
+    /* Parse command line args */
+    int j=1;
+    for(int i=1; i<argc; i++){
+        argv[j++]=argv[i];
+        if(!strcmp(argv[i],"-display") || !strcmp(argv[i],"--display")){
+            if(i+1 >= argc) die("%s requires an argument", argv[i]);
+            display_name=argv[++i];
+            argv[j++]=argv[i];
+        } else if(!strcmp(argv[i],"-geometry") || !strcmp(argv[i],"--geometry")){
+            if(i+1 >= argc) die("%s requires an argument", argv[i]);
+            geometry=argv[++i];
+            argv[j++]=argv[i];
+        } else if(!strcmp(argv[i],"--help")){
+            usage(0);
+        } else if(!strcmp(argv[i],"-V") || !strcmp(argv[i],"--version")){
+            printf("%s\n", VERSION);
+            exit(0);
+        } else if(!strcmp(argv[i],"-v") || !strcmp(argv[i],"--verbose")){
+            debug_level++;
+        } else if(!strcmp(argv[i],"-q") || !strcmp(argv[i],"--quiet")){
+            debug_level--;
+        } else if(!strcmp(argv[i],"--non-wmaker")){
+            nonwmaker=True;
+        } else if(!strcmp(argv[i],"-s") || !strcmp(argv[i],"--small")){
+            iconsize=16;
+            icons_per_row=3;
+            icons_per_col=3;
+        } else if(!strcmp(argv[i],"-w") || !strcmp(argv[i],"--windows")){
+            if(i+1 >= argc) die("%s requires an argument", argv[i]);
+            num_windows=strtol(argv[i+1],&c,0);
+            if(num_windows<1 || *c) die("%s requires a positive integer", argv[i]);
+            i++;
+            argv[j++]=argv[i];
+        } else if(!strcmp(argv[i],"--id-windows")){
+            id_windows=True;
+            j--;
+        } else if(!strcmp(argv[i],"-P") || !strcmp(argv[i],"--point-messages")){
+            point_messages=True;
+        } else if(!strcmp(argv[i],"--fill-rows")){
+            fill_style=1;
+        } else if(!strcmp(argv[i],"--arrows")){
+            if(i+1 >= argc) die("%s requires an argument", argv[i]);
+            if(!strcmp(argv[i+1],"all")){
+                arrow_style=0;
+            } else if(!strcmp(argv[i+1],"horizontal")){
+                arrow_style=1;
+            } else if(!strcmp(argv[i+1],"vertical")){
+                arrow_style=2;
+            } else {
+                die("Invalid value for %s", argv[i]);
+            }
+            i++;
+            argv[j++]=argv[i];
+        } else if(!strcmp(argv[i],"-c") || !strcmp(argv[i],"--fgcolor")){
+            fgcolor = argv[++i];
+            argv[j++]=argv[i];
+        } else if(!strcmp(argv[i],"--bgcolor")){
+            bgcolor = argv[++i];
+            argv[j++]=argv[i];
+        } else {
+            debug_level=DEBUG_ERROR;
+            warn(DEBUG_ERROR, "Unrecognized command line argument %s", argv[i]);
+            usage(1);
+        }
+    }
+    return j;
+}
+
+int main(int argc, char *argv[]){
+    warn(DEBUG_DEBUG, "Parsing args");
+    argc = parse_args(argc, argv);
+
+    warn(DEBUG_DEBUG, "Creating windows");
+    create_dock_windows(argc, argv);
+
+    warn(DEBUG_DEBUG, "Initializing protocols");
+    for(int i=0; i<NUM_TYPES; i++) types[i]=NULL;
+    if(!(types[0] = fdtray_init(0, argc, argv)))
+        die("Could not initialize the freedesktop.org tray protocol");
+
+    warn(DEBUG_DEBUG, "Setting signal handlers");
+    signal(SIGINT, sighandler);
+    signal(SIGQUIT, sighandler);
+    signal(SIGKILL, sighandler); // Useless, but...
+    signal(SIGPIPE, sighandler);
+    signal(SIGTERM, sighandler);
+    signal(SIGABRT, sighandler);
+    signal(SIGHUP, sighandler);
+    signal(SIGUSR1, sighandler);
+    signal(SIGUSR2, sighandler);
+
+    XEvent ev;
+    int fd;
+    fd_set rfds;
+    warn(DEBUG_DEBUG, "Entering main loop");
+    while(!exitapp){
+        while(XPending(display)){
+            XNextEvent(display, &ev);
+            warn(DEBUG_DEBUG, "Got X event %d", ev.type);
+            switch(ev.type){
+              case GraphicsExpose:
+              case Expose:
+              case MapRequest:
+                need_update=True;
+                break;
+
+              case DestroyNotify:
+                if(exitapp) break;
+                if(selwindow==ev.xdestroywindow.window){
+                    warn(DEBUG_WARN, "Selection window destroyed!", ev.xdestroywindow.window);
+                    exitapp=1;
+                }
+                for(int i=0; !exitapp && i<num_windows; i++){
+                    if(mainwin[i]==ev.xdestroywindow.window || iconwin[i]==ev.xdestroywindow.window){
+                        warn(DEBUG_WARN, "Window %lx destroyed!", ev.xdestroywindow.window);
+                        exitapp=1;
+                    }
+                }
+                break;
+
+              case ButtonPress:
+                switch (ev.xbutton.button) {
+                  case 1:
+                    down_window = ev.xbutton.window;
+                    down_button = 0;
+                    if(ev.xbutton.y >= 53 && ev.xbutton.y < 61){
+                        if(ev.xbutton.x >= 5 && ev.xbutton.x < 18){
+                            warn(DEBUG_INFO, "Left button mouse down");
+                            down_button = -1;
+                        } else if(ev.xbutton.x >= 46 && ev.xbutton.x < 59){
+                            warn(DEBUG_INFO, "Right button mouse down");
+                            down_button = 1;
+                        }
+                    }
+                    need_update = True;
+                    break;
+                  case 4:
+                  case 6:
+                    warn(DEBUG_INFO, "Left/Up scroll wheel");
+                    if(current_page > 0){
+                        current_page--;
+                        need_update=True;
+                    }
+                    break;
+                  case 5:
+                  case 7:
+                    warn(DEBUG_INFO, "Right/Down scroll wheel");
+                    if(current_page < (num_mapped_icons-1)/icons_per_row/icons_per_col/num_windows){
+                        current_page++;
+                        need_update=True;
+                    }
+                    break;
+                }
+                break;
+
+              case ButtonRelease:
+                switch (ev.xbutton.button) {
+                  case 1:
+                    if(ev.xbutton.y >= 53 && ev.xbutton.y < 61){
+                        if(ev.xbutton.x >= 5 && ev.xbutton.x < 18 && down_button == -1){
+                            warn(DEBUG_INFO, "Left button mouse up");
+                            if(current_page > 0){
+                                current_page--;
+                                need_update=True;
+                            }
+                        } else if(ev.xbutton.x >= 46 && ev.xbutton.x < 59 && down_button == 1){
+                            warn(DEBUG_INFO, "Right button mouse up");
+                            if(current_page < (num_mapped_icons-1)/icons_per_row/icons_per_col/num_windows){
+                                current_page++;
+                                need_update=True;
+                            }
+                        }
+                    }
+                    down_window = None;
+                    down_button = 0;
+                    need_update = True;
+                    break;
+                }
+                break;
+
+              case ClientMessage:
+                if(ev.xclient.message_type == WM_PROTOCOLS && ev.xclient.format == 32){
+                    if(ev.xclient.data.l[0] == _NET_WM_PING){
+                        warn(DEBUG_DEBUG, "_NET_WM_PING!");
+                        ev.xclient.window = root;
+                        XSendEvent(display, root, False, SubstructureNotifyMask|SubstructureRedirectMask, &ev);
+                    } else if(ev.xclient.data.l[0] == WM_DELETE_WINDOW){
+                        warn(DEBUG_DEBUG, "WM_DELETE_WINDOW called for %lx!", ev.xclient.window);
+                        exitapp=1;
+                    }
+                }
+                break;
+            }
+            for(int i=0; i<NUM_TYPES; i++){
+                if(types[i]->handle_event) types[i]->handle_event(&ev);
+            }
+        }
+        if(exitapp) break;
+        warn(DEBUG_DEBUG, "Need update? %s", need_update?"Yes":"No");
+        if(need_update) update();
+        if(testmessage){
+            testmessage=False;
+            if(!icon_begin_message(iconwin[0],++testmessagecount,12,1000)){
+                warn(DEBUG_ERROR,"Could not begin test message");
+            } else if(!icon_message_data(iconwin[0],testmessagecount,"Test message",12)){
+                warn(DEBUG_ERROR,"Could not send test message data");
+            }
+        }
+
+        if(XPending(display)) continue;
+        fd=ConnectionNumber(display);
+        FD_ZERO(&rfds);
+        FD_SET(fd, &rfds);
+        select(fd+1, &rfds, NULL, NULL, NULL);
+    }
+    warn(DEBUG_DEBUG, "Main loop ended");
+
+    cleanup();
+
+    warn(DEBUG_DEBUG, "Exiting app");
+    return 0;
+}
diff --git a/wmsystemtray.h b/wmsystemtray.h
new file mode 100644
index 0000000..1eca54f
--- /dev/null
+++ b/wmsystemtray.h
@@ -0,0 +1,129 @@
+#ifndef WMSYSTEMTRAY_H
+#define WMSYSTEMTRAY_H
+
+#include <signal.h>
+#include <X11/Xlib.h>
+
+/*
+ * Structs
+ */
+struct trayicon {
+    int type;
+    Window w;
+    void *data;
+
+    /* private */
+    Bool mapped;
+    Window parent;
+    int x, y;
+    struct trayicon *next;
+};
+
+// This struct holds the necessary interface functions
+struct trayfuncs {
+    // Handle an X event. Or just ignore it, if it doesn't pertain to you.
+    void (*handle_event)(XEvent *);
+
+    // Called when a tray icon is being removed.
+    void (*remove_icon)(struct trayicon *);
+
+    // Called when the app is closing, before all icons are removed.
+    // This should take whatever action is necessary to not accept any more
+    // icons.
+    void (*closing)(void);
+
+    // Called after all icons have been removed.
+    void (*deinit)(void);
+};
+
+/*
+ * Global variables
+ */
+
+// Set this if you want the app to exit
+extern volatile sig_atomic_t exitapp;
+
+// Set this if you want to trigger a redraw
+extern Bool need_update;
+
+// Read-only data
+extern const char *PROGNAME;
+extern Bool nonwmaker;
+extern Display *display;
+extern int screen;
+extern Window root;
+
+// Be sure your icons remain this size or smaller.
+extern int iconsize;
+
+// This (dummy) window exists for use in holding ICCCM manager selections and
+// such. Use the provided utility function instead of XSelectInput
+extern Window selwindow;
+void selwindow_add_mask(long mask);
+
+/*
+ * X functions
+ */
+
+// Call this before accessing any window not created by us. MUST be matched by
+// a call to uncatch_BadWindow_errors.
+void *catch_BadWindow_errors();
+
+// Pass the pointer from catch_BadWindow_errors. Returns True if an error was
+// ignored.
+Bool uncatch_BadWindow_errors(void *v);
+
+// Get the current X time
+Time get_X_time();
+
+
+/*
+ * Icon handling
+ */
+
+// Call this to find out if a particular window is one of ours
+Bool is_icon_parent(Window w);
+
+// Add an icon for the specified window. Returns NULL on error.
+struct trayicon *icon_add(int type, Window w, void *data);
+
+// Remove the icon for the specified window.
+void icon_remove(Window w);
+
+// Find the icon struct for the specified window.
+struct trayicon *icon_find(Window w);
+
+// Any sane icon protocol will have the icon indicate whether it should
+// be mapped or not, rather than just trying to call XMapWindow or XUnmapWindow
+// directly. Use this function to indicate that state change.
+Bool icon_set_mapping(struct trayicon *icon, Bool map);
+
+// If the icon wants us to display a popup message (rather than just using
+// libnotify itself), begin by passing the metadata here. "id" must be unique
+// for this window. Returns True if the message may be continued.
+Bool icon_begin_message(Window w, int id, int length, int timeout);
+
+// After begin_icon_message(), call this function to pass the text of the
+// message. It will automatically be displayed once the full length has been
+// sent. No trailing '\0' is necessary.
+Bool icon_message_data(Window w, int id, char *data, int datalen);
+
+// If an existing popup is to be cancelled, call this.
+void icon_cancel_message(Window w, int id);
+
+
+/*
+ * Error handling functions
+ */
+
+#include <stdarg.h>
+
+#define DEBUG_DEBUG 4
+#define DEBUG_INFO  3
+#define DEBUG_WARN  2
+#define DEBUG_ERROR 1
+
+void warn(int level, char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
+void die(char *fmt, ...) __attribute__ ((noreturn, format (printf, 1, 2)));
+
+#endif
diff --git a/wmsystemtray.xpm b/wmsystemtray.xpm
new file mode 100644
index 0000000..8551778
--- /dev/null
+++ b/wmsystemtray.xpm
@@ -0,0 +1,42 @@
+/* XPM */
+static char * wmsystemtray_xpm[] = {
+"30 30 9 1",
+" 	c #020202",
+".	c #F6F2FE",
+"+	c #C6C6C6",
+"@	c #AEAAAE",
+"#	c #575557",
+"$	c #AAAAAA",
+"%	c #555555",
+"&	c #82828A",
+"*	c #000000",
+"                              ",
+" ............ + ............ +",
+" .@@@#@@@@@@@ + .@@@@@@@#@@@ +",
+" .$$%%$$$$$$$ + .$$$$$$$%%$$ +",
+" .@#%%%%#%%$$ + .@@%%%%#%%%$ +",
+" .$%%%%%%%%$$ + .$$%%%%%%%%$ +",
+" .@@%%$$@$$$$ + .@@$$$$@%%$$ +",
+" .$$$%$$$$$$$ + .$$$$$$$%$$$ +",
+" &            + &            +",
+"++++++++++++++++++++++++++++++",
+"                              ",
+" ............ + ............ +",
+" .@@@*@@@@@@@ + .@@@@@@@*@@@ +",
+" .$$**$$$$$$$ + .$$$$$$$**$$ +",
+" .@********$$ + .@@********$ +",
+" .$********$$ + .$$********$ +",
+" .@@**$$@$$$$ + .@@$$$$@**$$ +",
+" .$$$*$$$$$$$ + .$$$$$$$*$$$ +",
+" &            + &            +",
+"++++++++++++++++++++++++++++++",
+"                              ",
+"             &+             &+",
+"  $$$*$$$$$$$.+  $$$$$$$*$$$.+",
+"  @@**@@@@@@@.+  @@@@@@@**@@.+",
+"  $********@$.+  $$********$.+",
+"  @********@@.+  @@********@.+",
+"  $$**@$$@@@$.+  $$@@@$$**@$.+",
+"  @@@*@@@@@@@.+  @@@@@@@*@@@.+",
+" &............+ &............+",
+"++++++++++++++++++++++++++++++"};

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



More information about the Pkg-wmaker-commits mailing list