initial release
authorroot <root@Nokia-N810-43-7.(none)>
Tue, 12 Jan 2010 05:38:00 +0000 (14:38 +0900)
committerroot <root@Nokia-N810-43-7.(none)>
Tue, 12 Jan 2010 05:38:00 +0000 (14:38 +0900)
24 files changed:
CHANGELOG [new file with mode: 0644]
COPYING [new file with mode: 0644]
Makefile [new file with mode: 0644]
clientwin.c [new file with mode: 0644]
clientwin.h [new file with mode: 0644]
config.c [new file with mode: 0644]
config.h [new file with mode: 0644]
debian/changelog [new file with mode: 0644]
dlist.c [new file with mode: 0644]
dlist.h [new file with mode: 0644]
focus.c [new file with mode: 0644]
focus.h [new file with mode: 0644]
layout.c [new file with mode: 0644]
layout.h [new file with mode: 0644]
mainwin.c [new file with mode: 0644]
mainwin.h [new file with mode: 0644]
skippy-debian.patch [new file with mode: 0644]
skippy-xd.rc-default [new file with mode: 0644]
skippy.c [new file with mode: 0644]
skippy.h [new file with mode: 0644]
tooltip.c [new file with mode: 0644]
tooltip.h [new file with mode: 0644]
wm.c [new file with mode: 0644]
wm.h [new file with mode: 0644]

