diff options
author | Havoc Pennington <hp@redhat.com> | 2002-10-20 17:49:54 +0000 |
---|---|---|
committer | Havoc Pennington <hp@redhat.com> | 2002-10-20 17:49:54 +0000 |
commit | 58144125e0fe2b43bcb6aad75f258c8042b70feb (patch) | |
tree | d9008a50edad4eaf1a6e2e8c3deb7f899d998ba6 | |
download | startup-notification-58144125e0fe2b43bcb6aad75f258c8042b70feb.tar.gz |
initial copy of liblf with files renamed to libsn
37 files changed, 6736 insertions, 0 deletions
@@ -0,0 +1 @@ +Havoc Pennington <hp@redhat.com>
\ No newline at end of file @@ -0,0 +1,490 @@ +Some code in liblf is under the LGPL, included in this file. This +means that effectively the whole library is LGPL until/unless the LGPL +code is replaced. However, most of the code is provided under a +so-called BSD/X-style license. Each source code file in liblf is +marked with the license that applies to that file. + + + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 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. + +[This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, 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 library, or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link a program with the library, you must provide +complete object files to the recipients so that they can relink them +with the library, after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, 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 companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, 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 library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete 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 distribute a copy of this License along with the +Library. + + 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 Library or any portion +of it, thus forming a work based on the Library, 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) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +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 Library, 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 Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you 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. + + If distribution of 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 satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also compile or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + d) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. 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. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library 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. + + 9. 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 Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +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. + + 11. 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 Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library 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 Library. + +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. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library 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. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library 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 Library +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 Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +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 + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "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 +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. 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 LIBRARY 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 +LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. 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 library's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; 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. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + <signature of Ty Coon>, 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..f257a05 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,70 @@ +2002-09-20 Havoc Pennington <hp@pobox.com> + + * liblf/lf-monitor.c: implement support for xmessage-based + protocol. + +2002-09-20 Havoc Pennington <hp@redhat.com> + + * liblf/lf-xmessages.c: implement parsing the property lists + as in KStartupInfo + +2002-09-20 Havoc Pennington <hp@redhat.com> + + * liblf/lf-xutils.c (lf_internal_send_event_all_screens): new + function + + * liblf/lf-launcher.c (lf_launcher_context_initiate): use new + lf_internal_send_event_all_screens + + * liblf/lf-xmessages.c: implement serializing strings + via client messages + + * liblf/lf-common.c (lf_display_process_event): process xmessage + events + +2002-09-12 Havoc Pennington <hp@pobox.com> + + * liblf/lf-launcher.c (lf_launcher_context_initiate): validate + UTF-8 for the hostname + +2002-09-12 Havoc Pennington <hp@redhat.com> + + * liblf/lf-monitor.c: fixes + +2002-09-12 Havoc Pennington <hp@redhat.com> + + * liblf/lf-monitor.c: mostly workingish now + +2002-09-12 Havoc Pennington <hp@pobox.com> + + * test/test-monitor.c: test program for the monitor + API + + * liblf/lf-monitor.c: finishing up + +2002-09-11 Havoc Pennington <hp@redhat.com> + + * liblf/lf-monitor.c: hacking + +2002-09-10 Havoc Pennington <hp@redhat.com> + + * lotsa fixes and more work + +2002-09-10 Havoc Pennington <hp@pobox.com> + + * liblf/lf-launcher.c: more implementation hacking + +2002-09-09 Havoc Pennington <hp@redhat.com> + + * liblf/lf-list.c: add a list abstraction + + * liblf/lf-launcher.c: start implementing + + * liblf/lf-common.c (lf_display_process_event): update docs + + * doc/launch-notification.txt: some small tweaks + + * COPYING: include LGPL + + * liblf/lf-util.c: fix the copyright + diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..b831b63 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,3 @@ + +SUBDIRS=liblf test + @@ -0,0 +1 @@ +No news.
\ No newline at end of file diff --git a/acconfig.h b/acconfig.h new file mode 100644 index 0000000..e100ec2 --- /dev/null +++ b/acconfig.h @@ -0,0 +1,10 @@ +#undef PACKAGE +#undef VERSION +#undef HAVE_CATGETS +#undef HAVE_GETTEXT +#undef HAVE_LC_MESSAGES +#undef HAVE_STPCPY +#undef ENABLE_NLS +#undef HAVE_PTHREAD_H +#undef GETTEXT_PACKAGE +#undef SANE_MALLOC_PROTOS diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..6fdf952 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,70 @@ +#!/bin/sh +# Run this to generate all the initial makefiles, etc. + +srcdir=`dirname $0` +test -z "$srcdir" && srcdir=. + +ORIGDIR=`pwd` +cd $srcdir + +PROJECT=launchfeedback +TEST_TYPE=-f +FILE=liblf-1.0.pc.in + +DIE=0 + +(autoconf --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "You must have autoconf installed to compile $PROJECT." + echo "Download the appropriate package for your distribution," + echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/" + DIE=1 +} + +AUTOMAKE=automake-1.4 +ACLOCAL=aclocal-1.4 + +($AUTOMAKE --version) < /dev/null > /dev/null 2>&1 || { + AUTOMAKE=automake + ACLOCAL=aclocal +} + +($AUTOMAKE --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "You must have automake installed to compile $PROJECT." + echo "Get ftp://ftp.cygnus.com/pub/home/tromey/automake-1.2d.tar.gz" + echo "(or a newer version if it is available)" + DIE=1 +} + +if test "$DIE" -eq 1; then + exit 1 +fi + +test $TEST_TYPE $FILE || { + echo "You must run this script in the top-level $PROJECT directory" + exit 1 +} + +if test -z "$*"; then + echo "I am going to run ./configure with no arguments - if you wish " + echo "to pass any to it, please specify them on the $0 command line." +fi + +libtoolize --copy --force + +echo $ACLOCAL $ACLOCAL_FLAGS +$ACLOCAL $ACLOCAL_FLAGS + +# optionally feature autoheader +(autoheader --version) < /dev/null > /dev/null 2>&1 && autoheader + +$AUTOMAKE -a $am_opt +autoconf || echo "autoconf failed - version 2.5x is probably required" + +cd $ORIGDIR + +$srcdir/configure --enable-maintainer-mode "$@" + +echo +echo "Now type 'make' to compile $PROJECT." diff --git a/configure.in b/configure.in new file mode 100644 index 0000000..3ad8285 --- /dev/null +++ b/configure.in @@ -0,0 +1,89 @@ +AC_INIT(liblf/lf-launchee.c) + +AM_CONFIG_HEADER(config.h) + +AM_INIT_AUTOMAKE(launchfeedback, 0.1) + +# Honor aclocal flags +ACLOCAL="$ACLOCAL $ACLOCAL_FLAGS" + +AM_MAINTAINER_MODE + +AC_PROG_CC +AC_ISC_POSIX +AC_HEADER_STDC +AC_ARG_PROGRAM +AM_PROG_LIBTOOL + +changequote(,)dnl +if test "x$GCC" = "xyes"; then + case " $CFLAGS " in + *[\ \ ]-Wall[\ \ ]*) ;; + *) CFLAGS="$CFLAGS -Wall" ;; + esac +fi +changequote([,])dnl + +AC_PATH_XTRA + +if test x$no_x = xyes ; then + AC_MSG_ERROR([X11 development libraries/headers not found]) +fi + + +dnl (check from glib) +dnl ok, here we try to check whether the systems prototypes for +dnl malloc and friends actually match the prototypes provided +dnl by lf-common.h (keep in sync). i currently only know how to check +dnl this reliably with gcc (-Werror), improvements for other +dnl compilers are appreciated. +SANE_MALLOC_PROTOS=no +AC_MSG_CHECKING([if malloc() and friends prototypes are lf-util.h compatible]) +lf_save_CFLAGS=$CFLAGS +if test "x$GCC" = "xyes"; then + CFLAGS="$CFLAGS -Werror" + AC_TRY_COMPILE([#include <stdlib.h>], [ + void* (*my_calloc_p) (size_t, size_t) = calloc; + void* (*my_malloc_p) (size_t) = malloc; + void (*my_free_p) (void*) = free; + void* (*my_realloc_p) (void*, size_t) = realloc; + my_calloc_p = 0; + my_malloc_p = 0; + my_free_p = 0; + my_realloc_p = 0; + ], + AC_DEFINE(SANE_MALLOC_PROTOS) + SANE_MALLOC_PROTOS=yes) +fi +AC_MSG_RESULT($SANE_MALLOC_PROTOS) +CFLAGS=$lf_save_CFLAGS + +dnl *** check for sane realloc() *** +AC_CACHE_CHECK([whether realloc (NULL,) will work],lf_cv_sane_realloc,[ + AC_TRY_RUN([ + #include <stdlib.h> + int main() { + return realloc (0, sizeof (int)) == 0; + }], + [lf_cv_sane_realloc=yes], + [lf_cv_sane_realloc=no], + []) +]) +if test x$lf_cv_sane_realloc = xyes; then + AC_DEFINE(REALLOC_0_WORKS,1,[whether realloc (NULL,) works]) +fi + +## try definining HAVE_BACKTRACE +AC_CHECK_HEADERS(execinfo.h, [AC_CHECK_FUNCS(backtrace)]) + +LIBLF_CFLAGS=$X_CFLAGS +LIBLF_LIBS=" $X_LIBS $X_PRE_LIBS -lX11 $X_EXTRA_LIBS" +AC_SUBST(LIBLF_CFLAGS) +AC_SUBST(LIBLF_LIBS) + +AC_OUTPUT([ +Makefile +liblf/Makefile +test/Makefile +liblf-1.0.pc +]) diff --git a/doc/startup-notification.txt b/doc/startup-notification.txt new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/doc/startup-notification.txt diff --git a/libsn-1.0.pc.in b/libsn-1.0.pc.in new file mode 100644 index 0000000..d980bab --- /dev/null +++ b/libsn-1.0.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: liblf +Description: Launch feedback library +Version: @VERSION@ +Libs: -L${libdir} -llf-1 +Cflags: -I${includedir}/liblf-1.0 + diff --git a/libsn/Makefile.am b/libsn/Makefile.am new file mode 100644 index 0000000..bfe4e45 --- /dev/null +++ b/libsn/Makefile.am @@ -0,0 +1,31 @@ + +INCLUDES=-I$(top_srcdir) $(LIBLF_CFLAGS) + +liblfincludedir=$(includedir)/liblf-1.0/liblf + +lib_LTLIBRARIES=liblf-1.la + +liblfinclude_HEADERS= \ + lf.h \ + lf-common.h \ + lf-launchee.h \ + lf-launcher.h \ + lf-monitor.h \ + lf-util.h + +liblf_1_la_SOURCES= \ + lf-common.c \ + lf-internals.c \ + lf-internals.h \ + lf-launchee.c \ + lf-launcher.c \ + lf-list.c \ + lf-list.h \ + lf-monitor.c \ + lf-util.c \ + lf-xmessages.c \ + lf-xmessages.h \ + lf-xutils.c \ + lf-xutils.h + +liblf_1_la_LIBADD= $(LIBLF_LIBS) diff --git a/libsn/sn-common.c b/libsn/sn-common.c new file mode 100644 index 0000000..f14141f --- /dev/null +++ b/libsn/sn-common.c @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2002 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <config.h> +#include "lf-common.h" +#include "lf-internals.h" + +struct LfDisplay +{ + int refcount; + Display *xdisplay; + int n_screens; + Screen **screens; + LfDisplayErrorTrapPush push_trap_func; + LfDisplayErrorTrapPop pop_trap_func; +}; + +/** + * lf_display_new: + * @xdisplay: an X window system display + * @push_trap_func: function to push an X error trap + * @pop_trap_func: function to pop an X error trap + * + * Creates a new #LfDisplay object, containing + * data that liblf associates with an X display. + * + * @push_trap_func should be a function that causes X errors to be + * ignored until @pop_trap_func is called as many times as + * @push_trap_func has been called. (Nested push/pop pairs must be + * supported.) The outermost @pop_trap_func in a set of nested pairs + * must call XSync() to ensure that all errors that will occur have in + * fact occurred. These functions are used to avoid X errors due to + * BadWindow and such. + * + * Return value: the new #LfDisplay + **/ +LfDisplay* +lf_display_new (Display *xdisplay, + LfDisplayErrorTrapPush push_trap_func, + LfDisplayErrorTrapPop pop_trap_func) +{ + LfDisplay *display; + int i; + + display = lf_new0 (LfDisplay, 1); + + display->xdisplay = xdisplay; + display->n_screens = ScreenCount (xdisplay); + display->screens = lf_new (Screen*, display->n_screens); + display->refcount = 1; + + display->push_trap_func = push_trap_func; + display->pop_trap_func = pop_trap_func; + + for (i = 0; i < display->n_screens; ++i) + display->screens[i] = ScreenOfDisplay (display->xdisplay, i); + + return display; +} + +/** + * lf_display_ref: + * @display: an #LfDisplay + * + * Increment the reference count for @display + **/ +void +lf_display_ref (LfDisplay *display) +{ + display->refcount += 1; +} + +/** + * lf_display_unref: + * @display: an #LfDisplay + * + * Decrement the reference count for @display, freeing + * display if the reference count reaches zero. + **/ +void +lf_display_unref (LfDisplay *display) +{ + display->refcount -= 1; + if (display->refcount == 0) + { + lf_free (display->screens); + lf_free (display); + } +} + +/** + * lf_display_get_x_display: + * @display: an #LfDisplay + * + * + * + * Return value: X display for this #LfDisplay + **/ +Display* +lf_display_get_x_display (LfDisplay *display) +{ + + return display->xdisplay; +} + +/** + * lf_display_get_x_screen: + * @display: an #LfDisplay + * @number: screen number to get + * + * Gets a screen by number; if the screen number + * does not exist, returns %NULL. + * + * Return value: X screen or %NULL + **/ +Screen* +lf_display_get_x_screen (LfDisplay *display, + int number) +{ + if (number < 0 || number >= display->n_screens) + return NULL; + else + return display->screens[number]; +} + +/** + * lf_display_process_event: + * @display: a display + * @xevent: X event + * + * liblf should be given a chance to see all X events by passing them + * to this function. If the event was a property notify or client + * message related to the launch feedback protocol, the + * lf_display_process_event() returns true. Calling + * lf_display_process_event() is not currently required for launchees, + * only launchers and launch feedback displayers. The function returns + * false for mapping, unmapping, window destruction, and selection + * events even if they were involved in launch feedback. + * + * Return value: true if the event was a property notify or client message involved in launch feedback + **/ +lf_bool_t +lf_display_process_event (LfDisplay *display, + XEvent *xevent) +{ + lf_bool_t retval; + + retval = FALSE; + + if (lf_internal_launcher_process_event (display, xevent)) + retval = TRUE; + + if (lf_internal_monitor_process_event (display, xevent)) + retval = TRUE; + + if (lf_internal_xmessage_process_event (display, xevent)) + retval = TRUE; + + return retval; +} + +/** + * lf_display_error_trap_push: + * @display: a display + * + * Calls the push_trap_func from lf_display_new() if non-NULL. + **/ +void +lf_display_error_trap_push (LfDisplay *display) +{ + if (display->push_trap_func) + (* display->push_trap_func) (display, display->xdisplay); +} + +/** + * lf_display_error_trap_pop: + * @display: a display + * + * Calls the pop_trap_func from lf_display_new() if non-NULL. + **/ +void +lf_display_error_trap_pop (LfDisplay *display) +{ + if (display->pop_trap_func) + (* display->pop_trap_func) (display, display->xdisplay); +} + diff --git a/libsn/sn-common.h b/libsn/sn-common.h new file mode 100644 index 0000000..57a1ba8 --- /dev/null +++ b/libsn/sn-common.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2002 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + +#ifndef __LF_COMMON_H__ +#define __LF_COMMON_H__ + +#include <liblf/lf-util.h> +#include <X11/Xlib.h> + +LF_BEGIN_DECLS + +typedef struct LfDisplay LfDisplay; + +typedef enum +{ + LF_LAUNCH_TYPE_OTHER, + LF_LAUNCH_TYPE_DOCK_ICON, + LF_LAUNCH_TYPE_DESKTOP_ICON, + LF_LAUNCH_TYPE_MENU, + LF_LAUNCH_TYPE_KEY_SHORTCUT + +} LfLaunchType; + +typedef void (* LfDisplayErrorTrapPush) (LfDisplay *display, + Display *xdisplay); +typedef void (* LfDisplayErrorTrapPop) (LfDisplay *display, + Display *xdisplay); + +LfDisplay* lf_display_new (Display *xdisplay, + LfDisplayErrorTrapPush push_trap_func, + LfDisplayErrorTrapPop pop_trap_func); +void lf_display_ref (LfDisplay *display); +void lf_display_unref (LfDisplay *display); +Display* lf_display_get_x_display (LfDisplay *display); +Screen* lf_display_get_x_screen (LfDisplay *display, + int number); +lf_bool_t lf_display_process_event (LfDisplay *display, + XEvent *xevent); +void lf_display_error_trap_push (LfDisplay *display); +void lf_display_error_trap_pop (LfDisplay *display); + + + +LF_END_DECLS + +#endif /* __LF_COMMON_H__ */ diff --git a/libsn/sn-internals.c b/libsn/sn-internals.c new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/libsn/sn-internals.c diff --git a/libsn/sn-internals.h b/libsn/sn-internals.h new file mode 100644 index 0000000..4500e19 --- /dev/null +++ b/libsn/sn-internals.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2002 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + +#ifndef __LF_INTERNALS_H__ +#define __LF_INTERNALS_H__ + +#include <liblf/lf-common.h> + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include <liblf/lf-list.h> +#include <liblf/lf-xutils.h> + +LF_BEGIN_DECLS + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef NULL +#define NULL ((void*) 0) +#endif + +/* --- From lf-launcher.c --- */ +lf_bool_t lf_internal_launcher_process_event (LfDisplay *display, + XEvent *xevent); + +/* --- From lf-monitor.c --- */ +lf_bool_t lf_internal_monitor_process_event (LfDisplay *display, + XEvent *xevent); + +/* --- From lf-util.c --- */ +lf_bool_t lf_internal_utf8_validate (const char *str, + int max_len); +char* lf_internal_strdup (const char *str); +char* lf_internal_strndup (const char *str, + int n); +void lf_internal_strfreev (char **strings); + +unsigned long lf_internal_string_to_ulong (const char* str); + +/* --- From lf-xmessages.c --- */ +lf_bool_t lf_internal_xmessage_process_event (LfDisplay *display, + XEvent *xevent); + +LF_END_DECLS + +#endif /* __LF_INTERNALS_H__ */ diff --git a/libsn/sn-launchee.c b/libsn/sn-launchee.c new file mode 100644 index 0000000..85e5e43 --- /dev/null +++ b/libsn/sn-launchee.c @@ -0,0 +1,222 @@ +/* + * Copyright (C) 2002 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "lf-launchee.h" +#include "lf-internals.h" +#include <errno.h> + +struct LfLauncheeContext +{ + int refcount; + LfDisplay *display; + char *launch_id; + Window launch_window; +}; + +/** + * lf_launchee_context_new: + * @display: an #LfDisplay + * @launch_id: launch ID as in DESKTOP_LAUNCH_ID + * @launch_window: launch window as in DESKTOP_LAUNCH_WINDOW + * + * Creates a new launchee-side context for the launch feedback + * protocol. + * + * Return value: a new launchee context + **/ +LfLauncheeContext* +lf_launchee_context_new (LfDisplay *display, + const char *launch_id, + Window launch_window) +{ + LfLauncheeContext *context; + + context = lf_new0 (LfLauncheeContext, 1); + + context->refcount = 1; + + context->display = display; + lf_display_ref (context->display); + + context->launch_id = lf_malloc (strlen (launch_id) + 1); + strcpy (context->launch_id, launch_id); + + context->launch_window = launch_window; + + return context; +} + +/** + * lf_launchee_context_new_from_environment: + * @display: an #LfDisplay + * + * Tries to create an #LfLauncheeContext given information in + * the program's environment (DESKTOP_LAUNCH_ID and DESKTOP_LAUNCH_WINDOW + * environment variables). Returns %NULL if the env variables are not + * available or can't be parsed. + * + * Return value: a new #LfLauncheeContext or %NULL + **/ +LfLauncheeContext* +lf_launchee_context_new_from_environment (LfDisplay *display) +{ + const char *id_str; + const char *window_str; + unsigned long window; + + id_str = getenv ("DESKTOP_LAUNCH_ID"); + window_str = getenv ("DESKTOP_LAUNCH_WINDOW"); + + if (id_str == NULL || window_str == NULL) + return NULL; + + window = lf_internal_string_to_ulong (window_str); + + if (window == None) + return NULL; + + return lf_launchee_context_new (display, id_str, window); +} + +void +lf_launchee_context_ref (LfLauncheeContext *context) +{ + context->refcount += 1; +} + +void +lf_launchee_context_unref (LfLauncheeContext *context) +{ + context->refcount -= 1; + if (context->refcount == 0) + { + lf_free (context->launch_id); + + lf_free (context); + } +} + +Window +lf_launchee_context_get_launch_window (LfLauncheeContext *context) +{ + return context->launch_window; +} + +const char* +lf_launchee_context_get_launch_id (LfLauncheeContext *context) +{ + return context->launch_id; +} + +/** + * lf_launchee_context_pulse: + * @context: an #LfLauncheeContext + * + * Notifies the launcher that progress is being made. Should be + * called regularly during a long launch operation. + * + **/ +void +lf_launchee_context_pulse (LfLauncheeContext *context) +{ + XEvent xev; + + xev.xclient.type = ClientMessage; + xev.xclient.serial = 0; + xev.xclient.send_event = True; + xev.xclient.display = lf_display_get_x_display (context->display); + xev.xclient.window = context->launch_window; + xev.xclient.message_type = lf_internal_atom_get (context->display, + "_NET_LAUNCH_PULSE"); + xev.xclient.format = 32; + xev.xclient.data.l[0] = 0; + xev.xclient.data.l[1] = 0; + xev.xclient.data.l[2] = 0; + xev.xclient.data.l[3] = 0; + + lf_display_error_trap_push (context->display); + XSendEvent (lf_display_get_x_display (context->display), + context->launch_window, + False, + PropertyChangeMask, + &xev); + XFlush (lf_display_get_x_display (context->display)); + lf_display_error_trap_pop (context->display); +} + +/** + * lf_launchee_context_cancel: + * @context: an #LfLauncheeContext + * + * Called by the launchee application to cancel a launch (will + * probably cause the launcher to kill the launchee). + * + **/ +void +lf_launchee_context_cancel (LfLauncheeContext *context) +{ + lf_internal_set_cardinal (context->display, + context->launch_window, + "_NET_LAUNCH_CANCELED", + 0); +} + +/** + * lf_launchee_context_complete: + * @context: an #LfLauncheeContext + * + * Called by the launchee application when it is fully started up + * and launch feedback should end. + * + **/ +void +lf_launchee_context_complete (LfLauncheeContext *context) +{ + lf_internal_set_cardinal (context->display, + context->launch_window, + "_NET_LAUNCH_COMPLETE", + 0); +} + +/** + * lf_launchee_context_setup_window: + * @context: a #LfLauncheeContext + * @xwindow: window to be set up + * + * Sets up @xwindow, marking it as launched by the launch sequence + * represented by @context. For example sets the _NET_LAUNCH_ID + * property. Only the group leader windows of an application + * MUST be set up with this function, though if + * any other windows come from a separate launch sequence, + * they can be setup up separately. + * + **/ +void +lf_launchee_context_setup_window (LfLauncheeContext *context, + Window xwindow) +{ + lf_internal_set_string (context->display, + xwindow, + "_NET_LAUNCH_ID", + context->launch_id); +} diff --git a/libsn/sn-launchee.h b/libsn/sn-launchee.h new file mode 100644 index 0000000..4969dc3 --- /dev/null +++ b/libsn/sn-launchee.h @@ -0,0 +1,52 @@ +/* Launchee API - if you are a program started by other programs */ +/* + * Copyright (C) 2002 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + +#ifndef __LF_LAUNCHEE_H__ +#define __LF_LAUNCHEE_H__ + +#include <liblf/lf-common.h> + +LF_BEGIN_DECLS + +typedef struct LfLauncheeContext LfLauncheeContext; + +LfLauncheeContext* lf_launchee_context_new (LfDisplay *display, + const char *launch_id, + Window launch_window); +LfLauncheeContext* lf_launchee_context_new_from_environment (LfDisplay *display); +void lf_launchee_context_ref (LfLauncheeContext *context); +void lf_launchee_context_unref (LfLauncheeContext *context); +Window lf_launchee_context_get_launch_window (LfLauncheeContext *context); +const char* lf_launchee_context_get_launch_id (LfLauncheeContext *context); +void lf_launchee_context_pulse (LfLauncheeContext *context); +void lf_launchee_context_cancel (LfLauncheeContext *context); +void lf_launchee_context_complete (LfLauncheeContext *context); +void lf_launchee_context_setup_window (LfLauncheeContext *context, + Window xwindow); + +LF_END_DECLS + +#endif /* __LF_LAUNCHEE_H__ */ diff --git a/libsn/sn-launcher.c b/libsn/sn-launcher.c new file mode 100644 index 0000000..f9803ff --- /dev/null +++ b/libsn/sn-launcher.c @@ -0,0 +1,1075 @@ +/* + * Copyright (C) 2002 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "lf-launcher.h" +#include "lf-internals.h" + +#include <sys/types.h> +#include <unistd.h> + +static LfList* context_list = NULL; + +struct LfLauncherContext +{ + int refcount; + LfDisplay *display; + LfLauncherEventFunc event_func; + void *event_func_data; + LfFreeFunc free_data_func; + char *launch_id; + Window launch_window; + LfLaunchType type; + Window geometry_window; + char *name; + char *description; + int workspace; + char *resource_class; + char *resource_name; + char *window_title; + char *binary_name; + int pid; + char *icon_name; + int x, y, width, height; + unsigned int supports_cancel : 1; + unsigned int completed : 1; + unsigned int canceled : 1; + unsigned int geometry_set : 1; +}; + +/** + * lf_launcher_context_new: + * @display: an #LfDisplay + * @event_func: function to be called when a notable event occurs + * @event_func_data: data to pass to @event_func + * @free_data_func: function to be called on @event_func_data when freeing the context + * + * Creates a new launcher context, to be used by the program that is + * starting a launch sequence. For example a file manager might + * create a launcher context when the user double-clicks on + * an application icon. + * + * Return value: a new #LfLauncherContext + **/ +LfLauncherContext* +lf_launcher_context_new (LfDisplay *display, + LfLauncherEventFunc event_func, + void *event_func_data, + LfFreeFunc free_data_func) +{ + LfLauncherContext *context; + + if (context_list == NULL) + context_list = lf_list_new (); + + context = lf_new0 (LfLauncherContext, 1); + + context->refcount = 1; + context->display = display; + lf_display_ref (context->display); + context->event_func = event_func; + context->event_func_data = event_func_data; + context->free_data_func = free_data_func; + + context->workspace = -1; + context->pid = -1; + context->type = LF_LAUNCH_TYPE_OTHER; + + lf_list_prepend (context_list, context); + + return context; +} + +/** + * lf_launcher_context_ref: + * @context: a #LfLauncherContext + * + * Increments the reference count of @context + **/ +void +lf_launcher_context_ref (LfLauncherContext *context) +{ + context->refcount += 1; +} + +/** + * lf_launcher_context_unref: + * @context: a #LfLauncherContext + * + * Decrements the reference count of @context and frees the + * context if the count reaches zero. + **/ +void +lf_launcher_context_unref (LfLauncherContext *context) +{ + context->refcount -= 1; + + if (context->refcount == 0) + { + lf_list_remove (context_list, context); + + if (context->free_data_func) + (* context->free_data_func) (context->event_func_data); + + lf_free (context->launch_id); + + if (context->launch_window != None) + { + lf_display_error_trap_push (context->display); + + XDestroyWindow (lf_display_get_x_display (context->display), + context->launch_window); + + lf_display_error_trap_pop (context->display); + } + + lf_display_unref (context->display); + lf_free (context); + } +} + +static char* +strip_slashes (const char *src) +{ + char *canonicalized_name; + char *s; + + canonicalized_name = lf_internal_strdup (src); + + s = canonicalized_name; + while (*s) + { + if (*s == '/') + *s = '|'; + ++s; + } + + return canonicalized_name; +} + +/** + * lf_launcher_context_initiate: + * @context: an #LfLaunchContext + * @launcher_name: name of the launcher app, suitable for debug output + * @launchee_name: name of the launchee app, suitable for debug output + * @timestamp: X timestamp of event causing the launch + * + * Initiates a launch sequence. All the properties of the launch (such + * as type, geometry, description) should be set up prior to + * initiating the sequence. + **/ +void +lf_launcher_context_initiate (LfLauncherContext *context, + const char *launcher_name, + const char *launchee_name, + Time timestamp) +{ + static int sequence_number = 0; + static lf_bool_t have_hostname = FALSE; + static char hostbuf[257]; + char *s; + int len; + Display *xdisplay; + char *canonicalized_launcher; + char *canonicalized_launchee; + Atom atoms[1]; + + if (context->launch_id != NULL) + { + fprintf (stderr, "%s called twice for the same LfLaunchContext\n", + __FUNCTION__); + return; + } + + if (!have_hostname) + { + if (gethostname (hostbuf, sizeof (hostbuf)-1) == 0) + have_hostname = TRUE; + else + hostbuf[0] = '\0'; + } + + canonicalized_launcher = strip_slashes (launcher_name); + canonicalized_launchee = strip_slashes (launchee_name); + + /* man I wish we could use g_strdup_printf */ + len = strlen (launcher_name) + strlen (launchee_name) + + 256; /* 256 is longer than a couple %d and some slashes */ + + s = lf_malloc (len + 3); + snprintf (s, len, "%s/%s/%lu/%d-%d-%s", + canonicalized_launcher, canonicalized_launchee, (unsigned long) timestamp, + (int) getpid (), (int) sequence_number, hostbuf); + ++sequence_number; + + lf_free (canonicalized_launcher); + lf_free (canonicalized_launchee); + + context->launch_id = s; + + xdisplay = lf_display_get_x_display (context->display); + + { + XSetWindowAttributes attrs; + + attrs.override_redirect = True; + attrs.event_mask = PropertyChangeMask | StructureNotifyMask; + + context->launch_window = + XCreateWindow (xdisplay, + RootWindow (xdisplay, 0), + -100, -100, 1, 1, + 0, + CopyFromParent, + CopyFromParent, + CopyFromParent, + CWOverrideRedirect | CWEventMask, + &attrs); + } + + /* push outer error to allow avoiding XSync after every + * property set + */ + lf_display_error_trap_push (context->display); + + lf_internal_set_string (context->display, + context->launch_window, + "_NET_LAUNCH_ID", + context->launch_id); + + lf_internal_set_string (context->display, + context->launch_window, + "_NET_LAUNCH_HOSTNAME", + hostbuf); + + switch (context->type) + { + case LF_LAUNCH_TYPE_OTHER: + atoms[0] = lf_internal_atom_get (context->display, + "_NET_LAUNCH_TYPE_OTHER"); + break; + case LF_LAUNCH_TYPE_DOCK_ICON: + atoms[0] = lf_internal_atom_get (context->display, + "_NET_LAUNCH_TYPE_DOCK_ICON"); + break; + case LF_LAUNCH_TYPE_DESKTOP_ICON: + atoms[0] = lf_internal_atom_get (context->display, + "_NET_LAUNCH_TYPE_DESKTOP_ICON"); + break; + case LF_LAUNCH_TYPE_MENU: + atoms[0] = lf_internal_atom_get (context->display, + "_NET_LAUNCH_TYPE_MENU"); + break; + case LF_LAUNCH_TYPE_KEY_SHORTCUT: + atoms[0] = lf_internal_atom_get (context->display, + "_NET_LAUNCH_TYPE_KEY_SHORTCUT"); + break; + } + + lf_internal_set_atom_list (context->display, + context->launch_window, + "_NET_LAUNCH_TYPE", + atoms, 1); + + if (context->geometry_set) + { + int cardinals[4]; + + cardinals[0] = context->x; + cardinals[1] = context->y; + cardinals[2] = context->width; + cardinals[3] = context->height; + + lf_internal_set_cardinal_list (context->display, + context->launch_window, + "_NET_LAUNCH_GEOMETRY", + cardinals, 4); + } + + if (context->geometry_window != None) + { + lf_internal_set_window (context->display, + context->launch_window, + "_NET_LAUNCH_GEOMETRY_WINDOW", + context->geometry_window); + } + + if (context->supports_cancel) + { + lf_internal_set_cardinal (context->display, + context->launch_window, + "_NET_LAUNCH_SUPPORTS_CANCEL", + context->supports_cancel); + } + + if (context->name) + { + lf_internal_set_utf8_string (context->display, + context->launch_window, + "_NET_LAUNCH_NAME", + context->name); + } + + if (context->description) + { + lf_internal_set_utf8_string (context->display, + context->launch_window, + "_NET_LAUNCH_DESCRIPTION", + context->description); + } + + if (context->workspace >= 0) + { + lf_internal_set_cardinal (context->display, + context->launch_window, + "_NET_LAUNCH_DESKTOP", + context->workspace); + } + + if (context->pid >= 0) + { + lf_internal_set_cardinal (context->display, + context->launch_window, + "_NET_LAUNCH_PID", + context->pid); + } + + if (context->binary_name) + { + lf_internal_set_string (context->display, + context->launch_window, + "_NET_LAUNCH_BINARY_NAME", + context->binary_name); + } + + if (context->icon_name) + { + lf_internal_set_string (context->display, + context->launch_window, + "_NET_LAUNCH_ICON_NAME", + context->icon_name); + } + + if (context->resource_class) + { + lf_internal_set_string (context->display, + context->launch_window, + "_NET_LAUNCH_LEGACY_RESOURCE_CLASS", + context->resource_class); + } + + if (context->resource_name) + { + lf_internal_set_string (context->display, + context->launch_window, + "_NET_LAUNCH_LEGACY_RESOURCE_NAME", + context->resource_name); + } + + if (context->window_title) + { + lf_internal_set_string (context->display, + context->launch_window, + "_NET_LAUNCH_LEGACY_NAME", + context->window_title); + } + + lf_display_error_trap_pop (context->display); + + /* Sync to server (so the launch window ID exists for example) */ + XFlush (xdisplay); + + /* Initiation message to all screens */ + { + XEvent xev; + + xev.xclient.type = ClientMessage; + xev.xclient.serial = 0; + xev.xclient.send_event = True; + xev.xclient.display = xdisplay; + xev.xclient.window = context->launch_window; + xev.xclient.message_type = lf_internal_atom_get (context->display, + "_NET_LAUNCH_INITIATE"); + xev.xclient.format = 32; + xev.xclient.data.l[0] = timestamp; + xev.xclient.data.l[1] = 0; + xev.xclient.data.l[2] = 0; + xev.xclient.data.l[3] = 0; + + lf_internal_send_event_all_screens (context->display, + PropertyChangeMask, + &xev); + } +} + +Window +lf_launcher_context_get_launch_window (LfLauncherContext *context) +{ + return context->launch_window; +} + +const char* +lf_launcher_context_get_launch_id (LfLauncherContext *context) +{ + + return context->launch_id; +} + +lf_bool_t +lf_launcher_context_get_initiated (LfLauncherContext *context) +{ + return context->launch_id != NULL; +} + +lf_bool_t +lf_launcher_context_get_canceled (LfLauncherContext *context) +{ + return context->canceled; +} + +/** + * lf_launcher_context_get_completed: + * @context: an #LfLauncherContext + * + * Returns %TRUE if _NET_LAUNCH_COMPLETE has been set or the + * launch sequence window has been destroyed. + * + * Return value: %TRUE if the launch sequence has been completed + **/ +lf_bool_t +lf_launcher_context_get_completed (LfLauncherContext *context) +{ + return context->completed; +} + +/** + * lf_launcher_context_cancel: + * @context: an #LfLauncherContext + * + * Marks the launch canceled by setting the _NET_LAUNCH_CANCELED + * property on the launch window. May not be called if the launch has + * not been initiated. An #LF_LAUNCHER_EVENT_CANCELED event should be + * received in response to the cancellation, under normal + * circumstances. + * + * lf_launcher_context_cancel() should be called to request a + * cancellation. Normally the launcher process is the process that + * performs the cancellation as well, in response to an + * #LF_LAUNCHER_EVENT_CANCELED event. + * + **/ +void +lf_launcher_context_cancel (LfLauncherContext *context) +{ + if (context->launch_id == NULL) + { + fprintf (stderr, "%s called for an LfLauncherContext that hasn't been initiated\n", + __FUNCTION__); + return; + } + + lf_internal_set_cardinal (context->display, + context->launch_window, + "_NET_LAUNCH_CANCELED", + 0); +} + +/** + * lf_launcher_context_complete: + * @context: an #LfLauncherContext + * + * Marks @context as completed. Normally the launchee process marks a + * launch sequence completed, however the launcher has to do it + * if the launch is canceled. + * + **/ +void +lf_launcher_context_complete (LfLauncherContext *context) +{ + if (context->launch_id == NULL) + { + fprintf (stderr, "%s called for an LfLauncherContext that hasn't been initiated\n", + __FUNCTION__); + return; + } + + lf_internal_set_cardinal (context->display, + context->launch_window, + "_NET_LAUNCH_COMPLETE", + 0); +} + +/** + * lf_launcher_context_setup_child_process: + * @context: an #LfLauncherContext + * + * This function should be called after forking, but before exec(), in + * the child process being launched. It sets up the environment variables + * telling the child process about the launch ID and launch window. + * This function will leak the strings passed to putenv() so should + * only be used prior to an exec(). + * + **/ +void +lf_launcher_context_setup_child_process (LfLauncherContext *context) +{ + char *launch_id; + char *s; + char *launch_window; + + if (context->launch_id == NULL) + { + fprintf (stderr, "%s called for an LfLauncherContext that hasn't been initiated\n", + __FUNCTION__); + return; + } + + /* Man we need glib here */ + + launch_id = lf_malloc (strlen (context->launch_id) + strlen ("DESKTOP_LAUNCH_ID") + 3); + strcpy (launch_id, "DESKTOP_LAUNCH_ID="); + strcat (launch_id, context->launch_id); + + putenv (launch_id); + + launch_window = lf_malloc (strlen ("DESKTOP_LAUNCH_WINDOW") + 128); + strcpy (launch_window, "DESKTOP_LAUNCH_WINDOW="); + s = launch_window; + while (*s) + ++s; + + snprintf (s, 100, "0x%lx", context->launch_window); + + putenv (launch_window); + + /* Can't free strings passed to putenv */ +} + +#define WARN_ALREADY_INITIATED(context) do { if ((context)->launch_id != NULL) { \ + fprintf (stderr, "%s called for an LfLauncherContext that has already been initiated\n", \ + __FUNCTION__); \ + return; \ +} } while (0) + +void +lf_launcher_context_set_launch_type (LfLauncherContext *context, + LfLaunchType type) +{ + WARN_ALREADY_INITIATED (context); + + context->type = type; +} + +void +lf_launcher_context_set_geometry_window (LfLauncherContext *context, + Window xwindow) +{ + WARN_ALREADY_INITIATED (context); + + context->geometry_window = xwindow; +} + +void +lf_launcher_context_set_supports_cancel (LfLauncherContext *context, + lf_bool_t supports_cancel) +{ + WARN_ALREADY_INITIATED (context); + + context->supports_cancel = supports_cancel; +} + +void +lf_launcher_context_set_launch_name (LfLauncherContext *context, + const char *name) +{ + WARN_ALREADY_INITIATED (context); + + lf_free (context->name); + context->name = lf_internal_strdup (name); +} + +void +lf_launcher_context_set_launch_description (LfLauncherContext *context, + const char *description) +{ + WARN_ALREADY_INITIATED (context); + + lf_free (context->description); + context->description = lf_internal_strdup (description); +} + +void +lf_launcher_context_set_launch_workspace (LfLauncherContext *context, + int workspace) +{ + WARN_ALREADY_INITIATED (context); + + context->workspace = workspace; +} + +void +lf_launcher_context_set_legacy_resource_class (LfLauncherContext *context, + const char *klass) +{ + WARN_ALREADY_INITIATED (context); + + lf_free (context->resource_class); + context->resource_class = lf_internal_strdup (klass); +} + +void +lf_launcher_context_set_legacy_resource_name (LfLauncherContext *context, + const char *name) +{ + WARN_ALREADY_INITIATED (context); + + lf_free (context->resource_name); + context->resource_name = lf_internal_strdup (name); +} + +void +lf_launcher_context_set_legacy_window_title (LfLauncherContext *context, + const char *title) +{ + WARN_ALREADY_INITIATED (context); + + lf_free (context->window_title); + context->window_title = lf_internal_strdup (title); +} + +void +lf_launcher_context_set_binary_name (LfLauncherContext *context, + const char *name) +{ + WARN_ALREADY_INITIATED (context); + + lf_free (context->binary_name); + context->binary_name = lf_internal_strdup (name); +} + +void +lf_launcher_context_set_pid (LfLauncherContext *context, + int pid) +{ + context->pid = pid; + + /* set the X property if launch window already exists */ + if (context->launch_id != NULL) + { + lf_internal_set_cardinal (context->display, + context->launch_window, + "_NET_LAUNCH_PID", + context->pid); + } +} + +void +lf_launcher_context_set_icon_name (LfLauncherContext *context, + const char *name) +{ + WARN_ALREADY_INITIATED (context); + + lf_free (context->icon_name); + context->icon_name = lf_internal_strdup (name); +} + +struct LfLauncherEvent +{ + int refcount; + LfLauncherEventType type; + Time timestamp; + LfLauncherContext *context; +}; + +/** + * lf_launcher_event_copy: + * @event: event to copy + * + * Creates a copy of @event, the copy has a reference count of one. + * + * Return value: a new #LfLauncherEvent that's a copy of @event + **/ +LfLauncherEvent* +lf_launcher_event_copy (LfLauncherEvent *event) +{ + LfLauncherEvent *copy; + + copy = lf_new (LfLauncherEvent, 1); + + copy->refcount = 1; + copy->type = event->type; + copy->timestamp = event->timestamp; + copy->context = event->context; + if (copy->context) + lf_launcher_context_ref (copy->context); + + return copy; +} + +/** + * lf_launcher_event_ref: + * @event: a #LfLauncherEvent + * + * Increments @event's reference count. + **/ +void +lf_launcher_event_ref (LfLauncherEvent *event) +{ + event->refcount += 1; +} + +/** + * lf_launcher_event_unref: + * @event: a #LfLauncherEvent + * + * Decrements @event's reference count and frees @event + * if the count reaches zero. + **/ +void +lf_launcher_event_unref (LfLauncherEvent *event) +{ + event->refcount -= 1; + + if (event->refcount == 0) + { + if (event->context) + lf_launcher_context_unref (event->context); + lf_free (event); + } +} + +/** + * lf_launcher_event_get_type: + * @event: a #LfLauncherEvent + * + * Gets the type of the launcher event. + * + * Return value: the type of event + **/ +LfLauncherEventType +lf_launcher_event_get_type (LfLauncherEvent *event) +{ + return event->type; +} + +/** + * lf_launcher_event_get_context: + * @event: a #LfLauncherEvent + * + * Gets the context associated with @event. The + * returned context is owned by the event, i.e. + * the caller of lf_launcher_event_get_context() should + * not unref the context. + * + * Return value: the context for this event + **/ +LfLauncherContext* +lf_launcher_event_get_context (LfLauncherEvent *event) +{ + return event->context; +} + +/** + * lf_launcher_event_get_time: + * @event: a #LfLauncherEvent + * + * Gets the X Window System timestamp associated with this launcher + * event. + * + * Return value: timestamp for the event, or CurrentTime if none available + **/ +Time +lf_launcher_event_get_time (LfLauncherEvent *event) +{ + return event->timestamp; +} + +static lf_bool_t +check_cardinal_exists (LfDisplay *display, + Window xwindow, + const char *property) +{ + int val; + + return lf_internal_get_cardinal (display, xwindow, property, + &val); +} + +typedef struct +{ + LfDisplay *display; + Window launch_window; + lf_bool_t result; +} HaveContextsData; + +static lf_bool_t +have_active_contexts_foreach (void *value, + void *data) +{ + LfLauncherContext *context = value; + HaveContextsData *hcd = data; + + if (!context->completed && + context->launch_window == hcd->launch_window && + lf_display_get_x_display (context->display) == + lf_display_get_x_display (hcd->display)) + { + hcd->result = TRUE; + return FALSE; + } + + return TRUE; +} + +typedef struct +{ + LfDisplay *display; + Window launch_window; + LfList *contexts; +} FindContextsData; + +static lf_bool_t +find_active_contexts_foreach (void *value, + void *data) +{ + LfLauncherContext *context = value; + FindContextsData *fcd = data; + + if (!context->completed && + context->launch_window == fcd->launch_window && + lf_display_get_x_display (context->display) == + lf_display_get_x_display (fcd->display)) + lf_list_prepend (fcd->contexts, context); + + return TRUE; +} + +typedef struct +{ + LfLauncherEvent *base_event; + LfList *events; +} CreateEventsData; + +static lf_bool_t +create_events_foreach (void *value, + void *data) +{ + LfLauncherContext *context = value; + CreateEventsData *ced = data; + LfLauncherEvent *event; + + event = lf_launcher_event_copy (ced->base_event); + event->context = context; + lf_launcher_context_ref (context); + + lf_list_prepend (ced->events, event); + + return TRUE; +} + +static lf_bool_t +dispatch_events_foreach (void *value, + void *data) +{ + LfLauncherEvent *event = value; + + /* Filter out duplicate events and update flags */ + switch (event->type) + { + case LF_LAUNCHER_EVENT_CANCELED: + if (event->context->canceled) + { + lf_launcher_event_unref (event); + event = NULL; + } + else + { + event->context->canceled = TRUE; + } + break; + case LF_LAUNCHER_EVENT_COMPLETED: + if (event->context->completed) + { + lf_launcher_event_unref (event); + event = NULL; + } + else + { + event->context->completed = TRUE; + } + break; + + default: + break; + } + + if (event) + { + if (event->context->event_func) + (* event->context->event_func) (event, + event->context->event_func_data); + lf_launcher_event_unref (event); + } + + return TRUE; +} + +static void +dispatch_event (LfDisplay *display, + Window launch_window, + LfLauncherEvent *event) +{ + /* Find all applicable contexts, create an event for each, and send + * the events out. + */ + FindContextsData fcd; + CreateEventsData ced; + + fcd.display = display; + fcd.launch_window = launch_window; + fcd.contexts = lf_list_new (); + + if (context_list != NULL) + lf_list_foreach (context_list, find_active_contexts_foreach, &fcd); + + ced.base_event = event; + ced.events = lf_list_new (); + lf_list_foreach (fcd.contexts, create_events_foreach, &ced); + + /* This unref's each event as it's dispatched */ + lf_list_foreach (ced.events, dispatch_events_foreach, NULL); + + lf_list_free (fcd.contexts); + lf_list_free (ced.events); +} + +lf_bool_t +lf_internal_launcher_process_event (LfDisplay *display, + XEvent *xevent) +{ + lf_bool_t retval; + LfLauncherEvent *event; + Window event_xwindow; + + if (context_list == NULL || + lf_list_empty (context_list)) + return FALSE; /* no one cares */ + + event_xwindow = None; + event = NULL; + retval = FALSE; + + switch (xevent->xany.type) + { + case PropertyNotify: + if (xevent->xproperty.atom == + lf_internal_atom_get (display, "_NET_LAUNCH_CANCELED")) + { + event_xwindow = xevent->xproperty.window; + + if (check_cardinal_exists (display, event_xwindow, + "_NET_LAUNCH_CANCELED")) + { + event = lf_new (LfLauncherEvent, 1); + + event->refcount = 1; + event->type = LF_LAUNCHER_EVENT_CANCELED; + event->timestamp = xevent->xproperty.time; + event->context = NULL; + } + + retval = TRUE; + } + else if (xevent->xproperty.atom == + lf_internal_atom_get (display, "_NET_LAUNCH_COMPLETE")) + { + event_xwindow = xevent->xproperty.window; + + if (check_cardinal_exists (display, event_xwindow, + "_NET_LAUNCH_COMPLETE")) + { + event = lf_new (LfLauncherEvent, 1); + + event->refcount = 1; + event->type = LF_LAUNCHER_EVENT_COMPLETED; + event->timestamp = xevent->xproperty.time; + event->context = NULL; + } + + retval = TRUE; + } + break; + + case ClientMessage: + if (xevent->xclient.message_type == + lf_internal_atom_get (display, + "_NET_LAUNCH_PULSE")) + { + event_xwindow = xevent->xclient.window; + + event = lf_new (LfLauncherEvent, 1); + + event->refcount = 1; + event->type = LF_LAUNCHER_EVENT_PULSE; + event->timestamp = CurrentTime; + event->context = NULL; + + retval = TRUE; + } + break; + + case DestroyNotify: + { + HaveContextsData hcd; + hcd.display = display; + hcd.launch_window = xevent->xdestroywindow.window; + hcd.result = FALSE; + if (context_list) + lf_list_foreach (context_list, have_active_contexts_foreach, &hcd); + if (hcd.result) + { + event_xwindow = hcd.launch_window; + + event = lf_new (LfLauncherEvent, 1); + + event->refcount = 1; + event->type = LF_LAUNCHER_EVENT_COMPLETED; + event->timestamp = CurrentTime; + event->context = NULL; + } + } + break; + + default: + break; + } + + if (event != NULL) + { + dispatch_event (display, event_xwindow, event); + + lf_launcher_event_unref (event); + } + + return retval; +} + diff --git a/libsn/sn-launcher.h b/libsn/sn-launcher.h new file mode 100644 index 0000000..5a805d6 --- /dev/null +++ b/libsn/sn-launcher.h @@ -0,0 +1,103 @@ +/* Launcher API - if you are a program that starts other programs */ +/* + * Copyright (C) 2002 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + +#ifndef __LF_LAUNCHER_H__ +#define __LF_LAUNCHER_H__ + +#include <liblf/lf-common.h> + +LF_BEGIN_DECLS + +typedef struct LfLauncherContext LfLauncherContext; +typedef struct LfLauncherEvent LfLauncherEvent; + +typedef void (* LfLauncherEventFunc) (LfLauncherEvent *event, + void *user_data); + +typedef enum +{ + LF_LAUNCHER_EVENT_CANCELED, + LF_LAUNCHER_EVENT_COMPLETED, + LF_LAUNCHER_EVENT_PULSE +} LfLauncherEventType; + +LfLauncherContext* lf_launcher_context_new (LfDisplay *display, + LfLauncherEventFunc event_func, + void *event_func_data, + LfFreeFunc free_data_func); +void lf_launcher_context_ref (LfLauncherContext *context); +void lf_launcher_context_unref (LfLauncherContext *context); + + +void lf_launcher_context_initiate (LfLauncherContext *context, + const char *launcher_name, + const char *launchee_name, + Time timestamp); +Window lf_launcher_context_get_launch_window (LfLauncherContext *context); +const char* lf_launcher_context_get_launch_id (LfLauncherContext *context); +lf_bool_t lf_launcher_context_get_initiated (LfLauncherContext *context); +lf_bool_t lf_launcher_context_get_canceled (LfLauncherContext *context); +lf_bool_t lf_launcher_context_get_completed (LfLauncherContext *context); +void lf_launcher_context_cancel (LfLauncherContext *context); +void lf_launcher_context_complete (LfLauncherContext *context); + +void lf_launcher_context_setup_child_process (LfLauncherContext *context); + +void lf_launcher_context_set_launch_type (LfLauncherContext *context, + LfLaunchType type); +void lf_launcher_context_set_geometry_window (LfLauncherContext *context, + Window xwindow); +void lf_launcher_context_set_supports_cancel (LfLauncherContext *context, + lf_bool_t supports_cancel); +void lf_launcher_context_set_launch_name (LfLauncherContext *context, + const char *name); +void lf_launcher_context_set_launch_description (LfLauncherContext *context, + const char *description); +void lf_launcher_context_set_launch_workspace (LfLauncherContext *context, + int workspace); +void lf_launcher_context_set_legacy_resource_class (LfLauncherContext *context, + const char *klass); +void lf_launcher_context_set_legacy_resource_name (LfLauncherContext *context, + const char *name); +void lf_launcher_context_set_legacy_window_title (LfLauncherContext *context, + const char *title); +void lf_launcher_context_set_binary_name (LfLauncherContext *context, + const char *name); +void lf_launcher_context_set_pid (LfLauncherContext *context, + int pid); +void lf_launcher_context_set_icon_name (LfLauncherContext *context, + const char *name); + +LfLauncherEvent* lf_launcher_event_copy (LfLauncherEvent *event); +void lf_launcher_event_ref (LfLauncherEvent *event); +void lf_launcher_event_unref (LfLauncherEvent *event); +LfLauncherEventType lf_launcher_event_get_type (LfLauncherEvent *event); +LfLauncherContext* lf_launcher_event_get_context (LfLauncherEvent *event); +Time lf_launcher_event_get_time (LfLauncherEvent *event); + +LF_END_DECLS + +#endif /* __LF_LAUNCHER_H__ */ diff --git a/libsn/sn-list.c b/libsn/sn-list.c new file mode 100644 index 0000000..dbbd0a2 --- /dev/null +++ b/libsn/sn-list.c @@ -0,0 +1,168 @@ +/* List abstraction used internally */ +/* + * Copyright (C) 2002 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "lf-list.h" +#include "lf-internals.h" + +typedef struct LfListNode +{ + void *data; + struct LfListNode *next; +} LfListNode; + +struct LfList +{ + LfListNode *head; +}; + +static LfListNode* +lf_list_node_alloc (void) +{ + return lf_new0 (LfListNode, 1); +} + +LfList* +lf_list_new (void) +{ + LfList *list; + + list = lf_new (LfList, 1); + list->head = NULL; + + return list; +} + +void +lf_list_free (LfList *list) +{ + LfListNode *node; + + node = list->head; + while (node != NULL) + { + LfListNode *next = node->next; + + lf_free (node); + + node = next; + } + + lf_free (list); +} + +void +lf_list_prepend (LfList *list, + void *data) +{ + if (list->head == NULL) + { + list->head = lf_list_node_alloc (); + list->head->data = data; + } + else + { + LfListNode *node; + + node = lf_list_node_alloc (); + node->data = data; + node->next = list->head; + list->head = node; + } +} + +void +lf_list_append (LfList *list, + void *data) +{ + if (list->head == NULL) + { + list->head = lf_list_node_alloc (); + list->head->data = data; + } + else + { + LfListNode *node; + + node = list->head; + while (node->next != NULL) + node = node->next; + + node->next = lf_list_node_alloc (); + node->next->data = data; + } +} + +void +lf_list_remove (LfList *list, + void *data) +{ + LfListNode *node; + LfListNode *prev; + + prev = NULL; + node = list->head; + while (node != NULL) + { + if (node->data == data) + { + if (prev) + prev->next = node->next; + else + list->head = node->next; + + lf_free (node); + + return; + } + + prev = node; + node = node->next; + } +} + +void +lf_list_foreach (LfList *list, + LfListForeachFunc func, + void *data) +{ + LfListNode *node; + + node = list->head; + while (node != NULL) + { + LfListNode *next = node->next; /* reentrancy safety */ + + if (!(* func) (node->data, data)) + return; + + node = next; + } +} + +lf_bool_t +lf_list_empty (LfList *list) +{ + return list->head == NULL; +} diff --git a/libsn/sn-list.h b/libsn/sn-list.h new file mode 100644 index 0000000..5a519bb --- /dev/null +++ b/libsn/sn-list.h @@ -0,0 +1,55 @@ +/* List abstraction used internally */ +/* + * Copyright (C) 2002 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + +#ifndef __LF_LIST_H__ +#define __LF_LIST_H__ + +#include <liblf/lf-util.h> + +LF_BEGIN_DECLS + +/* FIXME use lf_internal prefix for all this */ + +typedef struct LfList LfList; + +typedef lf_bool_t (* LfListForeachFunc) (void *value, void *data); + +LfList* lf_list_new (void); +void lf_list_free (LfList *list); +void lf_list_prepend (LfList *list, + void *data); +void lf_list_append (LfList *list, + void *data); +void lf_list_remove (LfList *list, + void *data); +void lf_list_foreach (LfList *list, + LfListForeachFunc func, + void *data); +lf_bool_t lf_list_empty (LfList *list); + +LF_END_DECLS + +#endif /* __LF_LIST_H__ */ diff --git a/libsn/sn-monitor.c b/libsn/sn-monitor.c new file mode 100644 index 0000000..52a6134 --- /dev/null +++ b/libsn/sn-monitor.c @@ -0,0 +1,1237 @@ +/* + * Copyright (C) 2002 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "lf-monitor.h" +#include "lf-internals.h" +#include "lf-xmessages.h" + +#define KDE_STARTUP_INFO_ATOM "_KDE_STARTUP_INFO" + +struct LfMonitorContext +{ + int refcount; + LfDisplay *display; + LfMonitorEventFunc event_func; + void *event_func_data; + LfFreeFunc free_data_func; + /* a context doesn't get events for sequences + * started prior to context creation + */ + int creation_serial; +}; + +struct LfMonitorEvent +{ + int refcount; + LfMonitorEventType type; + LfMonitorContext *context; + LfLaunchSequence *sequence; + Time timestamp; +}; + +struct LfLaunchSequence +{ + int refcount; + + char *id; + LfDisplay *display; + + /* launch_window is NULL for Xmessage-based launches */ + Window launch_window; + + char *name; + char *description; + + char *resource_class; + char *resource_name; + char *window_title; + + int workspace; + + char *binary_name; + char *hostname; + char *icon_name; + int pid; + + /* geometry */ + Window geometry_window; + int x, y, width, height; + + unsigned int geometry_set : 1; + unsigned int canceled : 1; + unsigned int completed : 1; + unsigned int supports_cancel : 1; + + int creation_serial; +}; + +static LfList *context_list = NULL; +static LfList *sequence_list = NULL; +static int next_sequence_serial = 0; + +static void xmessage_func (LfDisplay *display, + const char *message_type, + const char *message, + void *user_data); + +/** + * lf_monitor_context_new: + * @display: an #LfDisplay + * @event_func: function to call when an event is received + * @event_func_data: extra data to pass to @event_func + * @free_data_func: function to free @event_func_data when the context is freed + * + * Creates a new context for monitoring launch sequences. Normally + * only launch feedback indicator applications such as the window + * manager or task manager would use #LfMonitorContext. + * #LfLauncherContext and #LfLauncheeContext are more often used by + * applications. + * + * To detect launch sequence initiations, PropertyChangeMask must be + * selected on all root windows for a display. liblf does not do this + * for you because it's pretty likely to mess something up. So you + * have to do it yourself in programs that use #LfMonitorContext. + * + * Return value: a new #LfMonitorContext + **/ +LfMonitorContext* +lf_monitor_context_new (LfDisplay *display, + LfMonitorEventFunc event_func, + void *event_func_data, + LfFreeFunc free_data_func) +{ + LfMonitorContext *context; + + context = lf_new0 (LfMonitorContext, 1); + + context->refcount = 1; + context->event_func = event_func; + context->event_func_data = event_func_data; + context->free_data_func = free_data_func; + + context->display = display; + lf_display_ref (context->display); + + if (context_list == NULL) + context_list = lf_list_new (); + + if (lf_list_empty (context_list)) + lf_internal_add_xmessage_func (display, + KDE_STARTUP_INFO_ATOM, + xmessage_func, + NULL, NULL); + + lf_list_prepend (context_list, context); + + /* We get events for serials >= creation_serial */ + context->creation_serial = next_sequence_serial; + + return context; +} + +/** + * lf_monitor_context_ref: + * @context: an #LfMonitorContext + * + * Increments the reference count on @context. + * + **/ +void +lf_monitor_context_ref (LfMonitorContext *context) +{ + context->refcount += 1; +} + +/** + * lf_monitor_context_unref: + * @context: an #LfMonitorContext + * + * Decrements the reference count on @context and frees the + * context if the count reaches 0. + **/ +void +lf_monitor_context_unref (LfMonitorContext *context) +{ + context->refcount -= 1; + + if (context->refcount == 0) + { + lf_list_remove (context_list, context); + + if (lf_list_empty (context_list)) + lf_internal_remove_xmessage_func (context->display, + KDE_STARTUP_INFO_ATOM, + xmessage_func, + NULL); + + if (context->free_data_func) + (* context->free_data_func) (context->event_func_data); + + lf_display_unref (context->display); + lf_free (context); + } +} + +void +lf_monitor_event_ref (LfMonitorEvent *event) +{ + event->refcount += 1; +} + +void +lf_monitor_event_unref (LfMonitorEvent *event) +{ + event->refcount -= 1; + + if (event->refcount == 0) + { + if (event->context) + lf_monitor_context_unref (event->context); + if (event->sequence) + lf_launch_sequence_unref (event->sequence); + lf_free (event); + } +} + +LfMonitorEvent* +lf_monitor_event_copy (LfMonitorEvent *event) +{ + LfMonitorEvent *copy; + + copy = lf_new0 (LfMonitorEvent, 1); + + copy->refcount = 1; + + copy->type = event->type; + copy->context = event->context; + if (copy->context) + lf_monitor_context_ref (copy->context); + copy->sequence = event->sequence; + if (copy->sequence) + lf_launch_sequence_ref (copy->sequence); + + copy->timestamp = event->timestamp; + + return copy; +} + +LfMonitorEventType +lf_monitor_event_get_type (LfMonitorEvent *event) +{ + return event->type; +} + +LfLaunchSequence* +lf_monitor_event_get_launch_sequence (LfMonitorEvent *event) +{ + return event->sequence; +} + +LfMonitorContext* +lf_monitor_event_get_context (LfMonitorEvent *event) +{ + return event->context; +} + +Time +lf_monitor_event_get_time (LfMonitorEvent *event) +{ + return event->timestamp; +} + +static void +update_geometry (LfLaunchSequence *sequence) +{ + int *vals; + int n_vals; + + sequence->geometry_set = FALSE; + + vals = NULL; + n_vals = 0; + if (lf_internal_get_cardinal_list (sequence->display, + sequence->launch_window, + "_NET_LAUNCH_GEOMETRY", + &vals, &n_vals) && + n_vals == 4) + { + sequence->x = vals[0]; + sequence->y = vals[1]; + sequence->width = vals[2]; + sequence->height = vals[3]; + } + + lf_free (vals); +} + +static void +update_pid (LfLaunchSequence *sequence) +{ + int val; + + sequence->pid = -1; + + val = -1; + if (lf_internal_get_cardinal (sequence->display, + sequence->launch_window, + "_NET_LAUNCH_PID", + &val)) + sequence->pid = val; +} + +static LfLaunchSequence* +lf_launch_sequence_new (LfDisplay *display, + Window launch_window) +{ + LfLaunchSequence *sequence; + char *id; + int val; + + /* Select input, then get _NET_LAUNCH_ID, + * because we want to be sure we detect a BadWindow + * if it happens prior to selecting input, and + * getting _NET_LAUNCH_ID will bomb on BadWindow + */ + if (launch_window != None) /* launch_window may be NULL for xmessage sequence */ + { + lf_display_error_trap_push (display); + XSelectInput (lf_display_get_x_display (display), + launch_window, + PropertyChangeMask | StructureNotifyMask); + lf_display_error_trap_pop (display); + + id = NULL; + if (!lf_internal_get_string (display, launch_window, + "_NET_LAUNCH_ID", &id)) + { + return NULL; + } + } + + sequence = lf_new0 (LfLaunchSequence, 1); + + sequence->refcount = 1; + + sequence->creation_serial = next_sequence_serial; + ++next_sequence_serial; + + sequence->id = id; + sequence->launch_window = launch_window; + sequence->display = display; + lf_display_ref (display); + + sequence->workspace = -1; /* not set */ + sequence->pid = -1; + + /* Update stuff that can change over time */ + update_geometry (sequence); + update_pid (sequence); + + if (sequence->launch_window != None) + { + /* Grab all the stuff that can't be changed later */ + lf_internal_get_utf8_string (sequence->display, + sequence->launch_window, + "_NET_LAUNCH_NAME", + &sequence->name); + lf_internal_get_utf8_string (sequence->display, + sequence->launch_window, + "_NET_LAUNCH_DESCRIPTION", + &sequence->description); + lf_internal_get_string (sequence->display, + sequence->launch_window, + "_NET_LAUNCH_LEGACY_RESOURCE_CLASS", + &sequence->resource_class); + lf_internal_get_string (sequence->display, + sequence->launch_window, + "_NET_LAUNCH_LEGACY_RESOURCE_NAME", + &sequence->resource_name); + lf_internal_get_string (sequence->display, + sequence->launch_window, + "_NET_LAUNCH_LEGACY_NAME", + &sequence->window_title); + if (lf_internal_get_cardinal (sequence->display, + sequence->launch_window, + "_NET_LAUNCH_DESKTOP", + &val)) + sequence->workspace = val; + lf_internal_get_string (sequence->display, + sequence->launch_window, + "_NET_LAUNCH_BINARY_NAME", + &sequence->binary_name); + lf_internal_get_string (sequence->display, + sequence->launch_window, + "_NET_LAUNCH_HOSTNAME", + &sequence->hostname); + lf_internal_get_string (sequence->display, + sequence->launch_window, + "_NET_LAUNCH_ICON_NAME", + &sequence->icon_name); + lf_internal_get_window (sequence->display, + sequence->launch_window, + "_NET_LAUNCH_GEOMETRY_WINDOW", + &sequence->geometry_window); + + if (lf_internal_get_cardinal (sequence->display, + sequence->launch_window, + "_NET_LAUNCH_SUPPORTS_CANCEL", + &val)) + sequence->supports_cancel = val != 0; + } + + return sequence; +} + +void +lf_launch_sequence_ref (LfLaunchSequence *sequence) +{ + sequence->refcount += 1; +} + +void +lf_launch_sequence_unref (LfLaunchSequence *sequence) +{ + sequence->refcount -= 1; + + if (sequence->refcount == 0) + { + lf_free (sequence->id); + + lf_free (sequence->name); + lf_free (sequence->description); + lf_free (sequence->resource_class); + lf_free (sequence->resource_name); + lf_free (sequence->window_title); + lf_free (sequence->binary_name); + lf_free (sequence->icon_name); + lf_free (sequence->hostname); + + lf_display_unref (sequence->display); + lf_free (sequence); + } +} + +const char* +lf_launch_sequence_get_id (LfLaunchSequence *sequence) +{ + return sequence->id; +} + +Window +lf_launch_sequence_get_window (LfLaunchSequence *sequence) +{ + return sequence->launch_window; +} + +lf_bool_t +lf_launch_sequence_get_geometry (LfLaunchSequence *sequence, + int *x, + int *y, + int *width, + int *height) +{ + if (sequence->geometry_set) + { + *x = sequence->x; + *y = sequence->y; + *width = sequence->width; + *height = sequence->height; + return TRUE; + } + else + { + *x = 0; + *y = 0; + *width = 0; + *height = 0; + return FALSE; + } +} + +Window +lf_launch_sequence_get_geometry_window (LfLaunchSequence *sequence) +{ + return sequence->geometry_window; +} + +lf_bool_t +lf_launch_sequence_get_completed (LfLaunchSequence *sequence) +{ + return sequence->completed; +} + +lf_bool_t +lf_launch_sequence_get_canceled (LfLaunchSequence *sequence) +{ + return sequence->canceled; +} + +const char* +lf_launch_sequence_get_name (LfLaunchSequence *sequence) +{ + return sequence->name; +} + +const char* +lf_launch_sequence_get_description (LfLaunchSequence *sequence) +{ + return sequence->description; +} + +int +lf_launch_sequence_get_workspace (LfLaunchSequence *sequence) +{ + return sequence->workspace; +} + +const char* +lf_launch_sequence_get_legacy_resource_class (LfLaunchSequence *sequence) +{ + return sequence->resource_class; +} + +const char* +lf_launch_sequence_get_legacy_resource_name (LfLaunchSequence *sequence) +{ + return sequence->resource_name; +} + +const char* +lf_launch_sequence_get_legacy_window_title (LfLaunchSequence *sequence) +{ + return sequence->window_title; +} + +lf_bool_t +lf_launch_sequence_get_supports_cancel (LfLaunchSequence *sequence) +{ + return sequence->supports_cancel; +} + +int +lf_launch_sequence_get_pid (LfLaunchSequence *sequence) +{ + return sequence->pid; +} + +const char* +lf_launch_sequence_get_binary_name (LfLaunchSequence *sequence) +{ + return sequence->binary_name; +} + +const char* +lf_launch_sequence_get_hostname (LfLaunchSequence *sequence) +{ + return sequence->hostname; +} + +const char* +lf_launch_sequence_get_icon_name (LfLaunchSequence *sequence) +{ + return sequence->icon_name; +} + +void +lf_launch_sequence_cancel (LfLaunchSequence *sequence) +{ + if (sequence->supports_cancel) + lf_internal_set_cardinal (sequence->display, + sequence->launch_window, + "_NET_LAUNCH_CANCELED", + 0); +} + +static lf_bool_t +check_cardinal_exists (LfDisplay *display, + Window xwindow, + const char *property) +{ + int val; + + return lf_internal_get_cardinal (display, xwindow, property, + &val); +} + +typedef struct +{ + LfMonitorEvent *base_event; + LfList *events; +} CreateContextEventsData; + +static lf_bool_t +create_context_events_foreach (void *value, + void *data) +{ + /* Make a list of events holding a ref to the context they'll go to, + * for reentrancy robustness + */ + LfMonitorContext *context = value; + CreateContextEventsData *ced = data; + + /* Don't send events for launch sequences initiated before the + * context was created + */ + if (ced->base_event->sequence->creation_serial >= + context->creation_serial) + { + LfMonitorEvent *copy; + + copy = lf_monitor_event_copy (ced->base_event); + copy->context = context; + lf_monitor_context_ref (copy->context); + + lf_list_prepend (ced->events, copy); + } + + return TRUE; +} + +static lf_bool_t +dispatch_event_foreach (void *value, + void *data) +{ + LfMonitorEvent *event = value; + + /* Dispatch and free events */ + + if (event->context->event_func) + (* event->context->event_func) (event, + event->context->event_func_data); + + lf_monitor_event_unref (event); + + return TRUE; +} + +static lf_bool_t +filter_event (LfMonitorEvent *event) +{ + lf_bool_t retval; + + retval = FALSE; + + /* Filter out duplicate events and update flags */ + switch (event->type) + { + case LF_MONITOR_EVENT_CANCELED: + if (event->sequence->canceled) + { + retval = TRUE; + } + else + { + event->sequence->canceled = TRUE; + } + break; + case LF_MONITOR_EVENT_COMPLETED: + if (event->sequence->completed) + { + retval = TRUE; + } + else + { + event->sequence->completed = TRUE; + } + break; + + default: + break; + } + + return retval; +} + +typedef struct +{ + LfDisplay *display; + Window launch_window; + LfLaunchSequence *found; +} FindSequenceData; + +static lf_bool_t +find_sequence_foreach (void *value, + void *data) +{ + LfLaunchSequence *sequence = value; + FindSequenceData *fsd = data; + + if (sequence->launch_window == fsd->launch_window && + lf_display_get_x_display (sequence->display) == + lf_display_get_x_display (fsd->display)) + { + fsd->found = sequence; + return FALSE; + } + + return TRUE; +} + +static LfLaunchSequence* +find_sequence_for_window (LfDisplay *display, + Window event_window) +{ + FindSequenceData fsd; + + if (sequence_list == NULL) + return NULL; + + fsd.display = display; + fsd.launch_window = event_window; + fsd.found = NULL; + + lf_list_foreach (sequence_list, find_sequence_foreach, &fsd); + + return fsd.found; +} + +static LfLaunchSequence* +add_sequence (LfDisplay *display, + Window event_xwindow) +{ + LfLaunchSequence *sequence; + + sequence = + lf_launch_sequence_new (display, event_xwindow); + + if (sequence) + { + lf_launch_sequence_ref (sequence); /* ref held by sequence list */ + if (sequence_list == NULL) + sequence_list = lf_list_new (); + lf_list_prepend (sequence_list, sequence); + } + + return sequence; +} + +static void +remove_sequence (LfLaunchSequence *sequence) +{ + lf_list_remove (sequence_list, sequence); + lf_launch_sequence_unref (sequence); +} + +static void +dispatch_monitor_event (LfDisplay *display, + LfMonitorEvent *event, + Window event_xwindow) +{ + if (event->type == LF_MONITOR_EVENT_INITIATED) + { + if (event->sequence == NULL) + event->sequence = add_sequence (display, event_xwindow); + } + else if (event->sequence == NULL) + { + event->sequence = find_sequence_for_window (display, + event_xwindow); + if (event->sequence) + lf_launch_sequence_ref (event->sequence); /* ref held by event */ + } + + if (event->sequence != NULL) + { + switch (event->type) + { + case LF_MONITOR_EVENT_GEOMETRY_CHANGED: + update_geometry (event->sequence); + break; + + case LF_MONITOR_EVENT_PID_CHANGED: + update_pid (event->sequence); + break; + + default: + break; + } + } + + if (event->sequence != NULL && + !filter_event (event)) + { + CreateContextEventsData cced; + + cced.base_event = event; + cced.events = lf_list_new (); + + lf_list_foreach (context_list, create_context_events_foreach, + &cced); + + lf_list_foreach (cced.events, dispatch_event_foreach, NULL); + + /* values in the events list freed on dispatch */ + lf_list_free (cced.events); + + /* remove from sequence list */ + if (event->type == LF_MONITOR_EVENT_COMPLETED) + remove_sequence (event->sequence); + } +} + +lf_bool_t +lf_internal_monitor_process_event (LfDisplay *display, + XEvent *xevent) +{ + lf_bool_t retval; + LfMonitorEvent *event; + Window event_xwindow; + + if (context_list == NULL || + lf_list_empty (context_list)) + return FALSE; /* no one cares */ + + event_xwindow = None; + event = NULL; + retval = FALSE; + + switch (xevent->xany.type) + { + case PropertyNotify: + if (xevent->xproperty.atom == + lf_internal_atom_get (display, "_NET_LAUNCH_CANCELED")) + { + event_xwindow = xevent->xproperty.window; + + if (check_cardinal_exists (display, event_xwindow, + "_NET_LAUNCH_CANCELED")) + { + event = lf_new (LfMonitorEvent, 1); + + event->refcount = 1; + event->type = LF_MONITOR_EVENT_CANCELED; + event->timestamp = xevent->xproperty.time; + event->context = NULL; + event->sequence = NULL; + + retval = TRUE; + } + } + else if (xevent->xproperty.atom == + lf_internal_atom_get (display, "_NET_LAUNCH_COMPLETE")) + { + event_xwindow = xevent->xproperty.window; + + if (check_cardinal_exists (display, event_xwindow, + "_NET_LAUNCH_COMPLETE")) + { + event = lf_new (LfMonitorEvent, 1); + + event->refcount = 1; + event->type = LF_MONITOR_EVENT_COMPLETED; + event->timestamp = xevent->xproperty.time; + event->context = NULL; + event->sequence = NULL; + + retval = TRUE; + } + } + else if (xevent->xproperty.atom == + lf_internal_atom_get (display, "_NET_LAUNCH_GEOMETRY")) + { + event_xwindow = xevent->xproperty.window; + + event = lf_new (LfMonitorEvent, 1); + + event->refcount = 1; + event->type = LF_MONITOR_EVENT_GEOMETRY_CHANGED; + event->timestamp = xevent->xproperty.time; + event->context = NULL; + event->sequence = NULL; + + retval = TRUE; + } + else if (xevent->xproperty.atom == + lf_internal_atom_get (display, "_NET_LAUNCH_PID")) + { + event_xwindow = xevent->xproperty.window; + + event = lf_new (LfMonitorEvent, 1); + + event->refcount = 1; + event->type = LF_MONITOR_EVENT_PID_CHANGED; + event->timestamp = xevent->xproperty.time; + event->context = NULL; + event->sequence = NULL; + + retval = TRUE; + } + break; + + case ClientMessage: + if (xevent->xclient.message_type == + lf_internal_atom_get (display, + "_NET_LAUNCH_PULSE")) + { + event_xwindow = xevent->xclient.window; + + event = lf_new (LfMonitorEvent, 1); + + event->refcount = 1; + event->type = LF_MONITOR_EVENT_PULSE; + event->timestamp = CurrentTime; + event->context = NULL; + event->sequence = NULL; + + retval = TRUE; + } + else if (xevent->xclient.message_type == + lf_internal_atom_get (display, + "_NET_LAUNCH_INITIATE")) + { + LfLaunchSequence *sequence; + + /* Don't be fooled by duplicate initiate messages - + * check that the sequence doesn't exist yet + */ + sequence = find_sequence_for_window (display, + xevent->xclient.window); + + if (sequence == NULL) + { + event_xwindow = xevent->xclient.window; + + event = lf_new (LfMonitorEvent, 1); + + event->refcount = 1; + event->type = LF_MONITOR_EVENT_INITIATED; + event->timestamp = xevent->xclient.data.l[0]; + event->context = NULL; + event->sequence = NULL; + } + + retval = TRUE; + } + break; + + case DestroyNotify: + { + LfLaunchSequence *sequence; + + sequence = find_sequence_for_window (display, + xevent->xdestroywindow.window); + + if (sequence != NULL) + { + event_xwindow = xevent->xdestroywindow.window; + + event = lf_new (LfMonitorEvent, 1); + + event->refcount = 1; + event->type = LF_MONITOR_EVENT_COMPLETED; + event->timestamp = CurrentTime; + event->context = NULL; + event->sequence = sequence; + lf_launch_sequence_ref (sequence); + } + } + break; + + default: + break; + } + + if (event != NULL) + { + dispatch_monitor_event (display, event, event_xwindow); + + lf_monitor_event_unref (event); + } + + return retval; +} + +typedef struct +{ + LfDisplay *display; + const char *id; + LfLaunchSequence *found; +} FindSequenceByIdData; + +static lf_bool_t +find_sequence_by_id_foreach (void *value, + void *data) +{ + LfLaunchSequence *sequence = value; + FindSequenceByIdData *fsd = data; + + if (strcmp (sequence->id, fsd->id) == 0 && + lf_display_get_x_display (sequence->display) == + lf_display_get_x_display (fsd->display)) + { + fsd->found = sequence; + return FALSE; + } + + return TRUE; +} + +static LfLaunchSequence* +find_sequence_for_id (LfDisplay *display, + const char *id) +{ + FindSequenceByIdData fsd; + + if (sequence_list == NULL) + return NULL; + + fsd.display = display; + fsd.id = id; + fsd.found = NULL; + + lf_list_foreach (sequence_list, find_sequence_by_id_foreach, &fsd); + + return fsd.found; +} + +static lf_bool_t +do_xmessage_event_foreach (void *value, + void *data) +{ + LfMonitorEvent *event = value; + LfDisplay *display = data; + + dispatch_monitor_event (display, event, None); + + return TRUE; +} + +static lf_bool_t +unref_event_foreach (void *value, + void *data) +{ + lf_monitor_event_unref (value); + return TRUE; +} + +static void +xmessage_func (LfDisplay *display, + const char *message_type, + const char *message, + void *user_data) +{ + /* assert (strcmp (message_type, KDE_STARTUP_INFO_ATOM) == 0); */ + char *prefix; + char **names; + char **values; + int i; + const char *launch_id; + LfLaunchSequence *sequence; + LfList *events; + + prefix = NULL; + names = NULL; + values = NULL; + if (!lf_internal_unserialize_message (message, &prefix, &names, &values)) + return; + + launch_id = NULL; + i = 0; + while (names[i]) + { + if (strcmp (names[i], "ID") == 0) + { + launch_id = values[i]; + break; + } + ++i; + } + + events = lf_list_new (); + + if (launch_id == NULL) + goto out; + + sequence = find_sequence_for_id (display, launch_id); + + if (strcmp (prefix, "new") == 0) + { + if (sequence == NULL) + { + LfMonitorEvent *event; + + sequence = add_sequence (display, None); + if (sequence == NULL) + goto out; + + sequence->id = lf_internal_strdup (launch_id); + + event = lf_new (LfMonitorEvent, 1); + + event->refcount = 1; + event->type = LF_MONITOR_EVENT_INITIATED; + event->timestamp = CurrentTime; + event->context = NULL; + event->sequence = sequence; /* ref from add_sequence goes here */ + + lf_list_append (events, event); + } + } + + if (sequence == NULL) + goto out; + + if (strcmp (prefix, "change") == 0 || + strcmp (prefix, "new") == 0) + { + lf_bool_t random_stuff_changed = FALSE; + lf_bool_t pid_changed = FALSE; + lf_bool_t workspace_changed = FALSE; + + i = 0; + while (names[i]) + { + if (strcmp (names[i], "BIN") == 0) + { + if (sequence->binary_name == NULL) + { + sequence->binary_name = lf_internal_strdup (values[i]); + random_stuff_changed = TRUE; + } + } + else if (strcmp (names[i], "NAME") == 0) + { + if (sequence->name == NULL) + { + sequence->name = lf_internal_strdup (values[i]); + random_stuff_changed = TRUE; + } + } + else if (strcmp (names[i], "ICON") == 0) + { + if (sequence->icon_name == NULL) + { + sequence->icon_name = lf_internal_strdup (values[i]); + random_stuff_changed = TRUE; + } + } + else if (strcmp (names[i], "DESKTOP") == 0) + { + int workspace; + + workspace = lf_internal_string_to_ulong (values[i]); + + sequence->workspace = workspace; + workspace_changed = TRUE; + } + else if (strcmp (names[i], "WMCLASS") == 0) + { + if (sequence->resource_class == NULL) + { + sequence->resource_class = lf_internal_strdup (values[i]); + random_stuff_changed = TRUE; + } + } + else if (strcmp (names[i], "PID") == 0) + { + int pid; + + pid = lf_internal_string_to_ulong (values[i]); + + if (pid > 0) + { + sequence->pid = pid; + + pid_changed = TRUE; + } + } + else if (strcmp (names[i], "HOSTNAME") == 0) + { + if (sequence->hostname == NULL) + { + sequence->hostname = lf_internal_strdup (values[i]); + random_stuff_changed = TRUE; + } + } + + ++i; + } + + if (pid_changed) + { + LfMonitorEvent *event; + + event = lf_new (LfMonitorEvent, 1); + + event->refcount = 1; + event->type = LF_MONITOR_EVENT_PID_CHANGED; + event->timestamp = CurrentTime; + event->context = NULL; + event->sequence = sequence; + lf_launch_sequence_ref (sequence); + + lf_list_append (events, event); + } + + if (workspace_changed) + { + LfMonitorEvent *event; + + event = lf_new (LfMonitorEvent, 1); + + event->refcount = 1; + event->type = LF_MONITOR_EVENT_WORKSPACE_CHANGED; + event->timestamp = CurrentTime; + event->context = NULL; + event->sequence = sequence; + lf_launch_sequence_ref (sequence); + + lf_list_append (events, event); + } + + if (random_stuff_changed) + { + /* FIXME */ + } + } + else if (strcmp (prefix, "remove") == 0) + { + LfMonitorEvent *event; + + event = lf_new (LfMonitorEvent, 1); + + event->refcount = 1; + event->type = LF_MONITOR_EVENT_COMPLETED; + event->timestamp = CurrentTime; + event->context = NULL; + event->sequence = sequence; + lf_launch_sequence_ref (sequence); + + lf_list_append (events, event); + } + + lf_list_foreach (events, + do_xmessage_event_foreach, + display); + + out: + if (events != NULL) + { + lf_list_foreach (events, unref_event_foreach, NULL); + lf_list_free (events); + } + + lf_free (prefix); + lf_internal_strfreev (names); + lf_internal_strfreev (values); +} diff --git a/libsn/sn-monitor.h b/libsn/sn-monitor.h new file mode 100644 index 0000000..e9efdd9 --- /dev/null +++ b/libsn/sn-monitor.h @@ -0,0 +1,97 @@ +/* Monitor API - if you are a program that monitors launch sequences */ +/* + * Copyright (C) 2002 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + +#ifndef __LF_MONITOR_H__ +#define __LF_MONITOR_H__ + +#include <liblf/lf-common.h> + +LF_BEGIN_DECLS + +typedef struct LfMonitorContext LfMonitorContext; +typedef struct LfMonitorEvent LfMonitorEvent; +typedef struct LfLaunchSequence LfLaunchSequence; + +typedef void (* LfMonitorEventFunc) (LfMonitorEvent *event, + void *user_data); + +typedef enum +{ + LF_MONITOR_EVENT_INITIATED, + LF_MONITOR_EVENT_COMPLETED, + LF_MONITOR_EVENT_CANCELED, + LF_MONITOR_EVENT_PULSE, + LF_MONITOR_EVENT_GEOMETRY_CHANGED, + LF_MONITOR_EVENT_PID_CHANGED, + /* only allowed with xmessages protocol */ + LF_MONITOR_EVENT_WORKSPACE_CHANGED +} LfMonitorEventType; + +LfMonitorContext* lf_monitor_context_new (LfDisplay *display, + LfMonitorEventFunc event_func, + void *event_func_data, + LfFreeFunc free_data_func); +void lf_monitor_context_ref (LfMonitorContext *context); +void lf_monitor_context_unref (LfMonitorContext *context); + + +void lf_monitor_event_ref (LfMonitorEvent *event); +void lf_monitor_event_unref (LfMonitorEvent *event); +LfMonitorEvent* lf_monitor_event_copy (LfMonitorEvent *event); +LfMonitorEventType lf_monitor_event_get_type (LfMonitorEvent *event); +LfLaunchSequence* lf_monitor_event_get_launch_sequence (LfMonitorEvent *event); +LfMonitorContext* lf_monitor_event_get_context (LfMonitorEvent *event); +Time lf_monitor_event_get_time (LfMonitorEvent *event); + +void lf_launch_sequence_ref (LfLaunchSequence *sequence); +void lf_launch_sequence_unref (LfLaunchSequence *sequence); +const char* lf_launch_sequence_get_id (LfLaunchSequence *sequence); +Window lf_launch_sequence_get_window (LfLaunchSequence *sequence); +lf_bool_t lf_launch_sequence_get_geometry (LfLaunchSequence *sequence, + int *x, + int *y, + int *width, + int *height); +Window lf_launch_sequence_get_geometry_window (LfLaunchSequence *sequence); +lf_bool_t lf_launch_sequence_get_completed (LfLaunchSequence *sequence); +lf_bool_t lf_launch_sequence_get_canceled (LfLaunchSequence *sequence); +const char* lf_launch_sequence_get_name (LfLaunchSequence *sequence); +const char* lf_launch_sequence_get_description (LfLaunchSequence *sequence); +int lf_launch_sequence_get_workspace (LfLaunchSequence *sequence); +const char* lf_launch_sequence_get_legacy_resource_class (LfLaunchSequence *sequence); +const char* lf_launch_sequence_get_legacy_resource_name (LfLaunchSequence *sequence); +const char* lf_launch_sequence_get_legacy_window_title (LfLaunchSequence *sequence); +lf_bool_t lf_launch_sequence_get_supports_cancel (LfLaunchSequence *sequence); +int lf_launch_sequence_get_pid (LfLaunchSequence *sequence); +const char* lf_launch_sequence_get_binary_name (LfLaunchSequence *sequence); +const char* lf_launch_sequence_get_hostname (LfLaunchSequence *sequence); +const char* lf_launch_sequence_get_icon_name (LfLaunchSequence *sequence); + +void lf_launch_sequence_cancel (LfLaunchSequence *sequence); + +LF_END_DECLS + +#endif /* __LF_MONITOR_H__ */ diff --git a/libsn/sn-util.c b/libsn/sn-util.c new file mode 100644 index 0000000..7a07b26 --- /dev/null +++ b/libsn/sn-util.c @@ -0,0 +1,310 @@ +/* This code derived from GLib, Copyright (C) 1997-2002 + * by the GLib team. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <config.h> +#include "lf-util.h" +#include "lf-internals.h" + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> + +#ifndef REALLOC_0_WORKS +static void* +standard_realloc (void* mem, + lf_size_t n_bytes) +{ + if (!mem) + return malloc (n_bytes); + else + return realloc (mem, n_bytes); +} +#endif /* !REALLOC_0_WORKS */ + +#ifdef SANE_MALLOC_PROTOS +# define standard_malloc malloc +# ifdef REALLOC_0_WORKS +# define standard_realloc realloc +# endif /* REALLOC_0_WORKS */ +# define standard_free free +# define standard_calloc calloc +# define standard_try_malloc malloc +# define standard_try_realloc realloc +#else /* !SANE_MALLOC_PROTOS */ +static void* +standard_malloc (lf_size_t n_bytes) +{ + return malloc (n_bytes); +} +# ifdef REALLOC_0_WORKS +static void* +standard_realloc (void* mem, + lf_size_t n_bytes) +{ + return realloc (mem, n_bytes); +} +# endif /* REALLOC_0_WORKS */ +static void +standard_free (void* mem) +{ + free (mem); +} +static void* +standard_calloc (lf_size_t n_blocks, + lf_size_t n_bytes) +{ + return calloc (n_blocks, n_bytes); +} +#define standard_try_malloc standard_malloc +#define standard_try_realloc standard_realloc +#endif /* !SANE_MALLOC_PROTOS */ + + +/* --- variables --- */ +static LfMemVTable lf_mem_vtable = { + standard_malloc, + standard_realloc, + standard_free, + standard_calloc, + standard_try_malloc, + standard_try_realloc, + NULL, + NULL +}; + + +/* --- functions --- */ +void* +lf_malloc (lf_size_t n_bytes) +{ + if (n_bytes) + { + void* mem; + + mem = lf_mem_vtable.malloc (n_bytes); + if (mem) + return mem; + + fprintf (stderr, + "liblf: failed to allocate %lu bytes", + (unsigned long) n_bytes); + } + + return NULL; +} + +void* +lf_malloc0 (lf_size_t n_bytes) +{ + if (n_bytes) + { + void* mem; + + mem = lf_mem_vtable.calloc (1, n_bytes); + if (mem) + return mem; + + fprintf (stderr, + "liblf: failed to allocate %lu bytes", + (unsigned long) n_bytes); + } + + return NULL; +} + +void* +lf_realloc (void *mem, + lf_size_t n_bytes) +{ + if (n_bytes) + { + mem = lf_mem_vtable.realloc (mem, n_bytes); + if (mem) + return mem; + + fprintf (stderr, + "liblf: failed to allocate %lu bytes", + (unsigned long) n_bytes); + } + + if (mem) + lf_mem_vtable.free (mem); + + return NULL; +} + +void +lf_free (void* mem) +{ + if (mem) + lf_mem_vtable.free (mem); +} + +void* +lf_try_malloc (lf_size_t n_bytes) +{ + if (n_bytes) + return lf_mem_vtable.try_malloc (n_bytes); + else + return NULL; +} + +void* +lf_try_realloc (void *mem, + lf_size_t n_bytes) +{ + if (n_bytes) + return lf_mem_vtable.try_realloc (mem, n_bytes); + + if (mem) + lf_mem_vtable.free (mem); + + return NULL; +} + +static void* +fallback_calloc (lf_size_t n_blocks, + lf_size_t n_block_bytes) +{ + lf_size_t l = n_blocks * n_block_bytes; + void* mem = lf_mem_vtable.malloc (l); + + if (mem) + memset (mem, 0, l); + + return mem; +} + +static lf_bool_t vtable_set = FALSE; + +lf_bool_t +lf_mem_is_system_malloc (void) +{ + return !vtable_set; +} + +void +lf_mem_set_vtable (LfMemVTable *vtable) +{ + if (!vtable_set) + { + vtable_set = TRUE; + if (vtable->malloc && vtable->realloc && vtable->free) + { + lf_mem_vtable.malloc = vtable->malloc; + lf_mem_vtable.realloc = vtable->realloc; + lf_mem_vtable.free = vtable->free; + lf_mem_vtable.calloc = vtable->calloc ? vtable->calloc : fallback_calloc; + lf_mem_vtable.try_malloc = vtable->try_malloc ? vtable->try_malloc : lf_mem_vtable.malloc; + lf_mem_vtable.try_realloc = vtable->try_realloc ? vtable->try_realloc : lf_mem_vtable.realloc; + } + else + { + fprintf (stderr, + "liblf: memory allocation vtable lacks one of malloc(), realloc() or free()"); + } + } + else + { + fprintf (stderr, + "liblf: memory allocation vtable can only be set once at startup"); + } +} + +static LfUtf8ValidateFunc utf8_validator = NULL; + +void +lf_set_utf8_validator (LfUtf8ValidateFunc validate_func) +{ + utf8_validator = validate_func; +} + +lf_bool_t +lf_internal_utf8_validate (const char *str, + int max_len) +{ + if (utf8_validator) + { + if (max_len < 0) + max_len = strlen (str); + return (* utf8_validator) (str, max_len); + } + else + return TRUE; +} + +char* +lf_internal_strdup (const char *str) +{ + char *s; + + s = lf_malloc (strlen (str) + 1); + strcpy (s, str); + + return s; +} + +char* +lf_internal_strndup (const char *str, + int n) +{ + char *new_str; + + if (str) + { + new_str = lf_new (char, n + 1); + strncpy (new_str, str, n); + new_str[n] = '\0'; + } + else + new_str = NULL; + + return new_str; +} + +void +lf_internal_strfreev (char **strings) +{ + int i; + + if (strings == NULL) + return; + + i = 0; + while (strings[i]) + { + lf_free (strings[i]); + ++i; + } + lf_free (strings); +} + +unsigned long +lf_internal_string_to_ulong (const char* str) +{ + unsigned long retval; + char *end; + errno = 0; + retval = strtoul (str, &end, 0); + if (end == str || errno != 0) + retval = 0; + + return retval; +} diff --git a/libsn/sn-util.h b/libsn/sn-util.h new file mode 100644 index 0000000..4da3c1e --- /dev/null +++ b/libsn/sn-util.h @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2002 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + +#ifndef __LF_UTIL_H__ +#define __LF_UTIL_H__ + +/* Guard C code in headers, while including them from C++ */ +#ifdef __cplusplus +# define LF_BEGIN_DECLS extern "C" { +# define LF_END_DECLS } +#else +# define LF_BEGIN_DECLS +# define LF_END_DECLS +#endif + +LF_BEGIN_DECLS + +typedef unsigned long lf_size_t; +typedef int lf_bool_t; + +/* Padding in vtables */ +typedef void (* LfPaddingFunc) (void); +/* Free data */ +typedef void (* LfFreeFunc) (void *data); + +void* lf_malloc (lf_size_t n_bytes); +void* lf_malloc0 (lf_size_t n_bytes); +void* lf_realloc (void *mem, + lf_size_t n_bytes); +void lf_free (void *mem); +void* lf_try_malloc (lf_size_t n_bytes); +void* lf_try_realloc (void *mem, + lf_size_t n_bytes); + +/* Convenience memory allocators + */ +#define lf_new(struct_type, n_structs) \ + ((struct_type *) lf_malloc (((lf_size_t) sizeof (struct_type)) * ((lf_size_t) (n_structs)))) +#define lf_new0(struct_type, n_structs) \ + ((struct_type *) lf_malloc0 (((lf_size_t) sizeof (struct_type)) * ((lf_size_t) (n_structs)))) +#define lf_renew(struct_type, mem, n_structs) \ + ((struct_type *) lf_realloc ((mem), ((lf_size_t) sizeof (struct_type)) * ((lf_size_t) (n_structs)))) + + +/* Memory allocation virtualization, so you can override memory + * allocation behavior. lf_mem_set_vtable() has to be the very first + * liblf function called if being used + */ +typedef struct +{ + void* (*malloc) (lf_size_t n_bytes); + void* (*realloc) (void *mem, + lf_size_t n_bytes); + void (*free) (void *mem); + /* optional */ + void* (*calloc) (lf_size_t n_blocks, + lf_size_t n_block_bytes); + void* (*try_malloc) (lf_size_t n_bytes); + void* (*try_realloc) (void *mem, + lf_size_t n_bytes); + LfPaddingFunc padding1; + LfPaddingFunc padding2; +} LfMemVTable; + +void lf_mem_set_vtable (LfMemVTable *vtable); +lf_bool_t lf_mem_is_system_malloc (void); + +typedef lf_bool_t (* LfUtf8ValidateFunc) (const char *str, + int max_len); + +void lf_set_utf8_validator (LfUtf8ValidateFunc validate_func); + +LF_END_DECLS + +#endif /* __LF_UTIL_H__ */ diff --git a/libsn/sn-xmessages.c b/libsn/sn-xmessages.c new file mode 100644 index 0000000..b195b49 --- /dev/null +++ b/libsn/sn-xmessages.c @@ -0,0 +1,836 @@ +/* + * Copyright (C) 2002 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "lf-xmessages.h" +#include "lf-list.h" +#include "lf-internals.h" + +typedef struct +{ + Display *xdisplay; + Atom type_atom; + char *message_type; + LfXmessageFunc func; + void *func_data; + LfFreeFunc free_data_func; +} LfXmessageHandler; + +typedef struct +{ + Atom type_atom; + Window xwindow; + char *message; + int allocated; +} LfXmessage; + +static LfList *xmessage_funcs = NULL; +static LfList *pending_messages = NULL; + +void +lf_internal_add_xmessage_func (LfDisplay *display, + const char *message_type, + LfXmessageFunc func, + void *func_data, + LfFreeFunc free_data_func) +{ + LfXmessageHandler *handler; + + if (xmessage_funcs == NULL) + xmessage_funcs = lf_list_new (); + + handler = lf_new0 (LfXmessageHandler, 1); + + handler->xdisplay = lf_display_get_x_display (display); + handler->type_atom = lf_internal_atom_get (display, message_type); + handler->message_type = lf_internal_strdup (message_type); + handler->func= func; + handler->func_data = func_data; + handler->free_data_func = free_data_func; + + lf_list_prepend (xmessage_funcs, handler); +} + +typedef struct +{ + const char *message_type; + LfXmessageFunc func; + void *func_data; + LfXmessageHandler *handler; +} FindHandlerData; + +static lf_bool_t +find_handler_foreach (void *value, + void *data) +{ + FindHandlerData *fhd = data; + LfXmessageHandler *handler = value; + + if (handler->func == fhd->func && + handler->func_data == fhd->func_data && + strcmp (fhd->message_type, handler->message_type) == 0) + { + fhd->handler = handler; + return FALSE; + } + + return TRUE; +} + +void +lf_internal_remove_xmessage_func (LfDisplay *display, + const char *message_type, + LfXmessageFunc func, + void *func_data) +{ + FindHandlerData fhd; + + fhd.func = func; + fhd.func_data = func_data; + fhd.handler = NULL; + + if (xmessage_funcs != NULL) + lf_list_foreach (xmessage_funcs, find_handler_foreach, &fhd); + + if (fhd.handler != NULL) + { + lf_list_remove (xmessage_funcs, fhd.handler); + + lf_free (fhd.handler->message_type); + + if (fhd.handler->free_data_func) + (* fhd.handler->free_data_func) (fhd.handler->func_data); + + lf_free (fhd.handler); + } +} + +void +lf_internal_broadcast_xmessage (LfDisplay *display, + const char *message_type, + const char *message) +{ + Atom type_atom; + Window xwindow; + Display *xdisplay; + + if (!lf_internal_utf8_validate (message, -1)) + { + fprintf (stderr, + "Attempted to send non-UTF-8 X message: %s\n", + message); + return; + } + + xdisplay = lf_display_get_x_display (display); + + { + XSetWindowAttributes attrs; + + attrs.override_redirect = True; + attrs.event_mask = PropertyChangeMask | StructureNotifyMask; + + xwindow = + XCreateWindow (xdisplay, + RootWindow (xdisplay, 0), + -100, -100, 1, 1, + 0, + CopyFromParent, + CopyFromParent, + CopyFromParent, + CWOverrideRedirect | CWEventMask, + &attrs); + } + + type_atom = lf_internal_atom_get (display, message_type); + + { + XEvent xevent; + const char *src; + const char *src_end; + char *dest; + char *dest_end; + + xevent.xclient.type = ClientMessage; + xevent.xclient.message_type = type_atom; + xevent.xclient.display = xdisplay; + xevent.xclient.window = xwindow; + xevent.xclient.format = 8; + + src = message; + src_end = message + strlen (message) + 1; /* +1 to include nul byte */ + + while (src != src_end) + { + dest = &xevent.xclient.data.b[0]; + dest_end = dest + 20; + + while (dest != dest_end && + src != src_end) + { + *dest = *src; + ++dest; + ++src; + } + + lf_internal_send_event_all_screens (display, PropertyChangeMask, + &xevent); + } + } + + XDestroyWindow (xdisplay, xwindow); + XFlush (xdisplay); +} + +typedef struct +{ + Display *xdisplay; + Atom atom; + lf_bool_t found_handler; +} HandlerForAtomData; + +static lf_bool_t +handler_for_atom_foreach (void *value, + void *data) +{ + LfXmessageHandler *handler = value; + HandlerForAtomData *hfad = data; + + if (handler->xdisplay == hfad->xdisplay && + handler->type_atom == hfad->atom) + { + hfad->found_handler = TRUE; + return FALSE; + } + else + return TRUE; +} + +static lf_bool_t +some_handler_handles_event (LfDisplay *display, + XEvent *xevent) +{ + HandlerForAtomData hfad; + + hfad.atom = xevent->xclient.message_type; + hfad.xdisplay = lf_display_get_x_display (display); + hfad.found_handler = FALSE; + + if (xmessage_funcs) + lf_list_foreach (xmessage_funcs, + handler_for_atom_foreach, + &hfad); + + return hfad.found_handler; +} + +typedef struct +{ + Display *xdisplay; + XEvent *xevent; + LfXmessage *message; +} FindMessageData; + +static lf_bool_t +find_message_foreach (void *value, + void *data) +{ + LfXmessage *message = value; + FindMessageData *fmd = data; + + if (fmd->xevent->xclient.window == + message->xwindow && + fmd->xevent->xclient.message_type == + message->type_atom) + { + fmd->message = message; + return FALSE; + } + + return TRUE; +} + +static LfXmessage* +add_event_to_messages (LfDisplay *display, + XEvent *xevent) +{ + FindMessageData fmd; + LfXmessage *message; + const char *src; + const char *src_end; + char *dest; + lf_bool_t completed; + + /* We don't want screwy situations to end up causing us to allocate + * infinite memory. Cap the length of a message. + */ +#define MAX_MESSAGE_LENGTH 4096 + + fmd.xdisplay = lf_display_get_x_display (display); + fmd.xevent = xevent; + fmd.message = NULL; + + if (pending_messages) + lf_list_foreach (pending_messages, find_message_foreach, &fmd); + + message = fmd.message; + + if (message == NULL) + { + /* Create a new message */ + message = lf_new0 (LfXmessage, 1); + + message->type_atom = xevent->xclient.message_type; + message->xwindow = xevent->xclient.window; + message->message = NULL; + message->allocated = 0; + + if (pending_messages == NULL) + pending_messages = lf_list_new (); + + lf_list_prepend (pending_messages, message); + } + + if (message->allocated > MAX_MESSAGE_LENGTH) + { + /* This message is some kind of crap - just dump it. */ + lf_free (message->message); + lf_list_remove (pending_messages, message); + lf_free (message); + return NULL; + } + + src = &xevent->xclient.data.b[0]; + src_end = src + 20; + + message->message = lf_realloc (message->message, + message->allocated + 20); + dest = message->message + message->allocated; + message->allocated += 20; + + completed = FALSE; + + /* Copy bytes, be sure we get nul byte also */ + while (src != src_end) + { + *dest = *src; + + if (*src == '\0') + { + completed = TRUE; + break; + } + + ++dest; + ++src; + } + + if (completed) + { + /* Pull message out of the pending queue and return it */ + lf_list_remove (pending_messages, message); + return message; + } + else + return NULL; +} + +typedef struct +{ + LfDisplay *display; + LfXmessage *message; +} MessageDispatchData; + +static lf_bool_t +dispatch_message_foreach (void *value, + void *data) +{ + LfXmessageHandler *handler = value; + MessageDispatchData *mdd = data; + + (* handler->func) (mdd->display, + handler->message_type, + mdd->message->message, + handler->func_data); + + return TRUE; +} + +lf_bool_t +lf_internal_xmessage_process_event (LfDisplay *display, + XEvent *xevent) +{ + lf_bool_t retval; + LfXmessage *message; + + retval = FALSE; + message = NULL; + + switch (xevent->xany.type) + { + case ClientMessage: + if (some_handler_handles_event (display, xevent)) + { + retval = TRUE; + + message = add_event_to_messages (display, xevent); + } + break; + } + + if (message != NULL) + { + /* We need to dispatch and free this message; ignore + * messages containing invalid UTF-8 + */ + + if (lf_internal_utf8_validate (message->message, -1)) + { + MessageDispatchData mdd; + + mdd.display = display; + mdd.message = message; + + /* We could stand to be more reentrant here; it will + * barf if you add/remove a handler from inside the + * dispatch + */ + if (xmessage_funcs != NULL) + lf_list_foreach (xmessage_funcs, + dispatch_message_foreach, + &mdd); + } + + lf_free (message->message); + lf_free (message); + } + + return retval; +} + +static void +append_to_string (char **append_to, + int *current_len, + const char *append) +{ + int len; + char *end; + + len = strlen (append); + + *append_to = lf_realloc (*append_to, *current_len + len + 1); + + end = *append_to + *current_len; + strcpy (end, append); + *current_len = *current_len + len; +} + +static void +append_to_string_escaped (char **append_to, + int *current_len, + const char *append) +{ + char *escaped; + int len; + char buf[2]; + const char *p; + + buf[1] = '\0'; + len = 0; + escaped = NULL; + + /* We are the most inefficient algorithm ever! woot! */ + /* really need GString here */ + p = append; + while (*p) + { + if (*p == '\\' || *p == '"' || *p == ' ') + { + buf[0] = '\\'; + append_to_string (&escaped, &len, buf); + } + buf[0] = *p; + append_to_string (&escaped, &len, buf); + + ++p; + } + + append_to_string (append_to, current_len, escaped); + + lf_free (escaped); +} + +char* +lf_internal_serialize_message (const char *prefix, + const char **property_names, + const char **property_values) +{ + int len; + char *retval; + int i; + + /* GLib would simplify this a lot... */ + len = 0; + retval = NULL; + + append_to_string (&retval, &len, prefix); + append_to_string (&retval, &len, ": "); + + i = 0; + while (property_names[i]) + { + append_to_string (&retval, &len, property_names[i]); + append_to_string (&retval, &len, "="); + append_to_string_escaped (&retval, &len, property_values[i]); + + ++i; + } + + return retval; +} + +static void +append_string_to_list (char ***list, + const char *append) +{ + if (*list == NULL) + { + *list = lf_new0 (char*, 2); + (*list)[0] = lf_internal_strdup (append); + } + else + { + int i; + + i = 0; + while ((*list)[i] != NULL) + ++i; + + *list = lf_renew (char*, *list, i + 2); + (*list)[i] = lf_internal_strdup (append); + (*list)[i+1] = NULL; + } +} + +static char* +parse_prefix_up_to (const char *str, + int up_to, + const char **end) +{ + char *prefix; + const char *p; + int len; + + prefix = NULL; + *end = NULL; + + p = str; + while (*p && *p != up_to) + ++p; + + if (*p == '\0') + return NULL; + + len = p - str; + prefix = lf_internal_strndup (str, len); + + *end = str + len; + + return prefix; +} + + +/* Single quotes preserve the literal string exactly. escape + * sequences are not allowed; not even \' - if you want a ' + * in the quoted text, you have to do something like 'foo'\''bar' + * + * Double quotes allow $ ` " \ and newline to be escaped with backslash. + * Otherwise double quotes preserve things literally. + * + * (This is overkill for X messages, copied from GLib shell code, + * copyright Red Hat Inc. also) + */ + +static lf_bool_t +unquote_string_inplace (char *str, + char **end) +{ + char* dest; + char* s; + char quote_char; + + dest = s = str; + + quote_char = *s; + + if (!(*s == '"' || *s == '\'')) + { + /* doesn't begin with quotation mark */ + *end = str; + return FALSE; + } + + /* Skip the initial quote mark */ + ++s; + + if (quote_char == '"') + { + while (*s) + { + /* g_assert(s > dest); */ /* loop invariant */ + + switch (*s) + { + case '"': + /* End of the string, return now */ + *dest = '\0'; + ++s; + *end = s; + return TRUE; + break; + + case '\\': + /* Possible escaped quote or \ */ + ++s; + switch (*s) + { + case '"': + case '\\': + case '`': + case '$': + case '\n': + *dest = *s; + ++s; + ++dest; + break; + + default: + /* not an escaped char */ + *dest = '\\'; + ++dest; + /* ++s already done. */ + break; + } + break; + + default: + *dest = *s; + ++dest; + ++s; + break; + } + + /* g_assert(s > dest);*/ /* loop invariant */ + } + } + else + { + while (*s) + { + /* g_assert(s > dest); */ /* loop invariant */ + + if (*s == '\'') + { + /* End of the string, return now */ + *dest = '\0'; + ++s; + *end = s; + return TRUE; + } + else + { + *dest = *s; + ++dest; + ++s; + } + + /* g_assert(s > dest); */ /* loop invariant */ + } + } + + /* If we reach here this means the close quote was never encountered */ + + *dest = '\0'; + *end = s; + return FALSE; +} + +static lf_bool_t +unescape_string_inplace (char *str, + char **end) +{ + char* dest; + char* s; + lf_bool_t escaped; + + dest = s = str; + escaped = FALSE; + + while (*s) + { + if (escaped) + { + escaped = FALSE; + + *dest = *s; + ++dest; + } + else + { + if (*s == ' ') + break; + else if (*s == '\\') + escaped = TRUE; + else + { + *dest = *s; + ++dest; + } + } + + ++s; + } + + *dest = '\0'; + *end = s; + + return TRUE; +} + +static lf_bool_t +parse_property (const char *str, + char **name_p, + char **val_p, + const char **end_p) +{ + char *val; + char *name; + char *copy; + char *p; + + *end_p = NULL; + + copy = lf_internal_strdup (str); + p = copy; + + while (*p == ' ') + ++p; + + name = parse_prefix_up_to (p, '=', (const char**) &p); + if (name == NULL) + { + lf_free (copy); + return FALSE; + } + ++p; /* skip '=' */ + + while (*p == ' ') + ++p; + + if (*p == '\'' || + *p == '"') + { + char *end; + + end = NULL; + if (!unquote_string_inplace (p, &end)) + { + lf_free (copy); + return FALSE; + } + + val = lf_internal_strndup (p, end - p); + + p = end; + } + else + { + char *end; + + end = NULL; + if (!unescape_string_inplace (p, &end)) + { + lf_free (copy); + return FALSE; + } + + val = lf_internal_strndup (p, end - p); + + p = end; + } + + while (*p == ' ') + ++p; + + *end_p = str + (p - copy); + + lf_free (copy); + + *name_p = name; + *val_p = val; + + return TRUE; +} + +lf_bool_t +lf_internal_unserialize_message (const char *message, + char **prefix_p, + char ***property_names, + char ***property_values) +{ + /* GLib would simplify this a lot... */ + char *prefix; + char **names; + char **values; + const char *p; + char *name; + char *value; + + *prefix_p = NULL; + *property_names = NULL; + *property_values = NULL; + + prefix = NULL; + names = NULL; + values = NULL; + + prefix = parse_prefix_up_to (message, ':', &p); + if (prefix == NULL) + return FALSE; + + ++p; /* skip ':' */ + + name = NULL; + value = NULL; + while (parse_property (p, &name, &value, &p)) + { + append_string_to_list (&names, name); + append_string_to_list (&values, value); + } + + *prefix_p = prefix; + *property_names = names; + *property_values = values; + + return TRUE; +} diff --git a/libsn/sn-xmessages.h b/libsn/sn-xmessages.h new file mode 100644 index 0000000..6920d5d --- /dev/null +++ b/libsn/sn-xmessages.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2002 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __LF_XMESSAGES_H__ +#define __LF_XMESSAGES_H__ + +#include <liblf/lf-common.h> + +LF_BEGIN_DECLS + +typedef void (* LfXmessageFunc) (LfDisplay *display, + const char *message_type, + const char *message, + void *user_data); + +void lf_internal_add_xmessage_func (LfDisplay *display, + const char *message_type, + LfXmessageFunc func, + void *func_data, + LfFreeFunc free_data_func); +void lf_internal_remove_xmessage_func (LfDisplay *display, + const char *message_type, + LfXmessageFunc func, + void *func_data); +void lf_internal_broadcast_xmessage (LfDisplay *display, + const char *message_type, + const char *message); + +char* lf_internal_serialize_message (const char *prefix, + const char **property_names, + const char **property_values); +lf_bool_t lf_internal_unserialize_message (const char *message, + char **prefix, + char ***property_names, + char ***property_values); + +LF_END_DECLS + +#endif /* __LF_XMESSAGES_H__ */ diff --git a/libsn/sn-xutils.c b/libsn/sn-xutils.c new file mode 100644 index 0000000..1a9d187 --- /dev/null +++ b/libsn/sn-xutils.c @@ -0,0 +1,466 @@ +/* + * Copyright (C) 2002 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "lf-internals.h" +#include <X11/Xutil.h> +#include <X11/Xatom.h> + +Atom +lf_internal_atom_get (LfDisplay *display, + const char *atom_name) +{ + return XInternAtom (lf_display_get_x_display (display), + atom_name, + False); +} + +void +lf_internal_set_utf8_string (LfDisplay *display, + Window xwindow, + const char *property, + const char *str) +{ + lf_display_error_trap_push (display); + + XChangeProperty (lf_display_get_x_display (display), + xwindow, + lf_internal_atom_get (display, property), + lf_internal_atom_get (display, "UTF8_STRING"), + 8, PropModeReplace, (unsigned char*) str, + strlen (str)); + + lf_display_error_trap_pop (display); +} + +void +lf_internal_set_string (LfDisplay *display, + Window xwindow, + const char *property, + const char *str) +{ + lf_display_error_trap_push (display); + + XChangeProperty (lf_display_get_x_display (display), + xwindow, + lf_internal_atom_get (display, property), + XA_STRING, + 8, PropModeReplace, (unsigned char*) str, + strlen (str)); + + lf_display_error_trap_pop (display); +} + +void +lf_internal_set_cardinal (LfDisplay *display, + Window xwindow, + const char *property, + int val) +{ + unsigned long long_val = val; + + lf_display_error_trap_push (display); + + XChangeProperty (lf_display_get_x_display (display), + xwindow, + lf_internal_atom_get (display, property), + XA_CARDINAL, + 32, PropModeReplace, (unsigned char*) &long_val, 1); + + lf_display_error_trap_pop (display); +} + +void +lf_internal_set_window (LfDisplay *display, + Window xwindow, + const char *property, + Window val) +{ + lf_display_error_trap_push (display); + + XChangeProperty (lf_display_get_x_display (display), + xwindow, + lf_internal_atom_get (display, property), + XA_WINDOW, + 32, PropModeReplace, (unsigned char*) &val, 1); + + lf_display_error_trap_pop (display); +} + +void +lf_internal_set_cardinal_list (LfDisplay *display, + Window xwindow, + const char *property, + int *vals, + int n_vals) +{ + lf_display_error_trap_push (display); + + XChangeProperty (lf_display_get_x_display (display), + xwindow, + lf_internal_atom_get (display, property), + XA_CARDINAL, + 32, PropModeReplace, (unsigned char*) vals, n_vals); + + lf_display_error_trap_pop (display); +} + +void +lf_internal_set_atom_list (LfDisplay *display, + Window xwindow, + const char *property, + Atom *vals, + int n_vals) +{ + lf_display_error_trap_push (display); + + XChangeProperty (lf_display_get_x_display (display), + xwindow, + lf_internal_atom_get (display, property), + XA_ATOM, + 32, PropModeReplace, (unsigned char*) vals, n_vals); + + lf_display_error_trap_pop (display); +} + +lf_bool_t +lf_internal_get_cardinal (LfDisplay *display, + Window xwindow, + const char *property, + int *val) +{ + Atom type; + int format; + unsigned long nitems; + unsigned long bytes_after; + unsigned long *num; + int result; + + *val = 0; + + lf_display_error_trap_push (display); + type = None; + num = NULL; + result = XGetWindowProperty (lf_display_get_x_display (display), + xwindow, + lf_internal_atom_get (display, property), + 0, 256, /* 256 = random number */ + False, XA_CARDINAL, &type, &format, &nitems, + &bytes_after, (unsigned char **)&num); + lf_display_error_trap_pop (display); + if (result != Success || num == NULL || nitems == 0) + { + if (num) + XFree (num); + return FALSE; + } + + if (type != XA_CARDINAL) + { + XFree (num); + return FALSE; + } + + *val = *num; + XFree (num); + + return TRUE; +} + +lf_bool_t +lf_internal_get_utf8_string (LfDisplay *display, + Window xwindow, + const char *property, + char **val) +{ + Atom type; + int format; + unsigned long nitems; + unsigned long bytes_after; + char *str; + int result; + Atom utf8_string; + + utf8_string = lf_internal_atom_get (display, "UTF8_STRING"); + + *val = NULL; + + lf_display_error_trap_push (display); + type = None; + str = NULL; + result = XGetWindowProperty (lf_display_get_x_display (display), + xwindow, + lf_internal_atom_get (display, property), + 0, 20000, /* 20000 = random number */ + False, + utf8_string, + &type, &format, &nitems, + &bytes_after, (unsigned char **)&str); + lf_display_error_trap_pop (display); + if (result != Success || str == NULL) + { + if (str) + XFree (str); + return FALSE; + } + + if (type != utf8_string || + format != 8 || + nitems == 0) + { + XFree (str); + return FALSE; + } + + if (!lf_internal_utf8_validate (str, nitems)) + { + fprintf (stderr, "Warning: invalid UTF-8 in property %s on window 0x%lx\n", + property, xwindow); + XFree (str); + return FALSE; + } + + *val = lf_internal_strdup (str); + XFree (str); + + return TRUE; +} + +lf_bool_t +lf_internal_get_string (LfDisplay *display, + Window xwindow, + const char *property, + char **val) +{ + Atom type; + int format; + unsigned long nitems; + unsigned long bytes_after; + char *str; + int result; + + *val = NULL; + + lf_display_error_trap_push (display); + type = None; + str = NULL; + result = XGetWindowProperty (lf_display_get_x_display (display), + xwindow, + lf_internal_atom_get (display, property), + 0, 20000, /* 20000 = random number */ + False, + XA_STRING, + &type, &format, &nitems, + &bytes_after, (unsigned char **)&str); + lf_display_error_trap_pop (display); + if (result != Success || str == NULL) + { + if (str) + XFree (str); + return FALSE; + } + + if (type != XA_STRING || + format != 8 || + nitems == 0) + { + XFree (str); + return FALSE; + } + + *val = lf_internal_strdup (str); + XFree (str); + + return TRUE; +} + +lf_bool_t +lf_internal_get_window (LfDisplay *display, + Window xwindow, + const char *property, + Window *val) +{ + Atom type; + int format; + unsigned long nitems; + unsigned long bytes_after; + Window *win; + int result; + + *val = 0; + + lf_display_error_trap_push (display); + type = None; + win = NULL; + result = XGetWindowProperty (lf_display_get_x_display (display), + xwindow, + lf_internal_atom_get (display, property), + 0, 256, /* 256 = random number */ + False, XA_WINDOW, &type, &format, &nitems, + &bytes_after, (unsigned char **)&win); + lf_display_error_trap_pop (display); + if (result != Success || win == NULL || nitems == 0) + { + if (win) + XFree (win); + return FALSE; + } + + if (type != XA_WINDOW) + { + XFree (win); + return FALSE; + } + + *val = *win; + XFree (win); + + return TRUE; +} + +lf_bool_t +lf_internal_get_atom_list (LfDisplay *display, + Window xwindow, + const char *property, + Atom **atoms, + int *n_atoms) +{ + Atom type; + int format; + unsigned long nitems; + unsigned long bytes_after; + Atom *data; + int result; + + *atoms = NULL; + *n_atoms = 0; + data = NULL; + + lf_display_error_trap_push (display); + type = None; + result = XGetWindowProperty (lf_display_get_x_display (display), + xwindow, + lf_internal_atom_get (display, property), + 0, 1000, /* 1000 = random number */ + False, XA_ATOM, &type, &format, &nitems, + &bytes_after, (unsigned char **)&data); + lf_display_error_trap_pop (display); + if (result != Success || data == NULL) + { + if (data) + XFree (data); + return FALSE; + } + + if (type != XA_ATOM) + { + XFree (data); + return FALSE; + } + + *atoms = lf_new (Atom, nitems); + memcpy (*atoms, data, sizeof (Atom) * nitems); + *n_atoms = nitems; + + XFree (data); + + return TRUE; +} + +lf_bool_t +lf_internal_get_cardinal_list (LfDisplay *display, + Window xwindow, + const char *property, + int **vals, + int *n_vals) +{ + Atom type; + int format; + unsigned long nitems; + unsigned long bytes_after; + unsigned long *nums; + int result; + int i; + + *vals = NULL; + *n_vals = 0; + nums = NULL; + + lf_display_error_trap_push (display); + type = None; + result = XGetWindowProperty (lf_display_get_x_display (display), + xwindow, + lf_internal_atom_get (display, property), + 0, 1000, /* 1000 = random number */ + False, XA_CARDINAL, &type, &format, &nitems, + &bytes_after, (unsigned char **)&nums); + lf_display_error_trap_pop (display); + if (result != Success || nums == NULL) + { + if (nums) + XFree (nums); + return FALSE; + } + + if (type != XA_CARDINAL) + { + XFree (nums); + return FALSE; + } + + *vals = lf_new (int, nitems); + *n_vals = nitems; + i = 0; + while (i < *n_vals) + { + (*vals)[i] = nums[i]; + ++i; + } + + XFree (nums); + + return TRUE; +} + +void +lf_internal_send_event_all_screens (LfDisplay *display, + unsigned long mask, + XEvent *xevent) +{ + int i; + Display *xdisplay; + + xdisplay = lf_display_get_x_display (display); + + i = 0; + while (lf_display_get_x_screen (display, i) != NULL) + { + XSendEvent (xdisplay, + RootWindow (xdisplay, i), + False, + mask, + xevent); + + ++i; + } +} diff --git a/libsn/sn-xutils.h b/libsn/sn-xutils.h new file mode 100644 index 0000000..819a3c8 --- /dev/null +++ b/libsn/sn-xutils.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2002 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __LF_XUTILS_H__ +#define __LF_XUTILS_H__ + +#include <liblf/lf-common.h> + +LF_BEGIN_DECLS + +Atom lf_internal_atom_get (LfDisplay *display, + const char *atom_name); +void lf_internal_set_utf8_string (LfDisplay *display, + Window xwindow, + const char *property, + const char *str); +void lf_internal_set_string (LfDisplay *display, + Window xwindow, + const char *property, + const char *str); +void lf_internal_set_cardinal (LfDisplay *display, + Window xwindow, + const char *property, + int val); +void lf_internal_set_window (LfDisplay *display, + Window xwindow, + const char *property, + Window val); +void lf_internal_set_cardinal_list (LfDisplay *display, + Window xwindow, + const char *property, + int *vals, + int n_vals); +void lf_internal_set_atom_list (LfDisplay *display, + Window xwindow, + const char *property, + Atom *vals, + int n_vals); + + +lf_bool_t lf_internal_get_utf8_string (LfDisplay *display, + Window xwindow, + const char *property, + char **val); +lf_bool_t lf_internal_get_string (LfDisplay *display, + Window xwindow, + const char *property, + char **val); +lf_bool_t lf_internal_get_cardinal (LfDisplay *display, + Window xwindow, + const char *property, + int *val); +lf_bool_t lf_internal_get_window (LfDisplay *display, + Window xwindow, + const char *property, + Window *val); +lf_bool_t lf_internal_get_atom_list (LfDisplay *display, + Window xwindow, + const char *property, + Atom **atoms, + int *n_atoms); +lf_bool_t lf_internal_get_cardinal_list (LfDisplay *display, + Window xwindow, + const char *property, + int **vals, + int *n_vals); + +void lf_internal_send_event_all_screens (LfDisplay *display, + unsigned long mask, + XEvent *xevent); + + +LF_END_DECLS + +#endif /* __LF_XUTILS_H__ */ diff --git a/test/Makefile.am b/test/Makefile.am new file mode 100644 index 0000000..ab8fa4a --- /dev/null +++ b/test/Makefile.am @@ -0,0 +1,31 @@ + +INCLUDES=$(LIBLF_CFLAGS) -I$(top_srcdir) + +noinst_PROGRAMS= \ + test-launcher \ + test-launchee \ + test-monitor \ + test-send-xmessage \ + test-watch-xmessages + +test_launcher_SOURCES= test-launcher.c + +test_launcher_LDADD= $(LIBLF_LIBS) $(top_builddir)/liblf/liblf-1.la + +test_launchee_SOURCES= test-launchee.c + +test_launchee_LDADD= $(LIBLF_LIBS) $(top_builddir)/liblf/liblf-1.la + +test_monitor_SOURCES= test-monitor.c + +test_monitor_LDADD= $(LIBLF_LIBS) $(top_builddir)/liblf/liblf-1.la + +test_send_xmessage_SOURCES= test-send-xmessage.c + +test_send_xmessage_LDADD= $(LIBLF_LIBS) $(top_builddir)/liblf/liblf-1.la + +test_watch_xmessages_SOURCES= test-watch-xmessages.c + +test_watch_xmessages_LDADD= $(LIBLF_LIBS) $(top_builddir)/liblf/liblf-1.la + +EXTRA_DIST=test-boilerplate.h
\ No newline at end of file diff --git a/test/test-boilerplate.h b/test/test-boilerplate.h new file mode 100644 index 0000000..90e1fbd --- /dev/null +++ b/test/test-boilerplate.h @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2002 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <sys/types.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <signal.h> + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef NULL +#define NULL ((void*) 0) +#endif + +#ifdef HAVE_BACKTRACE +#include <execinfo.h> +static void +print_backtrace (void) +{ + void *bt[500]; + int bt_size; + int i; + char **syms; + + bt_size = backtrace (bt, 500); + + syms = backtrace_symbols (bt, bt_size); + + i = 0; + while (i < bt_size) + { + fprintf (stderr, " %s\n", syms[i]); + ++i; + } + + free (syms); +} +#else +static void +print_backtrace (void) +{ + fprintf (stderr, "Not compiled with backtrace support\n"); +} +#endif + +static int error_trap_depth = 0; + +static int +x_error_handler (Display *xdisplay, + XErrorEvent *error) +{ + char buf[64]; + + XGetErrorText (xdisplay, error->error_code, buf, 63); + + if (error_trap_depth == 0) + { + print_backtrace (); + + fprintf (stderr, "Unexpected X error: %s serial %ld error_code %d request_code %d minor_code %d)\n", + buf, + error->serial, + error->error_code, + error->request_code, + error->minor_code); + + exit (1); + } + + return 1; /* return value is meaningless */ +} + +static void +error_trap_push (LfDisplay *display, + Display *xdisplay) +{ + ++error_trap_depth; +} + +static void +error_trap_pop (LfDisplay *display, + Display *xdisplay) +{ + if (error_trap_depth == 0) + { + fprintf (stderr, "Error trap underflow!\n"); + exit (1); + } + + XSync (xdisplay, False); /* get all errors out of the queue */ + --error_trap_depth; +} diff --git a/test/test-launchee.c b/test/test-launchee.c new file mode 100644 index 0000000..ef77b1c --- /dev/null +++ b/test/test-launchee.c @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2002 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <config.h> +#include <liblf/lf.h> + +#include "test-boilerplate.h" + +int +main (int argc, char **argv) +{ + Display *xdisplay; + LfDisplay *display; + LfLauncheeContext *context; + int i; + + xdisplay = XOpenDisplay (NULL); + if (xdisplay == NULL) + { + fprintf (stderr, "Could not open display\n"); + return 1; + } + + if (getenv ("LIBLF_SYNC") != NULL) + XSynchronize (xdisplay, True); + + XSetErrorHandler (x_error_handler); + + display = lf_display_new (xdisplay, + error_trap_push, + error_trap_pop); + + context = lf_launchee_context_new_from_environment (display); + + if (context == NULL) + { + fprintf (stderr, "Failed to get launch feedback info from DESKTOP_LAUNCH_ID/DESKTOP_LAUNCH_WINDOW\n"); + exit (1); + } + + printf ("Launchee started with window 0x%lx ID \"%s\"\n", + lf_launchee_context_get_launch_window (context), + lf_launchee_context_get_launch_id (context)); + + /* simulate startup time */ + i = 0; + while (i < 4) + { + sleep (1); + lf_launchee_context_pulse (context); + ++i; + } + + printf ("Launchee startup complete\n"); + lf_launchee_context_complete (context); + + while (TRUE) + { + XEvent xevent; + + XNextEvent (xdisplay, &xevent); + + lf_display_process_event (display, &xevent); + } + + return 0; +} diff --git a/test/test-launcher.c b/test/test-launcher.c new file mode 100644 index 0000000..3a006e0 --- /dev/null +++ b/test/test-launcher.c @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2002 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <config.h> +#include <liblf/lf.h> + +#include "test-boilerplate.h" + +static pid_t child_pid = 0; + +static void +launcher_event_func (LfLauncherEvent *event, + void *user_data) +{ + LfLauncherContext *context; + + context = lf_launcher_event_get_context (event); + + switch (lf_launcher_event_get_type (event)) + { + case LF_LAUNCHER_EVENT_COMPLETED: + printf ("Completed!\n"); + break; + case LF_LAUNCHER_EVENT_CANCELED: + printf ("Canceled!\n"); + kill (child_pid, SIGTERM); + lf_launcher_context_complete (context); + break; + case LF_LAUNCHER_EVENT_PULSE: + printf (" pulse.\n"); + break; + } +} + +int +main (int argc, char **argv) +{ + Display *xdisplay; + LfDisplay *display; + LfLauncherContext *context; + + if (argc < 2) + { + fprintf (stderr, "must specify command line to launch\n"); + exit (1); + } + + xdisplay = XOpenDisplay (NULL); + if (xdisplay == NULL) + { + fprintf (stderr, "Could not open display\n"); + return 1; + } + + if (getenv ("LIBLF_SYNC") != NULL) + XSynchronize (xdisplay, True); + + XSetErrorHandler (x_error_handler); + + display = lf_display_new (xdisplay, + error_trap_push, + error_trap_pop); + + context = lf_launcher_context_new (display, + launcher_event_func, + NULL, NULL); + + lf_launcher_context_set_launch_name (context, "Test Launch"); + lf_launcher_context_set_launch_description (context, "Launching a test program for liblf"); + lf_launcher_context_set_supports_cancel (context, TRUE); + lf_launcher_context_set_binary_name (context, argv[1]); + + lf_launcher_context_initiate (context, + "test-launcher", + argv[1], + CurrentTime); /* CurrentTime bad */ + + switch ((child_pid = fork ())) + { + case -1: + fprintf (stderr, "Fork failed: %s\n", strerror (errno)); + break; + case 0: + lf_launcher_context_setup_child_process (context); + execv (argv[1], argv + 1); + fprintf (stderr, "Failed to exec %s: %s\n", argv[1], strerror (errno)); + _exit (1); + break; + } + + lf_launcher_context_set_pid (context, child_pid); + + while (TRUE) + { + XEvent xevent; + + XNextEvent (xdisplay, &xevent); + + lf_display_process_event (display, &xevent); + } + + lf_launcher_context_unref (context); + + return 0; +} diff --git a/test/test-monitor.c b/test/test-monitor.c new file mode 100644 index 0000000..59d4524 --- /dev/null +++ b/test/test-monitor.c @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2002 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <config.h> +#include <liblf/lf.h> + +#include "test-boilerplate.h" + +static void +monitor_event_func (LfMonitorEvent *event, + void *user_data) +{ + LfMonitorContext *context; + LfLaunchSequence *sequence; + + context = lf_monitor_event_get_context (event); + sequence = lf_monitor_event_get_launch_sequence (event); + + switch (lf_monitor_event_get_type (event)) + { + case LF_MONITOR_EVENT_INITIATED: + { + int x, y, w, h; + const char *s; + + printf ("Initiated sequence %s\n", + lf_launch_sequence_get_id (sequence)); + printf (" launch window 0x%lx\n", + lf_launch_sequence_get_window (sequence)); + + s = lf_launch_sequence_get_name (sequence); + printf (" name %s\n", s ? s : "(unset)"); + + s = lf_launch_sequence_get_description (sequence); + printf (" description %s\n", s ? s : "(unset)"); + + printf (" workspace %d\n", + lf_launch_sequence_get_workspace (sequence)); + + printf (" %s cancel\n", + lf_launch_sequence_get_supports_cancel (sequence) ? + "supports" : "does not support"); + + if (lf_launch_sequence_get_geometry (sequence, + &x, &y, &w, &h)) + printf (" geometry %d,%d %d x %d window 0x%lx\n", + x, y, w, h, + lf_launch_sequence_get_geometry_window (sequence)); + else + printf (" no geometry set\n"); + + printf (" pid %d\n", + lf_launch_sequence_get_pid (sequence)); + + s = lf_launch_sequence_get_binary_name (sequence); + printf (" binary name %s\n", s ? s : "(unset)"); + s = lf_launch_sequence_get_icon_name (sequence); + printf (" icon name %s\n", s ? s : "(unset)"); + s = lf_launch_sequence_get_hostname (sequence); + printf (" hostname %s\n", s ? s : "(unset)"); + + s = lf_launch_sequence_get_legacy_resource_class (sequence); + printf (" legacy class %s\n", s ? s : "(unset)"); + s = lf_launch_sequence_get_legacy_resource_name (sequence); + printf (" legacy name %s\n", s ? s : "(unset)"); + s = lf_launch_sequence_get_legacy_window_title (sequence); + printf (" legacy title %s\n", s ? s : "(unset)"); + } + break; + + case LF_MONITOR_EVENT_COMPLETED: + printf ("Completed sequence %s\n", + lf_launch_sequence_get_id (sequence)); + break; + + case LF_MONITOR_EVENT_CANCELED: + printf ("Canceled sequence %s\n", + lf_launch_sequence_get_id (sequence)); + break; + + case LF_MONITOR_EVENT_PULSE: + printf ("Pulse for sequence %s\n", + lf_launch_sequence_get_id (sequence)); + break; + + case LF_MONITOR_EVENT_GEOMETRY_CHANGED: + { + int x, y, w, h; + + printf ("Geometry changed for sequence %s\n", + lf_launch_sequence_get_id (sequence)); + if (lf_launch_sequence_get_geometry (sequence, + &x, &y, &w, &h)) + printf (" geometry %d,%d %d x %d window 0x%lx\n", + x, y, w, h, + lf_launch_sequence_get_geometry_window (sequence)); + else + printf (" no geometry set\n"); + } + break; + case LF_MONITOR_EVENT_PID_CHANGED: + { + printf ("PID for sequence %s is now %d\n", + lf_launch_sequence_get_id (sequence), + lf_launch_sequence_get_pid (sequence)); + } + break; + case LF_MONITOR_EVENT_WORKSPACE_CHANGED: + { + printf ("Workspace for sequence %s is now %d\n", + lf_launch_sequence_get_id (sequence), + lf_launch_sequence_get_workspace (sequence)); + } + break; + } +} + +int +main (int argc, char **argv) +{ + Display *xdisplay; + LfDisplay *display; + LfMonitorContext *context; + + xdisplay = XOpenDisplay (NULL); + if (xdisplay == NULL) + { + fprintf (stderr, "Could not open display\n"); + return 1; + } + + if (getenv ("LIBLF_SYNC") != NULL) + XSynchronize (xdisplay, True); + + XSetErrorHandler (x_error_handler); + + /* We have to select for property events on at least one + * root window (but not all as INITIATE messages go to + * all root windows) + */ + XSelectInput (xdisplay, DefaultRootWindow (xdisplay), + PropertyChangeMask); + + display = lf_display_new (xdisplay, + error_trap_push, + error_trap_pop); + + context = lf_monitor_context_new (display, + monitor_event_func, + NULL, NULL); + + while (TRUE) + { + XEvent xevent; + + XNextEvent (xdisplay, &xevent); + + lf_display_process_event (display, &xevent); + } + + lf_monitor_context_unref (context); + + return 0; +} diff --git a/test/test-send-xmessage.c b/test/test-send-xmessage.c new file mode 100644 index 0000000..3052a60 --- /dev/null +++ b/test/test-send-xmessage.c @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2002 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <config.h> +#include <liblf/lf.h> +#include <liblf/lf-xmessages.h> + +#include "test-boilerplate.h" + +int +main (int argc, char **argv) +{ + Display *xdisplay; + LfDisplay *display; + + if (argc != 3) + { + fprintf (stderr, "Must specify message type and message content as first and second args\n"); + return 1; + } + + xdisplay = XOpenDisplay (NULL); + if (xdisplay == NULL) + { + fprintf (stderr, "Could not open display\n"); + return 1; + } + + if (getenv ("LIBLF_SYNC") != NULL) + XSynchronize (xdisplay, True); + + XSetErrorHandler (x_error_handler); + + display = lf_display_new (xdisplay, + error_trap_push, + error_trap_pop); + + lf_internal_broadcast_xmessage (display, + argv[1], + argv[2]); + + return 0; +} diff --git a/test/test-watch-xmessages.c b/test/test-watch-xmessages.c new file mode 100644 index 0000000..150af5c --- /dev/null +++ b/test/test-watch-xmessages.c @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2002 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <config.h> +#include <liblf/lf.h> +#include <liblf/lf-xmessages.h> +#include <liblf/lf-internals.h> + +#include "test-boilerplate.h" + +static void +message_func (LfDisplay *display, + const char *message_type, + const char *message, + void *user_data) +{ + char *prefix; + char **names; + char **values; + int i; + +#if 0 + printf ("raw %s: %s\n", + message_type, message); +#endif + + prefix = NULL; + names = NULL; + values = NULL; + + if (lf_internal_unserialize_message (message, + &prefix, &names, &values)) + { + printf (" %s:\n", prefix); + + i = 0; + while (names && names[i]) + { + printf (" '%s' = '%s'\n", names[i], values[i]); + + ++i; + } + + lf_internal_strfreev (names); + lf_internal_strfreev (values); + } +} + +int +main (int argc, char **argv) +{ + Display *xdisplay; + LfDisplay *display; + + if (argc != 2) + { + fprintf (stderr, "argument must be type of events to watch\n"); + return 1; + } + + xdisplay = XOpenDisplay (NULL); + if (xdisplay == NULL) + { + fprintf (stderr, "Could not open display\n"); + return 1; + } + + if (getenv ("LIBLF_SYNC") != NULL) + XSynchronize (xdisplay, True); + + XSetErrorHandler (x_error_handler); + + /* We have to select for property events on one root window + */ + XSelectInput (xdisplay, DefaultRootWindow (xdisplay), + PropertyChangeMask); + + display = lf_display_new (xdisplay, + error_trap_push, + error_trap_pop); + + lf_internal_add_xmessage_func (display, + argv[1], + message_func, + NULL, NULL); + + while (TRUE) + { + XEvent xevent; + + XNextEvent (xdisplay, &xevent); + + lf_display_process_event (display, &xevent); + } + + return 0; +} |