--- /dev/null
+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
--- /dev/null
+ 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.
--- /dev/null
+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}
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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 */
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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 */
+
--- /dev/null
+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
--- /dev/null
+/* 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;
+ }
+ }
+}
+
--- /dev/null
+/* 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 */
--- /dev/null
+/* 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)
--- /dev/null
+/* 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 */
--- /dev/null
+/* 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);
+}
--- /dev/null
+/* 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 */
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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 */
--- /dev/null
+--- 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
--- /dev/null
+# 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
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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 */
--- /dev/null
+/* 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);
+ }
+}
--- /dev/null
+/* 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 */
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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 */