diff --git a/CHANGELOG b/CHANGELOG
new file mode 100644 (file)
index 0000000..14d7f49
--- /dev/null
+++ b/CHANGELOG
@@ -0,0 +1,11 @@
+Skippy-XD changelog
+
+0.5.0 -- "Damage, Inc."
+ - 'Lazy transparency' mode: let xcompmgr take care of alpha-blending the
+   mini-windows.
+ - Tooltip can have drop-shadow text, and can be translucent
+ - Read config from ~/.skippy-xd.rc since the configuration options have
+   changed
+ - Branched from 'regular' skippy, switched from Imlib2 to XRender
+   rendering, removed the old window snapshot code and replaced it with
+   XComposite / XDamage stuff
diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..d60c31a
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           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.
+\f
+                   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.)
+\f
+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.
+\f
+  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.
+\f
+  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
+\f
+           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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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 Library General
+Public License instead of this License.
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..3a75faf
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,34 @@
+PREFIX = /usr/
+BINDIR = ${PREFIX}/bin
+
+X11PREFIX = /usr/X11R6
+
+CFLAGS += -I${X11PREFIX}/include `imlib2-config --cflags` `pkg-config xft xrender xcomposite xdamage xfixes --cflags` -g -pedantic -Wall
+LDFLAGS += -L${X11PREFIX}/lib -lX11 -lm `imlib2-config --libs` `pkg-config xft xrender xcomposite xdamage xfixes --libs`
+
+# Disable post-processing effects
+#CFLAGS += -DNOEFFECTS
+
+# Comment these out to disable Xinerama support
+#CFLAGS += -DXINERAMA
+#LDFLAGS += -lXext -lXinerama
+
+# Uncomment this for Xinerama debugging
+#CFLAGS += -DDEBUG
+
+EXESUFFIX =
+
+SOURCES = skippy.c wm.c dlist.c mainwin.c clientwin.c layout.c focus.c config.c tooltip.c
+HEADERS = skippy.h wm.h dlist.h mainwin.h clientwin.h layout.h focus.h config.h tooltip.h
+
+all: skippy-xd${EXESUFFIX}
+
+skippy-xd${EXESUFFIX}: Makefile ${SOURCES} ${HEADERS}
+       gcc ${CFLAGS} -o skippy-xd${EXESUFFIX} ${SOURCES} ${LDFLAGS}
+
+clean:
+       rm -f skippy-xd${EXESUFFIX}
+
+install:
+       install -d ${DESTDIR}${BINDIR}
+       install -m 755 skippy-xd$(EXESUFFIX) ${DESTDIR}${BINDIR}/skippy-xd${EXESUFFIX}
diff --git a/clientwin.c b/clientwin.c
new file mode 100644 (file)
index 0000000..25eea4d
--- /dev/null
@@ -0,0 +1,412 @@
+/* Skippy - Seduces Kids Into Perversion
+ *
+ * Copyright (C) 2004 Hyriand <hyriand@thegraveyard.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "skippy.h"
+
+#define INTERSECTS(x1, y1, w1, h1, x2, y2, w2, h2) \
+       (((x1 >= x2 && x1 < (x2 + w2)) || (x2 >= x1 && x2 < (x1 + w1))) && \
+        ((y1 >= y2 && y1 < (y2 + h2)) || (y2 >= y1 && y2 < (y1 + h1))))
+
+int
+clientwin_cmp_func(dlist *l, void *data)
+{
+       return ((ClientWin*)l->data)->client.window == (Window)data;
+}
+
+int
+clientwin_validate_func(dlist *l, void *data)
+{
+       ClientWin *cw = (ClientWin *)l->data;
+       CARD32 desktop = (*(CARD32*)data),
+               w_desktop = wm_get_window_desktop(cw->mainwin->dpy, cw->client.window);
+       
+#ifdef XINERAMA
+       if(cw->mainwin->xin_active && ! INTERSECTS(cw->client.x, cw->client.y, cw->client.width, cw->client.height,
+                                                  cw->mainwin->xin_active->x_org, cw->mainwin->xin_active->y_org,
+                                                  cw->mainwin->xin_active->width, cw->mainwin->xin_active->height))
+               return 0;
+#endif
+       
+       return (w_desktop == (CARD32)-1 || desktop == w_desktop) &&
+              wm_validate_window(cw->mainwin->dpy, cw->client.window);
+}
+
+int
+clientwin_check_group_leader_func(dlist *l, void *data)
+{
+       ClientWin *cw = (ClientWin *)l->data;
+       return wm_get_group_leader(cw->mainwin->dpy, cw->client.window) == *((Window*)data);
+}
+
+int
+clientwin_sort_func(dlist* a, dlist* b, void* data)
+{
+       unsigned int pa = ((ClientWin*)a->data)->client.x * ((ClientWin*)a->data)->client.y,
+                    pb = ((ClientWin*)b->data)->client.x * ((ClientWin*)b->data)->client.y;
+       return (pa < pb) ? -1 : (pa == pb) ? 0 : 1;
+}
+
+ClientWin *
+clientwin_create(MainWin *mw, Window client)
+{
+       ClientWin *cw = (ClientWin *)malloc(sizeof(ClientWin));
+       XSetWindowAttributes sattr;
+       XWindowAttributes attr;
+       XRenderPictureAttributes pa;
+       
+       cw->mainwin = mw;
+       cw->pixmap = None;
+       cw->focused = 0;
+       cw->origin = cw->destination = None;
+       cw->damage = None;
+       cw->damaged = False;
+       /* cw->repair = None; */
+       
+       sattr.border_pixel = sattr.background_pixel = 0;
+       sattr.colormap = mw->colormap;
+       
+       sattr.event_mask = ButtonPressMask |
+                          ButtonReleaseMask |
+                          KeyReleaseMask |
+                          EnterWindowMask |
+                          LeaveWindowMask |
+                          PointerMotionMask |
+                          ExposureMask |
+                          FocusChangeMask;
+       
+       sattr.override_redirect = mw->lazy_trans;
+       
+       cw->client.window = client;
+       cw->mini.format = mw->format;
+       cw->mini.window = XCreateWindow(mw->dpy, mw->lazy_trans ? mw->root : mw->window, 0, 0, 1, 1, 0,
+                                       mw->depth, InputOutput, mw->visual,
+                                       CWColormap | CWBackPixel | CWBorderPixel | CWEventMask | CWOverrideRedirect, &sattr);
+       
+       if(cw->mini.window == None)
+       {
+               free(cw);
+               return 0;
+       }
+       
+       XGetWindowAttributes(mw->dpy, client, &attr);
+       cw->client.format = XRenderFindVisualFormat(mw->dpy, attr.visual);
+       
+       pa.subwindow_mode = IncludeInferiors;
+       cw->origin = XRenderCreatePicture (cw->mainwin->dpy, cw->client.window, cw->client.format, CPSubwindowMode, &pa);
+
+/*     XRenderSetPictureFilter(cw->mainwin->dpy, cw->origin, FilterBest, 0, 0); */
+       if(mw->gquality == 0)
+               XRenderSetPictureFilter(cw->mainwin->dpy, cw->origin, FilterFast, NULL, 0);
+       else if (mw->gquality == 1)
+                XRenderSetPictureFilter(cw->mainwin->dpy, cw->origin, FilterGood, NULL, 0);
+        else if (mw->gquality == 2)
+                XRenderSetPictureFilter(cw->mainwin->dpy, cw->origin, FilterBest, NULL, 0);
+        else if (mw->gquality == 3)
+                XRenderSetPictureFilter(cw->mainwin->dpy, cw->origin, FilterNearest, NULL, 0);
+        else if (mw->gquality == 4)
+                XRenderSetPictureFilter(cw->mainwin->dpy, cw->origin, FilterBilinear, NULL, 0);
+        else if (mw->gquality == 5)
+                XRenderSetPictureFilter(cw->mainwin->dpy, cw->origin, FilterConvolution, NULL, 0);
+
+       XSelectInput(cw->mainwin->dpy, cw->client.window, SubstructureNotifyMask | StructureNotifyMask);
+       
+       
+       return cw;
+}
+
+void
+clientwin_update(ClientWin *cw)
+{
+       Window tmpwin;
+       XWindowAttributes wattr;
+
+       XGetWindowAttributes(cw->mainwin->dpy, cw->client.window, &wattr);
+
+       cw->client.format = XRenderFindVisualFormat(cw->mainwin->dpy, wattr.visual);
+       XTranslateCoordinates(cw->mainwin->dpy, cw->client.window, wattr.root,
+                                     -wattr.border_width,
+/*                                     -90,-90,*/
+                                     -wattr.border_width,
+                                     &cw->client.x, &cw->client.y, &tmpwin);
+
+       cw->client.width = wattr.width;
+       cw->client.height = wattr.height;
+       
+       cw->mini.x = cw->mini.y = 0;
+       cw->mini.width = cw->mini.height = 1;
+}
+
+void
+clientwin_destroy(ClientWin *cw, Bool parentDestroyed)
+{
+       if(! parentDestroyed)
+       {
+               if(cw->origin != None)
+                       XRenderFreePicture(cw->mainwin->dpy, cw->origin);
+               if(cw->damage != None)
+                       XDamageDestroy(cw->mainwin->dpy, cw->damage);
+       }
+       if(cw->destination != None)
+               XRenderFreePicture(cw->mainwin->dpy, cw->destination);
+       if(cw->pixmap != None)
+               XFreePixmap(cw->mainwin->dpy, cw->pixmap);
+       
+       XDestroyWindow(cw->mainwin->dpy, cw->mini.window);
+       
+       free(cw);
+}
+
+static void
+clientwin_repaint(ClientWin *cw, XRectangle *rect)
+{
+       XRenderColor *tint = cw->focused ? &cw->mainwin->highlightTint : &cw->mainwin->normalTint;
+       int s_x = (double)rect->x * cw->factor,
+           s_y = (double)rect->y * cw->factor,
+           s_w = (double)rect->width * cw->factor,
+           s_h = (double)rect->height * cw->factor;
+
+       
+       if(cw->mainwin->lazy_trans)
+       {
+               XRenderComposite(cw->mainwin->dpy, PictOpSrc, cw->origin,
+                                cw->focused ? cw->mainwin->highlightPicture : cw->mainwin->normalPicture,
+                                cw->destination, s_x, s_y, 0, 0, s_x, s_y, s_w, s_h);
+       }
+       else
+       {
+               XRenderComposite(cw->mainwin->dpy, PictOpSrc, cw->mainwin->background, None, cw->destination, cw->mini.x + s_x, cw->mini.y + s_y, 0, 0, s_x, s_y, s_w, s_h);
+               XRenderComposite(cw->mainwin->dpy, PictOpOver, cw->origin,
+                                cw->focused ? cw->mainwin->highlightPicture : cw->mainwin->normalPicture,
+                                cw->destination, s_x, s_y, 0, 0, s_x, s_y, s_w, s_h);
+       }
+       
+       if(tint->alpha)
+               XRenderFillRectangle(cw->mainwin->dpy, PictOpOver, cw->destination, tint, s_x, s_y, s_w, s_h);
+       
+       XClearArea(cw->mainwin->dpy, cw->mini.window, s_x, s_y, s_w, s_h, False);
+}
+
+void
+clientwin_render(ClientWin *cw)
+{
+       XRectangle rect;
+       rect.x = rect.y = 0;
+       rect.width = cw->client.width;
+       rect.height = cw->client.height;
+       clientwin_repaint(cw, &rect);
+}
+
+void
+clientwin_repair(ClientWin *cw)
+{
+       int nrects, i;
+       XRectangle *rects;
+       XserverRegion rgn = XFixesCreateRegion(cw->mainwin->dpy, 0, 0);
+       
+       XDamageSubtract(cw->mainwin->dpy, cw->damage, None, rgn);
+       
+       rects = XFixesFetchRegion(cw->mainwin->dpy, rgn, &nrects);
+       XFixesDestroyRegion(cw->mainwin->dpy, rgn);
+       
+       for(i = 0; i < nrects; i++)
+               clientwin_repaint(cw, &rects[i]);
+       
+       if(rects)
+               XFree(rects);
+       
+       cw->damaged = False;
+}
+
+void
+clientwin_schedule_repair(ClientWin *cw, XRectangle *area)
+{
+       cw->damaged = True;
+}
+
+void
+clientwin_move(ClientWin *cw, float f, int x, int y)
+{
+       /* int border = MAX(1, (double)DISTANCE(cw->mainwin) * f * 0.25); */
+       int border = 0;
+       XSetWindowBorderWidth(cw->mainwin->dpy, cw->mini.window, border);
+       
+       cw->factor = f;
+       cw->mini.x = x + (int)cw->x * f;
+       cw->mini.y = y + (int)cw->y * f;
+       if(cw->mainwin->lazy_trans)
+       {
+               cw->mini.x += cw->mainwin->x;
+               cw->mini.y += cw->mainwin->y;
+       }
+       /*if(cw->client.width < 800)
+               cw->client.height -= 65;*/
+       cw->mini.width = MAX(1, (int)cw->client.width * f );
+       cw->mini.height = MAX(1, (int)cw->client.height * f );
+       XMoveResizeWindow(cw->mainwin->dpy, cw->mini.window, cw->mini.x - border, cw->mini.y - border, cw->mini.width, cw->mini.height);
+       
+       if(cw->pixmap)
+               XFreePixmap(cw->mainwin->dpy, cw->pixmap);
+       
+       if(cw->destination)
+               XRenderFreePicture(cw->mainwin->dpy, cw->destination);
+       
+       cw->pixmap = XCreatePixmap(cw->mainwin->dpy, cw->mini.window, cw->mini.width, cw->mini.height, cw->mainwin->depth);
+       XSetWindowBackgroundPixmap(cw->mainwin->dpy, cw->mini.window, cw->pixmap);
+       
+       cw->destination = XRenderCreatePicture(cw->mainwin->dpy, cw->pixmap, cw->mini.format, 0, 0);
+}
+
+void
+clientwin_map(ClientWin *cw)
+{
+       if(cw->damage)
+               XDamageDestroy(cw->mainwin->dpy, cw->damage);
+       
+       cw->damage = XDamageCreate(cw->mainwin->dpy, cw->client.window, XDamageReportDeltaRectangles);
+       XRenderSetPictureTransform(cw->mainwin->dpy, cw->origin, &cw->mainwin->transform);
+       
+       clientwin_render(cw);
+       
+       XMapWindow(cw->mainwin->dpy, cw->mini.window);
+}
+
+void
+clientwin_unmap(ClientWin *cw)
+{
+       if(cw->damage)
+       {
+               XDamageDestroy(cw->mainwin->dpy, cw->damage);
+               cw->damage = None;
+       }
+       
+       if(cw->destination)
+       {
+               XRenderFreePicture(cw->mainwin->dpy, cw->destination);
+               cw->destination = None;
+       }
+       
+       if(cw->pixmap)
+       {
+               XFreePixmap(cw->mainwin->dpy, cw->pixmap);
+               cw->pixmap = None;
+       }
+       
+       XUnmapWindow(cw->mainwin->dpy, cw->mini.window);
+       XSetWindowBackgroundPixmap(cw->mainwin->dpy, cw->mini.window, None);
+       
+       cw->focused = 0;
+}
+
+int client_msg(Display *disp, Window win, char *msg, /* {{{ */
+        unsigned long data0, unsigned long data1,
+        unsigned long data2, unsigned long data3,
+        unsigned long data4) {
+    XEvent event;
+    long mask = SubstructureRedirectMask | SubstructureNotifyMask;
+
+    event.xclient.type = ClientMessage;
+    event.xclient.serial = 0;
+    event.xclient.send_event = True;
+    event.xclient.message_type = XInternAtom(disp, msg, False);
+    event.xclient.window = win;
+    event.xclient.format = 32;
+    event.xclient.data.l[0] = data0;
+    event.xclient.data.l[1] = data1;
+    event.xclient.data.l[2] = data2;
+    event.xclient.data.l[3] = data3;
+    event.xclient.data.l[4] = data4;
+
+    if (XSendEvent(disp, DefaultRootWindow(disp), False, mask, &event)) {
+        return EXIT_SUCCESS;
+    }
+    else {
+        fprintf(stderr, "Cannot send %s event.\n", msg);
+        return EXIT_FAILURE;
+    }
+}
+
+
+static void
+childwin_focus(ClientWin *cw)
+{
+        XWarpPointer(cw->mainwin->dpy, None, cw->client.window, 0, 0, 0, 0, cw->client.width / 2, cw->client.height / 2);
+
+        client_msg(cw->mainwin->dpy, cw->client.window, "_NET_ACTIVE_WINDOW",
+                   0, 0, 0, 0, 0);
+        XMapRaised(cw->mainwin->dpy, cw->client.window);
+       /*XRaiseWindow(cw->mainwin->dpy, cw->client.window);*/
+       XSetInputFocus(cw->mainwin->dpy, cw->client.window, RevertToParent, CurrentTime);
+}
+
+int
+clientwin_handle(ClientWin *cw, XEvent *ev)
+{
+       if((ev->type == ButtonRelease && ev->xbutton.button == 1 && cw->mainwin->pressed == cw)) {
+               /*if((ev->xbutton.x >= 0 && ev->xbutton.y >= 0 && ev->xbutton.x < cw->mini.width && ev->xbutton.y < cw->mini.height))*/
+                       childwin_focus(cw);
+               cw->mainwin->pressed = 0;
+               return 1;
+       } else if(ev->type == KeyRelease) {
+               if(ev->xkey.keycode == cw->mainwin->key_up)
+                       focus_up(cw);
+               else if(ev->xkey.keycode == cw->mainwin->key_down)
+                       focus_down(cw);
+               else if(ev->xkey.keycode == cw->mainwin->key_left)
+                       focus_left(cw);
+               else if(ev->xkey.keycode == cw->mainwin->key_right)
+                       focus_right(cw);
+                else if(ev->xkey.keycode == cw->mainwin->key_f8)
+                       client_msg(cw->mainwin->dpy, cw->client.window, "_NET_CLOSE_WINDOW",
+                               0, 0, 0, 0, 0);
+                       /*client_msg(cw->mainwin->dpy, cw->client.window, "_NET_WM_STATE",
+                                       1, XInternAtom(cw->mainwin->dpy, "_NET_WM_STATE_FULLSCREEN", False), 0, 0, 0);*/
+               else if(ev->xkey.keycode == cw->mainwin->key_enter || ev->xkey.keycode == cw->mainwin->key_space) {
+                       childwin_focus(cw);
+                       return 1;
+               }
+       } else if(ev->type == ButtonPress && ev->xbutton.button == 1) {
+               cw->mainwin->pressed = cw;
+       } else if(ev->type == FocusIn) {
+               cw->focused = 1;
+               clientwin_render(cw);
+               XFlush(cw->mainwin->dpy);
+       } else if(ev->type == FocusOut) {
+               cw->focused = 0;
+               clientwin_render(cw);
+               XFlush(cw->mainwin->dpy);
+       } else if(ev->type == EnterNotify) {
+               XSetInputFocus(cw->mainwin->dpy, cw->mini.window, RevertToNone, CurrentTime);
+               if(cw->mainwin->tooltip)
+               {
+                       int win_title_len = 0;
+                       FcChar8 *win_title = wm_get_window_title(cw->mainwin->dpy, cw->client.window, &win_title_len);
+                       if(win_title)
+                       {
+                               tooltip_map(cw->mainwin->tooltip,
+                                           ev->xcrossing.x_root + 20, ev->xcrossing.y_root + 20,
+                                           win_title, win_title_len);
+                               free(win_title);
+                       }
+               }
+       } else if(ev->type == LeaveNotify) {
+               if(cw->mainwin->tooltip)
+                       tooltip_unmap(cw->mainwin->tooltip);
+       }
+       return 0;
+}
diff --git a/clientwin.h b/clientwin.h
new file mode 100644 (file)
index 0000000..bfed025
--- /dev/null
@@ -0,0 +1,74 @@
+/* Skippy - Seduces Kids Into Perversion
+ *
+ * Copyright (C) 2004 Hyriand <hyriand@thegraveyard.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef SKIPPY_CLIENT_H
+#define SKIPPY_CLIENT_H
+
+struct _SkippyWindow {
+       Window window;
+       int x, y;
+       unsigned int width, height;
+       XRenderPictFormat *format;
+};
+
+typedef struct _SkippyWindow SkippyWindow;
+
+struct _MainWin;
+struct _ClientWin{
+       struct _MainWin *mainwin;
+       
+       SkippyWindow client;
+       SkippyWindow mini;
+       
+       Pixmap pixmap;
+       Picture origin, destination;
+       Damage damage;
+       float factor;
+       
+       int focused;
+       
+       Bool damaged;
+       /* XserverRegion repair; */
+       
+       /* These are virtual positions set by the layout routine */
+       int x, y;
+};
+typedef struct _ClientWin ClientWin;
+
+int clientwin_validate_func(dlist *, void *);
+int clientwin_sort_func(dlist *, dlist *, void *);
+ClientWin *clientwin_create(struct _MainWin *, Window);
+void clientwin_destroy(ClientWin *, Bool parentDestroyed);
+void clientwin_move(ClientWin *, float, int, int);
+void clientwin_map(ClientWin *);
+void clientwin_unmap(ClientWin *);
+int clientwin_handle(ClientWin *, XEvent *);
+int clientwin_cmp_func(dlist *, void*);
+void clientwin_update(ClientWin *cw);
+int clientwin_check_group_leader_func(dlist *l, void *data);
+void clientwin_render(ClientWin *);
+void clientwin_schedule_repair(ClientWin *cw, XRectangle *area);
+void clientwin_repair(ClientWin *cw);
+int client_msg(Display *disp, Window win, char *msg, /* {{{ */
+        unsigned long data0, unsigned long data1,
+        unsigned long data2, unsigned long data3,
+        unsigned long data4);
+int clientwin_backhome();
+
+#endif /* SKIPPY_CLIENT_H */
diff --git a/config.c b/config.c
new file mode 100644 (file)
index 0000000..be67a64
--- /dev/null
+++ b/config.c
@@ -0,0 +1,198 @@
+/* Skippy - Seduces Kids Into Perversion
+ *
+ * Copyright (C) 2004 Hyriand <hyriand@thegraveyard.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "skippy.h"
+
+typedef struct
+{
+       char *section, *key, *value;
+} ConfigEntry;
+
+static char *
+copy_match(const char *line, regmatch_t *match)
+{
+       char *r;
+       r = (char *)malloc(match->rm_eo + 1);
+       strncpy(r, line + match->rm_so, match->rm_eo - match->rm_so);
+       r[match->rm_eo - match->rm_so] = 0;
+       return r;
+}
+
+static ConfigEntry *
+entry_new(const char *section, char *key, char *value)
+{
+       ConfigEntry *e = (ConfigEntry *)malloc(sizeof(ConfigEntry));
+       e->section = strdup(section);
+       e->key = key;
+       e->value = value;
+       return e;
+}
+
+static dlist *
+entry_set(dlist *config, const char *section, char *key, char *value)
+{
+       dlist *iter = dlist_first(config);
+       ConfigEntry *entry;
+       for(; iter; iter = iter->next)
+       {
+               entry = (ConfigEntry *)iter->data;
+               if(! strcasecmp(entry->section, section) && ! strcasecmp(entry->key, key)) {
+                       free(key);
+                       free(entry->value);
+                       entry->value = value;
+                       return config;
+               }
+       }
+       entry = entry_new(section, key, value);
+       return dlist_add(config, entry);
+}
+
+static dlist *
+config_parse(char *config)
+{
+       regex_t re_section, re_empty, re_entry;
+       regmatch_t matches[5];
+       char line[8192], *section = 0;
+       int ix = 0, l_ix = 0;
+       dlist *new_config = 0;
+       
+       regcomp(&re_section, "^[[:space:]]*\\[[[:space:]]*([[:alnum:]]*?)[[:space:]]*\\][[:space:]]*$", REG_EXTENDED);
+       regcomp(&re_empty, "^[[:space:]]*#|^[[:space:]]*$", REG_EXTENDED);
+       regcomp(&re_entry, "^[[:space:]]*([[:alnum:]]+)[[:space:]]*=[[:space:]]*(.*?)[[:space:]]*$", REG_EXTENDED);
+       
+       while(1)
+       {
+               if((config[ix] == '\0' || config[ix] == '\n'))
+               {
+                       line[l_ix] = 0;
+                       if(regexec(&re_empty, line, 5, matches, 0) == 0) {
+                               /* do nothing */
+                       } else if(regexec(&re_section, line, 5, matches, 0) == 0) {
+                               if(section)
+                                       free(section);
+                               section = copy_match(line, &matches[1]);
+                       } else if(section && regexec(&re_entry, line, 5, matches, 0) == 0) {
+                               char *key = copy_match(line, &matches[1]),
+                                    *value = copy_match(line, &matches[2]);
+                               new_config = entry_set(new_config, section, key, value);
+                       } else  {
+                               fprintf(stderr, "WARNING: Ignoring invalid line: %s\n", line);
+                       }
+                       l_ix = 0;
+               } else {
+                       line[l_ix] = config[ix];
+                       l_ix++;
+               }
+               if(config[ix] == 0)
+                       break;
+               ++ix;
+       }
+       
+       if(section)
+               free(section);
+       
+       regfree(&re_section);
+       regfree(&re_empty);
+       regfree(&re_entry);
+       
+       return new_config;
+}
+
+dlist *
+config_load(const char *path)
+{
+       FILE *fin = fopen(path, "r");
+       long flen;
+       char *data;
+       dlist *config;
+       
+       if(! fin)
+       {
+               fprintf(stderr, "WARNING: Couldn't load config file '%s'.\n", path);
+               return 0;
+       }
+       
+       fseek(fin, 0, SEEK_END);
+       flen = ftell(fin);
+       
+       if(! flen)
+       {
+               fprintf(stderr, "WARNING: '%s' is empty.\n", path);
+               fclose(fin);
+               return 0;
+       }
+       
+       fseek(fin, 0, SEEK_SET);
+       
+       data = (char *)malloc(flen + 1);
+       if(fread(data, 1, flen, fin) != flen)
+       {
+               fprintf(stderr, "WARNING: Couldn't read from config file '%s'.\n", path);
+               free(data);
+               fclose(fin);
+               return 0;
+       }
+       
+       fclose(fin);
+       
+       config = config_parse(data);
+       
+       free(data);
+       
+       return config;
+}
+
+static void
+entry_free(ConfigEntry *entry)
+{
+       free(entry->section);
+       free(entry->key);
+       free(entry->value);
+       free(entry);
+}
+
+void
+config_free(dlist *config)
+{
+       dlist_free_with_func(config, (dlist_free_func)entry_free);
+}
+
+static int
+entry_find_func(dlist *l, ConfigEntry *key)
+{
+       ConfigEntry *entry = (ConfigEntry*)l->data;
+       return ! (strcasecmp(entry->section, key->section) || strcasecmp(entry->key, key->key));
+}
+
+const char *
+config_get(dlist *config, const char *section, const char *key, const char *def)
+{
+       ConfigEntry needle;
+       dlist *iter;
+       
+       needle.section = (char *)section;
+       needle.key = (char *)key;
+       
+       iter = dlist_find(dlist_first(config), (dlist_match_func)entry_find_func, &needle);
+       
+       if(! iter)
+               return def;
+       
+       return ((ConfigEntry*)iter->data)->value;
+}
diff --git a/config.h b/config.h
new file mode 100644 (file)
index 0000000..ac4c983
--- /dev/null
+++ b/config.h
@@ -0,0 +1,28 @@
+/* Skippy - Seduces Kids Into Perversion
+ *
+ * Copyright (C) 2004 Hyriand <hyriand@thegraveyard.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef SKIPPY_CONFIG_H
+#define SKIPPY_CONFIG_H
+
+dlist *config_load(const char *);
+void config_free(dlist *);
+const char *config_get(dlist *, const char *, const char *, const char *);
+
+#endif /* SKIPPY_CONFIG_H */
+
diff --git a/debian/changelog b/debian/changelog
new file mode 100644 (file)
index 0000000..14d7f49
--- /dev/null
@@ -0,0 +1,11 @@
+Skippy-XD changelog
+
+0.5.0 -- "Damage, Inc."
+ - 'Lazy transparency' mode: let xcompmgr take care of alpha-blending the
+   mini-windows.
+ - Tooltip can have drop-shadow text, and can be translucent
+ - Read config from ~/.skippy-xd.rc since the configuration options have
+   changed
+ - Branched from 'regular' skippy, switched from Imlib2 to XRender
+   rendering, removed the old window snapshot code and replaced it with
+   XComposite / XDamage stuff
diff --git a/dlist.c b/dlist.c
new file mode 100644 (file)
index 0000000..70dd531
--- /dev/null
+++ b/dlist.c
@@ -0,0 +1,292 @@
+/* Skippy - Seduces Kids Into Perversion
+ *
+ * Copyright (C) 2004 Hyriand <hyriand@thegraveyard.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "skippy.h"
+
+dlist *
+dlist_last(dlist *l)
+{
+       while(l && l->next)
+               l = l->next;
+       return l;
+}
+
+dlist *
+dlist_first(dlist *l)
+{
+       while(l && l->prev)
+               l = l->prev;
+       return l;
+}
+
+dlist *
+dlist_add(dlist *l, void *d)
+{
+       dlist *new_elem;
+       
+       l = dlist_last(l);
+       new_elem = malloc(sizeof(dlist));
+       new_elem->prev = l;
+       new_elem->next = 0;
+       new_elem->data = d;
+       if(l)
+               l->next = new_elem;
+       return new_elem;
+}
+
+dlist *
+dlist_prepend(dlist *l, void *d)
+{
+       dlist *new_elem;
+       l = dlist_first(l);
+       new_elem = malloc(sizeof(dlist));
+       new_elem->prev = 0;
+       new_elem->next = l;
+       new_elem->data = d;
+       if(l)
+               l->prev = new_elem;
+       return new_elem;
+}
+
+dlist *
+dlist_remove(dlist *l)
+{
+       dlist *n = 0;
+       
+       if(! l)
+               return 0;
+       
+       if(l->prev) {
+               n = l->prev;
+               l->prev->next = l->next;
+       }
+       if(l->next) {
+               n = l->next;
+               l->next->prev = l->prev;
+       }
+       free(l);
+       return n;
+}
+
+dlist *
+dlist_remove_free_data(dlist *l)
+{
+       if(l && l->data)
+               free(l->data);
+       return dlist_remove(l);
+}
+
+dlist *
+dlist_remove_nth(dlist *l, unsigned int n)
+{
+       return dlist_remove(dlist_nth(l, n));
+}
+
+dlist *
+dlist_remove_nth_free_data(dlist *l, unsigned int n)
+{
+       return dlist_remove_free_data(dlist_nth(l, n));
+}
+
+int
+dlist_same(dlist *l1, dlist *l2)
+{
+       l1 = dlist_first(l1);
+       while(l1) {
+               if(l1 == l2)
+                       return 1;
+               l1 = l1->next;
+       }
+       return 0;
+}
+
+void
+dlist_reverse(dlist *l)
+{
+       dlist *iter1 = dlist_first(l),
+               *iter2 = dlist_last(l);
+       
+       while(iter1 != iter2) {
+               dlist_swap(iter1, iter2);
+               if(iter1->next == iter2)
+                       break;
+               iter1 = iter1->next;
+               iter2 = iter2->prev;
+       }
+}
+
+dlist *
+dlist_free(dlist *l)
+{
+       l = dlist_first(l);
+       
+       while(l) {
+               dlist *c = l;
+               l = l->next;
+               free(c);
+       }
+       
+       return 0;
+}
+
+dlist *
+dlist_dup(dlist *l)
+{
+       dlist *n = 0;
+       l = dlist_first(l);
+       
+       while(l) {
+               n = dlist_add(n, l->data);
+               l = l->next;
+       }
+       
+       return n;
+}
+
+dlist *
+dlist_find_all(dlist *l, dlist_match_func match, void *data)
+{
+       dlist *n = 0;
+       l = dlist_first(l);
+       
+       while(l) {
+               if(match(l, data))
+                       n = dlist_add(n, l->data);
+               l = l->next;
+       }
+       
+       return n;
+}
+
+dlist *
+dlist_find(dlist *l, dlist_match_func func, void *data)
+{
+       for(l = dlist_first(l); l; l = l->next)
+               if(func(l, data))
+                       break;
+       return l;
+}
+
+dlist *
+dlist_find_data(dlist *l, void *data)
+{
+       for(l = dlist_first(l); l; l = l->next)
+               if(l->data == data)
+                       break;
+       return l;
+}
+
+void
+dlist_free_data(dlist *l)
+{
+       l = dlist_first(l);
+       
+       while(l) {
+               if(l->data)
+                       free(l->data);
+               l->data = 0;
+               l = l->next;
+       }
+}
+
+dlist *
+dlist_free_with_data(dlist *l)
+{
+       l = dlist_first(l);
+       
+       while(l) {
+               dlist *c = l;
+               if(l->data)
+                       free(l->data);
+               l = l->next;
+               free(c);
+       }
+       
+       return 0;
+}
+
+dlist *
+dlist_free_with_func(dlist *l, dlist_free_func func)
+{
+       l = dlist_first(l);
+       
+       while(l) {
+               dlist *c = l;
+               if(l->data)
+                       func(l->data);
+               l = l->next;
+               free(c);
+       }
+       
+       return 0;
+}
+
+unsigned int
+dlist_len(dlist *l)
+{
+       unsigned int n = 0;
+       
+       l = dlist_first(l);
+       while(l) {
+               n++;
+               l = l->next;
+       }
+       
+       return n;
+}
+
+dlist *
+dlist_nth(dlist *l, unsigned int n)
+{
+       unsigned int i = 0;
+       l = dlist_first(l);
+       while(l && i != n) {
+               i++;
+               l = l->next;
+       }
+       
+       return l;
+}
+
+void
+dlist_swap(dlist *l1, dlist *l2)
+{
+       void *tmp = l1->data;
+       l1->data = l2->data;
+       l2->data = tmp;
+}
+
+void
+dlist_sort(dlist *l, dlist_cmp_func cmp, void *data)
+{
+       dlist *start = dlist_first(l);
+       while(start) {
+               dlist *iter = start;
+               start = 0;
+               while(iter) {
+                       if(iter->next && cmp(iter, iter->next, data) == 1) {
+                               dlist_swap(iter, iter->next);
+                               if(! start)
+                                       start = iter->prev;
+                       }
+                       iter = iter->next;
+               }
+       }
+}
+
diff --git a/dlist.h b/dlist.h
new file mode 100644 (file)
index 0000000..913375d
--- /dev/null
+++ b/dlist.h
@@ -0,0 +1,94 @@
+/* Skippy - Seduces Kids Into Perversion
+ *
+ * Copyright (C) 2004 Hyriand <hyriand@thegraveyard.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef SKIPPY_DLIST_H
+#define SKIPPY_DLIST_H
+
+struct dlist_element {
+       void *data;
+       struct dlist_element *next;
+       struct dlist_element *prev;
+};
+
+typedef struct dlist_element dlist;
+
+/* first element in list */
+dlist *dlist_first(dlist *);
+
+/* last element in list */
+dlist *dlist_last(dlist *);
+
+/* add element to the end of the list, returns new element */
+dlist *dlist_add(dlist *, void *);
+
+/* add element to the start of the list, returns new element (and thus, start of list) */
+dlist *dlist_prepend(dlist *, void *);
+
+/* remove an element from the list, returns another element in the list or 0 */
+dlist *dlist_remove(dlist *);
+dlist *dlist_remove_free_data(dlist *);
+dlist *dlist_remove_nth(dlist *, unsigned int n);
+dlist *dlist_remove_nth_free_data(dlist *, unsigned int n);
+
+/* free the list (not the data), returns 0 */
+dlist *dlist_free(dlist *);
+
+/* delete a list calling func on each data item */
+typedef void (*dlist_free_func)(void *);
+dlist *dlist_free_with_func(dlist *, dlist_free_func);
+
+/* free the data (not the list) */
+void dlist_free_data(dlist *);
+
+/* free both list and data, returns 0 */
+dlist *dlist_free_with_data(dlist *);
+
+/* return the length of the list */
+unsigned int dlist_len(dlist *);
+
+/* check if l1 and l2 are elements of the same list */
+int dlist_same(dlist *, dlist *);
+
+/* reverse a list (swaps data) */
+void dlist_reverse(dlist *);
+
+/* duplicate the list (not the data), returns new end */
+dlist *dlist_dup(dlist *);
+
+/* find all matching elements (returns new list or 0) */
+typedef int (*dlist_match_func)(dlist*, void *);
+dlist *dlist_find_all(dlist *, dlist_match_func, void *);
+
+/* find an element (returns element or 0) */
+dlist *dlist_find(dlist *, dlist_match_func, void *);
+
+/* find an element whose data pointer matches */
+dlist *dlist_find_data(dlist *, void *);
+
+/* return nth element or 0 */
+dlist *dlist_nth(dlist *l, unsigned int n);
+
+/* swap the data fields of 2 elements */
+void dlist_swap(dlist *, dlist *);
+
+/* sort a list (not very efficient, uses bubble-sort) */
+typedef int (*dlist_cmp_func)(dlist *, dlist *, void *);
+void dlist_sort(dlist *, dlist_cmp_func, void *);
+
+#endif /* SKIPPY_DLIST_H */
diff --git a/focus.c b/focus.c
new file mode 100644 (file)
index 0000000..7d1c3f2
--- /dev/null
+++ b/focus.c
@@ -0,0 +1,80 @@
+/* Skippy - Seduces Kids Into Perversion
+ *
+ * Copyright (C) 2004 Hyriand <hyriand@thegraveyard.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "skippy.h"
+
+typedef float (*dist_func)(SkippyWindow *, SkippyWindow *);
+typedef int (*match_func)(dlist *, SkippyWindow *);
+
+static void
+dir_focus(ClientWin *cw, match_func match, dist_func func)
+{
+       float diff = 0.0;
+       ClientWin *candidate = NULL;
+       dlist *iter, *candidates;
+       
+       candidates = dlist_first(dlist_find_all(cw->mainwin->cod, (dlist_match_func)match, &cw->mini));
+       if(! candidates)
+               return;
+       
+       for(iter = dlist_first(candidates); iter; iter = iter->next)
+       {
+               ClientWin *win = (ClientWin *)iter->data;
+               float distance = func(&cw->mini, &win->mini);
+               if(! candidate || distance < diff)
+               {
+                       candidate = win;
+                       diff = distance;
+               }
+       }
+       
+       XWarpPointer(candidate->mainwin->dpy, None, candidate->mini.window, 0, 0, 0, 0, candidate->mini.width / 2, candidate->mini.height / 2);
+       XSetInputFocus(candidate->mainwin->dpy, candidate->mini.window, RevertToParent, CurrentTime);
+       dlist_free(candidates);
+}
+
+#define HALF_H(w) (w->x + (int)w->width / 2)
+#define HALF_V(w) (w->y + (int)w->height / 2)
+#define SQR(x) pow(x, 2)
+
+#define DISTFUNC(name, d_x, d_y) \
+static float name (SkippyWindow *a, SkippyWindow *b) \
+{ return sqrt(SQR(d_x) + SQR(d_y)); }
+
+#define QUALFUNC(name, expr) \
+static int name(dlist *l, SkippyWindow *b) \
+{ SkippyWindow *a = &((ClientWin*)l->data)->mini; return expr; }
+
+#define FOCUSFUNC(name, qual, dist) \
+void name(ClientWin *cw) { dir_focus(cw, qual, dist); }
+
+DISTFUNC(dist_top_bottom, HALF_H(a) - HALF_H(b), a->y - b->y - (int)b->height)
+DISTFUNC(dist_bottom_top, HALF_H(a) - HALF_H(b), b->y - a->y - (int)a->height)
+DISTFUNC(dist_left_right, HALF_V(a) - HALF_V(b), a->x - b->x - (int)b->width)
+DISTFUNC(dist_right_left, HALF_V(a) - HALF_V(b), b->x - a->x - (int)a->width)
+
+QUALFUNC(win_above, a->y + a->height < b->y)
+QUALFUNC(win_below, b->y + b->height < a->y)
+QUALFUNC(win_left, a->x + a->width < b->x)
+QUALFUNC(win_right, b->x + b->width < a->x)
+
+FOCUSFUNC(focus_up, win_above, dist_top_bottom)
+FOCUSFUNC(focus_down, win_below, dist_bottom_top)
+FOCUSFUNC(focus_left, win_left, dist_left_right)
+FOCUSFUNC(focus_right, win_right, dist_right_left)
diff --git a/focus.h b/focus.h
new file mode 100644 (file)
index 0000000..f347891
--- /dev/null
+++ b/focus.h
@@ -0,0 +1,28 @@
+/* Skippy - Seduces Kids Into Perversion
+ *
+ * Copyright (C) 2004 Hyriand <hyriand@thegraveyard.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef SKIPPY_FOCUS_H
+#define SKIPPY_FOCUS_H
+
+void focus_up(ClientWin *cw);
+void focus_down(ClientWin *cw);
+void focus_left(ClientWin *cw);
+void focus_right(ClientWin *cw);
+
+#endif /* SKIPPY_FOCUS_H */
diff --git a/layout.c b/layout.c
new file mode 100644 (file)
index 0000000..78fb3be
--- /dev/null
+++ b/layout.c
@@ -0,0 +1,106 @@
+/* Skippy - Seduces Kids Into Perversion
+ *
+ * Copyright (C) 2004 Hyriand <hyriand@thegraveyard.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "skippy.h"
+
+void
+layout_run(MainWin *mw, dlist *windows, unsigned int *total_width, unsigned int *total_height)
+{
+       int sum_w = 0, max_h = 0, max_w = 0, max_row_w = 0;
+       int row_y = 0, y = 0, x = 0, row_h = 0;
+       
+       dlist *iter, *slots = 0, *slot_iter, *rows;
+       
+       rows = dlist_add(0, 0);
+       
+       windows = dlist_first(windows);
+       *total_width = *total_height = 0;
+       
+       for(iter = windows; iter; iter = iter->next)
+       {
+               ClientWin *cw = (ClientWin *)iter->data;
+               sum_w += cw->client.width;
+               max_w = MAX(max_w, cw->client.width);
+               max_h = MAX(max_h, cw->client.height);
+       }
+       
+       for(iter = windows; iter; iter = iter->next)
+       {
+               ClientWin *cw = (ClientWin*)iter->data;
+               dlist *slot_iter = dlist_first(slots);
+               for(; slot_iter; slot_iter = slot_iter->next)
+               {
+                       dlist *slot = (dlist *)slot_iter->data;
+                       int slot_h = -mw->distance;
+                       REDUCE(slot_h = slot_h + ((ClientWin*)iter->data)->client.height + mw->distance, slot);
+                       if(slot_h + mw->distance + cw->client.height < max_h)
+                       {
+                               slot_iter->data = dlist_add(slot, cw);
+                               break;
+                       }
+               }
+               if(! slot_iter)
+                       slots = dlist_add(slots, dlist_add(0, cw));
+       }
+       
+       max_row_w = sqrt(sum_w * max_h);
+       for(slot_iter = dlist_first(slots); slot_iter; slot_iter = slot_iter->next)
+       {
+               dlist *slot = (dlist *)slot_iter->data;
+               int slot_w = 0;
+               REDUCE(slot_w = MAX(slot_w, ((ClientWin*)iter->data)->client.width), slot);
+               y = row_y;
+               for(iter = dlist_first(slot); iter; iter = iter->next)
+               {
+                       ClientWin *cw = (ClientWin *)iter->data;
+                       cw->x = x + (slot_w - cw->client.width) / 2;
+                       cw->y = y;
+                       y += cw->client.height + mw->distance;
+                       rows->data = dlist_add((dlist *)rows->data, cw);
+               }
+               row_h = MAX(row_h, y - row_y);
+               *total_height = MAX(*total_height, y);
+               x += slot_w + mw->distance;
+               *total_width = MAX(*total_width, x);
+               if(x > max_row_w)
+               {
+                       x = 0;
+                       row_y += row_h;
+                       row_h = 0;
+                       rows = dlist_add(rows, 0);
+               }
+               dlist_free(slot);
+       }
+       dlist_free(slots);
+       
+       *total_width -= mw->distance;
+       *total_height -= mw->distance;
+       
+       for(iter = dlist_first(rows); iter; iter = iter->next)
+       {
+               dlist *row = (dlist *)iter->data;
+               int row_w = 0, xoff;
+               REDUCE(row_w = MAX(row_w, ((ClientWin*)iter->data)->x + ((ClientWin*)iter->data)->client.width), row);
+               xoff = (*total_width - row_w) / 2;
+               REDUCE(((ClientWin*)iter->data)->x += xoff, row);
+               dlist_free(row);
+       }
+       
+       dlist_free(rows);
+}
diff --git a/layout.h b/layout.h
new file mode 100644 (file)
index 0000000..36469ea
--- /dev/null
+++ b/layout.h
@@ -0,0 +1,25 @@
+/* Skippy - Seduces Kids Into Perversion
+ *
+ * Copyright (C) 2004 Hyriand <hyriand@thegraveyard.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef SKIPPY_LAYOUT_H
+#define SKIPPY_LAYOUT_H
+
+void layout_run(MainWin *, dlist *, unsigned int *, unsigned int *);
+
+#endif /* SKIPPY_LAYOUT_H */
diff --git a/mainwin.c b/mainwin.c
new file mode 100644 (file)
index 0000000..9d7fb8f
--- /dev/null
+++ b/mainwin.c
@@ -0,0 +1,491 @@
+/* Skippy - Seduces Kids Into Perversion
+ *
+ * Copyright (C) 2004 Hyriand <hyriand@thegraveyard.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "skippy.h"
+
+/* from 'uncover': */
+static Visual *
+find_argb_visual (Display *dpy, int scr)
+{
+    XVisualInfo                *xvi;
+    XVisualInfo                template;
+    int                        nvi;
+    int                        i;
+    XRenderPictFormat  *format;
+    Visual             *visual;
+    
+    template.screen = scr;
+    template.depth = 32;
+    template.class = TrueColor;
+    xvi = XGetVisualInfo (dpy, 
+                         VisualScreenMask |
+                         VisualDepthMask |
+                         VisualClassMask,
+                         &template,
+                         &nvi);
+    if (!xvi)
+       return 0;
+    visual = 0;
+    for (i = 0; i < nvi; i++)
+    {
+       format = XRenderFindVisualFormat (dpy, xvi[i].visual);
+       if (format->type == PictTypeDirect && format->direct.alphaMask)
+       {
+           visual = xvi[i].visual;
+           break;
+       }
+    }
+
+    XFree (xvi);
+    return visual;
+}
+
+MainWin *
+mainwin_create(Display *dpy, dlist *config)
+{
+       const char *tmp;
+       double tmp_d;
+       XColor exact_color;
+       XSetWindowAttributes wattr;
+       XWindowAttributes rootattr;
+       XRenderPictureAttributes pa;
+       XRenderColor clear;
+       int error_base;
+#ifdef XINERAMA
+       int event_base;
+#endif /* XINERAMA */
+       
+       MainWin *mw = (MainWin *)malloc(sizeof(MainWin));
+       
+       mw->dpy = dpy;
+       mw->screen = DefaultScreen(dpy);
+       mw->root = RootWindow(dpy, mw->screen);
+       mw->lazy_trans = (strcasecmp(config_get(config, "general", "lazyTrans", "false"), "true") == 0) ? True : False;
+       if(mw->lazy_trans)
+       {
+               mw->depth  = 32;
+               mw->visual = find_argb_visual(dpy, DefaultScreen(dpy));
+               if(! mw->visual)
+               {
+                       fprintf(stderr, "WARNING: Couldn't find argb visual, disabling lazy transparency.\n");
+                       mw->lazy_trans = False;
+               }
+       }
+       if(! mw->lazy_trans)
+       {
+               mw->depth = DefaultDepth(dpy, mw->screen);
+               mw->visual = DefaultVisual(dpy, mw->screen);
+       }
+       mw->colormap = XCreateColormap(dpy, mw->root, mw->visual, AllocNone);
+       mw->bg_pixmap = None;
+       mw->background = None;
+       mw->format = XRenderFindVisualFormat(dpy, mw->visual);
+#ifdef XINERAMA
+       mw->xin_info = mw->xin_active = 0;
+       mw->xin_screens = 0;
+#endif /* XINERAMA */
+       
+       mw->pressed = mw->focus = 0;
+       mw->tooltip = 0;
+       mw->cod = 0;
+       mw->key_up = XKeysymToKeycode(dpy, XK_Up);
+       mw->key_down = XKeysymToKeycode(dpy, XK_Down);
+       mw->key_left = XKeysymToKeycode(dpy, XK_Left);
+       mw->key_right = XKeysymToKeycode(dpy, XK_Right);
+       mw->key_enter = XKeysymToKeycode(dpy, XK_Return);
+       mw->key_space = XKeysymToKeycode(dpy, XK_space);
+       mw->key_escape = XKeysymToKeycode(dpy, XK_Escape);
+       mw->key_q = XKeysymToKeycode(dpy, XK_q);
+       mw->key_f6 = XKeysymToKeycode(dpy, XK_F6);
+        mw->key_f7 = XKeysymToKeycode(dpy, XK_F7);
+        mw->key_f8 = XKeysymToKeycode(dpy, XK_F8);
+        mw->key_f5 = XKeysymToKeycode(dpy, XK_F5);
+       
+       XGetWindowAttributes(dpy, mw->root, &rootattr);
+       mw->x = mw->y = 0;
+       mw->width = rootattr.width;
+       mw->height = rootattr.height;
+
+       wattr.colormap = mw->colormap;
+       wattr.background_pixel = wattr.border_pixel = 0;
+       wattr.event_mask = VisibilityChangeMask |
+                          ButtonReleaseMask;
+       
+       mw->window = XCreateWindow(dpy, mw->root, 0, 0, mw->width, mw->height, 0,
+                                  mw->depth, InputOutput, mw->visual,
+                                  CWBackPixel|CWBorderPixel|CWColormap|CWEventMask, &wattr);
+       if(mw->window == None) {
+               free(mw);
+               return 0;
+       }
+       
+#ifdef XINERAMA
+# ifdef DEBUG
+       fprintf(stderr, "--> checking for Xinerama extension... ");
+# endif /* DEBUG */
+       if(XineramaQueryExtension(dpy, &event_base, &error_base))
+       {
+# ifdef DEBUG
+           fprintf(stderr, "yes\n--> checking if Xinerama is enabled... ");
+# endif /* DEBUG */
+           if(XineramaIsActive(dpy))
+           {
+# ifdef DEBUG
+               fprintf(stderr, "yes\n--> fetching Xinerama info... ");
+# endif /* DEBUG */
+               mw->xin_info = XineramaQueryScreens(mw->dpy, &mw->xin_screens);
+# ifdef DEBUG          
+               fprintf(stderr, "done (%i screens)\n", mw->xin_screens);
+# endif /* DEBUG */
+           }
+# ifdef DEBUG
+           else
+               fprintf(stderr, "no\n");
+# endif /* DEBUG */
+       }
+# ifdef DEBUG
+       else
+           fprintf(stderr, "no\n");
+# endif /* DEBUG */
+#endif /* XINERAMA */
+       
+       if(! XDamageQueryExtension (dpy, &mw->damage_event_base, &error_base))
+       {
+               fprintf(stderr, "FATAL: XDamage extension not found.\n");
+               exit(1);
+       }
+       
+       if(! XCompositeQueryExtension(dpy, &event_base, &error_base))
+       {
+               fprintf(stderr, "FATAL: XComposite extension not found.\n");
+               exit(1);
+       }
+       
+       if(! XRenderQueryExtension(dpy, &event_base, &error_base))
+       {
+               fprintf(stderr, "FATAL: XRender extension not found.\n");
+               exit(1);
+       }
+       
+       if(! XFixesQueryExtension(dpy, &event_base, &error_base))
+       {
+               fprintf(stderr, "FATAL: XFixes extension not found.\n");
+               exit(1);
+       }
+       
+       XCompositeRedirectSubwindows (mw->dpy, mw->root, CompositeRedirectAutomatic);
+       
+       tmp_d = strtod(config_get(config, "general", "updateFreq", "10.0"), 0);
+       if(tmp_d != 0.0)
+               mw->poll_time = (1.0 / tmp_d) * 1000.0;
+       else
+               mw->poll_time = 0;
+       
+       tmp = config_get(config, "normal", "tint", "black");
+       if(! XParseColor(mw->dpy, mw->colormap, tmp, &exact_color))
+       {
+               fprintf(stderr, "Couldn't look up color '%s', reverting to black", tmp);
+               mw->normalTint.red = mw->normalTint.green = mw->normalTint.blue = 0;
+       }
+       else
+       {
+               mw->normalTint.red = exact_color.red;
+               mw->normalTint.green = exact_color.green;
+               mw->normalTint.blue = exact_color.blue;
+       }
+       mw->normalTint.alpha = MAX(0, MIN(strtol(config_get(config, "normal", "tintOpacity", "0"), 0, 0) * 256, 65535));
+       
+       tmp = config_get(config, "highlight", "tint", "#101020");
+       if(! XParseColor(mw->dpy, mw->colormap, tmp, &exact_color))
+       {
+               fprintf(stderr, "Couldn't look up color '%s', reverting to #101020", tmp);
+               mw->highlightTint.red = mw->highlightTint.green = 0x10;
+               mw->highlightTint.blue = 0x20;
+       }
+       else
+       {
+               mw->highlightTint.red = exact_color.red;
+               mw->highlightTint.green = exact_color.green;
+               mw->highlightTint.blue = exact_color.blue;
+       }
+       mw->highlightTint.alpha = MAX(0, MIN(strtol(config_get(config, "highlight", "tintOpacity", "64"), 0, 0) * 256, 65535));
+       
+       pa.repeat = True;
+       clear.alpha = MAX(0, MIN(strtol(config_get(config, "normal", "opacity", "200"), 0, 10) * 256, 65535));
+       mw->normalPixmap = XCreatePixmap(mw->dpy, mw->window, 1, 1, 8);
+       mw->normalPicture = XRenderCreatePicture(mw->dpy, mw->normalPixmap, XRenderFindStandardFormat(mw->dpy, PictStandardA8), CPRepeat, &pa);
+       XRenderFillRectangle(mw->dpy, PictOpSrc, mw->normalPicture, &clear, 0, 0, 1, 1);
+       
+       clear.alpha = MAX(0, MIN(strtol(config_get(config, "highlight", "opacity", "255"), 0, 10) * 256, 65535));
+       mw->highlightPixmap = XCreatePixmap(mw->dpy, mw->window, 1, 1, 8);
+       mw->highlightPicture = XRenderCreatePicture(mw->dpy, mw->highlightPixmap, XRenderFindStandardFormat(mw->dpy, PictStandardA8), CPRepeat, &pa);
+       XRenderFillRectangle(mw->dpy, PictOpSrc, mw->highlightPicture, &clear, 0, 0, 1, 1);
+       
+       tmp = config_get(config, "general", "distance", "50");
+       mw->distance = MAX(1, strtol(tmp, 0, 10));
+       
+       if(! strcasecmp(config_get(config, "tooltip", "show", "true"), "true"))
+                mw->tooltip = tooltip_create(mw, config);
+       
+       return mw;
+}
+
+int
+load_image (MainWin *mw, Bool rotate, Imlib_Image rootimg)
+{
+  Imlib_Image buffer;
+
+if (rotate)
+       buffer = mw->img_p;
+else
+       buffer = mw->img_l;
+
+  if (!buffer)
+    return 0;
+
+  imlib_context_set_image (buffer);
+
+  imlib_context_set_image (rootimg);
+  imlib_blend_image_onto_image (buffer, 0, 0, 0,  imlib_image_get_width (), imlib_image_get_height (),
+                                    0, 0, mw->width, mw->height);
+
+  imlib_context_set_image (buffer);
+  imlib_free_image ();
+
+  imlib_context_set_image (rootimg);
+
+  return 1;
+}
+
+
+
+void
+mainwin_update_background(MainWin *mw)
+{
+/*     Pixmap root = wm_get_root_pmap(mw->dpy);
+       XRenderColor black = { 0, 0, 0, 65535};*/
+       XRenderPictureAttributes pa;
+       Imlib_Context *context;
+       Imlib_Image image;
+       Bool rotate;
+
+       if(mw->width == 800)
+               rotate = False;
+       else
+               rotate = True;
+       
+       if(mw->bg_pixmap)
+               XFreePixmap(mw->dpy, mw->bg_pixmap);
+       if(mw->background)
+               XRenderFreePicture(mw->dpy, mw->background);
+       
+       mw->bg_pixmap = XCreatePixmap(mw->dpy, mw->window, mw->width, mw->height, mw->depth);
+       pa.repeat = True;
+       mw->background = XRenderCreatePicture(mw->dpy, mw->bg_pixmap, mw->format, CPRepeat, &pa);
+
+
+      context = imlib_context_new ();
+      imlib_context_push (context);
+      imlib_context_set_display (mw->dpy);
+      imlib_context_set_visual (mw->visual);
+      imlib_context_set_colormap (mw->colormap);
+      imlib_context_set_drawable (mw->bg_pixmap);
+      imlib_context_set_color_range (imlib_create_color_range ());
+
+      image = imlib_create_image (mw->width, mw->height);
+      imlib_context_set_image (image);
+
+      imlib_context_set_color (0, 0, 0, 255);
+      imlib_image_fill_rectangle (0, 0, mw->width, mw->height);
+
+      imlib_context_set_dither (1);
+      imlib_context_set_blend (1);
+
+      if (load_image (mw, rotate, image) == 0)
+      {
+       fprintf (stderr, "Bad image\n");
+        exit(1);
+      }
+
+      imlib_render_image_on_drawable (0, 0);
+      imlib_free_image ();
+      imlib_free_color_range ();
+
+/*     if(root == None)
+               XRenderFillRectangle(mw->dpy, PictOpSrc, mw->background, &black, 0, 0, mw->width, mw->height);
+       else
+       {
+               Picture from = XRenderCreatePicture(mw->dpy, root, XRenderFindVisualFormat(mw->dpy, DefaultVisual(mw->dpy, mw->screen)), 0, 0);
+               XRenderComposite(mw->dpy, PictOpSrc, from, None, mw->background, mw->x, mw->y, 0, 0, 0, 0, mw->width, mw->height);
+               XRenderFreePicture(mw->dpy, from);
+       }*/
+       
+       XSetWindowBackgroundPixmap(mw->dpy, mw->window, mw->bg_pixmap);
+       XClearWindow(mw->dpy, mw->window);
+}
+
+void
+mainwin_update(MainWin *mw)
+{
+#ifdef XINERAMA
+       XineramaScreenInfo *iter;
+       int i;
+       Window dummy_w;
+       int root_x, root_y, dummy_i;
+       unsigned int dummy_u;
+       
+       if(! mw->xin_info || ! mw->xin_screens)
+       {
+               mainwin_update_background(mw);
+               return;
+       }
+       
+# ifdef DEBUG
+       fprintf(stderr, "--> querying pointer... ");
+# endif /* DEBUG */
+       XQueryPointer(mw->dpy, mw->root, &dummy_w, &dummy_w, &root_x, &root_y, &dummy_i, &dummy_i, &dummy_u);
+# ifdef DEBUG  
+       fprintf(stderr, "+%i+%i\n", root_x, root_y);
+       
+       fprintf(stderr, "--> figuring out which screen we're on... ");
+# endif /* DEBUG */
+       iter = mw->xin_info;
+       for(i = 0; i < mw->xin_screens; ++i)
+       {
+               if(root_x >= iter->x_org && root_x < iter->x_org + iter->width &&
+                  root_y >= iter->y_org && root_y < iter->y_org + iter->height)
+               {
+# ifdef DEBUG
+                       fprintf(stderr, "screen %i %ix%i+%i+%i\n", iter->screen_number, iter->width, iter->height, iter->x_org, iter->y_org);
+# endif /* DEBUG */
+                       break;
+               }
+               iter++;
+       }
+       if(i == mw->xin_screens)
+       {
+# ifdef DEBUG 
+               fprintf(stderr, "unknown\n");
+# endif /* DEBUG */
+               return;
+       }
+       mw->x = iter->x_org;
+       mw->y = iter->y_org;
+       mw->width = iter->width;
+       mw->height = iter->height;
+       XMoveResizeWindow(mw->dpy, mw->window, iter->x_org, iter->y_org, mw->width, mw->height);
+       mw->xin_active = iter;
+#endif /* XINERAMA */
+       mainwin_update_background(mw);
+}
+
+void
+mainwin_map(MainWin *mw)
+{
+       wm_set_fullscreen(mw->dpy, mw->window, mw->x, mw->y, mw->width, mw->height);
+       mw->pressed = 0;
+       XMapWindow(mw->dpy, mw->window);
+       XRaiseWindow(mw->dpy, mw->window);
+}
+
+void
+mainwin_unmap(MainWin *mw)
+{
+       if(mw->tooltip)
+               tooltip_unmap(mw->tooltip);
+       if(mw->bg_pixmap)
+       {
+               XFreePixmap(mw->dpy, mw->bg_pixmap);
+               mw->bg_pixmap = None;
+       }
+       XUnmapWindow(mw->dpy, mw->window);
+}
+
+void
+mainwin_destroy(MainWin *mw)
+{
+       if(mw->tooltip)
+               tooltip_destroy(mw->tooltip);
+       
+       if(mw->background != None)
+               XRenderFreePicture(mw->dpy, mw->background);
+       
+       if(mw->bg_pixmap != None)
+               XFreePixmap(mw->dpy, mw->bg_pixmap);
+       
+       if(mw->normalPicture != None)
+               XRenderFreePicture(mw->dpy, mw->normalPicture);
+       
+       if(mw->highlightPicture != None)
+               XRenderFreePicture(mw->dpy, mw->highlightPicture);
+       
+       if(mw->normalPixmap != None)
+               XFreePixmap(mw->dpy, mw->normalPixmap);
+       
+       if(mw->highlightPixmap != None)
+               XFreePixmap(mw->dpy, mw->highlightPixmap);
+       
+       XDestroyWindow(mw->dpy, mw->window);
+       
+#ifdef XINERAMA
+       if(mw->xin_info)
+               XFree(mw->xin_info);
+#endif /* XINERAMA */
+       
+       free(mw);
+}
+
+void
+mainwin_transform(MainWin *mw, float f)
+{
+       mw->transform.matrix[0][0] = XDoubleToFixed(1.0 / f);
+       mw->transform.matrix[0][1] = 0.0;
+       mw->transform.matrix[0][2] = 0.0;
+       mw->transform.matrix[1][0] = 0.0;
+       mw->transform.matrix[1][1] = XDoubleToFixed(1.0 / f);
+       mw->transform.matrix[1][2] = 0.0;
+       mw->transform.matrix[2][0] = 0.0;
+       mw->transform.matrix[2][1] = 0.0;
+       mw->transform.matrix[2][2] = XDoubleToFixed(1.0);
+}
+
+int
+mainwin_handle(MainWin *mw, XEvent *ev)
+{
+       switch(ev->type)
+       {
+       case KeyPress:
+               if(ev->xkey.keycode == XKeysymToKeycode(mw->dpy, XK_q))
+                       return 2;
+               break;
+       case ButtonRelease:
+               return 3;
+               break;
+       case VisibilityNotify:
+               if(ev->xvisibility.state && mw->focus)
+               {
+                       XSetInputFocus(mw->dpy, mw->focus->mini.window, RevertToParent, CurrentTime);
+                       mw->focus = 0;
+               }
+               break;
+       default:
+               ;
+       }
+       return 0;
+}
diff --git a/mainwin.h b/mainwin.h
new file mode 100644 (file)
index 0000000..0099205
--- /dev/null
+++ b/mainwin.h
@@ -0,0 +1,78 @@
+/* Skippy - Seduces Kids Into Perversion
+ *
+ * Copyright (C) 2004 Hyriand <hyriand@thegraveyard.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef SKIPPY_MAINWIN_H
+#define SKIPPY_MAINWIN_H
+
+struct _Tooltip;
+
+struct _MainWin
+{
+       Display *dpy;
+       int screen;
+       Visual *visual;
+       Colormap colormap;
+       int depth;
+       Window root;
+       int damage_event_base;
+       
+       int poll_time;
+       Bool lazy_trans;
+
+       Imlib_Image img_p, img_l;
+       int gquality;
+
+       Window window;
+       Picture background;
+       Pixmap bg_pixmap;
+       int x, y;
+       unsigned int width, height, distance;
+       XRenderPictFormat *format;
+       XTransform transform;
+       
+       XRenderColor normalTint, highlightTint;
+       Pixmap normalPixmap, highlightPixmap;
+       Picture normalPicture, highlightPicture;
+       
+       ClientWin *pressed, *focus;
+       dlist *cod;
+       struct _Tooltip *tooltip;
+       
+       KeyCode key_act, key_up, key_down, key_left, key_right, key_enter, key_space, key_q, key_f5, key_f6, key_f7, key_f8, key_escape;
+       
+#ifdef XINERAMA
+       int xin_screens;
+       XineramaScreenInfo *xin_info, *xin_active;
+#endif /* XINERAMA */
+};
+typedef struct _MainWin MainWin;
+
+MainWin *mainwin_create(Display *, dlist *config);
+void mainwin_destroy(MainWin *);
+void mainwin_map(MainWin *);
+void mainwin_unmap(MainWin *);
+int mainwin_handle(MainWin *, XEvent *);
+int load_image (MainWin *mw, Bool rotate, Imlib_Image rootimg);
+void mainwin_update_background(MainWin *mw);
+void mainwin_update(MainWin *mw);
+void mainwin_transform(MainWin *mw, float f);
+int event_base;
+
+
+#endif /* SKIPPY_MAINWIN_H */
diff --git a/skippy-debian.patch b/skippy-debian.patch
new file mode 100644 (file)
index 0000000..b91c75d
--- /dev/null
@@ -0,0 +1,475 @@
+--- skippy-0.5.1rc1.orig/debian/docs
++++ skippy-0.5.1rc1/debian/docs
+@@ -0,0 +1 @@
++debian/README.Debian
+--- skippy-0.5.1rc1.orig/debian/menu
++++ skippy-0.5.1rc1/debian/menu
+@@ -0,0 +1,2 @@
++?package(skippy):needs="X11" section="Apps/Tools"\
++  title="skippy" command="/usr/bin/skippy"
+--- skippy-0.5.1rc1.orig/debian/control
++++ skippy-0.5.1rc1/debian/control
+@@ -0,0 +1,20 @@
++Source: skippy
++Section: x11
++Priority: optional
++Maintainer: Niv Altivanik (Debian Packages) <xaiki+deb@cxhome.ath.cx>
++Build-Depends: debhelper (>= 4.1.0), libxft-dev, libimlib2-dev, libxmu-dev, cdbs, libpcre3-dev
++Standards-Version: 3.6.1
++
++Package: skippy
++Architecture: any
++Depends: ${shlibs:Depends}
++Description: full-screen X11 task/window switcher, similar to OSX Expose
++ Skippy provides an alternative to taskbars or regular task-switchers,
++ by showing full-screen view of all the open windows on the current
++ desktop. You can navigate among the windows and select using the
++ mouse or keyboard. Skippy will work with any NETWM or GNOME WM Specs
++ compliant window-manager.
++ .
++ If you are using X.org's Xserver with the XDamage, Xcomposite, and
++ XFixes extensions, you probably want skippy-xd instead.
++ (they will be packaged in debian when X.org is available).
+--- skippy-0.5.1rc1.orig/debian/rules
++++ skippy-0.5.1rc1/debian/rules
+@@ -0,0 +1,22 @@
++#!/usr/bin/make -f
++# -*- makefile -*-
++
++include /usr/share/cdbs/1/rules/debhelper.mk
++include /usr/share/cdbs/1/class/makefile.mk
++include /usr/share/cdbs/1/rules/simple-patchsys.mk
++
++DEB_MAKE_INVOKE               := CFLAGS="$(CFLAGS)" make 
++DEB_MAKE_CLEAN_TARGET   := clean
++DEB_MAKE_BUILD_TARGET   := all
++DEB_MAKE_INSTALL_TARGET := install DESTDIR=$(CURDIR)/debian/skippy/ PREFIX=/usr/
++DEB_INSTALL_MANPAGES_skippy        := debian/skippyrc.1
++
++install/skippy::
++      mkdir -p $(CURDIR)/debian/skippy/etc/X11/skippy/
++      mkdir -p $(CURDIR)/debian/skippy/usr/share/doc/skippy/
++
++      cp -a $(CURDIR)/skippyrc-default \
++              $(CURDIR)/debian/skippy/etc/X11/skippy/skippyrc
++      cp -a $(CURDIR)/debian/examples \
++              $(CURDIR)/debian/skippy/usr/share/doc/skippy
++      cat $(CURDIR)/skippyrc-default >>  $(CURDIR)/debian/skippy/usr/share/doc/skippy/examples/skippyrc-default
+--- skippy-0.5.1rc1.orig/debian/watch
++++ skippy-0.5.1rc1/debian/watch
+@@ -0,0 +1,2 @@
++version=2
++http://thegraveyard.org/files/skippy-([0-9]+.*)\.tar\.bz2
+--- skippy-0.5.1rc1.orig/debian/changelog
++++ skippy-0.5.1rc1/debian/changelog
+@@ -0,0 +1,53 @@
++skippy (0.5.1rc1-1) unstable; urgency=low
++
++  * The "All Bugs Free Release".
++  * New Upstream Release.
++    . ported debian/patches/10_config_file_overflow_fix.patch to the new code.
++  * Fixed the watch file.
++  * Bug fix: "skippy's postinst should not echo", thanks to Michael Urman
++    (Closes: #302091).
++    . removed the skippy.postinst notification, 
++      ... let's hope the user will find the doc.
++  * Bug fix: "~/.skippyrc not loaded", thanks to Nick Hastings
++    (Closes: #302131).
++    . my patch was broken, this bug in fact was a missplaced printf.
++
++ -- Niv Altivanik (Debian Packages) <xaiki+deb@cxhome.ath.cx>  Wed, 30 Mar 2005 15:11:45 +0200
++
++skippy (0.5.0-3) unstable; urgency=low
++
++  * The "Documentation Bugs Free Release".
++  * Bug fix: "README.Debian mistake: XSession.d instead of Xsession.d", 
++    thanks to Adam Porter (Closes: #302055).
++    . fixed the (last ?) typo in README.Debian.
++  * Added a block to README.Debian about single user use
++
++ -- Niv Altivanik (Debian Packages) <xaiki+deb@cxhome.ath.cx>  Wed, 30 Mar 2005 01:03:56 +0200
++
++skippy (0.5.0-2) unstable; urgency=low
++
++  * Added a note about auto-startup of skippy to Readme.Debian, skippy.postinst
++    and created the /usr/share/doc/skippy/examples directory. 
++    (thanks to Steve Kemp).
++  * Added 11_find_skippyrc_in_etc.patch and copied the default skippyrc there.
++    (thanks to David Moreno Garza).
++  * Bug fix: "skippy: man page issues", thanks to Andre Lehovich and Zack Cerza
++    (Closes:  #301581).
++    . the default skippyrc is now on the right place.
++    . fixed typo, removed useless SYNOPSYS field
++  * Bug fix: "Error:BadAccess (attempt to access private resource
++    denied); on startup", thanks to Wesley J. Landaker (Closes:  #300991).
++    . Added a note on the README file.
++
++ -- Niv Altivanik (Debian Packages) <xaiki+deb@cxhome.ath.cx>  Sun, 27 Mar 2005 13:26:00 +0200
++
++skippy (0.5.0-1) unstable; urgency=low
++
++  * Initial Release.
++  * Fixed typos and first packages stupidity, thanks to Steve Greenland.
++    . .skippyrc manpage.
++    . Description: field.
++  * Moved package to cdbs
++  * Uploaded to Debian (closes: #259096)
++
++ -- Niv Altivanik (Debian Packages) <xaiki+deb@cxhome.ath.cx>  Sun, 30 Jan 2005 01:42:41 +0100
+--- skippy-0.5.1rc1.orig/debian/manpages
++++ skippy-0.5.1rc1/debian/manpages
+@@ -0,0 +1,2 @@
++debian/skippy.1
++debian/skippyrc.1
+--- skippy-0.5.1rc1.orig/debian/compat
++++ skippy-0.5.1rc1/debian/compat
+@@ -0,0 +1 @@
++4
+--- skippy-0.5.1rc1.orig/debian/skippy.1
++++ skippy-0.5.1rc1/debian/skippy.1
+@@ -0,0 +1,75 @@
++.\"                                      Hey, EMACS: -*- nroff -*-
++.\" 
++.\" 
++.\"   This manpage 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; version 2 dated June, 1991.
++.\"
++.\"   This package 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 package; if not, write to the Free Software
++.\"   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
++.\"   02111-1307, USA.
++.\"
++.\"  On Debian systems, the complete text of the GNU General Public
++.\"  License can be found in the file `/usr/share/common-licenses/GPL'.
++.\"
++.\"
++.\" First parameter, NAME, should be all caps
++.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
++.\" other parameters are allowed: see man(7), man(1)
++.TH SKIPPY 1 "December  6, 2004"
++.\" Please adjust this date whenever revising the manpage.
++.\"
++.\" Some roff macros, for reference:
++.\" .nh        disable hyphenation
++.\" .hy        enable hyphenation
++.\" .ad l      left justify
++.\" .ad b      justify to both left and right margins
++.\" .nf        disable filling
++.\" .fi        enable filling
++.\" .br        insert line break
++.\" .sp <n>    insert n+1 empty lines
++.\" for manpage-specific macros, see man(7)
++.SH NAME
++skippy \- Full Screen Task Switcher for X11
++.SH SYNOPSIS
++.B skippy
++.SH DESCRIPTION
++.\" TeX users may be more comfortable with the \fB<whatever>\fP and
++.\" \fI<whatever>\fP escape sequences to invode bold face and italics, 
++.\" respectively.
++\fBskippy\fP is a program that allows you to manage your windows in an
++efficient way. It is highly inspired by Apple Expose.
++.SH NOTE
++If you are using the X.org X server, there is a version of skippy,
++called skippy-xd that makes use of the new XDamage, XComposite and
++XFixes extensions. I suggest you use the xd version instead.
++
++Please note that skippy won't launch when another application is using
++X_GrabKey ( such as xbindkeys ), please see the README.Debian file for
++more informations.
++
++.SH USAGE
++Press the hotkey (the default hotkey is F11). Then choose a window
++with either the keyboard (you can use up, down, left and right to
++navigate) or the mouse (just hover over a window) and activate it by
++pressing the left mouse button or the return or spacebar key.
++
++.SH FILES
++.BR ~/.skippyrc
++.BR /etc/X11/skippy/skippyrc
++
++.SH SEE ALSO
++.BR skippyrc (1),
++.BR skippy-xd (1)
++
++.SH AUTHOR
++skippy was written by Hyriand <hyriand@thegraveyard.org>.
++.PP
++This manual page was written by Niv ALTIVANIK <xaiki@cxhome.ath.cx>,
++for the Debian project (but may be used by others).
+--- skippy-0.5.1rc1.orig/debian/README.Debian
++++ skippy-0.5.1rc1/debian/README.Debian
+@@ -0,0 +1,25 @@
++If you wish to have skippy automatically launched at X startup for all 
++your users please add the /usr/share/doc/skippy/examples/99Skippy file 
++to your /etc/X11/Xsession.d/
++
++If you only wish to have skippy loaded for single users, please add 
++skippy & 
++to your ~/.xsession ( for *dm based logins ) and ~/.xinitrc ( for startx 
++based X logins ) 
++
++--
++
++Warning: skippy won't work in conjunction of others x-key-grabbing application,
++such as xbindkeys for example
++
++If you get this error: 
++
++X Error of failed request:  BadAccess (attempt to access private resource denied)
++  Major opcode of failed request:  33 (X_GrabKey)
++  Serial number of failed request:  90
++  Current serial number in output stream:  90
++
++look for key-grabbing applications, and please report me if some package 
++auto-launches at X startup per default and breaks skippy.
++
++ -- Niv Altivanik (Debian Packages) <xaiki+deb@cxhome.ath.cx>, Wed Mar 30 01:03:51 2005
+--- skippy-0.5.1rc1.orig/debian/patches/10_config_file_overflow_fix.patch
++++ skippy-0.5.1rc1/debian/patches/10_config_file_overflow_fix.patch
+@@ -0,0 +1,70 @@
++--- skippy-0.5.1rc1/config.c  2004-09-13 19:41:37.000000000 +0200
+++++ skippy-0.5.1rc1-fixed/config.c    2005-03-30 15:17:18.000000000 +0200
++@@ -70,14 +70,16 @@
++ }
++ 
++ static dlist *
++-config_parse(char *config)
+++config_parse(char *config, long flen)
++ {
++      regex_t re_section, re_empty, re_entry;
++      regmatch_t matches[5];
++-     char line[8192], *section = 0;
+++     char *line, *section = 0;
++      int ix = 0, l_ix = 0;
++      dlist *new_config = 0;
++      
+++     line = (char *) malloc(sizeof(char)*flen);
+++
++      regcomp(&re_section, "^[[:space:]]*\\[[[:space:]]*([[:alnum:]]*?)[[:space:]]*\\][[:space:]]*$", REG_EXTENDED);
++      regcomp(&re_empty, "^[[:space:]]*#|^[[:space:]]*$", REG_EXTENDED);
++      regcomp(&re_entry, "^[[:space:]]*([[:alnum:]]+)[[:space:]]*=[[:space:]]*(.*?)[[:space:]]*$", REG_EXTENDED);
++@@ -117,24 +119,28 @@
++      regfree(&re_empty);
++      regfree(&re_entry);
++      
+++     free(line);
+++
++      return new_config;
++ }
++ 
++ #else /* Use PCRE regexps */
++ 
++ static dlist *
++-config_parse(char *config)
+++config_parse(char *config, long flen)
++ {
++      pcre * re_section, * re_empty, * re_entry;
++      const char * pcre_err;
++      int pcre_err_pos;
++      int ovec[30], matches;
++      
++-     char line[8192];
+++     char *line;
++      const char *section = 0;
++      int ix = 0, l_ix = 0;
++      
++      dlist * new_config = 0;
+++
+++     line = (char *) malloc(sizeof(char)*flen);
++      
++      re_section = pcre_compile("^\\s*\\[\\s*(\\w*)\\s*\\]\\s*$", 0, &pcre_err, &pcre_err_pos, 0);
++      re_empty = pcre_compile("^\\s*#|^\\s*$", 0, &pcre_err, &pcre_err_pos, 0);
++@@ -180,6 +186,8 @@
++      pcre_free(re_section);
++      pcre_free(re_empty);
++      pcre_free(re_entry);
+++
+++     free(line);
++      
++      return new_config;
++ }
++@@ -223,7 +231,7 @@
++      
++      fclose(fin);
++      
++-     config = config_parse(data);
+++     config = config_parse(data, flen);
++      
++      free(data);
++      
+--- skippy-0.5.1rc1.orig/debian/patches/11_find_skippyrc_in_etc.patch
++++ skippy-0.5.1rc1/debian/patches/11_find_skippyrc_in_etc.patch
+@@ -0,0 +1,17 @@
++--- skippy-0.5.0/skippy.c     2005-03-22 12:45:43.000000000 +0100
+++++ skippy-0.5.0-fixed/skippy.c       2005-03-30 14:27:09.000000000 +0200
++@@ -232,8 +232,13 @@
++              snprintf(cfgpath, 8191, "%s/%s", homedir, ".skippyrc");
++              config = config_load(cfgpath);
++      }
++-     else
+++     else
++              fprintf(stderr, "WARNING: $HOME not set, not loading config.\n");
+++     if(config == 0) {
+++       fprintf(stderr, "WARNING: $HOME/.skippyrc not found "
+++                       "loading default config.\n");
+++             config = config_load("/etc/X11/skippy/skippyrc");
+++     }
++      
++      wm_use_netwm_fullscreen(strcasecmp("true", config_get(config, "general", "useNETWMFullscreen", "true")) == 0);
++      wm_ignore_skip_taskbar(strcasecmp("true", config_get(config, "general", "ignoreSkipTaskbar", "false")) == 0);
+--- skippy-0.5.1rc1.orig/debian/copyright
++++ skippy-0.5.1rc1/debian/copyright
+@@ -0,0 +1,28 @@
++This package was debianized by Niv ALTIVANIK <xaiki@cxhome.ath.cx> on
++Mon,  6 Dec 2004 02:50:40 +0100.
++
++It was downloaded from http://thegraveyard.org/skippy.php
++
++Copyright: 2004 2005 Hyriand, TheGraveyard.org Project.
++
++Upstream Author: Hyriand hyriand@thegraveyard.org
++
++License:
++
++   This package 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; version 2 dated June, 1991.
++
++   This package 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 package; if not, write to the Free Software
++   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
++   02111-1307, USA.
++
++  On Debian systems, the complete text of the GNU General Public
++  License can be found in the file `/usr/share/common-licenses/GPL'.
++
+--- skippy-0.5.1rc1.orig/debian/skippyrc.1
++++ skippy-0.5.1rc1/debian/skippyrc.1
+@@ -0,0 +1,101 @@
++.\"                                      Hey, EMACS: -*- nroff -*-
++.\" 
++.\"   This manpage 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; version 2 dated June, 1991.
++.\"
++.\"   This package 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 package; if not, write to the Free Software
++.\"   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
++.\"   02111-1307, USA.
++.\"
++.\"  On Debian systems, the complete text of the GNU General Public
++.\"  License can be found in the file `/usr/share/common-licenses/GPL'.
++.\"
++.\" First parameter, NAME, should be all caps
++.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
++.\" other parameters are allowed: see man(7), man(1)
++.TH SKIPPYRC 5 "December  6, 2004"
++.\" Please adjust this date whenever revising the manpage.
++.\"
++.\" Some roff macros, for reference:
++.\" .nh        disable hyphenation
++.\" .hy        enable hyphenation
++.\" .ad l      left justify
++.\" .ad b      justify to both left and right margins
++.\" .nf        disable filling
++.\" .fi        enable filling
++.\" .br        insert line break
++.\" .sp <n>    insert n+1 empty lines
++.\" for manpage-specific macros, see man(7)
++.SH NAME
++skippyrc \- Configuration file for skippy, a Full Screen Task Switcher for X11
++.SH DESCRIPTION
++.\" TeX users may be more comfortable with the \fB<whatever>\fP and
++.\" \fI<whatever>\fP escape sequences to invode bold face and italics, 
++.\" respectively.
++\fBskippy\fP is a program that allows you to manage your windows in an
++efficient way. It is highly inspired by Apple Expose.
++
++\fB~/.skippyrc\fP is its configuration file.
++
++.SH FORMAT
++for each SECTION (see below), you can define the following variables:
++
++.BR keysym 
++ can be anything XStringToKeysym can handle
++ (like F11, KP_Enter or implementation specific keysyms)
++
++.BR colors 
++ can be anything XAllocNamedColor can handle
++ (like "black" or "#000000")
++
++.BR distance 
++ a relative number, and is scaled according to the scale
++ factor applied to windows
++
++.BR fonts 
++ Xft font descriptions
++
++.BR booleans 
++ "true" or anything but "true" (-> false)
++
++.BR opacity 
++ integer in the range of 0-255
++
++.BR brightness 
++ floating point number (with 0.0 as neutral)
++
++.SH SECTIONS
++.BR [general]
++.BR [xinerama]
++.BR [normal]
++.BR [highlight]
++.BR [tooltip]
++
++.SH NOTE
++In Debian you can find a copy of the default skippyrc in
++/usr/share/doc/skippy/examples
++If no ~/.skippyrc exist, skippy will use /etc/X11/skippy/skippyrc
++
++If you are using the X.org X server, there is a version of skippy,
++called skippy-xd that makes use of the new XDamage, XComposite and
++XFixes extensions. I suggest you use the xd version instead.
++
++.SH FILES
++/etc/X11/skippy/skippyrc
++~/.skippyrc
++
++.SH SEE ALSO
++.BR skippy (1),
++.BR skippy-xd (1)
++.SH AUTHOR
++skippy was written by Hyriand <hyriand@thegraveyard.org>.
++.PP
++This manual page was written by Niv ALTIVANIK <xaiki@cxhome.ath.cx>,
++for the Debian project (but may be used by others).
+--- skippy-0.5.1rc1.orig/debian/examples/99skippy
++++ skippy-0.5.1rc1/debian/examples/99skippy
+@@ -0,0 +1,5 @@
++#!/bin/sh
++# Add this file to /etc/X11/XSession.d/ if you wish the program to be 
++# started at login time for all the users on your system.
++
++/usr/bin/skippy & 
+--- skippy-0.5.1rc1.orig/debian/examples/skippyrc-default
++++ skippy-0.5.1rc1/debian/examples/skippyrc-default
+@@ -0,0 +1,3 @@
++# This is the Default Debian skippy configuration, it is copied to 
++# /etc/X11/skippy/skippyrc. 
++# Copy this to ~/.skippyrc and edit it to fit your needs
diff --git a/skippy-xd.rc-default b/skippy-xd.rc-default
new file mode 100644 (file)
index 0000000..45b88ab
--- /dev/null
@@ -0,0 +1,67 @@
+# Copy this to ~/.skippy-xd.rc and edit it to your liking
+#
+# Notes:
+#
+# - keysym can be anything XStringToKeysym can handle
+#   (like F11, KP_Enter or implementation specific keysyms)
+#
+# - colors can be anything XAllocNamedColor can handle
+#   (like "black" or "#000000")
+#
+# - distance is a relative number, and is scaled according to the scale
+#   factor applied to windows
+#
+# - fonts are Xft font descriptions
+#
+# - booleans are "true" or anything but "true" (-> false)
+#
+# - opacity is an integer in the range of 0-255
+#
+# - brighness is a floating point number (with 0.0 as neutral)
+#
+# - if the update frequency is a negative value, the mini-windows will only
+#   be updated when they're explicitly rendered (like, when they gain or
+#   lose focus).
+#
+# - the 'shadowText' option can be a color or 'none', in which case the
+#   drop-shadow effect is disabled
+#
+
+[general]
+keysym = F11
+distance = 50
+useNetWMFullscreen = true
+ignoreSkipTaskbar = true
+updateFreq = 10.0
+lazyTrans = false
+
+[maemo]
+#imgLandscape = /usr/share/skippy-xd/skippy-bg-l.jpg
+#imgPortrait = /usr/share/skippy-xd/skippy-bg-p.jpg
+#f7prog = "/usr/bin/osso-xterm &"
+#f6prog = "/usr/bin/dbus-send --system --type=method_call --dest=com.nokia.mce /com/nokia/mce/request com.nokia.mce.request.req_tklock_mode_change string:locked &"
+###  Fast=0, Good=1, Best=2, Nearest=3, Bilinear=4, Convolution=5
+gQuality = 0
+
+
+[xinerama]
+showAll = false
+
+[normal]
+tint = black
+tintOpacity = 0
+opacity = 200
+
+[highlight]
+tint = #101020
+tintOpacity = 64
+opacity = 255
+
+[tooltip]
+show = true
+border = #e0e0e0
+background = #404040
+opacity = 128
+text = #e0e0e0
+textShadow = black
+font = fixed-11:weight=bold
diff --git a/skippy.c b/skippy.c
new file mode 100644 (file)
index 0000000..03581fe
--- /dev/null
+++ b/skippy.c
@@ -0,0 +1,463 @@
+/* Skippy - Seduces Kids Into Perversion
+ *
+ * Copyright (C) 2004 Hyriand <hyriand@thegraveyard.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "skippy.h"
+
+const char *f7prog;
+const char *f6prog;
+static int DIE_NOW = 0;
+
+static int
+time_in_millis (void)
+{
+    struct timeval  tp;
+
+    gettimeofday (&tp, 0);
+    return(tp.tv_sec * 1000) + (tp.tv_usec / 1000);
+}
+
+static dlist *
+update_clients(MainWin *mw, dlist *clients, Bool *touched)
+{
+       dlist *stack, *iter;
+       
+       stack = dlist_first(wm_get_stack(mw->dpy));
+       iter = clients = dlist_first(clients);
+       
+       if(touched)
+               *touched = False;
+       
+       /* Terminate clients that are no longer managed */
+       while(iter)
+       {
+               ClientWin *cw = (ClientWin *)iter->data;
+               if(! dlist_find_data(stack, (void *)cw->client.window))
+               {
+                       dlist *tmp = iter->next;
+                       clientwin_destroy((ClientWin *)iter->data, True);
+                       clients = dlist_remove(iter);
+                       iter = tmp;
+                       if(touched)
+                               *touched = True;
+                       continue;
+               }
+               clientwin_update(cw);
+               iter = iter->next;
+       }
+       
+       /* Add new clients */
+       for(iter = dlist_first(stack); iter; iter = iter->next)
+       {
+               ClientWin *cw = (ClientWin*)dlist_find(clients, clientwin_cmp_func, iter->data);
+               if(! cw && (Window)iter->data != mw->window)
+               {
+                       cw = clientwin_create(mw, (Window)iter->data);
+                       if(! cw)
+                               continue;
+                       clients = dlist_add(clients, cw);
+                       clientwin_update(cw);
+                       if(touched)
+                               *touched = True;
+               }
+       }
+       
+       dlist_free(stack);
+       
+       return clients;
+}
+
+static dlist *
+do_layout(MainWin *mw, dlist *clients, Window focus, Window leader)
+{
+       CARD32 desktop = wm_get_current_desktop(mw->dpy);
+       unsigned int width, height;
+       float factor;
+       int xoff, yoff;
+       dlist *iter, *tmp;
+       
+       /* Update the client table, pick the ones we want and sort them */
+       clients = update_clients(mw, clients, 0);
+       
+       if(mw->cod)
+               dlist_free(mw->cod);
+       
+       tmp = dlist_first(dlist_find_all(clients, (dlist_match_func)clientwin_validate_func, &desktop));
+       if(leader != None)
+       {
+               mw->cod = dlist_first(dlist_find_all(tmp, clientwin_check_group_leader_func, (void*)&leader));
+               dlist_free(tmp);
+       } else
+               mw->cod = tmp;
+       
+       if(! mw->cod)
+               return clients;
+       
+       dlist_sort(mw->cod, clientwin_sort_func, 0);
+       
+       /* Move the mini windows around */
+       layout_run(mw, mw->cod, &width, &height);
+       factor = (float)(mw->width - 100) / width;
+       if(factor * height > mw->height - 100)
+               factor = (float)(mw->height - 100) / height;
+       
+       xoff = (mw->width - (float)width * factor) / 2;
+       yoff = (mw->height - (float)height * factor) / 2;
+       mainwin_transform(mw, factor);
+       for(iter = mw->cod; iter; iter = iter->next)
+               clientwin_move((ClientWin*)iter->data, factor, xoff, yoff);
+       
+       /* Get the currently focused window and select which mini-window to focus */
+       iter = dlist_find(mw->cod, clientwin_cmp_func, (void *)focus);
+       if(! iter)
+               iter = mw->cod;
+       mw->focus = (ClientWin*)iter->data;
+       mw->focus->focused = 1;
+       
+       /* Map the client windows */
+       for(iter = mw->cod; iter; iter = iter->next)
+               clientwin_map((ClientWin*)iter->data);
+       XWarpPointer(mw->dpy, None, mw->focus->mini.window, 0, 0, 0, 0, mw->focus->mini.width / 2, mw->focus->mini.height / 2);
+       
+       return clients;
+}
+
+static dlist *
+skippy_run(MainWin *mw, dlist *clients, Window focus, Window leader, Bool all_xin)
+{
+       XEvent ev;
+       int die = 0;
+       Bool refocus = False, refroot = False ;
+       int last_rendered;
+        XWindowAttributes rootattr;
+       
+       /* Update the main window's geometry (and Xinerama info if applicable) */
+       mainwin_update(mw);
+#ifdef XINERAMA
+       if(all_xin)
+               mw->xin_active = 0;
+#else /* ! XINERAMA */
+       if(all_xin);
+#endif /* XINERAMA */
+       
+       /* Map the main window and run our event loop */
+       if(mw->lazy_trans)
+       {
+               mainwin_map(mw);
+               XFlush(mw->dpy);
+       }
+
+        XGetWindowAttributes(mw->dpy, mw->root, &rootattr);
+       if(mw->width != rootattr.width)
+        {
+       /*printf("rotated\n");*/
+        mw->width = rootattr.width;
+        mw->height = rootattr.height;
+       mainwin_update_background(mw);
+       }
+
+        clients = do_layout(mw, clients, focus, leader);
+
+
+       if(! mw->cod)
+               return clients;
+
+       
+       /* Map the main window and run our event loop */
+       if(! mw->lazy_trans)
+               mainwin_map(mw);
+       
+       last_rendered = time_in_millis();
+       while(! die) {
+           int i, j, now, timeout, mh;
+           int move_x = -1, move_y = -1;
+           struct pollfd r_fd;
+           
+           XFlush(mw->dpy);
+           
+           r_fd.fd = ConnectionNumber(mw->dpy);
+           r_fd.events = POLLIN;
+           if(mw->poll_time > 0)
+               timeout = MAX(0, mw->poll_time + last_rendered - time_in_millis());
+           else
+               timeout = -1;
+           i = poll(&r_fd, 1, timeout);
+           
+           now = time_in_millis();
+           if(now >= last_rendered + mw->poll_time)
+           {
+               REDUCE(if( ((ClientWin*)iter->data)->damaged ) clientwin_repair(iter->data), mw->cod);
+               last_rendered = now;
+           }
+           
+           i = XPending(mw->dpy);
+           for(j = 0; j < i; ++j)
+           {
+               XNextEvent(mw->dpy, &ev);
+               if (ev.type == MotionNotify)
+               {
+                       move_x = ev.xmotion.x_root;
+                       move_y = ev.xmotion.y_root;
+               }
+               else if(ev.type == DestroyNotify || ev.type == UnmapNotify) {
+                       dlist *iter = dlist_find(clients, clientwin_cmp_func, (void *)ev.xany.window);
+                       if(iter)
+                       {
+                               ClientWin *cw = (ClientWin *)iter->data;
+                               clients = dlist_first(dlist_remove(iter));
+                               iter = dlist_find(mw->cod, clientwin_cmp_func, (void *)ev.xany.window);
+                               if(iter)
+                                       mw->cod = dlist_first(dlist_remove(iter));
+                               clientwin_destroy(cw, True);
+                               if(! mw->cod)
+                               {
+                                       die = 1;
+                                       break;
+                               }
+                       }
+               }
+               else if (mw->poll_time >= 0 && ev.type == mw->damage_event_base + XDamageNotify)
+               {
+                       XDamageNotifyEvent *d_ev = (XDamageNotifyEvent *)&ev;
+                       dlist *iter = dlist_find(mw->cod, clientwin_cmp_func, (void *)d_ev->drawable);
+                       if(iter)
+                       {
+                               if(mw->poll_time == 0)
+                                       clientwin_repair((ClientWin *)iter->data);
+                               else
+                                       ((ClientWin *)iter->data)->damaged = True;
+                       }
+                               
+               }
+               else if(ev.type == KeyRelease && ev.xkey.keycode == mw->key_escape)
+                {
+                        refocus = True;
+                        die = 1;
+                        break;
+                }
+                else if(ev.type == KeyRelease && ev.xkey.keycode == mw->key_f6)
+                {
+                        system(f6prog);
+                        refocus = True;
+                        die = 1;
+                        break;
+                }
+                else if(ev.type == KeyRelease && ev.xkey.keycode == mw->key_f7)
+               {
+                       system(f7prog);
+                        refocus = True;
+                        die = 1;
+                        break;
+               }
+               else if(ev.type == KeyRelease && ev.xkey.keycode == mw->key_q)
+               {
+                       DIE_NOW = 1;
+                       die = 1;
+                       break;
+               }
+               else if(ev.type == KeyPress && ev.xkey.keycode == mw->key_f5 )
+               {
+                       refroot = True;
+                       die = 1;
+                       break;
+               }
+               else if(ev.xany.window == mw->window)
+                       mh = mainwin_handle(mw, &ev);
+                       if(mh == 0 || mh == 1 || mh == 2){
+                               die = mh;
+                       }else if(mh == 3){
+                               die = 1;
+                               refroot = True;
+                       }               
+/*             else if(ev.type == PropertyNotify)
+                {
+                       if(ev.xproperty.atom == ESETROOT_PMAP_ID || ev.xproperty.atom == _XROOTPMAP_ID)
+                       {
+                               mainwin_update_background(mw);
+                               REDUCE(clientwin_render((ClientWin *)iter->data), mw->cod);
+                       }
+
+               }*/
+               else if(mw->tooltip && ev.xany.window == mw->tooltip->window)
+                       tooltip_handle(mw->tooltip, &ev);
+               else
+               {
+                       dlist *iter;
+                       for(iter = mw->cod; iter; iter = iter->next)
+                       {
+                               ClientWin *cw = (ClientWin *)iter->data;
+                               if(cw->mini.window == ev.xany.window)
+                               {
+                                       die = clientwin_handle(cw, &ev);
+                                       if(die)
+                                               break;
+                               }
+                       }
+                       if(die)
+                               break;
+               }
+           
+           }
+               
+           if(mw->tooltip && move_x != -1)
+               tooltip_move(mw->tooltip, move_x + 20, move_y + 20);
+           
+       }
+
+       /* Unmap the main window and clean up */
+       mainwin_unmap(mw);
+       XFlush(mw->dpy);
+       
+       REDUCE(clientwin_unmap((ClientWin*)iter->data), mw->cod);
+       dlist_free(mw->cod);
+       mw->cod = 0;
+       
+       if(die == 2)
+               DIE_NOW = 1;
+       
+       if(refocus)
+               XSetInputFocus(mw->dpy, focus, RevertToPointerRoot, CurrentTime);
+       
+       if(refroot)
+               client_msg(mw->dpy, DefaultRootWindow(mw->dpy), "_NET_SHOWING_DESKTOP",
+                       1, 0, 0, 0, 0);
+
+       
+       return clients;
+}
+
+int
+main(void)
+{
+       dlist *clients = 0, *config = 0;
+       Display *dpy = XOpenDisplay(NULL);
+       MainWin *mw;
+       KeyCode keycode;
+       KeySym keysym;
+       const char *tmp, *homedir;
+       char cfgpath[8192];
+       Bool invertShift = False;
+       const char *img_l_file, *img_p_file;
+        Window focused = None;
+        int revert_to;
+       
+       if(! dpy) {
+               fprintf(stderr, "FATAL: Couldn't connect to display.\n");
+               return -1;
+       }
+       
+       wm_get_atoms(dpy);
+       
+       if(! wm_check(dpy)) {
+               fprintf(stderr, "FATAL: WM not NETWM or GNOME WM Spec compliant.\n");
+               return -1;
+       }
+       
+       homedir = getenv("HOME");
+       if(homedir) {
+               snprintf(cfgpath, 8191, "%s/%s", homedir, ".skippy-xd.rc");
+               config = config_load(cfgpath);
+       }
+       else
+               fprintf(stderr, "WARNING: $HOME not set, not loading config.\n");
+       
+       wm_use_netwm_fullscreen(strcasecmp("true", config_get(config, "general", "useNETWMFullscreen", "true")) == 0);
+       wm_ignore_skip_taskbar(strcasecmp("true", config_get(config, "general", "ignoreSkipTaskbar", "false")) == 0);
+
+        XGetInputFocus(dpy, &focused, &revert_to);
+       
+       mw = mainwin_create(dpy, config);
+       if(! mw)
+       {
+               fprintf(stderr, "FATAL: Couldn't create main window.\n");
+               config_free(config);
+               XCloseDisplay(mw->dpy);
+               return -1;
+       }
+
+       client_msg(dpy, focused, "_NET_ACTIVE_WINDOW", 0, 0, 0, 0, 0);
+       XSetInputFocus(dpy, focused, RevertToParent, CurrentTime);
+
+
+        f6prog = config_get(config, "maemo", "f6prog", "/usr/bin/dbus-send --system --type=method_call --dest=com.nokia.mce /com/nokia/mce/request com.nokia.mce.request.req_tklock_mode_change string:locked &");     
+       f7prog = config_get(config, "maemo", "f7prog", "/usr/bin/osso-xterm &");
+       mw->gquality = atoi(config_get(config, "maemo", "gQuality", "0"));
+       printf("Filter: %d\n", mw->gquality);
+
+        img_l_file = config_get(config, "maemo", "imgLandscape", "/usr/share/skippy-xd/skippy-bg-l.jpg");
+       printf("ImgL: %s\n",  img_l_file);
+       img_p_file = config_get(config, "maemo", "imgPortrait", "/usr/share/skippy-xd/skippy-bg-p.jpg");
+       printf("ImgP: %s\n",  img_p_file);
+
+       mw->img_l = imlib_load_image(img_l_file);
+       mw->img_p = imlib_load_image(img_p_file);
+
+       invertShift = strcasecmp("true", config_get(config, "xinerama", "showAll", "false")) == 0;
+       
+       tmp = config_get(config, "general", "keysym", "F11");
+       keysym = XStringToKeysym(tmp);
+       if(keysym == NoSymbol)
+       {
+               fprintf(stderr, "FATAL: Couldn't look up keysym for '%s', bailing out.\n", tmp);
+               config_free(config);
+               XCloseDisplay(mw->dpy);
+               return -1;
+       }
+       
+/*     XSelectInput(mw->dpy, mw->root, ButtonReleaseMask | ButtonPressMask | PropertyChangeMask);*/
+
+
+/*     XSelectInput(mw->dpy, mw->root, PropertyChangeMask);*/
+        XSelectInput(mw->dpy, mw->window, ButtonReleaseMask | ButtonPressMask );
+       
+       keycode = XKeysymToKeycode(mw->dpy, keysym);
+       XGrabKey(mw->dpy, keycode, AnyModifier, mw->root, False, GrabModeAsync, GrabModeAsync);
+       while(! DIE_NOW)
+       {
+               XEvent ev;
+               XNextEvent(mw->dpy, &ev);
+               /*if(ev.type == PropertyNotify && (ev.xproperty.atom == ESETROOT_PMAP_ID || ev.xproperty.atom == _XROOTPMAP_ID))
+                       mainwin_update_background(mw);
+               else */if(ev.type == KeyPress && ev.xkey.keycode == keycode)
+               {
+                       Window leader = None, focused = wm_get_focused(mw->dpy);
+                       Bool shifted = (ev.xkey.state & ShiftMask) ? ! invertShift : invertShift;
+               
+                       if(ev.xkey.state & Mod1Mask)
+                       {
+                               if(focused != None)
+                                       leader = wm_get_group_leader(mw->dpy, focused);
+                               if(! leader)
+                                       continue;
+                       }
+                       clients = skippy_run(mw, clients, focused, leader, shifted);
+               }
+       }
+       
+       dlist_free_with_func(clients, (dlist_free_func)clientwin_destroy);
+       
+       XFlush(mw->dpy);
+       
+       mainwin_destroy(mw);
+       
+       XSync(dpy, True);
+       XCloseDisplay(dpy);
+       config_free(config);
+       
+       return 0;
+}
diff --git a/skippy.h b/skippy.h
new file mode 100644 (file)
index 0000000..ddcad1d
--- /dev/null
+++ b/skippy.h
@@ -0,0 +1,72 @@
+/* Skippy - Seduces Kids Into Perversion
+ *
+ * Copyright (C) 2004 Hyriand <hyriand@thegraveyard.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef SKIPPY_H
+#define SKIPPY_H
+
+#include <X11/Xlib.h>
+#include <X11/Xmd.h>
+#include <X11/Xatom.h>
+#include <X11/Xmu/WinUtil.h>
+#include <X11/keysym.h>
+#include <X11/Xft/Xft.h>
+
+#include <X11/extensions/Xrender.h>
+#include <X11/extensions/Xcomposite.h>
+#include <X11/extensions/Xdamage.h>
+#include <X11/extensions/Xfixes.h>
+
+#ifdef XINERAMA
+# include <X11/extensions/Xinerama.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <unistd.h>
+#include <time.h>
+#include <regex.h>
+#include <string.h>
+
+#include <Imlib2.h>
+
+
+#define MAX(a,b) ((a > b) ? a : b)
+#define MIN(a,b) ((a > b) ? b : a)
+#define REDUCE(statement, l) \
+{ \
+       dlist *iter = dlist_first(l); \
+       for(; iter; iter = iter->next) \
+               statement; \
+}
+
+
+#include "dlist.h"
+#include "wm.h"
+#include "clientwin.h"
+#include "mainwin.h"
+#include "layout.h"
+#include "focus.h"
+#include "config.h"
+#include "tooltip.h"
+
+#endif /* SKIPPY_H */
diff --git a/tooltip.c b/tooltip.c
new file mode 100644 (file)
index 0000000..8fc29c6
--- /dev/null
+++ b/tooltip.c
@@ -0,0 +1,220 @@
+/* Skippy - Seduces Kids Into Perversion
+ *
+ * Copyright (C) 2004 Hyriand <hyriand@thegraveyard.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "skippy.h"
+
+void
+tooltip_destroy(Tooltip *tt)
+{
+       if(tt->text)
+               free(tt->text);
+       if(tt->font)
+               XftFontClose(tt->mainwin->dpy, tt->font);
+       if(tt->draw)
+               XftDrawDestroy(tt->draw);
+       if(tt->color.pixel != None)
+               XftColorFree(tt->mainwin->dpy,
+                            tt->mainwin->visual,
+                            tt->mainwin->colormap,
+                            &tt->color);
+       if(tt->background.pixel != None)
+               XftColorFree(tt->mainwin->dpy,
+                            tt->mainwin->visual,
+                            tt->mainwin->colormap,
+                            &tt->background);
+       if(tt->border.pixel != None)
+               XftColorFree(tt->mainwin->dpy,
+                            tt->mainwin->visual,
+                            tt->mainwin->colormap,
+                            &tt->border);
+       if(tt->shadow.pixel != None)
+               XftColorFree(tt->mainwin->dpy,
+                            tt->mainwin->visual,
+                            tt->mainwin->colormap,
+                            &tt->shadow);
+       if(tt->window != None)
+               XDestroyWindow(tt->mainwin->dpy, tt->window);
+       
+       free(tt);
+}
+
+Tooltip *
+tooltip_create(MainWin *mw, dlist *config)
+{
+       Tooltip *tt;
+       XSetWindowAttributes attr;
+       const char *tmp;
+       long int tmp_l;
+       
+       tt = (Tooltip *)malloc(sizeof(Tooltip));
+       if(! tt)
+               return 0;
+       
+       tt->mainwin = mw;
+       tt->window = None;
+       tt->font = 0;
+       tt->draw = 0;
+       tt->text = 0;
+       tt->color.pixel = tt->background.pixel = tt->border.pixel = tt->shadow.pixel = None;
+       
+       attr.override_redirect = True;
+       attr.border_pixel = None;
+       attr.background_pixel = None;
+       attr.event_mask = ExposureMask;
+       attr.colormap = mw->colormap;
+       
+       tt->window = XCreateWindow(mw->dpy, mw->root,
+                                  0, 0, 1, 1, 0,
+                                  mw->depth, InputOutput, mw->visual,
+                                  CWBorderPixel|CWBackPixel|CWOverrideRedirect|CWEventMask|CWColormap,
+                                  &attr);
+       if(tt->window == None)
+       {
+               fprintf(stderr, "WARNING: Couldn't create tooltip window.\n");
+               tooltip_destroy(tt);
+               return 0;
+       }
+       
+       tmp = config_get(config, "tooltip", "border", "#e0e0e0");
+       if(! XftColorAllocName(mw->dpy, mw->visual, mw->colormap, tmp, &tt->border))
+       {
+               fprintf(stderr, "WARNING: Invalid color '%s'.\n", tmp);
+               tooltip_destroy(tt);
+               return 0;
+       }
+       
+       tmp = config_get(config, "tooltip", "background", "#404040");
+       if(! XftColorAllocName(mw->dpy, mw->visual, mw->colormap, tmp, &tt->background))
+       {
+               fprintf(stderr, "WARNING: Invalid color '%s'.\n", tmp);
+               tooltip_destroy(tt);
+               return 0;
+       }
+       
+       tmp = config_get(config, "tooltip", "opacity", "128");
+       tmp_l = MIN(MAX(0, strtol(tmp, 0, 0) * 256), 65535);
+       tt->background.color.alpha = tmp_l;
+       tt->border.color.alpha = tmp_l;
+       
+       tmp = config_get(config, "tooltip", "text", "#e0e0e0");
+       if(! XftColorAllocName(mw->dpy, mw->visual, mw->colormap, tmp, &tt->color))
+       {
+               fprintf(stderr, "WARNING: Couldn't allocate color '%s'.\n", tmp);
+               tooltip_destroy(tt);
+               return 0;
+       }
+       
+       tmp = config_get(config, "tooltip", "textShadow", "black");
+       if(strcasecmp(tmp, "none") != 0)
+       {
+               if(! XftColorAllocName(mw->dpy, mw->visual, mw->colormap, tmp, &tt->shadow))
+               {
+                       fprintf(stderr, "WARNING: Couldn't allocate color '%s'.\n", tmp);
+                       tooltip_destroy(tt);
+                       return 0;
+               }
+       }
+       
+       tt->draw = XftDrawCreate(mw->dpy, tt->window, mw->visual, mw->colormap);
+       if(! tt->draw)
+       {
+               fprintf(stderr, "WARNING: Couldn't create Xft draw surface.\n");
+               tooltip_destroy(tt);
+               return 0;
+       }
+       
+       tt->font = XftFontOpenName(mw->dpy, mw->screen, config_get(config, "tooltip", "font", "fixed-11:weight=bold"));
+       if(! tt->font)
+       {
+               fprintf(stderr, "WARNING: Couldn't open Xft font.\n");
+               tooltip_destroy(tt);
+               return 0;
+       }
+       
+       tt->font_height = tt->font->ascent + tt->font->descent;
+       
+       return tt;
+}
+
+void
+tooltip_map(Tooltip *tt, int x, int y, const FcChar8 *text, int len)
+{
+       XUnmapWindow(tt->mainwin->dpy, tt->window);
+       
+       XftTextExtents8(tt->mainwin->dpy, tt->font, text, len, &tt->extents);
+       
+       tt->width = tt->extents.width + 8;
+       tt->height = tt->font_height + 5 + (tt->shadow.pixel ? 2 : 0);
+       XResizeWindow(tt->mainwin->dpy, tt->window, tt->width, tt->height);
+       tooltip_move(tt, x, y);
+       
+       if(tt->text)
+               free(tt->text);
+       
+       tt->text = (FcChar8 *)malloc(len);
+       memcpy(tt->text, text, len);
+       
+       tt->text_len = len;
+       
+       XMapWindow(tt->mainwin->dpy, tt->window);
+       XRaiseWindow(tt->mainwin->dpy, tt->window);
+}
+
+void
+tooltip_move(Tooltip *tt, int x, int y)
+{
+       if(x + tt->extents.width + 9 > tt->mainwin->x + tt->mainwin->width)
+               x = tt->mainwin->x + tt->mainwin->width - tt->extents.width - 9;
+       x = MAX(0, x);
+       
+       if(y + tt->extents.height + 8 > tt->mainwin->y + tt->mainwin->height)
+               y = tt->mainwin->height + tt->mainwin->y - tt->extents.height - 8;
+       y = MAX(0, y);
+       
+       XMoveWindow(tt->mainwin->dpy, tt->window, x, y);
+}
+
+void
+tooltip_unmap(Tooltip *tt)
+{
+       XUnmapWindow(tt->mainwin->dpy, tt->window);
+       if(tt->text)
+               free(tt->text);
+       tt->text = 0;
+       tt->text_len = 0;
+}
+
+void
+tooltip_handle(Tooltip *tt, XEvent *ev)
+{
+       if(! tt->text)
+               return;
+       
+       if(ev->type == Expose && ev->xexpose.count == 0)
+       {
+               XftDrawRect(tt->draw, &tt->border, 0, 0, tt->width, 1);
+               XftDrawRect(tt->draw, &tt->border, 0, 1, 1, tt->height - 2);
+               XftDrawRect(tt->draw, &tt->border, 0, tt->height - 1, tt->width, 1);
+               XftDrawRect(tt->draw, &tt->border, tt->width - 1, 1, 1, tt->height - 2);
+               XftDrawRect(tt->draw, &tt->background, 1, 1, tt->width - 2, tt->height - 2);
+               if(tt->shadow.pixel)
+                       XftDrawString8(tt->draw, &tt->shadow, tt->font, 6, 3 + tt->extents.y + (tt->font_height - tt->extents.y) / 2, tt->text, tt->text_len);
+               XftDrawString8(tt->draw, &tt->color, tt->font, 4, 1 + tt->extents.y + (tt->font_height - tt->extents.y) / 2, tt->text, tt->text_len);
+       }
+}
diff --git a/tooltip.h b/tooltip.h
new file mode 100644 (file)
index 0000000..e6d08b0
--- /dev/null
+++ b/tooltip.h
@@ -0,0 +1,47 @@
+/* Skippy - Seduces Kids Into Perversion
+ *
+ * Copyright (C) 2004 Hyriand <hyriand@thegraveyard.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef SKIPPY_TOOLTIP_H
+#define SKIPPY_TOOLTIP_H
+
+struct _Tooltip {
+       MainWin *mainwin;
+       
+       unsigned int width, height;
+       
+       Window window;
+       XftFont *font;
+       XftDraw *draw;
+       XftColor color, background, border, shadow;
+       XGlyphInfo extents;
+       int font_height;
+       
+       FcChar8 *text;
+       int text_len;
+};
+typedef struct _Tooltip Tooltip;
+
+Tooltip *tooltip_create(MainWin *, dlist *);
+void tooltip_destroy(Tooltip *);
+void tooltip_map(Tooltip *, int, int, const FcChar8 *, int);
+void tooltip_unmap(Tooltip *);
+void tooltip_handle(Tooltip *, XEvent *);
+void tooltip_move(Tooltip *, int, int);
+
+#endif /* SKIPPY_TOOLTIP_H */
diff --git a/wm.c b/wm.c
new file mode 100644 (file)
index 0000000..4b8bb31
--- /dev/null
+++ b/wm.c
@@ -0,0 +1,602 @@
+/* Skippy - Seduces Kids Into Perversion
+ *
+ * Copyright (C) 2004 Hyriand <hyriand@thegraveyard.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "skippy.h"
+
+#define WM_PERSONALITY_NETWM 0
+#define WM_PERSONALITY_GNOME 1
+
+/* From WindowMaker's gnome.c */
+#define WIN_HINTS_SKIP_FOCUS      (1<<0) /*"alt-tab" skips this win*/
+#define WIN_HINTS_SKIP_WINLIST    (1<<1) /*do not show in window list*/
+#define WIN_HINTS_SKIP_TASKBAR    (1<<2) /*do not show on taskbar*/
+#define WIN_HINTS_GROUP_TRANSIENT (1<<3) /*Reserved - definition is unclear*/
+#define WIN_HINTS_FOCUS_ON_CLICK  (1<<4) /*app only accepts focus if clicked*/
+#define WIN_HINTS_DO_NOT_COVER    (1<<5) /* attempt to not cover this window */
+
+
+#define WIN_STATE_STICKY          (1<<0) /*everyone knows sticky*/
+#define WIN_STATE_MINIMIZED       (1<<1) /*Reserved - definition is unclear*/
+#define WIN_STATE_MAXIMIZED_VERT  (1<<2) /*window in maximized V state*/
+#define WIN_STATE_MAXIMIZED_HORIZ (1<<3) /*window in maximized H state*/
+#define WIN_STATE_HIDDEN          (1<<4) /*not on taskbar but window visible*/
+#define WIN_STATE_SHADED          (1<<5) /*shaded (MacOS / Afterstep style)*/
+/* these are bogus states defined in "the spec" */
+#define WIN_STATE_HID_WORKSPACE   (1<<6) /*not on current desktop*/
+#define WIN_STATE_HID_TRANSIENT   (1<<7) /*owner of transient is hidden*/
+#define WIN_STATE_FIXED_POSITION  (1<<8) /*window is fixed in position even*/
+#define WIN_STATE_ARRANGE_IGNORE  (1<<9) /*ignore for auto arranging*/
+
+
+static int WM_PERSONALITY = WM_PERSONALITY_NETWM,
+           NETWM_HAS_FULLSCREEN = 0,
+           IGNORE_SKIP_TASKBAR = 0;
+
+void
+wm_get_atoms(Display *dpy)
+{
+       XA_WM_STATE = XInternAtom(dpy, "WM_STATE", 0);
+       WM_CLIENT_LEADER = XInternAtom(dpy, "WM_CLIENT_LEADER", 0);
+       XA_UTF8_STRING = XInternAtom(dpy, "UTF8_STRING", 0);
+       
+       _XROOTPMAP_ID = XInternAtom(dpy, "_XROOTPMAP_ID", 0);
+       ESETROOT_PMAP_ID = XInternAtom(dpy, "ESETROOT_PMAP_ID", 0);
+       
+       _NET_SUPPORTING_WM_CHECK = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", 0);
+       _NET_SUPPORTED = XInternAtom(dpy, "_NET_SUPPORTED", 0);
+       _NET_NUMBER_OF_DESKTOPS = XInternAtom(dpy, "_NET_NUMBER_OF_DESKTOPS", 0);
+       _NET_CLIENT_LIST = XInternAtom(dpy, "_NET_CLIENT_LIST", 0);
+       _NET_CLIENT_LIST_STACKING = XInternAtom(dpy, "_NET_CLIENT_LIST_STACKING", 0);
+       _NET_CURRENT_DESKTOP = XInternAtom(dpy, "_NET_CURRENT_DESKTOP", 0);
+       _NET_WM_DESKTOP = XInternAtom(dpy, "_NET_WM_DESKTOP", 0);
+       _NET_WM_STATE = XInternAtom(dpy, "_NET_WM_STATE", 0);
+       _NET_WM_STATE_HIDDEN = XInternAtom(dpy, "_NET_WM_STATE_HIDDEN", 0);
+       _NET_WM_STATE_SKIP_TASKBAR = XInternAtom(dpy, "_NET_WM_STATE_SKIP_TASKBAR", 0);
+       _NET_WM_STATE_SKIP_PAGER = XInternAtom(dpy, "_NET_WM_STATE_SKIP_PAGER", 0);
+       _NET_WM_STATE_FULLSCREEN = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", 0);
+       _NET_WM_STATE_SHADED = XInternAtom(dpy, "_NET_WM_STATE_SHADED", 0);
+       _NET_WM_STATE_ABOVE = XInternAtom(dpy, "_NET_WM_STATE_ABOVE", 0);
+       _NET_WM_STATE_STICKY = XInternAtom(dpy, "_NET_WM_STATE_STICKY", 0);
+       _NET_WM_WINDOW_TYPE = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", 0);
+       _NET_WM_WINDOW_TYPE_DESKTOP = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DESKTOP", 0);
+       _NET_WM_WINDOW_TYPE_DOCK = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DOCK", 0);
+       _NET_WM_VISIBLE_NAME = XInternAtom(dpy, "_NET_WM_VISIBLE_NAME", 0);
+       _NET_WM_NAME = XInternAtom(dpy, "_NET_WM_VISIBLE_NAME", 0);
+       
+       _WIN_SUPPORTING_WM_CHECK = XInternAtom(dpy, "_WIN_SUPPORTING_WM_CHECK", 0);
+       _WIN_WORKSPACE = XInternAtom(dpy, "_WIN_WORKSPACE", 0);
+       _WIN_WORKSPACE_COUNT = XInternAtom(dpy, "_WIN_WORKSPACE_COUNT", 0);
+       _WIN_PROTOCOLS = XInternAtom(dpy, "_WIN_PROTOCOLS", 0);
+       _WIN_CLIENT_LIST = XInternAtom(dpy, "_WIN_CLIENT_LIST", 0);
+       _WIN_STATE = XInternAtom(dpy, "_WIN_STATE", 0);
+       _WIN_HINTS = XInternAtom(dpy, "_WIN_HINTS", 0);
+}
+
+char
+wm_check_netwm(Display *dpy)
+{
+       Window wm_check;
+       unsigned char *data, *data2;
+       
+       int status, real_format;
+       Atom real_type;
+       unsigned long items_read, items_left, i;
+       
+       char req = 0;
+       
+       status = XGetWindowProperty(dpy, DefaultRootWindow(dpy), _NET_SUPPORTING_WM_CHECK,
+                         0L, 1L, False, XA_WINDOW, &real_type, &real_format,
+                         &items_read, &items_left, &data);
+       if(status != Success || ! items_read) {
+               if(status == Success)
+                       XFree(data);
+               return 0;
+       }
+       
+       wm_check = ((Window*)data)[0];
+       XFree(data);
+       
+       status = XGetWindowProperty(dpy, wm_check, _NET_SUPPORTING_WM_CHECK,
+                         0L, 1L, False, XA_WINDOW, &real_type, &real_format,
+                         &items_read, &items_left, &data);
+       
+       if(status != Success && ! items_read) {
+               if(status == Success)
+                       XFree(data);
+               return 0;
+       }
+       
+       if(wm_check != ((Window*)data)[0]) {
+               XFree(data);
+               return 0;
+       }
+       
+       XFree(data);
+       
+       status = XGetWindowProperty(dpy, DefaultRootWindow(dpy), _NET_SUPPORTED,
+                         0L, 8192L, False, XA_ATOM, &real_type, &real_format,
+                         &items_read, &items_left, &data2);
+       
+       if(status != Success || ! items_read) {
+               if(status == Success)
+                       XFree(data2);
+               return 0;
+       }
+       
+       for(i = 0; i < items_read; i++) {
+               if(((Atom*)data2)[i] == _NET_NUMBER_OF_DESKTOPS)
+                       req |= 1;
+               else if(((Atom*)data2)[i] == _NET_CURRENT_DESKTOP)
+                       req |= 2;
+               else if(((Atom*)data2)[i] == _NET_WM_STATE)
+                       req |= 4;
+               else if(((Atom*)data2)[i] == _NET_CLIENT_LIST)
+                       req |= 8;
+               else if(((Atom*)data2)[i] == _NET_CLIENT_LIST_STACKING)
+                       req |= 16;
+               else if(((Atom*)data2)[i] == _NET_WM_STATE_FULLSCREEN)
+                       NETWM_HAS_FULLSCREEN = 1;
+       }
+       XFree(data2);
+       if(req & 16) {
+               req |= 8;
+               _NET_CLIENT_LIST = _NET_CLIENT_LIST_STACKING;
+       } 
+       
+       return ((req & 15) == 15);
+}
+
+char
+wm_check_gnome(Display *dpy)
+{
+       Window wm_check;
+       unsigned char *data, *data2;
+       
+       int status, real_format;
+       Atom real_type;
+       unsigned long items_read, items_left, i;
+       
+       char req = 0;
+       
+       WM_PERSONALITY = WM_PERSONALITY_GNOME;
+       
+       status = XGetWindowProperty(dpy, DefaultRootWindow(dpy), _WIN_SUPPORTING_WM_CHECK,
+                         0L, 1L, False, XA_CARDINAL, &real_type, &real_format,
+                         &items_read, &items_left, &data);
+       if(status != Success || ! items_read) {
+               if(status == Success)
+                       XFree(data);
+               return 0;
+       }
+       
+       wm_check = ((Window*)data)[0];
+       XFree(data);
+       
+       status = XGetWindowProperty(dpy, wm_check, _WIN_SUPPORTING_WM_CHECK,
+                         0L, 1L, False, XA_CARDINAL, &real_type, &real_format,
+                         &items_read, &items_left, &data);
+       
+       if(status != Success && ! items_read) {
+               if(status == Success)
+                       XFree(data);
+               return 0;
+       }
+       
+       if(wm_check != ((Window*)data)[0]) {
+               XFree(data);
+               return 0;
+       }
+       
+       XFree(data);
+       
+       status = XGetWindowProperty(dpy, DefaultRootWindow(dpy), _WIN_PROTOCOLS,
+                         0L, 8192L, False, XA_ATOM, &real_type, &real_format,
+                         &items_read, &items_left, &data2);
+       
+       if(status != Success || ! items_read) {
+               if(status == Success)
+                       XFree(data2);
+               return 0;
+       }
+       
+       for(i = 0; i < items_read; i++) {
+               if(((Atom*)data2)[i] == _WIN_WORKSPACE)
+                       req |= 1;
+               else if(((Atom*)data2)[i] == _WIN_WORKSPACE_COUNT)
+                       req |= 2;
+               else if(((Atom*)data2)[i] == _WIN_STATE)
+                       req |= 4;
+               else if(((Atom*)data2)[i] == _WIN_CLIENT_LIST)
+                       req |= 8;
+       }
+       XFree(data2);
+       
+       return ((req & 15) == 15);
+}
+
+char
+wm_check(Display *dpy)
+{
+       return wm_check_netwm(dpy) || wm_check_gnome(dpy);
+}
+
+dlist *
+wm_get_stack(Display *dpy)
+{
+       dlist *l = 0;
+       unsigned char *data;
+       int status, real_format;
+       Atom real_type;
+       unsigned long items_read, items_left, i;
+       
+       if(WM_PERSONALITY == WM_PERSONALITY_NETWM)
+               status = XGetWindowProperty(dpy, DefaultRootWindow(dpy), _NET_CLIENT_LIST,
+                                 0L, 8192L, False, XA_WINDOW, &real_type, &real_format,
+                                 &items_read, &items_left, &data);
+       else
+               status = XGetWindowProperty(dpy, DefaultRootWindow(dpy), _WIN_CLIENT_LIST,
+                                 0L, 8192L, False, XA_CARDINAL, &real_type, &real_format,
+                                 &items_read, &items_left, &data);
+       
+       if(status != Success)
+               return 0;
+       
+       for(i = 0; i < items_read; i++)
+               l = dlist_add(l, (void*)((CARD32*)data)[i]);
+       
+       XFree(data);
+       
+       return l;
+}
+
+Pixmap
+wm_get_root_pmap(Display *dpy)
+{
+       Pixmap rootpmap = None;
+       unsigned char *data;
+       int status, real_format;
+       Atom real_type;
+       unsigned long items_read, items_left;
+       
+       status = XGetWindowProperty(dpy, DefaultRootWindow(dpy), _XROOTPMAP_ID,
+                         0L, 1L, False, XA_PIXMAP, &real_type, &real_format,
+                         &items_read, &items_left, &data);
+       if(status != Success) {
+               status = XGetWindowProperty(dpy, DefaultRootWindow(dpy), ESETROOT_PMAP_ID,
+                                 0L, 1L, False, XA_PIXMAP, &real_type, &real_format,
+                                 &items_read, &items_left, &data);
+               if(status != Success)
+                       return None;
+       }
+       
+       if(items_read)
+               rootpmap = ((Pixmap*)data)[0];
+       
+       XFree(data);
+       
+       return rootpmap;
+}
+
+CARD32
+wm_get_current_desktop(Display *dpy)
+{
+       CARD32 desktop = 1;
+       /*unsigned char *data;
+       int status, real_format;
+       Atom real_type;
+       unsigned long items_read, items_left;
+       
+        status = XGetWindowProperty(dpy, DefaultRootWindow(dpy),
+                         (WM_PERSONALITY == WM_PERSONALITY_NETWM) ?  _NET_CURRENT_DESKTOP : _WIN_WORKSPACE,
+                         0L, 1L, False, XA_CARDINAL, &real_type, &real_format,
+                         &items_read, &items_left, &data);
+       if(status != Success)
+               return 0;
+       if(items_read)
+               desktop = ((CARD32*)data)[0];
+       XFree(data);*/
+       
+       return desktop;
+}
+
+FcChar8 *
+wm_get_window_title(Display *dpy, Window window, int *length_return)
+{
+       unsigned char *data;
+       FcChar8 *ret = 0;
+       int status, real_format;
+       Atom real_type;
+       unsigned long items_read, items_left;
+       
+       *length_return = 0;
+       
+       status = XGetWindowProperty(dpy, window, _NET_WM_VISIBLE_NAME,
+                         0, 8192, False, XA_UTF8_STRING, &real_type, &real_format,
+                         &items_read, &items_left, &data);
+       if(status != Success || items_read == 0)
+       {
+               if(status == Success)
+                       XFree(data);
+               status = XGetWindowProperty(dpy, window, _NET_WM_NAME,
+                                 0, 8192, False, XA_UTF8_STRING, &real_type, &real_format,
+                                 &items_read, &items_left, &data);
+       }
+       if(status != Success || items_read == 0)
+       {
+               if(status == Success)
+                       XFree(data);
+               status = XGetWindowProperty(dpy, window, XA_WM_NAME,
+                                 0, 8192, False, XA_STRING, &real_type, &real_format,
+                                 &items_read, &items_left, &data);
+       }
+       if(status != Success)
+               return 0;
+       
+       if(items_read)
+       {
+               ret = (FcChar8 *)malloc(items_read);
+               memcpy(ret, data, items_read);
+               *length_return = items_read;
+       }
+       
+       XFree(data);
+       
+       return ret;
+}
+
+Window
+wm_get_group_leader(Display *dpy, Window window)
+{
+       unsigned char *data;
+       int status, real_format;
+       Atom real_type;
+       unsigned long items_read, items_left;
+       Window leader = None;
+       
+       status = XGetWindowProperty(dpy, window, WM_CLIENT_LEADER,
+                         0, 1, False, XA_WINDOW, &real_type, &real_format,
+                         &items_read, &items_left, &data);
+       
+       if(status != Success)
+       {
+               XWMHints *hints = XGetWMHints(dpy, window);
+               if(! hints)
+                       return None;
+               
+               if(hints->flags & WindowGroupHint)
+                       leader = hints->window_group;
+               
+               return leader;
+       }
+       
+       if(items_read)
+               leader = ((Window*)data)[0];
+       
+       XFree(data);
+       
+       return leader;
+}
+
+void
+wm_use_netwm_fullscreen(Bool b)
+{
+       NETWM_HAS_FULLSCREEN = b ? NETWM_HAS_FULLSCREEN : False;
+}
+
+void
+wm_ignore_skip_taskbar(Bool b)
+{
+       IGNORE_SKIP_TASKBAR = b;
+}
+
+void
+wm_set_fullscreen(Display *dpy, Window window, int x, int y, unsigned int width, unsigned int height)
+{
+       if(WM_PERSONALITY == WM_PERSONALITY_NETWM && NETWM_HAS_FULLSCREEN)
+       {
+               Atom props[6];
+               CARD32 desktop = (CARD32)0;
+               
+               props[0] = _NET_WM_STATE_FULLSCREEN;
+               props[1] = _NET_WM_STATE_SKIP_TASKBAR;
+               props[2] = _NET_WM_STATE_SKIP_PAGER;
+               props[3] = _NET_WM_STATE_ABOVE;
+               props[4] = _NET_WM_STATE_STICKY;
+               props[5] = 0;
+               XChangeProperty(dpy, window, _NET_WM_STATE, XA_ATOM, 32, PropModeReplace, (unsigned char*)props, 5);
+               XChangeProperty(dpy, window, _NET_WM_DESKTOP, XA_CARDINAL, 32, PropModeReplace, (unsigned char*)&desktop, 1);
+       }
+       else
+       {
+               XSetWindowAttributes wattr;
+               wattr.override_redirect = True;
+               XChangeWindowAttributes(dpy, window, CWOverrideRedirect, &wattr);
+               XMoveResizeWindow(dpy, window, x, y, width, height);
+       }
+}
+
+int
+wm_validate_window(Display *dpy, Window win)
+{
+       unsigned char *data;
+       Atom *atoms;
+       int status, real_format;
+       Atom real_type;
+       unsigned long items_read, items_left, i;
+       int result = 1;
+       
+       if(WM_PERSONALITY == WM_PERSONALITY_NETWM)
+       {
+               status = XGetWindowProperty(dpy, win, _NET_WM_STATE,
+                                 0L, 8192L, False, XA_ATOM, &real_type, &real_format,
+                                 &items_read, &items_left, &data);
+               
+               if(status != Success)
+                       return 0;
+               
+               atoms = (Atom *)data;
+               
+               for(i = 0; result && i < items_read; i++) {
+                       if(atoms[i] == _NET_WM_STATE_HIDDEN)
+                               result = 0;
+                       else if(! IGNORE_SKIP_TASKBAR && atoms[i] == _NET_WM_STATE_SKIP_TASKBAR)
+                               result = 0;
+                       else if(atoms[i] == _NET_WM_STATE_SHADED)
+                               result = 0;
+                       if(! result)
+                               break;
+               }
+               XFree(data);
+               
+               if(! result)
+                       return 0;
+               
+               status = XGetWindowProperty(dpy, win, _NET_WM_WINDOW_TYPE,
+                                           0L, 1L, False, XA_ATOM, &real_type, &real_format,
+                                           &items_read, &items_left, &data);
+               if(status != Success)
+                       return 1;
+               
+               atoms = (Atom *)data;
+               
+               if(items_read && (atoms[0] == _NET_WM_WINDOW_TYPE_DESKTOP || atoms[0] == _NET_WM_WINDOW_TYPE_DOCK))
+                       result = 0;
+               
+               XFree(data);
+               
+               return result;
+       } else {
+               CARD32 attr;
+               
+               status = XGetWindowProperty(dpy, win, _WIN_STATE,
+                                 0L, 1L, False, XA_CARDINAL, &real_type, &real_format,
+                                 &items_read, &items_left, &data);
+               if(status != Success || ! items_read)
+               {
+                       if(status == Success)
+                               XFree(data);
+                       return 0;
+               }
+               attr = (((CARD32*)data)[0]) & (WIN_STATE_MINIMIZED |
+                                            WIN_STATE_SHADED |
+                                            WIN_STATE_HIDDEN);
+               if(attr)
+                       result = 0;
+               XFree(data);
+               if(! result)
+                       return 0;
+               
+               if(! IGNORE_SKIP_TASKBAR)
+               {
+                       status = XGetWindowProperty(dpy, win, _WIN_HINTS,
+                                         0L, 1L, False, XA_CARDINAL, &real_type, &real_format,
+                                         &items_read, &items_left, &data);
+                       if(status != Success || ! items_read)
+                       {
+                               if(status == Success)
+                                       XFree(data);
+                               return 1; /* If there's no _WIN_HINTS, assume it's 0, thus valid */
+                       }
+                       attr = ((CARD32*)data)[0];
+                       if(attr & WIN_HINTS_SKIP_TASKBAR)
+                               result = 0;
+                       XFree(data);
+               }
+               
+               return result;
+       }
+}
+
+CARD32
+wm_get_window_desktop(Display *dpy, Window win)
+{
+       int status, real_format;
+       Atom real_type;
+       unsigned long items_read, items_left;
+       unsigned char *data;
+       CARD32 desktop = 0;
+       
+       if(WM_PERSONALITY == WM_PERSONALITY_GNOME)
+       {
+               status = XGetWindowProperty(dpy, win, _WIN_STATE,
+                                 0L, 1L, False, XA_CARDINAL, &real_type, &real_format,
+                                 &items_read, &items_left, &data);
+               if(status == Success)
+               {
+                       if(items_read)
+                               desktop = (((CARD32*)data)[0] & WIN_STATE_STICKY) ? (CARD32)-1 : 0;
+                       
+                       XFree(data);
+                       
+                       if(desktop)
+                               return desktop;
+               }
+       }
+       
+       status = XGetWindowProperty(dpy, win,
+                         (WM_PERSONALITY == WM_PERSONALITY_NETWM) ? _NET_WM_DESKTOP : _WIN_WORKSPACE,
+                         0L, 1L, False, XA_CARDINAL, &real_type, &real_format,
+                         &items_read, &items_left, &data);
+       
+       if(status != Success)
+               return wm_get_current_desktop(dpy);
+       
+       if(items_read)
+               desktop = ((CARD32*)data)[0];
+       else
+               desktop = wm_get_current_desktop(dpy);
+       
+       XFree(data);
+       
+       return desktop;
+}
+
+/* Get focused window and traverse towards the root window until a window with WM_STATE is found */
+Window
+wm_get_focused(Display *dpy)
+{
+       Window focused = None, root = None, *children;
+       unsigned int tmp_u;
+       int revert_to, status, real_format;
+       Atom real_type;
+       unsigned long items_read, items_left;
+       unsigned char *data;
+       
+       XGetInputFocus(dpy, &focused, &revert_to);
+       
+       /*printf("%x\n",focused);*/
+
+       while(focused != None && focused != root)
+       {
+               status = XGetWindowProperty(dpy, focused, XA_WM_STATE,
+                                           0L, 1L, False, XA_WM_STATE, &real_type, &real_format,
+                                           &items_read, &items_left, &data);
+               if(status == Success)
+               {
+                       XFree(data);
+                       if(items_read)
+                               break;
+               }
+               XQueryTree(dpy, focused, &root, &focused, &children, &tmp_u);
+               if(children)
+                       XFree(children);
+       }
+       
+       return focused;
+}
diff --git a/wm.h b/wm.h
new file mode 100644 (file)
index 0000000..050581d
--- /dev/null
+++ b/wm.h
@@ -0,0 +1,78 @@
+/* Skippy - Seduces Kids Into Perversion
+ *
+ * Copyright (C) 2004 Hyriand <hyriand@thegraveyard.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef SKIPPY_WM_H
+#define SKIPPY_WM_H
+
+Atom
+       /* Generic atoms */
+       XA_WM_STATE,
+       WM_CLIENT_LEADER,
+       XA_UTF8_STRING,
+       
+       /* Root pixmap / wallpaper atoms */
+       _XROOTPMAP_ID,
+       ESETROOT_PMAP_ID,
+       
+       /* NetWM atoms */
+       _NET_SUPPORTING_WM_CHECK,
+       _NET_SUPPORTED,
+       _NET_NUMBER_OF_DESKTOPS,
+       _NET_CLIENT_LIST,
+       _NET_CLIENT_LIST_STACKING,
+       _NET_CURRENT_DESKTOP,
+       _NET_WM_DESKTOP,
+       _NET_WM_STATE,
+       _NET_WM_STATE_HIDDEN,
+       _NET_WM_STATE_SKIP_TASKBAR,
+       _NET_WM_STATE_SKIP_PAGER,
+       _NET_WM_STATE_FULLSCREEN,
+       _NET_WM_STATE_SHADED,
+       _NET_WM_STATE_ABOVE,
+       _NET_WM_STATE_STICKY,
+       _NET_WM_WINDOW_TYPE,
+       _NET_WM_WINDOW_TYPE_DESKTOP,
+       _NET_WM_WINDOW_TYPE_DOCK,
+       _NET_WM_VISIBLE_NAME,
+       _NET_WM_NAME,
+       
+       /* Old gnome atoms */
+       _WIN_SUPPORTING_WM_CHECK,
+       _WIN_WORKSPACE,
+       _WIN_WORKSPACE_COUNT,
+       _WIN_PROTOCOLS,
+       _WIN_CLIENT_LIST,
+       _WIN_STATE,
+       _WIN_HINTS;
+
+void wm_get_atoms(Display *dpy);
+char wm_check(Display *dpy);
+void wm_use_netwm_fullscreen(Bool b);
+dlist *wm_get_stack(Display *dpy);
+Pixmap wm_get_root_pmap(Display *dpy);
+CARD32 wm_get_current_desktop(Display *dpy);
+FcChar8 *wm_get_window_title(Display *dpy, Window window, int *length_return);
+Window wm_get_group_leader(Display *dpy, Window window);
+void wm_set_fullscreen(Display *dpy, Window window, int x, int y, unsigned int width, unsigned int height);
+int wm_validate_window(Display *dpy, Window win);
+CARD32 wm_get_window_desktop(Display *dpy, Window win);
+Window wm_get_focused(Display *dpy);
+void wm_ignore_skip_taskbar(Bool b);
+
+#endif /* SKIPPY_WM_H */