diff options
author | Ray Strode <rstrode@redhat.com> | 2007-04-08 22:00:20 -0400 |
---|---|---|
committer | Ray Strode <rstrode@redhat.com> | 2007-04-08 22:00:20 -0400 |
commit | cb0925c5702c5fe76fe02cddf399fc7b9fec3416 (patch) | |
tree | 80abb2d54ec154b3cb0395a8c511ebbf0c31d0c0 | |
parent | bb78615a633823720139d7a62e1e1b9c8f5364e1 (diff) | |
parent | 6896d3d790755ef7d0972c64254378eb13481efe (diff) |
Merge branch 'event-rewrite'master
Conflicts:
pop-window.c
-rw-r--r-- | AUTHORS | 1 | ||||
-rw-r--r-- | COPYING | 340 | ||||
-rw-r--r-- | ChangeLog | 0 | ||||
-rw-r--r-- | FIGURING-OUT-WHAT-TO-REDRAW | 41 | ||||
-rw-r--r-- | INSTALL | 0 | ||||
-rw-r--r-- | Makefile | 3 | ||||
-rw-r--r-- | Makefile.am | 27 | ||||
-rw-r--r-- | NEWS | 0 | ||||
-rw-r--r-- | README | 0 | ||||
-rw-r--r-- | TODO | 11 | ||||
-rwxr-xr-x | autogen.sh | 6 | ||||
-rw-r--r-- | configure.ac | 44 | ||||
-rw-r--r-- | po/ChangeLog | 0 | ||||
-rw-r--r-- | po/POTFILES.in | 7 | ||||
-rw-r--r-- | po/POTFILES.skip | 0 | ||||
-rw-r--r-- | pop-demo.c | 301 | ||||
-rw-r--r-- | pop-window.c | 723 | ||||
-rw-r--r-- | pop-window.h | 79 | ||||
-rwxr-xr-x | scripts/new-object.sh | 387 | ||||
-rw-r--r-- | src/Makefile.am | 29 | ||||
-rw-r--r-- | src/pop-demo.c | 437 | ||||
-rw-r--r-- | src/pop-event-listener.c | 1165 | ||||
-rw-r--r-- | src/pop-event-listener.h | 88 | ||||
-rw-r--r-- | src/pop-marshal.list | 2 | ||||
-rw-r--r-- | src/pop-overlay-window.c (renamed from pop-overlay-window.c) | 4 | ||||
-rw-r--r-- | src/pop-overlay-window.h (renamed from pop-overlay-window.h) | 0 | ||||
-rw-r--r-- | src/pop-window-stack.c | 492 | ||||
-rw-r--r-- | src/pop-window-stack.h | 83 | ||||
-rw-r--r-- | src/pop-window-view.c | 1061 | ||||
-rw-r--r-- | src/pop-window-view.h | 83 |
30 files changed, 4306 insertions, 1108 deletions
@@ -0,0 +1 @@ +Ray Strode <rstrode@redhat.com> @@ -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. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/ChangeLog diff --git a/FIGURING-OUT-WHAT-TO-REDRAW b/FIGURING-OUT-WHAT-TO-REDRAW new file mode 100644 index 0000000..d8d32cf --- /dev/null +++ b/FIGURING-OUT-WHAT-TO-REDRAW @@ -0,0 +1,41 @@ +Right now every toplevel (direct descendent of a root window) window is +given an associated "view". The view is responsible for translating the +various offscreen window events to some on screen representation. In +it's current form that means just showing the window exactly as it was +before, verbatim. Eventually it will also mean doing translucency and +animations. + +One interesting property of the views is that they track window +"damage". That means that they know when parts of their windows get +drawn off screen. The views should take advantage of this to only +update the on screen area that corresponds to the offscreen area that +got redrawn (the damaged areas). + +Also, in order to facilitate a reasonable amount of large-sized windows, +we need to make sure that views that are completely occluded are +properly culled and not drawn. Figuring out the proper culling logic is +a bit tricky, because only the view knows its geometry at any given +instant, and each view doesn't know the geometry or relative stacking +order and opacity of its siblings. + +One possible way to solve the problem is to institute a sort comparison +function that returns the difference of two views. This function would +return an area that encompasses the part of the first view that doesn't +overlap with the second view. In addition, the area would need to +account for (I mean, include) overlapping areas where the second view isn't +fully opaque. To figure out which part of a given view needs to be +redrawn, the application would call the comparison function for that +view against all of the views that are higher in the stack than it. The +view's initial bounding area would be intersected with each run of the +comparison function, and the remaining area is the area that isn't +occluded. This area should get passed to the view when requesting it +render itself. It would then only redraw damaged areas within the +passed in area. + +It's important that the application keep a list of views with windows +that have been recently damaged and only do the comparison on those views +and on views that been recently exposed because of a higher stacked view +moving. + +Also, the final rendered output of any given run should be +cached and redrawn initially next time around. diff --git a/Makefile b/Makefile deleted file mode 100644 index a2d8a44..0000000 --- a/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -all: - gcc pop-overlay-window.c pop-window.c pop-demo.c -o pop-demo `pkg-config --cflags --libs glib-2.0 gtk+-2.0 cairo cairo-xlib xcomposite xdamage ` -ggdb3 -O0 - diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..e1babde --- /dev/null +++ b/Makefile.am @@ -0,0 +1,27 @@ +SUBDIRS = src po + +EXTRA_DIST = ChangeLog \ + README \ + intltool-extract.in \ + intltool-merge.in \ + intltool-update.in + +DISTCLEANFILES = intltool-extract \ + intltool-merge \ + intltool-update + +MAINTAINERCLEANFILES = aclocal.m4 \ + compile \ + config.guess \ + config.h.in \ + config.h.in~ \ + config.sub \ + configure \ + depcomp \ + install-sh \ + intltool-extract.in \ + intltool-merge.in \ + intltool-update.in \ + ltmain.sh \ + Makefile.in \ + missing @@ -0,0 +1,11 @@ +- make event listener support multiple screens +- fix window stack to only XQueryTree the root once and then + update the list from X events +- get rid of the junk test pop-demo.c file and come up with a real + program +- remove some of the commented out boilerplate created from the + object generator script +- don't repaint every window on every damage event +- advertise the _NET_WM_CM selection +- add some of the common effects found in compositing managers (drop shadows, +animations (?), support for tranlucency) diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..2c5bd8a --- /dev/null +++ b/autogen.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +(cd $(dirname $0); + autoreconf --install --symlink; + intltoolize --force; + ./configure --enable-maintainer-mode $@) diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..5912c18 --- /dev/null +++ b/configure.ac @@ -0,0 +1,44 @@ +# the versioning scheme is 0.0.1-unstable +# followed by 0.1.0 for initial release +# followed by 0.1.0-stable and 0.1.0-unstable +# followed by 0.1.1 and 0.2.0 +# followed by 0.1.1-stable and 0.2.0-unstable +# etc., etc. +AC_INIT(pop, 0.0.1-unstable, "rstrode@redhat.com") +AC_CONFIG_SRCDIR(src/pop-demo.c) +AC_CONFIG_HEADER(config.h) + +AC_PROG_AWK +AC_PROG_CC +AM_PROG_CC_C_O +AC_HEADER_STDC +AC_C_CONST + +AM_INIT_AUTOMAKE([dist-bzip2]) +AM_MAINTAINER_MODE + +GETTEXT_PACKAGE=pop +AC_DEFINE([GETTEXT_PACKAGE], [], "a toy compositing manager") +AC_SUBST(GETTEXT_PACKAGE) +AM_GLIB_GNU_GETTEXT +ALL_LINGUAS="" + +IT_PROG_INTLTOOL +PKG_PROG_PKG_CONFIG + +PKG_CHECK_MODULES(POP, [glib-2.0 >= 2.12.11 + gtk+-2.0 >= 2.10.11 + cairo >= 1.4.2 + cairo-xlib >= 1.4.2 + xcomposite >= 0.3.1 + xdamage >= 1.1.1 +]) +AC_SUBST(POP_CFLAGS) +AC_SUBST(POP_LIBS) + +AC_PATH_PROG(GLIB_GENMARSHAL, glib-genmarshal) + +AC_OUTPUT([Makefile + src/Makefile + po/Makefile.in +]) diff --git a/po/ChangeLog b/po/ChangeLog new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/po/ChangeLog diff --git a/po/POTFILES.in b/po/POTFILES.in new file mode 100644 index 0000000..a608b0b --- /dev/null +++ b/po/POTFILES.in @@ -0,0 +1,7 @@ +[encoding: UTF-8] +src/pop-event-listener.c +src/pop-window-view.c +src/pop-overlay-window.c +src/pop-marshal.c +src/pop-window-stack.c +src/pop-demo.c diff --git a/po/POTFILES.skip b/po/POTFILES.skip new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/po/POTFILES.skip diff --git a/pop-demo.c b/pop-demo.c deleted file mode 100644 index 3286034..0000000 --- a/pop-demo.c +++ /dev/null @@ -1,301 +0,0 @@ -#include <unistd.h> - -#include <glib.h> - -#include <X11/extensions/Xcomposite.h> -#include <X11/extensions/Xdamage.h> -#include <X11/extensions/Xfixes.h> - -#include <cairo.h> -#include <cairo-xlib.h> - -#include <gdk/gdk.h> -#include <gdk/gdkcairo.h> -#include <gdk/gdkx.h> - -#include "pop-overlay-window.h" -#include "pop-window.h" - -static GList *window_list = NULL; - -static int damage_extension_event_base, - damage_extension_error_base; - -static int shape_extension_event_base, - shape_extension_error_base; - -static gboolean -composite_is_available (void) -{ - int event_base, error_base; - - event_base = 0; - error_base = 0; - if (XCompositeQueryExtension (GDK_DISPLAY (), &event_base, &error_base)) - return TRUE; - return FALSE; -} - -static gboolean -initialize_damage_extension (void) -{ - if (XDamageQueryExtension (GDK_DISPLAY (), &damage_extension_event_base, - &damage_extension_error_base)) - return TRUE; - - return FALSE; -} - -static gboolean -initialize_shape_extension (void) -{ - if (XShapeQueryExtension (GDK_DISPLAY (), &shape_extension_event_base, - &shape_extension_error_base)) - return TRUE; - - return FALSE; -} - -static void -draw_windows (cairo_t *cairo_context) -{ - GList *tmp; - - for (tmp = window_list; tmp != NULL; tmp = tmp->next) - pop_window_draw (POP_WINDOW (tmp->data), cairo_context); -} - -gint -window_compare (PopWindow *window_1, - Window *x_window_id) -{ - if (!POP_IS_WINDOW (window_1)) - { - g_warning ("passed invalid data to compare func: %p\n", window_1); - return -1; - } - return ((gulong) pop_window_get_x_window_id (window_1)) - - *x_window_id; -} - -static PopWindow * -find_window_from_x_window_id (Window x_window_id) -{ - - GList *node; - - node = g_list_find_custom (window_list, &x_window_id, - (GCompareFunc) window_compare); - - if (node == NULL) - return NULL; - - return (PopWindow *) node->data; -} - -static void -add_window_to_list (Window x_window_id) -{ - PopWindow *window; - XWindowAttributes attributes; - - if (!XGetWindowAttributes (GDK_DISPLAY (), x_window_id, &attributes)) - return; - - if (attributes.class == InputOnly) - return; - - window = pop_window_new (x_window_id); - gtk_widget_realize (GTK_WIDGET (window)); - window_list = g_list_append (window_list, window); -} - -static void -remove_window_from_list (Window x_window_id) -{ - PopWindow *window; - - window = find_window_from_x_window_id (x_window_id); - - if (window == NULL) - return; - - g_assert (POP_IS_WINDOW (window)); - - window_list = g_list_remove (window_list, window); - gtk_widget_destroy (GTK_WIDGET (window)); -} - -static void -get_initial_window_list (void) -{ - Window root, parent; - Window *x_window_ids; - guint number_of_windows, i; - - gdk_x11_grab_server (); - if (!XQueryTree (GDK_DISPLAY (), - GDK_ROOT_WINDOW (), &root, &parent, - &x_window_ids, &number_of_windows)) - { - gdk_x11_ungrab_server (); - return; - } - - for (i = 0; i < number_of_windows; i++) - { - if (!find_window_from_x_window_id (x_window_ids[i])) - add_window_to_list (x_window_ids[i]); - } - - XFree (x_window_ids); - gdk_x11_ungrab_server (); -} - -static gboolean -on_expose_event (GtkWidget *widget, - GdkEventExpose *event) -{ - cairo_t *cairo_context; - - cairo_context = gdk_cairo_create (widget->window); - cairo_set_source_rgb (cairo_context, 0.5, 0.5, 0.6); - cairo_paint (cairo_context); - draw_windows (cairo_context); - cairo_destroy (cairo_context); - - return FALSE; -} - -static void -on_map (GtkWidget *widget) -{ - /* redirect the direct descendents of root window (all toplevels) - * I think that the overlay window is automatically excluded even - * though it is a toplevel (which makes sense). It's the window we - * are going to be outputing the redirecting windows to, so it shouldn't - * be redirected itself. - */ - XCompositeRedirectSubwindows (GDK_DISPLAY (), - GDK_ROOT_WINDOW (), - CompositeRedirectManual); - get_initial_window_list (); -} - -static void -on_damage_event (XDamageNotifyEvent *damage_event, - PopOverlayWindow *overlay_window) -{ - GdkDisplay *display; - PopWindow *window; - Window x_window_id; - - x_window_id = (Window) damage_event->drawable; - window = find_window_from_x_window_id (x_window_id); - - gdk_error_trap_push (); - - XDamageSubtract (GDK_DISPLAY (), - damage_event->damage, - None, None); - - gdk_flush (); - gdk_error_trap_pop (); - - if (window == NULL) - return; - - gtk_widget_queue_draw (GTK_WIDGET (overlay_window)); -} - -static void -on_create_window_event (XCreateWindowEvent *window_event, - PopOverlayWindow *overlay_window) -{ - g_assert (window_event->parent == GDK_ROOT_WINDOW ()); - add_window_to_list (window_event->window); -} - -static void -on_destroy_window_event (XDestroyWindowEvent *window_event, - PopOverlayWindow *overlay_window) -{ - remove_window_from_list (window_event->window); - gtk_widget_queue_draw (GTK_WIDGET (overlay_window)); -} - -static gboolean -x_event_is_damage_event (XEvent *x_event) -{ - return x_event->type == damage_extension_event_base + XDamageNotify; -} - -static gboolean -x_event_is_create_window_event (XEvent *x_event) -{ - return x_event->type == CreateNotify; -} - -static gboolean -x_event_is_destroy_window_event (XEvent *x_event) -{ - return x_event->type == DestroyNotify; -} - -static GdkFilterReturn -on_event (XEvent *x_event, - GdkEvent *event, - PopOverlayWindow *overlay_window) -{ - if (x_event_is_damage_event (x_event)) - on_damage_event ((XDamageNotifyEvent *) x_event, overlay_window); - else if (x_event_is_create_window_event (x_event)) - on_create_window_event ((XCreateWindowEvent *) x_event, overlay_window); - else if (x_event_is_destroy_window_event (x_event)) - on_destroy_window_event ((XDestroyWindowEvent *) x_event, overlay_window); - - - return GDK_FILTER_CONTINUE; -} - -int -main (int argc, - char **argv) -{ - GtkWidget *overlay_window; - GdkWindow *root_window; - - gtk_init (&argc, &argv); - - if (!composite_is_available ()) - { - g_printerr ("composite extension not found\n"); - return 1; - } - - if (!initialize_damage_extension ()) - { - g_printerr ("damage extension not found\n"); - return 2; - } - - initialize_shape_extension (); - - overlay_window = pop_overlay_window_new (); - gtk_container_set_border_width (GTK_CONTAINER (overlay_window), 200); - - g_signal_connect (G_OBJECT (overlay_window), "expose-event", - G_CALLBACK (on_expose_event), NULL); - g_signal_connect (G_OBJECT (overlay_window), "map", - G_CALLBACK (on_map), NULL); - - root_window = gdk_get_default_root_window (); - gdk_window_set_events (root_window, GDK_SUBSTRUCTURE_MASK); - gdk_window_add_filter (NULL, (GdkFilterFunc) on_event, overlay_window); - - gtk_widget_show (overlay_window); - - gtk_main(); - - return 0; -} diff --git a/pop-window.c b/pop-window.c deleted file mode 100644 index f35adc2..0000000 --- a/pop-window.c +++ /dev/null @@ -1,723 +0,0 @@ -/* pop-window.c - - * - * Copyright (C) 2007 Ray Strode <rstrode@redhat.com> - * - * 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, 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 "pop-window.h" - -#include <errno.h> -#include <string.h> - -#include <glib.h> -#include <glib-object.h> -#include <glib/gi18n.h> - -#include <cairo.h> - -#include <X11/extensions/Xcomposite.h> -#include <X11/extensions/Xdamage.h> -#include <X11/extensions/shape.h> - -#include <cairo-xlib.h> -#include <gdk/gdkx.h> - -#include <gdk/gdk.h> - -#include <gtk/gtk.h> - -struct _PopWindowPrivate -{ - Window x_window_id; - Damage x_damage_id; - cairo_pattern_t *cairo_pattern; - cairo_path_t *cairo_clip_path; -}; - -#if 0 -static void pop_window_class_install_signals (PopWindowClass * window_class); -#endif -static void pop_window_class_install_properties (PopWindowClass * - window_class); - -static void pop_window_set_property (GObject * object, - guint prop_id, - const GValue * value, - GParamSpec * pspec); -static void pop_window_get_property (GObject * object, - guint prop_id, - GValue * value, GParamSpec * pspec); -static void pop_window_set_x_window_id (PopWindow *window, - Window x_window_id); - -static void pop_window_do_finalize (GObject *object); -static void pop_window_do_realize (GtkWidget *widget); -static void pop_window_do_unrealize (GtkWidget *widget); -static gboolean pop_window_do_configure_event (GtkWidget *widget, - GdkEventConfigure *configure_event); -static gboolean pop_window_do_map_event (GtkWidget *widget, - GdkEventAny *event); -static gboolean pop_window_do_unmap_event (GtkWidget *widget, - GdkEventAny *event); - -enum -{ - PROP_0 = 0, - PROP_X_WINDOW_ID -}; - -#define POP_WINDOW_INVALID_X_WINDOW_ID (Window) 0 -#define POP_WINDOW_INVALID_X_PIXMAP_ID (Pixmap) 0 -#define POP_WINDOW_INVALID_X_DAMAGE_ID (Damage) 0 - -#if 0 -enum -{ - FOO = 0, - NUMBER_OF_SIGNALS -}; - -static guint pop_window_signals[NUMBER_OF_SIGNALS]; -#endif - -G_DEFINE_TYPE (PopWindow, pop_window, GTK_TYPE_INVISIBLE); - -static void -pop_window_class_init (PopWindowClass *window_class) -{ - GObjectClass *object_class; - GtkWidgetClass *widget_class; - - object_class = G_OBJECT_CLASS (window_class); - widget_class = GTK_WIDGET_CLASS (window_class); - - object_class->finalize = pop_window_do_finalize; - - widget_class->configure_event = pop_window_do_configure_event; - widget_class->map_event = pop_window_do_map_event; - widget_class->unmap_event = pop_window_do_unmap_event; - widget_class->realize = pop_window_do_realize; - - pop_window_class_install_properties (window_class); -#if 0 - pop_window_class_install_signals (window_class); -#endif - - g_type_class_add_private (window_class, sizeof (PopWindowPrivate)); -} - -#if 0 -static void -pop_window_class_install_signals (PopWindowClass * window_class) -{ - GObjectClass *object_class; - - object_class = G_OBJECT_CLASS (window_class); - - pop_window_signals[FOO] = - g_signal_new ("foo", G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (PopWindowClass, foo), - NULL, NULL, g_cclosure_marshal_VOID__NONE; G_TYPE_NONE, 0); - window_class->foo = NULL; -} -#endif - -static void -pop_window_class_install_properties (PopWindowClass *window_class) -{ - GObjectClass *object_class; - GParamSpec *param_spec; - - object_class = G_OBJECT_CLASS (window_class); - object_class->set_property = pop_window_set_property; - object_class->get_property = pop_window_get_property; - - param_spec = g_param_spec_ulong ("x-window-id", _("X Window ID"), - _("A client-side identifier representing a " - "server-side window"), 0, G_MAXULONG, - (gulong) POP_WINDOW_INVALID_X_WINDOW_ID, - G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE); - g_object_class_install_property (object_class, PROP_X_WINDOW_ID, param_spec); -} - -static gboolean -pop_window_enable_damage_reporting (PopWindow *window) -{ - GtkWidget *widget; - - g_assert (POP_IS_WINDOW (window)); - - widget = GTK_WIDGET (window); - - gdk_error_trap_push (); - window->priv->x_damage_id = - XDamageCreate (GDK_WINDOW_XDISPLAY (widget->window), - window->priv->x_window_id, - XDamageReportNonEmpty); - gdk_flush (); - if (gdk_error_trap_pop ()) - return FALSE; - - return TRUE; -} - -static void -pop_window_disable_damage_reporting (PopWindow *window) -{ - GtkWidget *widget; - - g_assert (POP_IS_WINDOW (window)); - - widget = GTK_WIDGET (window); - - gdk_error_trap_push (); - XDamageDestroy (GDK_WINDOW_XDISPLAY (widget->window), - window->priv->x_damage_id); - window->priv->x_damage_id = POP_WINDOW_INVALID_X_DAMAGE_ID; - - gdk_flush (); - gdk_error_trap_pop (); -} - -static void -pop_window_init (PopWindow *window) -{ - g_assert (POP_IS_WINDOW (window)); - - window->priv = G_TYPE_INSTANCE_GET_PRIVATE (window, - POP_TYPE_WINDOW, - PopWindowPrivate); -} - -static void -pop_window_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - PopWindow *window = POP_WINDOW (object); - - switch (prop_id) - { - case PROP_X_WINDOW_ID: - pop_window_set_x_window_id (window, (Window) g_value_get_ulong (value)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -pop_window_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - PopWindow *window = POP_WINDOW (object); - - switch (prop_id) - { - case PROP_X_WINDOW_ID: - g_value_set_ulong (value, (gulong) pop_window_get_x_window_id (window)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -pop_window_do_finalize (GObject *object) -{ - PopWindow *window; - GObjectClass *parent_class; - - window = POP_WINDOW (object); - - parent_class = G_OBJECT_CLASS (pop_window_parent_class); - - if (parent_class->finalize != NULL) - parent_class->finalize (object); -} - -static Pixmap -pop_window_fetch_x_pixmap_id (PopWindow *window) -{ - GtkWidget *widget; - Pixmap x_pixmap_id; - - g_assert (POP_IS_WINDOW (window)); - g_assert (GTK_WIDGET (window)->window != NULL); - - widget = GTK_WIDGET (window); - - gdk_error_trap_push (); - x_pixmap_id = - XCompositeNameWindowPixmap (GDK_WINDOW_XDISPLAY (widget->window), - window->priv->x_window_id); - gdk_flush(); - if (gdk_error_trap_pop ()) - return POP_WINDOW_INVALID_X_PIXMAP_ID; - - return x_pixmap_id; -} - -static cairo_surface_t * -pop_window_get_cairo_surface (PopWindow *window) -{ - cairo_surface_t *surface; - cairo_status_t status; - - g_assert (POP_IS_WINDOW (window)); - g_assert (window->priv->cairo_pattern != NULL); - - surface = NULL; - status = cairo_pattern_get_surface (window->priv->cairo_pattern, &surface); - g_assert (status != CAIRO_STATUS_PATTERN_TYPE_MISMATCH); - - return surface; -} - -static void -pop_window_free_x_pixmap (PopWindow *window) -{ - GtkWidget *widget; - cairo_surface_t *surface; - Pixmap x_pixmap_id; - - g_assert (POP_IS_WINDOW (window)); - - widget = GTK_WIDGET (window); - - if (widget->window == NULL) - return; - - surface = pop_window_get_cairo_surface (window); - - g_assert (surface != NULL); - x_pixmap_id = (Pixmap) cairo_xlib_surface_get_drawable (surface); - XFreePixmap (GDK_WINDOW_XDISPLAY (widget->window), x_pixmap_id); -} - -static void -pop_window_create_cairo_pattern_from_x_pixmap_id (PopWindow *window, - Pixmap x_pixmap_id) -{ - GtkWidget *widget; - GdkVisual *visual; - cairo_surface_t *surface; - int width, height; - - g_assert (POP_IS_WINDOW (window)); - g_assert (GTK_WIDGET (window)->window != NULL); - g_assert (window->priv->cairo_pattern == NULL); - - widget = GTK_WIDGET (window); - visual = gtk_widget_get_visual (widget); - - gdk_drawable_get_size (GDK_DRAWABLE (widget->window), - &width, &height); - - surface = cairo_xlib_surface_create (GDK_WINDOW_XDISPLAY (widget->window), - (Drawable) x_pixmap_id, - GDK_VISUAL_XVISUAL (visual), - width, height); - window->priv->cairo_pattern = cairo_pattern_create_for_surface (surface); - cairo_surface_destroy (surface); -} - -static void -pop_window_set_cairo_clip_path_from_x_rectangles (PopWindow *window, - XRectangle *x_rectangles, - int number_of_x_rectangles) -{ - GtkWidget *widget; - cairo_t *cairo_context; - cairo_surface_t *surface; - int width, height, i; - int x, y; - - g_assert (POP_IS_WINDOW (window)); - g_assert (GTK_WIDGET (window) != NULL); - g_assert (x_rectangles != NULL); - g_assert (number_of_x_rectangles > 0); - - widget = GTK_WIDGET (window); - - if (window->priv->cairo_clip_path != NULL) - cairo_path_destroy (window->priv->cairo_clip_path); - - gdk_drawable_get_size (GDK_DRAWABLE (widget->window), - &width, &height); - gdk_window_get_position (GTK_WIDGET (window)->window, - &x, &y); - - surface = cairo_image_surface_create (CAIRO_FORMAT_A1, - width, height); - cairo_context = cairo_create (surface); - - for (i = 0; i < number_of_x_rectangles; i++) - { - cairo_rectangle (cairo_context, - x + x_rectangles[i].x, - y + x_rectangles[i].y, - x_rectangles[i].width, - x_rectangles[i].height); - } - - window->priv->cairo_clip_path = cairo_copy_path_flat (cairo_context); - cairo_destroy (cairo_context); -} - -static void -pop_window_create_cairo_pattern (PopWindow *window) -{ - GtkWidget *widget; - GdkVisual *visual; - Pixmap x_pixmap_id; - g_assert (POP_IS_WINDOW (window)); - g_assert (GTK_WIDGET (window)->window != NULL); - g_assert (window->priv->cairo_pattern == NULL); - - widget = GTK_WIDGET (window); - - x_pixmap_id = pop_window_fetch_x_pixmap_id (window); - - if (x_pixmap_id == POP_WINDOW_INVALID_X_PIXMAP_ID) - return; - - pop_window_create_cairo_pattern_from_x_pixmap_id (window, x_pixmap_id); -} - -static gboolean -pop_window_size_is_different (PopWindow *window) -{ - GtkWidget *widget; - cairo_surface_t *surface; - gint old_window_width, old_window_height; - gint window_width, window_height; - - g_assert (POP_IS_WINDOW (window)); - g_assert (window->priv->cairo_pattern != NULL); - - widget = GTK_WIDGET (window); - surface = pop_window_get_cairo_surface (window); - - g_assert (surface != NULL); - old_window_width = cairo_xlib_surface_get_width (surface); - old_window_height = cairo_xlib_surface_get_height (surface); - - gdk_drawable_get_size (GDK_DRAWABLE (widget->window), - &window_width, &window_height); - - if ((window_width != old_window_width) - || (window_height != old_window_height)) - return TRUE; - - return FALSE; -} - -static void -pop_window_destroy_pattern (PopWindow *window) -{ - g_assert (POP_IS_WINDOW (window)); - g_assert (window->priv->cairo_pattern != NULL); - - pop_window_free_x_pixmap (window); - cairo_pattern_destroy (window->priv->cairo_pattern); - window->priv->cairo_pattern = NULL; -} - -static void -pop_window_update_pattern_size (PopWindow *window) -{ - GtkWidget *widget; - Pixmap old_x_pixmap_id, x_pixmap_id; - cairo_surface_t *surface; - gint window_width, window_height; - - g_assert (POP_IS_WINDOW (window)); - g_assert (window->priv->cairo_pattern != NULL); - - widget = GTK_WIDGET (window); - pop_window_free_x_pixmap (window); - x_pixmap_id = pop_window_fetch_x_pixmap_id (window); - - if (x_pixmap_id == POP_WINDOW_INVALID_X_PIXMAP_ID) - { - pop_window_destroy_pattern (window); - return; - } - - gdk_drawable_get_size (GDK_DRAWABLE (widget->window), - &window_width, &window_height); - - surface = pop_window_get_cairo_surface (window); - g_assert (surface != NULL); - cairo_xlib_surface_set_drawable (surface, (Drawable) x_pixmap_id, - window_width, window_height); -} - -static void -pop_window_sync_pattern_with_server (PopWindow *window) -{ - GtkWidget *widget; - cairo_surface_t *surface; - cairo_matrix_t matrix; - gint x, y; - XRectangle *x_rectangles; - int ordering, number_of_x_rectangles; - - g_assert (POP_IS_WINDOW (window)); - g_assert (GTK_WIDGET (window)->window != NULL); - - widget = GTK_WIDGET (window); - - if (window->priv->cairo_pattern == NULL) - pop_window_create_cairo_pattern (window); - else if (pop_window_size_is_different (window)) - pop_window_update_pattern_size (window); - - if (window->priv->cairo_pattern == NULL) - return; - - number_of_x_rectangles = 0; - x_rectangles = XShapeGetRectangles (GDK_WINDOW_XDISPLAY (widget->window), - window->priv->x_window_id, - ShapeBounding, &number_of_x_rectangles, - &ordering); - - if (number_of_x_rectangles > 0) - pop_window_set_cairo_clip_path_from_x_rectangles (window, - x_rectangles, - number_of_x_rectangles); - XFree (x_rectangles); - - gdk_window_get_position (GDK_WINDOW (widget->window), &x, &y); - cairo_matrix_init_identity (&matrix); - cairo_matrix_translate (&matrix, -x, -y); - cairo_pattern_set_matrix (window->priv->cairo_pattern, &matrix); -} - -static void -pop_window_setup_gdk_window (PopWindow *window) -{ - GtkWidget *widget; - - g_assert (POP_IS_WINDOW (window)); - - widget = GTK_WIDGET (window); - - widget->window = - gdk_window_foreign_new ((GdkNativeWindow) window->priv->x_window_id); - - if (widget->window == NULL) - return; - - gdk_window_set_events (widget->window, - GDK_STRUCTURE_MASK); - - XShapeInputSelected (GDK_WINDOW_XDISPLAY (widget->window), - window->priv->x_window_id); - - gdk_window_set_user_data (widget->window, window); - - pop_window_sync_pattern_with_server (POP_WINDOW (widget)); - if (!pop_window_enable_damage_reporting (window)) - { - gdk_window_destroy (widget->window); - widget->window = NULL; - } -} - -static void -pop_window_do_realize (GtkWidget *widget) -{ - PopWindow *window; - - window = POP_WINDOW (widget); - - pop_window_setup_gdk_window (POP_WINDOW (widget)); - - widget->style = gtk_style_attach (widget->style, widget->window); - GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED); -} - -static void -pop_window_do_unrealize (GtkWidget *widget) -{ - PopWindow *window; - - window = POP_WINDOW (widget); - - if (widget->window == NULL) - { - gdk_window_destroy (widget->window); - widget->window = NULL; - } - - if (window->priv->cairo_pattern != NULL) - pop_window_destroy_pattern (window); - - pop_window_disable_damage_reporting (window); - - GTK_WIDGET_UNSET_FLAGS (widget, GTK_REALIZED); -} - -GQuark -pop_window_error_quark (void) -{ - static GQuark error_quark = 0; - - if (error_quark == 0) - error_quark = g_quark_from_static_string ("pop-window"); - - return error_quark; -} - -PopWindow * -pop_window_new (Window x_window_id) -{ - PopWindow *window; - - window = g_object_new (POP_TYPE_WINDOW, - "x-window-id", x_window_id, NULL); - - return window; -} - -static void -pop_window_disable_content_tracking (PopWindow *window) -{ - GtkWidget *widget; - - g_assert (POP_IS_WINDOW (window)); - - widget = GTK_WIDGET (window); - - if (window->priv->cairo_pattern != NULL) - { - gdk_error_trap_push (); - pop_window_destroy_pattern (window); - gdk_flush (); - if (gdk_error_trap_pop ()) - { - pop_window_disable_damage_reporting (window); - gdk_window_destroy (widget->window); - widget->window = NULL; - } - } -} - -static gboolean -pop_window_do_configure_event (GtkWidget *widget, - GdkEventConfigure *configure_event) -{ - PopWindow *window; - - g_assert (POP_IS_WINDOW (widget)); - - window = POP_WINDOW (widget); - - gdk_error_trap_push (); - pop_window_sync_pattern_with_server (window); - gdk_flush (); - if (gdk_error_trap_pop ()) - pop_window_disable_content_tracking (window); - - return FALSE; -} - -static gboolean -pop_window_do_map_event (GtkWidget *widget, - GdkEventAny *event) -{ - PopWindow *window; - - g_assert (POP_IS_WINDOW (widget)); - - window = POP_WINDOW (widget); - - gdk_error_trap_push (); - pop_window_sync_pattern_with_server (POP_WINDOW (widget)); - gdk_flush (); - if (gdk_error_trap_pop ()) - pop_window_disable_content_tracking (window); -} - -static gboolean -pop_window_do_unmap_event (GtkWidget *widget, - GdkEventAny *event) -{ - PopWindow *window; - - g_assert (POP_IS_WINDOW (widget)); - - window = POP_WINDOW (widget); - - if (window->priv->cairo_pattern != NULL) - { - gdk_error_trap_push (); - pop_window_destroy_pattern (window); - gdk_flush (); - if (gdk_error_trap_pop ()) - pop_window_disable_content_tracking (window); - } - return FALSE; -} - -static void -pop_window_set_x_window_id (PopWindow *window, - Window x_window_id) -{ - g_assert (POP_IS_WINDOW (window)); - g_assert (x_window_id != POP_WINDOW_INVALID_X_WINDOW_ID); - g_assert (window->priv->x_window_id == POP_WINDOW_INVALID_X_WINDOW_ID); - - window->priv->x_window_id = x_window_id; -} - -Window -pop_window_get_x_window_id (PopWindow *window) -{ - g_return_val_if_fail (POP_IS_WINDOW (window), POP_WINDOW_INVALID_X_WINDOW_ID); - - return window->priv->x_window_id; -} - -void -pop_window_draw (PopWindow *window, - cairo_t *cairo_context) -{ - cairo_surface_t *surface; - - if (window->priv->cairo_pattern == NULL) - return; - - cairo_set_source (cairo_context, window->priv->cairo_pattern); - if (window->priv->cairo_clip_path != NULL) - { - cairo_save (cairo_context); - cairo_append_path (cairo_context, window->priv->cairo_clip_path); - cairo_clip (cairo_context); - cairo_paint_with_alpha (cairo_context, .9); - cairo_restore (cairo_context); - } - else - cairo_paint (cairo_context); -} diff --git a/pop-window.h b/pop-window.h deleted file mode 100644 index 99d41cf..0000000 --- a/pop-window.h +++ /dev/null @@ -1,79 +0,0 @@ -/* pop-window.h - - * - * Copyright (C) 2007 Ray Strode <rstrode@redhat.com> - * - * 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, 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 POP_WINDOW_H -#define POP_WINDOW_H - -#include <glib.h> -#include <glib-object.h> - -#include <X11/X.h> - -#include <cairo.h> - -#include <gtk/gtk.h> - -G_BEGIN_DECLS -#define POP_TYPE_WINDOW (pop_window_get_type ()) -#define POP_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), POP_TYPE_WINDOW, PopWindow)) -#define POP_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), POP_TYPE_WINDOW, PopWindowClass)) -#define POP_IS_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), POP_TYPE_WINDOW)) -#define POP_IS_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), POP_TYPE_WINDOW)) -#define POP_WINDOW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), POP_TYPE_WINDOW, PopWindowClass)) -#define POP_WINDOW_ERROR (pop_window_error_quark ()) -typedef struct _PopWindow PopWindow; -typedef struct _PopWindowClass PopWindowClass; -typedef struct _PopWindowPrivate PopWindowPrivate; -typedef enum _PopWindowError PopWindowError; - -struct _PopWindow -{ - GtkInvisible parent; - - /*< private > */ - PopWindowPrivate *priv; -}; - -struct _PopWindowClass -{ - GtkInvisibleClass parent_class; - - /* signals */ -#if 0 - void (*foo) (PopWindow * window); -#endif -}; - -enum _PopWindowError -{ - POP_WINDOW_ERROR_GENERIC = 0, -}; - -#ifndef POP_HIDE_FUNCTION_DECLARATIONS -GType pop_window_get_type (void); -GQuark pop_window_error_quark (void); - -PopWindow *pop_window_new (Window x_window_id) G_GNUC_MALLOC; -Window pop_window_get_x_window_id (PopWindow *window); -void pop_window_draw (PopWindow *window, cairo_t *cairo_context); - -#endif - -G_END_DECLS -#endif /* POP_WINDOW_H */ diff --git a/scripts/new-object.sh b/scripts/new-object.sh new file mode 100755 index 0000000..b5f31dc --- /dev/null +++ b/scripts/new-object.sh @@ -0,0 +1,387 @@ +#!/bin/bash + +NAMESPACE="Pop" +AUTHOR="Ray Strode <rstrode@redhat.com>" +#----------------------------------------------------------------------------- +uppercase () +{ + echo "${1}" | tr 'a-z' 'A-Z' +} + +lowercase () +{ + echo "${1}" | tr 'A-Z' 'a-z' +} + +change_character () +{ + echo "${1}" | tr "${2}" "${3}" +} + +delete_character () +{ + echo "${1}" | tr -d "${2}" +} + +#----------------------------------------------------------------------------- +MACRO_NAMESPACE="$(change_character $(uppercase ${NAMESPACE}) '-' '_')" +METHOD_NAMESPACE="$(change_character $(lowercase ${NAMESPACE}) '-' '_')" + +OBJECT_NAME="$(delete_character ${1} '-')" +MACRO_OBJECT_NAME="$(change_character $(uppercase ${1}) '-' '_')" +METHOD_OBJECT_NAME="$(change_character $(lowercase ${1}) '-' '_')" +FULL_OBJECT_NAME="$(delete_character ${NAMESPACE} '-')${OBJECT_NAME}" +SHORT_OBJECT_NAME="$(lowercase $(echo ${1} | awk -F- '{print $NF}'))" + +HEADER_FILENAME="$(lowercase ${NAMESPACE}-${1}).h" +HEADER_GUARD="$(change_character $(change_character $(uppercase ${HEADER_FILENAME}) '-' '_') '.' '_')" + +SOURCE_FILENAME="$(lowercase ${NAMESPACE}-${1}).c" + +MACRO_PREFIX="${MACRO_NAMESPACE}_${MACRO_OBJECT_NAME}" +METHOD_PREFIX="${METHOD_NAMESPACE}_${METHOD_OBJECT_NAME}" + +ERROR_QUARK="${METHOD_NAMESPACE}-$(lowercase ${1})" +HUMAN_READABLE_NAME=$(change_character $(lowercase ${1}) '-' ' ') + +#----------------------------------------------------------------------------- +cat << > ${HEADER_FILENAME} +/* ${HEADER_FILENAME} - ${2} + * + * Copyright (C) $(date +%Y) ${AUTHOR} + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, 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 ${HEADER_GUARD} +#define ${HEADER_GUARD} + +#include <glib.h> +#include <glib-object.h> + +G_BEGIN_DECLS + +#define ${MACRO_NAMESPACE}_TYPE_${MACRO_OBJECT_NAME} (${METHOD_PREFIX}_get_type ()) +#define ${MACRO_PREFIX}(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ${MACRO_NAMESPACE}_TYPE_${MACRO_OBJECT_NAME}, ${FULL_OBJECT_NAME})) +#define ${MACRO_PREFIX}_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ${MACRO_NAMESPACE}_TYPE_${MACRO_OBJECT_NAME}, ${FULL_OBJECT_NAME}Class)) +#define ${MACRO_NAMESPACE}_IS_${MACRO_OBJECT_NAME}(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ${MACRO_NAMESPACE}_TYPE_${MACRO_OBJECT_NAME})) +#define ${MACRO_NAMESPACE}_IS_${MACRO_OBJECT_NAME}_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ${MACRO_NAMESPACE}_TYPE_${MACRO_OBJECT_NAME})) +#define ${MACRO_PREFIX}_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), ${MACRO_NAMESPACE}_TYPE_${MACRO_OBJECT_NAME}, ${FULL_OBJECT_NAME}Class)) +#define ${MACRO_PREFIX}_ERROR (${METHOD_PREFIX}_error_quark ()) + +typedef struct _${FULL_OBJECT_NAME} ${FULL_OBJECT_NAME}; +typedef struct _${FULL_OBJECT_NAME}Class ${FULL_OBJECT_NAME}Class; +typedef struct _${FULL_OBJECT_NAME}Private ${FULL_OBJECT_NAME}Private; +typedef enum _${FULL_OBJECT_NAME}Error ${FULL_OBJECT_NAME}Error; + +struct _${FULL_OBJECT_NAME} +{ + GObject parent; + + /*< private >*/ + ${FULL_OBJECT_NAME}Private *priv; +}; + +struct _${FULL_OBJECT_NAME}Class +{ + GObjectClass parent_class; + + /* signals */ +#if 0 + void (* foo) (${FULL_OBJECT_NAME} *${SHORT_OBJECT_NAME}); +#endif +}; + +enum _${FULL_OBJECT_NAME}Error +{ + ${MACRO_PREFIX}_ERROR_GENERIC = 0, +}; + +#ifndef ${MACRO_NAMESPACE}_HIDE_FUNCTION_DECLARATIONS +GType ${METHOD_PREFIX}_get_type (void); +GQuark ${METHOD_PREFIX}_error_quark (void); + +${FULL_OBJECT_NAME} *${METHOD_PREFIX}_new (void) G_GNUC_MALLOC; +#endif + +G_END_DECLS +#endif /* ${HEADER_GUARD} */ + +#----------------------------------------------------------------------------- +cat << > ${SOURCE_FILENAME} +/* ${SOURCE_FILENAME} - ${2} + * + * Copyright (C) $(date +%Y) ${AUTHOR} + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, 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 "config.h" +#include "${HEADER_FILENAME}" + +#include <errno.h> +#include <string.h> + +#include <glib.h> +#include <glib-object.h> +#include <glib/gi18n.h> + +struct _${FULL_OBJECT_NAME}Private +{ + int bar; +}; + +static void ${METHOD_PREFIX}_finalize (GObject *object); +#if 0 +static void ${METHOD_PREFIX}_class_install_signals (${FULL_OBJECT_NAME}Class *${SHORT_OBJECT_NAME}_class); +static void ${METHOD_PREFIX}_class_install_properties (${FULL_OBJECT_NAME}Class *${SHORT_OBJECT_NAME}_class); + +static void ${METHOD_PREFIX}_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void ${METHOD_PREFIX}_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); + +enum +{ + PROP_0 = 0, + PROP_BAR +}; + +#define ${MACRO_PREFIX}_DEFAULT_BAR 1 + +enum +{ + FOO = 0, + NUMBER_OF_SIGNALS +}; + +static guint ${METHOD_PREFIX}_signals[NUMBER_OF_SIGNALS]; +#endif + +G_DEFINE_TYPE (${FULL_OBJECT_NAME}, ${METHOD_PREFIX}, G_TYPE_OBJECT); + +static void +${METHOD_PREFIX}_class_init (${FULL_OBJECT_NAME}Class *${SHORT_OBJECT_NAME}_class) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (${SHORT_OBJECT_NAME}_class); + + object_class->finalize = ${METHOD_PREFIX}_finalize; + +#if 0 + ${METHOD_PREFIX}_class_install_properties (${SHORT_OBJECT_NAME}_class); + ${METHOD_PREFIX}_class_install_signals (${SHORT_OBJECT_NAME}_class); +#endif + + g_type_class_add_private (${SHORT_OBJECT_NAME}_class, sizeof (${FULL_OBJECT_NAME}Private)); +} + +#if 0 +static void +${METHOD_PREFIX}_class_install_signals (${FULL_OBJECT_NAME}Class *${SHORT_OBJECT_NAME}_class) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (${SHORT_OBJECT_NAME}_class); + + ${METHOD_PREFIX}_signals[FOO] = + g_signal_new ("foo", G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (${FULL_OBJECT_NAME}Class, foo), + NULL, NULL, g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + ${SHORT_OBJECT_NAME}_class->foo = NULL; +} + +static void +${METHOD_PREFIX}_class_install_properties (${FULL_OBJECT_NAME}Class *${SHORT_OBJECT_NAME}_class) +{ + GObjectClass *object_class; + GParamSpec *param_spec; + + object_class = G_OBJECT_CLASS (${SHORT_OBJECT_NAME}_class); + object_class->set_property = ${METHOD_PREFIX}_set_property; + object_class->get_property = ${METHOD_PREFIX}_get_property; + + param_spec = g_param_spec_int ("bar", _("Bar"), + _("The amount of bar"), + 0, G_MAXINT, + ${MACRO_PREFIX}_DEFAULT_BAR, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT); + g_object_class_install_property (object_class, PROP_BAR, param_spec); +} +#endif + +static void +${METHOD_PREFIX}_init (${FULL_OBJECT_NAME} *${SHORT_OBJECT_NAME}) +{ + ${SHORT_OBJECT_NAME}->priv = G_TYPE_INSTANCE_GET_PRIVATE (${SHORT_OBJECT_NAME}, + ${MACRO_NAMESPACE}_TYPE_${MACRO_OBJECT_NAME}, + ${FULL_OBJECT_NAME}Private); + +} + +static void +${METHOD_PREFIX}_finalize (GObject *object) +{ + ${FULL_OBJECT_NAME} *${SHORT_OBJECT_NAME}; + GObjectClass *parent_class; + + ${SHORT_OBJECT_NAME} = ${MACRO_PREFIX} (object); + + parent_class = G_OBJECT_CLASS (${METHOD_PREFIX}_parent_class); + + + + if (parent_class->finalize != NULL) + parent_class->finalize (object); +} + +#if 0 +static void +${METHOD_PREFIX}_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + ${FULL_OBJECT_NAME} *${SHORT_OBJECT_NAME} = ${MACRO_PREFIX} (object); + + switch (prop_id) + { + case PROP_BAR: + ${METHOD_PREFIX}_set_bar (${SHORT_OBJECT_NAME}, + g_value_get_int (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +${METHOD_PREFIX}_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + ${FULL_OBJECT_NAME} *${SHORT_OBJECT_NAME} = ${MACRO_PREFIX} (object); + + switch (prop_id) + { + case PROP_BAR: + g_value_set_int (value, + ${METHOD_PREFIX}_get_bar (${SHORT_OBJECT_NAME})); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} +#endif + +GQuark +${METHOD_PREFIX}_error_quark (void) +{ + static GQuark error_quark = 0; + + if (error_quark == 0) + error_quark = g_quark_from_static_string ("${ERROR_QUARK}"); + + return error_quark; +} + +${FULL_OBJECT_NAME} * +${METHOD_PREFIX}_new (void) +{ + ${FULL_OBJECT_NAME} *${SHORT_OBJECT_NAME}; + + ${SHORT_OBJECT_NAME} = g_object_new (${MACRO_NAMESPACE}_TYPE_${MACRO_OBJECT_NAME}, NULL); + + return ${SHORT_OBJECT_NAME}; +} + +#if 0 +void +${METHOD_PREFIX}_set_bar (${FULL_OBJECT_NAME} *${SHORT_OBJECT_NAME}, + int bar) +{ + if (${SHORT_OBJECT_NAME}->priv->bar != bar) + { + ${SHORT_OBJECT_NAME}->priv->bar = bar; + g_object_notify (G_OBJECT (${SHORT_OBJECT_NAME}), "bar"); + } +} + +int +${METHOD_PREFIX}_get_bar (${FULL_OBJECT_NAME} *${SHORT_OBJECT_NAME}) +{ + return ${SHORT_OBJECT_NAME}->priv->bar; +} +#endif + +#ifdef ${MACRO_PREFIX}_ENABLE_TEST + +#include <stdio.h> +#include <glib.h> + +int +main (int argc, + char **argv) +{ + ${FULL_OBJECT_NAME} *${SHORT_OBJECT_NAME}; + int exit_code; + + g_log_set_always_fatal (G_LOG_LEVEL_ERROR + | G_LOG_LEVEL_CRITICAL + | G_LOG_LEVEL_WARNING); + + g_type_init (); + + g_message ("creating instance of '${HUMAN_READABLE_NAME}' object..."); + ${SHORT_OBJECT_NAME} = ${METHOD_PREFIX}_new (); + g_message ("'${HUMAN_READABLE_NAME}' object created successfully"); + + g_message ("destroying previously created '${HUMAN_READABLE_NAME}' object..."); + g_object_unref (${SHORT_OBJECT_NAME}); + g_message ("'${HUMAN_READABLE_NAME}' object destroyed successfully"); + + exit_code = 0; + + return exit_code; +} +#endif /* ${MACRO_PREFIX}_ENABLE_TEST */ + + +indent -gnu ${HEADER_FILENAME} +rm -f ${HEADER_FILENAME}~ +indent -gnu ${SOURCE_FILENAME} +rm -f ${SOURCE_FILENAME}~ diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..f401637 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,29 @@ +INCLUDES = -I$(top_srcdir) \ + -I$(srcdir) + +BUILT_SOURCES = pop-marshal.c \ + pop-marshal.h +CLEANFILES = $(BUILT_SOURCES) + +EXTRA_DIST = pop-marshal.list + +pop-marshal.c: pop-marshal.list $(GLIB_GENMARSHAL) + $(GLIB_GENMARSHAL) $< --body --prefix=pop_marshal > $@ + +pop-marshal.h: pop-marshal.list $(GLIB_GENMARSHAL) + $(GLIB_GENMARSHAL) $< --header --prefix=pop_marshal > $@ + +pop_demo_CFLAGS = $(POP_CFLAGS) +pop_demo_LDADD = $(POP_LIBS) +pop_demo_SOURCES = pop-window-stack.c \ + pop-window-stack.h \ + pop-window-view.c \ + pop-window-view.h \ + pop-event-listener.c \ + pop-event-listener.h \ + pop-overlay-window.c \ + pop-overlay-window.h \ + $(BUILT_SOURCES) \ + pop-demo.c + +noinst_PROGRAMS = pop-demo diff --git a/src/pop-demo.c b/src/pop-demo.c new file mode 100644 index 0000000..e035d2f --- /dev/null +++ b/src/pop-demo.c @@ -0,0 +1,437 @@ +#include "config.h" + +#include <unistd.h> + +#include <glib.h> + +#include <X11/extensions/shape.h> +#include <X11/extensions/Xcomposite.h> +#include <X11/extensions/Xdamage.h> +#include <X11/extensions/Xfixes.h> + +#include <cairo.h> +#include <cairo-xlib.h> + +#include <gdk/gdk.h> +#include <gdk/gdkcairo.h> +#include <gdk/gdkx.h> + +#include "pop-overlay-window.h" +#include "pop-window-stack.h" +#include "pop-event-listener.h" +#include "pop-window-view.h" + +static PopWindowStack *stack = NULL; +static PopEventListener *listener = NULL; +GtkWidget *overlay_window; +static int damage_extension_event_base, + damage_extension_error_base; + +static int shape_extension_event_base, + shape_extension_error_base; + +static gboolean +composite_is_available (void) +{ + int event_base, error_base; + + event_base = 0; + error_base = 0; + if (XCompositeQueryExtension (GDK_DISPLAY (), &event_base, &error_base)) + return TRUE; + return FALSE; +} + +static gboolean +initialize_damage_extension (void) +{ + if (XDamageQueryExtension (GDK_DISPLAY (), &damage_extension_event_base, + &damage_extension_error_base)) + return TRUE; + + return FALSE; +} + +static gboolean +initialize_shape_extension (void) +{ + if (XShapeQueryExtension (GDK_DISPLAY (), &shape_extension_event_base, + &shape_extension_error_base)) + return TRUE; + + return FALSE; +} + +gint +window_compare (PopWindowView *view, + Window *x_window_id) +{ + GdkWindow *window; + if (!POP_IS_WINDOW_VIEW (view)) + { + g_warning ("passed invalid data to compare func: %p\n", view); + return -1; + } + + window = pop_window_view_get_window (view); + return ((gulong) GDK_WINDOW_XWINDOW (window)) - *x_window_id; +} + +static void +remove_window_from_list (GdkWindow *window) +{ + PopWindowView *view; + + view = g_object_get_data (G_OBJECT (window), "pop-window-view"); + + if (view == NULL) + return; + + g_assert (POP_IS_WINDOW_VIEW (view)); + + pop_window_view_unset_window (view); + g_object_unref (view); + + g_object_set_data (G_OBJECT (window), "pop-window-view", NULL); + g_object_unref (G_OBJECT (window)); +} + +static void +add_window_to_list (GdkWindow *window) +{ + PopWindowView *view; + + if (window == GTK_WIDGET (overlay_window)->window) + return; + { + Window parent, root, *children; + guint number_of_children, i; + + gdk_error_trap_push (); + if (!XQueryTree (GDK_DISPLAY (), + GDK_WINDOW_XWINDOW (window), + &root, &parent, &children, &number_of_children)) + return; + if (gdk_error_trap_pop ()) + return; + + for (i = 0; i < number_of_children; i++) + { + if (children[i] == GDK_WINDOW_XWINDOW (GTK_WIDGET (overlay_window)->window)) + break; + } + XFree (children); + if (i != number_of_children) + return; + } + + view = pop_window_view_new (); + if (!pop_window_view_set_window (view, window)) + { + g_print ("couldn't redirect 0x%lx to overlay window\n", + GDK_WINDOW_XWINDOW (window)); + return; + } + + g_object_ref (G_OBJECT (window)); + g_object_set_data (G_OBJECT (window), "pop-window-view", g_object_ref (view)); +} + +static void +view_subtract_overlapping_region (PopWindowStack *stack, + GdkWindow *window, + GdkWindow *above_window, + GdkRegion **visible_region) +{ + PopWindowView *view, *above_view; + GdkRegion *difference; + + g_assert (POP_IS_WINDOW_STACK (stack)); + g_assert (GDK_IS_WINDOW (window)); + g_assert (GDK_IS_WINDOW (above_window)); + g_assert (visible_region != NULL); + + above_view = g_object_get_data (G_OBJECT (above_window), "pop-window-view"); + + if (!POP_IS_WINDOW_VIEW (above_view)) + return; + + view = g_object_get_data (G_OBJECT (window), "pop-window-view"); + g_assert (POP_IS_WINDOW_VIEW (view)); + + difference = pop_window_view_get_difference (view, above_view); + + g_assert (difference != NULL); + + if (*visible_region == NULL) + *visible_region = difference; + else + { + gdk_region_intersect (*visible_region, difference); + gdk_region_destroy (difference); + } + +} + +static GdkRegion * +get_view_visible_region (PopWindowStack *stack, + PopWindowView *view) +{ + GdkWindow *window; + GdkRegion *visible_region; + + g_assert (POP_IS_WINDOW_VIEW (view)); + + window = pop_window_view_get_window (view); + + g_assert (GDK_IS_WINDOW (window)); + + visible_region = NULL; + pop_window_stack_above_window_foreach (stack, window, + (PopWindowStackAboveWindowForeachFunc) + view_subtract_overlapping_region, + &visible_region); + if (visible_region == NULL) + visible_region = gdk_region_new (); + + return visible_region; +} + +static void +draw_window_from_stack (PopWindowStack *stack, + GdkWindow *window, + cairo_t *cairo_context) +{ + PopWindowView *view; + GdkRegion *visible_region; + + view = g_object_get_data (G_OBJECT (window), "pop-window-view"); + + if (view == NULL) + return; + + g_assert (POP_IS_WINDOW_VIEW (view)); + +#if 0 + visible_region = get_view_visible_region (stack, view); + + if (!gdk_region_empty (visible_region)) + { +#endif + cairo_save (cairo_context); + pop_window_view_render_to_context (view, cairo_context); + cairo_restore (cairo_context); +#if 0 + } + + gdk_region_destroy (visible_region); +#endif +} + +static void +draw_windows (cairo_t *cairo_context) +{ + pop_window_stack_foreach (stack, + (PopWindowStackForeachFunc) + draw_window_from_stack, cairo_context); +} + +void +add_window_from_stack_to_list (PopWindowStack *stack, + GdkWindow *window) +{ + add_window_to_list (g_object_ref (window)); +} + +static void +get_initial_view_list (void) +{ + + stack = pop_window_stack_get_for_screen (NULL); + + pop_window_stack_foreach (stack, + (PopWindowStackForeachFunc) + add_window_from_stack_to_list, NULL); +} + +static gboolean +on_expose_event (GtkWidget *widget, + GdkEventExpose *event) +{ + cairo_t *cairo_context; + GdkRectangle monitor_area; + GdkScreen *screen; + double x_scale_factor, y_scale_factor; + + screen = gtk_widget_get_screen (widget); + gdk_screen_get_monitor_geometry (screen, + gdk_screen_get_monitor_at_window (screen, widget->window), &monitor_area); + + x_scale_factor = widget->allocation.width / ((double) monitor_area.width); + y_scale_factor = widget->allocation.height / ((double) monitor_area.height); + + cairo_context = gdk_cairo_create (widget->window); + //cairo_scale (cairo_context, x_scale_factor, y_scale_factor); + //cairo_set_source_rgb (cairo_context, 0.5, 0.5, 0.6); + //cairo_paint (cairo_context); + draw_windows (cairo_context); + cairo_destroy (cairo_context); + + return FALSE; +} + +static void +on_map (GtkWidget *widget) +{ + get_initial_view_list (); +} + +static void +remove_window_from_stack_from_list (PopWindowStack *stack, + GdkWindow *window) +{ + remove_window_from_list (window); +} + +static gboolean +on_close (GtkWidget *widget) +{ + pop_window_stack_foreach (stack, + (PopWindowStackForeachFunc) + remove_window_from_stack_from_list, + NULL); + g_object_unref (stack); + stack = NULL; + + gtk_main_quit (); + + return FALSE; +} + +static void +on_damage_event (XDamageNotifyEvent *damage_event, + PopOverlayWindow *overlay_window) +{ + gtk_widget_queue_draw (GTK_WIDGET (overlay_window)); +} + +static void +on_create_window_event (XCreateWindowEvent *window_event, + PopOverlayWindow *overlay_window) +{ + GdkWindow *window; + + if (window_event->parent != GDK_ROOT_WINDOW ()) + return; + + window = gdk_window_foreign_new (window_event->window); + + if (window != NULL) + add_window_to_list (window); + else + g_print ("recently created window 0x%lx has already been destroyed\n", + window_event->window); +} + +static void +on_destroy_window_event (XDestroyWindowEvent *window_event, + PopOverlayWindow *overlay_window) +{ + GdkWindow *window; + + window = gdk_window_lookup (window_event->window); + if (window == NULL) + return; + remove_window_from_list (window); + gtk_widget_queue_draw (GTK_WIDGET (overlay_window)); +} + +static gboolean +x_event_is_damage_event (XEvent *x_event) +{ + return x_event->type == damage_extension_event_base + XDamageNotify; +} + +static gboolean +x_event_is_create_window_event (XEvent *x_event) +{ + return /* x_event->type == CreateNotify || */ x_event->type == MapNotify; +} + +static gboolean +x_event_is_destroy_window_event (XEvent *x_event) +{ + return /* x_event->type == DestroyNotify || */ x_event->type == UnmapNotify; +} + +static GdkFilterReturn +on_event (XEvent *x_event, + GdkEvent *event, + PopOverlayWindow *overlay_window) +{ + + if (((GdkEventAny *) event)->window == GTK_WIDGET (overlay_window)->window) + return GDK_FILTER_CONTINUE; + if (x_event_is_damage_event (x_event)) + on_damage_event ((XDamageNotifyEvent *) x_event, overlay_window); + else if (x_event_is_create_window_event (x_event)) + on_create_window_event ((XCreateWindowEvent *) x_event, overlay_window); + else if (x_event_is_destroy_window_event (x_event)) + on_destroy_window_event ((XDestroyWindowEvent *) x_event, overlay_window); + + gtk_widget_queue_draw (GTK_WIDGET (overlay_window)); + + return GDK_FILTER_CONTINUE; +} + +int +main (int argc, + char **argv) +{ + GdkWindow *root_window; + + gtk_init (&argc, &argv); + + if (!composite_is_available ()) + { + g_printerr ("composite extension not found\n"); + return 1; + } + + if (!initialize_damage_extension ()) + { + g_printerr ("damage extension not found\n"); + return 2; + } + + initialize_shape_extension (); + + overlay_window = pop_overlay_window_new (); + overlay_window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_widget_set_app_paintable (GTK_WIDGET (overlay_window), TRUE); + gtk_window_set_default_size (GTK_WINDOW (overlay_window), 640, 480); +#if 0 + gtk_widget_realize (GTK_WIDGET (overlay_window)); +#endif + + g_signal_connect (G_OBJECT (overlay_window), "expose-event", + G_CALLBACK (on_expose_event), NULL); + g_signal_connect (G_OBJECT (overlay_window), "map", + G_CALLBACK (on_map), NULL); + g_signal_connect (G_OBJECT (overlay_window), "delete-event", + G_CALLBACK (on_close), NULL); + + root_window = gdk_get_default_root_window (); + gdk_window_set_events (root_window, GDK_SUBSTRUCTURE_MASK); + gdk_window_add_filter (NULL, (GdkFilterFunc) on_event, overlay_window); + + listener = pop_event_listener_get_default (); + + gtk_widget_show (overlay_window); + + g_print ("overlay window is 0x%lx\n", GDK_WINDOW_XWINDOW (overlay_window->window)); + + gtk_main(); + + return 0; +} diff --git a/src/pop-event-listener.c b/src/pop-event-listener.c new file mode 100644 index 0000000..9667b98 --- /dev/null +++ b/src/pop-event-listener.c @@ -0,0 +1,1165 @@ +/* pop-event-listener.c - emits signals based on display events + * + * Copyright (C) 2007 Ray Strode <rstrode@redhat.com> + * + * 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, 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 "config.h" +#include "pop-event-listener.h" + +#include <errno.h> +#include <string.h> + +#include <glib.h> +#include <glib-object.h> +#include <glib/gi18n.h> + +#include <X11/extensions/shape.h> +#include <X11/extensions/Xdamage.h> + +#include <gdk/gdkx.h> +#include <gdk/gdk.h> + +#include <gtk/gtk.h> + +#include "pop-marshal.h" + +static void pop_event_listener_do_finalize (GObject *object); +static void pop_event_listener_destroy_handler (PopEventListener *listener, + GdkEvent *event, + GdkWindow *window); +static void pop_event_listener_configure_handler (PopEventListener *listener, + GdkEvent *event, + GdkWindow *window); +static void pop_event_listener_map_handler (PopEventListener *listener, + GdkEvent *event, + GdkWindow *window); +static void pop_event_listener_unmap_handler (PopEventListener *listener, + GdkEvent *event, + GdkWindow *window); +static void pop_event_listener_damage_handler (PopEventListener *listener, + GdkEvent *event, + GdkWindow *window); +static void pop_event_listener_shape_handler (PopEventListener *listener, + GdkEvent *event, + GdkWindow *window); + +static void pop_event_listener_reparent_handler (PopEventListener *listener, + GdkEvent *event, + GdkWindow *window); +static void pop_event_listener_circulate_handler (PopEventListener *listener, + GdkEvent *event, + GdkWindow *window); + +static void pop_event_listener_stop (PopEventListener *listener); + + +static int pop_event_listener_damage_notify_event_type, + pop_event_listener_shape_notify_event_type; + +struct _PopEventListenerPrivate +{ + GHashTable *event_handlers; +}; + + +/* XXX: This is a bit of a hack. gdk doesn't have + * events for some of X extensions we need, so we + * are rolling our own using an event filter. + * + * The event types are given high negative values so they + * less likely to conflict with gdk in the future, and all + * the event structures are less than sizeof (GdkEvent) and + * have the same first few members as GdkEventAny + */ +typedef enum +{ + POP_DAMAGE = -1000, + POP_SHAPE = -1001, + POP_REPARENT = -1002, + POP_CIRCULATE = -1003, +} PopEventType; + +typedef struct _PopEventDamage PopEventDamage; +struct _PopEventDamage +{ + PopEventType type; + GdkWindow *window; + gint8 send_event; + GdkRectangle area; +}; + +typedef struct _PopEventShape PopEventShape; +struct _PopEventShape +{ + PopEventType type; + GdkWindow *window; + gint8 send_event; +}; + +typedef struct _PopEventReparent PopEventReparent; +struct _PopEventReparent +{ + PopEventType type; + GdkWindow *window; + gint8 send_event; + GdkWindow *parent; + gint x; + gint y; +}; + +typedef enum _PopEventCirculatePlacement PopEventCirculatePlacement; +enum _PopEventCirculatePlacement +{ + POP_EVENT_CIRCULATE_PLACEMENT_TOP, + POP_EVENT_CIRCULATE_PLACEMENT_BOTTOM +}; + +typedef struct _PopEventCirculate PopEventCirculate; +struct _PopEventCirculate +{ + PopEventType type; + GdkWindow *window; + gint8 send_event; + PopEventCirculatePlacement placement; +}; + +typedef void (* PopEventListenerEventHandler) (PopEventListener *listener, + GdkEvent *event, + GdkWindow *window); + +static void +pop_event_listener_class_install_signals (PopEventListenerClass *listener_class); + +#if 0 +static void +pop_event_listener_class_install_properties +(PopEventListenerClass * listener_class); + +static void pop_event_listener_set_property (GObject * object, + guint prop_id, + const GValue * value, + GParamSpec * pspec); +static void pop_event_listener_get_property (GObject * object, + guint prop_id, + GValue * value, + GParamSpec * pspec); + +enum +{ + PROP_0 = 0, + PROP_BAR +}; + +#define POP_EVENT_LISTENER_DEFAULT_BAR 1 + +#endif + +enum +{ + WINDOW_CREATED = 0, + WINDOW_DESTROYED, + WINDOW_SHOWN, + WINDOW_HIDDEN, + WINDOW_MOVED, + WINDOW_RESIZED, + WINDOW_RESHAPED, + WINDOW_REPARENTED, + WINDOW_REDRAWN, + WINDOW_LAYERED_UNDER, + WINDOW_LAYERED_ON_TOP, + WINDOW_LAYERED_ON_BOTTOM, + NUMBER_OF_SIGNALS +}; + +static guint pop_event_listener_signals[NUMBER_OF_SIGNALS]; + +G_DEFINE_TYPE (PopEventListener, pop_event_listener, G_TYPE_OBJECT); + +static void +pop_event_listener_class_init (PopEventListenerClass *listener_class) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (listener_class); + + object_class->finalize = pop_event_listener_do_finalize; + +#if 0 + pop_event_listener_class_install_properties (listener_class); +#endif + pop_event_listener_class_install_signals (listener_class); + + g_type_class_add_private (listener_class, + sizeof (PopEventListenerPrivate)); +} + +static void +created (PopEventListener *listener, + GdkWindow *window) +{ + g_print ("created 0x%lx\n", GDK_WINDOW_XWINDOW (window)); +} + +static void +destroyed (PopEventListener *listener, + GdkWindow *window) +{ + g_print ("destroyed 0x%lx\n", GDK_WINDOW_XWINDOW (window)); +} + +static void +moved (PopEventListener *listener, + GdkWindow *window) +{ + g_print ("moved 0x%lx\n", GDK_WINDOW_XWINDOW (window)); +} + +static void +resized (PopEventListener *listener, + GdkWindow *window) +{ + g_print ("resized 0x%lx\n", GDK_WINDOW_XWINDOW (window)); +} + +static void +redrawn (PopEventListener *listener, + GdkWindow *window, + GdkRectangle *area) +{ +#if 0 + g_print ("redrawn 0x%lx\n", GDK_WINDOW_XWINDOW (window)); +#endif +} + +static void +reparented (PopEventListener *listener, + GdkWindow *window, + GdkWindow *parent) +{ + g_print ("reparented 0x%lx\n", GDK_WINDOW_XWINDOW (window)); +} + +static void +shown (PopEventListener *listener, + GdkWindow *window) +{ + g_print ("shown 0x%lx\n", GDK_WINDOW_XWINDOW (window)); +} + +static void +hidden (PopEventListener *listener, + GdkWindow *window) +{ + g_print ("hidden 0x%lx\n", GDK_WINDOW_XWINDOW (window)); +} + +static void +pop_event_listener_class_install_signals (PopEventListenerClass *listener_class) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (listener_class); + + pop_event_listener_signals[WINDOW_CREATED] = + g_signal_new ("window-created", G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + G_STRUCT_OFFSET (PopEventListenerClass, window_created), + NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, + G_TYPE_OBJECT); + listener_class->window_created = created; + + pop_event_listener_signals[WINDOW_DESTROYED] = + g_signal_new ("window-destroyed", G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + G_STRUCT_OFFSET (PopEventListenerClass, window_destroyed), + NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, + G_TYPE_OBJECT); + listener_class->window_destroyed = destroyed; + + pop_event_listener_signals[WINDOW_SHOWN] = + g_signal_new ("window-shown", G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + G_STRUCT_OFFSET (PopEventListenerClass, window_shown), + NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, + G_TYPE_OBJECT); + listener_class->window_shown = shown; + + pop_event_listener_signals[WINDOW_HIDDEN] = + g_signal_new ("window-hidden", G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + G_STRUCT_OFFSET (PopEventListenerClass, window_hidden), + NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, + G_TYPE_OBJECT); + listener_class->window_hidden = hidden; + + pop_event_listener_signals[WINDOW_MOVED] = + g_signal_new ("window-moved", G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + G_STRUCT_OFFSET (PopEventListenerClass, window_moved), + NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, + G_TYPE_OBJECT); + listener_class->window_moved = moved; + + pop_event_listener_signals[WINDOW_RESIZED] = + g_signal_new ("window-resized", G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + G_STRUCT_OFFSET (PopEventListenerClass, window_resized), + NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, + G_TYPE_OBJECT); + listener_class->window_resized = resized; + + pop_event_listener_signals[WINDOW_RESHAPED] = + g_signal_new ("window-reshaped", G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + G_STRUCT_OFFSET (PopEventListenerClass, window_reshaped), + NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, + G_TYPE_OBJECT); + listener_class->window_reshaped = NULL; + + pop_event_listener_signals[WINDOW_REPARENTED] = + g_signal_new ("window-reparented", G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + G_STRUCT_OFFSET (PopEventListenerClass, window_reparented), + NULL, NULL, pop_marshal_VOID__OBJECT_OBJECT, G_TYPE_NONE, 2, + G_TYPE_OBJECT, G_TYPE_OBJECT); + listener_class->window_reparented = reparented; + + pop_event_listener_signals[WINDOW_REDRAWN] = + g_signal_new ("window-redrawn", G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + G_STRUCT_OFFSET (PopEventListenerClass, window_redrawn), + NULL, NULL, pop_marshal_VOID__OBJECT_POINTER, G_TYPE_NONE, 2, + G_TYPE_OBJECT, G_TYPE_POINTER); + listener_class->window_redrawn = redrawn; + + pop_event_listener_signals[WINDOW_LAYERED_UNDER] = + g_signal_new ("window-layered-under", G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + G_STRUCT_OFFSET (PopEventListenerClass, window_layered_under), + NULL, NULL, pop_marshal_VOID__OBJECT_OBJECT, G_TYPE_NONE, 2, + G_TYPE_OBJECT, G_TYPE_OBJECT); + listener_class->window_layered_under = NULL; + + pop_event_listener_signals[WINDOW_LAYERED_ON_TOP] = + g_signal_new ("window-layered-on-top", G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + G_STRUCT_OFFSET (PopEventListenerClass, window_layered_on_top), + NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, + G_TYPE_OBJECT); + listener_class->window_layered_on_top = NULL; + + pop_event_listener_signals[WINDOW_LAYERED_ON_BOTTOM] = + g_signal_new ("window-layered-on-bottom", G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + G_STRUCT_OFFSET (PopEventListenerClass, window_layered_on_bottom), + NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, + G_TYPE_OBJECT); + listener_class->window_layered_on_bottom = NULL; +} + +#if 0 +static void +pop_event_listener_class_install_properties (PopEventListenerClass *listener_class) +{ + GObjectClass *object_class; + GParamSpec *bar_spec; + + object_class = G_OBJECT_CLASS (listener_class); + object_class->set_property = pop_event_listener_set_property; + object_class->get_property = pop_event_listener_get_property; + + bar_spec = g_param_spec_int ("bar", _("Bar"), + _("The amount of bar"), + 0, G_MAXINT, + POP_EVENT_LISTENER_DEFAULT_BAR, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT); + g_object_class_install_property (object_class, PROP_BAR, bar_spec); +} +#endif + +static void +pop_event_listener_initialize_damage_extension (PopEventListener *listener) +{ + int event_base, error_base; + XDamageQueryExtension (GDK_DISPLAY (), &event_base, &error_base); + + pop_event_listener_damage_notify_event_type = event_base + XDamageNotify; +} + +static void +pop_event_listener_initialize_shape_extension (PopEventListener *listener) +{ + int event_base, error_base; + XShapeQueryExtension (GDK_DISPLAY (), &event_base, &error_base); + + pop_event_listener_shape_notify_event_type = event_base + ShapeNotify; +} + +static void +pop_event_listener_register_event_handlers (PopEventListener *listener) +{ + listener->priv->event_handlers = g_hash_table_new (NULL, NULL); + + g_hash_table_insert (listener->priv->event_handlers, + GINT_TO_POINTER (GDK_DESTROY), + pop_event_listener_destroy_handler); + + g_hash_table_insert (listener->priv->event_handlers, + GINT_TO_POINTER (GDK_CONFIGURE), + pop_event_listener_configure_handler); + + g_hash_table_insert (listener->priv->event_handlers, + GINT_TO_POINTER (GDK_MAP), + pop_event_listener_map_handler); + + g_hash_table_insert (listener->priv->event_handlers, + GINT_TO_POINTER (GDK_UNMAP), + pop_event_listener_unmap_handler); + + g_hash_table_insert (listener->priv->event_handlers, + GINT_TO_POINTER (POP_DAMAGE), + pop_event_listener_damage_handler); + + g_hash_table_insert (listener->priv->event_handlers, + GINT_TO_POINTER (POP_SHAPE), + pop_event_listener_shape_handler); + + g_hash_table_insert (listener->priv->event_handlers, + GINT_TO_POINTER (POP_REPARENT), + pop_event_listener_reparent_handler); + + g_hash_table_insert (listener->priv->event_handlers, + GINT_TO_POINTER (POP_CIRCULATE), + pop_event_listener_circulate_handler); + +} + +static void +pop_event_listener_init (PopEventListener *listener) +{ + listener->priv = G_TYPE_INSTANCE_GET_PRIVATE (listener, + POP_TYPE_EVENT_LISTENER, + PopEventListenerPrivate); + pop_event_listener_initialize_damage_extension (listener); + pop_event_listener_initialize_shape_extension (listener); + pop_event_listener_register_event_handlers (listener); +} + +static void +pop_event_listener_do_finalize (GObject *object) +{ + PopEventListener *listener; + GObjectClass *parent_class; + + listener = POP_EVENT_LISTENER (object); + + g_hash_table_destroy (listener->priv->event_handlers); + + parent_class = G_OBJECT_CLASS (pop_event_listener_parent_class); + + pop_event_listener_stop (listener); + + if (parent_class->finalize != NULL) + parent_class->finalize (object); +} + +#if 0 +static void +pop_event_listener_set_property (GObject * object, + guint prop_id, + const GValue * value, + GParamSpec * pspec) +{ + PopEventListener *listener = POP_EVENT_LISTENER (object); + + switch (prop_id) + { + case PROP_BAR: + pop_event_listener_set_bar (listener, g_value_get_int (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +pop_event_listener_get_property (GObject * object, + guint prop_id, + GValue * value, GParamSpec * pspec) +{ + PopEventListener *listener = POP_EVENT_LISTENER (object); + + switch (prop_id) + { + case PROP_BAR: + g_value_set_int (value, pop_event_listener_get_bar (listener)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} +#endif + +GQuark +pop_event_listener_error_quark (void) +{ + static GQuark error_quark = 0; + + if (error_quark == 0) + error_quark = g_quark_from_static_string ("pop-event-listener"); + + return error_quark; +} + +static PopEventListener * +pop_event_listener_new (GdkDisplay *display) +{ + PopEventListener *listener; + + listener = g_object_new (POP_TYPE_EVENT_LISTENER, NULL); + + return listener; +} + +#if 0 +void +pop_event_listener_set_bar (PopEventListener * listener, int bar) +{ + if (listener->priv->bar != bar) + { + listener->priv->bar = bar; + g_object_notify (G_OBJECT (listener), "bar"); + } +} + +int +pop_event_listener_get_bar (PopEventListener * listener) +{ + return listener->priv->bar; +} +#endif + +static GQuark +pop_event_listener_get_signal_detail_for_window (PopEventListener *listener, + GdkWindow *window) +{ + gchar window_id[sizeof ("ffffffff")]; + g_snprintf (window_id, sizeof (window_id), + "%lx", GDK_WINDOW_XWINDOW (window)); + + return g_quark_from_string (window_id); +} + +static void +pop_event_listener_destroy_handler (PopEventListener *listener, + GdkEvent *event, + GdkWindow *window) +{ + g_signal_emit (G_OBJECT (listener), + pop_event_listener_signals[WINDOW_CREATED], + pop_event_listener_get_signal_detail_for_window (listener, + window), + window); +} + +static gboolean +pop_event_listener_get_cached_window_geometry (PopEventListener *listener, + GdkWindow *window, + int *x, + int *y, + int *width, + int *height) +{ + gboolean has_cached_geometry; + + has_cached_geometry = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (window), + "pop-event-listener-" + "has-cached-" + "window-geometry")); + + if (!has_cached_geometry) + return FALSE; + + if (x) + *x = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (window), + "pop-event-listener-cached-window-x")); + + if (y) + *y = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (window), + "pop-event-listener-cached-window-y")); + + if (width) + *width = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (window), + "pop-event-listener-cached-window-width")); + + if (height) + *height = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (window), + "pop-event-listener-cached-window-height")); + + return TRUE; +} + +static void +pop_event_listener_cache_window_geometry (PopEventListener *listener, + GdkWindow *window, + int x, + int y, + int width, + int height) +{ + g_object_set_data (G_OBJECT (window), + "pop-event-listener-cached-window-x", + GINT_TO_POINTER (x)); + + g_object_set_data (G_OBJECT (window), + "pop-event-listener-cached-window-y", + GINT_TO_POINTER (y)); + + g_object_set_data (G_OBJECT (window), + "pop-event-listener-cached-window-width", + GINT_TO_POINTER (width)); + + g_object_set_data (G_OBJECT (window), + "pop-event-listener-cached-window-height", + GINT_TO_POINTER (height)); + + g_object_set_data (G_OBJECT (window), + "pop-event-listener-has-cached-window-geometry", + GINT_TO_POINTER (TRUE)); +} + +static gboolean +pop_event_listener_get_cached_window_layer (PopEventListener *listener, + GdkWindow *window, + GdkWindow **window_above) +{ + gboolean has_cached_layer; + GdkWindow *cached_window_above, *cached_window_below; + + has_cached_layer = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (window), + "pop-event-listener-" + "has-cached-" + "window-layer")); + + if (!has_cached_layer) + return FALSE; + + cached_window_above = g_object_get_data (G_OBJECT (window), + "pop-event-listener-cached-window-" + "layer-window-above"); + + if (cached_window_above) + { + cached_window_below = g_object_get_data (G_OBJECT (cached_window_above), + "pop-event-listener-cached-window-" + "layer-window-below"); + + /* referential integrity is broken, assume it's stale + */ + if (cached_window_below != window) + { + g_object_set_data (G_OBJECT (window), + "pop-event-listener-cached-window-" + "layer-window-above", NULL); + g_object_set_data (G_OBJECT (window), + "pop-event-listener-has-cached-window-layer", + GINT_TO_POINTER (FALSE)); + g_object_unref (cached_window_above); + return FALSE; + } + } + + return TRUE; +} + +static void +pop_event_listener_cache_window_layer (PopEventListener *listener, + GdkWindow *window, + GdkWindow *window_above) +{ + GdkWindow *old_window_above; + + old_window_above = g_object_get_data (G_OBJECT (window), + "pop-event-listener-cached-window-layer-window-above"); + if (old_window_above != NULL) + g_object_unref (old_window_above); + + g_object_set_data (G_OBJECT (window), + "pop-event-listener-cached-window-layer-window-above", + window_above); + if (window_above != NULL) + { + g_object_ref (window_above); + + g_object_set_data (G_OBJECT (window_above), + "pop-event-listener-cached-window-layer-window-below", + g_object_ref (window)); + } + + g_object_set_data (G_OBJECT (window), + "pop-event-listener-has-cached-window-layer", + GINT_TO_POINTER (TRUE)); +} + +static void +pop_event_listener_configure_handler (PopEventListener *listener, + GdkEvent *event, + GdkWindow *window) +{ + GdkEventConfigure *configure_event; + GdkWindow *new_window_above, *window_above; + gint x, y, width, height; + gboolean changed_position, changed_size, changed_layer; + + configure_event = (GdkEventConfigure *) event; + + new_window_above = g_object_get_data (G_OBJECT (window), + "pop-event-listener-window-above"); + g_assert ((new_window_above == NULL) || (G_IS_OBJECT (new_window_above))); + g_object_set_data (G_OBJECT (window), + "pop-event-listener-window-above", + NULL); + + if (pop_event_listener_get_cached_window_geometry (listener, window, + &x, &y, &width, &height)) + { + changed_position = (x != configure_event->x) || (y != configure_event->y); + changed_size = (width != configure_event->width) + || (height != configure_event->height); + } + else + { + changed_position = TRUE; + changed_size = TRUE; + } + + if (pop_event_listener_get_cached_window_layer (listener, window, &window_above)) + changed_layer = window_above != new_window_above; + else + changed_layer = TRUE; + + PopWindowView *view; + view = g_object_get_data (G_OBJECT (window), "pop-window-view"); + if (changed_position) + { + g_signal_emit (G_OBJECT (listener), + pop_event_listener_signals[WINDOW_MOVED], + pop_event_listener_get_signal_detail_for_window (listener, + window), + window); + } + + if (changed_size) + { + g_signal_emit (G_OBJECT (listener), + pop_event_listener_signals[WINDOW_RESIZED], + pop_event_listener_get_signal_detail_for_window (listener, + window), + window); + } + + if (changed_layer) + { + g_print ("window 0x%lx changed layers\n", GDK_WINDOW_XWINDOW (window)); + if (new_window_above != NULL) + g_signal_emit (G_OBJECT (listener), + pop_event_listener_signals[WINDOW_LAYERED_UNDER], + pop_event_listener_get_signal_detail_for_window (listener, + window), + window, + new_window_above); + else + g_signal_emit (G_OBJECT (listener), + pop_event_listener_signals[WINDOW_LAYERED_ON_BOTTOM], + pop_event_listener_get_signal_detail_for_window (listener, + window), + window); + pop_event_listener_cache_window_layer (listener, window, new_window_above); + } + + if (new_window_above != NULL) + g_object_unref (new_window_above); + + pop_event_listener_cache_window_geometry (listener, window, + configure_event->x, + configure_event->y, + configure_event->width, + configure_event->height); +} + +static void +pop_event_listener_map_handler (PopEventListener *listener, + GdkEvent *event, + GdkWindow *window) +{ + g_signal_emit (G_OBJECT (listener), + pop_event_listener_signals[WINDOW_SHOWN], + pop_event_listener_get_signal_detail_for_window (listener, + window), + window); +} + +static void +pop_event_listener_unmap_handler (PopEventListener *listener, + GdkEvent *event, + GdkWindow *window) +{ + + g_signal_emit (G_OBJECT (listener), + pop_event_listener_signals[WINDOW_HIDDEN], + pop_event_listener_get_signal_detail_for_window (listener, + window), + window); +} + +static void +pop_event_listener_damage_handler (PopEventListener *listener, + GdkEvent *event, + GdkWindow *window) +{ + PopEventDamage *damage_event; + + g_assert (POP_IS_EVENT_LISTENER (listener)); + g_assert (event->type == POP_DAMAGE); + + damage_event = (PopEventDamage *) event; + + g_signal_emit (G_OBJECT (listener), + pop_event_listener_signals[WINDOW_REDRAWN], + pop_event_listener_get_signal_detail_for_window (listener, + window), + window, &damage_event->area); +} + +static void +pop_event_listener_shape_handler (PopEventListener *listener, + GdkEvent *event, + GdkWindow *window) +{ + PopEventShape *shape_event; + + g_assert (POP_IS_EVENT_LISTENER (listener)); + g_assert (event->type == POP_SHAPE); + + shape_event = (PopEventShape *) event; + g_signal_emit (G_OBJECT (listener), + pop_event_listener_signals[WINDOW_RESHAPED], + pop_event_listener_get_signal_detail_for_window (listener, + window), + window); +} + +static void +pop_event_listener_reparent_handler (PopEventListener *listener, + GdkEvent *event, + GdkWindow *window) +{ + PopEventReparent *reparent_event; + + g_assert (POP_IS_EVENT_LISTENER (listener)); + g_assert (event->type == POP_REPARENT); + + reparent_event = (PopEventReparent *) event; + + g_signal_emit (G_OBJECT (listener), + pop_event_listener_signals[WINDOW_REPARENTED], + pop_event_listener_get_signal_detail_for_window (listener, + window), + window, reparent_event->parent); +} + +static void +pop_event_listener_circulate_handler (PopEventListener *listener, + GdkEvent *event, + GdkWindow *window) +{ + PopEventCirculate *circulate_event; + + g_assert (POP_IS_EVENT_LISTENER (listener)); + g_assert (event->type == POP_CIRCULATE); + + circulate_event = (PopEventCirculate *) event; + + if (circulate_event->placement == POP_EVENT_CIRCULATE_PLACEMENT_TOP) + { + g_print ("window 0x%lx moved to top of stack\n", GDK_WINDOW_XWINDOW (window)); + g_signal_emit (G_OBJECT (listener), + pop_event_listener_signals[WINDOW_LAYERED_ON_TOP], + pop_event_listener_get_signal_detail_for_window (listener, + window), + window); + } + else if (circulate_event->placement == POP_EVENT_CIRCULATE_PLACEMENT_BOTTOM) + { + g_print ("window 0x%lx moved to bottom of stack\n", GDK_WINDOW_XWINDOW (window)); + g_signal_emit (G_OBJECT (listener), + pop_event_listener_signals[WINDOW_LAYERED_ON_BOTTOM], + pop_event_listener_get_signal_detail_for_window (listener, + window), + window); + } +} + +static void +pop_event_listener_process_event_for_window (PopEventListener *listener, + GdkEvent *event, + GdkWindow *window) +{ + PopEventListenerEventHandler event_handler; + + event_handler = (PopEventListenerEventHandler) + g_hash_table_lookup (listener->priv->event_handlers, + GINT_TO_POINTER (event->type)); + + if (event_handler == NULL) + return; + + event_handler (listener, event, window); +} + +static void +pop_event_listener_event_handler (GdkEvent *event, + gpointer data) +{ + PopEventListener *listener; + + g_assert (event != NULL); + g_assert (POP_IS_EVENT_LISTENER (data)); + + listener = POP_EVENT_LISTENER (data); + + if (event->any.window != NULL) + pop_event_listener_process_event_for_window (listener, event, + event->any.window); + + /* chain up to the default handler if it's not one of our custom + * events + */ + if (event->type >= GDK_NOTHING) + gtk_main_do_event (event); +} + +GdkFilterReturn +pop_event_listener_filter_damage_event (XDamageNotifyEvent *x_damage_event, + PopEventDamage *damage_event, + GdkWindow *window) +{ + damage_event->window = g_object_ref (window); + damage_event->type = POP_DAMAGE; + damage_event->send_event = FALSE; + damage_event->area.x = x_damage_event->area.x; + damage_event->area.y = x_damage_event->area.y; + damage_event->area.width = x_damage_event->area.width; + damage_event->area.height = x_damage_event->area.height; + + return GDK_FILTER_TRANSLATE; +} + +GdkFilterReturn +pop_event_listener_filter_shape_event (XShapeEvent *x_shape_event, + PopEventShape *shape_event, + GdkWindow *window) +{ + shape_event->window = g_object_ref (window); + shape_event->type = POP_SHAPE; + shape_event->send_event = FALSE; + + return GDK_FILTER_TRANSLATE; +} + +GdkFilterReturn +pop_event_listener_filter_reparent_event (XReparentEvent *x_reparent_event, + PopEventReparent *reparent_event, + GdkWindow *window) +{ + GdkWindow *parent; + + reparent_event->window = g_object_ref (window); + reparent_event->type = POP_REPARENT; + reparent_event->send_event = FALSE; + + parent = gdk_window_lookup (x_reparent_event->parent); + + if (parent == NULL) + parent = gdk_window_foreign_new (x_reparent_event->parent); + + reparent_event->parent = parent; + + return GDK_FILTER_TRANSLATE; +} + +GdkFilterReturn +pop_event_listener_filter_circulate_event (XCirculateEvent *x_circulate_event, + PopEventCirculate *circulate_event, + GdkWindow *window) +{ + circulate_event->window = g_object_ref (window); + circulate_event->type = POP_CIRCULATE; + circulate_event->send_event = FALSE; + + g_assert ((x_circulate_event->place == PlaceOnTop) || + (x_circulate_event->place == PlaceOnBottom)); + + if (x_circulate_event->place == PlaceOnTop) + circulate_event->placement = POP_EVENT_CIRCULATE_PLACEMENT_TOP; + else if (x_circulate_event->place == PlaceOnBottom) + circulate_event->placement = POP_EVENT_CIRCULATE_PLACEMENT_BOTTOM; + + return GDK_FILTER_TRANSLATE; +} + +GdkFilterReturn +pop_event_listener_filter_configure_event (XConfigureEvent *x_configure_event, + GdkEventConfigure *configure_event, + GdkWindow *window) +{ + GdkWindow *window_above; + + /* Unfortunately, GdkEventConfigure doesn't have the same "above" member that + * the XConfigureEvent has, and we need it to report some stacking events. + */ + + if (x_configure_event->above == None) + window_above = NULL; + else + window_above = gdk_window_foreign_new (x_configure_event->above); + + g_object_set_data (G_OBJECT (window), "pop-event-listener-window-above", + window_above); + + if (window_above != NULL) + g_object_ref (window_above); + + return GDK_FILTER_CONTINUE; +} + +GdkFilterReturn +pop_event_listener_filter (XEvent *x_event, + GdkEvent *event, + PopEventListener *listener) +{ + GdkWindow *window; + + window = gdk_window_lookup (x_event->xany.window); + + if (window == NULL) + return GDK_FILTER_CONTINUE; + + if (x_event->type == pop_event_listener_damage_notify_event_type) + { + g_assert (sizeof (PopEventDamage) <= sizeof (*event)); + return pop_event_listener_filter_damage_event ((XDamageNotifyEvent *) x_event, + (PopEventDamage *) event, + window); + } + else if (x_event->type == pop_event_listener_shape_notify_event_type) + { + g_assert (sizeof (PopEventShape) <= sizeof (*event)); + return pop_event_listener_filter_shape_event ((XShapeEvent *) x_event, + (PopEventShape *) event, + window); + } + else if (x_event->type == ReparentNotify) + { + g_assert (sizeof (PopEventReparent) <= sizeof (*event)); + return pop_event_listener_filter_reparent_event ((XReparentEvent *) x_event, + (PopEventReparent *) event, + window); + } + else if (x_event->type == CirculateNotify) + { + g_assert (sizeof (PopEventCirculate) <= sizeof (*event)); + return pop_event_listener_filter_circulate_event ((XCirculateEvent *) x_event, + (PopEventCirculate *) event, + window); + } + else if (x_event->type == ConfigureNotify) + { + return pop_event_listener_filter_configure_event ((XConfigureEvent *) x_event, + (GdkEventConfigure *) event, + window); + } + + return GDK_FILTER_CONTINUE; +} + +static void +pop_event_listener_start (PopEventListener *listener) +{ + /* used for questionable hack to get damage and shape events reported + */ + gdk_window_add_filter (NULL, (GdkFilterFunc) pop_event_listener_filter, + listener); + + /* translates gdk events to signals on the listener object + */ + gdk_event_handler_set (pop_event_listener_event_handler, + listener, NULL); +} + +static void +pop_event_listener_stop (PopEventListener *listener) +{ + gdk_event_handler_set ((GdkEventFunc) gtk_main_do_event, NULL, NULL); +} + +PopEventListener * +pop_event_listener_get_default (void) +{ + static PopEventListener *listener = NULL; + + if (listener == NULL) + { + listener = pop_event_listener_new (gdk_display_get_default ()); + pop_event_listener_start (listener); + } + + return g_object_ref (listener); +} + +#ifdef POP_EVENT_LISTENER_ENABLE_TEST + +#include <stdio.h> +#include <glib.h> + +int +main (int argc, char **argv) +{ + PopEventListener *listener; + int exit_code; + + g_log_set_always_fatal (G_LOG_LEVEL_ERROR + | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING); + + g_type_init (); + + g_message ("creating instance of 'event listener' object..."); + listener = pop_event_listener_new (gdk_display_get_default ()); + g_message ("'event listener' object created successfully"); + + g_message + ("destroying previously created 'event listener' object..."); + g_object_unref (listener); + g_message ("'event listener' object destroyed successfully"); + + exit_code = 0; + + return exit_code; +} +#endif /* POP_EVENT_LISTENER_ENABLE_TEST */ diff --git a/src/pop-event-listener.h b/src/pop-event-listener.h new file mode 100644 index 0000000..fce4fcc --- /dev/null +++ b/src/pop-event-listener.h @@ -0,0 +1,88 @@ +/* pop-event-listener.h - Listens for events on toplevel window and + * passes them to the appropriate window model + * + * Copyright (C) 2007 Ray Strode <rstrode@redhat.com> + * + * 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, 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 POP_EVENT_LISTENER_H +#define POP_EVENT_LISTENER_H + +#include <glib.h> +#include <glib-object.h> + +#include <gdk/gdk.h> + +#include "pop-window-view.h" + +G_BEGIN_DECLS +#define POP_TYPE_EVENT_LISTENER (pop_event_listener_get_type ()) +#define POP_EVENT_LISTENER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), POP_TYPE_EVENT_LISTENER, PopEventListener)) +#define POP_EVENT_LISTENER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), POP_TYPE_EVENT_LISTENER, PopEventListenerClass)) +#define POP_IS_EVENT_LISTENER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), POP_TYPE_EVENT_LISTENER)) +#define POP_IS_EVENT_LISTENER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), POP_TYPE_EVENT_LISTENER)) +#define POP_EVENT_LISTENER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), POP_TYPE_EVENT_LISTENER, PopEventListenerClass)) +#define POP_EVENT_LISTENER_ERROR (pop_event_listener_error_quark ()) +typedef struct _PopEventListener PopEventListener; +typedef struct _PopEventListenerClass PopEventListenerClass; +typedef struct _PopEventListenerPrivate PopEventListenerPrivate; +typedef enum _PopEventListenerError PopEventListenerError; + +struct _PopEventListener +{ + GObject parent; + + /*< private > */ + PopEventListenerPrivate *priv; +}; + +struct _PopEventListenerClass +{ + GObjectClass parent_class; + + /* signals */ + void (* window_created) (PopEventListener *listener, GdkWindow *window); + void (* window_destroyed) (PopEventListener *listener, GdkWindow *window); + void (* window_shown) (PopEventListener *listener, GdkWindow *window); + void (* window_hidden) (PopEventListener *listener, GdkWindow *window); + void (* window_resized) (PopEventListener *listener, GdkWindow *window); + void (* window_reshaped) (PopEventListener *listener, GdkWindow *window); + void (* window_reparented) (PopEventListener *listener, GdkWindow *window, + GdkWindow *new_parent); + void (* window_moved) (PopEventListener *listener, GdkWindow *window); + void (* window_redrawn) (PopEventListener *listener, GdkWindow *window, + GdkRectangle *redrawn_area); + void (* window_layered_under) (PopEventListener *listener, GdkWindow *window, + GdkWindow *above_window); + void (* window_layered_on_top) (PopEventListener *listener, GdkWindow *window); + void (* window_layered_on_bottom) (PopEventListener *listener, GdkWindow *window); +}; + +enum _PopEventListenerError +{ + POP_EVENT_LISTENER_ERROR_GENERIC = 0, +}; + +#ifndef POP_HIDE_FUNCTION_DECLARATIONS +GType pop_event_listener_get_type (void); +GQuark pop_event_listener_error_quark (void); + +PopEventListener *pop_event_listener_get_default (void); + +#endif + +G_END_DECLS +#endif /* POP_EVENT_LISTENER_H */ diff --git a/src/pop-marshal.list b/src/pop-marshal.list new file mode 100644 index 0000000..cc41355 --- /dev/null +++ b/src/pop-marshal.list @@ -0,0 +1,2 @@ +VOID:OBJECT,OBJECT +VOID:OBJECT,POINTER diff --git a/pop-overlay-window.c b/src/pop-overlay-window.c index a64507a..955eccb 100644 --- a/pop-overlay-window.c +++ b/src/pop-overlay-window.c @@ -20,6 +20,7 @@ * * Originally written by: Ray Strode <rstrode@redhat.com> */ +#include "config.h" #include "pop-overlay-window.h" #include <errno.h> @@ -89,7 +90,7 @@ static void pop_overlay_window_setup_parent_window (PopOverlayWindow *overlay_window) { GdkDisplay *display; - GdkWindow *root_window, *parent_window; + GdkWindow *root_window; Window parent_xwindow; g_assert (POP_IS_OVERLAY_WINDOW (overlay_window)); @@ -265,7 +266,6 @@ static GdkRegion * pop_overlay_window_create_region_from_allocation (PopOverlayWindow *overlay_window, GtkAllocation *allocation) { - GdkDisplay *display; GdkRectangle rectangle; GdkRegion *region; diff --git a/pop-overlay-window.h b/src/pop-overlay-window.h index 9f3f832..9f3f832 100644 --- a/pop-overlay-window.h +++ b/src/pop-overlay-window.h diff --git a/src/pop-window-stack.c b/src/pop-window-stack.c new file mode 100644 index 0000000..8ad0320 --- /dev/null +++ b/src/pop-window-stack.c @@ -0,0 +1,492 @@ +/* pop-window-stack.c - tracks the stacking order of toplevel windows + * + * Copyright (C) 2007 Ray Strode <rstrode@redhat.com> + * + * 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, 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 "config.h" +#include "pop-window-stack.h" + +#include <errno.h> +#include <string.h> + +#include <glib.h> +#include <glib-object.h> +#include <glib/gi18n.h> + +#include <gdk/gdkx.h> +#include <gdk/gdk.h> + +#include "pop-event-listener.h" + +struct _PopWindowStackPrivate +{ + GdkScreen *screen; + PopEventListener *event_listener; + GQueue *windows; +}; + +typedef struct +{ + PopWindowStack *stack; + PopWindowStackForeachFunc func; + gpointer user_data; +} PopWindowStackForeachClosure; + +typedef struct +{ + PopWindowStack *stack; + GdkWindow *window; + PopWindowStackAboveWindowForeachFunc func; + gpointer user_data; +} PopWindowStackAboveWindowForeachClosure; + +static void pop_window_stack_finalize (GObject * object); +#if 0 +static void pop_window_stack_class_install_signals (PopWindowStackClass * + stack_class); +#endif +static void pop_window_stack_class_install_properties (PopWindowStackClass * + stack_class); + +static void pop_window_stack_set_property (GObject * object, + guint prop_id, + const GValue * value, + GParamSpec * pspec); +static void pop_window_stack_get_property (GObject * object, + guint prop_id, + GValue * value, + GParamSpec * pspec); + +static void pop_window_stack_set_screen (PopWindowStack *stack, + GdkScreen *screen); +static void pop_window_stack_clear_window_list (PopWindowStack *stack); +static void pop_window_stack_get_initial_window_list (PopWindowStack *stack); + + +enum +{ + PROP_0 = 0, + PROP_SCREEN +}; + +#if 0 +enum +{ + FOO = 0, + NUMBER_OF_SIGNALS +}; + +static guint pop_window_stack_signals[NUMBER_OF_SIGNALS]; +#endif + +G_DEFINE_TYPE (PopWindowStack, pop_window_stack, G_TYPE_OBJECT); + +static void +pop_window_stack_class_init (PopWindowStackClass * stack_class) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (stack_class); + + object_class->finalize = pop_window_stack_finalize; + + pop_window_stack_class_install_properties (stack_class); +#if 0 + pop_window_stack_class_install_signals (stack_class); +#endif + + g_type_class_add_private (stack_class, sizeof (PopWindowStackPrivate)); +} + +#if 0 +static void +pop_window_stack_class_install_signals (PopWindowStackClass * stack_class) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (stack_class); + + pop_window_stack_signals[FOO] = + g_signal_new ("foo", G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (PopWindowStackClass, foo), + NULL, NULL, g_cclosure_marshal_VOID__NONE; G_TYPE_NONE, 0); + stack_class->foo = NULL; +} +#endif + +static void +pop_window_stack_class_install_properties (PopWindowStackClass * stack_class) +{ + GObjectClass *object_class; + GParamSpec *param_spec; + + object_class = G_OBJECT_CLASS (stack_class); + object_class->set_property = pop_window_stack_set_property; + object_class->get_property = pop_window_stack_get_property; + + param_spec = g_param_spec_pointer ("screen", _("Screen"), + _("Which screen to user for tracking window stack"), + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (object_class, PROP_SCREEN, param_spec); +} + +static void +pop_window_stack_init (PopWindowStack *stack) +{ + stack->priv = G_TYPE_INSTANCE_GET_PRIVATE (stack, + POP_TYPE_WINDOW_STACK, + PopWindowStackPrivate); + + stack->priv->event_listener = pop_event_listener_get_default (); + g_signal_connect_swapped (G_OBJECT (stack->priv->event_listener), + "window-layered-under", + G_CALLBACK (pop_window_stack_get_initial_window_list), + stack); + g_signal_connect_swapped (G_OBJECT (stack->priv->event_listener), + "window-layered-on-top", + G_CALLBACK (pop_window_stack_get_initial_window_list), + stack); + g_signal_connect_swapped (G_OBJECT (stack->priv->event_listener), + "window-layered-on-bottom", + G_CALLBACK (pop_window_stack_get_initial_window_list), + stack); + + stack->priv->windows = g_queue_new (); +} + +static void +pop_window_stack_finalize (GObject * object) +{ + PopWindowStack *stack; + GObjectClass *parent_class; + + stack = POP_WINDOW_STACK (object); + + parent_class = G_OBJECT_CLASS (pop_window_stack_parent_class); + + g_object_unref (stack->priv->event_listener); + + pop_window_stack_clear_window_list (stack); + g_queue_free (stack->priv->windows); + + if (parent_class->finalize != NULL) + parent_class->finalize (object); +} + +static void +pop_window_stack_set_property (GObject * object, + guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + PopWindowStack *stack = POP_WINDOW_STACK (object); + + switch (prop_id) + { + case PROP_SCREEN: + pop_window_stack_set_screen (stack, g_value_get_pointer (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +pop_window_stack_get_property (GObject * object, + guint prop_id, + GValue * value, GParamSpec * pspec) +{ + PopWindowStack *stack = POP_WINDOW_STACK (object); + + switch (prop_id) + { + case PROP_SCREEN: + g_value_set_pointer (value, stack->priv->screen); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +GQuark +pop_window_stack_error_quark (void) +{ + static GQuark error_quark = 0; + + if (error_quark == 0) + error_quark = g_quark_from_static_string ("pop-window-stack"); + + return error_quark; +} + +static PopWindowStack * +pop_window_stack_new (GdkScreen *screen) +{ + PopWindowStack *stack; + + if (screen == NULL) + screen = gdk_screen_get_default (); + + stack = g_object_new (POP_TYPE_WINDOW_STACK, + "screen", screen, NULL); + + return stack; +} + +PopWindowStack * +pop_window_stack_get_for_screen (GdkScreen *screen) +{ + static GList *stack_list = NULL, *tmp; + PopWindowStack *stack; + + if (screen == NULL) + screen = gdk_screen_get_default (); + + for (tmp = stack_list; tmp != NULL; tmp = tmp->next) + { + g_assert (POP_IS_WINDOW_STACK (tmp->data)); + + stack = POP_WINDOW_STACK (tmp->data); + + if (gdk_screen_get_number (stack->priv->screen) == + gdk_screen_get_number (screen)) + return g_object_ref (screen); + } + + stack = pop_window_stack_new (screen); + stack_list = g_list_prepend (stack_list, stack); + + return g_object_ref (stack); +} + +guint +pop_window_stack_get_length (PopWindowStack *stack) +{ + g_return_val_if_fail (POP_IS_WINDOW_STACK (stack), 0); + + return g_queue_get_length (stack->priv->windows); +} + +static void +pop_window_stack_foreach_func (gpointer data, + gpointer user_data) +{ + GdkWindow *window; + PopWindowStackForeachClosure *closure; + + window = (GdkWindow *) data; + closure = (PopWindowStackForeachClosure *) user_data; + + closure->func (closure->stack, window, closure->user_data); +} + +void +pop_window_stack_foreach (PopWindowStack *stack, + PopWindowStackForeachFunc func, + gpointer user_data) +{ + PopWindowStackForeachClosure closure; + + g_return_if_fail (POP_IS_WINDOW_STACK (stack)); + g_return_if_fail (func != NULL); + + closure.stack = stack; + closure.func = func; + closure.user_data = user_data; + + g_queue_foreach (stack->priv->windows, pop_window_stack_foreach_func, &closure); +} + +static void +pop_window_stack_above_window_foreach_func (gpointer data, + gpointer user_data) +{ + GdkWindow *above_window; + PopWindowStackAboveWindowForeachClosure *closure; + + above_window = (GdkWindow *) data; + closure = (PopWindowStackAboveWindowForeachClosure *) user_data; + + closure->func (closure->stack, closure->window, above_window, closure->user_data); +} + +void +pop_window_stack_above_window_foreach (PopWindowStack *stack, + GdkWindow *window, + PopWindowStackAboveWindowForeachFunc func, + gpointer user_data) +{ + PopWindowStackAboveWindowForeachClosure closure; + GList *window_link; + + g_return_if_fail (POP_IS_WINDOW_STACK (stack)); + g_return_if_fail (func != NULL); + g_return_if_fail (window != NULL); + + window_link = g_queue_find (stack->priv->windows, window); + + if (window_link == NULL) + { + g_warning ("window with id 0x%lx is not in window stack\n", + GDK_WINDOW_XWINDOW (window)); + return; + } + + closure.stack = stack; + closure.window = g_object_ref (window); closure.func = func; + closure.user_data = user_data; + + g_list_foreach (window_link->next, pop_window_stack_above_window_foreach_func, &closure); + + g_object_unref (window); +} + +static void +pop_window_stack_clear_window_list (PopWindowStack *stack) +{ + if (g_queue_is_empty (stack->priv->windows)) + return; + + g_queue_foreach (stack->priv->windows, (GFunc) g_object_unref, NULL); + + g_queue_free (stack->priv->windows); + stack->priv->windows = g_queue_new (); +} + +static gboolean +window_can_output (GdkWindow *window) +{ + XWindowAttributes attrs; + gboolean received_attrs; + + /* FIXME: this sucks because it requires a server round trip. + */ + + gdk_error_trap_push (); + received_attrs = XGetWindowAttributes (GDK_WINDOW_XDISPLAY (window), + GDK_WINDOW_XWINDOW (window), + &attrs); + + if (gdk_error_trap_pop () || !received_attrs) + return FALSE; + + return attrs.class == InputOutput; +} + +static void +pop_window_stack_get_initial_window_list (PopWindowStack *stack) +{ + GdkScreen *screen; + GdkDisplay *display; + Window root, parent, *children; + guint number_of_children, i; + gboolean query_was_successful; + + pop_window_stack_clear_window_list (stack); + + screen = stack->priv->screen; + + if (screen == NULL) + screen = gdk_screen_get_default (); + + display = gdk_screen_get_display (screen); + + gdk_x11_display_grab (display); + query_was_successful = + XQueryTree (GDK_DISPLAY_XDISPLAY (display), + GDK_WINDOW_XWINDOW (gdk_screen_get_root_window (screen)), + &root, &parent, &children, &number_of_children); + + if (query_was_successful) + { + for (i = 0; i < number_of_children; i++) + { + GdkWindow *window; + + window = gdk_window_foreign_new_for_display (display, children[i]); + + if (window == NULL) + continue; + + if (!window_can_output (window)) + { + g_print ("excluding window 0x%lx\n", GDK_WINDOW_XWINDOW (window)); + g_object_unref (window); + continue; + } + + g_queue_push_tail (stack->priv->windows, window); + } + XFree (children); + } + gdk_x11_display_ungrab (display); +} + +static void +pop_window_stack_set_screen (PopWindowStack *stack, + GdkScreen *screen) +{ + if (screen != stack->priv->screen) + { + stack->priv->screen = screen; + pop_window_stack_get_initial_window_list (stack); + g_object_notify (G_OBJECT (stack), "screen"); + } +} + +#ifdef POP_WINDOW_STACK_ENABLE_TEST + +#include <stdio.h> +#include <glib.h> + +static void +print_toplevel (PopWindowStack *stack, + GdkWindow *window, + gpointer data) +{ + g_print ("0x%lx\n", GDK_WINDOW_XWINDOW (window)); +} + +int +main (int argc, char **argv) +{ + PopWindowStack *stack; + int exit_code; + + g_log_set_always_fatal (G_LOG_LEVEL_ERROR + | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING); + + g_type_init (); + gtk_init (&argc, &argv); + + g_message ("creating instance of 'window stack' object..."); + stack = pop_window_stack_new (gdk_screen_get_default ()); + g_message ("'window stack' object created successfully"); + + pop_window_stack_foreach (stack, print_toplevel, NULL); + + g_message ("destroying previously created 'window stack' object..."); + g_object_unref (stack); + g_message ("'window stack' object destroyed successfully"); + + exit_code = 0; + + return exit_code; +} +#endif /* POP_WINDOW_STACK_ENABLE_TEST */ diff --git a/src/pop-window-stack.h b/src/pop-window-stack.h new file mode 100644 index 0000000..d8e7803 --- /dev/null +++ b/src/pop-window-stack.h @@ -0,0 +1,83 @@ +/* pop-window-stack.h - tracks the stacking order of toplevel windows + * + * Copyright (C) 2007 Ray Strode <rstrode@redhat.com> + * + * 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, 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 POP_WINDOW_STACK_H +#define POP_WINDOW_STACK_H + +#include <glib.h> +#include <glib-object.h> + +#include <gdk/gdk.h> + +G_BEGIN_DECLS +#define POP_TYPE_WINDOW_STACK (pop_window_stack_get_type ()) +#define POP_WINDOW_STACK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), POP_TYPE_WINDOW_STACK, PopWindowStack)) +#define POP_WINDOW_STACK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), POP_TYPE_WINDOW_STACK, PopWindowStackClass)) +#define POP_IS_WINDOW_STACK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), POP_TYPE_WINDOW_STACK)) +#define POP_IS_WINDOW_STACK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), POP_TYPE_WINDOW_STACK)) +#define POP_WINDOW_STACK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), POP_TYPE_WINDOW_STACK, PopWindowStackClass)) +#define POP_WINDOW_STACK_ERROR (pop_window_stack_error_quark ()) +typedef struct _PopWindowStack PopWindowStack; +typedef struct _PopWindowStackClass PopWindowStackClass; +typedef struct _PopWindowStackPrivate PopWindowStackPrivate; +typedef enum _PopWindowStackError PopWindowStackError; +typedef void (* PopWindowStackForeachFunc) (PopWindowStack *stack, GdkWindow *window, gpointer user_data); +typedef void (* PopWindowStackAboveWindowForeachFunc) (PopWindowStack *stack, GdkWindow *window, GdkWindow *above_window, gpointer user_data); + +struct _PopWindowStack +{ + GObject parent; + + /*< private > */ + PopWindowStackPrivate *priv; +}; + +struct _PopWindowStackClass +{ + GObjectClass parent_class; + + /* signals */ +#if 0 + void (*foo) (PopWindowStack * stack); +#endif +}; + +enum _PopWindowStackError +{ + POP_WINDOW_STACK_ERROR_GENERIC = 0, +}; + +#ifndef POP_HIDE_FUNCTION_DECLARATIONS +GType pop_window_stack_get_type (void); +GQuark pop_window_stack_error_quark (void); + +PopWindowStack *pop_window_stack_get_for_screen (GdkScreen *screen); +guint pop_window_stack_get_length (PopWindowStack *stack); +void pop_window_stack_foreach (PopWindowStack *stack, + PopWindowStackForeachFunc func, + gpointer user_data); +void pop_window_stack_above_window_foreach (PopWindowStack *stack, + GdkWindow *window, + PopWindowStackAboveWindowForeachFunc func, + gpointer user_data); + +#endif + +G_END_DECLS +#endif /* POP_WINDOW_STACK_H */ diff --git a/src/pop-window-view.c b/src/pop-window-view.c new file mode 100644 index 0000000..9e7f3dd --- /dev/null +++ b/src/pop-window-view.c @@ -0,0 +1,1061 @@ +/* pop-window-view.c - view of redirected offscreen windows + * + * Copyright (C) 2007 Ray Strode <rstrode@redhat.com> + * + * 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, 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 "config.h" +#include "pop-window-view.h" + +#include <errno.h> +#include <string.h> + +#include <glib.h> +#include <glib-object.h> +#include <glib/gi18n.h> + +#include <cairo.h> + +#include <X11/extensions/Xcomposite.h> +#include <X11/extensions/Xdamage.h> +#include <X11/extensions/shape.h> + +#include <cairo-xlib.h> +#include <gdk/gdkx.h> + +#include <gdk/gdk.h> + +#include "pop-event-listener.h" + +struct _PopWindowViewPrivate +{ + GdkWindow *window; + PopEventListener *event_listener; + + Damage damage; + cairo_pattern_t *pattern; + cairo_path_t *clip_path; + + GdkRectangle damaged_area; + + guint is_mapped : 1; + guint is_damaged : 1; + + gint x, y; + gint width, height; +}; + +static void pop_window_view_class_install_signals (PopWindowViewClass *view_class); +static void pop_window_view_class_install_properties (PopWindowViewClass *view_class); + +static void pop_window_view_set_property (GObject * object, + guint prop_id, + const GValue * value, + GParamSpec * pspec); +static void pop_window_view_get_property (GObject * object, + guint prop_id, + GValue * value, GParamSpec * pspec); + +static void pop_window_view_do_finalize (GObject *object); + +enum +{ + PROP_0 = 0, + PROP_WINDOW +}; + +#define POP_WINDOW_VIEW_INVALID_WINDOW (Window) 0 +#define POP_WINDOW_VIEW_INVALID_PIXMAP (Pixmap) 0 +#define POP_WINDOW_VIEW_INVALID_DAMAGE (Damage) 0 + +#if 0 +enum +{ + FOO = 0, + NUMBER_OF_SIGNALS +}; + +static guint pop_window_view_signals[NUMBER_OF_SIGNALS]; +#endif + +G_DEFINE_TYPE (PopWindowView, pop_window_view, G_TYPE_OBJECT); + +static void +pop_window_view_class_init (PopWindowViewClass *view_class) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (view_class); + + object_class->finalize = pop_window_view_do_finalize; + + pop_window_view_class_install_properties (view_class); +#if 0 + pop_window_view_class_install_signals (view_class); +#endif + + g_type_class_add_private (view_class, sizeof (PopWindowViewPrivate)); +} + +#if 0 +static void +pop_window_view_class_install_signals (PopWindowViewClass * view_class) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (view_class); + + pop_window_view_signals[FOO] = + g_signal_new ("foo", G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (PopWindowViewClass, foo), + NULL, NULL, g_cclosure_marshal_VOID__NONE; G_TYPE_NONE, 0); + view_class->foo = NULL; +} +#endif + +static void +pop_window_view_class_install_properties (PopWindowViewClass *view_class) +{ + GObjectClass *object_class; + GParamSpec *param_spec; + + object_class = G_OBJECT_CLASS (view_class); + object_class->set_property = pop_window_view_set_property; + object_class->get_property = pop_window_view_get_property; + + param_spec = NULL; +#if 0 + param_spec = g_param_spec_ulong ("x-window-id", _("X Window ID"), + _("A client-side identifier representing a " + "server-side window"), 0, G_MAXULONG, + (gulong) POP_WINDOW_VIEW_INVALID_WINDOW, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE); + g_object_class_install_property (object_class, PROP_WINDOW, param_spec); +#endif +} + +static void +pop_window_view_init (PopWindowView *view) +{ + g_assert (POP_IS_WINDOW_VIEW (view)); + + view->priv = G_TYPE_INSTANCE_GET_PRIVATE (view, + POP_TYPE_WINDOW_VIEW, + PopWindowViewPrivate); + + view->priv->event_listener = pop_event_listener_get_default (); +} + +static void +pop_window_view_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + PopWindowView *view = POP_WINDOW_VIEW (object); + + switch (prop_id) + { + case PROP_WINDOW: + view = NULL; + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +pop_window_view_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + PopWindowView *view = POP_WINDOW_VIEW (object); + + switch (prop_id) + { + case PROP_WINDOW: + view = NULL; + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +pop_window_view_do_finalize (GObject *object) +{ + PopWindowView *view; + GObjectClass *parent_class; + + view = POP_WINDOW_VIEW (object); + + if (view->priv->window != NULL) + pop_window_view_unset_window (view); + + parent_class = G_OBJECT_CLASS (pop_window_view_parent_class); + + if (parent_class->finalize != NULL) + parent_class->finalize (object); +} + +static gboolean +pop_window_get_initial_geometry (PopWindowView *view) +{ + g_assert (POP_IS_WINDOW_VIEW (view)); + g_assert (view->priv->window != NULL); + + gdk_window_get_geometry (view->priv->window, + &view->priv->x, + &view->priv->y, + &view->priv->width, + &view->priv->height, + NULL); + return TRUE; +} + +static cairo_surface_t * +pop_window_view_get_surface (PopWindowView *view) +{ + cairo_surface_t *surface; + cairo_status_t status; + + g_assert (POP_IS_WINDOW_VIEW (view)); + g_assert (view->priv->pattern != NULL); + + surface = NULL; + status = cairo_pattern_get_surface (view->priv->pattern, &surface); + g_assert (status != CAIRO_STATUS_PATTERN_TYPE_MISMATCH); + + return surface; +} + +static void +pop_window_view_free_x_pixmap (PopWindowView *view) +{ + cairo_surface_t *surface; + Pixmap pixmap; + + g_assert (POP_IS_WINDOW_VIEW (view)); + + surface = pop_window_view_get_surface (view); + + g_assert (surface != NULL); + pixmap = (Pixmap) cairo_xlib_surface_get_drawable (surface); + XFreePixmap (GDK_WINDOW_XDISPLAY (view->priv->window), pixmap); +} + +static void +pop_window_view_destroy_pattern (PopWindowView *view) +{ + g_assert (POP_IS_WINDOW_VIEW (view)); + + if (view->priv->pattern == NULL) + return; + + pop_window_view_free_x_pixmap (view); + cairo_pattern_destroy (view->priv->pattern); + view->priv->pattern = NULL; +} + +static void +pop_window_view_destroy_clip_path (PopWindowView *view) +{ + g_assert (POP_IS_WINDOW_VIEW (view)); + + if (view->priv->clip_path == NULL) + return; + + cairo_path_destroy (view->priv->clip_path); + view->priv->clip_path = NULL; +} + +GQuark +pop_window_view_error_quark (void) +{ + static GQuark error_quark = 0; + + if (error_quark == 0) + error_quark = g_quark_from_static_string ("pop-window"); + + return error_quark; +} + +PopWindowView * +pop_window_view_new (void) +{ + PopWindowView *view; + + view = g_object_new (POP_TYPE_WINDOW_VIEW, NULL); + + return view; +} + +static gboolean +pop_window_view_redirect_window_off_screen (PopWindowView *view) +{ + g_assert (POP_IS_WINDOW_VIEW (view)); + + gdk_error_trap_push (); + XCompositeRedirectWindow (GDK_WINDOW_XDISPLAY (view->priv->window), + GDK_WINDOW_XWINDOW (view->priv->window), + CompositeRedirectAutomatic); + gdk_flush (); + if (gdk_error_trap_pop ()) + return FALSE; + + return TRUE; +} + +static Pixmap +pop_window_view_fetch_window_pixmap (PopWindowView *view) +{ + Pixmap pixmap; + gint status; + + g_assert (POP_IS_WINDOW_VIEW (view)); + + gdk_error_trap_push (); + pixmap = + XCompositeNameWindowPixmap (GDK_WINDOW_XDISPLAY (view->priv->window), + GDK_WINDOW_XWINDOW (view->priv->window)); + gdk_flush (); + status = gdk_error_trap_pop (); + + if (status != Success) + { + char error_message[64]; + + XGetErrorText (GDK_WINDOW_XDISPLAY (view->priv->window), + status, error_message, sizeof (error_message) - 1); + g_print ("could not name pixmap for window 0x%lx: %s\n", + GDK_WINDOW_XWINDOW (view->priv->window), + error_message); + + return POP_WINDOW_VIEW_INVALID_PIXMAP; + } + + return pixmap; +} + +static gboolean +pop_window_view_create_pattern_from_window (PopWindowView *view) +{ + GdkVisual *visual; + Pixmap pixmap; + cairo_surface_t *surface; + cairo_status_t status; + int width, height; + + g_assert (POP_IS_WINDOW_VIEW (view)); + g_assert (view->priv->window != NULL); + g_assert (view->priv->pattern == NULL); + + pixmap = pop_window_view_fetch_window_pixmap (view); + + if (pixmap == POP_WINDOW_VIEW_INVALID_PIXMAP) + { + g_print ("couldn't fetch pixmap for window\n"); + return FALSE; + } + + gdk_drawable_get_size (GDK_DRAWABLE (view->priv->window), + &width, &height); + + visual = gdk_drawable_get_visual (GDK_DRAWABLE (view->priv->window)); + surface = cairo_xlib_surface_create (GDK_WINDOW_XDISPLAY (view->priv->window), + (Drawable) pixmap, + GDK_VISUAL_XVISUAL (visual), + width, height); + status = cairo_surface_status (surface); + if (status != CAIRO_STATUS_SUCCESS) + { + g_print ("could create xlib surface for window: %s\n", cairo_status_to_string (status)); + goto failed; + } + + view->priv->pattern = cairo_pattern_create_for_surface (surface); + cairo_surface_destroy (surface); + + status = cairo_pattern_status (view->priv->pattern); + if (status != CAIRO_STATUS_SUCCESS) + { + g_print ("couldn't create cairo pattern: %s\n", cairo_status_to_string (status)); + goto failed; + } + + return TRUE; + +failed: + if (view->priv->pattern != NULL) + { + cairo_pattern_destroy (view->priv->pattern); + view->priv->pattern = NULL; + } + + XFreePixmap (GDK_WINDOW_XDISPLAY (view->priv->window), + pixmap); + + return FALSE; +} + +static XRectangle * +pop_window_view_get_tesselated_window_shape (PopWindowView *view, + gint *number_of_rectangles) +{ + XRectangle *rectangles; + gint ordering; + gint status; + + g_assert (POP_IS_WINDOW_VIEW (view)); + g_assert (number_of_rectangles != NULL); + + *number_of_rectangles = 0; + + gdk_error_trap_push (); + rectangles = XShapeGetRectangles (GDK_WINDOW_XDISPLAY (view->priv->window), + GDK_WINDOW_XWINDOW (view->priv->window), + ShapeBounding, number_of_rectangles, + &ordering); + gdk_flush (); + status = gdk_error_trap_pop (); + + if (status != Success) + return NULL; + + return rectangles; +} + + +static gboolean +pop_window_view_set_clip_path_from_window (PopWindowView *view) +{ + XRectangle *rectangles; + gint number_of_rectangles; + cairo_t *cairo_context; + cairo_surface_t *surface; + cairo_status_t status; + gint i; + + g_assert (POP_IS_WINDOW_VIEW (view)); + g_assert (view->priv->clip_path == NULL); + + rectangles = pop_window_view_get_tesselated_window_shape (view, + &number_of_rectangles); + + if (rectangles == NULL) + return FALSE; + + surface = cairo_image_surface_create (CAIRO_FORMAT_A1, + view->priv->width, view->priv->height); + + status = cairo_surface_status (surface); + + if (status != CAIRO_STATUS_SUCCESS) + { + cairo_surface_destroy (surface); + XFree (rectangles); + return FALSE; + } + + cairo_context = cairo_create (surface); + + for (i = 0; i < number_of_rectangles; i++) + { + cairo_rectangle (cairo_context, + rectangles[i].x, + rectangles[i].y, + rectangles[i].width, + rectangles[i].height); + } + XFree (rectangles); + + view->priv->clip_path = cairo_copy_path_flat (cairo_context); + cairo_destroy (cairo_context); + cairo_surface_destroy (surface); + + return TRUE; +} + +static gboolean +pop_window_view_set_clip_path_for_placeholder (PopWindowView *view) +{ + cairo_t *cairo_context; + cairo_surface_t *surface; + cairo_status_t status; + + g_assert (POP_IS_WINDOW_VIEW (view)); + g_assert (view->priv->clip_path == NULL); + + surface = cairo_image_surface_create (CAIRO_FORMAT_A1, + view->priv->width, view->priv->height); + + status = cairo_surface_status (surface); + + if (status != CAIRO_STATUS_SUCCESS) + { + cairo_surface_destroy (surface); + return FALSE; + } + + cairo_context = cairo_create (surface); + + cairo_rectangle (cairo_context, + view->priv->x, view->priv->y, + view->priv->width, view->priv->height); + + view->priv->clip_path = cairo_copy_path_flat (cairo_context); + cairo_destroy (cairo_context); + cairo_surface_destroy (surface); + + return TRUE; +} + +static void +pop_window_view_enable_window_geometry_reporting (PopWindowView *view) +{ + GdkEventMask event_mask; + + g_assert (POP_IS_WINDOW_VIEW (view)); + + event_mask = gdk_window_get_events (view->priv->window); + + gdk_window_set_events (view->priv->window, event_mask | GDK_STRUCTURE_MASK | GDK_EXPOSURE_MASK); +} + +static void +pop_window_view_disable_window_geometry_reporting (PopWindowView *view) +{ + GdkEventMask event_mask; + + g_assert (POP_IS_WINDOW_VIEW (view)); + + event_mask = gdk_window_get_events (view->priv->window); + + gdk_window_set_events (view->priv->window, event_mask & ~GDK_STRUCTURE_MASK); +} + +static void +pop_window_view_enable_window_shape_reporting (PopWindowView *view) +{ + g_assert (POP_IS_WINDOW_VIEW (view)); + + XShapeSelectInput (GDK_WINDOW_XDISPLAY (view->priv->window), + GDK_WINDOW_XWINDOW (view->priv->window), + ShapeNotifyMask); +} + +static void +pop_window_view_disable_window_shape_reporting (PopWindowView *view) +{ + g_assert (POP_IS_WINDOW_VIEW (view)); + + XShapeSelectInput (GDK_WINDOW_XDISPLAY (view->priv->window), + GDK_WINDOW_XWINDOW (view->priv->window), + 0); +} + +static void +pop_window_view_enable_window_damage_reporting (PopWindowView *view) +{ + g_assert (POP_IS_WINDOW_VIEW (view)); + + view->priv->damage = + XDamageCreate (GDK_WINDOW_XDISPLAY (view->priv->window), + GDK_WINDOW_XWINDOW (view->priv->window), + XDamageReportBoundingBox); +} + +static void +pop_window_view_disable_window_damage_reporting (PopWindowView *view) +{ + g_assert (POP_IS_WINDOW_VIEW (view)); + + gdk_error_trap_push (); + XDamageDestroy (GDK_WINDOW_XDISPLAY (view->priv->window), + view->priv->damage); + gdk_flush (); + gdk_error_trap_pop (); + view->priv->damage = POP_WINDOW_VIEW_INVALID_DAMAGE; +} + +static void +pop_window_view_window_redrawn (PopWindowView *view, + GdkWindow *window, + GdkRectangle *rectangle) +{ + g_print ("window 0x%lx redrawn (area: %dx%d+%d+%d)\n", GDK_WINDOW_XWINDOW (view->priv->window), rectangle->x, rectangle->y, rectangle->width, rectangle->height); + if (view->priv->pattern == NULL) + pop_window_view_create_pattern_from_window (view); + + if (view->priv->clip_path == NULL) + pop_window_view_set_clip_path_from_window (view); + + gdk_rectangle_union (&view->priv->damaged_area, + rectangle, + &view->priv->damaged_area); + + view->priv->is_damaged = TRUE; +} + +void +pop_window_view_window_resized (PopWindowView *view) +{ + gint old_width, old_height; + + g_print ("window 0x%lx resized\n", GDK_WINDOW_XWINDOW (view->priv->window)); + + old_width = view->priv->width; + old_height = view->priv->height; + + gdk_drawable_get_size (GDK_DRAWABLE (view->priv->window), + &view->priv->width, + &view->priv->height); + if (view->priv->pattern != NULL) + { + pop_window_view_destroy_pattern (view); + pop_window_view_create_pattern_from_window (view); + } + + if (view->priv->clip_path != NULL) + { + pop_window_view_destroy_clip_path (view); + pop_window_view_set_clip_path_from_window (view); + } + + g_print ("window 0x%lx resized from (%dx%d) to (%dx%d)\n", + GDK_WINDOW_XWINDOW (view->priv->window), + old_width, old_height, + view->priv->width, view->priv->height); +} + +void +pop_window_view_window_moved (PopWindowView *view) +{ + gint old_x, old_y; + + old_x = view->priv->x; + old_y = view->priv->y; + + gdk_window_get_position (GDK_DRAWABLE (view->priv->window), + &view->priv->x, + &view->priv->y); + + g_print ("window 0x%lx moved from (%d, %d) to (%d, %d)\n", + GDK_WINDOW_XWINDOW (view->priv->window), + old_x, old_y, view->priv->x, view->priv->y); +} + +static void +pop_window_view_window_shown (PopWindowView *view) +{ + g_print ("window 0x%lx is now visible\n", + GDK_WINDOW_XWINDOW (view->priv->window)); + + view->priv->is_mapped = TRUE; + +#if 0 + if (view->priv->pattern == NULL) + pop_window_view_create_pattern_from_window (view); + + if (view->priv->clip_path == NULL) + pop_window_view_set_clip_path_from_window (view); +#endif +} + +static void +pop_window_view_window_hidden (PopWindowView *view) +{ + + view->priv->is_mapped = FALSE; + g_print ("window 0x%lx hidden\n", GDK_WINDOW_XWINDOW (view->priv->window)); + pop_window_view_destroy_pattern (view); +} + +static void +pop_window_view_window_destroyed (PopWindowView *view) +{ + g_print ("window 0x%lx destroyed\n", GDK_WINDOW_XWINDOW (view->priv->window)); + pop_window_view_unset_window (view); +} + +static void +pop_window_view_window_reshaped (PopWindowView *view) +{ + gint old_width, old_height; + + g_print ("window 0x%lx reshaped\n", GDK_WINDOW_XWINDOW (view->priv->window)); + old_width = view->priv->width; + old_height = view->priv->height; + + gdk_drawable_get_size (GDK_DRAWABLE (view->priv->window), + &view->priv->width, + &view->priv->height); + if (view->priv->pattern != NULL) + { + pop_window_view_destroy_pattern (view); + pop_window_view_create_pattern_from_window (view); + } + + if (view->priv->clip_path != NULL) + { + pop_window_view_destroy_clip_path (view); + pop_window_view_set_clip_path_from_window (view); + } +} + +static void +pop_window_view_window_reparented (PopWindowView *view) +{ + g_print ("window 0x%lx reparented\n", GDK_WINDOW_XWINDOW (view->priv->window)); + pop_window_view_unset_window (view); +} + +static void +pop_window_view_listen_to_window (PopWindowView *view) +{ + gchar detailed_signal[sizeof ("window-XXXXXXXXXX::ffffffff")]; + + g_print ("listening to window 0x%lx\n", GDK_WINDOW_XWINDOW (view->priv->window)); + + snprintf (detailed_signal, sizeof (detailed_signal), + "window-destroyed::%lx", GDK_WINDOW_XWINDOW (view->priv->window)); + g_signal_connect_swapped (G_OBJECT (view->priv->event_listener), + detailed_signal, + G_CALLBACK (pop_window_view_window_destroyed), + view); + snprintf (detailed_signal, sizeof (detailed_signal), + "window-shown::%lx", GDK_WINDOW_XWINDOW (view->priv->window)); + g_signal_connect_swapped (G_OBJECT (view->priv->event_listener), + detailed_signal, + G_CALLBACK (pop_window_view_window_shown), + view); + snprintf (detailed_signal, sizeof (detailed_signal), + "window-hidden::%lx", GDK_WINDOW_XWINDOW (view->priv->window)); + g_signal_connect_swapped (G_OBJECT (view->priv->event_listener), + detailed_signal, + G_CALLBACK (pop_window_view_window_hidden), + view); + snprintf (detailed_signal, sizeof (detailed_signal), + "window-resized::%lx", GDK_WINDOW_XWINDOW (view->priv->window)); + g_signal_connect_swapped (G_OBJECT (view->priv->event_listener), + detailed_signal, + G_CALLBACK (pop_window_view_window_resized), + view); + snprintf (detailed_signal, sizeof (detailed_signal), + "window-reshaped::%lx", GDK_WINDOW_XWINDOW (view->priv->window)); + g_signal_connect_swapped (G_OBJECT (view->priv->event_listener), + detailed_signal, + G_CALLBACK (pop_window_view_window_reshaped), + view); + snprintf (detailed_signal, sizeof (detailed_signal), + "window-reparented::%lx", GDK_WINDOW_XWINDOW (view->priv->window)); + g_signal_connect_swapped (G_OBJECT (view->priv->event_listener), + detailed_signal, + G_CALLBACK (pop_window_view_window_reparented), + view); + snprintf (detailed_signal, sizeof (detailed_signal), + "window-moved::%lx", GDK_WINDOW_XWINDOW (view->priv->window)); + g_signal_connect_swapped (G_OBJECT (view->priv->event_listener), + detailed_signal, + G_CALLBACK (pop_window_view_window_moved), + view); + snprintf (detailed_signal, sizeof (detailed_signal), + "window-redrawn::%lx", GDK_WINDOW_XWINDOW (view->priv->window)); + g_signal_connect_swapped (G_OBJECT (view->priv->event_listener), + detailed_signal, + G_CALLBACK (pop_window_view_window_redrawn), + view); +} + +static void +pop_window_view_ignore_window (PopWindowView *view) +{ + g_print ("ignoring window 0x%lx\n", GDK_WINDOW_XWINDOW (view->priv->window)); + + g_signal_handlers_disconnect_by_func (G_OBJECT (view->priv->event_listener), + G_CALLBACK (pop_window_view_window_destroyed), + view); + g_signal_handlers_disconnect_by_func (G_OBJECT (view->priv->event_listener), + G_CALLBACK (pop_window_view_window_shown), + view); + g_signal_handlers_disconnect_by_func (G_OBJECT (view->priv->event_listener), + G_CALLBACK (pop_window_view_window_hidden), + view); + g_signal_handlers_disconnect_by_func (G_OBJECT (view->priv->event_listener), + G_CALLBACK (pop_window_view_window_resized), + view); + g_signal_handlers_disconnect_by_func (G_OBJECT (view->priv->event_listener), + G_CALLBACK (pop_window_view_window_reshaped), + view); + g_signal_handlers_disconnect_by_func (G_OBJECT (view->priv->event_listener), + G_CALLBACK (pop_window_view_window_reparented), + view); + g_signal_handlers_disconnect_by_func (G_OBJECT (view->priv->event_listener), + G_CALLBACK (pop_window_view_window_moved), + view); + g_signal_handlers_disconnect_by_func (G_OBJECT (view->priv->event_listener), + G_CALLBACK (pop_window_view_window_redrawn), + view); +} + +static gboolean +pop_window_view_enable_window_status_reporting (PopWindowView *view) +{ + gint status; + + g_assert (POP_IS_WINDOW_VIEW (view)); + + gdk_error_trap_push (); + pop_window_view_enable_window_geometry_reporting (view); + pop_window_view_enable_window_shape_reporting (view); + pop_window_view_enable_window_damage_reporting (view); + pop_window_view_listen_to_window (view); + gdk_flush (); + status = gdk_error_trap_pop (); + + return status == Success; +} + +static void +pop_window_view_disable_window_status_reporting (PopWindowView *view) +{ + g_assert (POP_IS_WINDOW_VIEW (view)); + + if ((view->priv->window == NULL) || GDK_WINDOW_DESTROYED (view->priv->window)) + return; + + pop_window_view_disable_window_damage_reporting (view); + + gdk_error_trap_push (); + pop_window_view_disable_window_geometry_reporting (view); + pop_window_view_disable_window_shape_reporting (view); + pop_window_view_ignore_window (view); + gdk_flush (); + gdk_error_trap_pop (); +} + +gboolean +pop_window_view_set_window (PopWindowView *view, + GdkWindow *window) +{ + g_return_val_if_fail (POP_IS_WINDOW_VIEW (view), FALSE); + g_return_val_if_fail (window != NULL, FALSE); + + g_return_val_if_fail (view->priv->window == NULL, FALSE); + view->priv->window = window; + + if (!pop_window_get_initial_geometry (view)) + { + g_print ("couldn't get initial geometry\n"); + view->priv->window = NULL; + return FALSE; + } + + if (!pop_window_view_enable_window_status_reporting (view)) + { + g_print ("couldn't enable damage for window\n"); + pop_window_view_disable_window_status_reporting (view); + view->priv->window = NULL; + return FALSE; + } + + if (!pop_window_view_redirect_window_off_screen (view)) + { + g_print ("couldn't redirect window off screen\n"); + view->priv->window = NULL; + return FALSE; + } + + if (gdk_window_is_visible (view->priv->window)) + view->priv->is_mapped = TRUE; + + g_object_ref (window); + + return TRUE; +} + +void +pop_window_view_unset_window (PopWindowView *view) +{ + g_return_if_fail (POP_IS_WINDOW_VIEW (view)); + g_return_if_fail (view->priv->window != NULL); + + pop_window_view_disable_window_status_reporting (view); + pop_window_view_destroy_clip_path (view); + pop_window_view_destroy_pattern (view); + + g_object_unref (view->priv->window); + view->priv->window = NULL; + + g_object_unref (view->priv->event_listener); + view->priv->event_listener = NULL; +} + +GdkWindow * +pop_window_view_get_window (PopWindowView *view) +{ + g_return_val_if_fail (POP_IS_WINDOW_VIEW (view), NULL); + + return view->priv->window; +} + +void +pop_window_view_report_fixed_damage (PopWindowView *view) +{ + gdk_error_trap_push (); + XDamageSubtract (GDK_WINDOW_XDISPLAY (view->priv->window), + view->priv->damage, None, None); + gdk_flush (); + gdk_error_trap_pop (); + + view->priv->is_damaged = FALSE; + view->priv->damaged_area.x = 0; + view->priv->damaged_area.y = 0; + view->priv->damaged_area.width = 0; + view->priv->damaged_area.height = 0; +} + +static GdkRegion * +pop_window_view_get_bounding_region (PopWindowView *view) +{ + GdkRectangle bounding_area; + + g_assert (POP_IS_WINDOW_VIEW (view)); + + bounding_area.x = view->priv->x; + bounding_area.y = view->priv->y; + bounding_area.width = view->priv->width; + bounding_area.height = view->priv->height; + + return gdk_region_rectangle (&bounding_area); +} + +GdkRegion * +pop_window_view_get_difference (PopWindowView *view, + PopWindowView *other_view) +{ + GdkRegion *view_bounding_region, *other_view_bounding_region; + + g_return_val_if_fail (POP_IS_WINDOW_VIEW (view), NULL); + g_return_val_if_fail (POP_IS_WINDOW_VIEW (other_view), NULL); + + if (!gdk_window_is_visible (view->priv->window)) + return gdk_region_new (); + + view_bounding_region = pop_window_view_get_bounding_region (view); + + if (!gdk_window_is_visible (other_view->priv->window)) + return view_bounding_region; + other_view_bounding_region = pop_window_view_get_bounding_region (other_view); + + /* This is the part of the view that isn't overlapped by the + * other view + */ + gdk_region_subtract (view_bounding_region, other_view_bounding_region); + gdk_region_destroy (other_view_bounding_region); + other_view_bounding_region = NULL; + + /* FIXME: need to also add in overlapping areas where the other view isn't + * fully opaque. + */ + return view_bounding_region; +} + +static void +pop_window_view_render_placeholder_to_context (PopWindowView *view, + cairo_t *cairo_context) +{ + + cairo_translate (cairo_context, view->priv->x, view->priv->y); + + cairo_set_source_rgba (cairo_context, 1.0, 1.0, 1.0, .75); + cairo_rectangle (cairo_context, 0.0, 0.0, + view->priv->width, view->priv->height); + cairo_clip (cairo_context); + cairo_paint (cairo_context); + + cairo_set_source_rgb (cairo_context, 0.0, 0.0, 0.0); + cairo_rectangle (cairo_context, 0.0, 0.0, + view->priv->width, view->priv->height); + cairo_stroke (cairo_context); +} + +void +pop_window_view_render_to_context (PopWindowView *view, + cairo_t *cairo_context) +{ + g_return_if_fail (POP_IS_WINDOW_VIEW (view)); + g_return_if_fail (cairo_context != NULL); + + if (view->priv->pattern == NULL) + { + if (gdk_window_is_visible (view->priv->window)) + pop_window_view_render_placeholder_to_context (view, cairo_context); + return; + } + + if (!view->priv->is_damaged && FALSE) + return; + + g_assert (view->priv->clip_path != NULL); + + pop_window_view_report_fixed_damage (view); + + cairo_translate (cairo_context, view->priv->x, view->priv->y); +#if 0 + char *text; + text = g_strdup_printf ("0x%lx", GDK_WINDOW_XWINDOW (view->priv->window)); + cairo_move_to (cairo_context, view->priv->width, view->priv->height); + cairo_set_source_rgb (cairo_context, 1.0, 0.0, 1.0); + cairo_show_text (cairo_context, text); + g_free (text); +#endif + + cairo_set_source (cairo_context, view->priv->pattern); + cairo_move_to (cairo_context, 0.0, 0.0); + cairo_append_path (cairo_context, view->priv->clip_path); + cairo_clip (cairo_context); + cairo_paint (cairo_context); +} + +void +pop_window_view_get_size (PopWindowView *view, + int *width, + int *height) +{ + + if (width) + *width = view->priv->width; + + if (height) + *height = view->priv->height; +} + +void +pop_window_view_get_position (PopWindowView *view, + int *x, + int *y) +{ + if (x) + *x = view->priv->x; + + if (y) + *y = view->priv->y; +} diff --git a/src/pop-window-view.h b/src/pop-window-view.h new file mode 100644 index 0000000..3e3bb5b --- /dev/null +++ b/src/pop-window-view.h @@ -0,0 +1,83 @@ +/* pop-window-view.h - view of redirected offscreen windows + * + * Copyright (C) 2007 Ray Strode <rstrode@redhat.com> + * + * 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, 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 POP_WINDOW_VIEW_H +#define POP_WINDOW_VIEW_H + +#include <glib.h> +#include <glib-object.h> + +#include <X11/X.h> + +#include <cairo.h> + +#include <gdk/gdk.h> + +G_BEGIN_DECLS +#define POP_TYPE_WINDOW_VIEW (pop_window_view_get_type ()) +#define POP_WINDOW_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), POP_TYPE_WINDOW_VIEW, PopWindowView)) +#define POP_WINDOW_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), POP_TYPE_WINDOW_VIEW, PopWindowViewClass)) +#define POP_IS_WINDOW_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), POP_TYPE_WINDOW_VIEW)) +#define POP_IS_WINDOW_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), POP_TYPE_WINDOW_VIEW)) +#define POP_WINDOW_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), POP_TYPE_WINDOW_VIEW, PopWindowViewClass)) +#define POP_WINDOW_VIEW_ERROR (pop_window_view_error_quark ()) +typedef struct _PopWindowView PopWindowView; +typedef struct _PopWindowViewClass PopWindowViewClass; +typedef struct _PopWindowViewPrivate PopWindowViewPrivate; +typedef enum _PopWindowViewError PopWindowViewError; + +struct _PopWindowView +{ + GObject parent; + + /*< private > */ + PopWindowViewPrivate *priv; +}; + +struct _PopWindowViewClass +{ + GObjectClass parent_class; + + /* signals */ + void (* foo) (PopWindowView * window); +}; + +enum _PopWindowViewError +{ + POP_WINDOW_VIEW_ERROR_GENERIC = 0, +}; + +#ifndef POP_HIDE_FUNCTION_DECLARATIONS +GType pop_window_view_get_type (void); +GQuark pop_window_view_error_quark (void); + +PopWindowView *pop_window_view_new (void) G_GNUC_MALLOC; +gboolean pop_window_view_set_window (PopWindowView *view, GdkWindow *window); +void pop_window_view_unset_window (PopWindowView *view); +GdkWindow *pop_window_view_get_window (PopWindowView *view); +void pop_window_view_get_size (PopWindowView *view, gint *width, gint *height); +void pop_window_view_get_position (PopWindowView *view, int *x, int *y); +GdkRegion *pop_window_view_get_difference (PopWindowView *view, + PopWindowView *other_view); +void pop_window_view_render_to_context (PopWindowView *view, + cairo_t *cairo_context); +#endif + +G_END_DECLS +#endif /* POP_WINDOW_VIEW_H */ |