From: root Date: Tue, 12 Jan 2010 05:38:00 +0000 (+0900) Subject: initial release X-Git-Url: https://vcs.maemo.org/git/?a=commitdiff_plain;h=c6e2f3a0576dfcb93d45328a40b3659aefc0473b;p=skippy-xd initial release --- c6e2f3a0576dfcb93d45328a40b3659aefc0473b diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 0000000..14d7f49 --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,11 @@ +Skippy-XD changelog + +0.5.0 -- "Damage, Inc." + - 'Lazy transparency' mode: let xcompmgr take care of alpha-blending the + mini-windows. + - Tooltip can have drop-shadow text, and can be translucent + - Read config from ~/.skippy-xd.rc since the configuration options have + changed + - Branched from 'regular' skippy, switched from Imlib2 to XRender + rendering, removed the old window snapshot code and replaced it with + XComposite / XDamage stuff diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..d60c31a --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3a75faf --- /dev/null +++ b/Makefile @@ -0,0 +1,34 @@ +PREFIX = /usr/ +BINDIR = ${PREFIX}/bin + +X11PREFIX = /usr/X11R6 + +CFLAGS += -I${X11PREFIX}/include `imlib2-config --cflags` `pkg-config xft xrender xcomposite xdamage xfixes --cflags` -g -pedantic -Wall +LDFLAGS += -L${X11PREFIX}/lib -lX11 -lm `imlib2-config --libs` `pkg-config xft xrender xcomposite xdamage xfixes --libs` + +# Disable post-processing effects +#CFLAGS += -DNOEFFECTS + +# Comment these out to disable Xinerama support +#CFLAGS += -DXINERAMA +#LDFLAGS += -lXext -lXinerama + +# Uncomment this for Xinerama debugging +#CFLAGS += -DDEBUG + +EXESUFFIX = + +SOURCES = skippy.c wm.c dlist.c mainwin.c clientwin.c layout.c focus.c config.c tooltip.c +HEADERS = skippy.h wm.h dlist.h mainwin.h clientwin.h layout.h focus.h config.h tooltip.h + +all: skippy-xd${EXESUFFIX} + +skippy-xd${EXESUFFIX}: Makefile ${SOURCES} ${HEADERS} + gcc ${CFLAGS} -o skippy-xd${EXESUFFIX} ${SOURCES} ${LDFLAGS} + +clean: + rm -f skippy-xd${EXESUFFIX} + +install: + install -d ${DESTDIR}${BINDIR} + install -m 755 skippy-xd$(EXESUFFIX) ${DESTDIR}${BINDIR}/skippy-xd${EXESUFFIX} diff --git a/clientwin.c b/clientwin.c new file mode 100644 index 0000000..25eea4d --- /dev/null +++ b/clientwin.c @@ -0,0 +1,412 @@ +/* Skippy - Seduces Kids Into Perversion + * + * Copyright (C) 2004 Hyriand + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "skippy.h" + +#define INTERSECTS(x1, y1, w1, h1, x2, y2, w2, h2) \ + (((x1 >= x2 && x1 < (x2 + w2)) || (x2 >= x1 && x2 < (x1 + w1))) && \ + ((y1 >= y2 && y1 < (y2 + h2)) || (y2 >= y1 && y2 < (y1 + h1)))) + +int +clientwin_cmp_func(dlist *l, void *data) +{ + return ((ClientWin*)l->data)->client.window == (Window)data; +} + +int +clientwin_validate_func(dlist *l, void *data) +{ + ClientWin *cw = (ClientWin *)l->data; + CARD32 desktop = (*(CARD32*)data), + w_desktop = wm_get_window_desktop(cw->mainwin->dpy, cw->client.window); + +#ifdef XINERAMA + if(cw->mainwin->xin_active && ! INTERSECTS(cw->client.x, cw->client.y, cw->client.width, cw->client.height, + cw->mainwin->xin_active->x_org, cw->mainwin->xin_active->y_org, + cw->mainwin->xin_active->width, cw->mainwin->xin_active->height)) + return 0; +#endif + + return (w_desktop == (CARD32)-1 || desktop == w_desktop) && + wm_validate_window(cw->mainwin->dpy, cw->client.window); +} + +int +clientwin_check_group_leader_func(dlist *l, void *data) +{ + ClientWin *cw = (ClientWin *)l->data; + return wm_get_group_leader(cw->mainwin->dpy, cw->client.window) == *((Window*)data); +} + +int +clientwin_sort_func(dlist* a, dlist* b, void* data) +{ + unsigned int pa = ((ClientWin*)a->data)->client.x * ((ClientWin*)a->data)->client.y, + pb = ((ClientWin*)b->data)->client.x * ((ClientWin*)b->data)->client.y; + return (pa < pb) ? -1 : (pa == pb) ? 0 : 1; +} + +ClientWin * +clientwin_create(MainWin *mw, Window client) +{ + ClientWin *cw = (ClientWin *)malloc(sizeof(ClientWin)); + XSetWindowAttributes sattr; + XWindowAttributes attr; + XRenderPictureAttributes pa; + + cw->mainwin = mw; + cw->pixmap = None; + cw->focused = 0; + cw->origin = cw->destination = None; + cw->damage = None; + cw->damaged = False; + /* cw->repair = None; */ + + sattr.border_pixel = sattr.background_pixel = 0; + sattr.colormap = mw->colormap; + + sattr.event_mask = ButtonPressMask | + ButtonReleaseMask | + KeyReleaseMask | + EnterWindowMask | + LeaveWindowMask | + PointerMotionMask | + ExposureMask | + FocusChangeMask; + + sattr.override_redirect = mw->lazy_trans; + + cw->client.window = client; + cw->mini.format = mw->format; + cw->mini.window = XCreateWindow(mw->dpy, mw->lazy_trans ? mw->root : mw->window, 0, 0, 1, 1, 0, + mw->depth, InputOutput, mw->visual, + CWColormap | CWBackPixel | CWBorderPixel | CWEventMask | CWOverrideRedirect, &sattr); + + if(cw->mini.window == None) + { + free(cw); + return 0; + } + + XGetWindowAttributes(mw->dpy, client, &attr); + cw->client.format = XRenderFindVisualFormat(mw->dpy, attr.visual); + + pa.subwindow_mode = IncludeInferiors; + cw->origin = XRenderCreatePicture (cw->mainwin->dpy, cw->client.window, cw->client.format, CPSubwindowMode, &pa); + +/* XRenderSetPictureFilter(cw->mainwin->dpy, cw->origin, FilterBest, 0, 0); */ + if(mw->gquality == 0) + XRenderSetPictureFilter(cw->mainwin->dpy, cw->origin, FilterFast, NULL, 0); + else if (mw->gquality == 1) + XRenderSetPictureFilter(cw->mainwin->dpy, cw->origin, FilterGood, NULL, 0); + else if (mw->gquality == 2) + XRenderSetPictureFilter(cw->mainwin->dpy, cw->origin, FilterBest, NULL, 0); + else if (mw->gquality == 3) + XRenderSetPictureFilter(cw->mainwin->dpy, cw->origin, FilterNearest, NULL, 0); + else if (mw->gquality == 4) + XRenderSetPictureFilter(cw->mainwin->dpy, cw->origin, FilterBilinear, NULL, 0); + else if (mw->gquality == 5) + XRenderSetPictureFilter(cw->mainwin->dpy, cw->origin, FilterConvolution, NULL, 0); + + XSelectInput(cw->mainwin->dpy, cw->client.window, SubstructureNotifyMask | StructureNotifyMask); + + + return cw; +} + +void +clientwin_update(ClientWin *cw) +{ + Window tmpwin; + XWindowAttributes wattr; + + XGetWindowAttributes(cw->mainwin->dpy, cw->client.window, &wattr); + + cw->client.format = XRenderFindVisualFormat(cw->mainwin->dpy, wattr.visual); + XTranslateCoordinates(cw->mainwin->dpy, cw->client.window, wattr.root, + -wattr.border_width, +/* -90,-90,*/ + -wattr.border_width, + &cw->client.x, &cw->client.y, &tmpwin); + + cw->client.width = wattr.width; + cw->client.height = wattr.height; + + cw->mini.x = cw->mini.y = 0; + cw->mini.width = cw->mini.height = 1; +} + +void +clientwin_destroy(ClientWin *cw, Bool parentDestroyed) +{ + if(! parentDestroyed) + { + if(cw->origin != None) + XRenderFreePicture(cw->mainwin->dpy, cw->origin); + if(cw->damage != None) + XDamageDestroy(cw->mainwin->dpy, cw->damage); + } + if(cw->destination != None) + XRenderFreePicture(cw->mainwin->dpy, cw->destination); + if(cw->pixmap != None) + XFreePixmap(cw->mainwin->dpy, cw->pixmap); + + XDestroyWindow(cw->mainwin->dpy, cw->mini.window); + + free(cw); +} + +static void +clientwin_repaint(ClientWin *cw, XRectangle *rect) +{ + XRenderColor *tint = cw->focused ? &cw->mainwin->highlightTint : &cw->mainwin->normalTint; + int s_x = (double)rect->x * cw->factor, + s_y = (double)rect->y * cw->factor, + s_w = (double)rect->width * cw->factor, + s_h = (double)rect->height * cw->factor; + + + if(cw->mainwin->lazy_trans) + { + XRenderComposite(cw->mainwin->dpy, PictOpSrc, cw->origin, + cw->focused ? cw->mainwin->highlightPicture : cw->mainwin->normalPicture, + cw->destination, s_x, s_y, 0, 0, s_x, s_y, s_w, s_h); + } + else + { + XRenderComposite(cw->mainwin->dpy, PictOpSrc, cw->mainwin->background, None, cw->destination, cw->mini.x + s_x, cw->mini.y + s_y, 0, 0, s_x, s_y, s_w, s_h); + XRenderComposite(cw->mainwin->dpy, PictOpOver, cw->origin, + cw->focused ? cw->mainwin->highlightPicture : cw->mainwin->normalPicture, + cw->destination, s_x, s_y, 0, 0, s_x, s_y, s_w, s_h); + } + + if(tint->alpha) + XRenderFillRectangle(cw->mainwin->dpy, PictOpOver, cw->destination, tint, s_x, s_y, s_w, s_h); + + XClearArea(cw->mainwin->dpy, cw->mini.window, s_x, s_y, s_w, s_h, False); +} + +void +clientwin_render(ClientWin *cw) +{ + XRectangle rect; + rect.x = rect.y = 0; + rect.width = cw->client.width; + rect.height = cw->client.height; + clientwin_repaint(cw, &rect); +} + +void +clientwin_repair(ClientWin *cw) +{ + int nrects, i; + XRectangle *rects; + XserverRegion rgn = XFixesCreateRegion(cw->mainwin->dpy, 0, 0); + + XDamageSubtract(cw->mainwin->dpy, cw->damage, None, rgn); + + rects = XFixesFetchRegion(cw->mainwin->dpy, rgn, &nrects); + XFixesDestroyRegion(cw->mainwin->dpy, rgn); + + for(i = 0; i < nrects; i++) + clientwin_repaint(cw, &rects[i]); + + if(rects) + XFree(rects); + + cw->damaged = False; +} + +void +clientwin_schedule_repair(ClientWin *cw, XRectangle *area) +{ + cw->damaged = True; +} + +void +clientwin_move(ClientWin *cw, float f, int x, int y) +{ + /* int border = MAX(1, (double)DISTANCE(cw->mainwin) * f * 0.25); */ + int border = 0; + XSetWindowBorderWidth(cw->mainwin->dpy, cw->mini.window, border); + + cw->factor = f; + cw->mini.x = x + (int)cw->x * f; + cw->mini.y = y + (int)cw->y * f; + if(cw->mainwin->lazy_trans) + { + cw->mini.x += cw->mainwin->x; + cw->mini.y += cw->mainwin->y; + } + /*if(cw->client.width < 800) + cw->client.height -= 65;*/ + cw->mini.width = MAX(1, (int)cw->client.width * f ); + cw->mini.height = MAX(1, (int)cw->client.height * f ); + XMoveResizeWindow(cw->mainwin->dpy, cw->mini.window, cw->mini.x - border, cw->mini.y - border, cw->mini.width, cw->mini.height); + + if(cw->pixmap) + XFreePixmap(cw->mainwin->dpy, cw->pixmap); + + if(cw->destination) + XRenderFreePicture(cw->mainwin->dpy, cw->destination); + + cw->pixmap = XCreatePixmap(cw->mainwin->dpy, cw->mini.window, cw->mini.width, cw->mini.height, cw->mainwin->depth); + XSetWindowBackgroundPixmap(cw->mainwin->dpy, cw->mini.window, cw->pixmap); + + cw->destination = XRenderCreatePicture(cw->mainwin->dpy, cw->pixmap, cw->mini.format, 0, 0); +} + +void +clientwin_map(ClientWin *cw) +{ + if(cw->damage) + XDamageDestroy(cw->mainwin->dpy, cw->damage); + + cw->damage = XDamageCreate(cw->mainwin->dpy, cw->client.window, XDamageReportDeltaRectangles); + XRenderSetPictureTransform(cw->mainwin->dpy, cw->origin, &cw->mainwin->transform); + + clientwin_render(cw); + + XMapWindow(cw->mainwin->dpy, cw->mini.window); +} + +void +clientwin_unmap(ClientWin *cw) +{ + if(cw->damage) + { + XDamageDestroy(cw->mainwin->dpy, cw->damage); + cw->damage = None; + } + + if(cw->destination) + { + XRenderFreePicture(cw->mainwin->dpy, cw->destination); + cw->destination = None; + } + + if(cw->pixmap) + { + XFreePixmap(cw->mainwin->dpy, cw->pixmap); + cw->pixmap = None; + } + + XUnmapWindow(cw->mainwin->dpy, cw->mini.window); + XSetWindowBackgroundPixmap(cw->mainwin->dpy, cw->mini.window, None); + + cw->focused = 0; +} + +int client_msg(Display *disp, Window win, char *msg, /* {{{ */ + unsigned long data0, unsigned long data1, + unsigned long data2, unsigned long data3, + unsigned long data4) { + XEvent event; + long mask = SubstructureRedirectMask | SubstructureNotifyMask; + + event.xclient.type = ClientMessage; + event.xclient.serial = 0; + event.xclient.send_event = True; + event.xclient.message_type = XInternAtom(disp, msg, False); + event.xclient.window = win; + event.xclient.format = 32; + event.xclient.data.l[0] = data0; + event.xclient.data.l[1] = data1; + event.xclient.data.l[2] = data2; + event.xclient.data.l[3] = data3; + event.xclient.data.l[4] = data4; + + if (XSendEvent(disp, DefaultRootWindow(disp), False, mask, &event)) { + return EXIT_SUCCESS; + } + else { + fprintf(stderr, "Cannot send %s event.\n", msg); + return EXIT_FAILURE; + } +} + + +static void +childwin_focus(ClientWin *cw) +{ + XWarpPointer(cw->mainwin->dpy, None, cw->client.window, 0, 0, 0, 0, cw->client.width / 2, cw->client.height / 2); + + client_msg(cw->mainwin->dpy, cw->client.window, "_NET_ACTIVE_WINDOW", + 0, 0, 0, 0, 0); + XMapRaised(cw->mainwin->dpy, cw->client.window); + /*XRaiseWindow(cw->mainwin->dpy, cw->client.window);*/ + XSetInputFocus(cw->mainwin->dpy, cw->client.window, RevertToParent, CurrentTime); +} + +int +clientwin_handle(ClientWin *cw, XEvent *ev) +{ + if((ev->type == ButtonRelease && ev->xbutton.button == 1 && cw->mainwin->pressed == cw)) { + /*if((ev->xbutton.x >= 0 && ev->xbutton.y >= 0 && ev->xbutton.x < cw->mini.width && ev->xbutton.y < cw->mini.height))*/ + childwin_focus(cw); + cw->mainwin->pressed = 0; + return 1; + } else if(ev->type == KeyRelease) { + if(ev->xkey.keycode == cw->mainwin->key_up) + focus_up(cw); + else if(ev->xkey.keycode == cw->mainwin->key_down) + focus_down(cw); + else if(ev->xkey.keycode == cw->mainwin->key_left) + focus_left(cw); + else if(ev->xkey.keycode == cw->mainwin->key_right) + focus_right(cw); + else if(ev->xkey.keycode == cw->mainwin->key_f8) + client_msg(cw->mainwin->dpy, cw->client.window, "_NET_CLOSE_WINDOW", + 0, 0, 0, 0, 0); + /*client_msg(cw->mainwin->dpy, cw->client.window, "_NET_WM_STATE", + 1, XInternAtom(cw->mainwin->dpy, "_NET_WM_STATE_FULLSCREEN", False), 0, 0, 0);*/ + else if(ev->xkey.keycode == cw->mainwin->key_enter || ev->xkey.keycode == cw->mainwin->key_space) { + childwin_focus(cw); + return 1; + } + } else if(ev->type == ButtonPress && ev->xbutton.button == 1) { + cw->mainwin->pressed = cw; + } else if(ev->type == FocusIn) { + cw->focused = 1; + clientwin_render(cw); + XFlush(cw->mainwin->dpy); + } else if(ev->type == FocusOut) { + cw->focused = 0; + clientwin_render(cw); + XFlush(cw->mainwin->dpy); + } else if(ev->type == EnterNotify) { + XSetInputFocus(cw->mainwin->dpy, cw->mini.window, RevertToNone, CurrentTime); + if(cw->mainwin->tooltip) + { + int win_title_len = 0; + FcChar8 *win_title = wm_get_window_title(cw->mainwin->dpy, cw->client.window, &win_title_len); + if(win_title) + { + tooltip_map(cw->mainwin->tooltip, + ev->xcrossing.x_root + 20, ev->xcrossing.y_root + 20, + win_title, win_title_len); + free(win_title); + } + } + } else if(ev->type == LeaveNotify) { + if(cw->mainwin->tooltip) + tooltip_unmap(cw->mainwin->tooltip); + } + return 0; +} diff --git a/clientwin.h b/clientwin.h new file mode 100644 index 0000000..bfed025 --- /dev/null +++ b/clientwin.h @@ -0,0 +1,74 @@ +/* Skippy - Seduces Kids Into Perversion + * + * Copyright (C) 2004 Hyriand + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef SKIPPY_CLIENT_H +#define SKIPPY_CLIENT_H + +struct _SkippyWindow { + Window window; + int x, y; + unsigned int width, height; + XRenderPictFormat *format; +}; + +typedef struct _SkippyWindow SkippyWindow; + +struct _MainWin; +struct _ClientWin{ + struct _MainWin *mainwin; + + SkippyWindow client; + SkippyWindow mini; + + Pixmap pixmap; + Picture origin, destination; + Damage damage; + float factor; + + int focused; + + Bool damaged; + /* XserverRegion repair; */ + + /* These are virtual positions set by the layout routine */ + int x, y; +}; +typedef struct _ClientWin ClientWin; + +int clientwin_validate_func(dlist *, void *); +int clientwin_sort_func(dlist *, dlist *, void *); +ClientWin *clientwin_create(struct _MainWin *, Window); +void clientwin_destroy(ClientWin *, Bool parentDestroyed); +void clientwin_move(ClientWin *, float, int, int); +void clientwin_map(ClientWin *); +void clientwin_unmap(ClientWin *); +int clientwin_handle(ClientWin *, XEvent *); +int clientwin_cmp_func(dlist *, void*); +void clientwin_update(ClientWin *cw); +int clientwin_check_group_leader_func(dlist *l, void *data); +void clientwin_render(ClientWin *); +void clientwin_schedule_repair(ClientWin *cw, XRectangle *area); +void clientwin_repair(ClientWin *cw); +int client_msg(Display *disp, Window win, char *msg, /* {{{ */ + unsigned long data0, unsigned long data1, + unsigned long data2, unsigned long data3, + unsigned long data4); +int clientwin_backhome(); + +#endif /* SKIPPY_CLIENT_H */ diff --git a/config.c b/config.c new file mode 100644 index 0000000..be67a64 --- /dev/null +++ b/config.c @@ -0,0 +1,198 @@ +/* Skippy - Seduces Kids Into Perversion + * + * Copyright (C) 2004 Hyriand + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "skippy.h" + +typedef struct +{ + char *section, *key, *value; +} ConfigEntry; + +static char * +copy_match(const char *line, regmatch_t *match) +{ + char *r; + r = (char *)malloc(match->rm_eo + 1); + strncpy(r, line + match->rm_so, match->rm_eo - match->rm_so); + r[match->rm_eo - match->rm_so] = 0; + return r; +} + +static ConfigEntry * +entry_new(const char *section, char *key, char *value) +{ + ConfigEntry *e = (ConfigEntry *)malloc(sizeof(ConfigEntry)); + e->section = strdup(section); + e->key = key; + e->value = value; + return e; +} + +static dlist * +entry_set(dlist *config, const char *section, char *key, char *value) +{ + dlist *iter = dlist_first(config); + ConfigEntry *entry; + for(; iter; iter = iter->next) + { + entry = (ConfigEntry *)iter->data; + if(! strcasecmp(entry->section, section) && ! strcasecmp(entry->key, key)) { + free(key); + free(entry->value); + entry->value = value; + return config; + } + } + entry = entry_new(section, key, value); + return dlist_add(config, entry); +} + +static dlist * +config_parse(char *config) +{ + regex_t re_section, re_empty, re_entry; + regmatch_t matches[5]; + char line[8192], *section = 0; + int ix = 0, l_ix = 0; + dlist *new_config = 0; + + regcomp(&re_section, "^[[:space:]]*\\[[[:space:]]*([[:alnum:]]*?)[[:space:]]*\\][[:space:]]*$", REG_EXTENDED); + regcomp(&re_empty, "^[[:space:]]*#|^[[:space:]]*$", REG_EXTENDED); + regcomp(&re_entry, "^[[:space:]]*([[:alnum:]]+)[[:space:]]*=[[:space:]]*(.*?)[[:space:]]*$", REG_EXTENDED); + + while(1) + { + if((config[ix] == '\0' || config[ix] == '\n')) + { + line[l_ix] = 0; + if(regexec(&re_empty, line, 5, matches, 0) == 0) { + /* do nothing */ + } else if(regexec(&re_section, line, 5, matches, 0) == 0) { + if(section) + free(section); + section = copy_match(line, &matches[1]); + } else if(section && regexec(&re_entry, line, 5, matches, 0) == 0) { + char *key = copy_match(line, &matches[1]), + *value = copy_match(line, &matches[2]); + new_config = entry_set(new_config, section, key, value); + } else { + fprintf(stderr, "WARNING: Ignoring invalid line: %s\n", line); + } + l_ix = 0; + } else { + line[l_ix] = config[ix]; + l_ix++; + } + if(config[ix] == 0) + break; + ++ix; + } + + if(section) + free(section); + + regfree(&re_section); + regfree(&re_empty); + regfree(&re_entry); + + return new_config; +} + +dlist * +config_load(const char *path) +{ + FILE *fin = fopen(path, "r"); + long flen; + char *data; + dlist *config; + + if(! fin) + { + fprintf(stderr, "WARNING: Couldn't load config file '%s'.\n", path); + return 0; + } + + fseek(fin, 0, SEEK_END); + flen = ftell(fin); + + if(! flen) + { + fprintf(stderr, "WARNING: '%s' is empty.\n", path); + fclose(fin); + return 0; + } + + fseek(fin, 0, SEEK_SET); + + data = (char *)malloc(flen + 1); + if(fread(data, 1, flen, fin) != flen) + { + fprintf(stderr, "WARNING: Couldn't read from config file '%s'.\n", path); + free(data); + fclose(fin); + return 0; + } + + fclose(fin); + + config = config_parse(data); + + free(data); + + return config; +} + +static void +entry_free(ConfigEntry *entry) +{ + free(entry->section); + free(entry->key); + free(entry->value); + free(entry); +} + +void +config_free(dlist *config) +{ + dlist_free_with_func(config, (dlist_free_func)entry_free); +} + +static int +entry_find_func(dlist *l, ConfigEntry *key) +{ + ConfigEntry *entry = (ConfigEntry*)l->data; + return ! (strcasecmp(entry->section, key->section) || strcasecmp(entry->key, key->key)); +} + +const char * +config_get(dlist *config, const char *section, const char *key, const char *def) +{ + ConfigEntry needle; + dlist *iter; + + needle.section = (char *)section; + needle.key = (char *)key; + + iter = dlist_find(dlist_first(config), (dlist_match_func)entry_find_func, &needle); + + if(! iter) + return def; + + return ((ConfigEntry*)iter->data)->value; +} diff --git a/config.h b/config.h new file mode 100644 index 0000000..ac4c983 --- /dev/null +++ b/config.h @@ -0,0 +1,28 @@ +/* Skippy - Seduces Kids Into Perversion + * + * Copyright (C) 2004 Hyriand + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef SKIPPY_CONFIG_H +#define SKIPPY_CONFIG_H + +dlist *config_load(const char *); +void config_free(dlist *); +const char *config_get(dlist *, const char *, const char *, const char *); + +#endif /* SKIPPY_CONFIG_H */ + diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..14d7f49 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,11 @@ +Skippy-XD changelog + +0.5.0 -- "Damage, Inc." + - 'Lazy transparency' mode: let xcompmgr take care of alpha-blending the + mini-windows. + - Tooltip can have drop-shadow text, and can be translucent + - Read config from ~/.skippy-xd.rc since the configuration options have + changed + - Branched from 'regular' skippy, switched from Imlib2 to XRender + rendering, removed the old window snapshot code and replaced it with + XComposite / XDamage stuff diff --git a/dlist.c b/dlist.c new file mode 100644 index 0000000..70dd531 --- /dev/null +++ b/dlist.c @@ -0,0 +1,292 @@ +/* Skippy - Seduces Kids Into Perversion + * + * Copyright (C) 2004 Hyriand + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "skippy.h" + +dlist * +dlist_last(dlist *l) +{ + while(l && l->next) + l = l->next; + return l; +} + +dlist * +dlist_first(dlist *l) +{ + while(l && l->prev) + l = l->prev; + return l; +} + +dlist * +dlist_add(dlist *l, void *d) +{ + dlist *new_elem; + + l = dlist_last(l); + new_elem = malloc(sizeof(dlist)); + new_elem->prev = l; + new_elem->next = 0; + new_elem->data = d; + if(l) + l->next = new_elem; + return new_elem; +} + +dlist * +dlist_prepend(dlist *l, void *d) +{ + dlist *new_elem; + l = dlist_first(l); + new_elem = malloc(sizeof(dlist)); + new_elem->prev = 0; + new_elem->next = l; + new_elem->data = d; + if(l) + l->prev = new_elem; + return new_elem; +} + +dlist * +dlist_remove(dlist *l) +{ + dlist *n = 0; + + if(! l) + return 0; + + if(l->prev) { + n = l->prev; + l->prev->next = l->next; + } + if(l->next) { + n = l->next; + l->next->prev = l->prev; + } + free(l); + return n; +} + +dlist * +dlist_remove_free_data(dlist *l) +{ + if(l && l->data) + free(l->data); + return dlist_remove(l); +} + +dlist * +dlist_remove_nth(dlist *l, unsigned int n) +{ + return dlist_remove(dlist_nth(l, n)); +} + +dlist * +dlist_remove_nth_free_data(dlist *l, unsigned int n) +{ + return dlist_remove_free_data(dlist_nth(l, n)); +} + +int +dlist_same(dlist *l1, dlist *l2) +{ + l1 = dlist_first(l1); + while(l1) { + if(l1 == l2) + return 1; + l1 = l1->next; + } + return 0; +} + +void +dlist_reverse(dlist *l) +{ + dlist *iter1 = dlist_first(l), + *iter2 = dlist_last(l); + + while(iter1 != iter2) { + dlist_swap(iter1, iter2); + if(iter1->next == iter2) + break; + iter1 = iter1->next; + iter2 = iter2->prev; + } +} + +dlist * +dlist_free(dlist *l) +{ + l = dlist_first(l); + + while(l) { + dlist *c = l; + l = l->next; + free(c); + } + + return 0; +} + +dlist * +dlist_dup(dlist *l) +{ + dlist *n = 0; + l = dlist_first(l); + + while(l) { + n = dlist_add(n, l->data); + l = l->next; + } + + return n; +} + +dlist * +dlist_find_all(dlist *l, dlist_match_func match, void *data) +{ + dlist *n = 0; + l = dlist_first(l); + + while(l) { + if(match(l, data)) + n = dlist_add(n, l->data); + l = l->next; + } + + return n; +} + +dlist * +dlist_find(dlist *l, dlist_match_func func, void *data) +{ + for(l = dlist_first(l); l; l = l->next) + if(func(l, data)) + break; + return l; +} + +dlist * +dlist_find_data(dlist *l, void *data) +{ + for(l = dlist_first(l); l; l = l->next) + if(l->data == data) + break; + return l; +} + +void +dlist_free_data(dlist *l) +{ + l = dlist_first(l); + + while(l) { + if(l->data) + free(l->data); + l->data = 0; + l = l->next; + } +} + +dlist * +dlist_free_with_data(dlist *l) +{ + l = dlist_first(l); + + while(l) { + dlist *c = l; + if(l->data) + free(l->data); + l = l->next; + free(c); + } + + return 0; +} + +dlist * +dlist_free_with_func(dlist *l, dlist_free_func func) +{ + l = dlist_first(l); + + while(l) { + dlist *c = l; + if(l->data) + func(l->data); + l = l->next; + free(c); + } + + return 0; +} + +unsigned int +dlist_len(dlist *l) +{ + unsigned int n = 0; + + l = dlist_first(l); + while(l) { + n++; + l = l->next; + } + + return n; +} + +dlist * +dlist_nth(dlist *l, unsigned int n) +{ + unsigned int i = 0; + l = dlist_first(l); + while(l && i != n) { + i++; + l = l->next; + } + + return l; +} + +void +dlist_swap(dlist *l1, dlist *l2) +{ + void *tmp = l1->data; + l1->data = l2->data; + l2->data = tmp; +} + +void +dlist_sort(dlist *l, dlist_cmp_func cmp, void *data) +{ + dlist *start = dlist_first(l); + while(start) { + dlist *iter = start; + start = 0; + while(iter) { + if(iter->next && cmp(iter, iter->next, data) == 1) { + dlist_swap(iter, iter->next); + if(! start) + start = iter->prev; + } + iter = iter->next; + } + } +} + diff --git a/dlist.h b/dlist.h new file mode 100644 index 0000000..913375d --- /dev/null +++ b/dlist.h @@ -0,0 +1,94 @@ +/* Skippy - Seduces Kids Into Perversion + * + * Copyright (C) 2004 Hyriand + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef SKIPPY_DLIST_H +#define SKIPPY_DLIST_H + +struct dlist_element { + void *data; + struct dlist_element *next; + struct dlist_element *prev; +}; + +typedef struct dlist_element dlist; + +/* first element in list */ +dlist *dlist_first(dlist *); + +/* last element in list */ +dlist *dlist_last(dlist *); + +/* add element to the end of the list, returns new element */ +dlist *dlist_add(dlist *, void *); + +/* add element to the start of the list, returns new element (and thus, start of list) */ +dlist *dlist_prepend(dlist *, void *); + +/* remove an element from the list, returns another element in the list or 0 */ +dlist *dlist_remove(dlist *); +dlist *dlist_remove_free_data(dlist *); +dlist *dlist_remove_nth(dlist *, unsigned int n); +dlist *dlist_remove_nth_free_data(dlist *, unsigned int n); + +/* free the list (not the data), returns 0 */ +dlist *dlist_free(dlist *); + +/* delete a list calling func on each data item */ +typedef void (*dlist_free_func)(void *); +dlist *dlist_free_with_func(dlist *, dlist_free_func); + +/* free the data (not the list) */ +void dlist_free_data(dlist *); + +/* free both list and data, returns 0 */ +dlist *dlist_free_with_data(dlist *); + +/* return the length of the list */ +unsigned int dlist_len(dlist *); + +/* check if l1 and l2 are elements of the same list */ +int dlist_same(dlist *, dlist *); + +/* reverse a list (swaps data) */ +void dlist_reverse(dlist *); + +/* duplicate the list (not the data), returns new end */ +dlist *dlist_dup(dlist *); + +/* find all matching elements (returns new list or 0) */ +typedef int (*dlist_match_func)(dlist*, void *); +dlist *dlist_find_all(dlist *, dlist_match_func, void *); + +/* find an element (returns element or 0) */ +dlist *dlist_find(dlist *, dlist_match_func, void *); + +/* find an element whose data pointer matches */ +dlist *dlist_find_data(dlist *, void *); + +/* return nth element or 0 */ +dlist *dlist_nth(dlist *l, unsigned int n); + +/* swap the data fields of 2 elements */ +void dlist_swap(dlist *, dlist *); + +/* sort a list (not very efficient, uses bubble-sort) */ +typedef int (*dlist_cmp_func)(dlist *, dlist *, void *); +void dlist_sort(dlist *, dlist_cmp_func, void *); + +#endif /* SKIPPY_DLIST_H */ diff --git a/focus.c b/focus.c new file mode 100644 index 0000000..7d1c3f2 --- /dev/null +++ b/focus.c @@ -0,0 +1,80 @@ +/* Skippy - Seduces Kids Into Perversion + * + * Copyright (C) 2004 Hyriand + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "skippy.h" + +typedef float (*dist_func)(SkippyWindow *, SkippyWindow *); +typedef int (*match_func)(dlist *, SkippyWindow *); + +static void +dir_focus(ClientWin *cw, match_func match, dist_func func) +{ + float diff = 0.0; + ClientWin *candidate = NULL; + dlist *iter, *candidates; + + candidates = dlist_first(dlist_find_all(cw->mainwin->cod, (dlist_match_func)match, &cw->mini)); + if(! candidates) + return; + + for(iter = dlist_first(candidates); iter; iter = iter->next) + { + ClientWin *win = (ClientWin *)iter->data; + float distance = func(&cw->mini, &win->mini); + if(! candidate || distance < diff) + { + candidate = win; + diff = distance; + } + } + + XWarpPointer(candidate->mainwin->dpy, None, candidate->mini.window, 0, 0, 0, 0, candidate->mini.width / 2, candidate->mini.height / 2); + XSetInputFocus(candidate->mainwin->dpy, candidate->mini.window, RevertToParent, CurrentTime); + dlist_free(candidates); +} + +#define HALF_H(w) (w->x + (int)w->width / 2) +#define HALF_V(w) (w->y + (int)w->height / 2) +#define SQR(x) pow(x, 2) + +#define DISTFUNC(name, d_x, d_y) \ +static float name (SkippyWindow *a, SkippyWindow *b) \ +{ return sqrt(SQR(d_x) + SQR(d_y)); } + +#define QUALFUNC(name, expr) \ +static int name(dlist *l, SkippyWindow *b) \ +{ SkippyWindow *a = &((ClientWin*)l->data)->mini; return expr; } + +#define FOCUSFUNC(name, qual, dist) \ +void name(ClientWin *cw) { dir_focus(cw, qual, dist); } + +DISTFUNC(dist_top_bottom, HALF_H(a) - HALF_H(b), a->y - b->y - (int)b->height) +DISTFUNC(dist_bottom_top, HALF_H(a) - HALF_H(b), b->y - a->y - (int)a->height) +DISTFUNC(dist_left_right, HALF_V(a) - HALF_V(b), a->x - b->x - (int)b->width) +DISTFUNC(dist_right_left, HALF_V(a) - HALF_V(b), b->x - a->x - (int)a->width) + +QUALFUNC(win_above, a->y + a->height < b->y) +QUALFUNC(win_below, b->y + b->height < a->y) +QUALFUNC(win_left, a->x + a->width < b->x) +QUALFUNC(win_right, b->x + b->width < a->x) + +FOCUSFUNC(focus_up, win_above, dist_top_bottom) +FOCUSFUNC(focus_down, win_below, dist_bottom_top) +FOCUSFUNC(focus_left, win_left, dist_left_right) +FOCUSFUNC(focus_right, win_right, dist_right_left) diff --git a/focus.h b/focus.h new file mode 100644 index 0000000..f347891 --- /dev/null +++ b/focus.h @@ -0,0 +1,28 @@ +/* Skippy - Seduces Kids Into Perversion + * + * Copyright (C) 2004 Hyriand + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef SKIPPY_FOCUS_H +#define SKIPPY_FOCUS_H + +void focus_up(ClientWin *cw); +void focus_down(ClientWin *cw); +void focus_left(ClientWin *cw); +void focus_right(ClientWin *cw); + +#endif /* SKIPPY_FOCUS_H */ diff --git a/layout.c b/layout.c new file mode 100644 index 0000000..78fb3be --- /dev/null +++ b/layout.c @@ -0,0 +1,106 @@ +/* Skippy - Seduces Kids Into Perversion + * + * Copyright (C) 2004 Hyriand + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "skippy.h" + +void +layout_run(MainWin *mw, dlist *windows, unsigned int *total_width, unsigned int *total_height) +{ + int sum_w = 0, max_h = 0, max_w = 0, max_row_w = 0; + int row_y = 0, y = 0, x = 0, row_h = 0; + + dlist *iter, *slots = 0, *slot_iter, *rows; + + rows = dlist_add(0, 0); + + windows = dlist_first(windows); + *total_width = *total_height = 0; + + for(iter = windows; iter; iter = iter->next) + { + ClientWin *cw = (ClientWin *)iter->data; + sum_w += cw->client.width; + max_w = MAX(max_w, cw->client.width); + max_h = MAX(max_h, cw->client.height); + } + + for(iter = windows; iter; iter = iter->next) + { + ClientWin *cw = (ClientWin*)iter->data; + dlist *slot_iter = dlist_first(slots); + for(; slot_iter; slot_iter = slot_iter->next) + { + dlist *slot = (dlist *)slot_iter->data; + int slot_h = -mw->distance; + REDUCE(slot_h = slot_h + ((ClientWin*)iter->data)->client.height + mw->distance, slot); + if(slot_h + mw->distance + cw->client.height < max_h) + { + slot_iter->data = dlist_add(slot, cw); + break; + } + } + if(! slot_iter) + slots = dlist_add(slots, dlist_add(0, cw)); + } + + max_row_w = sqrt(sum_w * max_h); + for(slot_iter = dlist_first(slots); slot_iter; slot_iter = slot_iter->next) + { + dlist *slot = (dlist *)slot_iter->data; + int slot_w = 0; + REDUCE(slot_w = MAX(slot_w, ((ClientWin*)iter->data)->client.width), slot); + y = row_y; + for(iter = dlist_first(slot); iter; iter = iter->next) + { + ClientWin *cw = (ClientWin *)iter->data; + cw->x = x + (slot_w - cw->client.width) / 2; + cw->y = y; + y += cw->client.height + mw->distance; + rows->data = dlist_add((dlist *)rows->data, cw); + } + row_h = MAX(row_h, y - row_y); + *total_height = MAX(*total_height, y); + x += slot_w + mw->distance; + *total_width = MAX(*total_width, x); + if(x > max_row_w) + { + x = 0; + row_y += row_h; + row_h = 0; + rows = dlist_add(rows, 0); + } + dlist_free(slot); + } + dlist_free(slots); + + *total_width -= mw->distance; + *total_height -= mw->distance; + + for(iter = dlist_first(rows); iter; iter = iter->next) + { + dlist *row = (dlist *)iter->data; + int row_w = 0, xoff; + REDUCE(row_w = MAX(row_w, ((ClientWin*)iter->data)->x + ((ClientWin*)iter->data)->client.width), row); + xoff = (*total_width - row_w) / 2; + REDUCE(((ClientWin*)iter->data)->x += xoff, row); + dlist_free(row); + } + + dlist_free(rows); +} diff --git a/layout.h b/layout.h new file mode 100644 index 0000000..36469ea --- /dev/null +++ b/layout.h @@ -0,0 +1,25 @@ +/* Skippy - Seduces Kids Into Perversion + * + * Copyright (C) 2004 Hyriand + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef SKIPPY_LAYOUT_H +#define SKIPPY_LAYOUT_H + +void layout_run(MainWin *, dlist *, unsigned int *, unsigned int *); + +#endif /* SKIPPY_LAYOUT_H */ diff --git a/mainwin.c b/mainwin.c new file mode 100644 index 0000000..9d7fb8f --- /dev/null +++ b/mainwin.c @@ -0,0 +1,491 @@ +/* Skippy - Seduces Kids Into Perversion + * + * Copyright (C) 2004 Hyriand + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "skippy.h" + +/* from 'uncover': */ +static Visual * +find_argb_visual (Display *dpy, int scr) +{ + XVisualInfo *xvi; + XVisualInfo template; + int nvi; + int i; + XRenderPictFormat *format; + Visual *visual; + + template.screen = scr; + template.depth = 32; + template.class = TrueColor; + xvi = XGetVisualInfo (dpy, + VisualScreenMask | + VisualDepthMask | + VisualClassMask, + &template, + &nvi); + if (!xvi) + return 0; + visual = 0; + for (i = 0; i < nvi; i++) + { + format = XRenderFindVisualFormat (dpy, xvi[i].visual); + if (format->type == PictTypeDirect && format->direct.alphaMask) + { + visual = xvi[i].visual; + break; + } + } + + XFree (xvi); + return visual; +} + +MainWin * +mainwin_create(Display *dpy, dlist *config) +{ + const char *tmp; + double tmp_d; + XColor exact_color; + XSetWindowAttributes wattr; + XWindowAttributes rootattr; + XRenderPictureAttributes pa; + XRenderColor clear; + int error_base; +#ifdef XINERAMA + int event_base; +#endif /* XINERAMA */ + + MainWin *mw = (MainWin *)malloc(sizeof(MainWin)); + + mw->dpy = dpy; + mw->screen = DefaultScreen(dpy); + mw->root = RootWindow(dpy, mw->screen); + mw->lazy_trans = (strcasecmp(config_get(config, "general", "lazyTrans", "false"), "true") == 0) ? True : False; + if(mw->lazy_trans) + { + mw->depth = 32; + mw->visual = find_argb_visual(dpy, DefaultScreen(dpy)); + if(! mw->visual) + { + fprintf(stderr, "WARNING: Couldn't find argb visual, disabling lazy transparency.\n"); + mw->lazy_trans = False; + } + } + if(! mw->lazy_trans) + { + mw->depth = DefaultDepth(dpy, mw->screen); + mw->visual = DefaultVisual(dpy, mw->screen); + } + mw->colormap = XCreateColormap(dpy, mw->root, mw->visual, AllocNone); + mw->bg_pixmap = None; + mw->background = None; + mw->format = XRenderFindVisualFormat(dpy, mw->visual); +#ifdef XINERAMA + mw->xin_info = mw->xin_active = 0; + mw->xin_screens = 0; +#endif /* XINERAMA */ + + mw->pressed = mw->focus = 0; + mw->tooltip = 0; + mw->cod = 0; + mw->key_up = XKeysymToKeycode(dpy, XK_Up); + mw->key_down = XKeysymToKeycode(dpy, XK_Down); + mw->key_left = XKeysymToKeycode(dpy, XK_Left); + mw->key_right = XKeysymToKeycode(dpy, XK_Right); + mw->key_enter = XKeysymToKeycode(dpy, XK_Return); + mw->key_space = XKeysymToKeycode(dpy, XK_space); + mw->key_escape = XKeysymToKeycode(dpy, XK_Escape); + mw->key_q = XKeysymToKeycode(dpy, XK_q); + mw->key_f6 = XKeysymToKeycode(dpy, XK_F6); + mw->key_f7 = XKeysymToKeycode(dpy, XK_F7); + mw->key_f8 = XKeysymToKeycode(dpy, XK_F8); + mw->key_f5 = XKeysymToKeycode(dpy, XK_F5); + + XGetWindowAttributes(dpy, mw->root, &rootattr); + mw->x = mw->y = 0; + mw->width = rootattr.width; + mw->height = rootattr.height; + + wattr.colormap = mw->colormap; + wattr.background_pixel = wattr.border_pixel = 0; + wattr.event_mask = VisibilityChangeMask | + ButtonReleaseMask; + + mw->window = XCreateWindow(dpy, mw->root, 0, 0, mw->width, mw->height, 0, + mw->depth, InputOutput, mw->visual, + CWBackPixel|CWBorderPixel|CWColormap|CWEventMask, &wattr); + if(mw->window == None) { + free(mw); + return 0; + } + +#ifdef XINERAMA +# ifdef DEBUG + fprintf(stderr, "--> checking for Xinerama extension... "); +# endif /* DEBUG */ + if(XineramaQueryExtension(dpy, &event_base, &error_base)) + { +# ifdef DEBUG + fprintf(stderr, "yes\n--> checking if Xinerama is enabled... "); +# endif /* DEBUG */ + if(XineramaIsActive(dpy)) + { +# ifdef DEBUG + fprintf(stderr, "yes\n--> fetching Xinerama info... "); +# endif /* DEBUG */ + mw->xin_info = XineramaQueryScreens(mw->dpy, &mw->xin_screens); +# ifdef DEBUG + fprintf(stderr, "done (%i screens)\n", mw->xin_screens); +# endif /* DEBUG */ + } +# ifdef DEBUG + else + fprintf(stderr, "no\n"); +# endif /* DEBUG */ + } +# ifdef DEBUG + else + fprintf(stderr, "no\n"); +# endif /* DEBUG */ +#endif /* XINERAMA */ + + if(! XDamageQueryExtension (dpy, &mw->damage_event_base, &error_base)) + { + fprintf(stderr, "FATAL: XDamage extension not found.\n"); + exit(1); + } + + if(! XCompositeQueryExtension(dpy, &event_base, &error_base)) + { + fprintf(stderr, "FATAL: XComposite extension not found.\n"); + exit(1); + } + + if(! XRenderQueryExtension(dpy, &event_base, &error_base)) + { + fprintf(stderr, "FATAL: XRender extension not found.\n"); + exit(1); + } + + if(! XFixesQueryExtension(dpy, &event_base, &error_base)) + { + fprintf(stderr, "FATAL: XFixes extension not found.\n"); + exit(1); + } + + XCompositeRedirectSubwindows (mw->dpy, mw->root, CompositeRedirectAutomatic); + + tmp_d = strtod(config_get(config, "general", "updateFreq", "10.0"), 0); + if(tmp_d != 0.0) + mw->poll_time = (1.0 / tmp_d) * 1000.0; + else + mw->poll_time = 0; + + tmp = config_get(config, "normal", "tint", "black"); + if(! XParseColor(mw->dpy, mw->colormap, tmp, &exact_color)) + { + fprintf(stderr, "Couldn't look up color '%s', reverting to black", tmp); + mw->normalTint.red = mw->normalTint.green = mw->normalTint.blue = 0; + } + else + { + mw->normalTint.red = exact_color.red; + mw->normalTint.green = exact_color.green; + mw->normalTint.blue = exact_color.blue; + } + mw->normalTint.alpha = MAX(0, MIN(strtol(config_get(config, "normal", "tintOpacity", "0"), 0, 0) * 256, 65535)); + + tmp = config_get(config, "highlight", "tint", "#101020"); + if(! XParseColor(mw->dpy, mw->colormap, tmp, &exact_color)) + { + fprintf(stderr, "Couldn't look up color '%s', reverting to #101020", tmp); + mw->highlightTint.red = mw->highlightTint.green = 0x10; + mw->highlightTint.blue = 0x20; + } + else + { + mw->highlightTint.red = exact_color.red; + mw->highlightTint.green = exact_color.green; + mw->highlightTint.blue = exact_color.blue; + } + mw->highlightTint.alpha = MAX(0, MIN(strtol(config_get(config, "highlight", "tintOpacity", "64"), 0, 0) * 256, 65535)); + + pa.repeat = True; + clear.alpha = MAX(0, MIN(strtol(config_get(config, "normal", "opacity", "200"), 0, 10) * 256, 65535)); + mw->normalPixmap = XCreatePixmap(mw->dpy, mw->window, 1, 1, 8); + mw->normalPicture = XRenderCreatePicture(mw->dpy, mw->normalPixmap, XRenderFindStandardFormat(mw->dpy, PictStandardA8), CPRepeat, &pa); + XRenderFillRectangle(mw->dpy, PictOpSrc, mw->normalPicture, &clear, 0, 0, 1, 1); + + clear.alpha = MAX(0, MIN(strtol(config_get(config, "highlight", "opacity", "255"), 0, 10) * 256, 65535)); + mw->highlightPixmap = XCreatePixmap(mw->dpy, mw->window, 1, 1, 8); + mw->highlightPicture = XRenderCreatePicture(mw->dpy, mw->highlightPixmap, XRenderFindStandardFormat(mw->dpy, PictStandardA8), CPRepeat, &pa); + XRenderFillRectangle(mw->dpy, PictOpSrc, mw->highlightPicture, &clear, 0, 0, 1, 1); + + tmp = config_get(config, "general", "distance", "50"); + mw->distance = MAX(1, strtol(tmp, 0, 10)); + + if(! strcasecmp(config_get(config, "tooltip", "show", "true"), "true")) + mw->tooltip = tooltip_create(mw, config); + + return mw; +} + +int +load_image (MainWin *mw, Bool rotate, Imlib_Image rootimg) +{ + Imlib_Image buffer; + +if (rotate) + buffer = mw->img_p; +else + buffer = mw->img_l; + + if (!buffer) + return 0; + + imlib_context_set_image (buffer); + + imlib_context_set_image (rootimg); + imlib_blend_image_onto_image (buffer, 0, 0, 0, imlib_image_get_width (), imlib_image_get_height (), + 0, 0, mw->width, mw->height); + + imlib_context_set_image (buffer); + imlib_free_image (); + + imlib_context_set_image (rootimg); + + return 1; +} + + + +void +mainwin_update_background(MainWin *mw) +{ +/* Pixmap root = wm_get_root_pmap(mw->dpy); + XRenderColor black = { 0, 0, 0, 65535};*/ + XRenderPictureAttributes pa; + Imlib_Context *context; + Imlib_Image image; + Bool rotate; + + if(mw->width == 800) + rotate = False; + else + rotate = True; + + if(mw->bg_pixmap) + XFreePixmap(mw->dpy, mw->bg_pixmap); + if(mw->background) + XRenderFreePicture(mw->dpy, mw->background); + + mw->bg_pixmap = XCreatePixmap(mw->dpy, mw->window, mw->width, mw->height, mw->depth); + pa.repeat = True; + mw->background = XRenderCreatePicture(mw->dpy, mw->bg_pixmap, mw->format, CPRepeat, &pa); + + + context = imlib_context_new (); + imlib_context_push (context); + imlib_context_set_display (mw->dpy); + imlib_context_set_visual (mw->visual); + imlib_context_set_colormap (mw->colormap); + imlib_context_set_drawable (mw->bg_pixmap); + imlib_context_set_color_range (imlib_create_color_range ()); + + image = imlib_create_image (mw->width, mw->height); + imlib_context_set_image (image); + + imlib_context_set_color (0, 0, 0, 255); + imlib_image_fill_rectangle (0, 0, mw->width, mw->height); + + imlib_context_set_dither (1); + imlib_context_set_blend (1); + + if (load_image (mw, rotate, image) == 0) + { + fprintf (stderr, "Bad image\n"); + exit(1); + } + + imlib_render_image_on_drawable (0, 0); + imlib_free_image (); + imlib_free_color_range (); + +/* if(root == None) + XRenderFillRectangle(mw->dpy, PictOpSrc, mw->background, &black, 0, 0, mw->width, mw->height); + else + { + Picture from = XRenderCreatePicture(mw->dpy, root, XRenderFindVisualFormat(mw->dpy, DefaultVisual(mw->dpy, mw->screen)), 0, 0); + XRenderComposite(mw->dpy, PictOpSrc, from, None, mw->background, mw->x, mw->y, 0, 0, 0, 0, mw->width, mw->height); + XRenderFreePicture(mw->dpy, from); + }*/ + + XSetWindowBackgroundPixmap(mw->dpy, mw->window, mw->bg_pixmap); + XClearWindow(mw->dpy, mw->window); +} + +void +mainwin_update(MainWin *mw) +{ +#ifdef XINERAMA + XineramaScreenInfo *iter; + int i; + Window dummy_w; + int root_x, root_y, dummy_i; + unsigned int dummy_u; + + if(! mw->xin_info || ! mw->xin_screens) + { + mainwin_update_background(mw); + return; + } + +# ifdef DEBUG + fprintf(stderr, "--> querying pointer... "); +# endif /* DEBUG */ + XQueryPointer(mw->dpy, mw->root, &dummy_w, &dummy_w, &root_x, &root_y, &dummy_i, &dummy_i, &dummy_u); +# ifdef DEBUG + fprintf(stderr, "+%i+%i\n", root_x, root_y); + + fprintf(stderr, "--> figuring out which screen we're on... "); +# endif /* DEBUG */ + iter = mw->xin_info; + for(i = 0; i < mw->xin_screens; ++i) + { + if(root_x >= iter->x_org && root_x < iter->x_org + iter->width && + root_y >= iter->y_org && root_y < iter->y_org + iter->height) + { +# ifdef DEBUG + fprintf(stderr, "screen %i %ix%i+%i+%i\n", iter->screen_number, iter->width, iter->height, iter->x_org, iter->y_org); +# endif /* DEBUG */ + break; + } + iter++; + } + if(i == mw->xin_screens) + { +# ifdef DEBUG + fprintf(stderr, "unknown\n"); +# endif /* DEBUG */ + return; + } + mw->x = iter->x_org; + mw->y = iter->y_org; + mw->width = iter->width; + mw->height = iter->height; + XMoveResizeWindow(mw->dpy, mw->window, iter->x_org, iter->y_org, mw->width, mw->height); + mw->xin_active = iter; +#endif /* XINERAMA */ + mainwin_update_background(mw); +} + +void +mainwin_map(MainWin *mw) +{ + wm_set_fullscreen(mw->dpy, mw->window, mw->x, mw->y, mw->width, mw->height); + mw->pressed = 0; + XMapWindow(mw->dpy, mw->window); + XRaiseWindow(mw->dpy, mw->window); +} + +void +mainwin_unmap(MainWin *mw) +{ + if(mw->tooltip) + tooltip_unmap(mw->tooltip); + if(mw->bg_pixmap) + { + XFreePixmap(mw->dpy, mw->bg_pixmap); + mw->bg_pixmap = None; + } + XUnmapWindow(mw->dpy, mw->window); +} + +void +mainwin_destroy(MainWin *mw) +{ + if(mw->tooltip) + tooltip_destroy(mw->tooltip); + + if(mw->background != None) + XRenderFreePicture(mw->dpy, mw->background); + + if(mw->bg_pixmap != None) + XFreePixmap(mw->dpy, mw->bg_pixmap); + + if(mw->normalPicture != None) + XRenderFreePicture(mw->dpy, mw->normalPicture); + + if(mw->highlightPicture != None) + XRenderFreePicture(mw->dpy, mw->highlightPicture); + + if(mw->normalPixmap != None) + XFreePixmap(mw->dpy, mw->normalPixmap); + + if(mw->highlightPixmap != None) + XFreePixmap(mw->dpy, mw->highlightPixmap); + + XDestroyWindow(mw->dpy, mw->window); + +#ifdef XINERAMA + if(mw->xin_info) + XFree(mw->xin_info); +#endif /* XINERAMA */ + + free(mw); +} + +void +mainwin_transform(MainWin *mw, float f) +{ + mw->transform.matrix[0][0] = XDoubleToFixed(1.0 / f); + mw->transform.matrix[0][1] = 0.0; + mw->transform.matrix[0][2] = 0.0; + mw->transform.matrix[1][0] = 0.0; + mw->transform.matrix[1][1] = XDoubleToFixed(1.0 / f); + mw->transform.matrix[1][2] = 0.0; + mw->transform.matrix[2][0] = 0.0; + mw->transform.matrix[2][1] = 0.0; + mw->transform.matrix[2][2] = XDoubleToFixed(1.0); +} + +int +mainwin_handle(MainWin *mw, XEvent *ev) +{ + switch(ev->type) + { + case KeyPress: + if(ev->xkey.keycode == XKeysymToKeycode(mw->dpy, XK_q)) + return 2; + break; + case ButtonRelease: + return 3; + break; + case VisibilityNotify: + if(ev->xvisibility.state && mw->focus) + { + XSetInputFocus(mw->dpy, mw->focus->mini.window, RevertToParent, CurrentTime); + mw->focus = 0; + } + break; + default: + ; + } + return 0; +} diff --git a/mainwin.h b/mainwin.h new file mode 100644 index 0000000..0099205 --- /dev/null +++ b/mainwin.h @@ -0,0 +1,78 @@ +/* Skippy - Seduces Kids Into Perversion + * + * Copyright (C) 2004 Hyriand + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef SKIPPY_MAINWIN_H +#define SKIPPY_MAINWIN_H + +struct _Tooltip; + +struct _MainWin +{ + Display *dpy; + int screen; + Visual *visual; + Colormap colormap; + int depth; + Window root; + int damage_event_base; + + int poll_time; + Bool lazy_trans; + + Imlib_Image img_p, img_l; + int gquality; + + Window window; + Picture background; + Pixmap bg_pixmap; + int x, y; + unsigned int width, height, distance; + XRenderPictFormat *format; + XTransform transform; + + XRenderColor normalTint, highlightTint; + Pixmap normalPixmap, highlightPixmap; + Picture normalPicture, highlightPicture; + + ClientWin *pressed, *focus; + dlist *cod; + struct _Tooltip *tooltip; + + KeyCode key_act, key_up, key_down, key_left, key_right, key_enter, key_space, key_q, key_f5, key_f6, key_f7, key_f8, key_escape; + +#ifdef XINERAMA + int xin_screens; + XineramaScreenInfo *xin_info, *xin_active; +#endif /* XINERAMA */ +}; +typedef struct _MainWin MainWin; + +MainWin *mainwin_create(Display *, dlist *config); +void mainwin_destroy(MainWin *); +void mainwin_map(MainWin *); +void mainwin_unmap(MainWin *); +int mainwin_handle(MainWin *, XEvent *); +int load_image (MainWin *mw, Bool rotate, Imlib_Image rootimg); +void mainwin_update_background(MainWin *mw); +void mainwin_update(MainWin *mw); +void mainwin_transform(MainWin *mw, float f); +int event_base; + + +#endif /* SKIPPY_MAINWIN_H */ diff --git a/skippy-debian.patch b/skippy-debian.patch new file mode 100644 index 0000000..b91c75d --- /dev/null +++ b/skippy-debian.patch @@ -0,0 +1,475 @@ +--- skippy-0.5.1rc1.orig/debian/docs ++++ skippy-0.5.1rc1/debian/docs +@@ -0,0 +1 @@ ++debian/README.Debian +--- skippy-0.5.1rc1.orig/debian/menu ++++ skippy-0.5.1rc1/debian/menu +@@ -0,0 +1,2 @@ ++?package(skippy):needs="X11" section="Apps/Tools"\ ++ title="skippy" command="/usr/bin/skippy" +--- skippy-0.5.1rc1.orig/debian/control ++++ skippy-0.5.1rc1/debian/control +@@ -0,0 +1,20 @@ ++Source: skippy ++Section: x11 ++Priority: optional ++Maintainer: Niv Altivanik (Debian Packages) ++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) 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) 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) 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) 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 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\fP and ++.\" \fI\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 . ++.PP ++This manual page was written by Niv ALTIVANIK , ++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) , 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 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 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\fP and ++.\" \fI\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 . ++.PP ++This manual page was written by Niv ALTIVANIK , ++for the Debian project (but may be used by others). +--- skippy-0.5.1rc1.orig/debian/examples/99skippy ++++ skippy-0.5.1rc1/debian/examples/99skippy +@@ -0,0 +1,5 @@ ++#!/bin/sh ++# Add this file to /etc/X11/XSession.d/ if you wish the program to be ++# started at login time for all the users on your system. ++ ++/usr/bin/skippy & +--- skippy-0.5.1rc1.orig/debian/examples/skippyrc-default ++++ skippy-0.5.1rc1/debian/examples/skippyrc-default +@@ -0,0 +1,3 @@ ++# This is the Default Debian skippy configuration, it is copied to ++# /etc/X11/skippy/skippyrc. ++# Copy this to ~/.skippyrc and edit it to fit your needs diff --git a/skippy-xd.rc-default b/skippy-xd.rc-default new file mode 100644 index 0000000..45b88ab --- /dev/null +++ b/skippy-xd.rc-default @@ -0,0 +1,67 @@ +# Copy this to ~/.skippy-xd.rc and edit it to your liking +# +# Notes: +# +# - keysym can be anything XStringToKeysym can handle +# (like F11, KP_Enter or implementation specific keysyms) +# +# - colors can be anything XAllocNamedColor can handle +# (like "black" or "#000000") +# +# - distance is a relative number, and is scaled according to the scale +# factor applied to windows +# +# - fonts are Xft font descriptions +# +# - booleans are "true" or anything but "true" (-> false) +# +# - opacity is an integer in the range of 0-255 +# +# - brighness is a floating point number (with 0.0 as neutral) +# +# - if the update frequency is a negative value, the mini-windows will only +# be updated when they're explicitly rendered (like, when they gain or +# lose focus). +# +# - the 'shadowText' option can be a color or 'none', in which case the +# drop-shadow effect is disabled +# + +[general] +keysym = F11 +distance = 50 +useNetWMFullscreen = true +ignoreSkipTaskbar = true +updateFreq = 10.0 +lazyTrans = false + +[maemo] +#imgLandscape = /usr/share/skippy-xd/skippy-bg-l.jpg +#imgPortrait = /usr/share/skippy-xd/skippy-bg-p.jpg +#f7prog = "/usr/bin/osso-xterm &" +#f6prog = "/usr/bin/dbus-send --system --type=method_call --dest=com.nokia.mce /com/nokia/mce/request com.nokia.mce.request.req_tklock_mode_change string:locked &" +### Fast=0, Good=1, Best=2, Nearest=3, Bilinear=4, Convolution=5 +gQuality = 0 + + +[xinerama] +showAll = false + +[normal] +tint = black +tintOpacity = 0 +opacity = 200 + +[highlight] +tint = #101020 +tintOpacity = 64 +opacity = 255 + +[tooltip] +show = true +border = #e0e0e0 +background = #404040 +opacity = 128 +text = #e0e0e0 +textShadow = black +font = fixed-11:weight=bold diff --git a/skippy.c b/skippy.c new file mode 100644 index 0000000..03581fe --- /dev/null +++ b/skippy.c @@ -0,0 +1,463 @@ +/* Skippy - Seduces Kids Into Perversion + * + * Copyright (C) 2004 Hyriand + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "skippy.h" + +const char *f7prog; +const char *f6prog; +static int DIE_NOW = 0; + +static int +time_in_millis (void) +{ + struct timeval tp; + + gettimeofday (&tp, 0); + return(tp.tv_sec * 1000) + (tp.tv_usec / 1000); +} + +static dlist * +update_clients(MainWin *mw, dlist *clients, Bool *touched) +{ + dlist *stack, *iter; + + stack = dlist_first(wm_get_stack(mw->dpy)); + iter = clients = dlist_first(clients); + + if(touched) + *touched = False; + + /* Terminate clients that are no longer managed */ + while(iter) + { + ClientWin *cw = (ClientWin *)iter->data; + if(! dlist_find_data(stack, (void *)cw->client.window)) + { + dlist *tmp = iter->next; + clientwin_destroy((ClientWin *)iter->data, True); + clients = dlist_remove(iter); + iter = tmp; + if(touched) + *touched = True; + continue; + } + clientwin_update(cw); + iter = iter->next; + } + + /* Add new clients */ + for(iter = dlist_first(stack); iter; iter = iter->next) + { + ClientWin *cw = (ClientWin*)dlist_find(clients, clientwin_cmp_func, iter->data); + if(! cw && (Window)iter->data != mw->window) + { + cw = clientwin_create(mw, (Window)iter->data); + if(! cw) + continue; + clients = dlist_add(clients, cw); + clientwin_update(cw); + if(touched) + *touched = True; + } + } + + dlist_free(stack); + + return clients; +} + +static dlist * +do_layout(MainWin *mw, dlist *clients, Window focus, Window leader) +{ + CARD32 desktop = wm_get_current_desktop(mw->dpy); + unsigned int width, height; + float factor; + int xoff, yoff; + dlist *iter, *tmp; + + /* Update the client table, pick the ones we want and sort them */ + clients = update_clients(mw, clients, 0); + + if(mw->cod) + dlist_free(mw->cod); + + tmp = dlist_first(dlist_find_all(clients, (dlist_match_func)clientwin_validate_func, &desktop)); + if(leader != None) + { + mw->cod = dlist_first(dlist_find_all(tmp, clientwin_check_group_leader_func, (void*)&leader)); + dlist_free(tmp); + } else + mw->cod = tmp; + + if(! mw->cod) + return clients; + + dlist_sort(mw->cod, clientwin_sort_func, 0); + + /* Move the mini windows around */ + layout_run(mw, mw->cod, &width, &height); + factor = (float)(mw->width - 100) / width; + if(factor * height > mw->height - 100) + factor = (float)(mw->height - 100) / height; + + xoff = (mw->width - (float)width * factor) / 2; + yoff = (mw->height - (float)height * factor) / 2; + mainwin_transform(mw, factor); + for(iter = mw->cod; iter; iter = iter->next) + clientwin_move((ClientWin*)iter->data, factor, xoff, yoff); + + /* Get the currently focused window and select which mini-window to focus */ + iter = dlist_find(mw->cod, clientwin_cmp_func, (void *)focus); + if(! iter) + iter = mw->cod; + mw->focus = (ClientWin*)iter->data; + mw->focus->focused = 1; + + /* Map the client windows */ + for(iter = mw->cod; iter; iter = iter->next) + clientwin_map((ClientWin*)iter->data); + XWarpPointer(mw->dpy, None, mw->focus->mini.window, 0, 0, 0, 0, mw->focus->mini.width / 2, mw->focus->mini.height / 2); + + return clients; +} + +static dlist * +skippy_run(MainWin *mw, dlist *clients, Window focus, Window leader, Bool all_xin) +{ + XEvent ev; + int die = 0; + Bool refocus = False, refroot = False ; + int last_rendered; + XWindowAttributes rootattr; + + /* Update the main window's geometry (and Xinerama info if applicable) */ + mainwin_update(mw); +#ifdef XINERAMA + if(all_xin) + mw->xin_active = 0; +#else /* ! XINERAMA */ + if(all_xin); +#endif /* XINERAMA */ + + /* Map the main window and run our event loop */ + if(mw->lazy_trans) + { + mainwin_map(mw); + XFlush(mw->dpy); + } + + XGetWindowAttributes(mw->dpy, mw->root, &rootattr); + if(mw->width != rootattr.width) + { + /*printf("rotated\n");*/ + mw->width = rootattr.width; + mw->height = rootattr.height; + mainwin_update_background(mw); + } + + clients = do_layout(mw, clients, focus, leader); + + + if(! mw->cod) + return clients; + + + /* Map the main window and run our event loop */ + if(! mw->lazy_trans) + mainwin_map(mw); + + last_rendered = time_in_millis(); + while(! die) { + int i, j, now, timeout, mh; + int move_x = -1, move_y = -1; + struct pollfd r_fd; + + XFlush(mw->dpy); + + r_fd.fd = ConnectionNumber(mw->dpy); + r_fd.events = POLLIN; + if(mw->poll_time > 0) + timeout = MAX(0, mw->poll_time + last_rendered - time_in_millis()); + else + timeout = -1; + i = poll(&r_fd, 1, timeout); + + now = time_in_millis(); + if(now >= last_rendered + mw->poll_time) + { + REDUCE(if( ((ClientWin*)iter->data)->damaged ) clientwin_repair(iter->data), mw->cod); + last_rendered = now; + } + + i = XPending(mw->dpy); + for(j = 0; j < i; ++j) + { + XNextEvent(mw->dpy, &ev); + if (ev.type == MotionNotify) + { + move_x = ev.xmotion.x_root; + move_y = ev.xmotion.y_root; + } + else if(ev.type == DestroyNotify || ev.type == UnmapNotify) { + dlist *iter = dlist_find(clients, clientwin_cmp_func, (void *)ev.xany.window); + if(iter) + { + ClientWin *cw = (ClientWin *)iter->data; + clients = dlist_first(dlist_remove(iter)); + iter = dlist_find(mw->cod, clientwin_cmp_func, (void *)ev.xany.window); + if(iter) + mw->cod = dlist_first(dlist_remove(iter)); + clientwin_destroy(cw, True); + if(! mw->cod) + { + die = 1; + break; + } + } + } + else if (mw->poll_time >= 0 && ev.type == mw->damage_event_base + XDamageNotify) + { + XDamageNotifyEvent *d_ev = (XDamageNotifyEvent *)&ev; + dlist *iter = dlist_find(mw->cod, clientwin_cmp_func, (void *)d_ev->drawable); + if(iter) + { + if(mw->poll_time == 0) + clientwin_repair((ClientWin *)iter->data); + else + ((ClientWin *)iter->data)->damaged = True; + } + + } + else if(ev.type == KeyRelease && ev.xkey.keycode == mw->key_escape) + { + refocus = True; + die = 1; + break; + } + else if(ev.type == KeyRelease && ev.xkey.keycode == mw->key_f6) + { + system(f6prog); + refocus = True; + die = 1; + break; + } + else if(ev.type == KeyRelease && ev.xkey.keycode == mw->key_f7) + { + system(f7prog); + refocus = True; + die = 1; + break; + } + else if(ev.type == KeyRelease && ev.xkey.keycode == mw->key_q) + { + DIE_NOW = 1; + die = 1; + break; + } + else if(ev.type == KeyPress && ev.xkey.keycode == mw->key_f5 ) + { + refroot = True; + die = 1; + break; + } + else if(ev.xany.window == mw->window) + mh = mainwin_handle(mw, &ev); + if(mh == 0 || mh == 1 || mh == 2){ + die = mh; + }else if(mh == 3){ + die = 1; + refroot = True; + } +/* else if(ev.type == PropertyNotify) + { + if(ev.xproperty.atom == ESETROOT_PMAP_ID || ev.xproperty.atom == _XROOTPMAP_ID) + { + mainwin_update_background(mw); + REDUCE(clientwin_render((ClientWin *)iter->data), mw->cod); + } + + }*/ + else if(mw->tooltip && ev.xany.window == mw->tooltip->window) + tooltip_handle(mw->tooltip, &ev); + else + { + dlist *iter; + for(iter = mw->cod; iter; iter = iter->next) + { + ClientWin *cw = (ClientWin *)iter->data; + if(cw->mini.window == ev.xany.window) + { + die = clientwin_handle(cw, &ev); + if(die) + break; + } + } + if(die) + break; + } + + } + + if(mw->tooltip && move_x != -1) + tooltip_move(mw->tooltip, move_x + 20, move_y + 20); + + } + + /* Unmap the main window and clean up */ + mainwin_unmap(mw); + XFlush(mw->dpy); + + REDUCE(clientwin_unmap((ClientWin*)iter->data), mw->cod); + dlist_free(mw->cod); + mw->cod = 0; + + if(die == 2) + DIE_NOW = 1; + + if(refocus) + XSetInputFocus(mw->dpy, focus, RevertToPointerRoot, CurrentTime); + + if(refroot) + client_msg(mw->dpy, DefaultRootWindow(mw->dpy), "_NET_SHOWING_DESKTOP", + 1, 0, 0, 0, 0); + + + return clients; +} + +int +main(void) +{ + dlist *clients = 0, *config = 0; + Display *dpy = XOpenDisplay(NULL); + MainWin *mw; + KeyCode keycode; + KeySym keysym; + const char *tmp, *homedir; + char cfgpath[8192]; + Bool invertShift = False; + const char *img_l_file, *img_p_file; + Window focused = None; + int revert_to; + + if(! dpy) { + fprintf(stderr, "FATAL: Couldn't connect to display.\n"); + return -1; + } + + wm_get_atoms(dpy); + + if(! wm_check(dpy)) { + fprintf(stderr, "FATAL: WM not NETWM or GNOME WM Spec compliant.\n"); + return -1; + } + + homedir = getenv("HOME"); + if(homedir) { + snprintf(cfgpath, 8191, "%s/%s", homedir, ".skippy-xd.rc"); + config = config_load(cfgpath); + } + else + fprintf(stderr, "WARNING: $HOME not set, not loading config.\n"); + + wm_use_netwm_fullscreen(strcasecmp("true", config_get(config, "general", "useNETWMFullscreen", "true")) == 0); + wm_ignore_skip_taskbar(strcasecmp("true", config_get(config, "general", "ignoreSkipTaskbar", "false")) == 0); + + XGetInputFocus(dpy, &focused, &revert_to); + + mw = mainwin_create(dpy, config); + if(! mw) + { + fprintf(stderr, "FATAL: Couldn't create main window.\n"); + config_free(config); + XCloseDisplay(mw->dpy); + return -1; + } + + client_msg(dpy, focused, "_NET_ACTIVE_WINDOW", 0, 0, 0, 0, 0); + XSetInputFocus(dpy, focused, RevertToParent, CurrentTime); + + + f6prog = config_get(config, "maemo", "f6prog", "/usr/bin/dbus-send --system --type=method_call --dest=com.nokia.mce /com/nokia/mce/request com.nokia.mce.request.req_tklock_mode_change string:locked &"); + f7prog = config_get(config, "maemo", "f7prog", "/usr/bin/osso-xterm &"); + mw->gquality = atoi(config_get(config, "maemo", "gQuality", "0")); + printf("Filter: %d\n", mw->gquality); + + img_l_file = config_get(config, "maemo", "imgLandscape", "/usr/share/skippy-xd/skippy-bg-l.jpg"); + printf("ImgL: %s\n", img_l_file); + img_p_file = config_get(config, "maemo", "imgPortrait", "/usr/share/skippy-xd/skippy-bg-p.jpg"); + printf("ImgP: %s\n", img_p_file); + + mw->img_l = imlib_load_image(img_l_file); + mw->img_p = imlib_load_image(img_p_file); + + invertShift = strcasecmp("true", config_get(config, "xinerama", "showAll", "false")) == 0; + + tmp = config_get(config, "general", "keysym", "F11"); + keysym = XStringToKeysym(tmp); + if(keysym == NoSymbol) + { + fprintf(stderr, "FATAL: Couldn't look up keysym for '%s', bailing out.\n", tmp); + config_free(config); + XCloseDisplay(mw->dpy); + return -1; + } + +/* XSelectInput(mw->dpy, mw->root, ButtonReleaseMask | ButtonPressMask | PropertyChangeMask);*/ + + +/* XSelectInput(mw->dpy, mw->root, PropertyChangeMask);*/ + XSelectInput(mw->dpy, mw->window, ButtonReleaseMask | ButtonPressMask ); + + keycode = XKeysymToKeycode(mw->dpy, keysym); + XGrabKey(mw->dpy, keycode, AnyModifier, mw->root, False, GrabModeAsync, GrabModeAsync); + while(! DIE_NOW) + { + XEvent ev; + XNextEvent(mw->dpy, &ev); + /*if(ev.type == PropertyNotify && (ev.xproperty.atom == ESETROOT_PMAP_ID || ev.xproperty.atom == _XROOTPMAP_ID)) + mainwin_update_background(mw); + else */if(ev.type == KeyPress && ev.xkey.keycode == keycode) + { + Window leader = None, focused = wm_get_focused(mw->dpy); + Bool shifted = (ev.xkey.state & ShiftMask) ? ! invertShift : invertShift; + + if(ev.xkey.state & Mod1Mask) + { + if(focused != None) + leader = wm_get_group_leader(mw->dpy, focused); + if(! leader) + continue; + } + clients = skippy_run(mw, clients, focused, leader, shifted); + } + } + + dlist_free_with_func(clients, (dlist_free_func)clientwin_destroy); + + XFlush(mw->dpy); + + mainwin_destroy(mw); + + XSync(dpy, True); + XCloseDisplay(dpy); + config_free(config); + + return 0; +} diff --git a/skippy.h b/skippy.h new file mode 100644 index 0000000..ddcad1d --- /dev/null +++ b/skippy.h @@ -0,0 +1,72 @@ +/* Skippy - Seduces Kids Into Perversion + * + * Copyright (C) 2004 Hyriand + * + * 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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef XINERAMA +# include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +#define MAX(a,b) ((a > b) ? a : b) +#define MIN(a,b) ((a > b) ? b : a) +#define REDUCE(statement, l) \ +{ \ + dlist *iter = dlist_first(l); \ + for(; iter; iter = iter->next) \ + statement; \ +} + + +#include "dlist.h" +#include "wm.h" +#include "clientwin.h" +#include "mainwin.h" +#include "layout.h" +#include "focus.h" +#include "config.h" +#include "tooltip.h" + +#endif /* SKIPPY_H */ diff --git a/tooltip.c b/tooltip.c new file mode 100644 index 0000000..8fc29c6 --- /dev/null +++ b/tooltip.c @@ -0,0 +1,220 @@ +/* Skippy - Seduces Kids Into Perversion + * + * Copyright (C) 2004 Hyriand + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "skippy.h" + +void +tooltip_destroy(Tooltip *tt) +{ + if(tt->text) + free(tt->text); + if(tt->font) + XftFontClose(tt->mainwin->dpy, tt->font); + if(tt->draw) + XftDrawDestroy(tt->draw); + if(tt->color.pixel != None) + XftColorFree(tt->mainwin->dpy, + tt->mainwin->visual, + tt->mainwin->colormap, + &tt->color); + if(tt->background.pixel != None) + XftColorFree(tt->mainwin->dpy, + tt->mainwin->visual, + tt->mainwin->colormap, + &tt->background); + if(tt->border.pixel != None) + XftColorFree(tt->mainwin->dpy, + tt->mainwin->visual, + tt->mainwin->colormap, + &tt->border); + if(tt->shadow.pixel != None) + XftColorFree(tt->mainwin->dpy, + tt->mainwin->visual, + tt->mainwin->colormap, + &tt->shadow); + if(tt->window != None) + XDestroyWindow(tt->mainwin->dpy, tt->window); + + free(tt); +} + +Tooltip * +tooltip_create(MainWin *mw, dlist *config) +{ + Tooltip *tt; + XSetWindowAttributes attr; + const char *tmp; + long int tmp_l; + + tt = (Tooltip *)malloc(sizeof(Tooltip)); + if(! tt) + return 0; + + tt->mainwin = mw; + tt->window = None; + tt->font = 0; + tt->draw = 0; + tt->text = 0; + tt->color.pixel = tt->background.pixel = tt->border.pixel = tt->shadow.pixel = None; + + attr.override_redirect = True; + attr.border_pixel = None; + attr.background_pixel = None; + attr.event_mask = ExposureMask; + attr.colormap = mw->colormap; + + tt->window = XCreateWindow(mw->dpy, mw->root, + 0, 0, 1, 1, 0, + mw->depth, InputOutput, mw->visual, + CWBorderPixel|CWBackPixel|CWOverrideRedirect|CWEventMask|CWColormap, + &attr); + if(tt->window == None) + { + fprintf(stderr, "WARNING: Couldn't create tooltip window.\n"); + tooltip_destroy(tt); + return 0; + } + + tmp = config_get(config, "tooltip", "border", "#e0e0e0"); + if(! XftColorAllocName(mw->dpy, mw->visual, mw->colormap, tmp, &tt->border)) + { + fprintf(stderr, "WARNING: Invalid color '%s'.\n", tmp); + tooltip_destroy(tt); + return 0; + } + + tmp = config_get(config, "tooltip", "background", "#404040"); + if(! XftColorAllocName(mw->dpy, mw->visual, mw->colormap, tmp, &tt->background)) + { + fprintf(stderr, "WARNING: Invalid color '%s'.\n", tmp); + tooltip_destroy(tt); + return 0; + } + + tmp = config_get(config, "tooltip", "opacity", "128"); + tmp_l = MIN(MAX(0, strtol(tmp, 0, 0) * 256), 65535); + tt->background.color.alpha = tmp_l; + tt->border.color.alpha = tmp_l; + + tmp = config_get(config, "tooltip", "text", "#e0e0e0"); + if(! XftColorAllocName(mw->dpy, mw->visual, mw->colormap, tmp, &tt->color)) + { + fprintf(stderr, "WARNING: Couldn't allocate color '%s'.\n", tmp); + tooltip_destroy(tt); + return 0; + } + + tmp = config_get(config, "tooltip", "textShadow", "black"); + if(strcasecmp(tmp, "none") != 0) + { + if(! XftColorAllocName(mw->dpy, mw->visual, mw->colormap, tmp, &tt->shadow)) + { + fprintf(stderr, "WARNING: Couldn't allocate color '%s'.\n", tmp); + tooltip_destroy(tt); + return 0; + } + } + + tt->draw = XftDrawCreate(mw->dpy, tt->window, mw->visual, mw->colormap); + if(! tt->draw) + { + fprintf(stderr, "WARNING: Couldn't create Xft draw surface.\n"); + tooltip_destroy(tt); + return 0; + } + + tt->font = XftFontOpenName(mw->dpy, mw->screen, config_get(config, "tooltip", "font", "fixed-11:weight=bold")); + if(! tt->font) + { + fprintf(stderr, "WARNING: Couldn't open Xft font.\n"); + tooltip_destroy(tt); + return 0; + } + + tt->font_height = tt->font->ascent + tt->font->descent; + + return tt; +} + +void +tooltip_map(Tooltip *tt, int x, int y, const FcChar8 *text, int len) +{ + XUnmapWindow(tt->mainwin->dpy, tt->window); + + XftTextExtents8(tt->mainwin->dpy, tt->font, text, len, &tt->extents); + + tt->width = tt->extents.width + 8; + tt->height = tt->font_height + 5 + (tt->shadow.pixel ? 2 : 0); + XResizeWindow(tt->mainwin->dpy, tt->window, tt->width, tt->height); + tooltip_move(tt, x, y); + + if(tt->text) + free(tt->text); + + tt->text = (FcChar8 *)malloc(len); + memcpy(tt->text, text, len); + + tt->text_len = len; + + XMapWindow(tt->mainwin->dpy, tt->window); + XRaiseWindow(tt->mainwin->dpy, tt->window); +} + +void +tooltip_move(Tooltip *tt, int x, int y) +{ + if(x + tt->extents.width + 9 > tt->mainwin->x + tt->mainwin->width) + x = tt->mainwin->x + tt->mainwin->width - tt->extents.width - 9; + x = MAX(0, x); + + if(y + tt->extents.height + 8 > tt->mainwin->y + tt->mainwin->height) + y = tt->mainwin->height + tt->mainwin->y - tt->extents.height - 8; + y = MAX(0, y); + + XMoveWindow(tt->mainwin->dpy, tt->window, x, y); +} + +void +tooltip_unmap(Tooltip *tt) +{ + XUnmapWindow(tt->mainwin->dpy, tt->window); + if(tt->text) + free(tt->text); + tt->text = 0; + tt->text_len = 0; +} + +void +tooltip_handle(Tooltip *tt, XEvent *ev) +{ + if(! tt->text) + return; + + if(ev->type == Expose && ev->xexpose.count == 0) + { + XftDrawRect(tt->draw, &tt->border, 0, 0, tt->width, 1); + XftDrawRect(tt->draw, &tt->border, 0, 1, 1, tt->height - 2); + XftDrawRect(tt->draw, &tt->border, 0, tt->height - 1, tt->width, 1); + XftDrawRect(tt->draw, &tt->border, tt->width - 1, 1, 1, tt->height - 2); + XftDrawRect(tt->draw, &tt->background, 1, 1, tt->width - 2, tt->height - 2); + if(tt->shadow.pixel) + XftDrawString8(tt->draw, &tt->shadow, tt->font, 6, 3 + tt->extents.y + (tt->font_height - tt->extents.y) / 2, tt->text, tt->text_len); + XftDrawString8(tt->draw, &tt->color, tt->font, 4, 1 + tt->extents.y + (tt->font_height - tt->extents.y) / 2, tt->text, tt->text_len); + } +} diff --git a/tooltip.h b/tooltip.h new file mode 100644 index 0000000..e6d08b0 --- /dev/null +++ b/tooltip.h @@ -0,0 +1,47 @@ +/* Skippy - Seduces Kids Into Perversion + * + * Copyright (C) 2004 Hyriand + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef SKIPPY_TOOLTIP_H +#define SKIPPY_TOOLTIP_H + +struct _Tooltip { + MainWin *mainwin; + + unsigned int width, height; + + Window window; + XftFont *font; + XftDraw *draw; + XftColor color, background, border, shadow; + XGlyphInfo extents; + int font_height; + + FcChar8 *text; + int text_len; +}; +typedef struct _Tooltip Tooltip; + +Tooltip *tooltip_create(MainWin *, dlist *); +void tooltip_destroy(Tooltip *); +void tooltip_map(Tooltip *, int, int, const FcChar8 *, int); +void tooltip_unmap(Tooltip *); +void tooltip_handle(Tooltip *, XEvent *); +void tooltip_move(Tooltip *, int, int); + +#endif /* SKIPPY_TOOLTIP_H */ diff --git a/wm.c b/wm.c new file mode 100644 index 0000000..4b8bb31 --- /dev/null +++ b/wm.c @@ -0,0 +1,602 @@ +/* Skippy - Seduces Kids Into Perversion + * + * Copyright (C) 2004 Hyriand + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "skippy.h" + +#define WM_PERSONALITY_NETWM 0 +#define WM_PERSONALITY_GNOME 1 + +/* From WindowMaker's gnome.c */ +#define WIN_HINTS_SKIP_FOCUS (1<<0) /*"alt-tab" skips this win*/ +#define WIN_HINTS_SKIP_WINLIST (1<<1) /*do not show in window list*/ +#define WIN_HINTS_SKIP_TASKBAR (1<<2) /*do not show on taskbar*/ +#define WIN_HINTS_GROUP_TRANSIENT (1<<3) /*Reserved - definition is unclear*/ +#define WIN_HINTS_FOCUS_ON_CLICK (1<<4) /*app only accepts focus if clicked*/ +#define WIN_HINTS_DO_NOT_COVER (1<<5) /* attempt to not cover this window */ + + +#define WIN_STATE_STICKY (1<<0) /*everyone knows sticky*/ +#define WIN_STATE_MINIMIZED (1<<1) /*Reserved - definition is unclear*/ +#define WIN_STATE_MAXIMIZED_VERT (1<<2) /*window in maximized V state*/ +#define WIN_STATE_MAXIMIZED_HORIZ (1<<3) /*window in maximized H state*/ +#define WIN_STATE_HIDDEN (1<<4) /*not on taskbar but window visible*/ +#define WIN_STATE_SHADED (1<<5) /*shaded (MacOS / Afterstep style)*/ +/* these are bogus states defined in "the spec" */ +#define WIN_STATE_HID_WORKSPACE (1<<6) /*not on current desktop*/ +#define WIN_STATE_HID_TRANSIENT (1<<7) /*owner of transient is hidden*/ +#define WIN_STATE_FIXED_POSITION (1<<8) /*window is fixed in position even*/ +#define WIN_STATE_ARRANGE_IGNORE (1<<9) /*ignore for auto arranging*/ + + +static int WM_PERSONALITY = WM_PERSONALITY_NETWM, + NETWM_HAS_FULLSCREEN = 0, + IGNORE_SKIP_TASKBAR = 0; + +void +wm_get_atoms(Display *dpy) +{ + XA_WM_STATE = XInternAtom(dpy, "WM_STATE", 0); + WM_CLIENT_LEADER = XInternAtom(dpy, "WM_CLIENT_LEADER", 0); + XA_UTF8_STRING = XInternAtom(dpy, "UTF8_STRING", 0); + + _XROOTPMAP_ID = XInternAtom(dpy, "_XROOTPMAP_ID", 0); + ESETROOT_PMAP_ID = XInternAtom(dpy, "ESETROOT_PMAP_ID", 0); + + _NET_SUPPORTING_WM_CHECK = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", 0); + _NET_SUPPORTED = XInternAtom(dpy, "_NET_SUPPORTED", 0); + _NET_NUMBER_OF_DESKTOPS = XInternAtom(dpy, "_NET_NUMBER_OF_DESKTOPS", 0); + _NET_CLIENT_LIST = XInternAtom(dpy, "_NET_CLIENT_LIST", 0); + _NET_CLIENT_LIST_STACKING = XInternAtom(dpy, "_NET_CLIENT_LIST_STACKING", 0); + _NET_CURRENT_DESKTOP = XInternAtom(dpy, "_NET_CURRENT_DESKTOP", 0); + _NET_WM_DESKTOP = XInternAtom(dpy, "_NET_WM_DESKTOP", 0); + _NET_WM_STATE = XInternAtom(dpy, "_NET_WM_STATE", 0); + _NET_WM_STATE_HIDDEN = XInternAtom(dpy, "_NET_WM_STATE_HIDDEN", 0); + _NET_WM_STATE_SKIP_TASKBAR = XInternAtom(dpy, "_NET_WM_STATE_SKIP_TASKBAR", 0); + _NET_WM_STATE_SKIP_PAGER = XInternAtom(dpy, "_NET_WM_STATE_SKIP_PAGER", 0); + _NET_WM_STATE_FULLSCREEN = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", 0); + _NET_WM_STATE_SHADED = XInternAtom(dpy, "_NET_WM_STATE_SHADED", 0); + _NET_WM_STATE_ABOVE = XInternAtom(dpy, "_NET_WM_STATE_ABOVE", 0); + _NET_WM_STATE_STICKY = XInternAtom(dpy, "_NET_WM_STATE_STICKY", 0); + _NET_WM_WINDOW_TYPE = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", 0); + _NET_WM_WINDOW_TYPE_DESKTOP = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DESKTOP", 0); + _NET_WM_WINDOW_TYPE_DOCK = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DOCK", 0); + _NET_WM_VISIBLE_NAME = XInternAtom(dpy, "_NET_WM_VISIBLE_NAME", 0); + _NET_WM_NAME = XInternAtom(dpy, "_NET_WM_VISIBLE_NAME", 0); + + _WIN_SUPPORTING_WM_CHECK = XInternAtom(dpy, "_WIN_SUPPORTING_WM_CHECK", 0); + _WIN_WORKSPACE = XInternAtom(dpy, "_WIN_WORKSPACE", 0); + _WIN_WORKSPACE_COUNT = XInternAtom(dpy, "_WIN_WORKSPACE_COUNT", 0); + _WIN_PROTOCOLS = XInternAtom(dpy, "_WIN_PROTOCOLS", 0); + _WIN_CLIENT_LIST = XInternAtom(dpy, "_WIN_CLIENT_LIST", 0); + _WIN_STATE = XInternAtom(dpy, "_WIN_STATE", 0); + _WIN_HINTS = XInternAtom(dpy, "_WIN_HINTS", 0); +} + +char +wm_check_netwm(Display *dpy) +{ + Window wm_check; + unsigned char *data, *data2; + + int status, real_format; + Atom real_type; + unsigned long items_read, items_left, i; + + char req = 0; + + status = XGetWindowProperty(dpy, DefaultRootWindow(dpy), _NET_SUPPORTING_WM_CHECK, + 0L, 1L, False, XA_WINDOW, &real_type, &real_format, + &items_read, &items_left, &data); + if(status != Success || ! items_read) { + if(status == Success) + XFree(data); + return 0; + } + + wm_check = ((Window*)data)[0]; + XFree(data); + + status = XGetWindowProperty(dpy, wm_check, _NET_SUPPORTING_WM_CHECK, + 0L, 1L, False, XA_WINDOW, &real_type, &real_format, + &items_read, &items_left, &data); + + if(status != Success && ! items_read) { + if(status == Success) + XFree(data); + return 0; + } + + if(wm_check != ((Window*)data)[0]) { + XFree(data); + return 0; + } + + XFree(data); + + status = XGetWindowProperty(dpy, DefaultRootWindow(dpy), _NET_SUPPORTED, + 0L, 8192L, False, XA_ATOM, &real_type, &real_format, + &items_read, &items_left, &data2); + + if(status != Success || ! items_read) { + if(status == Success) + XFree(data2); + return 0; + } + + for(i = 0; i < items_read; i++) { + if(((Atom*)data2)[i] == _NET_NUMBER_OF_DESKTOPS) + req |= 1; + else if(((Atom*)data2)[i] == _NET_CURRENT_DESKTOP) + req |= 2; + else if(((Atom*)data2)[i] == _NET_WM_STATE) + req |= 4; + else if(((Atom*)data2)[i] == _NET_CLIENT_LIST) + req |= 8; + else if(((Atom*)data2)[i] == _NET_CLIENT_LIST_STACKING) + req |= 16; + else if(((Atom*)data2)[i] == _NET_WM_STATE_FULLSCREEN) + NETWM_HAS_FULLSCREEN = 1; + } + XFree(data2); + if(req & 16) { + req |= 8; + _NET_CLIENT_LIST = _NET_CLIENT_LIST_STACKING; + } + + return ((req & 15) == 15); +} + +char +wm_check_gnome(Display *dpy) +{ + Window wm_check; + unsigned char *data, *data2; + + int status, real_format; + Atom real_type; + unsigned long items_read, items_left, i; + + char req = 0; + + WM_PERSONALITY = WM_PERSONALITY_GNOME; + + status = XGetWindowProperty(dpy, DefaultRootWindow(dpy), _WIN_SUPPORTING_WM_CHECK, + 0L, 1L, False, XA_CARDINAL, &real_type, &real_format, + &items_read, &items_left, &data); + if(status != Success || ! items_read) { + if(status == Success) + XFree(data); + return 0; + } + + wm_check = ((Window*)data)[0]; + XFree(data); + + status = XGetWindowProperty(dpy, wm_check, _WIN_SUPPORTING_WM_CHECK, + 0L, 1L, False, XA_CARDINAL, &real_type, &real_format, + &items_read, &items_left, &data); + + if(status != Success && ! items_read) { + if(status == Success) + XFree(data); + return 0; + } + + if(wm_check != ((Window*)data)[0]) { + XFree(data); + return 0; + } + + XFree(data); + + status = XGetWindowProperty(dpy, DefaultRootWindow(dpy), _WIN_PROTOCOLS, + 0L, 8192L, False, XA_ATOM, &real_type, &real_format, + &items_read, &items_left, &data2); + + if(status != Success || ! items_read) { + if(status == Success) + XFree(data2); + return 0; + } + + for(i = 0; i < items_read; i++) { + if(((Atom*)data2)[i] == _WIN_WORKSPACE) + req |= 1; + else if(((Atom*)data2)[i] == _WIN_WORKSPACE_COUNT) + req |= 2; + else if(((Atom*)data2)[i] == _WIN_STATE) + req |= 4; + else if(((Atom*)data2)[i] == _WIN_CLIENT_LIST) + req |= 8; + } + XFree(data2); + + return ((req & 15) == 15); +} + +char +wm_check(Display *dpy) +{ + return wm_check_netwm(dpy) || wm_check_gnome(dpy); +} + +dlist * +wm_get_stack(Display *dpy) +{ + dlist *l = 0; + unsigned char *data; + int status, real_format; + Atom real_type; + unsigned long items_read, items_left, i; + + if(WM_PERSONALITY == WM_PERSONALITY_NETWM) + status = XGetWindowProperty(dpy, DefaultRootWindow(dpy), _NET_CLIENT_LIST, + 0L, 8192L, False, XA_WINDOW, &real_type, &real_format, + &items_read, &items_left, &data); + else + status = XGetWindowProperty(dpy, DefaultRootWindow(dpy), _WIN_CLIENT_LIST, + 0L, 8192L, False, XA_CARDINAL, &real_type, &real_format, + &items_read, &items_left, &data); + + if(status != Success) + return 0; + + for(i = 0; i < items_read; i++) + l = dlist_add(l, (void*)((CARD32*)data)[i]); + + XFree(data); + + return l; +} + +Pixmap +wm_get_root_pmap(Display *dpy) +{ + Pixmap rootpmap = None; + unsigned char *data; + int status, real_format; + Atom real_type; + unsigned long items_read, items_left; + + status = XGetWindowProperty(dpy, DefaultRootWindow(dpy), _XROOTPMAP_ID, + 0L, 1L, False, XA_PIXMAP, &real_type, &real_format, + &items_read, &items_left, &data); + if(status != Success) { + status = XGetWindowProperty(dpy, DefaultRootWindow(dpy), ESETROOT_PMAP_ID, + 0L, 1L, False, XA_PIXMAP, &real_type, &real_format, + &items_read, &items_left, &data); + if(status != Success) + return None; + } + + if(items_read) + rootpmap = ((Pixmap*)data)[0]; + + XFree(data); + + return rootpmap; +} + +CARD32 +wm_get_current_desktop(Display *dpy) +{ + CARD32 desktop = 1; + /*unsigned char *data; + int status, real_format; + Atom real_type; + unsigned long items_read, items_left; + + status = XGetWindowProperty(dpy, DefaultRootWindow(dpy), + (WM_PERSONALITY == WM_PERSONALITY_NETWM) ? _NET_CURRENT_DESKTOP : _WIN_WORKSPACE, + 0L, 1L, False, XA_CARDINAL, &real_type, &real_format, + &items_read, &items_left, &data); + if(status != Success) + return 0; + if(items_read) + desktop = ((CARD32*)data)[0]; + XFree(data);*/ + + return desktop; +} + +FcChar8 * +wm_get_window_title(Display *dpy, Window window, int *length_return) +{ + unsigned char *data; + FcChar8 *ret = 0; + int status, real_format; + Atom real_type; + unsigned long items_read, items_left; + + *length_return = 0; + + status = XGetWindowProperty(dpy, window, _NET_WM_VISIBLE_NAME, + 0, 8192, False, XA_UTF8_STRING, &real_type, &real_format, + &items_read, &items_left, &data); + if(status != Success || items_read == 0) + { + if(status == Success) + XFree(data); + status = XGetWindowProperty(dpy, window, _NET_WM_NAME, + 0, 8192, False, XA_UTF8_STRING, &real_type, &real_format, + &items_read, &items_left, &data); + } + if(status != Success || items_read == 0) + { + if(status == Success) + XFree(data); + status = XGetWindowProperty(dpy, window, XA_WM_NAME, + 0, 8192, False, XA_STRING, &real_type, &real_format, + &items_read, &items_left, &data); + } + if(status != Success) + return 0; + + if(items_read) + { + ret = (FcChar8 *)malloc(items_read); + memcpy(ret, data, items_read); + *length_return = items_read; + } + + XFree(data); + + return ret; +} + +Window +wm_get_group_leader(Display *dpy, Window window) +{ + unsigned char *data; + int status, real_format; + Atom real_type; + unsigned long items_read, items_left; + Window leader = None; + + status = XGetWindowProperty(dpy, window, WM_CLIENT_LEADER, + 0, 1, False, XA_WINDOW, &real_type, &real_format, + &items_read, &items_left, &data); + + if(status != Success) + { + XWMHints *hints = XGetWMHints(dpy, window); + if(! hints) + return None; + + if(hints->flags & WindowGroupHint) + leader = hints->window_group; + + return leader; + } + + if(items_read) + leader = ((Window*)data)[0]; + + XFree(data); + + return leader; +} + +void +wm_use_netwm_fullscreen(Bool b) +{ + NETWM_HAS_FULLSCREEN = b ? NETWM_HAS_FULLSCREEN : False; +} + +void +wm_ignore_skip_taskbar(Bool b) +{ + IGNORE_SKIP_TASKBAR = b; +} + +void +wm_set_fullscreen(Display *dpy, Window window, int x, int y, unsigned int width, unsigned int height) +{ + if(WM_PERSONALITY == WM_PERSONALITY_NETWM && NETWM_HAS_FULLSCREEN) + { + Atom props[6]; + CARD32 desktop = (CARD32)0; + + props[0] = _NET_WM_STATE_FULLSCREEN; + props[1] = _NET_WM_STATE_SKIP_TASKBAR; + props[2] = _NET_WM_STATE_SKIP_PAGER; + props[3] = _NET_WM_STATE_ABOVE; + props[4] = _NET_WM_STATE_STICKY; + props[5] = 0; + XChangeProperty(dpy, window, _NET_WM_STATE, XA_ATOM, 32, PropModeReplace, (unsigned char*)props, 5); + XChangeProperty(dpy, window, _NET_WM_DESKTOP, XA_CARDINAL, 32, PropModeReplace, (unsigned char*)&desktop, 1); + } + else + { + XSetWindowAttributes wattr; + wattr.override_redirect = True; + XChangeWindowAttributes(dpy, window, CWOverrideRedirect, &wattr); + XMoveResizeWindow(dpy, window, x, y, width, height); + } +} + +int +wm_validate_window(Display *dpy, Window win) +{ + unsigned char *data; + Atom *atoms; + int status, real_format; + Atom real_type; + unsigned long items_read, items_left, i; + int result = 1; + + if(WM_PERSONALITY == WM_PERSONALITY_NETWM) + { + status = XGetWindowProperty(dpy, win, _NET_WM_STATE, + 0L, 8192L, False, XA_ATOM, &real_type, &real_format, + &items_read, &items_left, &data); + + if(status != Success) + return 0; + + atoms = (Atom *)data; + + for(i = 0; result && i < items_read; i++) { + if(atoms[i] == _NET_WM_STATE_HIDDEN) + result = 0; + else if(! IGNORE_SKIP_TASKBAR && atoms[i] == _NET_WM_STATE_SKIP_TASKBAR) + result = 0; + else if(atoms[i] == _NET_WM_STATE_SHADED) + result = 0; + if(! result) + break; + } + XFree(data); + + if(! result) + return 0; + + status = XGetWindowProperty(dpy, win, _NET_WM_WINDOW_TYPE, + 0L, 1L, False, XA_ATOM, &real_type, &real_format, + &items_read, &items_left, &data); + if(status != Success) + return 1; + + atoms = (Atom *)data; + + if(items_read && (atoms[0] == _NET_WM_WINDOW_TYPE_DESKTOP || atoms[0] == _NET_WM_WINDOW_TYPE_DOCK)) + result = 0; + + XFree(data); + + return result; + } else { + CARD32 attr; + + status = XGetWindowProperty(dpy, win, _WIN_STATE, + 0L, 1L, False, XA_CARDINAL, &real_type, &real_format, + &items_read, &items_left, &data); + if(status != Success || ! items_read) + { + if(status == Success) + XFree(data); + return 0; + } + attr = (((CARD32*)data)[0]) & (WIN_STATE_MINIMIZED | + WIN_STATE_SHADED | + WIN_STATE_HIDDEN); + if(attr) + result = 0; + XFree(data); + if(! result) + return 0; + + if(! IGNORE_SKIP_TASKBAR) + { + status = XGetWindowProperty(dpy, win, _WIN_HINTS, + 0L, 1L, False, XA_CARDINAL, &real_type, &real_format, + &items_read, &items_left, &data); + if(status != Success || ! items_read) + { + if(status == Success) + XFree(data); + return 1; /* If there's no _WIN_HINTS, assume it's 0, thus valid */ + } + attr = ((CARD32*)data)[0]; + if(attr & WIN_HINTS_SKIP_TASKBAR) + result = 0; + XFree(data); + } + + return result; + } +} + +CARD32 +wm_get_window_desktop(Display *dpy, Window win) +{ + int status, real_format; + Atom real_type; + unsigned long items_read, items_left; + unsigned char *data; + CARD32 desktop = 0; + + if(WM_PERSONALITY == WM_PERSONALITY_GNOME) + { + status = XGetWindowProperty(dpy, win, _WIN_STATE, + 0L, 1L, False, XA_CARDINAL, &real_type, &real_format, + &items_read, &items_left, &data); + if(status == Success) + { + if(items_read) + desktop = (((CARD32*)data)[0] & WIN_STATE_STICKY) ? (CARD32)-1 : 0; + + XFree(data); + + if(desktop) + return desktop; + } + } + + status = XGetWindowProperty(dpy, win, + (WM_PERSONALITY == WM_PERSONALITY_NETWM) ? _NET_WM_DESKTOP : _WIN_WORKSPACE, + 0L, 1L, False, XA_CARDINAL, &real_type, &real_format, + &items_read, &items_left, &data); + + if(status != Success) + return wm_get_current_desktop(dpy); + + if(items_read) + desktop = ((CARD32*)data)[0]; + else + desktop = wm_get_current_desktop(dpy); + + XFree(data); + + return desktop; +} + +/* Get focused window and traverse towards the root window until a window with WM_STATE is found */ +Window +wm_get_focused(Display *dpy) +{ + Window focused = None, root = None, *children; + unsigned int tmp_u; + int revert_to, status, real_format; + Atom real_type; + unsigned long items_read, items_left; + unsigned char *data; + + XGetInputFocus(dpy, &focused, &revert_to); + + /*printf("%x\n",focused);*/ + + while(focused != None && focused != root) + { + status = XGetWindowProperty(dpy, focused, XA_WM_STATE, + 0L, 1L, False, XA_WM_STATE, &real_type, &real_format, + &items_read, &items_left, &data); + if(status == Success) + { + XFree(data); + if(items_read) + break; + } + XQueryTree(dpy, focused, &root, &focused, &children, &tmp_u); + if(children) + XFree(children); + } + + return focused; +} diff --git a/wm.h b/wm.h new file mode 100644 index 0000000..050581d --- /dev/null +++ b/wm.h @@ -0,0 +1,78 @@ +/* Skippy - Seduces Kids Into Perversion + * + * Copyright (C) 2004 Hyriand + * + * 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 */